Manpages

NOM

inotify - Surveiller les événements des systèmes de fichiers

DESCRIPTION

L’API inotify fournit un mécanisme pour surveiller les événements au niveau des systèmes de fichiers. Inotify peut être utilisé pour surveiller des fichiers individuels ou des répertoires. Quand un répertoire est surveillé, inotify va signaler des événements pour le répertoire lui-même et pour les fichiers de ce répertoire.

Les appels système suivants sont utilisés avec cette interface de programmation :

*

inotify_init(2) crée une instance inotify et renvoie un descripteur de fichier se référant à cette instance inotify. L’appel système plus récent inotify_init1(2) est comme inotify_init(2), mais a un argument flags qui fournit un accès à des fonctionnalités supplémentaires.

*

inotify_add_watch(2) manipule la « liste de surveillance » associée à une instance inotify. Chaque élément (« watch ») de la liste de surveillance indique le chemin d’un fichier ou d’un répertoire, avec un ensemble d’événements que le noyau doit surveiller pour le fichier indiqué par ce chemin. inotify_add_watch(2) crée un nouvel élément de surveillance ou modifie un élément existant. Chaque élément a un unique « descripteur de surveillance », un entier renvoyé par inotify_add_watch(2) lorsque cet élément est créé.

*

Quand les événements ont lieu pour des fichiers et répertoires surveillés, ces événements sont rendus disponibles à l’application comme des données structurées qui peuvent être lues depuis le descripteur de fichier inotify en utilisant read(2) (voir plus bas).

*

inotify_rm_watch(2) retire un élément d’une liste de surveillance inotify.

*

Quand tous les descripteurs de fichier se référant à une instance inotify ont été fermés (en utilisant close(2)), l’objet sous-jacent et ses ressources sont libérés pour être réutilisés par le noyau ; tous les éléments de surveillance associés sont automatiquement libérés.

Avec une programmation prudente, une application peut utiliser inotify pour surveiller et mettre en cache efficacement l’état d’un ensemble d’objets de système de fichiers. Cependant, les applications robustes devraient prendre en compte que des bogues dans la logique de surveillance ou des situations de compétition du type décrit ci-dessous pourraient laisser le cache incohérent avec l’état du système de fichiers. Réaliser des vérifications de cohérence et reconstruire le cache en cas de détection d’incohérences serait sans doute sage.

Lecture d’événements d’un descripteur de fichier inotify
Pour déterminer quels événements ont eu lieu, une application va lire avec read(2) le descripteur de fichier inotify. Si aucun événement n’a eu lieu, alors, en supposant qu’il s’agisse d’un descripteur de fichier bloquant, read(2) se bloquera jusqu’à ce qu’au moins un événement ait lieu (à moins qu’elle ne soit interrompue par un signal, auquel cas l’appel échouera avec l’erreur EINTR ; consultez signal(7)).

Chaque lecture (avec read(2)) réussie renvoie un tampon contenant une ou plusieurs des structures suivantes :

struct inotify_event {
    int      wd;       /* Descripteur de surveillance */
    uint32_t mask;     /* Masque décrivant l’événement */
    uint32_t cookie;   /* Cookie unique d’association des
                          événements (pour rename(2)) */
    uint32_t len;      /* Taille du champ name */
    char     name[];   /* Nom optionnel terminé par un nul */
};

wd identifie l’élément de surveillance pour lequel cet événement a lieu. Il s’agit de l’un des descripteurs de surveillance renvoyés par un précédent appel à inotify_add_watch(2).

mask contient des bits qui décrivent l’événement qui a eu lieu (voir ci-dessous).

cookie est un entier unique qui relie les événements. Ce n’est actuellement utilisé que pour les événements de renommage, et permet à la paire d’événements IN_MOVED_FROM et IN_MOVED_TO en résultant d’être associée par l’application. Pour tous les autres types d’événements, cookie est mis à 0.

The name field is present only when an event is returned for a file inside a watched directory; it identifies the filename within the watched directory. This filename is null-terminated, and may include further null bytes ('\0') to align subsequent reads to a suitable address boundary.

