diff --git a/inotify/Makefile b/inotify/Makefile new file mode 100644 index 0000000..6220e27 --- /dev/null +++ b/inotify/Makefile @@ -0,0 +1,8 @@ +# Makefile for sample program +# @miura + +all: + gcc -o show_events show_events.c + gcc -o exec_on_modify exec_on_modify.c +clean: + rm -f exec_on_modify show_events diff --git a/inotify/exec_on_modify.c b/inotify/exec_on_modify.c new file mode 100644 index 0000000..e3a1968 --- /dev/null +++ b/inotify/exec_on_modify.c @@ -0,0 +1,307 @@ +/** + * Original: + * https://blpr.net/?p=210 + * + * 20201130 @miura added #include to prevent warning of implicit declaration of functions: + * read / close. + */ +#include +#include +#include +#include +#include +#include +#include +#include +// @miura added +#include +#include +#include + +#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); +} + +char cmd[255] = {}; +char prefix[63] = {}; + +/** return true when target ends with find_prefix */ +int ends_with(char* target, char* find_prefix){ + char *p = target; + char *q = find_prefix; + while( *p ) p++; + while( *q ) q++; + while( find_prefix <= q){ + if (*p-- != *q--) return 0; + } + return 1; +} +void exec_cmd(char* target) +{ + pid_t pid; +// printf("target: %s\n", target); + if (strlen(prefix) == 0 || ends_with(target, prefix)){ + if ((pid = fork()) == 0){ + char *argv[] = { cmd, NULL }; + execvp( cmd, argv ); + } else { + wait(0); + } + } +} + +int main(int argc, char** argv) +{ + struct timeval waitval; + int fd; + int ret; + fd_set readfds; + + if (argc < 2){ + printf("Usage: exec_on_modify command [prefix]\n"); + printf(" command is executed when a file ends with [prefix] modified.\n"); + printf(" example1: exec_on_modify make .c \n"); + printf(" example2: exec_on_modify platex .tex \n"); + exit(0); + } +// printf("%d\n", argc); + if (argc >= 2){ + strcpy(cmd, argv[1]); + } + if (argc == 3){ + strcpy(prefix, argv[2]); + } + printf("%s\n", cmd); + printf("%s\n", prefix); + + 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); + } + + if(event->mask & IN_CLOSE_WRITE){ + if (target[0] !='.'){ + print_datetime(); + printf("Writtable [%s] was closed.\n", target); + exec_cmd(target); + } + } + + if(event->mask & IN_CREATE && event->name[0]!='.' ){ + print_datetime(); + 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_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; +} + diff --git a/inotify/show_events.c b/inotify/show_events.c new file mode 100644 index 0000000..16ea210 --- /dev/null +++ b/inotify/show_events.c @@ -0,0 +1,288 @@ +/** + * Original: + * https://blpr.net/?p=210 + * + * 20201130 @miura added #include to prevent warning of implicit declaration of functions: + * read / close. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +// @miura added +#include +#include + +#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; +} +