Objectif du cours :
Comprendre l'API inotify, savoir quelles fonctions retenir, quelles structures utiliser, quels événements surveiller, et comment écrire un petit programme capable de détecter les créations, suppressions, modifications et renommages de fichiers sous Linux.
1. Introduction
Sous Linux, un programme peut demander au noyau d'être prévenu quand quelque chose change dans un fichier ou dans un répertoire.
Par exemple, on peut vouloir détecter :
- la création d'un fichier ;
- la suppression d'un fichier ;
- la modification du contenu d'un fichier ;
- l'ouverture ou la fermeture d'un fichier ;
- le renommage d'un fichier ;
- le déplacement d'un fichier d'un répertoire vers un autre ;
- la suppression du répertoire surveillé lui-même ;
- la saturation de la file d'événements.
Le principe est simple :
- on crée une instance inotify ;
- on ajoute un ou plusieurs éléments à surveiller ;
- le noyau place les événements dans une file ;
- le programme lit cette file avec read() ;
- chaque événement est représenté par une structure struct inotify_event.
2. Comparaison rapide avec Windows
Si on connaît déjà Win32, le parallèle est très naturel.
Code: Select all
Linux Windows
------------------------------------------------------------
inotify_init() ouverture d'un handle de surveillance
inotify_add_watch() choix du dossier/fichier à surveiller
read() ReadDirectoryChangesW()
struct inotify_event FILE_NOTIFY_INFORMATION
IN_CREATE FILE_ACTION_ADDED
IN_DELETE FILE_ACTION_REMOVED
IN_MODIFY FILE_ACTION_MODIFIED
IN_MOVED_FROM FILE_ACTION_RENAMED_OLD_NAME
IN_MOVED_TO FILE_ACTION_RENAMED_NEW_NAME
- Windows peut surveiller récursivement un arbre de répertoires avec bWatchSubtree = TRUE ;
- inotify ne surveille pas récursivement automatiquement ;
- sous Linux, il faut ajouter manuellement un watch sur chaque sous-répertoire si on veut une surveillance récursive.
Code: Select all
inotify = ReadDirectoryChangesW version Linux,
mais avec fd + read(), et sans récursivité automatique.
Pour utiliser inotify, le header principal est :
Code: Select all
#include <sys/inotify.h>
Code: Select all
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
- <sys/inotify.h> : contient les fonctions, constantes et structures de l'API inotify ;
- <unistd.h> : contient read(), close(), etc. ;
- <stdint.h> : contient les types comme uint32_t ;
- <stdio.h> : contient printf(), perror(), etc. ;
- <stdlib.h> : contient exit(), EXIT_SUCCESS, EXIT_FAILURE ;
- <string.h> : utile pour manipuler les chaînes ou buffers ;
- <errno.h> : utile pour comprendre les erreurs système.
Les fonctions importantes sont :
Code: Select all
inotify_init()
inotify_init1()
inotify_add_watch()
inotify_rm_watch()
read()
close()
Code: Select all
1. fd = inotify_init();
2. wd = inotify_add_watch(fd, "chemin", mask);
3. read(fd, buffer, sizeof(buffer));
4. interpréter les struct inotify_event présentes dans le buffer ;
5. inotify_rm_watch(fd, wd);
6. close(fd);
L'instance inotify est représentée par un fd.
Le fichier ou dossier surveillé est représenté par un watch descriptor, souvent appelé wd.
5. Créer une instance inotify
La fonction de base est :
Code: Select all
int inotify_init(void);
Exemple :
Code: Select all
int fd = inotify_init();
if(fd == -1){
perror("inotify_init");
exit(EXIT_FAILURE);
}
- si la fonction réussit, elle retourne un fd ;
- si elle échoue, elle retourne -1 ;
- ce fd sera utilisé ensuite avec read() ;
- quand on ferme ce fd avec close(), tous les watches associés sont supprimés.
Il existe une variante plus moderne :
Code: Select all
int inotify_init1(int flags);
Les deux flags les plus utiles sont :
Code: Select all
IN_NONBLOCK
IN_CLOEXEC
Code: Select all
int fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if(fd == -1){
perror("inotify_init1");
exit(EXIT_FAILURE);
}
IN_CLOEXEC signifie que le fd sera automatiquement fermé lors d'un exec().
C'est utile dans les programmes propres, surtout quand un processus lance d'autres programmes.
7. Ajouter un watch
Pour demander au noyau de surveiller un fichier ou un répertoire, on utilise :
Code: Select all
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
- fd : le descripteur retourné par inotify_init() ou inotify_init1() ;
- pathname : le chemin du fichier ou dossier à surveiller ;
- mask : les événements que l'on veut recevoir.
Exemple :
Code: Select all
int wd = inotify_add_watch(fd, "test", IN_CREATE | IN_DELETE | IN_MODIFY);
if(wd == -1){
perror("inotify_add_watch");
close(fd);
exit(EXIT_FAILURE);
}
Si on surveille plusieurs dossiers avec la même instance inotify, chaque dossier aura son propre wd.
8. Supprimer un watch
Pour arrêter de surveiller un élément, on utilise :
Code: Select all
int inotify_rm_watch(int fd, int wd);
Code: Select all
if(inotify_rm_watch(fd, wd) == -1){
perror("inotify_rm_watch");
}
9. Lire les événements
Les événements sont lus avec read().
Code: Select all
ssize_t read(int fd, void *buf, size_t count);
Code: Select all
char buffer[4096];
ssize_t len = read(fd, buffer, sizeof(buffer));
if(len == -1){
perror("read");
}
Le point très important :
Code: Select all
Un seul read() peut retourner plusieurs événements.
10. La structure centrale : struct inotify_event
La structure principale est :
Code: Select all
struct inotify_event {
int wd;
uint32_t mask;
uint32_t cookie;
uint32_t len;
char name[];
};
10.1 Champ wd
Code: Select all
int wd;
Exemple :
Code: Select all
if(event->wd == wd_dir1){
printf("evenement dans dir1\n");
}
if(event->wd == wd_dir2){
printf("evenement dans dir2\n");
}
10.2 Champ mask
Code: Select all
uint32_t mask;
Exemple :
Code: Select all
if(event->mask & IN_CREATE){
printf("creation detectee\n");
}
if(event->mask & IN_DELETE){
printf("suppression detectee\n");
}
Il faut donc tester avec l'opérateur &.
10.3 Champ cookie
Code: Select all
uint32_t cookie;
Quand un fichier est renommé ou déplacé, le noyau peut générer deux événements :
Code: Select all
IN_MOVED_FROM
IN_MOVED_TO
Exemple mental :
Code: Select all
ancien nom : a.txt
nouveau nom : b.txt
Code: Select all
IN_MOVED_FROM name = a.txt cookie = 1234
IN_MOVED_TO name = b.txt cookie = 1234
10.4 Champ len
Code: Select all
uint32_t len;
Le champ name n'a pas toujours la même taille, donc il faut utiliser len pour parcourir correctement le buffer.
10.5 Champ name
Code: Select all
char name[];
Important : il est surtout rempli quand on surveille un répertoire.
Exemple :
Code: Select all
inotify_add_watch(fd, "dir", IN_CREATE);
Code: Select all
dir/test.txt
Code: Select all
event->name = "test.txt";
Il contient généralement le nom relatif dans le dossier surveillé.
11. Parcourir correctement le buffer
Comme un seul read() peut renvoyer plusieurs événements, il faut parcourir le buffer avec un pointeur.
Le pattern important est :
Code: Select all
char *p = buffer;
while(p < buffer + len){
struct inotify_event *event = (struct inotify_event *)p;
/* traiter event ici */
p += sizeof(struct inotify_event) + event->len;
}
Code: Select all
p += sizeof(struct inotify_event) + event->len;
Parce que struct inotify_event a une partie fixe :
Code: Select all
wd
mask
cookie
len
Code: Select all
name[]
12. Les événements principaux
12.1 IN_ACCESS
Code: Select all
IN_ACCESS
Exemple :
Code: Select all
cat fichier.txt
12.2 IN_OPEN
Code: Select all
IN_OPEN
Exemple :
Code: Select all
open("fichier.txt", O_RDONLY);
12.3 IN_MODIFY
Code: Select all
IN_MODIFY
Exemple :
Code: Select all
echo "test" >> fichier.txt
12.4 IN_ATTRIB
Code: Select all
IN_ATTRIB
Cela peut inclure :
- les permissions ;
- le propriétaire ;
- le groupe ;
- certains timestamps ;
- certains attributs liés au fichier.
Code: Select all
chmod 777 fichier.txt
chown user fichier.txt
Code: Select all
IN_CLOSE_WRITE
C'est un événement très utile.
Exemple :
Code: Select all
echo "abc" > fichier.txt
C'est souvent plus propre que de réagir immédiatement à IN_MODIFY, car IN_MODIFY peut arriver pendant que le fichier est encore en cours d'écriture.
12.6 IN_CLOSE_NOWRITE
Code: Select all
IN_CLOSE_NOWRITE
Exemple : un programme ouvre un fichier en lecture puis le ferme.
12.7 IN_CLOSE
Code: Select all
IN_CLOSE
Code: Select all
IN_CLOSE_WRITE | IN_CLOSE_NOWRITE
Code: Select all
IN_CREATE
Exemple :
Code: Select all
touch dir/a.txt
mkdir dir/sub
12.9 IN_DELETE
Code: Select all
IN_DELETE
Exemple :
Code: Select all
rm dir/a.txt
12.10 IN_DELETE_SELF
Code: Select all
IN_DELETE_SELF
Différence importante :
Code: Select all
IN_DELETE = un enfant du dossier surveillé est supprimé
IN_DELETE_SELF = le fichier/dossier surveillé lui-même est supprimé
Code: Select all
inotify_add_watch(fd, "dir", IN_DELETE_SELF);
rm -r dir
12.11 IN_MOVE_SELF
Code: Select all
IN_MOVE_SELF
Exemple :
Code: Select all
inotify_add_watch(fd, "dir", IN_MOVE_SELF);
mv dir autre_nom
Code: Select all
IN_MOVED_FROM
Exemple :
Code: Select all
mv dir1/a.txt dir2/a.txt
12.13 IN_MOVED_TO
Code: Select all
IN_MOVED_TO
Exemple :
Code: Select all
mv dir1/a.txt dir2/a.txt
12.14 IN_MOVE
Code: Select all
IN_MOVE
Code: Select all
IN_MOVED_FROM | IN_MOVED_TO
Code: Select all
IN_IGNORED
Cela peut arriver :
- après un appel à inotify_rm_watch() ;
- si le fichier ou dossier surveillé disparaît ;
- si le noyau retire automatiquement le watch.
Code: Select all
IN_Q_OVERFLOW
Cela signifie que le programme n'a pas lu les événements assez vite et que certains événements ont été perdus.
C'est très important pour un outil sérieux.
Si on reçoit IN_Q_OVERFLOW, il ne faut pas faire comme si tout était fiable. Il faut considérer que l'état réel du système de fichiers peut être différent de ce que l'on croit.
12.17 IN_UNMOUNT
Code: Select all
IN_UNMOUNT
13. Les masks pratiques
Pour surveiller presque tous les événements :
Code: Select all
IN_ALL_EVENTS
Code: Select all
IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO
Code: Select all
IN_CLOSE_WRITE
Code: Select all
IN_MOVED_FROM | IN_MOVED_TO
Code: Select all
IN_DELETE | IN_DELETE_SELF
En plus des événements, il existe des flags qui modifient le comportement du watch.
14.1 IN_ONLYDIR
Code: Select all
IN_ONLYDIR
Exemple :
Code: Select all
int wd = inotify_add_watch(fd, "dir", IN_ONLYDIR | IN_CREATE | IN_DELETE);
14.2 IN_DONT_FOLLOW
Code: Select all
IN_DONT_FOLLOW
C'est utile si on veut éviter qu'un symlink pointe vers une autre cible que celle attendue.
14.3 IN_ONESHOT
Code: Select all
IN_ONESHOT
Exemple :
Code: Select all
int wd = inotify_add_watch(fd, "fichier.txt", IN_ONESHOT | IN_MODIFY);
14.4 IN_MASK_ADD
Code: Select all
IN_MASK_ADD
Sans ce flag, ajouter un watch sur un chemin déjà surveillé peut remplacer le mask précédent.
15. Surveillance d'un fichier vs surveillance d'un dossier
15.1 Surveiller un fichier
Exemple :
Code: Select all
int wd = inotify_add_watch(fd, "a.txt", IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF);
On peut recevoir des événements si :
- le fichier est modifié ;
- le fichier est supprimé ;
- le fichier est déplacé ;
- ses attributs changent.
Exemple :
Code: Select all
int wd = inotify_add_watch(fd, "dir", IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO);
Si on crée :
Code: Select all
dir/test.txt
Code: Select all
test.txt
C'est un point fondamental.
Si on fait :
Code: Select all
inotify_add_watch(fd, "/home/user/projet", IN_CREATE | IN_DELETE);
Code: Select all
/home/user/projet
Code: Select all
/home/user/projet/src
/home/user/projet/src/core
/home/user/projet/include
- parcourir récursivement l'arborescence ;
- ajouter un watch sur chaque sous-dossier ;
- quand un nouveau sous-dossier est créé, ajouter aussi un watch dessus ;
- quand un sous-dossier est supprimé, retirer ou oublier son watch.
17. Exemple minimal : surveiller un dossier
Ce programme surveille un dossier appelé test.
Code: Select all
#include <sys/inotify.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int fd = inotify_init();
if(fd == -1){
perror("inotify_init");
exit(EXIT_FAILURE);
}
int wd = inotify_add_watch(fd, "test", IN_CREATE | IN_DELETE | IN_MODIFY | IN_MOVED_FROM | IN_MOVED_TO);
if(wd == -1){
perror("inotify_add_watch");
close(fd);
exit(EXIT_FAILURE);
}
char buffer[4096];
while(1){
ssize_t len = read(fd, buffer, sizeof(buffer));
if(len == -1){
perror("read");
break;
}
char *p = buffer;
while(p < buffer + len){
struct inotify_event *event = (struct inotify_event *)p;
if(event->mask & IN_CREATE){
printf("creation : %s\n", event->len ? event->name : "");
}
if(event->mask & IN_DELETE){
printf("suppression : %s\n", event->len ? event->name : "");
}
if(event->mask & IN_MODIFY){
printf("modification : %s\n", event->len ? event->name : "");
}
if(event->mask & IN_MOVED_FROM){
printf("deplace depuis le dossier : %s\n", event->len ? event->name : "");
}
if(event->mask & IN_MOVED_TO){
printf("deplace vers le dossier : %s\n", event->len ? event->name : "");
}
p += sizeof(struct inotify_event) + event->len;
}
}
inotify_rm_watch(fd, wd);
close(fd);
return 0;
}
Code: Select all
gcc -Wall -Wextra -o watcher watcher.c
Code: Select all
mkdir test
./watcher
Code: Select all
touch test/a.txt
echo "hello" > test/a.txt
mv test/a.txt test/b.txt
rm test/b.txt
Il est pratique d'écrire une fonction qui affiche les événements présents dans le mask.
Code: Select all
#include <sys/inotify.h>
#include <stdio.h>
void print_mask(uint32_t mask)
{
if(mask & IN_ACCESS){
printf("IN_ACCESS ");
}
if(mask & IN_ATTRIB){
printf("IN_ATTRIB ");
}
if(mask & IN_CLOSE_WRITE){
printf("IN_CLOSE_WRITE ");
}
if(mask & IN_CLOSE_NOWRITE){
printf("IN_CLOSE_NOWRITE ");
}
if(mask & IN_CREATE){
printf("IN_CREATE ");
}
if(mask & IN_DELETE){
printf("IN_DELETE ");
}
if(mask & IN_DELETE_SELF){
printf("IN_DELETE_SELF ");
}
if(mask & IN_MODIFY){
printf("IN_MODIFY ");
}
if(mask & IN_MOVE_SELF){
printf("IN_MOVE_SELF ");
}
if(mask & IN_MOVED_FROM){
printf("IN_MOVED_FROM ");
}
if(mask & IN_MOVED_TO){
printf("IN_MOVED_TO ");
}
if(mask & IN_OPEN){
printf("IN_OPEN ");
}
if(mask & IN_IGNORED){
printf("IN_IGNORED ");
}
if(mask & IN_Q_OVERFLOW){
printf("IN_Q_OVERFLOW ");
}
if(mask & IN_UNMOUNT){
printf("IN_UNMOUNT ");
}
if(mask & IN_ISDIR){
printf("IN_ISDIR ");
}
}
19. Exemple plus propre avec une fonction de traitement
Code: Select all
#include <sys/inotify.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
void display_event(struct inotify_event *event)
{
printf("wd=%d ", event->wd);
if(event->len > 0){
printf("name=%s ", event->name);
}
printf("mask=");
if(event->mask & IN_CREATE){
printf("IN_CREATE ");
}
if(event->mask & IN_DELETE){
printf("IN_DELETE ");
}
if(event->mask & IN_MODIFY){
printf("IN_MODIFY ");
}
if(event->mask & IN_MOVED_FROM){
printf("IN_MOVED_FROM ");
}
if(event->mask & IN_MOVED_TO){
printf("IN_MOVED_TO ");
}
if(event->mask & IN_ISDIR){
printf("IN_ISDIR ");
}
if(event->cookie != 0){
printf("cookie=%u ", event->cookie);
}
printf("\n");
}
int main(int argc, char *argv[])
{
if(argc < 2){
fprintf(stderr, "usage: %s <directory>\n", argv[0]);
exit(EXIT_FAILURE);
}
int fd = inotify_init();
if(fd == -1){
perror("inotify_init");
exit(EXIT_FAILURE);
}
int wd = inotify_add_watch(fd, argv[1], IN_ALL_EVENTS);
if(wd == -1){
perror("inotify_add_watch");
close(fd);
exit(EXIT_FAILURE);
}
char buffer[4096];
while(1){
ssize_t len = read(fd, buffer, sizeof(buffer));
if(len == -1){
perror("read");
break;
}
char *p = buffer;
while(p < buffer + len){
struct inotify_event *event = (struct inotify_event *)p;
display_event(event);
p += sizeof(struct inotify_event) + event->len;
}
}
inotify_rm_watch(fd, wd);
close(fd);
return 0;
}
Code: Select all
gcc -Wall -Wextra -o watch_all watch_all.c
Code: Select all
mkdir demo
./watch_all demo
Code: Select all
touch demo/a.txt
echo "hello" > demo/a.txt
cat demo/a.txt
mv demo/a.txt demo/b.txt
rm demo/b.txt
Un renommage peut produire deux événements.
Exemple :
Code: Select all
mv a.txt b.txt
Code: Select all
IN_MOVED_FROM name=a.txt cookie=12345
IN_MOVED_TO name=b.txt cookie=12345
- un fichier nommé a.txt a quitté son ancien nom ;
- un fichier nommé b.txt est apparu comme destination ;
- le cookie identique permet de comprendre que c'est la même opération.
21. Les limites système dans /proc
inotify utilise de la mémoire noyau.
Il existe donc des limites.
Elles sont visibles dans :
Code: Select all
/proc/sys/fs/inotify/
Code: Select all
max_queued_events
max_user_instances
max_user_watches
Code: Select all
/proc/sys/fs/inotify/max_queued_events
Si le programme ne lit pas assez vite, la file peut déborder et le noyau peut générer :
Code: Select all
IN_Q_OVERFLOW
Code: Select all
/proc/sys/fs/inotify/max_user_instances
Chaque appel à inotify_init() crée une instance.
21.3 max_user_watches
Code: Select all
/proc/sys/fs/inotify/max_user_watches
C'est une limite importante pour les outils qui surveillent beaucoup de dossiers.
Exemple : un IDE, un outil de synchronisation, un watcher de projet, etc.
22. Lire les valeurs des limites
On peut lire les limites avec cat :
Code: Select all
cat /proc/sys/fs/inotify/max_queued_events
cat /proc/sys/fs/inotify/max_user_instances
cat /proc/sys/fs/inotify/max_user_watches
Exemple :
Code: Select all
sudo sysctl fs.inotify.max_user_watches=524288
23. Erreurs classiques
23.1 Oublier que read() peut retourner plusieurs événements
Mauvais raisonnement :
Code: Select all
read() = un seul événement
Code: Select all
read() = un buffer contenant un ou plusieurs événements
Code: Select all
p += sizeof(struct inotify_event) + event->len;
Mauvais raisonnement :
Code: Select all
Je surveille /home/user/projet, donc je surveille aussi tous les sous-dossiers.
Code: Select all
Je surveille seulement le dossier demandé.
Pour surveiller les sous-dossiers, je dois ajouter un watch sur chacun.
Code: Select all
IN_DELETE = un enfant du dossier surveillé est supprimé
IN_DELETE_SELF = l'objet surveillé lui-même est supprimé
Code: Select all
IN_MOVED_FROM = le fichier quitte le dossier surveillé
IN_MOVED_TO = le fichier arrive dans le dossier surveillé
Si IN_Q_OVERFLOW arrive, des événements ont pu être perdus.
Dans un vrai outil, il faut alors resynchroniser l'état réel du dossier surveillé.
24. Cas d'utilisation réels
inotify peut servir à créer :
- un outil qui surveille un dossier de logs ;
- un système d'auto-reload de configuration ;
- un watcher de projet pour recompiler automatiquement ;
- un outil de synchronisation de fichiers ;
- un système de détection de fichiers nouvellement créés ;
- un petit système de monitoring ;
- un outil qui déclenche une action quand un fichier est modifié ;
- un service qui surveille les uploads dans un dossier ;
- un programme qui attend qu'un fichier soit complètement écrit avant de le traiter.
Code: Select all
Un programme surveille /var/log.
Quand un fichier de log est modifié, il lit les nouvelles lignes.
Code: Select all
Un programme surveille un dossier d'upload.
Quand un fichier reçoit IN_CLOSE_WRITE, cela signifie que l'écriture est probablement terminée.
Le programme peut alors traiter le fichier.
Avant inotify, Linux avait un mécanisme plus ancien appelé dnotify.
Il est aujourd'hui considéré comme dépassé.
Ses limites principales :
- il repose sur des signaux ;
- il est moins pratique ;
- il est moins précis ;
- il surveille surtout les répertoires ;
- il nécessite d'ouvrir les répertoires surveillés ;
- il est plus lourd à utiliser proprement.
26. Résumé des API à retenir
Code: Select all
int inotify_init(void);
Code: Select all
int inotify_init1(int flags);
Code: Select all
int inotify_add_watch(int fd, const char *pathname, uint32_t mask);
Code: Select all
int inotify_rm_watch(int fd, int wd);
Code: Select all
ssize_t read(int fd, void *buf, size_t count);
Code: Select all
int close(int fd);
27. Résumé des structures à retenir
La structure essentielle :
Code: Select all
struct inotify_event {
int wd;
uint32_t mask;
uint32_t cookie;
uint32_t len;
char name[];
};
- wd : identifiant du watch qui a généré l'événement ;
- mask : type d'événement ;
- cookie : lien entre IN_MOVED_FROM et IN_MOVED_TO ;
- len : taille du champ name ;
- name : nom du fichier concerné dans le dossier surveillé.
Code: Select all
IN_ACCESS fichier lu
IN_OPEN fichier ouvert
IN_MODIFY contenu modifié
IN_ATTRIB attributs changés
IN_CLOSE_WRITE fichier fermé après écriture
IN_CLOSE_NOWRITE fichier fermé sans écriture
IN_CREATE fichier créé dans un dossier surveillé
IN_DELETE fichier supprimé dans un dossier surveillé
IN_DELETE_SELF objet surveillé lui-même supprimé
IN_MOVE_SELF objet surveillé lui-même déplacé
IN_MOVED_FROM fichier sorti du dossier surveillé
IN_MOVED_TO fichier arrivé dans le dossier surveillé
IN_IGNORED watch supprimé
IN_Q_OVERFLOW file d'événements saturée
IN_UNMOUNT système de fichiers démonté
IN_ISDIR l'objet concerné est un dossier
inotify est l'API Linux moderne pour surveiller les événements du système de fichiers.
Elle permet à un programme de recevoir des notifications quand des fichiers ou dossiers sont ouverts, lus, modifiés, créés, supprimés, renommés ou déplacés.
Le fonctionnement repose sur :
- une instance inotify représentée par un file descriptor ;
- des watches ajoutés avec inotify_add_watch() ;
- une lecture des événements avec read() ;
- une structure struct inotify_event pour chaque événement ;
- un mask pour identifier le type d'événement ;
- un cookie pour relier certains déplacements ;
- un champ name pour connaître le nom du fichier concerné dans un dossier surveillé.
Code: Select all
inotify_init()
inotify_init1()
inotify_add_watch()
inotify_rm_watch()
read()
close()
struct inotify_event
Code: Select all
read() peut retourner plusieurs événements.
inotify n'est pas récursif.
IN_DELETE et IN_DELETE_SELF sont différents.
IN_MOVED_FROM et IN_MOVED_TO sont reliés par cookie.
IN_Q_OVERFLOW signifie que des événements ont pu être perdus.
Code: Select all
inotify est très proche de ReadDirectoryChangesW,
mais dans le style Linux : fd, read(), buffer, structures variables.
Défi :
Écrire un programme qui prend un dossier en argument et affiche :
- quand un fichier est créé ;
- quand un fichier est supprimé ;
- quand un fichier est modifié ;
- quand un fichier est renommé ;
- si l'objet concerné est un dossier ;
- le cookie pour les événements de déplacement.
Code: Select all
./watcher dossier
Code: Select all
CREATE name=a.txt
MODIFY name=a.txt
MOVED_FROM name=a.txt cookie=3812
MOVED_TO name=b.txt cookie=3812
DELETE name=b.txt