Le champ len compte tous les octets de name, incluant les caractères nuls. La longueur de chaque structure inotify_event vaut donc sizeof(structinotify_event)+len.

Le comportement, lorsque le tampon donné à read(2) est trop petit pour renvoyer l’information sur le prochain événement, dépend de la version du noyau : avant 2.6.21, read(2) renvoie 0 ; depuis le noyau 2.6.21, read(2) échoue avec l’erreur EINVAL. Indiquer un tampon de taille

sizeof(struct inotify_event) + NAME_MAX + 1

est suffisant pour lire au moins un événement.

Événements inotify
L’argument mask passé à inotify_add_watch(2) et le champ mask de la structure inotify_event renvoyés lors de la lecture avec read(2) d’un descripteur de fichier inotify sont tous deux des masques binaires identifiant les événements inotify. Les bits suivants peuvent être définis dans l’argument mask lors de l’appel à inotify_add_watch(2) et peuvent être renvoyés dans le champ mask renvoyé par read(2).

IN_ACCESS (+)

Accès au fichier (par exemple read(2), execve(2)).

IN_ATTRIB (*)

Modification des métadonnées, par exemple, les permissions (par exemple chmod(2)), les horodatages (par exemple utimensat(2)), les attributs étendus (setxattr(2)), le compteur de liens (depuis Linux 2.6.25 ; par exemple pour la cible de link(2) et unlink(2)) et les UID ou GID (par exemple chown(2)).

IN_CLOSE_WRITE (+)

Fichier ouvert en écriture fermé.

IN_CLOSE_NOWRITE (*)

Fichier ou répertoire non ouverts en écriture fermés.

IN_CREATE (+)

Fichier ou répertoire créés dans le répertoire surveillé (par exemple open(2) O_CREAT, mkdir(2), link(2), symlink(2), bind(2) sur une socket de domaine UNIX).

IN_DELETE (+)

Fichier ou répertoire supprimés dans le répertoire surveillé.

IN_DELETE_SELF

Fichier ou répertoire surveillés supprimés (cet événement se produit également si un objet est déplacé vers un autre système de fichiers, puisque mv(1) copie effectivement le fichier vers l’autre système de fichiers puis le supprime du système de fichiers d’origine). De plus, un événement IN_IGNORED sera ensuite généré pour le descripteur de surveillance.

IN_MODIFY (+)

Fichier modifié (par exemple write(2), truncate(2)).

IN_MOVE_SELF

Fichier ou répertoire surveillés déplacés.

IN_MOVED_FROM (+)

Généré pour le répertoire contenant l’ancien nom quand un fichier est renommé.

IN_MOVED_TO (+)

Généré pour le répertoire contenant le nouveau nom quand un fichier est renommé.

IN_OPEN (*)

Fichier ou répertoire ouvert.

La surveillance par Inotify est basée sur les inodes : lorsqu’un fichier est surveillé (mais pas lors de la surveillance d’un répertoire contenant un fichier), un événement peut être créé sur tout lien vers le fichier (dans le même répertoire ou dans un répertoire différent).

Lors de la surveillance d’un répertoire :

*

les événements marqués précédemment par un astérisque (*) peuvent avoir lieu à la fois pour le répertoire et pour les objets à l’intérieur du répertoire ;

*

les événements marqués par un signe plus (+) n’ont lieu que pour les objets à l’intérieur du répertoire (et non pour le répertoire lui-même).

Note : lorsqu’un répertoire est surveillé, les événements ne sont pas créés pour les fichiers contenus dans le répertoire quand des événements sont exécutés avec un nom de chemin (par exemple, un lien) qui se trouve hors du répertoire surveillé.

Lorsque les événements sont créés pour les objets dans un répertoire surveillé, le champ name dans la structure inotify_event renvoyée identifie le nom du fichier dans ce répertoire.

La macro IN_ALL_EVENTS est définie comme un masque binaire de tous les événements décrits ci-dessus. Cette macro peut être utilisée comme l’argument mask lors de l’appel à inotify_add_watch(2).

Deux macros supplémentaires de convenance sont définies :

