/** * 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> #include <sys/wait.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); } 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; }