Newsgroup: it.comp.os.linux.development
Titolo: inotify e lettura di strutture dati da file descriptor

Salve a tutti,
sono un vecchio niubbo del c (nel senso che lo studiai, tanto tempo
fa, ma oramai ho dimenticato quasi tutto) e ho un problema.
Sto facendo un programmino che utilizza le API di inotify
(inotify_init(), inotify_add_watch(), inotify_rm_watch()) per
controllare una serie di directory ricorsivamente.
Solo che ho un problema con la read!
cioè: inotify_init ritorna un file descriptor, che la documentazione
mi dice essere lo stream associato alla coda degli eventi che accadono
ai miei file monitorati. Solo che la struttura di un singolo evento è
di questo tipo:
struct inotify_event {
    int      wd;       /* Watch descriptor */
    uint32_t mask;     /* Mask of events */
    uint32_t cookie;   /* Unique cookie associating related
                          events (for rename(2)) */
    uint32_t len;      /* Size of 'name' field */
    char     name[];   /* Optional null-terminated name */
};
come si può vedere l'ultimo campo è a dimensione variabile (tant'è che
"len" è lì proprio a questo scopo).
Io, con la read, vorrei poter leggere "direttamente" dal fd, e
scrivere "direttamente" in una variabile di tipo struct inotify_event,
solo che la dimensione della struttura la devo calcolare al volo (in
man inotify stessso leggo che la dimensione di un evento è size(struct
inotify_event) + evento->len), e non so come fare, visto che è
contenuta nella struttura stessa, e quindi non so che parametro
passare a malloc() per poter allocare lo spazio "giusto" per il dato
che mi viene dal file!
Sapete aiutarmi?

 writes:
VD> struct inotify_event {
VD>     int      wd;       /* Watch descriptor */
VD>     uint32_t mask;     /* Mask of events */
VD>     uint32_t cookie;   /* Unique cookie associating related
VD>                           events (for rename(2)) */
VD>     uint32_t len;      /* Size of 'name' field */
VD>     char     name[];   /* Optional null-terminated name */
VD> };
VD> come si può vedere l'ultimo campo è a dimensione variabile (tant'è
VD> che "len" è lì proprio a questo scopo).
VD> Io, con la read, vorrei poter leggere "direttamente" dal fd, e
VD> scrivere "direttamente" in una variabile di tipo struct
VD> inotify_event, solo che la dimensione della struttura la devo
VD> calcolare al volo (in man inotify stessso leggo che la dimensione di
VD> un evento è size(struct inotify_event) + evento->len), e non so come
VD> fare, visto che è contenuta nella struttura stessa, e quindi non so
VD> che parametro passare a malloc() per poter allocare lo spazio
VD> "giusto" per il dato che mi viene dal file!
Hmmm...
Se la mia conoscenza del C non mi tradisce ( K&R forever :)!!! ), name è
un puntarore, ovvero in memoria non hai una cosa del tipo:
(leggere con font fixed e senza riformattazioni - DEADBEEF è un valore a
 32 bit che a noi non interessa)
campo :[   wd   ][  mask  ][ cookie ][  len   ][         name          ]
valore: DEADBEEF  DEADBEEF  DEADBEEF     23    [IoSonoIlNomeDell'Evento]
ma
campo :[   wd   ][  mask  ][ cookie ][  len   ][  name  ]
valore: DEADBEEF  DEADBEEF  DEADBEEF     23     *pointer
 
ed altrove nella memoria (alla locazione *pointer) 
[IoSonoIlNomeDell'Evento]
Quindi tu allochi un inotify_event  e leggi quello, poi vedi quanto vale
len e  allochi len+1*sizeof(char) byte  per il nome  (+ 1 perché  è null
terminated ed  il sizeof(char)  perché non capita,  ma se char  non vale
1... :) )
Sperando di non aver detto vaccate... Ma non dovrei.
-- 
 /\           ___                                    Ubuntu: ancient
/___/\_|_|\_|__|___Gian Uberto Lauri_____               African word
  //--\| | \|  |   Integralista GNUslamico            meaning "I can
\/                   e coltivatore diretto               not install
                               di software                   Debian"
a Cesare avrei detto di scrivermi | I would have told Caesar to write
a 
fn...@rat.vg
                    | me at 
fn...@rat.vg

 contenuta nella struttura stessa,  [...]
Funziona così. 
Ogni 'read' restituisce un certo numero (anche 1) di blocchi che
rappresentano gli eventi. I blocchi hanno dimensioni variabili. La presenza
di una coda (name) dipende dal tipo di evento.
Per leggere almeno un evento è necessario che il buffer abbia spazio
sufficiente per ospitarlo. Direi che lo spazio massimo che può occupare un
evento sia sizeof(inotify_event)+PATH_MAX (+qualcosa per all'allineamento).
Basta quindi predisporre un buffer di quelle dimensioni, o magari qualche
multiplo. Non occorre allocarlo ogni volta con malloc. Puoi usare sempre lo
stesso buffer, o puoi anche dichiararlo come array nella funzione che
esegue la 'read'.
Ricordati che -come ho detto- ogni singola 'read' può ricevere anche più
eventi. Pertanto devi scorrere i blocchi uno per uno, usando ogni volta il
valore di 'len' per passare quello successivo, finché non esaurisci la
quantità di dati disponibili (che è il valore restituito da 'read', *non*
la dimensione del buffer).
Max

 valore: DEADBEEF  DEADBEEF  DEADBEEF     23    [IoSonoIlNomeDell'Evento]
Sě, hai proprio una cosa di questo tipo.
Max

 la dimensione del buffer).
prima di tutto, grazie! Senza di te non avrei mai colto che read
poteva restituire anche più strutture di fila!
in secondo luogo: non riesco a fare quello che dici:
diciamo che questo è il mio codice (semplificato e senza i controlli)
size_t base_size = sizeof(struct inotify_event);
size_t size = base_size + MAX_FILENAME_LEN;
struct inotify_event* events = malloc(size);
while (1) {
  ssize_t readed = read(inotify_instance, events, size);
  while (readed) {
    // leggi events
    int dimensioni_struttura = base_size + events->len;
    readed -= dimensioni_struttura;
    events += dimensioni_struttura;
  }
}
a volte, però, mi accorgo che "mi perdo qualche evento per strada",
nel senso che quella mia variabile "dimensioni struttura" a volte è
troppo grande, e arrivo in una locazione di memoria totalmente
inutile...
ho provato anche con solo events->len (o con solo base_size, se è per
questo), ma non sono riuscito a capire dov'e' che sbaglio.
invece, a parte (giustamente) un warning da parte del compilatore sul
mismach dei tipi di dato, le cose vanno perfettamente se sostituisco
l'ultima riga con
            events = events->name + events->len;
cosa mi sfugge?

 cosa mi sfugge?
Ti sfugge una regola dell'aritmetica dei puntatori. Se 'p' č un puntatore,
quando scrivi 'p += n' sposti 'p' di ( n * sizeof(*p) ) byte, non di n byte
(a meno che sizeof(*p) non valga 1).
Quindi se scrivi 'events += dimensioni_struttura', non ottieni quello che
desideri.
Dovresti fare qualcosa del genere:
 char buffer[ MAX_SIZE ];
 int count = read( fd, buffer, sizeof(buffer) );
 int offset = 0;
 while( offset < count )
 {
     struct inotify_event* event = (struct inotify_event*)(buffer+offset);
     // leggi event ...
    offset += sizeof(struct inotify_event) + event->len;    
 }
Max