IN_MOVE

Équivalent à IN_MOVED_FROM | IN_MOVED_TO.

IN_CLOSE

Équivalent à IN_CLOSE_WRITE | IN_CLOSE_NOWRITE.

Les bits supplémentaires suivants peuvent être indiqués dans l’argument mask lors de l’appel à inotify_add_watch(2) :

IN_DONT_FOLLOW (depuis Linux 2.6.15)

Ne pas déréférencer pathname s’il s’agit d’un lien symbolique.

IN_EXCL_UNLINK (depuis Linux 2.6.36)

Par défaut, lors de la surveillance d’événements sur les entrées d’un répertoire, des événements sont créés pour ces entrées même après leur suppression du répertoire. De nombreux événements inintéressants pour certaines applications peuvent ainsi être créés (par exemple, lors de la surveillance de /tmp, où de nombreuses applications créent des fichiers temporaires donc les noms sont immédiatement supprimés). Indiquer IN_EXCL_UNLINK modifie le comportement par défaut, de telle sorte qu’aucun événement n’est créé pour ces entrées après leur suppression du répertoire surveillé.

IN_MASK_ADD

If a watch instance already exists for the filesystem object corresponding to pathname, add (OR) the events in mask to the watch mask (instead of replacing the mask); the error EINVAL results if IN_MASK_CREATE is also specified.

IN_ONESHOT

Surveiller l’objet de système de fichiers correspondant à pathname jusqu’au premier événement, puis le supprimer de la liste de surveillance.

IN_ONLYDIR (depuis Linux 2.6.15)

Watch pathname only if it is a directory; the error ENOTDIR results if pathname is not a directory. Using this flag provides an application with a race-free way of ensuring that the monitored object is a directory.

IN_MASK_CREATE (since Linux 4.18)

Watch pathname only if it does not already have a watch associated with it; the error EEXIST results if pathname is already being watched.

Using this flag provides an application with a way of ensuring that new watches do not modify existing ones. This is useful because multiple paths may refer to the same inode, and multiple calls to inotify_add_watch(2) without this flag may clobber existing watch masks.

Les bits suivants peuvent avoir été définis dans le champ mask renvoyé par read(2) :

IN_IGNORED

Le surveillant a été retiré explicitement (inotify_rm_watch(2)) ou automatiquement (le fichier a été effacé, ou le système de fichiers a été démonté). Consultez également BOGUES.

IN_ISDIR

Le sujet de cet événement est un répertoire.

IN_Q_OVERFLOW

Queue des événements surchargée (wd vaut alors -1).

IN_UNMOUNT

Le système de fichiers contenant l’objet surveillé a été démonté. De plus, un événement IN_IGNORED sera ensuite généré pour le descripteur de surveillance.

Exemples
Soit une application surveillant le répertoire rép et le fichier rép/monfichier pour tous les événements. Les exemples ci-dessous montrent quelques événements qui seront générés pour ces deux objets.

fd = open("rép/monfichier", O_RDWR);

Génère des événements IN_OPEN à la fois pour rép et rép/monfichier.

read(fd, buf, count);

Génère des événements IN_ACCESS à la fois pour rép et rép/monfichier.

write(fd, buf, count);

Génère des événements IN_MODIFY à la fois pour rép et rép/monfichier.

fchmod(fd, mode);

Génère des événements IN_ATTRIB à la fois pour rép et rép/monfichier.

close(fd);

Génère des événements IN_CLOSE_WRITE à la fois pour rép et rép/monfichier.

Soit une application surveillant les répertoires rép1 et rép2, et le fichier rép1/monfichier. Les exemples suivants montrent quelques événements qui pourraient être générés.

link("rép1/monfichier", "rép2/nouveau");

Génère un événement IN_ATTRIB pour monfichier et un événement IN_CREATE pour rép2.

rename("rép1/monfichier", "rép2/monfichier");

Génère un événement IN_MOVED_FROM pour dir1, un événement IN_MOVED_TO pour rép2 et un événement IN_MOVE_SELF pour monfichier. Les événements IN_MOVED_FROM et IN_MOVED_TO auront la même valeur cookie.

