Отслеживание изменений в директории с помощью Inotify

от автора

Столкнулся с задачей, где необходимо было отслеживать в ОС Linux изменение файла в директории на чистом С++. Так как чистый С++, Qt с его QFileSystemWatcher сразу отпадал, из-за того что необходимо было подключать QObject. В итоге решил пользоваться линуксовой функцией Inotify.

Inotify позволяет через файловый дескриптор наблюдать за директорией или файлом, отслеживая их события. Все события ввода-вывода ссылаются на открытый файл с использованием файлового дескриптора. Файловый дескриптор представляет собой целое число типа int.

Итак, работа типичной программы мониторинга организована следующим образом:

  1. С помощью inotify_init() открываем файловый дескриптор
  2. Добавляем одно или несколько событий для наблюдений
  3. Ожидаем добавленное событие
  4. Обрабатываем события, после чего снова начинаем ждать в бесконечном цикле
  5. При отсутствии активных наблюдений или при получении определенного сигнала файловый дескриптор закрывается, выполняется очистка и программа завершает работу.

В API Inotify используются следующие системные вызовы:

inotify_init() инициализирует новый экземпляр inotify и возвращает файловый дескриптор. На данный момент используется более новый системный вызов inotify_init1(int flags). Если используется флаг 0 или флаги вообще не указаны, то
inotify_init1(int flags) будет работать как inotify_init(). Так же используются другие флаги:
IN_NONBLOCK – устанавливает файловый дескриптор в неблокирующий режим.
IN_CLOEXEC – устанавливает флаг закрытия при выполнении.

Системный вызов inotify_add_watch(int fd, const char *pathname, uint32_t mask) добавляет новый элемент в список наблюдения для объекта inotify, ссылка на который осуществляется с помощью файлового дескриптора. Аргумент mask добавляет тип событий, которые должны мониториться файловым дескриптором. Рассмотрим флаги событий, за которыми можно наблюдать:

IN_ACCESS — Файл был прочитан (read())
IN_ATTRIB — Метаданные файла изменены
IN_CLOSE_WRITE — Файл был открыт для записи, а потом закрыт
IN_CLOSE_NOWRITE — Файл был открыт для записи, а потом закрыт
IN_CREATE — Файл/каталог создан внутри наблюдаемого каталога
IN_DELETE — Файл/каталог удален из наблюдаемого каталога
IN_DELETE_SELF — Наблюдаемый файл/каталог был удален
IN_MODIFY — Файл был изменен
IN_MOVE_SELF — Наблюдаемый файл/каталог был перемещен
IN_MOVED_FROM — Наблюдаемый файл/каталог был перемещен из наблюдаемого каталога
IN_MOVED_TO — Наблюдаемый файл/каталог был перемещен в наблюдаемый каталог
IN_OPEN — Файл был открыт
IN_ALL_EVENTS — Сокращение для всех вышеперечисленных событий ввода
IN_MOVE — Сокращение для IN_MOVED_FROM | IN_MOVED_TO
IN_CLOSE — Сокращение для IN CLOSE WRITE | IN CLOSE NOWRITE
IN_DONT_FОLLOW — He разыменовывать символьную ссылку (начиная с Linux 2.6.15)
IN_MASK_ADD — Добавить события в маску текущего элемента списка наблюдения для файла pathname
IN_ONESHOT — Наблюдать для файла pathname только одно событие
IN_ONLYDIR — Ошибка, если pathname не каталог (начиная с Linux 2.6.15)

Для удаления события из списка наблюдаемых событий используется функция inotify_rm_watch().

Функция read() читает данные из буфера об одном или нескольких событиях.

close() — закрывает файловый дескриптор и удаляет все связанные с ним наблюдения. Когда все файловые дескрипторы экземпляра inotify закрываются, все системные ресурсы освобождаются, чтобы ядро могло их повторно использовать.

Итак рассмотрим листинг программы. В программе будем отслеживать изменения, происходящие в директории по абсолютному пути «home/user/dev». Отслеживаемые события это закрытие файла и его изменение.

#define MAX_EVENTS 1024 /*Максимальное кличество событий для обработки за один раз*/ #define LEN_NAME 16 /*Будем считать, что длина имени файла не превышает 16 символов*/ #define EVENT_SIZE  ( sizeof (struct inotify_event) ) /*размер структуры события*/ #define BUF_LEN     ( MAX_EVENTS * ( EVENT_SIZE + LEN_NAME )) /*буфер для хранения данных о событиях*/  int main(int argc, char *argv[]) {     int length, i = 0, wd;     int fd;     char buffer[BUF_LEN];       /* Инициализация Inotify*/     fd = inotify_init1(0);     if ( fd < 0 ) {         perror( "Couldn't initialize inotify");     }     const char path[] = "/home/user/dev";      /* добавим наблюдение для директории*/     wd = inotify_add_watch(fd, path, IN_CLOSE | IN_MODIFY);     if (wd == -1)     {         printf("Couldn't add watch to %s\n",path);     }     else     {         printf("Watching:: %s\n",path);     }      while(1)     {         i = 0;   /* высчитываем размер файлового дескриптора*/         length = read( fd, buffer, BUF_LEN );         if ( length < 0 ) {             perror( "read" );         }          while ( i < length ) {         struct inotify_event *event = ( struct inotify_event * ) &buffer[ i ];         if ( event->len ) {             if ( event->mask & IN_CLOSE) {                 if (event->mask & IN_ISDIR)                     printf( "The directory %s was closed.\n", event->name );                 else                     printf( "The file %s was closed with WD %d\n", event->name, event->wd );             }             if ( event->mask & IN_MODIFY) {                 if (event->mask & IN_ISDIR)                     printf( "The directory %s was modified.\n", event->name );                 else                     printf( "The file %s was modified with WD %d\n", event->name, event->wd );             }                 i += EVENT_SIZE + event->len;             }         }     }     /* Освобождение ресурсов*/     inotify_rm_watch( fd, wd );     close( fd );     return 0; } 


ссылка на оригинал статьи https://habr.com/ru/articles/782742/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *