From 3fa886653289d9698c8a2f91418975579e070950 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Mon, 20 Sep 2021 17:18:03 +0200 Subject: [PATCH] Adding new dmon version with dmon_watch_add --- src/dmon.h | 121 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 110 insertions(+), 11 deletions(-) diff --git a/src/dmon.h b/src/dmon.h index 92fba034..a1f6f38d 100644 --- a/src/dmon.h +++ b/src/dmon.h @@ -115,6 +115,7 @@ DMON_API_DECL dmon_watch_id dmon_watch(const char* rootdir, const char* oldfilepath, void* user), uint32_t flags, void* user_data); DMON_API_DECL void dmon_unwatch(dmon_watch_id id); +DMON_API_DECL bool dmon_watch_add(dmon_watch_id id, const char* subdir); #ifdef __cplusplus } @@ -670,7 +671,6 @@ DMON_API_IMPL void dmon_unwatch(dmon_watch_id id) _InterlockedExchange(&_dmon.modify_watches, 0); } -// clang-format off #elif DMON_OS_LINUX // inotify linux backend #define _DMON_TEMP_BUFFSIZE ((sizeof(struct inotify_event) + PATH_MAX) * 1024) @@ -710,7 +710,6 @@ typedef struct dmon__state { static bool _dmon_init; static dmon__state _dmon; -// clang-format on _DMON_PRIVATE void dmon__watch_recursive(const char* dirname, int fd, uint32_t mask, bool followlinks, dmon__watch_state* watch) @@ -742,7 +741,7 @@ _DMON_PRIVATE void dmon__watch_recursive(const char* dirname, int fd, uint32_t m // add sub-directory to watch dirs if (entry_valid) { - int watchdir_len = strlen(watchdir); + int watchdir_len = (int)strlen(watchdir); if (watchdir[watchdir_len - 1] != '/') { watchdir[watchdir_len] = '/'; watchdir[watchdir_len + 1] = '\0'; @@ -767,6 +766,60 @@ _DMON_PRIVATE void dmon__watch_recursive(const char* dirname, int fd, uint32_t m closedir(dir); } +DMON_API_IMPL bool dmon_watch_add(dmon_watch_id id, const char* watchdir) +{ + DMON_ASSERT(id.id > 0 && id.id <= DMON_MAX_WATCHES); + + pthread_mutex_lock(&_dmon.mutex); + dmon__watch_state* watch = &_dmon.watches[id.id - 1]; + + // check if the directory exists + // if watchdir contains absolute/root-included path, try to strip the rootdir from it + // else, we assume that watchdir is correct, so save it as it is + struct stat st; + dmon__watch_subdir subdir; + if (stat(watchdir, &st) == 0 && (st.st_mode & S_IFDIR)) { + dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir); + if (strstr(subdir.rootdir, watch->rootdir) == subdir.rootdir) { + dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir + strlen(watch->rootdir)); + } + } else { + char fullpath[DMON_MAX_PATH]; + dmon__strcpy(fullpath, sizeof(fullpath), watch->rootdir); + dmon__strcat(fullpath, sizeof(fullpath), watchdir); + if (stat(fullpath, &st) != 0 || (st.st_mode & S_IFDIR) == 0) { + _DMON_LOG_ERRORF("Watch directory '%s' is not valid", watchdir); + pthread_mutex_unlock(&_dmon.mutex); + return false; + } + dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), watchdir); + } + + int dirlen = (int)strlen(subdir.rootdir); + if (subdir.rootdir[dirlen - 1] != '/') { + subdir.rootdir[dirlen] = '/'; + subdir.rootdir[dirlen + 1] = '\0'; + } + + const uint32_t inotify_mask = IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE | IN_MODIFY; + char fullpath[DMON_MAX_PATH]; + + dmon__strcpy(fullpath, sizeof(fullpath), watch->rootdir); + dmon__strcat(fullpath, sizeof(fullpath), subdir.rootdir); + int wd = inotify_add_watch(watch->fd, fullpath, inotify_mask); + if (wd == -1) { + _DMON_LOG_ERRORF("Error watching directory '%s'. (inotify_add_watch:err=%d)", watchdir, errno); + pthread_mutex_unlock(&_dmon.mutex); + return false; + } + + stb_sb_push(watch->subdirs, subdir); + stb_sb_push(watch->wds, wd); + + pthread_mutex_unlock(&_dmon.mutex); + return true; +} + _DMON_PRIVATE const char* dmon__find_subdir(const dmon__watch_state* watch, int wd) { const int* wds = watch->wds; @@ -780,6 +833,39 @@ _DMON_PRIVATE const char* dmon__find_subdir(const dmon__watch_state* watch, int return NULL; } +_DMON_PRIVATE void dmon__gather_recursive(dmon__watch_state* watch, const char* dirname) +{ + struct dirent* entry; + DIR* dir = opendir(dirname); + DMON_ASSERT(dir); + + char newdir[DMON_MAX_PATH]; + while ((entry = readdir(dir)) != NULL) { + bool entry_valid = false; + bool is_dir = false; + if (strcmp(entry->d_name, "..") != 0 && strcmp(entry->d_name, ".") != 0) { + dmon__strcpy(newdir, sizeof(newdir), dirname); + dmon__strcat(newdir, sizeof(newdir), entry->d_name); + is_dir = (entry->d_type == DT_DIR); + entry_valid = true; + } + + // add sub-directory to watch dirs + if (entry_valid) { + dmon__watch_subdir subdir; + dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), newdir); + if (strstr(subdir.rootdir, watch->rootdir) == subdir.rootdir) { + dmon__strcpy(subdir.rootdir, sizeof(subdir.rootdir), newdir + strlen(watch->rootdir)); + } + + dmon__inotify_event dev = { { 0 }, IN_CREATE|(is_dir ? IN_ISDIR : 0), 0, watch->id, false }; + dmon__strcpy(dev.filepath, sizeof(dev.filepath), subdir.rootdir); + stb_sb_push(_dmon.events, dev); + } + } + closedir(dir); +} + _DMON_PRIVATE void dmon__inotify_process_events(void) { for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) { @@ -799,8 +885,8 @@ _DMON_PRIVATE void dmon__inotify_process_events(void) // in some cases, particularly when created files under sub directories // there can be two modify events for a single subdir one with trailing slash and one without // remove traling slash from both cases and test - int l1 = strlen(ev->filepath); - int l2 = strlen(check_ev->filepath); + int l1 = (int)strlen(ev->filepath); + int l2 = (int)strlen(check_ev->filepath); if (ev->filepath[l1-1] == '/') ev->filepath[l1-1] = '\0'; if (check_ev->filepath[l2-1] == '/') check_ev->filepath[l2-1] = '\0'; if (strcmp(ev->filepath, check_ev->filepath) == 0) { @@ -864,11 +950,20 @@ _DMON_PRIVATE void dmon__inotify_process_events(void) if (!move_valid) { ev->mask = IN_CREATE; } + } else if (ev->mask & IN_DELETE) { + for (int j = i + 1; j < c; j++) { + dmon__inotify_event* check_ev = &_dmon.events[j]; + // if the file is DELETED and then MODIFIED after, just ignore the modify event + if ((check_ev->mask & IN_MODIFY) && strcmp(ev->filepath, check_ev->filepath) == 0) { + check_ev->skip = true; + break; + } + } } } // trigger user callbacks - for (int i = 0, c = stb_sb_count(_dmon.events); i < c; i++) { + for (int i = 0; i < stb_sb_count(_dmon.events); i++) { dmon__inotify_event* ev = &_dmon.events[i]; if (ev->skip) { continue; @@ -899,6 +994,11 @@ _DMON_PRIVATE void dmon__inotify_process_events(void) stb_sb_push(watch->subdirs, subdir); stb_sb_push(watch->wds, wd); + + // some directories may be already created, for instance, with the command: mkdir -p + // so we will enumerate them manually and add them to the events + dmon__gather_recursive(watch, watchdir); + ev = &_dmon.events[i]; // gotta refresh the pointer because it may be relocated } } watch->watch_cb(ev->watch_id, DMON_ACTION_CREATE, watch->rootdir, ev->filepath, NULL, watch->user_data); @@ -907,7 +1007,7 @@ _DMON_PRIVATE void dmon__inotify_process_events(void) watch->watch_cb(ev->watch_id, DMON_ACTION_MODIFY, watch->rootdir, ev->filepath, NULL, watch->user_data); } else if (ev->mask & IN_MOVED_FROM) { - for (int j = i + 1; j < c; j++) { + for (int j = i + 1; j < stb_sb_count(_dmon.events); j++) { dmon__inotify_event* check_ev = &_dmon.events[j]; if (check_ev->mask & IN_MOVED_TO && ev->cookie == check_ev->cookie) { watch->watch_cb(check_ev->watch_id, DMON_ACTION_MOVE, watch->rootdir, @@ -921,7 +1021,6 @@ _DMON_PRIVATE void dmon__inotify_process_events(void) } } - stb_sb_reset(_dmon.events); } @@ -1093,7 +1192,7 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, } // add trailing slash - int rootdir_len = strlen(watch->rootdir); + int rootdir_len = (int)strlen(watch->rootdir); if (watch->rootdir[rootdir_len - 1] != '/') { watch->rootdir[rootdir_len] = '/'; watch->rootdir[rootdir_len + 1] = '\0'; @@ -1110,7 +1209,7 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, uint32_t inotify_mask = IN_MOVED_TO | IN_CREATE | IN_MOVED_FROM | IN_DELETE | IN_MODIFY; int wd = inotify_add_watch(watch->fd, watch->rootdir, inotify_mask); if (wd < 0) { - _DMON_LOG_ERRORF("watch failed: %s", watch->rootdir); + _DMON_LOG_ERRORF("Error watching directory '%s'. (inotify_add_watch:err=%d)", watch->rootdir, errno); pthread_mutex_unlock(&_dmon.mutex); __sync_lock_test_and_set(&_dmon.modify_watches, 0); return dmon__make_id(0); @@ -1491,7 +1590,7 @@ DMON_API_IMPL dmon_watch_id dmon_watch(const char* rootdir, } // add trailing slash - int rootdir_len = strlen(watch->rootdir); + int rootdir_len = (int)strlen(watch->rootdir); if (watch->rootdir[rootdir_len - 1] != '/') { watch->rootdir[rootdir_len] = '/'; watch->rootdir[rootdir_len + 1] = '\0';