Soient rép1/xx et rép2/yy les (seuls) liens vers le même ficher, et une application surveillant rép1, rép2, rép1/xx et rép2/yy. L’exécution des appels suivants dans l’ordre donné ci-dessous générera les événements suivants :

unlink("rép2/yy");

Génère un événement IN_ATTRIB pour xx (à cause du changement de son compteur de liens) et un événement IN_DELETE pour rép2.

unlink("rép1/xx");

Génère des événements IN_ATTRIB, IN_DELETE_SELF et IN_IGNORED pour xx et un événement IN_DELETE pour rép1.

Soit une application surveillant le répertoire rép et le répertoire (vide) rép/sousrép. Les exemples suivants montrent quelques événements qui pourraient être générés.

mkdir("rép/nouveau", mode);

Génère un événement IN_CREATE | IN_ISDIR pour rép.

rmdir("rép/sousrép");

Génère des événements IN_DELETE_SELF et IN_IGNORED pour sousrép et un événement IN_DELETE | IN_ISDIR pour rép.

Interfaces /proc
Les interfaces suivantes peuvent être utilisées pour limiter la quantité de mémoire du noyau utilisée par inotify :
/proc/sys/fs/inotify/max_queued_events

La valeur dans ce fichier est utilisée lorsqu’une application appelle inotify_init(2) pour définir la limite maximale du nombre des événements qui peuvent entrer dans la file d’attente de l’instance inotify correspondante. Les événements au-delà de cette limite sont annulés, mais un événement IN_Q_OVERFLOW est systématiquement généré.

/proc/sys/fs/inotify/max_user_instances

Cela indique la limite maximale du nombre d’instances inotify qui peuvent être créées par identifiant utilisateur réel.

/proc/sys/fs/inotify/max_user_watches

Cela indique la limite maximale du nombre de « watch » qui peuvent être créés par identifiant utilisateur réel.

VERSIONS

Inotify a été inclus dans le noyau Linux 2.6.13. Les interfaces bibliothèque nécessaires ont été ajoutées dans la version 2.4 de glibc (IN_DONT_FOLLOW, IN_MASK_ADD et IN_ONLYDIR ont été ajoutées dans la version 2.5 de glibc).

CONFORMITÉ

L’interface de programmation inotify est spécifique à Linux.

NOTES

Les descripteurs de fichier inotify peuvent être surveillés en utilisant select(2), poll(2) et epoll(7). Lorsqu’un événement est disponible, le descripteur de fichier indique qu’il est accessible en lecture.

Depuis Linux 2.6.25, il est possible d’être notifié par des signaux pour des entrées-sorties des descripteurs de fichier inotify ; consultez la discussion de F_SETFL (pour la configuration de l’attribut O_ASYNC), F_SETOWN, et F_SETSIG dans fcntl(2). La structure siginfo_t (décrite dans sigaction(2)) qui est passée au gestionnaire de signal a les champs suivants définis : si_fd est défini avec le numéro de descripteur de fichiers inotify ; si_signo est défini avec le numéro du signal ; si_code est défini avec POLL_IN ; et si_band est défini avec POLLIN.

Si deux événements inotify de sortie successifs produits sur le descripteur de fichier inotify sont identiques (wd, mask, cookie, et name identiques), alors ils sont fusionnés en un seul événement si l’événement le plus ancien n’a toujours pas été lu (mais consultez la section BOGUES). Cela permet de réduire la quantité de mémoire en espace noyau nécessaire à la file d’événements, mais signifie également qu’une application ne peut utiliser inotify pour compter de manière fiable les événements liés à un fichier.

Les événements renvoyés lors de la lecture d’un descripteur de fichier inotify forment une file ordonnée. Ainsi, par exemple, il est garanti que lors du renommage d’un répertoire, les événements seront produits dans l’ordre convenable sur le descripteur de fichier inotify.

