make filewatcher more robust
This commit is contained in:
@@ -47,6 +47,7 @@ add_library(hiload SHARED
|
|||||||
src/memory.c
|
src/memory.c
|
||||||
src/string/string.c
|
src/string/string.c
|
||||||
src/filewatcher/filewatcher.c
|
src/filewatcher/filewatcher.c
|
||||||
|
src/filewatcher/filewatch.c
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
src/logger/sc_log.c
|
src/logger/sc_log.c
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ void hi_deinit(void);
|
|||||||
/**
|
/**
|
||||||
* Print a random assortment of information from current state
|
* 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
|
* Reload shared library if it has changed since last reload or init was called
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
#ifndef COMMON_H_
|
#ifndef COMMON_H_
|
||||||
#define COMMON_H_
|
#define COMMON_H_
|
||||||
|
|
||||||
|
#include "logger/logger.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
|
|
||||||
static inline
|
static inline u32 has_mask(u32 flags, u32 mask) { return flags & mask; }
|
||||||
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])))
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef FILEEVENT_H_
|
|
||||||
#define FILEEVENT_H_
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // FILEEVENT_H_
|
|
||||||
193
src/filewatcher/filewatch.c
Normal file
193
src/filewatcher/filewatch.c
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
#include "filewatch.h"
|
||||||
|
|
||||||
|
#include "common.h"
|
||||||
|
#include "filewatch_context.h"
|
||||||
|
#include "filewatch_type.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
22
src/filewatcher/filewatch.h
Normal file
22
src/filewatcher/filewatch.h
Normal file
@@ -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_
|
||||||
35
src/filewatcher/filewatch_context.h
Normal file
35
src/filewatcher/filewatch_context.h
Normal file
@@ -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_
|
||||||
36
src/filewatcher/filewatch_type.h
Normal file
36
src/filewatcher/filewatch_type.h
Normal file
@@ -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_
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdatomic.h>
|
#include <stdatomic.h>
|
||||||
|
#include <stddef.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
@@ -9,287 +10,253 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
#include "array/array.h"
|
#include "array/array.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "filewatch.h"
|
||||||
|
#include "filewatch_context.h"
|
||||||
|
#include "filewatch_type.h"
|
||||||
#include "logger/logger.h"
|
#include "logger/logger.h"
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
sc_array_def(hiFileEvent, fevent);
|
sc_array_def(hiFileEvent, fwevent);
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
typedef struct hiFileWatcher {
|
typedef struct hiFileWatcher {
|
||||||
thrd_t thread;
|
thrd_t thread;
|
||||||
mtx_t mutex;
|
mtx_t mutex;
|
||||||
cnd_t cond;
|
cnd_t cond;
|
||||||
int running;
|
int running;
|
||||||
struct hiWatchPaths watchpaths;
|
struct hiFileWatcherContext context;
|
||||||
struct hiFileEvents events;
|
struct sc_array_fwevent events;
|
||||||
} hiFileWatcher;
|
} 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
|
* File Events
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static hiFileEvent create_file_event(const struct inotify_event *event) {
|
#define NULL_EVENT (hiFileEvent){0};
|
||||||
hiFileEvent hievent = {0};
|
|
||||||
|
|
||||||
if (!event) {
|
static hiFileEvent file_event_create(const struct inotify_event *inevent,
|
||||||
return hievent;
|
hiFileWatcherContext *ctx) {
|
||||||
|
hiFileEvent event = {0};
|
||||||
|
|
||||||
|
if (!inevent) {
|
||||||
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->len)
|
const hiFileWatch *fw = file_watch_find_by_wd(ctx, inevent->wd, NULL);
|
||||||
hievent.file = strdup(event->name);
|
|
||||||
|
|
||||||
if (has_mask(event->mask, IN_ACCESS)) {
|
if (has_mask(inevent->mask, IN_IGNORED)) {
|
||||||
hievent.type = HI_FILE_ACCESS;
|
sc_log_debug("IN_IGNORED received on %s\n", fw->path);
|
||||||
} else if (has_mask(event->mask, IN_MODIFY)) {
|
return NULL_EVENT;
|
||||||
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;
|
|
||||||
}
|
|
||||||
return hievent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void hi_file_event_free(hiFileEvent *event) {
|
if (inevent->len) {
|
||||||
if (event) {
|
// find if file is associated with any path
|
||||||
free((void *)event->file);
|
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)) {
|
||||||
|
|
||||||
|
// check if event mask matches the watcher mask
|
||||||
|
hiWatchParams *params =
|
||||||
|
watch_params_find_by_path(watched_file, ctx, NULL);
|
||||||
|
if (!params) {
|
||||||
|
return NULL_EVENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hi_file_event_destroy(hiFileEvent *event) {
|
if (has_mask(inevent->mask, IN_MODIFY)) {
|
||||||
if (event) {
|
event.type = HI_FILE_MODIFY;
|
||||||
free((void *)event->file);
|
} else if (has_mask(inevent->mask, IN_CREATE)) {
|
||||||
memset(event, 0, sizeof(*event));
|
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) {
|
hiFileEvent hi_file_event_pop(hiFileWatcher *ctx) {
|
||||||
mtx_lock(&ctx->mutex);
|
mtx_lock(&ctx->mutex);
|
||||||
hiFileEvent *last = &sc_array_last(&ctx->events.events);
|
hiFileEvent *last = &sc_array_last(&ctx->events);
|
||||||
hiFileEvent e = *last;
|
hiFileEvent e = *last;
|
||||||
|
|
||||||
// prevent destroy from freeing the file string
|
sc_array_del_last(&ctx->events);
|
||||||
last->file = 0;
|
|
||||||
hi_file_event_destroy(last);
|
|
||||||
sc_array_del_last(&ctx->events.events);
|
|
||||||
|
|
||||||
mtx_unlock(&ctx->mutex);
|
mtx_unlock(&ctx->mutex);
|
||||||
return e;
|
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
|
* 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
|
// Declare the thread func
|
||||||
int file_watcher_watch(void *arg);
|
int file_watcher_watch(void *arg);
|
||||||
|
|
||||||
hiFileWatcher *hi_file_watcher_create() {
|
hiFileWatcher *hi_file_watcher_create() {
|
||||||
|
|
||||||
// Allocate context
|
// Allocate context
|
||||||
hiFileWatcher *context = malloc(sizeof(hiFileWatcher));
|
hiFileWatcher *fw = malloc(sizeof(hiFileWatcher));
|
||||||
memset(context, 0, sizeof(hiFileWatcher));
|
memset(fw, 0, sizeof(hiFileWatcher));
|
||||||
|
|
||||||
if (context) {
|
if (fw) {
|
||||||
if (!HILOADRES(watch_paths_init(&context->watchpaths))) {
|
if (!HILOADRES(file_watcher_ctx_init(&fw->context))) {
|
||||||
sc_log_error("Failed to initialize watch paths\n");
|
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");
|
sc_log_error("Couldn't initialize inotify\n");
|
||||||
}
|
}
|
||||||
free(context);
|
free(fw);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
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(fw);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!HILOADRES(file_events_init(&context->events))) {
|
HiloadResult hi_file_watcher_add(struct hiFileWatcher *fw, const char *filename,
|
||||||
sc_log_error("Failed to initialize file events\n");
|
u32 flags) {
|
||||||
free(context);
|
if (!fw) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
free(context);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
HiloadResult hi_file_watcher_add(struct hiFileWatcher *ctx,
|
|
||||||
const char *filename, u32 flags) {
|
|
||||||
if (!ctx) {
|
|
||||||
sc_log_error("Attempted to add file watcher for '%s' with null context\n",
|
sc_log_error("Attempted to add file watcher for '%s' with null context\n",
|
||||||
filename);
|
filename);
|
||||||
return HILOAD_FAIL;
|
return HILOAD_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mtx_lock(&ctx->mutex);
|
mtx_lock(&fw->mutex);
|
||||||
if (!HILOADRES(watch_path_add(&ctx->watchpaths, flags, filename))) {
|
|
||||||
mtx_unlock(&ctx->mutex);
|
if (!HILOADRES(file_watch_add(&fw->context, flags, filename))) {
|
||||||
|
mtx_unlock(&fw->mutex);
|
||||||
return HILOAD_FAIL;
|
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;
|
return HILOAD_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HiloadResult hi_file_watcher_remove(struct hiFileWatcher *ctx,
|
HiloadResult hi_file_watcher_remove(struct hiFileWatcher *fw,
|
||||||
const char *filename) {
|
const char *filename) {
|
||||||
if (!ctx) {
|
if (!fw) {
|
||||||
return HILOAD_FAIL;
|
return HILOAD_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mtx_lock(&ctx->mutex);
|
mtx_lock(&fw->mutex);
|
||||||
if (!HILOADRES(watch_path_remove(&ctx->watchpaths, filename))) {
|
if (!HILOADRES(file_watch_remove(&fw->context, filename))) {
|
||||||
mtx_unlock(&ctx->mutex);
|
mtx_unlock(&fw->mutex);
|
||||||
return HILOAD_FAIL;
|
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");
|
sc_log_info("Removed file: '%s' from watch, filename");
|
||||||
return HILOAD_OK;
|
return HILOAD_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hi_file_watcher_notify(hiFileWatcher *ctx) {
|
void hi_file_watcher_notify(hiFileWatcher *fw) {
|
||||||
if (ctx && ctx->running) {
|
if (fw && fw->running) {
|
||||||
cnd_signal(&ctx->cond);
|
cnd_signal(&fw->cond);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hi_file_watcher_destroy(hiFileWatcher *ctx) {
|
void hi_file_watcher_destroy(hiFileWatcher *fw) {
|
||||||
if (!ctx)
|
if (!fw)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (ctx->running) {
|
if (fw->running) {
|
||||||
ctx->running = false;
|
fw->running = false;
|
||||||
hi_file_watcher_notify(ctx);
|
hi_file_watcher_notify(fw);
|
||||||
thrd_join(ctx->thread, NULL);
|
thrd_join(fw->thread, NULL);
|
||||||
}
|
}
|
||||||
file_events_destroy(&ctx->events);
|
|
||||||
watch_paths_destroy(&ctx->watchpaths);
|
file_watcher_ctx_destroy(&fw->context);
|
||||||
free(ctx);
|
free(fw);
|
||||||
}
|
}
|
||||||
|
|
||||||
int file_watcher_watch(void *arg) {
|
int file_watcher_watch(void *arg) {
|
||||||
@@ -304,7 +271,7 @@ int file_watcher_watch(void *arg) {
|
|||||||
while (ctx->running) {
|
while (ctx->running) {
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
|
|
||||||
n = read(ctx->watchpaths.fd, buf, sizeof(buf));
|
n = read(ctx->context.fd, buf, sizeof(buf));
|
||||||
if (n == -1 && errno != EAGAIN) {
|
if (n == -1 && errno != EAGAIN) {
|
||||||
sc_log_error("Failed to read inotify: %s. Exiting thread.\n",
|
sc_log_error("Failed to read inotify: %s. Exiting thread.\n",
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
@@ -316,8 +283,13 @@ int file_watcher_watch(void *arg) {
|
|||||||
ptr += sizeof(struct inotify_event) + event->len) {
|
ptr += sizeof(struct inotify_event) + event->len) {
|
||||||
|
|
||||||
event = (const struct inotify_event *)ptr;
|
event = (const struct inotify_event *)ptr;
|
||||||
hiFileEvent e = create_file_event(event);
|
hiFileEvent e = file_event_create(event, &ctx->context);
|
||||||
sc_array_add(&ctx->events.events, e);
|
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
|
continue; // read again without waiting
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
#ifndef HI_FILEWATCHER_H_
|
#ifndef HI_FILEWATCHER_H_
|
||||||
#define HI_FILEWATCHER_H_
|
#define HI_FILEWATCHER_H_
|
||||||
|
|
||||||
|
#include "filewatch_type.h"
|
||||||
#include "types.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 {
|
typedef struct {
|
||||||
const char *watcher; // Path given to the corresponding wd. Not owning.
|
const char *pathname; // Pathname given with the `hi_file_watcher_add` call.
|
||||||
const char *file; // Owning, should be freed by the event handler by calling
|
// Do not free.
|
||||||
// `hi_file_event_destroy`)
|
HiFileWatchType type;
|
||||||
HiFileEventType type;
|
|
||||||
} hiFileEvent;
|
} hiFileEvent;
|
||||||
|
|
||||||
struct hiFileWatcher;
|
struct hiFileWatcher;
|
||||||
@@ -60,18 +58,10 @@ HiloadResult hi_file_watcher_add(struct hiFileWatcher *context,
|
|||||||
*/
|
*/
|
||||||
void hi_file_watcher_notify(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
|
* Pop an event from event stack. Call `hi_file_event_destroy` when done with
|
||||||
* it.
|
* it.
|
||||||
*/
|
*/
|
||||||
hiFileEvent hi_file_event_pop(struct hiFileWatcher *ctx);
|
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_
|
#endif // HI_FILEWATCHER_H_
|
||||||
|
|||||||
107
src/hiload.c
107
src/hiload.c
@@ -2,11 +2,12 @@
|
|||||||
|
|
||||||
#include "array/sc_array.h"
|
#include "array/sc_array.h"
|
||||||
#include "filewatcher/filewatcher.h"
|
#include "filewatcher/filewatcher.h"
|
||||||
|
#include "logger/logger.h"
|
||||||
|
#include "logger/sc_log.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
#include "string/string.h"
|
#include "string/string.h"
|
||||||
#include "symbols.h"
|
#include "symbols.h"
|
||||||
#include "types.h"
|
#include "types.h"
|
||||||
#include "logger/logger.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
@@ -31,56 +32,62 @@ typedef struct {
|
|||||||
struct sc_array_memreg memory_regions;
|
struct sc_array_memreg memory_regions;
|
||||||
struct sc_array_str enabled_modules;
|
struct sc_array_str enabled_modules;
|
||||||
struct hiFileWatcher *filewatcher;
|
struct hiFileWatcher *filewatcher;
|
||||||
|
ModuleInfos *loaded_modules;
|
||||||
} HiloadContext;
|
} HiloadContext;
|
||||||
|
|
||||||
static HiloadContext context = {0};
|
static HiloadContext context = {0};
|
||||||
static ModuleInfos *module_infos = 0;
|
|
||||||
|
|
||||||
// Callback function for dl_iterate_phdr
|
// Callback function for dl_iterate_phdr
|
||||||
static int gather_module_infos_callback(struct dl_phdr_info *info, size_t size,
|
static int gather_module_infos_callback(struct dl_phdr_info *info, size_t size,
|
||||||
void *data) {
|
void *data) {
|
||||||
|
|
||||||
ModuleInfos *infos = (ModuleInfos *)data;
|
ModuleInfos *mod_infos = (ModuleInfos *)data;
|
||||||
|
|
||||||
// Store the module name
|
// Store the module name
|
||||||
const char *modname = info->dlpi_name;
|
const char *modname = info->dlpi_name;
|
||||||
sc_array_add(&infos->names, strdup(modname));
|
sc_array_add(&mod_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_log_info("Processing: '%s'\n", info->dlpi_name);
|
sc_log_info("Processing: '%s'\n", info->dlpi_name);
|
||||||
|
|
||||||
// Try to get the handle
|
// Try to get the handle
|
||||||
void *handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD);
|
void *handle = dlopen(info->dlpi_name, RTLD_LAZY | RTLD_NOLOAD);
|
||||||
assert(handle);
|
assert(handle);
|
||||||
infos->count++;
|
mod_infos->count++;
|
||||||
|
|
||||||
sc_array_add(&infos->handles, handle);
|
sc_array_add(&mod_infos->handles, handle);
|
||||||
sc_array_add(&infos->addresses, info->dlpi_addr);
|
sc_array_add(&mod_infos->addresses, info->dlpi_addr);
|
||||||
sc_log_debug(" size: %u\n", size);
|
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_addr: %p\n", info->dlpi_addr);
|
||||||
sc_log_debug(" dlpi_tls_modid: %zu\n", info->dlpi_tls_modid);
|
sc_log_debug(" dlpi_tls_modid: %zu\n", info->dlpi_tls_modid);
|
||||||
sc_log_debug(" dlpi_tls_data: %p\n", info->dlpi_tls_data);
|
sc_log_debug(" dlpi_tls_data: %p\n", info->dlpi_tls_data);
|
||||||
|
|
||||||
Dl_info dl_info = {0};
|
Dl_info dl_info = {0};
|
||||||
if (!dladdr((void *)sc_array_last(&infos->addresses), &dl_info)) {
|
if (dladdr((void *)sc_array_last(&mod_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);
|
|
||||||
|
|
||||||
SymbolInfos symbol_info = {0};
|
// Check if it's in the provided watchlist and add a watcher
|
||||||
sc_array_add(&infos->symbols, symbol_info);
|
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
|
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) {
|
ReloadResult hi_reload_solib(const char *module_name) {
|
||||||
assert(module_infos);
|
|
||||||
|
|
||||||
void *new_handle = NULL;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hi_print_module_infos() {
|
void hi_print_loaded_modules(void) {
|
||||||
assert(module_infos);
|
|
||||||
|
|
||||||
const ModuleInfos *modules = module_infos;
|
sc_log_info("Loaded module info:\n");
|
||||||
if (!modules) {
|
sc_log_info("-------------------\n");
|
||||||
|
|
||||||
|
const ModuleInfos *mod_infos = context.loaded_modules;
|
||||||
|
if (!mod_infos) {
|
||||||
sc_log_error("No module information available.\n");
|
sc_log_error("No module information available.\n");
|
||||||
return;
|
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_info("'%s'\n", sc_array_at(&mod_infos->names, i));
|
||||||
sc_log_debug(" handle: %p\n", sc_array_at(&modules->handles, i));
|
sc_log_info(" handle: %p\n", sc_array_at(&mod_infos->handles, i));
|
||||||
sc_log_debug(" address: %p\n", sc_array_at(&modules->addresses, i));
|
sc_log_info(" address: %p\n", sc_array_at(&mod_infos->addresses, i));
|
||||||
|
|
||||||
const SymbolInfos *symbols = &sc_array_at(&modules->symbols, i);
|
// 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++) {
|
for (size_t j = 0; j < sc_array_size(&symbols->names); j++) {
|
||||||
const void *addr = sc_array_at(&symbols->addresses, j);
|
const void *addr = sc_array_at(&symbols->addresses, j);
|
||||||
const char *name = sc_array_at(&symbols->names, j);
|
const char *name = sc_array_at(&symbols->names, j);
|
||||||
sc_log_debug(" %p: %s\n", addr, name);
|
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) {
|
int hi_init(unsigned n, const char **enabled_modules) {
|
||||||
assert(!module_infos);
|
|
||||||
|
|
||||||
if (sc_log_init() != 0) {
|
if (sc_log_init() != 0) {
|
||||||
fprintf(stderr, "Failed to init logger.\n");
|
fprintf(stderr, "Failed to init logger.\n");
|
||||||
@@ -253,17 +266,19 @@ int hi_init(unsigned n, const char **enabled_modules) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Override existing
|
// Override existing
|
||||||
if (module_infos) {
|
if (context.loaded_modules) {
|
||||||
module_infos_free(module_infos);
|
module_infos_free(context.loaded_modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
module_infos = infos;
|
context.loaded_modules = infos;
|
||||||
hi_print_module_infos();
|
hi_print_loaded_modules();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hi_deinit() {
|
void hi_deinit() {
|
||||||
module_infos_free(module_infos);
|
module_infos_free(context.loaded_modules);
|
||||||
|
hi_file_watcher_destroy(context.filewatcher);
|
||||||
sc_log_term();
|
sc_log_term();
|
||||||
|
memset(&context, 0, sizeof(context));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ HiloadResult read_memory_maps_self(struct sc_array_memreg *regions) {
|
|||||||
char pathname[256];
|
char pathname[256];
|
||||||
|
|
||||||
// clang-format off
|
// 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
|
// 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",
|
int result = sscanf(line, "%lx-%lx %4s %lx %x:%x %lu %255s",
|
||||||
®.region_start,
|
®.region_start,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ int main(int argc, char *argv[]) {
|
|||||||
printf("minimal_lib::getNewValue address: %p\n", minimal_lib::getNewValue);
|
printf("minimal_lib::getNewValue address: %p\n", minimal_lib::getNewValue);
|
||||||
printf("minimal_lib::otherValue address: %p\n", &minimal_lib::otherValue);
|
printf("minimal_lib::otherValue address: %p\n", &minimal_lib::otherValue);
|
||||||
printf("==========================\n");
|
printf("==========================\n");
|
||||||
printf("\n");
|
printf("");
|
||||||
|
|
||||||
modified = minimal_lib::getNewValue(5);
|
modified = minimal_lib::getNewValue(5);
|
||||||
printf("getModified(5): %d\n", modified);
|
printf("getModified(5): %d\n", modified);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
namespace minimal_lib {
|
namespace minimal_lib {
|
||||||
|
|
||||||
int getNewValue(int x) {
|
int getNewValue(int x) {
|
||||||
static int value = 0;
|
static int value = 1;
|
||||||
value = value + x;
|
value = value + x;
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user