/**
* 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;
}