L’ensemble des descripteurs surveillés grâce à un descripteur de fichier inotify peut être vu dans l’entrée du descripteur de fichier inotify dans le répertoire /proc/[pid]/fdinfo du processus. Consultez proc(5) pour de plus amples détails. FIONREAD ioctl(2) renvoie le nombre d’octets disponibles à la lecture dans un descripteur de fichier inotify.

Limites et réserves
L’interface inotify ne fournit aucun renseignement sur l’utilisateur ou le processus qui a déclenché l’événement inotify. En particulier, un processus en train de surveiller des événements à l’aide d’inotify ne dispose d’aucun moyen facile pour distinguer les événements qu’il déclenche lui-même de ceux qui ont été déclenchés par d’autres processus.

Inotify ne signale que les événements déclenchés par un programme en espace utilisateur à l’aide de l’interface de programmation de système de fichiers. Par conséquent, elle n’intercepte pas les événements qui surviennent sur les systèmes de fichiers en réseau (les applications doivent avoir recours à la scrutation (polling) pour intercepter ce type d’événements). De plus, divers pseudo-systèmes de fichiers comme /proc, /sys et /dev/pts ne sont pas surveillables avec inotify.

L’interface inotify ne signale pas les accès ni les modifications de fichier qui pourraient survenir à cause de mmap(2), msync(2) ou munmap(2).

L’interface inotify identifie les fichiers affectés par leur nom. Cependant, au moment où l’application traite un événement inotify, ce nom de fichier peut avoir déjà été supprimé ou renommé.

L’interface inotify identifie les événements à l’aide de descripteurs de surveillance. L’application est responsable de mettre en cache une correspondance (si nécessaire) entre les descripteurs de fichier et les chemins. Soyez vigilants aux renommages de répertoire qui pourraient affecter plusieurs chemins en cache.

La surveillance inotify des répertoires n’est pas récursive : pour surveiller les sous-répertoires, des éléments de surveillance supplémentaires doivent être créés. Cela peut être assez long pour les répertoires contenant une grande arborescence.

Si la surveillance concerne une arborescence dans son intégralité, et si un nouveau sous-répertoire est créé dans ce répertoire ou si un répertoire existant est renommé dans cette arborescence, soyez conscient qu’au moment où vous créez un élément de surveillance pour le nouveau sous-répertoire, de nouveaux fichiers (et sous-répertoires) peuvent déjà exister dans le sous-répertoire. Ainsi, vous devriez analyser le contenu du sous-répertoire immédiatement après avoir ajouté l’élément de surveillance (et, si nécessaire, ajouter des éléments de surveillance pour tous les sous-répertoires qu’il contient).

Remarquez que la file d’événements peut déborder. Dans ce cas, des événements sont perdus. Les applications robustes devraient gérer correctement la possibilité de perdre des événements. Par exemple, la reconstruction de tout ou partie du cache de l’application pourrait être nécessaire (une approche simple, mais éventuellement coûteuse, est de fermer le descripteur de fichier inotify, vider le cache, créer un nouveau descripteur de fichier inotify et recréer les éléments de surveillance et les entrées du cache pour les objets à surveiller).

Si un système de fichiers est monté par dessus un répertoire surveillé, aucun événement n’est généré, pas plus que pour les objets se trouvant directement sous le nouveau point de montage. Si le système de fichiers est par la suite démonté, les événements seront créés pour le répertoire et les objets qu’il contient.

Traitement des événements rename()
Comme noté précédemment, la paire d’événements IN_MOVED_FROM et IN_MOVED_TO générée par rename(2) peut être assemblée à l’aide de la valeur de cookie partagé. Cependant, la tâche d’assemblage peut poser quelques problèmes.

Ces deux événements sont normalement consécutifs dans le flux d’événements disponibles lors de la lecture depuis le descripteur de fichiers inotify. Cependant, ce n’est pas garanti. Si plusieurs processus déclenchent des événements pour des objets surveillés, alors (rarement) un nombre arbitraire d’autres événements pourrait apparaître entre les événements IN_MOVED_FROM et IN_MOVED_TO. De plus, il n’est pas garanti que la paire d’événements soit insérée de façon atomique dans la file : il pourrait y avoir un bref intervalle au cours duquel IN_MOVED_FROM est apparu, mais pas IN_MOVED_TO.

