Newer
Older
advsyssoft / inotify / show_events.c
@Motoki Miura Motoki Miura on 30 Nov 2020 6 KB inotify sample
/**
 * Original:
 * https://blpr.net/?p=210
 *
 * 20201130 @miura added #include<unistd.h> to prevent warning of implicit declaration of functions:
 * read / close.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/inotify.h>
#include <sys/select.h>
#include <limits.h>
// @miura added
#include <unistd.h>
#include <time.h>
 
#define WATCH_DIR "."
 
//イベントサイズは16バイト境界
#define INOTIFY_EVENT_MAX (((sizeof(struct inotify_event)+NAME_MAX+1)+16)&~16)
 
typedef struct _WD_INFO
{
  struct _WD_INFO* prev;
  struct _WD_INFO* next;
  int wd;
  char* path;
} WD_INFO;
 
static WD_INFO* topWdInfo = NULL;
 
static void getWdInfo(int fd, char* dirname)
{
  DIR* dir = NULL;
  struct dirent* entry;
  struct stat st;
  int wd;
  int dirname_len;
  int entname_len;
  char* fullpath = NULL;
  WD_INFO* newWdInfo;
 
  newWdInfo = (WD_INFO*)malloc(sizeof(WD_INFO));
 
  if(topWdInfo == NULL){
    // first
    topWdInfo = newWdInfo;
    topWdInfo->prev = topWdInfo;
    topWdInfo->next = topWdInfo;
  }else{
    newWdInfo->prev = topWdInfo->prev;
    topWdInfo->prev->next = newWdInfo;
    topWdInfo->prev = newWdInfo;
    newWdInfo->next = topWdInfo;
  }
 
  newWdInfo->wd = inotify_add_watch(fd, dirname, IN_ALL_EVENTS);
  newWdInfo->path = strdup(dirname);
 
  //Search Sub directry
  dir = opendir(dirname);
 
  dirname_len = strlen(dirname);
 
  while((entry = readdir(dir)) != NULL){
 
    if(strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0){
      continue;
    }
 
    //エントリのタイプ種別を非標準のd_typeを使わず、statで取得
    entname_len = strlen(entry->d_name);
    fullpath = (char*)malloc(dirname_len + 1 + entname_len + 1);
    strcpy(fullpath, dirname);
    strcat(fullpath, "/");
    strcat(fullpath, entry->d_name);
    stat(fullpath, &st);
 
    if(S_ISDIR(st.st_mode)){
      //再帰呼び出し
      getWdInfo(fd, fullpath);
    }
 
    free(fullpath);
  }
 
  closedir(dir);
}


static char* wd2path(int wd)
{
  WD_INFO* p;
 
  if(topWdInfo == NULL){
    return NULL;
  }
 
  p = topWdInfo;
  do{
    if(p->wd == wd){
      return p->path;
    }
    p = p->next;
  }while(p != topWdInfo);
 
  return NULL;
}
 
static void closeAllWdInfo(int fd)
{
  WD_INFO* p;
 
  if(topWdInfo == NULL){
    return;
  }
 
  p = topWdInfo;
  do{
    WD_INFO* del;
    del = p;
    p = p->next;
    free(del->path);
    inotify_rm_watch(fd, del->wd);
    free(del);
  }while(p != topWdInfo);
  topWdInfo = NULL;
}
 
static void deleteWdInfo(int wd)
{
  WD_INFO* p;
 
  if(topWdInfo == NULL){
    return;
  }
 
  p = topWdInfo;
  do{
    if(p->wd == wd){
      if(p->next == p->prev){
        topWdInfo = NULL;
      }else{
        if(p == topWdInfo){
          topWdInfo = p->next;
        }
        p->next->prev = p->prev;
        p->prev->next = p->next;
      }
      free(p->path);
      free(p);
 
      return;
    }
    p = p->next;
  }while(p != topWdInfo);
 
  return;
}

/** @miura added */
void print_datetime()
{
	time_t t = time(NULL);
	struct tm *lcl = localtime(&t);
	printf("%02d/%02d %02d:%02d:%02d ", lcl->tm_mon+1, lcl->tm_mday,
			lcl->tm_hour, lcl->tm_min, lcl->tm_sec);
}

int main(int argc, char** argv)
{
  struct timeval waitval;
  int fd;
  int ret;
  fd_set readfds;

  fd = inotify_init();

  getWdInfo(fd, (char*)WATCH_DIR);

  while(1){
    FD_ZERO(&readfds);
    FD_SET(fd, &readfds);
    ret = select(fd+1, &readfds, NULL, NULL, NULL);
    if(0 < ret){
      if(FD_ISSET(fd, &readfds)){
        char* buf;
        int len;
        struct inotify_event* event;

        buf = (char*)malloc(INOTIFY_EVENT_MAX);

        //INOTIFY_EVENT_MAXを指定し、最低でも一つのイベントは読み込む。
        len = read(fd, buf, INOTIFY_EVENT_MAX);

        event = (struct inotify_event*)buf;

        //複数イベントがあるかもしれない。全部処理するまでループ。
        while(len > 0){
          char* target;
          if(event->len){
            target = event->name;
          }else{
            target = wd2path(event->wd);
          }

	  print_datetime();


          if(event->mask & IN_ACCESS){
            printf("[%s] was accessed.\n", target);
          }
          if(event->mask & IN_MODIFY){
            printf("[%s] was modified.\n", target);
          }
          if(event->mask & IN_ATTRIB){
            printf("Metadata of [%s] changed.\n", target);
          }
          if(event->mask & IN_CLOSE_WRITE){
            printf("Writtable [%s] was closed.\n", target);
          }
          if(event->mask & IN_CLOSE_NOWRITE){
            printf("Unwrittable [%s] closed.\n", target);
          }
          if(event->mask & IN_OPEN){
            printf("[%s] was opened.\n", target);
          }
          if(event->mask & IN_MOVED_FROM){
            printf("[%s] was moved from X.\n", target);
          }
          if(event->mask & IN_MOVED_TO){
            printf("[%s] was moved to Y.\n", target);
          }
          if(event->mask & IN_CREATE){
            printf("[%s] was created in [%s].\n", event->name, wd2path(event->wd));
            char* dirname;
            int dirname_len;
            int eventname_len;
            char* fullpath;
            struct stat st;

            dirname = wd2path(event->wd);
            eventname_len = strlen(event->name);
            fullpath = (char*)malloc(dirname_len + 1 + eventname_len + 1);
            strcpy(fullpath, dirname);
            strcat(fullpath, "/");
            strcat(fullpath, event->name);
            stat(fullpath, &st);

            if(S_ISDIR(st.st_mode)){
              //監視対象追加
              getWdInfo(fd, fullpath);
            }

	              }
          if(event->mask & IN_DELETE){
            printf("[%s] was deleted in [%s].\n", event->name, wd2path(event->wd));
          }
          if(event->mask & IN_DELETE_SELF){
            printf("[%s] was deleted.\n", target);
          }
          if(event->mask & IN_MOVE_SELF){
            printf("[%s] was moved.\n", target);
          }

          if(event->mask & IN_IGNORED){
            printf("[%s] was ignored.\n", target);
            //監視対象削除
            deleteWdInfo(event->wd);
          }

          len -= (sizeof(struct inotify_event) + event->len);
          event = (struct inotify_event*)(((char*)event)+sizeof(struct inotify_event) + event->len);
        }
        free(buf);
      }
    }
  }

  closeAllWdInfo(fd);
  close(fd);

  return 0;
}