diff --git a/CMakeLists.txt b/CMakeLists.txt index 823d5df..6391e01 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,7 @@ add_library(hiload SHARED src/memory.c src/string/string.c src/filewatcher/filewatcher.c + src/filewatcher/filewatch.c # dependencies src/logger/sc_log.c diff --git a/include/hiload/hiload.h b/include/hiload/hiload.h index 07706d9..9b8475f 100644 --- a/include/hiload/hiload.h +++ b/include/hiload/hiload.h @@ -29,7 +29,7 @@ void hi_deinit(void); /** * Print a random assortment of information from current state */ -void hi_print_module_infos(void); +void hi_print_loaded_modules(void); /** * Reload shared library if it has changed since last reload or init was called diff --git a/src/common.h b/src/common.h index e8ecb1b..ac14777 100644 --- a/src/common.h +++ b/src/common.h @@ -1,13 +1,11 @@ #ifndef COMMON_H_ #define COMMON_H_ +#include "logger/logger.h" #include "types.h" -static inline -u32 has_mask(u32 flags, u32 mask) { - return flags & mask; -} +static inline u32 has_mask(u32 flags, u32 mask) { return flags & mask; } -#define ARRLEN(A) (sizeof((A))/(sizeof ((A)[0]))) +#define ARRLEN(A) (sizeof((A)) / (sizeof((A)[0]))) #endif // COMMON_H_ diff --git a/src/filewatcher/fileevent.h b/src/filewatcher/fileevent.h deleted file mode 100644 index a153b00..0000000 --- a/src/filewatcher/fileevent.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef FILEEVENT_H_ -#define FILEEVENT_H_ - - - -#endif // FILEEVENT_H_ diff --git a/src/filewatcher/filewatch.c b/src/filewatcher/filewatch.c new file mode 100644 index 0000000..710037a --- /dev/null +++ b/src/filewatcher/filewatch.c @@ -0,0 +1,193 @@ +#include "filewatch.h" + +#include "common.h" +#include "filewatch_context.h" +#include "filewatch_type.h" + +#include +#include +#include +#include + +/* + * File Watch + */ + +struct WatchMaskParams { + u32 file_mask; + u32 parent_mask; +}; + +static inline struct WatchMaskParams inode_watch_masks_create(u32 flags) { + + u32 file_mask = 0u; + u32 parent_mask = 0u; + + if (has_mask(flags, HI_FILE_MODIFY)) { + file_mask |= IN_MODIFY; + } + if (has_mask(flags, HI_FILE_CREATE)) { + parent_mask |= IN_CREATE; + } + if (has_mask(flags, HI_FILE_DELETE)) { + file_mask |= IN_DELETE_SELF; + parent_mask |= IN_DELETE; + } + if (has_mask(flags, HI_FILE_MOVE)) { + file_mask |= IN_MOVE_SELF; + parent_mask |= IN_MOVED_TO; + } + + // Append if watch exists + file_mask |= IN_MASK_ADD; + parent_mask |= IN_MASK_ADD; + + return (struct WatchMaskParams){.file_mask = file_mask, + .parent_mask = parent_mask}; +} + +static void file_watch_destroy(int fd, hiFileWatch *fw) { + inotify_rm_watch(fd, fw->wd); + free((void *)fw->path); + + fw->wd = -1; + fw->path = NULL; +} + +void file_watch_destroy_watches(struct hiFileWatcherContext *ctx) { + for (size_t i = 0; i < sc_array_size(&ctx->watches); i++) { + hiFileWatch *fw = &sc_array_at(&ctx->watches, i); + file_watch_destroy(ctx->fd, fw); + } + sc_array_term(&ctx->watches); +} + +hiFileWatch *file_watch_find_by_wd(struct hiFileWatcherContext *ctx, int wd, + size_t *index) { + struct sc_array_fwatch *watches = &ctx->watches; + + for (size_t i = 0; i < sc_array_size(watches); ++i) { + hiFileWatch *fw = &sc_array_at(watches, i); + if (fw->wd == wd) { + if (index) { + *index = i; + } + return fw; + } + } + return NULL; +} + +hiFileWatch *file_watch_find_by_path(struct hiFileWatcherContext *ctx, + const char *path, size_t *index) { + for (size_t i = 0; i < sc_array_size(&ctx->watches); i++) { + hiFileWatch *watch = &sc_array_at(&ctx->watches, i); + if (strcmp(watch->path, path) == 0) { + if (index) { + *index = i; + } + return watch; + } + } + return NULL; +} + +/** + * Return null terminated char array with the parent, or NULL if no path + * separator was found. + * */ +static char *get_parent_folder(const char path[static 1]) { + const char *last_separator = strrchr(path, '/'); + if (!last_separator) + return NULL; + + size_t length = last_separator - path; + char *parent = calloc(length, sizeof(char)); + strncpy(parent, path, length); + parent[length] = '\0'; + return parent; +} + +HiloadResult file_watch_add(hiFileWatcherContext *ctx, u32 mask, + const char *path) { + if (!ctx || ctx->fd == -1) { + sc_log_error("Invalid inotify context\n"); + return HILOAD_FAIL; + } + struct WatchMaskParams params = inode_watch_masks_create(mask); + + hiFileWatch *watch = file_watch_find_by_path(ctx, path, NULL); + if (!watch) { + int wd = inotify_add_watch(ctx->fd, path, params.file_mask); + if (wd == -1) { + sc_log_error("Couldn't watch: %s: %s\n", path, strerror(errno)); + return HILOAD_FAIL; + } + hiFileWatch wp = {.wd = wd, .mask = mask, .path = strdup(path)}; + sc_array_add(&ctx->watches, wp); + watch = &sc_array_last(&ctx->watches); + } else { + inotify_add_watch(ctx->fd, path, params.file_mask); + } + + if (!has_mask(HI_FILE_PARENT, mask)) { + char *parent = get_parent_folder(path); + hiFileWatch *parent_watch = file_watch_find_by_path(ctx, parent, NULL); + if (!parent_watch) { + // parent not yet watched + int wd = inotify_add_watch(ctx->fd, parent, params.parent_mask); + hiFileWatch wp = {.wd = wd, .mask = HI_FILE_PARENT, .path = parent}; + sc_array_init(&wp.files); + sc_array_add(&wp.files, strdup(watch->path)); + sc_array_add(&ctx->watches, wp); + } else { + free(parent); + inotify_add_watch(ctx->fd, parent_watch->path, params.parent_mask); + { + // Make sure we don't add a duplicate if the file is already marked + bool exists = false; + for (size_t i = 0; i < sc_array_size(&parent_watch->files); i++) { + const char *file = sc_array_at(&parent_watch->files, i); + // we store the pointer, so this should be fine + if (file == watch->path) { + exists = true; + break; + } + } + if (!exists) { + sc_array_add(&parent_watch->files, watch->path); + } + } + } + } + + return HILOAD_OK; +} + +HiloadResult file_watch_remove(hiFileWatcherContext *ctx, const char *path) { + + size_t i = 0; + hiFileWatch *watch = file_watch_find_by_path(ctx, path, &i); + if (watch) { + + // destroy parent reference. We assume it will only have one. + char *parent = get_parent_folder(path); + if (parent) { + hiFileWatch *pw = file_watch_find_by_path(ctx, parent, NULL); + if (pw) { + for (size_t i = 0; i < sc_array_size(&pw->files); i++) { + const char *fn = sc_array_at(&pw->files, i); + if (fn == watch->path) { + sc_array_del(&pw->files, i); + break; + } + } + } + free(parent); + } + file_watch_destroy(ctx->fd, watch); + sc_array_del(&ctx->watches, i); + return HILOAD_OK; + } + return HILOAD_FAIL; +} diff --git a/src/filewatcher/filewatch.h b/src/filewatcher/filewatch.h new file mode 100644 index 0000000..64bb0dd --- /dev/null +++ b/src/filewatcher/filewatch.h @@ -0,0 +1,22 @@ +#ifndef FILEWATCH_H_ +#define FILEWATCH_H_ + +#include "array/array.h" +#include "filewatch_context.h" +#include "types.h" + +struct hiFileWatcherContext; +struct hiFileWatch; + +hiFileWatch *file_watch_find_by_wd(struct hiFileWatcherContext *ctx, + int wd, size_t *index); +hiFileWatch *file_watch_find_by_path(struct hiFileWatcherContext *ctx, + const char *path, size_t *index); + +HiloadResult file_watch_add(struct hiFileWatcherContext *ctx, u32 mask, + const char *path); +HiloadResult file_watch_remove(struct hiFileWatcherContext *ctx, + const char *path); +void file_watch_destroy_watches(struct hiFileWatcherContext *ctx); + +#endif // FILEWATCH_H_ diff --git a/src/filewatcher/filewatch_context.h b/src/filewatcher/filewatch_context.h new file mode 100644 index 0000000..6b9c391 --- /dev/null +++ b/src/filewatcher/filewatch_context.h @@ -0,0 +1,35 @@ +#ifndef FILEWATCH_CONTEXT_H_ +#define FILEWATCH_CONTEXT_H_ + +#include "array/array.h" +#include "types.h" + +/** + * Added for each file watch call. + * + * Events and watches refer to the same string as params for efficient string + * comparison, but this means care has to be taken when deleting the params. + * Events should be cleared first. + */ +typedef struct { + const char *path; // Params own their path + u32 mask; +} hiWatchParams; +sc_array_def(hiWatchParams, fwparam); + +typedef struct { + int wd; // Watch descriptor + u32 mask; // watch mask used + const char *path; // inotify watch path, owning if it's a parent watcher + struct sc_array_str + files; // param paths associated with that path. Pointers to paths params. +} hiFileWatch; +sc_array_def(hiFileWatch, fwatch); + +typedef struct hiFileWatcherContext { + int fd; + struct sc_array_fwparam params; + struct sc_array_fwatch watches; +} hiFileWatcherContext; + +#endif // FILEWATCH_CONTEXT_H_ diff --git a/src/filewatcher/filewatch_type.h b/src/filewatcher/filewatch_type.h new file mode 100644 index 0000000..fd79f6b --- /dev/null +++ b/src/filewatcher/filewatch_type.h @@ -0,0 +1,36 @@ +#ifndef FILEWATCH_TYPE_H_ +#define FILEWATCH_TYPE_H_ + +typedef enum { + HI_FILE_NONE = 0, // Used to signal invalid event + HI_FILE_MODIFY = 1 << 1, + HI_FILE_CREATE = 1 << 2, + HI_FILE_DELETE = 1 << 3, + HI_FILE_MOVE = 1 << 4, + HI_FILE_CHANGE = + (HI_FILE_MODIFY | HI_FILE_CREATE | HI_FILE_DELETE | HI_FILE_MOVE), + HI_FILE_ALL_EVENTS = (HI_FILE_CHANGE), + HI_FILE_PARENT = 1 << 31, +} HiFileWatchType; + +static inline const char *hi_file_watch_type_to_str(HiFileWatchType type) { + switch (type) { + case HI_FILE_NONE: + return "HI_FILE_NONE"; + case HI_FILE_MODIFY: + return "HI_FILE_MODIFY"; + case HI_FILE_CREATE: + return "HI_FILE_CREATE"; + case HI_FILE_DELETE: + return "HI_FILE_DELETE"; + case HI_FILE_MOVE: + return "HI_FILE_MOVE"; + default: + return "Unknown HI_FILE_TYPE"; + + return "Unknown HI_FILE_TYPE"; + // clang-format on + } +} + +#endif // FILEWATCH_TYPE_H_ diff --git a/src/filewatcher/filewatcher.c b/src/filewatcher/filewatcher.c index 33f01e4..06efa0d 100644 --- a/src/filewatcher/filewatcher.c +++ b/src/filewatcher/filewatcher.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -9,287 +10,253 @@ #include #include -#include "types.h" #include "array/array.h" #include "common.h" +#include "filewatch.h" +#include "filewatch_context.h" +#include "filewatch_type.h" #include "logger/logger.h" +#include "types.h" -sc_array_def(hiFileEvent, fevent); - -typedef struct hiFileEvents { - struct sc_array_fevent events; -} hiFileEvents; - -typedef struct { - int wd; // Watch descriptor - u32 mask; // watch mask used - const char *path; // Owning -} WatchPath; -sc_array_def(WatchPath, watch); - -typedef struct hiWatchPaths { - int fd; - struct sc_array_watch watches; -} hiWatchPaths; +sc_array_def(hiFileEvent, fwevent); typedef struct hiFileWatcher { thrd_t thread; mtx_t mutex; cnd_t cond; int running; - struct hiWatchPaths watchpaths; - struct hiFileEvents events; + struct hiFileWatcherContext context; + struct sc_array_fwevent events; } hiFileWatcher; +/* + * Watch Params + */ + +static inline hiWatchParams watch_params_create(const char *path, u32 mask) { + return (hiWatchParams){.path = strdup(path), .mask = mask}; +} + +static inline void watch_params_destroy(hiWatchParams *params) { + free((char *)params->path); +} + +static hiWatchParams *watch_params_find_by_path(const char *path, + hiFileWatcherContext *ctx, + size_t *index) { + const struct sc_array_fwparam *params = &ctx->params; + for (size_t i = 0; i < sc_array_size(params); ++i) { + hiWatchParams *param = &sc_array_at(params, i); + if (strcmp(path, param->path) == 0) { + if (index) + *index = i; + return param; + } + } + return NULL; +} + /* * File Events */ -static hiFileEvent create_file_event(const struct inotify_event *event) { - hiFileEvent hievent = {0}; +#define NULL_EVENT (hiFileEvent){0}; - if (!event) { - return hievent; +static hiFileEvent file_event_create(const struct inotify_event *inevent, + hiFileWatcherContext *ctx) { + hiFileEvent event = {0}; + + if (!inevent) { + return event; } - if (event->len) - hievent.file = strdup(event->name); + const hiFileWatch *fw = file_watch_find_by_wd(ctx, inevent->wd, NULL); - if (has_mask(event->mask, IN_ACCESS)) { - hievent.type = HI_FILE_ACCESS; - } else if (has_mask(event->mask, IN_MODIFY)) { - hievent.type = HI_FILE_MODIFY; - } else if (has_mask(event->mask, IN_CREATE)) { - hievent.type = HI_FILE_CREATE; - } else if (has_mask(event->mask, IN_DELETE)) { - hievent.type = HI_FILE_DELETE; + if (has_mask(inevent->mask, IN_IGNORED)) { + sc_log_debug("IN_IGNORED received on %s\n", fw->path); + return NULL_EVENT; } - return hievent; -} -void hi_file_event_free(hiFileEvent *event) { - if (event) { - free((void *)event->file); - } -} + if (inevent->len) { + // find if file is associated with any path + for (size_t i = 0; i < sc_array_size(&fw->files); ++i) { + const char *watched_file = sc_array_at(&fw->files, i); + if (strcmp(watched_file, inevent->name)) { -void hi_file_event_destroy(hiFileEvent *event) { - if (event) { - free((void *)event->file); - memset(event, 0, sizeof(*event)); + // check if event mask matches the watcher mask + hiWatchParams *params = + watch_params_find_by_path(watched_file, ctx, NULL); + if (!params) { + return NULL_EVENT; + } + + if (has_mask(inevent->mask, IN_MODIFY)) { + event.type = HI_FILE_MODIFY; + } else if (has_mask(inevent->mask, IN_CREATE)) { + event.type = HI_FILE_CREATE; + } else if (has_mask(inevent->mask, IN_DELETE)) { + event.type = HI_FILE_DELETE; + } else if (has_mask(inevent->mask, IN_DELETE_SELF)) { + event.type = HI_FILE_DELETE; + } else if (has_mask(inevent->mask, IN_MOVE)) { + event.type = HI_FILE_MOVE; + } + + // We aren't watching for this event on the file, discard + if (!has_mask(params->mask, event.type)) { + return NULL_EVENT; + } + + event.pathname = watched_file; + + // Event matched and we're watching for it + return event; + } + } + // Something happened with a file we're not watching + return NULL_EVENT; } + // Event on the folder itself + + return NULL_EVENT; } hiFileEvent hi_file_event_pop(hiFileWatcher *ctx) { mtx_lock(&ctx->mutex); - hiFileEvent *last = &sc_array_last(&ctx->events.events); + hiFileEvent *last = &sc_array_last(&ctx->events); hiFileEvent e = *last; - // prevent destroy from freeing the file string - last->file = 0; - hi_file_event_destroy(last); - sc_array_del_last(&ctx->events.events); + sc_array_del_last(&ctx->events); mtx_unlock(&ctx->mutex); return e; } -static HiloadResult file_events_init(hiFileEvents *events) { - sc_array_init(&events->events); - return HILOAD_OK; -} - -static void file_events_destroy(hiFileEvents *events) { - if (events) { - for (size_t i = 0; i < sc_array_size(&events->events); ++i) { - hi_file_event_free(&sc_array_at(&events->events, i)); - } - sc_array_term(&events->events); - } -} - -/* - * Watch paths - */ - -static inline u32 create_watch_mask(u32 flags) { - u32 inmask = 0; - if (has_mask(flags, HI_FILE_ACCESS)) { - inmask |= IN_ACCESS; - } - if (has_mask(flags, HI_FILE_MODIFY)) { - inmask |= IN_MODIFY; - } - if (has_mask(flags, HI_FILE_CREATE)) { - inmask |= IN_CREATE; - } - if (has_mask(flags, HI_FILE_DELETE)) { - inmask |= IN_DELETE; - } - return inmask; -} - -static HiloadResult watch_path_add(hiWatchPaths *paths, u32 mask, - const char *path) { - - if (!paths || paths->fd == -1) { - sc_log_error("Invalid inotify context\n"); - return HILOAD_FAIL; - } - - int wd = inotify_add_watch(paths->fd, path, create_watch_mask(mask)); - if (wd == -1) { - sc_log_error("Couldn't watch: %s: %s\n", path, strerror(errno)); - return HILOAD_FAIL; - } - - { - WatchPath wp = {.wd = wd, .mask = mask, .path = strdup(path)}; - sc_array_add(&paths->watches, wp); - } - - return HILOAD_OK; -} - -static void watch_path_destroy(int fd, WatchPath *wf) { - inotify_rm_watch(fd, wf->wd); - free((void *)wf->path); - - wf->wd = -1; - wf->path = NULL; -} - -static HiloadResult watch_path_remove(hiWatchPaths *paths, const char *path) { - - bool found = false; - { - for (size_t i = 0; i < sc_array_size(&paths->watches); ++i) { - WatchPath *wp = &sc_array_at(&paths->watches, i); - if (strcmp(wp->path, path) == 0) { - watch_path_destroy(paths->fd, wp); - sc_array_del(&paths->watches, i); - found = true; - break; - } - } - } - return found ? HILOAD_OK : HILOAD_FAIL; -} - -static HiloadResult watch_paths_init(hiWatchPaths *paths) { - - paths->fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); - if (paths->fd == -1) { - return HILOAD_FAIL; - } - - sc_array_init(&paths->watches); - return HILOAD_OK; -} - -static void watch_paths_destroy(hiWatchPaths *paths) { - if (paths) { - for (size_t i = 0; i < sc_array_size(&paths->watches); i++) { - WatchPath *wp = &sc_array_at(&paths->watches, i); - watch_path_destroy(paths->fd, wp); - } - sc_array_term(&paths->watches); - } -} - /* * File Watcher */ +static HiloadResult file_watcher_ctx_init(hiFileWatcherContext *ctx) { + + ctx->fd = inotify_init1(IN_CLOEXEC | IN_NONBLOCK); + if (ctx->fd == -1) { + return HILOAD_FAIL; + } + + sc_array_init(&ctx->watches); + sc_array_init(&ctx->params); + return HILOAD_OK; +} + +static void file_watcher_ctx_destroy(hiFileWatcherContext *ctx) { + if (ctx) { + file_watch_destroy_watches(ctx); + } +} + // Declare the thread func int file_watcher_watch(void *arg); hiFileWatcher *hi_file_watcher_create() { // Allocate context - hiFileWatcher *context = malloc(sizeof(hiFileWatcher)); - memset(context, 0, sizeof(hiFileWatcher)); + hiFileWatcher *fw = malloc(sizeof(hiFileWatcher)); + memset(fw, 0, sizeof(hiFileWatcher)); - if (context) { - if (!HILOADRES(watch_paths_init(&context->watchpaths))) { + if (fw) { + if (!HILOADRES(file_watcher_ctx_init(&fw->context))) { sc_log_error("Failed to initialize watch paths\n"); - if (context->watchpaths.fd == -1) { + if (fw->context.fd == -1) { sc_log_error("Couldn't initialize inotify\n"); } - free(context); + free(fw); return NULL; } - - if (!HILOADRES(file_events_init(&context->events))) { - sc_log_error("Failed to initialize file events\n"); - free(context); - return NULL; - } - - mtx_init(&context->mutex, mtx_plain); - cnd_init(&context->cond); - context->running = true; - thrd_create(&context->thread, file_watcher_watch, context); - return context; + sc_array_init(&fw->events); + mtx_init(&fw->mutex, mtx_plain); + cnd_init(&fw->cond); + fw->running = true; + thrd_create(&fw->thread, file_watcher_watch, fw); + return fw; } - free(context); + free(fw); return NULL; } -HiloadResult hi_file_watcher_add(struct hiFileWatcher *ctx, - const char *filename, u32 flags) { - if (!ctx) { +HiloadResult hi_file_watcher_add(struct hiFileWatcher *fw, const char *filename, + u32 flags) { + if (!fw) { sc_log_error("Attempted to add file watcher for '%s' with null context\n", filename); return HILOAD_FAIL; } - mtx_lock(&ctx->mutex); - if (!HILOADRES(watch_path_add(&ctx->watchpaths, flags, filename))) { - mtx_unlock(&ctx->mutex); + mtx_lock(&fw->mutex); + + if (!HILOADRES(file_watch_add(&fw->context, flags, filename))) { + mtx_unlock(&fw->mutex); return HILOAD_FAIL; } - mtx_unlock(&ctx->mutex); + { + // Add watch param as well + hiWatchParams *params = + watch_params_find_by_path(filename, &fw->context, NULL); + if (!params) { + hiWatchParams params = watch_params_create(filename, flags); + sc_array_add(&fw->context.params, params); + } else { + params->mask |= flags; + } + } - sc_log_info("Watching file: %s\n", filename); + mtx_unlock(&fw->mutex); return HILOAD_OK; } -HiloadResult hi_file_watcher_remove(struct hiFileWatcher *ctx, +HiloadResult hi_file_watcher_remove(struct hiFileWatcher *fw, const char *filename) { - if (!ctx) { + if (!fw) { return HILOAD_FAIL; } - mtx_lock(&ctx->mutex); - if (!HILOADRES(watch_path_remove(&ctx->watchpaths, filename))) { - mtx_unlock(&ctx->mutex); + mtx_lock(&fw->mutex); + if (!HILOADRES(file_watch_remove(&fw->context, filename))) { + mtx_unlock(&fw->mutex); return HILOAD_FAIL; } - mtx_unlock(&ctx->mutex); + { + // remove watchparam as well + size_t i = 0; + hiWatchParams *params = + watch_params_find_by_path(filename, &fw->context, &i); + watch_params_destroy(params); + sc_array_del(&fw->context.params, i); + } + + mtx_unlock(&fw->mutex); sc_log_info("Removed file: '%s' from watch, filename"); return HILOAD_OK; } -void hi_file_watcher_notify(hiFileWatcher *ctx) { - if (ctx && ctx->running) { - cnd_signal(&ctx->cond); +void hi_file_watcher_notify(hiFileWatcher *fw) { + if (fw && fw->running) { + cnd_signal(&fw->cond); } } -void hi_file_watcher_destroy(hiFileWatcher *ctx) { - if (!ctx) +void hi_file_watcher_destroy(hiFileWatcher *fw) { + if (!fw) return; - if (ctx->running) { - ctx->running = false; - hi_file_watcher_notify(ctx); - thrd_join(ctx->thread, NULL); + if (fw->running) { + fw->running = false; + hi_file_watcher_notify(fw); + thrd_join(fw->thread, NULL); } - file_events_destroy(&ctx->events); - watch_paths_destroy(&ctx->watchpaths); - free(ctx); + + file_watcher_ctx_destroy(&fw->context); + free(fw); } int file_watcher_watch(void *arg) { @@ -304,7 +271,7 @@ int file_watcher_watch(void *arg) { while (ctx->running) { ssize_t n; - n = read(ctx->watchpaths.fd, buf, sizeof(buf)); + n = read(ctx->context.fd, buf, sizeof(buf)); if (n == -1 && errno != EAGAIN) { sc_log_error("Failed to read inotify: %s. Exiting thread.\n", strerror(errno)); @@ -316,8 +283,13 @@ int file_watcher_watch(void *arg) { ptr += sizeof(struct inotify_event) + event->len) { event = (const struct inotify_event *)ptr; - hiFileEvent e = create_file_event(event); - sc_array_add(&ctx->events.events, e); + hiFileEvent e = file_event_create(event, &ctx->context); + sc_array_add(&ctx->events, e); + + if (event->len) { + sc_log_debug("Event created: queue-size: %u, %s: %s\n", sc_array_size(&ctx->events), + e.pathname, hi_file_watch_type_to_str(e.type)); + } } continue; // read again without waiting } diff --git a/src/filewatcher/filewatcher.h b/src/filewatcher/filewatcher.h index 48f9eb7..2bc961a 100644 --- a/src/filewatcher/filewatcher.h +++ b/src/filewatcher/filewatcher.h @@ -1,27 +1,25 @@ #ifndef HI_FILEWATCHER_H_ #define HI_FILEWATCHER_H_ +#include "filewatch_type.h" #include "types.h" /** - * File event watching related functionality and types + * File event watcher using inotify. */ -typedef enum { - HI_FILE_NONE = 0, - HI_FILE_ACCESS = 1 << 0, - HI_FILE_MODIFY = 1 << 1, - HI_FILE_CREATE = 1 << 2, - HI_FILE_DELETE = 1 << 3, - HI_FILE_ALL = - (HI_FILE_ACCESS | HI_FILE_MODIFY | HI_FILE_CREATE | HI_FILE_DELETE) -} HiFileEventType; +/** + * File Event produced by FileWatcher. + * + * As long as the event queue is cleared before making modifications to the File + * Watcher, pointers are safe to use and don't need freeing, as they refer to + * memory inside the file watcher struct. + */ typedef struct { - const char *watcher; // Path given to the corresponding wd. Not owning. - const char *file; // Owning, should be freed by the event handler by calling - // `hi_file_event_destroy`) - HiFileEventType type; + const char *pathname; // Pathname given with the `hi_file_watcher_add` call. + // Do not free. + HiFileWatchType type; } hiFileEvent; struct hiFileWatcher; @@ -60,18 +58,10 @@ HiloadResult hi_file_watcher_add(struct hiFileWatcher *context, */ void hi_file_watcher_notify(struct hiFileWatcher *context); -/** - * Frees held memory but doesn't zero fields - */ -void hi_file_event_free(hiFileEvent *event); /** * Pop an event from event stack. Call `hi_file_event_destroy` when done with * it. */ hiFileEvent hi_file_event_pop(struct hiFileWatcher *ctx); -/** - * Free and zero held memory. Call after pop. - */ -void hi_file_event_destroy(hiFileEvent *event); #endif // HI_FILEWATCHER_H_ diff --git a/src/hiload.c b/src/hiload.c index 4cb9384..bc54194 100644 --- a/src/hiload.c +++ b/src/hiload.c @@ -2,11 +2,12 @@ #include "array/sc_array.h" #include "filewatcher/filewatcher.h" +#include "logger/logger.h" +#include "logger/sc_log.h" #include "memory.h" #include "string/string.h" #include "symbols.h" #include "types.h" -#include "logger/logger.h" #include #include @@ -31,56 +32,62 @@ typedef struct { struct sc_array_memreg memory_regions; struct sc_array_str enabled_modules; struct hiFileWatcher *filewatcher; + ModuleInfos *loaded_modules; } HiloadContext; static HiloadContext context = {0}; -static ModuleInfos *module_infos = 0; // Callback function for dl_iterate_phdr static int gather_module_infos_callback(struct dl_phdr_info *info, size_t size, void *data) { - ModuleInfos *infos = (ModuleInfos *)data; + ModuleInfos *mod_infos = (ModuleInfos *)data; // Store the module name const char *modname = info->dlpi_name; - sc_array_add(&infos->names, strdup(modname)); - for (size_t i = 0; i < sc_array_size(&context.enabled_modules); i++) { - - if (hi_path_has_filename(modname, - sc_array_at(&context.enabled_modules, i))) { - if (!HILOADRES( - hi_file_watcher_add(context.filewatcher, modname, HI_FILE_ALL))) { - sc_log_error("Failed to set filewatcher for: '%s'\n", modname); - } - } - } + sc_array_add(&mod_infos->names, strdup(modname)); sc_log_info("Processing: '%s'\n", info->dlpi_name); // Try to get the handle void *handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD); assert(handle); - infos->count++; + mod_infos->count++; - sc_array_add(&infos->handles, handle); - sc_array_add(&infos->addresses, info->dlpi_addr); + sc_array_add(&mod_infos->handles, handle); + sc_array_add(&mod_infos->addresses, info->dlpi_addr); sc_log_debug(" size: %u\n", size); - sc_log_debug(" handle: %p\n", sc_array_last(&infos->handles)); + sc_log_debug(" handle: %p\n", sc_array_last(&mod_infos->handles)); sc_log_debug(" dlpi_addr: %p\n", info->dlpi_addr); sc_log_debug(" dlpi_tls_modid: %zu\n", info->dlpi_tls_modid); sc_log_debug(" dlpi_tls_data: %p\n", info->dlpi_tls_data); Dl_info dl_info = {0}; - if (!dladdr((void *)sc_array_last(&infos->addresses), &dl_info)) { - sc_log_error("Couldn't load dlinfo for %s: %s\n", - (sc_array_last(&infos->names)), dlerror()); - } - sc_log_debug(" dli_fname: %s\n", dl_info.dli_fname); - sc_log_debug(" dli_fbase: %p\n", dl_info.dli_fbase); + if (dladdr((void *)sc_array_last(&mod_infos->addresses), &dl_info)) { - SymbolInfos symbol_info = {0}; - sc_array_add(&infos->symbols, symbol_info); + // Check if it's in the provided watchlist and add a watcher + for (size_t i = 0; i < sc_array_size(&context.enabled_modules); i++) { + + if (hi_path_has_filename(modname, + sc_array_at(&context.enabled_modules, i))) { + + const char *filename = modname; + if (strcmp(modname, "") == 0) { + // The executable itself. Name needs to be resolved for filewatcher + filename = dl_info.dli_fname; + } + if (!HILOADRES(hi_file_watcher_add(context.filewatcher, filename, + HI_FILE_ALL_EVENTS))) { + sc_log_error("Failed to set filewatcher for: '%s'\n", filename); + } else { + sc_log_info("Watching file: %s\n", filename); + } + } + } + } else { + sc_log_error("Couldn't load dlinfo for %s: %s\n", + (sc_array_last(&mod_infos->names)), dlerror()); + } return 0; // Continue iteration } @@ -186,44 +193,50 @@ static ReloadResult reload_solib(ModuleInfos *modules, const char *filename, } ReloadResult hi_reload_solib(const char *module_name) { - assert(module_infos); void *new_handle = NULL; - ReloadResult result = reload_solib(module_infos, module_name, &new_handle); + ReloadResult result = + reload_solib(context.loaded_modules, module_name, &new_handle); return result; } -void hi_print_module_infos() { - assert(module_infos); +void hi_print_loaded_modules(void) { - const ModuleInfos *modules = module_infos; - if (!modules) { + sc_log_info("Loaded module info:\n"); + sc_log_info("-------------------\n"); + + const ModuleInfos *mod_infos = context.loaded_modules; + if (!mod_infos) { sc_log_error("No module information available.\n"); return; } - sc_log_info("Found %zu loaded modules:\n", modules->count); + sc_log_info("Found %zu loaded modules:\n", mod_infos->count); - for (size_t i = 0; i < modules->count; i++) { + for (size_t i = 0; i < mod_infos->count; i++) { - sc_log_debug("'%s'\n", sc_array_at(&modules->names, i)); - sc_log_debug(" handle: %p\n", sc_array_at(&modules->handles, i)); - sc_log_debug(" address: %p\n", sc_array_at(&modules->addresses, i)); + sc_log_info("'%s'\n", sc_array_at(&mod_infos->names, i)); + sc_log_info(" handle: %p\n", sc_array_at(&mod_infos->handles, i)); + sc_log_info(" address: %p\n", sc_array_at(&mod_infos->addresses, i)); - const SymbolInfos *symbols = &sc_array_at(&modules->symbols, i); - for (size_t j = 0; j < sc_array_size(&symbols->names); j++) { - const void *addr = sc_array_at(&symbols->addresses, j); - const char *name = sc_array_at(&symbols->names, j); - sc_log_debug(" %p: %s\n", addr, name); + // If we have loaded symbols, print those + if (sc_array_size(&mod_infos->symbols) > 0) { + const SymbolInfos *symbols = &sc_array_at(&mod_infos->symbols, i); + if (symbols) { + for (size_t j = 0; j < sc_array_size(&symbols->names); j++) { + const void *addr = sc_array_at(&symbols->addresses, j); + const char *name = sc_array_at(&symbols->names, j); + sc_log_info(" %p: %s\n", addr, name); + } + } } - sc_log_debug("\n"); + sc_log_info("\n"); } } int hi_init(unsigned n, const char **enabled_modules) { - assert(!module_infos); if (sc_log_init() != 0) { fprintf(stderr, "Failed to init logger.\n"); @@ -253,17 +266,19 @@ int hi_init(unsigned n, const char **enabled_modules) { } // Override existing - if (module_infos) { - module_infos_free(module_infos); + if (context.loaded_modules) { + module_infos_free(context.loaded_modules); } - module_infos = infos; - hi_print_module_infos(); + context.loaded_modules = infos; + hi_print_loaded_modules(); return 0; } void hi_deinit() { - module_infos_free(module_infos); + module_infos_free(context.loaded_modules); + hi_file_watcher_destroy(context.filewatcher); sc_log_term(); + memset(&context, 0, sizeof(context)); } diff --git a/src/memory.c b/src/memory.c index 56566fd..7e1d7d3 100644 --- a/src/memory.c +++ b/src/memory.c @@ -53,7 +53,7 @@ HiloadResult read_memory_maps_self(struct sc_array_memreg *regions) { char pathname[256]; // clang-format off - // Example from `cat /proc/self/maps` + // Format example from the file: // 7fa0b66ca000-7fa0b66cc000 rw-p 00033000 fe:02 28063911 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 int result = sscanf(line, "%lx-%lx %4s %lx %x:%x %lu %255s", ®.region_start, diff --git a/test/manual/minimal.cpp b/test/manual/minimal.cpp index 3de407d..e518576 100644 --- a/test/manual/minimal.cpp +++ b/test/manual/minimal.cpp @@ -19,7 +19,7 @@ int main(int argc, char *argv[]) { printf("minimal_lib::getNewValue address: %p\n", minimal_lib::getNewValue); printf("minimal_lib::otherValue address: %p\n", &minimal_lib::otherValue); printf("==========================\n"); - printf("\n"); + printf(""); modified = minimal_lib::getNewValue(5); printf("getModified(5): %d\n", modified); diff --git a/test/manual/minimal_lib.cpp b/test/manual/minimal_lib.cpp index c900d1b..d997707 100644 --- a/test/manual/minimal_lib.cpp +++ b/test/manual/minimal_lib.cpp @@ -3,7 +3,7 @@ namespace minimal_lib { int getNewValue(int x) { - static int value = 0; + static int value = 1; value = value + x; return value; }