L’assemblage de la paire d’événements IN_MOVED_FROM et IN_MOVED_TO générés par rename(2) pose donc intrinsèquement un risque de situation de compétition (n’oubliez pas que si un objet est renommé en dehors d’un répertoire surveillé, un événement IN_MOVED_TO pourrait ne même pas être envoyé). Des approches heuristiques (par exemple supposer que les événements sont toujours consécutifs) permettent d’assurer un assemblage dans la plupart des cas, mais manqueront forcément certains cas, forçant l’application à percevoir les événements IN_MOVED_FROM et IN_MOVED_TO comme indépendants. Si les descripteurs de surveillance sont détruits et recréés par conséquent, alors ces descripteurs de surveillance seront incohérents avec les descripteurs de surveillance dans tous les événements en attente (la recréation du descripteur de fichier inotify et la reconstruction du cache pourrait être utile dans ce cas).

Les applications devraient aussi considérer la possibilité que l’événement IN_MOVED_FROM soit le dernier événement ayant pu entrer dans le tampon renvoyé pour l’appel actuel de read(2) et l’événement IN_MOVED_TO accompagnant pourrait n’être récupéré que lors de l’appel read(2) suivant, ce qui devrait être fait avec un (léger) délai pour permettre que l’insertion de la paire d’événements IN_MOVED_FROM-IN_MOVED_TO ne soit pas atomique et aussi qu’il n’y ait pas d’événement IN_MOVED_TO.

BOGUES

Avant Linux 3.19, fallocate(2) ne créait pas d’événements inotify. Depuis Linux 3.19, les appels à fallocate(2) créent des événements IN_MODIFY.

Dans les noyaux antérieurs à 2.6.16, l’attribut IN_ONESHOT de mask ne fonctionne pas.

Tel que conçu et implémenté à l’origine, l’attribut IN_ONESHOT ne forçait pas à générer un appel IN_IGNORED lorsque la surveillance était supprimée après un événement. Cependant, en conséquence involontaire d’autres modifications, depuis Linux 2.6.36, un événement IN_IGNORED est généré dans ce cas.

Avant le noyau 2.6.25, le code du noyau qui était sensé regrouper deux événements successifs (c’est-à-dire que les deux événements les plus récents pouvaient être fusionnés si le plus ancien des deux n’avait toujours pas été lu) vérifiait à la place si l’événement le plus récent pouvait être fusionné à l’événement non lu le plus ancien.

Quand un descripteur de surveillance est supprimé en appelant inotify_rm_watch(2) (ou parce qu’un fichier de surveillance est supprimé ou que le système de fichiers qui le contient est démonté), tous les événements non lus en attente pour ce descripteur de fichier restent disponibles en lecture. Comme les descripteurs de surveillance sont ensuite alloués avec inotify_add_watch(2), le noyau boucle sur l’intervalle des descripteurs de surveillance possibles (O à INT_MAX) de façon incrémentielle. Lors de l’allocation d’un descripteur de surveillance libre, aucune vérification n’est effectuée pour voir si ce numéro de descripteur de surveillance a des événements non lus en attente dans la file inotify. Ainsi, un descripteur de surveillance pourrait être réalloué même quand des événements non lus en attente existent pour une incarnation précédente de ce numéro de descripteur de surveillance, avec comme résultat que l’application pourrait alors lire ces événements et les interpréter comme appartenant au fichier associé au descripteur de surveillance nouvellement recyclé. En pratique, la probabilité d’être victime de ce bogue devrait être extrêmement basse, puisqu’il nécessite qu’une application boucle sur INT_MAX descripteurs de surveillance, relâche un descripteur de surveillance tout en laissant des événements non lus pour ce descripteur de fichier dans la file et ensuite recycle ce descripteur de surveillance. Pour cette raison, et parce qu’il n’y a eu aucun rapports de bogue à propos de réelles applications, dans Linux 3.15, aucune modification de noyau n’a encore été faite pour éliminer ce bogue éventuel.

EXEMPLES

