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}
\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}
\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.\\
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)}
\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
\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
\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
/* 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
/*
* 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();
}
}
* 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;
}
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");
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");
+}