From e62b1d15cd14bfe362613091cad4d621c7ffc904 Mon Sep 17 00:00:00 2001 From: Simone Piccardi Date: Fri, 20 Jul 2007 23:08:51 +0000 Subject: [PATCH] Esempio di inotify, e qualche correzione sui sysctl dei socket --- fileadv.tex | 142 ++++++++++++++++++++++++++------------ sockctrl.tex | 19 +++-- sources/inotify_monitor.c | 88 ++++++++++++++++++----- 3 files changed, 182 insertions(+), 67 deletions(-) diff --git a/fileadv.tex b/fileadv.tex index 2f9e795..9da4c61 100644 --- a/fileadv.tex +++ b/fileadv.tex @@ -1309,10 +1309,10 @@ modifiche con una qualunque delle modalit illustrate in sez.~\ref{sec:file_multiplexing}. Infine l'interfaccia di \textit{inotify} consente di mettere sotto -osservazione, oltre che una directory anche singoli file. Una volta creata la -coda di notifica si devono definire gli eventi da tenere sotto osservazione; -questo viene fatto attraverso una \textsl{lista di osservazione} (o -\textit{watch list}) che è associata alla coda. Per gestire la lista di +osservazione, oltre che una directory, anche singoli file. Una volta creata +la coda di notifica si devono definire gli eventi da tenere sotto +osservazione; questo viene fatto attraverso una \textsl{lista di osservazione} +(o \textit{watch list}) che è associata alla coda. Per gestire la lista di osservazione l'interfaccia fornisce due funzioni, la prima di queste è \funcd{inotify\_add\_watch}, il cui prototipo è: \begin{prototype}{sys/inotify.h} @@ -1325,7 +1325,7 @@ osservazione l'interfaccia fornisce due funzioni, la prima di queste \begin{errlist} \item[\errcode{EACCESS}] non si ha accesso in lettura al file indicato. \item[\errcode{EINVAL}] \param{mask} non contiene eventi legali o \param{fd} - non è un filesystem di \textit{inotify}. + non è un file descriptor di \textit{inotify}. \item[\errcode{ENOSPC}] si è raggiunto il numero massimo di voci di osservazione o il kernel non ha potuto allocare una risorsa necessaria. \end{errlist} @@ -1383,7 +1383,7 @@ flag della prima parte. \const{IN\_DELETE\_SELF} & & È stato cancellato il file (o la directory) sotto osservazione.\\ \const{IN\_MODIFY} &$\bullet$& È stato modificato il file.\\ - \const{IN\_MOVE\_SELF} & & è stato rinominato il file (o la + \const{IN\_MOVE\_SELF} & & È stato rinominato il file (o la directory) sotto osservazione.\\ \const{IN\_MOVED\_FROM} &$\bullet$& Un file è stato spostato fuori dalla directory sotto osservazione.\\ @@ -1468,12 +1468,14 @@ automaticamente rimosso dalla lista di osservazione e nessun ulteriore evento sarà più notificato. In caso di successo \func{inotify\_add\_watch} ritorna un intero positivo, -detto \textit{watch descriptor}; è tramite questo valore che si identifica -univocamente un \textsl{osservatore} su una coda di notifica, sia per quanto -riguarda i risultati restituiti da \textit{inotify}, che per quanto riguarda -la eventuale rimozione dello stesso; la seconda funzione per la gestione delle -liste di osservazione è infatti \funcd{inotify\_rm\_watch}, che permette di -rimuovere un \textsl{osservatore}; il suo prototipo è: +detto \textit{watch descriptor}, che identifica univocamente un +\textsl{osservatore} su una coda di notifica; esso viene usato per farvi +riferimento sia riguardo i risultati restituiti da \textit{inotify}, che per +la eventuale rimozione dello stesso. + +La seconda funzione per la gestione delle code di notifica, che permette di +rimuovere un \textsl{osservatore}, è \funcd{inotify\_rm\_watch}, ed il suo +prototipo è: \begin{prototype}{sys/inotify.h} {int inotify\_rm\_watch(int fd, uint32\_t wd)} @@ -1494,17 +1496,23 @@ La funzione rimuove dalla coda di notifica identificata dall'argomento \param{fd} l'osservatore identificato dal \textit{watch descriptor} \param{wd};\footnote{ovviamente deve essere usato per questo argomento un valore ritornato da \func{inotify\_add\_watch}, altrimenti si avrà un errore - di \errval{EINVAL}.} inoltre, contemporaneamente alla rimozione -dell'osservatore, sulla coda di notifica verrà generato un evento di tipo -\const{IN\_IGNORED} (vedi tab.~\ref{tab:inotify_read_event_flag}). - - -Oltre che per la rimozione, il \textit{watch descriptor} viene usato anche per -identificare l'evento a cui si fa riferimento nella lista dei risultati -restituiti da \textit{inotify}; questi ultimi infatti vengono notificati alle -applicazioni che usano l'interfaccia di \textit{inotify} come dati presenti in -lettura su file descriptor creato con \func{inotify\_init}. - + di \errval{EINVAL}.} in caso di successo della rimozione, contemporaneamente +alla cancellazione dell'osservatore, sulla coda di notifica verrà generato un +evento di tipo \const{IN\_IGNORED} (vedi +tab.~\ref{tab:inotify_read_event_flag}). Si tenga presente che se un file +viene cancellato o un filesystem viene smontato i relativi osservatori vengono +rimossi automaticamente e non è necessario utilizzare +\func{inotify\_rm\_watch}. + + +Come accennato l'interfaccia di \textit{inotify} prevede che gli eventi siano +notificati come dati presenti in lettura sul file descriptor associato alla +coda di notifica. Una applicazione pertanto dovrà leggere i dati da detto file +con una \func{read}, che ritornerà sul buffer i dati presenti nella forma di +una o più strutture di tipo \struct{inotify\_event} (la cui definizione è +riportata in fig.~\ref{fig:inotify_event}). Qualora non siano presenti dati la +\func{read} si bloccherà (a meno di non aver impostato il file descriptor in +modalità non bloccante) fino all'arrivo di almeno un evento. \begin{figure}[!htb] \footnotesize \centering @@ -1512,24 +1520,33 @@ lettura su file descriptor creato con \func{inotify\_init}. \includestruct{listati/inotify_event.h} \end{minipage} \normalsize - \caption{La struttura \structd{inotify\_event}.} + \caption{La struttura \structd{inotify\_event} usata dall'interfaccia di + \textit{inotify} per riportare gli eventi.} \label{fig:inotify_event} \end{figure} - -Inoltre l'interfaccia di \textit{inotify} permette di conoscere, come avviene -per i file descriptor associati ai socket (si veda al proposito quanto -trattato in sez.~\ref{sec:sock_ioctl_IP}) il numero di byte disponibili in -lettura sul nostro file descriptor, utilizzando su di esso l'operazione -\const{FIONREAD} con \func{ioctl}.\footnote{questa è una delle operazioni - speciali (che abbiamo visto in sez.~\ref{sec:file_ioctl}) che nel caso è - disponibile solo per i socket e per i file descriptor creati con - \func{inotify\_init}.} Questo consente anche di ottenere rapidamente il -numero di file che sono cambiati. - - - - +Una ulteriore caratteristica dell'interfaccia di \textit{inotify} è che essa +permette di ottenere con \func{ioctl}, come per i file descriptor associati ai +socket (si veda sez.~\ref{sec:sock_ioctl_IP}) il numero di byte disponibili in +lettura sul file descriptor, utilizzando su di esso l'operazione +\const{FIONREAD}.\footnote{questa è una delle operazioni speciali per i file + (vedi sez.~\ref{sec:file_ioctl}), che è disponibile solo per i socket e per + i file descriptor creati con \func{inotify\_init}.} Si può così utilizzare +questa operazione, oltre che per predisporre una operazione di lettura con un +buffer di dimensioni adeguate, anche per ottenere rapidamente il numero di +file che sono cambiati. + +Una volta effettuata la lettura con \func{read} a ciascun evento sarà +associata una struttura \struct{inotify\_event} contenente i rispettivi dati. +Per identificare a quale file o directory l'evento corrisponde viene +restituito nel campo \var{wd} il \textit{watch descriptor} con cui il relativo +osservatore è stato registrato. Il campo \var{mask} contiene invece una +maschera di bit che identifica il tipo di evento verificatosi; in essa +compariranno sia i bit elencati nella prima parte di +tab.~\ref{tab:inotify_event_watch}, che gli eventuali valori +aggiuntivi\footnote{questi compaiono solo nel campo \var{maks} di + \struct{inotify\_event}, e non utilizzabili in fase di registrazione + dell'osservatore.} di tab.~\ref{tab:inotify_read_event_flag}. \begin{table}[htb] \centering @@ -1539,17 +1556,56 @@ numero di file che sono cambiati. \textbf{Flag} & \textbf{Significato} \\ \hline \hline - \const{IN\_IGNORED} & .\\ - \const{IN\_ISDIR} & .\\ - \const{IN\_Q\_OVERFLOW}& .\\ - \const{IN\_UNMOUNT} & .\\ + \const{IN\_IGNORED} & L'osservatore è stato rimosso, sia in maniera + esplicita con l'uso di \func{inotify\_rm\_watch}, + che in maniera implicita per la rimozione + dell'oggetto osservato o per lo smontaggio del + filesystem su cui questo si trova.\\ + \const{IN\_ISDIR} & L'evento avvenuto fa riferimento ad una directory + (consente così di distinguere, quando si pone + sotto osservazione una directory, fra gli eventi + relativi ad essa e quelli relativi ai file che + essa contiene).\\ + \const{IN\_Q\_OVERFLOW}& Si sono eccedute le dimensioni della coda degli + eventi (\textit{overflow} della coda); in questo + caso il valore di \var{wd} è $-1$.\footnotemark\\ + \const{IN\_UNMOUNT} & Il filesystem contenente l'oggetto posto sotto + osservazione è stato smontato.\\ \hline \end{tabular} \caption{Le costanti che identificano i flag aggiuntivi usati nella maschera - binaria del campo \var{mask} di \structd{inotify\_event}.} + binaria del campo \var{mask} di \struct{inotify\_event}.} \label{tab:inotify_read_event_flag} \end{table} +\footnotetext{la coda di notifica ha una dimensione massima specificata dal + parametro di sistema \procfile{/proc/sys/fs/inotify/max\_queued\_events} che + indica il numero massimo di eventi che possono essere mantenuti sulla + stessa; quando detto valore viene ecceduto gli ulteriori eventi vengono + scartati, ma viene comunque generato un evento di tipo + \const{IN\_Q\_OVERFLOW}.} + +Il campo \var{cookie} contiene invece un intero univoco che permette di +identificare eventi correlati (per i quali avrà lo stesso valore), al momento +viene utilizzato soltanto per rilevare lo spostamento di un file, consentendo +così all'applicazione di collegare la corrispondente coppia di eventi +\const{IN\_MOVED\_TO} e \const{IN\_MOVED\_FROM}. + +Infine due campi \var{name} e \var{len} sono utilizzati soltanto quando +l'evento è relativo ad un file presente in una directory posta sotto +osservazione, in tal caso essi contengono rispettivamente il nome del file +(come pathname relativo alla directory osservata) e la relativa dimensione in +byte. Il campo \var{name} viene sempre restituito come stringa terminata da +NUL, con uno o più zeri di terminazione, a seconda di eventuali necessità di +allineamento del risultato, ed il valore di \var{len} corrisponde al totale +della dimensione di \var{name}, zeri aggiuntivi compresi. Questo significa che +le dimensioni di ciascun evento di \textit{inotify} saranno pari al valore +\code{sizeof(\struct{inotify\_event}) + len}. + +Vediamo allora un esempio dell'uso dell'interfaccia di \textit{inotify} con un +semplice programma che permette di mettere sotto osservazione un file o una +directory. + % TODO inserire anche inotify, vedi http://www.linuxjournal.com/article/8478 diff --git a/sockctrl.tex b/sockctrl.tex index 1c5bef9..85c26fa 100644 --- a/sockctrl.tex +++ b/sockctrl.tex @@ -4190,11 +4190,20 @@ pagina di manuale (accessibile con \texttt{man 7 tcp}), sono i seguenti: che consente di diminuire il numero di pacchetti scambiati.} \item[\procrelfile{/proc/sys/net/ipv4}{tcp\_ecn}] Abilita il meccanismo della - \textit{Explicit Congestion Notification} (o ECN) definito - nell'\href{http://www.ietf.org/rfc/rfc2884.txt}{RFC~2884}. Si tenga presente - che se si abilita questa opzione si possono avere dei malfunzionamenti - apparentemente casuali dipendenti dalla destinazione, dovuti al fatto che - alcuni vecchi router non supportano il meccanismo ed alla sua attivazione + \textit{Explicit Congestion Notification} (in breve ECN) nelle connessioni + TCP. Questo è un meccanismo (è descritto in dettaglio + nell'\href{http://www.ietf.org/rfc/rfc3168.txt}{RFC~3168} mentre gli effetti + sulle prestazioni del suo utilizzo sono documentate + nell'\href{http://www.ietf.org/rfc/rfc2884.txt}{RFC~2884} ) che consente di + notificare quando una rotta o una rete è congestionata da un eccesso di + traffico, si può così essere avvisati e cercare rotte alternative oppure + diminuire l'emissione di pacchetti (in + + + Si tenga presente che se si abilita questa opzione si possono avere dei + malfunzionamenti apparentemente casuali dipendenti dalla destinazione, + dovuti al fatto che alcuni vecchi router non supportano il meccanismo ed + alla sua attivazione scartano i relativi pacchetti.\\ \item[\procrelfile{/proc/sys/net/ipv4}{tcp\_fack}] è un valore logico che diff --git a/sources/inotify_monitor.c b/sources/inotify_monitor.c index d2c4f2d..92391cf 100644 --- a/sources/inotify_monitor.c +++ b/sources/inotify_monitor.c @@ -39,12 +39,15 @@ /* Help printing routine */ void usage(void); +void printevent(unsigned int mask); int main(int argc, char *argv[]) { - int i; - int fd; + int i, size; + int fd, wd; + char buffer[128*sizeof(struct inotify_event)]; unsigned int mask=0; + struct inotify_event * event; /* * Input section: decode command line parameters * Use getopt function @@ -55,30 +58,28 @@ int main(int argc, char *argv[]) /* * Handling options */ - case 'h': /* help option */ + case 'h': /* help option */ printf("Wrong -h option use\n"); usage(); - return -1; - break; - case 'r': /* read access */ + case 'r': /* read access */ mask |= IN_ACCESS; break; - case 'w': /* write access */ + case 'w': /* write access */ mask |= IN_MODIFY; break; - case 'c': /* creation */ + case 'c': /* creation */ mask |= IN_CREATE; break; - case 'd': /* deletion */ + case 'd': /* deletion */ mask |= IN_DELETE; break; - case 'a': /* all events */ + case 'a': /* all events */ mask |= IN_ALL_EVENTS; break; - case '?': /* unrecognized options */ + case '?': /* unrecognized options */ printf("Unrecognized options -%c\n",optopt); usage(); - default: /* should not reached */ + default: /* should not reached */ usage(); } } @@ -89,22 +90,40 @@ int main(int argc, char *argv[]) * Main code beginning * * ***********************************************************/ - if ((argc - optind) != 1) { /* There must be remaing parameters */ + /* There must be one argument */ + if ((argc - optind) != 1) { printf("Wrong number of arguments %d\n", argc - optind); usage(); } - /* initalialize */ + /* initalialize inotify */ fd = inotify_init (); if (fd < 0) perror("Failing on inotify_init"); /* add watch */ - if (inotify_add_watch(fd, argv[1], mask) != 0) { - printf("Failing to add watched file %s; %s\n", - argv[1], strerror(errno)); + wd = inotify_add_watch(fd, argv[optind], mask); + if ( wd <= 0) { + printf("Failing to add watched file %s, mask %i; %s\n", + argv[optind], mask, strerror(errno)); exit(-1); + } + /* + * Main Loop: read events and print them + */ + while (1) { + size = read(fd, buffer, sizeof(buffer)); + + event = (struct inotify_event *) buffer; + if (wd != event->wd) { + printf("Error, getting different watch descriptor, %i and %i\n", + wd, event->wd); + exit(1); + } + printf("Observed event on %s\n", argv[optind-1+event->wd]); + if (event->name != NULL) + printf("On file %s\n", event->name); + printevent(event->mask); } - return 0; } @@ -114,7 +133,7 @@ int main(int argc, char *argv[]) void usage(void) { printf("Program inotify_monitor: monitor file changes \n"); printf("Usage:\n"); - printf(" inotify_monitor [-h] -rwcd dirname/filename \n"); + printf(" inotify_monitor [-h] -rwcd dirname/filename dir/file ... \n"); printf(" -h print this help\n"); printf(" -w watch write\n"); printf(" -r watch read\n"); @@ -123,3 +142,34 @@ void usage(void) { printf(" -a watch all\n"); exit(1); } + +void printevent(unsigned int mask) { + int i; + int val; + char * inotify_event[] = { + "IN_ACCESS", + "IN_MODIFY", + "IN_ATTRIB", + "IN_CLOSE_WRITE", + "IN_CLOSE_NOWRITE", + "IN_OPEN", + "IN_MOVED_FROM", + "IN_MOVED_TO", + "IN_CREATE", + "IN_DELETE", + "IN_DELETE_SELF", + "IN_MOVE_SELF", + "ERROR!!!", + "IN_UNMOUNT", + "IN_Q_OVERFLOW", + "IN_IGNORED" + }; + + val=1; + for (i=0; i<16; i++) { + if (mask & val) + printf("%s, ", inotify_event[i]); + val = val << 1; + } + printf("\n"); +} -- 2.30.2