Le programme suivant montre l’utilisation de l’interface de programmation inotify. Il marque les répertoires passés en arguments de ligne de commande et attend les événements de type IN_OPEN, IN_CLOSE_NOWRITE et IN_CLOSE_WRITE.

La sortie suivante a été enregistrée lors de la modification du fichier /home/utilisateur/temp/toto et de l’affichage du contenu du répertoire /tmp. Avant d’ouvrir le fichier et le répertoire, un événement IN_OPEN est survenu. Après la fermeture du fichier, un événement IN_CLOSE_WRITE est survenu. Après la fermeture du répertoire, un événement IN_CLOSE_NOWRITE est survenu. L’exécution du programme s’est terminée quand l’utilisateur a appuyé sur la touche Entrée.

Exemple de sortie

$ ./a.out /tmp /home/utilisateur/temp

Appuyer sur la touche Entrée pour quitter.
En écoute d’événements.
IN_OPEN : /home/utilisateur/temp/toto [fichier]
IN_CLOSE_WRITE : /home/utilisateur/temp/toto [fichier]
IN_OPEN : /tmp/ [répertoire]
IN_CLOSE_NOWRITE : /tmp/ [répertoire]


Arrêt de l’écoute d’événements.

Source du programme

#include <errno.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>

/* Lire tous les événements inotify disponibles à partir du
     descripteur de fichier « fd ».
   wd est le tableau des descripteurs de surveillance pour
     les répertoires en argv.
   argc est la taille de wd et argv.
   argv est la liste des répertoires surveillés.
   L’entrée 0 de wd et argv n’est pas utilisée. */

static void
handle_events(int fd, int *wd, int argc, char* argv[])
{
    /* Certains systèmes ne peuvent pas lire de variables entières
       si elles ne sont pas alignées correctement. Sur d’autres
       systèmes, un alignement incorrect pourrait diminuer les
       performances. Par conséquent, le tampon utilisé pour lire
       le descripteur de fichier inotify devrait avoir le même
       alignement que struct inotify_event. */

    char buf[4096]
        __attribute__ ((aligned(__alignof__(struct inotify_event))));
    const struct inotify_event *event;
    int i;
    ssize_t len;
    char *ptr;

    /* Boucler tant que les événements peuvent être lus à partir du
       descripteur de fichier inotify */

    for (;;) {

        /* Lire certains événements. */

        len = read(fd, buf, sizeof buf);
        if (len == -1 && errno != EAGAIN) {
            perror("read");
            exit(EXIT_FAILURE);
        }

        /* If the nonblocking read() found no events to read, then
           it returns -1 with errno set to EAGAIN. In that case,
           we exit the loop. */

        if (len <= 0)
            break;

        /* Boucler sur tous les événements du tampon */

        for (ptr = buf; ptr < buf + len;
                ptr += sizeof(struct inotify_event) + event->len) {

            event = (const struct inotify_event *) ptr;

            /* Afficher le type d’événement */

            if (event->mask & IN_OPEN)
                printf("IN_OPEN : ");
            if (event->mask & IN_CLOSE_NOWRITE)
                printf("IN_CLOSE_NOWRITE : ");
            if (event->mask & IN_CLOSE_WRITE)
                printf("IN_CLOSE_WRITE : ");

            /* Afficher le nom du répertoire surveillé */

            for (i = 1; i < argc; ++i) {
                if (wd[i] == event->wd) {
                    printf("%s/", argv[i]);
                    break;
                }
            }

            /* Afficher le nom du fichier */

            if (event->len)
                printf("%s", event->name);

            /* Afficher le type d’objet de système de fichiers */

            if (event->mask & IN_ISDIR)
                printf(" [répertoire]\n");
            else
                printf(" [fichier]\n");
        }
    }
}

