add threaded filewatcher
This commit is contained in:
@@ -44,18 +44,20 @@ add_library(hiload SHARED
|
||||
src/symbols.c
|
||||
src/files.c
|
||||
src/memory.c
|
||||
src/string/string.c
|
||||
src/filewatcher/filewatcher.c
|
||||
|
||||
# dependencies
|
||||
src/string/hi_string.c
|
||||
src/logger/sc_log.c
|
||||
)
|
||||
|
||||
set_property(TARGET hiload PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
target_link_libraries(hiload dl)
|
||||
|
||||
# Specify the public headers location
|
||||
target_include_directories(hiload PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # During build
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src> # During build
|
||||
$<INSTALL_INTERFACE:include> # When installed
|
||||
)
|
||||
|
||||
|
||||
@@ -16,18 +16,28 @@ typedef enum {
|
||||
HI_RELOAD_OPEN_ERROR
|
||||
} ReloadResult;
|
||||
|
||||
/* Initialiez the module. Must be called before anything else */
|
||||
int hi_init();
|
||||
/**
|
||||
* Initialiez the module. Must be called before anything else
|
||||
*/
|
||||
int hi_init(unsigned n, const char **enabled_modules);
|
||||
|
||||
/* Frees memory and cleans up */
|
||||
/**
|
||||
* Frees memory and cleans up
|
||||
*/
|
||||
void hi_deinit();
|
||||
|
||||
/* Print a random assortment of information from current state */
|
||||
/**
|
||||
* Print a random assortment of information from current state
|
||||
*/
|
||||
void hi_print_module_infos();
|
||||
|
||||
/* 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
|
||||
*/
|
||||
ReloadResult hi_reload_solib(const char *module_name);
|
||||
|
||||
|
||||
|
||||
#pragma GCC visibility pop
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
13
src/common.h
Normal file
13
src/common.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef COMMON_H_
|
||||
#define COMMON_H_
|
||||
|
||||
#include "types.h"
|
||||
|
||||
static inline
|
||||
u32 has_mask(u32 flags, u32 mask) {
|
||||
return flags & mask;
|
||||
}
|
||||
|
||||
#define ARRLEN(A) (sizeof((A))/(sizeof ((A)[0])))
|
||||
|
||||
#endif // COMMON_H_
|
||||
@@ -1,10 +0,0 @@
|
||||
#ifndef COMPILATION_H_
|
||||
#define COMPILATION_H_
|
||||
|
||||
typedef struct {
|
||||
const char *directory;
|
||||
const char *file;
|
||||
const char *command;
|
||||
} CompileCommand;
|
||||
|
||||
#endif // COMPILATION_H_
|
||||
@@ -1,11 +1,9 @@
|
||||
#include "files.h"
|
||||
|
||||
#include "logger.h"
|
||||
#include "types.h"
|
||||
#include "string/hi_string.h"
|
||||
|
||||
const char* hi_file_to_str_dyn(const char *filename) {
|
||||
#include "string/string.h"
|
||||
#include "logger/logger.h"
|
||||
|
||||
const char *hi_file_to_str_dyn(const char *filename) {
|
||||
size_t n = 0;
|
||||
const char *s = hi_string_from_file_dyn(filename, 0, 0);
|
||||
if (!s) {
|
||||
@@ -14,3 +12,4 @@ const char* hi_file_to_str_dyn(const char *filename) {
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
10
src/files.h
10
src/files.h
@@ -3,6 +3,14 @@
|
||||
|
||||
#include "types.h"
|
||||
|
||||
const char* hi_file_to_str_dyn(const char *filename);
|
||||
/**
|
||||
* Read file dynamically to a string
|
||||
*
|
||||
* Reads the file until no more bytes are read. Can be used for virtual files
|
||||
* with no size. The realistic optimum case contains two allocations, one for
|
||||
* the initial memory and a reallocation to match the string size.
|
||||
*/
|
||||
const char *hi_file_to_str_dyn(const char *filename);
|
||||
|
||||
|
||||
#endif // FILES_H_
|
||||
|
||||
6
src/filewatcher/fileevent.h
Normal file
6
src/filewatcher/fileevent.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef FILEEVENT_H_
|
||||
#define FILEEVENT_H_
|
||||
|
||||
|
||||
|
||||
#endif // FILEEVENT_H_
|
||||
348
src/filewatcher/filewatcher.c
Normal file
348
src/filewatcher/filewatcher.c
Normal file
@@ -0,0 +1,348 @@
|
||||
#include "filewatcher.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdatomic.h>
|
||||
#include <string.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/select.h>
|
||||
#include <threads.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "array/array.h"
|
||||
#include "common.h"
|
||||
#include "logger/logger.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;
|
||||
|
||||
typedef struct hiFileWatcherContext {
|
||||
thrd_t thread;
|
||||
mtx_t mutex;
|
||||
cnd_t cond;
|
||||
int running;
|
||||
struct hiWatchPaths watchpaths;
|
||||
struct hiFileEvents events;
|
||||
} hiFileWatcherContext;
|
||||
|
||||
/*
|
||||
* File Events
|
||||
*/
|
||||
|
||||
static hiFileEvent create_file_event(const struct inotify_event *event) {
|
||||
hiFileEvent hievent = {0};
|
||||
|
||||
if (!event) {
|
||||
return hievent;
|
||||
}
|
||||
|
||||
if (event->len)
|
||||
hievent.file = strdup(event->name);
|
||||
|
||||
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;
|
||||
}
|
||||
return hievent;
|
||||
}
|
||||
|
||||
static hiFileEvent copy_file_event(const hiFileEvent *event) {
|
||||
hiFileEvent cpy = *event;
|
||||
// Copy the file string, so this can be destroyed without destroying the
|
||||
// original
|
||||
cpy.file = strdup(event->file);
|
||||
return cpy;
|
||||
}
|
||||
|
||||
void hi_file_event_free(hiFileEvent *event) {
|
||||
if (event) {
|
||||
free((void *)event->file);
|
||||
}
|
||||
}
|
||||
|
||||
void hi_file_event_destroy(hiFileEvent *event) {
|
||||
if (event) {
|
||||
free((void *)event->file);
|
||||
memset(event, 0, sizeof(*event));
|
||||
}
|
||||
}
|
||||
|
||||
hiFileEvent hi_file_event_pop(hiFileWatcherContext *ctx) {
|
||||
mtx_lock(&ctx->mutex);
|
||||
hiFileEvent *last = &sc_array_last(&ctx->events.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);
|
||||
|
||||
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 (int 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 (int 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 (int 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
|
||||
*/
|
||||
|
||||
// Declare the thread func
|
||||
int file_watcher_watch(void *arg);
|
||||
|
||||
hiFileWatcherContext *hi_file_watcher_create() {
|
||||
|
||||
// Allocate context
|
||||
hiFileWatcherContext *context = malloc(sizeof(hiFileWatcherContext));
|
||||
memset(context, 0, sizeof(hiFileWatcherContext));
|
||||
|
||||
if (context) {
|
||||
if (!HILOADRES(watch_paths_init(&context->watchpaths))) {
|
||||
sc_log_error("Failed to initialize watch paths\n");
|
||||
if (context->watchpaths.fd == -1) {
|
||||
sc_log_error("Couldn't initialize inotify\n");
|
||||
}
|
||||
free(context);
|
||||
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;
|
||||
}
|
||||
free(context);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HiloadResult hi_file_watcher_add(struct hiFileWatcherContext *ctx,
|
||||
const char *filename, u32 flags) {
|
||||
if (!ctx) {
|
||||
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);
|
||||
return HILOAD_FAIL;
|
||||
}
|
||||
mtx_unlock(&ctx->mutex);
|
||||
|
||||
sc_log_info("Watching file: %s\n", filename);
|
||||
return HILOAD_OK;
|
||||
}
|
||||
|
||||
HiloadResult hi_file_watcher_remove(struct hiFileWatcherContext *ctx,
|
||||
const char *filename) {
|
||||
if (!ctx) {
|
||||
return HILOAD_FAIL;
|
||||
}
|
||||
|
||||
mtx_lock(&ctx->mutex);
|
||||
if (!HILOADRES(watch_path_remove(&ctx->watchpaths, filename))) {
|
||||
mtx_unlock(&ctx->mutex);
|
||||
return HILOAD_FAIL;
|
||||
}
|
||||
|
||||
mtx_unlock(&ctx->mutex);
|
||||
sc_log_info("Removed file: '%s' from watch, filename");
|
||||
return HILOAD_OK;
|
||||
}
|
||||
|
||||
void hi_file_watcher_notify(hiFileWatcherContext *ctx) {
|
||||
if (ctx && ctx->running) {
|
||||
cnd_signal(&ctx->cond);
|
||||
}
|
||||
}
|
||||
|
||||
void hi_file_watcher_destroy(hiFileWatcherContext *ctx) {
|
||||
if (!ctx)
|
||||
return;
|
||||
|
||||
if (ctx->running) {
|
||||
ctx->running = false;
|
||||
hi_file_watcher_notify(ctx);
|
||||
thrd_join(ctx->thread, NULL);
|
||||
}
|
||||
file_events_destroy(&ctx->events);
|
||||
watch_paths_destroy(&ctx->watchpaths);
|
||||
free(ctx);
|
||||
}
|
||||
|
||||
int file_watcher_watch(void *arg) {
|
||||
hiFileWatcherContext *ctx = (hiFileWatcherContext *)arg;
|
||||
sc_log_set_thread_name("File Watcher");
|
||||
|
||||
char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event))));
|
||||
const struct inotify_event *event = NULL;
|
||||
|
||||
mtx_lock(&ctx->mutex);
|
||||
|
||||
while (ctx->running) {
|
||||
ssize_t n;
|
||||
|
||||
n = read(ctx->watchpaths.fd, buf, sizeof(buf));
|
||||
if (n == -1 && errno != EAGAIN) {
|
||||
sc_log_error("Failed to read inotify: %s. Exiting thread.\n",
|
||||
strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (n > 0) {
|
||||
for (char *ptr = buf; ptr < buf + n;
|
||||
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);
|
||||
}
|
||||
continue; // read again without waiting
|
||||
}
|
||||
|
||||
// wait for 100ms
|
||||
#define SEC_IN_NS 1000000000
|
||||
struct timespec timeout;
|
||||
i64 wait_time_ns = 1000 * 1000 * 100;
|
||||
timespec_get(&timeout, TIME_UTC);
|
||||
timeout.tv_nsec += wait_time_ns;
|
||||
if (timeout.tv_nsec > SEC_IN_NS) {
|
||||
timeout.tv_sec += timeout.tv_nsec / SEC_IN_NS;
|
||||
timeout.tv_nsec = timeout.tv_nsec % SEC_IN_NS;
|
||||
}
|
||||
#undef SEC_IN_NS
|
||||
cnd_timedwait(&ctx->cond, &ctx->mutex, &timeout);
|
||||
}
|
||||
|
||||
mtx_unlock(&ctx->mutex);
|
||||
return 0;
|
||||
}
|
||||
77
src/filewatcher/filewatcher.h
Normal file
77
src/filewatcher/filewatcher.h
Normal file
@@ -0,0 +1,77 @@
|
||||
#ifndef HI_FILEWATCHER_H_
|
||||
#define HI_FILEWATCHER_H_
|
||||
|
||||
#include "types.h"
|
||||
|
||||
/**
|
||||
* File event watching related functionality and types
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
} hiFileEvent;
|
||||
|
||||
struct hiFileWatcherContext;
|
||||
/**
|
||||
* FileEvents is a thread safe list of events.
|
||||
*
|
||||
* It is implemented as a stack, so the events will be popped
|
||||
* in reverse chronological order.
|
||||
* */
|
||||
struct hiFileEvents;
|
||||
|
||||
/**
|
||||
* Create watcher and necessary data to run it.
|
||||
*
|
||||
* Will start the watcher thread immediately.
|
||||
*/
|
||||
struct hiFileWatcherContext *hi_file_watcher_create();
|
||||
/**
|
||||
* Destroy a previously created file watcher context.
|
||||
*
|
||||
* Will join the thread and destroy event stack.
|
||||
*/
|
||||
void hi_file_watcher_destroy(struct hiFileWatcherContext *context);
|
||||
/**
|
||||
* Add a file to the watch list of a file watcher.
|
||||
*
|
||||
* @param context Previously created watcher context
|
||||
* @param pathname Absolute path to the file or directory
|
||||
* @param flags The events that will be watched for
|
||||
*/
|
||||
HiloadResult hi_file_watcher_add(struct hiFileWatcherContext *context,
|
||||
const char *pathname, u32 flags);
|
||||
/**
|
||||
* Can be used to poke file watcher thread to make sure it empties
|
||||
* events before doing something, e.g. for shutting it down.
|
||||
*/
|
||||
void hi_file_watcher_notify(struct hiFileWatcherContext *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 hiFileWatcherContext *ctx);
|
||||
/**
|
||||
* Free and zero held memory. Call after pop.
|
||||
*/
|
||||
void hi_file_event_destroy(hiFileEvent *event);
|
||||
|
||||
#endif // HI_FILEWATCHER_H_
|
||||
88
src/hiload.c
88
src/hiload.c
@@ -1,27 +1,37 @@
|
||||
#include "hiload/hiload.h"
|
||||
|
||||
#include "logger.h"
|
||||
#include "array/sc_array.h"
|
||||
#include "files.h"
|
||||
#include "filewatcher/filewatcher.h"
|
||||
#include "logger/logger.h"
|
||||
#include "memory.h"
|
||||
#include "string/string.h"
|
||||
#include "symbols.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <dlfcn.h>
|
||||
#include <libdwarf/libdwarf.h>
|
||||
#include <libelf.h>
|
||||
#include <link.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
sc_array_def(uptr, uptr);
|
||||
|
||||
typedef struct {
|
||||
struct sc_array_str names; // Array of library names
|
||||
struct sc_array_ptr handles; // Array of library handles
|
||||
struct sc_array_uptr addresses; // Array of library base addresses
|
||||
struct sc_array_syms symbols; // Symbol info for modules
|
||||
size_t count; // Number of modules
|
||||
} ModuleInfos;
|
||||
|
||||
typedef struct {
|
||||
struct sc_array_memreg memory_regions;
|
||||
struct sc_array_str enabled_modules;
|
||||
struct hiFileWatcherContext *filewatcher;
|
||||
} HiloadContext;
|
||||
|
||||
static HiloadContext context = {0};
|
||||
@@ -44,14 +54,24 @@ static inline int if_load_symbols_for_module(struct dl_phdr_info *info) {
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
ModuleInfos *infos = (ModuleInfos *)data;
|
||||
|
||||
// Store the module name
|
||||
sc_array_add(&infos->names, strdup(info->dlpi_name));
|
||||
const char *modname = info->dlpi_name;
|
||||
sc_array_add(&infos->names, strdup(modname));
|
||||
for (int 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);
|
||||
|
||||
@@ -61,20 +81,28 @@ int gather_module_infos_callback(struct dl_phdr_info *info, size_t size,
|
||||
infos->count++;
|
||||
|
||||
sc_array_add(&infos->handles, handle);
|
||||
sc_array_add(&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(" 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);
|
||||
|
||||
SymbolInfos symbol_info = {0};
|
||||
sc_array_add(&infos->symbols, symbol_info);
|
||||
|
||||
return 0; // Continue iteration
|
||||
}
|
||||
|
||||
static
|
||||
void free_module_infos(ModuleInfos *modules) {
|
||||
static void module_infos_free(ModuleInfos *modules) {
|
||||
if (!modules)
|
||||
return;
|
||||
|
||||
@@ -94,8 +122,7 @@ void free_module_infos(ModuleInfos *modules) {
|
||||
free(modules);
|
||||
}
|
||||
|
||||
static
|
||||
ModuleInfos *gather_module_infos(void) {
|
||||
static ModuleInfos *gather_module_infos(void) {
|
||||
ModuleInfos *result = NULL;
|
||||
|
||||
// Allocate the result structure
|
||||
@@ -107,15 +134,14 @@ ModuleInfos *gather_module_infos(void) {
|
||||
// Iterate over all loaded shared objects
|
||||
if (dl_iterate_phdr(gather_module_infos_callback, result) != 0) {
|
||||
// Error occurred in callback
|
||||
free_module_infos(result);
|
||||
module_infos_free(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
ReloadResult reload_solib(ModuleInfos *modules, const char *filename,
|
||||
static ReloadResult reload_solib(ModuleInfos *modules, const char *filename,
|
||||
void **updated_handle) {
|
||||
if (!modules || !filename) {
|
||||
return HI_RELOAD_NOT_FOUND;
|
||||
@@ -129,21 +155,11 @@ ReloadResult reload_solib(ModuleInfos *modules, const char *filename,
|
||||
// Check if this is the module we're looking for
|
||||
const char *pathname = sc_array_at(&modules->names, i);
|
||||
|
||||
if (pathname && strcmp(pathname, filename) == 0) {
|
||||
if (hi_path_has_filename(pathname, filename)) {
|
||||
found = 1;
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
|
||||
// Also check if the filename is at the end of the path
|
||||
if (pathname) {
|
||||
const char *basename = strrchr(pathname, '/');
|
||||
if (basename && strcmp(basename + 1, filename) == 0) {
|
||||
found = 1;
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
@@ -164,7 +180,7 @@ ReloadResult reload_solib(ModuleInfos *modules, const char *filename,
|
||||
}
|
||||
}
|
||||
|
||||
// Open the module again with RTLD_NOW
|
||||
// Open the module with RTLD_NOW
|
||||
void *new_handle = dlopen(fullpath, RTLD_NOW);
|
||||
if (!new_handle) {
|
||||
sc_log_error("Error reloading module: %s\n", dlerror());
|
||||
@@ -200,15 +216,13 @@ void hi_print_module_infos() {
|
||||
return;
|
||||
}
|
||||
|
||||
sc_log_info("Found %zu loaded modules:\n\n", modules->count);
|
||||
sc_log_info("Found %zu loaded modules:\n", modules->count);
|
||||
|
||||
for (size_t i = 0; i < modules->count; i++) {
|
||||
// Get handle information where possible
|
||||
Dl_info info = {0};
|
||||
int has_info = 0;
|
||||
|
||||
sc_log_debug("'%s': %p\n", sc_array_at(&modules->names, i),
|
||||
sc_array_at(&modules->handles, 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));
|
||||
|
||||
const SymbolInfos *symbols = &sc_array_at(&modules->symbols, i);
|
||||
for (int j = 0; j < sc_array_size(&symbols->names); j++) {
|
||||
@@ -221,7 +235,7 @@ void hi_print_module_infos() {
|
||||
}
|
||||
}
|
||||
|
||||
int hi_init() {
|
||||
int hi_init(unsigned n, const char *enabled_modules[n]) {
|
||||
assert(!module_infos);
|
||||
|
||||
if (sc_log_init() != 0) {
|
||||
@@ -229,6 +243,16 @@ int hi_init() {
|
||||
return 1;
|
||||
}
|
||||
sc_log_set_level("DEBUG");
|
||||
sc_log_set_thread_name("Main");
|
||||
|
||||
// Start the filewatcher
|
||||
context.filewatcher = hi_file_watcher_create();
|
||||
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
const char *module_name = enabled_modules[i];
|
||||
sc_log_info("Enabling module: '%s'\n", module_name);
|
||||
sc_array_add(&context.enabled_modules, module_name);
|
||||
}
|
||||
|
||||
if (read_memory_maps_self(&context.memory_regions) != HILOAD_OK) {
|
||||
sc_log_error("Could not populate program memory maps.\n");
|
||||
@@ -243,7 +267,7 @@ int hi_init() {
|
||||
|
||||
// Override existing
|
||||
if (module_infos) {
|
||||
free_module_infos(module_infos);
|
||||
module_infos_free(module_infos);
|
||||
}
|
||||
|
||||
module_infos = infos;
|
||||
@@ -253,6 +277,6 @@ int hi_init() {
|
||||
}
|
||||
|
||||
void hi_deinit() {
|
||||
free_module_infos(module_infos);
|
||||
module_infos_free(module_infos);
|
||||
sc_log_term();
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include "array/sc_array.h"
|
||||
#include "files.h"
|
||||
#include "logger.h"
|
||||
#include "logger/logger.h"
|
||||
#include "types.h"
|
||||
|
||||
static inline int ptr_in_range(uptr ptr, uptr start, uptr end) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#ifndef MEMORY_H_
|
||||
#define MEMORY_H_
|
||||
|
||||
#include "array.h"
|
||||
#include "array/array.h"
|
||||
#include "types.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
#ifndef HI_STRING_H_
|
||||
#define HI_STRING_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/***
|
||||
* @brief Copy file content to a null terminated string, allocating memory while reading.
|
||||
*
|
||||
* This doesn't assume it can read file size, so it allocates memory in chunks
|
||||
* (default 4096 bytes) and keeps reading until 0 bytes is read. If nmax is non-zero, instead that amount of bytes is allocated
|
||||
* and that is read.
|
||||
*
|
||||
* In either case, the string is reallocated to match the length before returning.
|
||||
* @param filename
|
||||
* @param nread if not null, this will have the total amount read in bytes
|
||||
* @param nmax if not 0, this amount of memory in bytes is read ant allocated
|
||||
***/
|
||||
const char *hi_string_from_file_dyn(const char *filename, size_t *nread,
|
||||
size_t nmax);
|
||||
|
||||
#endif // HI_STRING_H_
|
||||
@@ -1,8 +1,23 @@
|
||||
#include "hi_string.h"
|
||||
#include "string.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int hi_path_has_filename(const char *path, const char *filename) {
|
||||
|
||||
const char *compared_filename = path;
|
||||
const char *basename = strrchr(path, '/');
|
||||
|
||||
if (basename)
|
||||
compared_filename = basename + 1;
|
||||
|
||||
if (strcmp(compared_filename, filename) == 0) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *hi_string_from_file_dyn(const char *filename, size_t *nread,
|
||||
size_t nmax) {
|
||||
31
src/string/string.h
Normal file
31
src/string/string.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#ifndef HI_STRING_H_
|
||||
#define HI_STRING_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* @brief Copy file content to a null terminated string, allocating memory while
|
||||
* reading.
|
||||
*
|
||||
* This doesn't assume it can read file size, so it allocates memory in chunks
|
||||
* (default 4096 bytes) and keeps reading until 0 bytes is read. If nmax is
|
||||
* non-zero, instead that amount of bytes is allocated and that is read.
|
||||
*
|
||||
* In either case, the string is reallocated to match the length before
|
||||
* returning.
|
||||
* @param filename
|
||||
* @param nread if not null, this will have the total amount bytes read
|
||||
* @param nmax if not 0, this amount of memory in bytes is read and used as
|
||||
* initial allocation
|
||||
*/
|
||||
const char *hi_string_from_file_dyn(const char *filename, size_t *nread,
|
||||
size_t nmax);
|
||||
|
||||
/**
|
||||
* Find if filename exists at the end of path.
|
||||
*
|
||||
* @return 0 if not found, positive if found
|
||||
*/
|
||||
int hi_path_has_filename(const char *path, const char *filename);
|
||||
|
||||
#endif // HI_STRING_H_
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "symbols.h"
|
||||
|
||||
#include "logger.h"
|
||||
#include "logger/logger.h"
|
||||
#include "memory.h"
|
||||
#include "types.h"
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@ typedef double f64;
|
||||
typedef uintptr_t uptr;
|
||||
typedef ptrdiff_t ptrdiff;
|
||||
|
||||
typedef enum { HILOAD_OK = 0, HILOAD_FAIL } HiloadResult;
|
||||
typedef enum { HILOAD_FAIL = 0, HILOAD_OK } HiloadResult;
|
||||
#define HILOADRES(res) ((res) == HILOAD_OK)
|
||||
|
||||
|
||||
#endif // TYPES_H_
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
hi_init();
|
||||
const char *reloadable_modules[] = {"", "libmini.so"};
|
||||
hi_init(2, reloadable_modules);
|
||||
|
||||
int modified = -1;
|
||||
while (modified != 0) {
|
||||
|
||||
Reference in New Issue
Block a user