int
main(int argc, char* argv[])
{
    char buf;
    int fd, i, poll_num;
    int *wd;
    nfds_t nfds;
    struct pollfd fds[2];

    if (argc < 2) {
        printf("Utilisation : %s CHEMIN [CHEMIN ...]\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    printf("Appuyer sur la touche Entrée pour quitter.\n");

    /* Créer le descripteur de fichier pour accéder à l’interface de
       programmation inotify */

    fd = inotify_init1(IN_NONBLOCK);
    if (fd == -1) {
        perror("inotify_init1");
        exit(EXIT_FAILURE);
    }

    /* Allouer la mémoire pour les descripteurs de surveillance */

    wd = calloc(argc, sizeof(int));
    if (wd == NULL) {
        perror("calloc");
        exit(EXIT_FAILURE);
    }

    /* Marquer les répertoires pour les événements :
       - un fichier a été ouvert ;
       - un fichier a été fermé

    for (i = 1; i < argc; i++) {
        wd[i] = inotify_add_watch(fd, argv[i],
                                  IN_OPEN | IN_CLOSE);
        if (wd[i] == -1) {
            fprintf(stderr, "Cannot watch ’%s’: %s\n",
                    argv[i], strerror(errno));
            exit(EXIT_FAILURE);
        }
    }

    /* Préparer pour la scrutation (polling) */

    nfds = 2;

    /* Entrée de console */

    fds[0].fd = STDIN_FILENO;
    fds[0].events = POLLIN;

    /* Entrée d’Inotify */

    fds[1].fd = fd;
    fds[1].events = POLLIN;

    /* Attendre les événements ou une entrée du terminal */

    printf("En écoute d’événements.\n");
    while (1) {
        poll_num = poll(fds, nfds, -1);
        if (poll_num == -1) {
            if (errno == EINTR)
                continue;
            perror("poll");
            exit(EXIT_FAILURE);
        }

        if (poll_num > 0) {

            if (fds[0].revents & POLLIN) {

                /* Entrée de console disponible.
                   Vider l’entrée standard et quitter */

                while (read(STDIN_FILENO, &buf, 1) > 0 && buf != ’\n’)
                    continue;
                break;
            }

            if (fds[1].revents & POLLIN) {

                /* Des événements inotify sont disponibles */

                handle_events(fd, wd, argc, argv);
            }
        }
    }

    printf("Arrêt de l’écoute d’événements.\n");

    /* Fermer le descripteur de fichier inotify */

    close(fd);

    free(wd);
    exit(EXIT_SUCCESS);
}

VOIR AUSSI

inotifywait(1), inotifywatch(1), inotify_add_watch(2), inotify_init(2), inotify_init1(2), inotify_rm_watch(2), read(2), stat(2), fanotify(7)

Documentation/filesystems/inotify.txt dans les sources du noyau Linux

COLOPHON

Cette page fait partie de la publication 5.07 du projet man-pages Linux. Une description du projet et des instructions pour signaler des anomalies et la dernière version de cette page, peuvent être trouvées à l’adresse https://www.kernel.org/doc/man-pages/.

TRADUCTION

La traduction française de cette page de manuel a été créée par Christophe Blaess <https://www.blaess.fr/christophe/>;, Stéphan Rafin <stephan.rafin [AT] laposte.net>, Thierry Vignaud <tvignaud [AT] mandriva.com>, François Micaux, Alain Portal <aportal [AT] univ-montp2.fr>, Jean-Philippe Guérard <fevrier [AT] tigreraye.org>, Jean-Luc Coulon (f5ibh) <jean-luc.coulon [AT] wanadoo.fr>, Julien Cristau <jcristau [AT] debian.org>, Thomas Huriaux <thomas.huriaux [AT] gmail.com>, Nicolas François <nicolas.francois [AT] centraliens.net>, Florentin Duneau <fduneau [AT] gmail.com>, Simon Paillard <simon.paillard [AT] resel.fr>, Denis Barbier <barbier [AT] debian.org> et David Prévot <david [AT] tilapin.org>

Cette traduction est une documentation libre ; veuillez vous reporter à la GNU General Public License version 3 concernant les conditions de copie et de distribution. Il n’y a aucune RESPONSABILITÉ LÉGALE.

Si vous découvrez un bogue dans la traduction de cette page de manuel, veuillez envoyer un message à <debian-l10n-french [AT] lists.org>.