Remove hi_ prefixes from internal code. Add documentation.
This commit is contained in:
2733
doc/Doxyfile
Normal file
2733
doc/Doxyfile
Normal file
File diff suppressed because it is too large
Load Diff
18
src/files.c
18
src/files.c
@@ -11,9 +11,9 @@
|
|||||||
#define HI_FILE_BUFFER_SIZE 4096
|
#define HI_FILE_BUFFER_SIZE 4096
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char *hi_file_to_str_dyn(const char *filename) {
|
char *file_to_str_dyn(const char *filename) {
|
||||||
|
|
||||||
char *s = hi_string_from_file_dyn(filename, 0, 0);
|
char *s = hi_str_from_file(filename, 0, 0);
|
||||||
if (!s) {
|
if (!s) {
|
||||||
sc_log_error("Failed to read file: %s\n", filename);
|
sc_log_error("Failed to read file: %s\n", filename);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -21,14 +21,14 @@ char *hi_file_to_str_dyn(const char *filename) {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
HiFileType hi_file_interpret_as_type(const char *path) {
|
static FileType file_interpret_as_type(const char *path) {
|
||||||
const char *pathname_end = path + strlen(path);
|
const char *pathname_end = path + strlen(path);
|
||||||
char last_char = *(pathname_end - 1);
|
char last_char = *(pathname_end - 1);
|
||||||
bool as_dir = (last_char == '/') || (last_char == '\\');
|
bool as_dir = (last_char == '/') || (last_char == '\\');
|
||||||
return as_dir ? HI_FILE_TYPE_DIR : HI_FILE_TYPE_FILE;
|
return as_dir ? HI_FILE_TYPE_DIR : HI_FILE_TYPE_FILE;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *hi_file_name_from_path(const char *path) {
|
const char *file_name_from_path(const char *path) {
|
||||||
const char *filename = path;
|
const char *filename = path;
|
||||||
|
|
||||||
// Find the last directory separator
|
// Find the last directory separator
|
||||||
@@ -45,7 +45,7 @@ const char *hi_file_name_from_path(const char *path) {
|
|||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
HiResult hi_file_copy(const char *srcname, const char *destination) {
|
HiResult file_copy(const char *srcname, const char *destination) {
|
||||||
|
|
||||||
HiResult ret = HI_FAIL;
|
HiResult ret = HI_FAIL;
|
||||||
|
|
||||||
@@ -59,10 +59,10 @@ HiResult hi_file_copy(const char *srcname, const char *destination) {
|
|||||||
|
|
||||||
const char *dstname = destination;
|
const char *dstname = destination;
|
||||||
|
|
||||||
HiFileType dst_type = hi_file_interpret_as_type(destination);
|
FileType dst_type = file_interpret_as_type(destination);
|
||||||
if (dst_type == HI_FILE_TYPE_DIR) {
|
if (dst_type == HI_FILE_TYPE_DIR) {
|
||||||
hi_strncat_buf(sizeof(buf), buf, destination,
|
hi_str_concat_buf(sizeof(buf), buf, destination,
|
||||||
hi_file_name_from_path(srcname));
|
file_name_from_path(srcname));
|
||||||
dstname = buf;
|
dstname = buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ cleanup:
|
|||||||
*
|
*
|
||||||
* TAG: posix
|
* TAG: posix
|
||||||
*/
|
*/
|
||||||
HiFileType hi_file_type(const char *path) {
|
FileType file_type(const char *path) {
|
||||||
struct stat path_stat;
|
struct stat path_stat;
|
||||||
int err = stat(path, &path_stat);
|
int err = stat(path, &path_stat);
|
||||||
if (err == -1) {
|
if (err == -1) {
|
||||||
|
|||||||
13
src/files.h
13
src/files.h
@@ -14,7 +14,7 @@ typedef enum {
|
|||||||
|
|
||||||
HI_FILE_TYPE_COUNT,
|
HI_FILE_TYPE_COUNT,
|
||||||
HI_FILE_TYPE_NONE,
|
HI_FILE_TYPE_NONE,
|
||||||
} HiFileType;
|
} FileType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read file dynamically to a string
|
* Read file dynamically to a string
|
||||||
@@ -24,18 +24,19 @@ typedef enum {
|
|||||||
* the initial memory and a reallocation to match the string size.
|
* the initial memory and a reallocation to match the string size.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
char *hi_file_to_str_dyn(const char *filename);
|
char *file_to_str_dyn(const char *filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy file \p filename to \p dest
|
* Copy file \p filename to \p dest
|
||||||
*
|
*
|
||||||
* If \p dest has either '/' or '\' as the last character, it is interpreted as
|
* If \p dest has either '/' or '\' as the last character, it is interpreted as
|
||||||
* a directory and the file is copied to the directory with the same filename.
|
* a directory and the file is copied to the directory with the same filename as
|
||||||
|
* the source.
|
||||||
*/
|
*/
|
||||||
HiResult hi_file_copy(const char *filename, const char *dest);
|
HiResult file_copy(const char *filename, const char *dest);
|
||||||
|
|
||||||
HiFileType hi_file_type(const char *path);
|
FileType file_type(const char *path);
|
||||||
|
|
||||||
const char *hi_file_name_from_path(const char *path);
|
const char *file_name_from_path(const char *path);
|
||||||
|
|
||||||
#endif // FILES_H_
|
#endif // FILES_H_
|
||||||
|
|||||||
@@ -9,10 +9,9 @@
|
|||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* File Watch
|
* Used to pass information between some functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct WatchMaskParams {
|
struct WatchMaskParams {
|
||||||
u32 file_mask;
|
u32 file_mask;
|
||||||
u32 parent_mask;
|
u32 parent_mask;
|
||||||
@@ -46,6 +45,24 @@ static inline struct WatchMaskParams inode_watch_masks_create(u32 flags) {
|
|||||||
.parent_mask = parent_mask};
|
.parent_mask = parent_mask};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
// add one for the trailing slash
|
||||||
|
size_t length = last_separator - path + 1;
|
||||||
|
char *parent = calloc(length, sizeof(char));
|
||||||
|
strncpy(parent, path, length);
|
||||||
|
parent[length - 1] = '/';
|
||||||
|
parent[length] = '\0';
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
static void file_watch_destroy(int fd, FileWatch *fw) {
|
static void file_watch_destroy(int fd, FileWatch *fw) {
|
||||||
inotify_rm_watch(fd, fw->wd);
|
inotify_rm_watch(fd, fw->wd);
|
||||||
free((void *)fw->path);
|
free((void *)fw->path);
|
||||||
@@ -78,8 +95,8 @@ FileWatch *file_watch_find_by_wd(FileWatcherContext *ctx, int wd,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileWatch *file_watch_find_by_path(FileWatcherContext *ctx,
|
FileWatch *file_watch_find_by_path(FileWatcherContext *ctx, const char *path,
|
||||||
const char *path, size_t *index) {
|
size_t *index) {
|
||||||
for (size_t i = 0; i < sc_array_size(&ctx->watches); i++) {
|
for (size_t i = 0; i < sc_array_size(&ctx->watches); i++) {
|
||||||
FileWatch *watch = &sc_array_at(&ctx->watches, i);
|
FileWatch *watch = &sc_array_at(&ctx->watches, i);
|
||||||
if (strcmp(watch->path, path) == 0) {
|
if (strcmp(watch->path, path) == 0) {
|
||||||
@@ -92,26 +109,7 @@ FileWatch *file_watch_find_by_path(FileWatcherContext *ctx,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
HiResult file_watch_add(FileWatcherContext *ctx, u32 mask, const char *path) {
|
||||||
* 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;
|
|
||||||
|
|
||||||
// add one for the trailing slash
|
|
||||||
size_t length = last_separator - path + 1;
|
|
||||||
char *parent = calloc(length, sizeof(char));
|
|
||||||
strncpy(parent, path, length);
|
|
||||||
parent[length - 1] = '/';
|
|
||||||
parent[length] = '\0';
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
HiResult file_watch_add(FileWatcherContext *ctx, u32 mask,
|
|
||||||
const char *path) {
|
|
||||||
if (!ctx || ctx->fd == -1) {
|
if (!ctx || ctx->fd == -1) {
|
||||||
log_error("Invalid inotify context\n");
|
log_error("Invalid inotify context\n");
|
||||||
return HI_FAIL;
|
return HI_FAIL;
|
||||||
@@ -167,25 +165,26 @@ HiResult file_watch_add(FileWatcherContext *ctx, u32 mask,
|
|||||||
}
|
}
|
||||||
|
|
||||||
HiResult file_watch_remove(FileWatcherContext *ctx, const char *path) {
|
HiResult file_watch_remove(FileWatcherContext *ctx, const char *path) {
|
||||||
|
// BUG: Parent watchers are never properly cleared if all children are removed
|
||||||
|
// without removing the parent separately
|
||||||
|
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
FileWatch *watch = file_watch_find_by_path(ctx, path, &i);
|
FileWatch *watch = file_watch_find_by_path(ctx, path, &i);
|
||||||
if (watch) {
|
if (watch) {
|
||||||
|
// Destroy parent reference. Parent should only have one, but lets make
|
||||||
// Destroy parent reference. We assume it will only have one.
|
// sure.
|
||||||
char *parent = get_parent_folder(path);
|
char *parent_name = get_parent_folder(path);
|
||||||
if (parent) {
|
if (parent_name) {
|
||||||
FileWatch *pw = file_watch_find_by_path(ctx, parent, NULL);
|
FileWatch *pw = file_watch_find_by_path(ctx, parent_name, NULL);
|
||||||
if (pw) {
|
if (pw) {
|
||||||
for (size_t i = 0; i < sc_array_size(&pw->files); i++) {
|
for (size_t i = 0; i < sc_array_size(&pw->files); i++) {
|
||||||
const char *fn = sc_array_at(&pw->files, i);
|
const char *fn = sc_array_at(&pw->files, i);
|
||||||
if (fn == watch->path) {
|
if (fn == watch->path) {
|
||||||
sc_array_del(&pw->files, i);
|
sc_array_del(&pw->files, i);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
free(parent);
|
free(parent_name);
|
||||||
}
|
}
|
||||||
file_watch_destroy(ctx->fd, watch);
|
file_watch_destroy(ctx->fd, watch);
|
||||||
sc_array_del(&ctx->watches, i);
|
sc_array_del(&ctx->watches, i);
|
||||||
|
|||||||
@@ -7,15 +7,40 @@
|
|||||||
typedef struct FileWatcherContext FileWatcherContext;
|
typedef struct FileWatcherContext FileWatcherContext;
|
||||||
typedef struct FileWatch FileWatch;
|
typedef struct FileWatch FileWatch;
|
||||||
|
|
||||||
FileWatch *file_watch_find_by_wd(FileWatcherContext *ctx,
|
/**
|
||||||
int wd, size_t *index);
|
* Search for a FileWatch matching by watch descriptor.
|
||||||
FileWatch *file_watch_find_by_path(FileWatcherContext *ctx,
|
*
|
||||||
const char *path, size_t *index);
|
* @param ctx
|
||||||
|
* @param wd Watch descriptor given by inotipy
|
||||||
|
* @param index Will contain the first occurrance of a matching FileWatch. Can
|
||||||
|
* be null.
|
||||||
|
*/
|
||||||
|
FileWatch *file_watch_find_by_wd(FileWatcherContext *ctx, int wd,
|
||||||
|
size_t *index);
|
||||||
|
/**
|
||||||
|
* Search for a FileWatch in a particular context matching by filepath.
|
||||||
|
* @param ctx
|
||||||
|
* @param path An absolute path for the file searched for
|
||||||
|
* @param index Will contain the first occurrance of a matching FileWatch. Can
|
||||||
|
* be null. */
|
||||||
|
FileWatch *file_watch_find_by_path(FileWatcherContext *ctx, const char *path,
|
||||||
|
size_t *index);
|
||||||
|
|
||||||
HiResult file_watch_add(FileWatcherContext *ctx, u32 mask,
|
/**
|
||||||
const char *path);
|
* Create FileWatch, start monitoring for changes and generating events.
|
||||||
HiResult file_watch_remove(FileWatcherContext *ctx,
|
*
|
||||||
const char *path);
|
* Creats a parent FileWatch if it doesn't already exist.
|
||||||
|
*/
|
||||||
|
HiResult file_watch_add(FileWatcherContext *ctx, u32 mask, const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop a watch, clear all references and free memory
|
||||||
|
*/
|
||||||
|
HiResult file_watch_remove(FileWatcherContext *ctx, const char *path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Free memory and clear all watches in this context
|
||||||
|
*/
|
||||||
void file_watch_destroy_watches(FileWatcherContext *ctx);
|
void file_watch_destroy_watches(FileWatcherContext *ctx);
|
||||||
|
|
||||||
#endif // FILEWATCH_H_
|
#endif // FILEWATCH_H_
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ typedef struct sc_array_fwparam VectorWatchParams;
|
|||||||
* reused, and @a files is a list of files that have this watch as their parent.
|
* reused, and @a files is a list of files that have this watch as their parent.
|
||||||
*/
|
*/
|
||||||
typedef struct FileWatch {
|
typedef struct FileWatch {
|
||||||
i32 wd; // Watch descriptor
|
i32 wd;
|
||||||
u32 mask; // watch mask used
|
u32 mask;
|
||||||
const char *path; // Owning
|
const char *path; // Owning
|
||||||
VectorStr files;
|
VectorStr files;
|
||||||
} FileWatch;
|
} FileWatch;
|
||||||
|
|||||||
@@ -22,6 +22,9 @@
|
|||||||
sc_array_def(FileEvent, fwevent);
|
sc_array_def(FileEvent, fwevent);
|
||||||
typedef struct sc_array_fwevent VectorFileEvent;
|
typedef struct sc_array_fwevent VectorFileEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All context and state required to run a file watcher.
|
||||||
|
*/
|
||||||
typedef struct FileWatcher {
|
typedef struct FileWatcher {
|
||||||
thrd_t thread;
|
thrd_t thread;
|
||||||
mtx_t mutex;
|
mtx_t mutex;
|
||||||
@@ -31,10 +34,10 @@ typedef struct FileWatcher {
|
|||||||
VectorFileEvent events;
|
VectorFileEvent events;
|
||||||
} FileWatcher;
|
} FileWatcher;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Watch Params
|
* Watch Params
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline WatchParams watch_params_create(const char *path, u32 mask) {
|
static inline WatchParams watch_params_create(const char *path, u32 mask) {
|
||||||
return (WatchParams){.path = strdup(path), .mask = mask};
|
return (WatchParams){.path = strdup(path), .mask = mask};
|
||||||
}
|
}
|
||||||
@@ -46,7 +49,7 @@ static inline void watch_params_destroy(WatchParams *params) {
|
|||||||
static WatchParams *watch_params_find_by_path(const char *path,
|
static WatchParams *watch_params_find_by_path(const char *path,
|
||||||
FileWatcherContext *ctx,
|
FileWatcherContext *ctx,
|
||||||
size_t *index) {
|
size_t *index) {
|
||||||
const struct sc_array_fwparam *params = &ctx->params;
|
const VectorWatchParams *params = &ctx->params;
|
||||||
for (size_t i = 0; i < sc_array_size(params); ++i) {
|
for (size_t i = 0; i < sc_array_size(params); ++i) {
|
||||||
WatchParams *param = &sc_array_at(params, i);
|
WatchParams *param = &sc_array_at(params, i);
|
||||||
if (strcmp(path, param->path) == 0) {
|
if (strcmp(path, param->path) == 0) {
|
||||||
@@ -140,7 +143,7 @@ static FileEvent file_event_create(const struct inotify_event *inevent,
|
|||||||
return NULL_EVENT;
|
return NULL_EVENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileEvent hi_file_event_pop(FileWatcher *fw) {
|
FileEvent file_event_pop(FileWatcher *fw) {
|
||||||
mtx_lock(&fw->mutex);
|
mtx_lock(&fw->mutex);
|
||||||
|
|
||||||
FileEvent e = NULL_EVENT;
|
FileEvent e = NULL_EVENT;
|
||||||
@@ -178,7 +181,7 @@ static void file_watcher_ctx_destroy(FileWatcherContext *ctx) {
|
|||||||
// Declare the thread func
|
// Declare the thread func
|
||||||
int file_watcher_watch(void *arg);
|
int file_watcher_watch(void *arg);
|
||||||
|
|
||||||
FileWatcher *hi_file_watcher_create() {
|
FileWatcher *file_watcher_create() {
|
||||||
|
|
||||||
// Allocate context
|
// Allocate context
|
||||||
FileWatcher *fw = malloc(sizeof(FileWatcher));
|
FileWatcher *fw = malloc(sizeof(FileWatcher));
|
||||||
@@ -204,7 +207,7 @@ FileWatcher *hi_file_watcher_create() {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
HiResult hi_file_watcher_add(FileWatcher *fw, const char *filename, u32 flags) {
|
HiResult file_watcher_add(FileWatcher *fw, const char *filename, u32 flags) {
|
||||||
if (!fw) {
|
if (!fw) {
|
||||||
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);
|
||||||
@@ -257,19 +260,19 @@ HiResult hi_file_watcher_remove(FileWatcher *fw, const char *filename) {
|
|||||||
return HI_OK;
|
return HI_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void hi_file_watcher_notify(FileWatcher *fw) {
|
void file_watcher_notify(FileWatcher *fw) {
|
||||||
if (fw && fw->running) {
|
if (fw && fw->running) {
|
||||||
cnd_signal(&fw->cond);
|
cnd_signal(&fw->cond);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void hi_file_watcher_destroy(FileWatcher *fw) {
|
void file_watcher_destroy(FileWatcher *fw) {
|
||||||
if (!fw)
|
if (!fw)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (fw->running) {
|
if (fw->running) {
|
||||||
fw->running = false;
|
fw->running = false;
|
||||||
hi_file_watcher_notify(fw);
|
file_watcher_notify(fw);
|
||||||
thrd_join(fw->thread, NULL);
|
thrd_join(fw->thread, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,8 +21,9 @@
|
|||||||
* memory inside the file watcher struct.
|
* memory inside the file watcher struct.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char *pathname; // Pathname given to a `hi_file_watcher_add` call.
|
/** Path of the file this event is about. Do not free. */
|
||||||
// Do not free.
|
const char *pathname;
|
||||||
|
/** Might not be only those given to @a file_watcher_add. */
|
||||||
FileWatchType type;
|
FileWatchType type;
|
||||||
} FileEvent;
|
} FileEvent;
|
||||||
|
|
||||||
@@ -31,44 +32,38 @@ typedef struct FileWatcher FileWatcher;
|
|||||||
/**
|
/**
|
||||||
* FileEvents is a thread safe list of events.
|
* FileEvents is a thread safe list of events.
|
||||||
*
|
*
|
||||||
* It is implemented as a stack, so the events will be popped
|
* Events will be popped mostly in reverse chronological order, but strict
|
||||||
* in reverse chronological order.
|
* chronology is not guaranteed.
|
||||||
* */
|
*/
|
||||||
typedef struct FileEvents FileEvents;
|
typedef struct FileEvents FileEvents;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create watcher and necessary data to run it.
|
* Create watcher and necessary data to run it.
|
||||||
*
|
*
|
||||||
* Will start the watcher thread immediately.
|
* Starts a new watcher thread immediately.
|
||||||
*/
|
*/
|
||||||
FileWatcher *hi_file_watcher_create(void);
|
FileWatcher *file_watcher_create(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a previously created file watcher context.
|
* Destroy a previously created file watcher.
|
||||||
*
|
*
|
||||||
* Will join the thread and destroy event stack.
|
* Will join the thread and destroy event stack. Any FileEvent still in
|
||||||
|
* existence will contain invalid data after this call.
|
||||||
*/
|
*/
|
||||||
void hi_file_watcher_destroy(FileWatcher *context);
|
void file_watcher_destroy(FileWatcher *fw);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a file to the watch list of a file watcher.
|
* Start watching for events on a file.
|
||||||
*
|
*
|
||||||
* @param context Previously created watcher context
|
* @param fw The watcher instance
|
||||||
* @param pathname Absolute path to the file or directory
|
* @param pathname Absolute path to the file or directory
|
||||||
* @param flags The events that will be watched for
|
* @param flags The event mask for the events to watch for
|
||||||
*/
|
*/
|
||||||
HiResult hi_file_watcher_add(FileWatcher *context,
|
HiResult file_watcher_add(FileWatcher *fw, const char *pathname, u32 flags);
|
||||||
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(FileWatcher *context);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pop an event from event stack. Call `hi_file_event_destroy` when done with
|
* Pop an event from event stack.
|
||||||
* it.
|
|
||||||
*/
|
*/
|
||||||
FileEvent hi_file_event_pop(FileWatcher *ctx);
|
FileEvent file_event_pop(FileWatcher *ctx);
|
||||||
|
|
||||||
#endif // HI_FILEWATCHER_H_
|
#endif // HI_FILEWATCHER_H_
|
||||||
|
|||||||
12
src/hiload.c
12
src/hiload.c
@@ -71,7 +71,7 @@ static int gather_module_data_callback(struct dl_phdr_info *info, size_t size,
|
|||||||
|
|
||||||
// Mark everything but system and virtual modules as patchable
|
// Mark everything but system and virtual modules as patchable
|
||||||
if (module.name[0] == '/') {
|
if (module.name[0] == '/') {
|
||||||
if (!hi_string_starts_with(module.name, ARRLEN(module_exclude_filter),
|
if (!hi_str_starts_with(module.name, ARRLEN(module_exclude_filter),
|
||||||
module_exclude_filter)) {
|
module_exclude_filter)) {
|
||||||
module.info = hi_modinfo_add(module.info, HI_MODULE_STATE_PATCHABLE);
|
module.info = hi_modinfo_add(module.info, HI_MODULE_STATE_PATCHABLE);
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ static int gather_module_data_callback(struct dl_phdr_info *info, size_t size,
|
|||||||
// Replace shortform with str pointer from module info
|
// Replace shortform with str pointer from module info
|
||||||
sc_array_at(&context.enabled_modules, i) = module.name;
|
sc_array_at(&context.enabled_modules, i) = module.name;
|
||||||
|
|
||||||
if (!HIOK(hi_file_watcher_add(context.filewatcher, modpath,
|
if (!HIOK(file_watcher_add(context.filewatcher, modpath,
|
||||||
HI_FILE_ALL_EVENTS))) {
|
HI_FILE_ALL_EVENTS))) {
|
||||||
log_error("Failed to set filewatcher for: '%s'\n", modpath);
|
log_error("Failed to set filewatcher for: '%s'\n", modpath);
|
||||||
} else {
|
} else {
|
||||||
@@ -151,7 +151,7 @@ static ModuleData *get_module_by_path(const char *path,
|
|||||||
*/
|
*/
|
||||||
static void handle_events(FileWatcher *fw, VectorModuleData *modules) {
|
static void handle_events(FileWatcher *fw, VectorModuleData *modules) {
|
||||||
|
|
||||||
FileEvent event = hi_file_event_pop(fw);
|
FileEvent event = file_event_pop(fw);
|
||||||
while (event.type != HI_FILE_NONE) {
|
while (event.type != HI_FILE_NONE) {
|
||||||
log_debug("Event pop: %s – %s\n", event.pathname,
|
log_debug("Event pop: %s – %s\n", event.pathname,
|
||||||
hi_file_watch_type_to_str(event.type));
|
hi_file_watch_type_to_str(event.type));
|
||||||
@@ -162,7 +162,7 @@ static void handle_events(FileWatcher *fw, VectorModuleData *modules) {
|
|||||||
} else {
|
} else {
|
||||||
module->info = hi_modinfo_add(module->info, HI_MODULE_STATE_DIRTY);
|
module->info = hi_modinfo_add(module->info, HI_MODULE_STATE_DIRTY);
|
||||||
}
|
}
|
||||||
event = hi_file_event_pop(context.filewatcher);
|
event = file_event_pop(context.filewatcher);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -216,7 +216,7 @@ int hi_init(size_t n, const char **enabled_modules) {
|
|||||||
log_set_thread_name("Main");
|
log_set_thread_name("Main");
|
||||||
|
|
||||||
// Start the filewatcher
|
// Start the filewatcher
|
||||||
context.filewatcher = hi_file_watcher_create();
|
context.filewatcher = file_watcher_create();
|
||||||
|
|
||||||
for (unsigned i = 0; i < n; i++) {
|
for (unsigned i = 0; i < n; i++) {
|
||||||
const char *module_name = enabled_modules[i];
|
const char *module_name = enabled_modules[i];
|
||||||
@@ -241,7 +241,7 @@ int hi_init(size_t n, const char **enabled_modules) {
|
|||||||
|
|
||||||
void hi_deinit() {
|
void hi_deinit() {
|
||||||
module_infos_free(&context.modules);
|
module_infos_free(&context.modules);
|
||||||
hi_file_watcher_destroy(context.filewatcher);
|
file_watcher_destroy(context.filewatcher);
|
||||||
log_term();
|
log_term();
|
||||||
memset(&context, 0, sizeof(context));
|
memset(&context, 0, sizeof(context));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ HiResult memory_find_region(uptr ptr, struct sc_array_memreg *const regions,
|
|||||||
HiResult read_memory_maps_self(struct sc_array_memreg *regions) {
|
HiResult read_memory_maps_self(struct sc_array_memreg *regions) {
|
||||||
memory_clear_memregs(regions);
|
memory_clear_memregs(regions);
|
||||||
|
|
||||||
char *maps_str = hi_file_to_str_dyn("/proc/self/maps");
|
char *maps_str = file_to_str_dyn("/proc/self/maps");
|
||||||
if (!maps_str)
|
if (!maps_str)
|
||||||
return HI_FAIL;
|
return HI_FAIL;
|
||||||
|
|
||||||
|
|||||||
@@ -135,8 +135,8 @@ static HiResult gather_patchable_symbols(struct sc_array_sym *symbols,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void *sym_addr = (void *)((uintptr_t)module_base + sym.st_value);
|
void *sym_addr = (void *)((uintptr_t)module_base + sym.st_value);
|
||||||
HiSymbolBind binding = symbol_bind_from_efibind(GELF_ST_BIND(sym.st_info));
|
SymbolBind binding = symbol_bind_from_efibind(GELF_ST_BIND(sym.st_info));
|
||||||
HiSymbolType type = symbol_type_from_efitype(GELF_ST_TYPE(sym.st_info));
|
SymbolType type = symbol_type_from_efitype(GELF_ST_TYPE(sym.st_info));
|
||||||
size_t size = sym.st_size;
|
size_t size = sym.st_size;
|
||||||
|
|
||||||
// Gather global symbols and local object symbols. Local functions are
|
// Gather global symbols and local object symbols. Local functions are
|
||||||
@@ -145,7 +145,7 @@ static HiResult gather_patchable_symbols(struct sc_array_sym *symbols,
|
|||||||
if (binding == HI_SYMBOL_BIND_GLOBAL ||
|
if (binding == HI_SYMBOL_BIND_GLOBAL ||
|
||||||
(binding == HI_SYMBOL_BIND_LOCAL &&
|
(binding == HI_SYMBOL_BIND_LOCAL &&
|
||||||
type == HI_SYMBOL_TYPE_OBJECT)) {
|
type == HI_SYMBOL_TYPE_OBJECT)) {
|
||||||
HiSymbol hisym = {.name = strdup(name),
|
Symbol hisym = {.name = strdup(name),
|
||||||
.size = size,
|
.size = size,
|
||||||
.binding = binding,
|
.binding = binding,
|
||||||
.type = type,
|
.type = type,
|
||||||
@@ -166,7 +166,7 @@ cleanup:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HiResult moduler_apply_module_patch(HiSymbols *psymbols,
|
static HiResult moduler_apply_module_patch(VectorSymbol *psymbols,
|
||||||
MemoryRegionSpan module_memory) {
|
MemoryRegionSpan module_memory) {
|
||||||
|
|
||||||
void *module_base = (void *)module_memory.region_start;
|
void *module_base = (void *)module_memory.region_start;
|
||||||
@@ -246,7 +246,7 @@ static HiResult moduler_apply_module_patch(HiSymbols *psymbols,
|
|||||||
|
|
||||||
// Check if this is a symbol we want to patch
|
// Check if this is a symbol we want to patch
|
||||||
for (size_t j = 0; j < num_symbols; j++) {
|
for (size_t j = 0; j < num_symbols; j++) {
|
||||||
HiSymbol *sym = &sc_array_at(psymbols, j);
|
Symbol *sym = &sc_array_at(psymbols, j);
|
||||||
if (strcmp(sym->name, name) == 0) {
|
if (strcmp(sym->name, name) == 0) {
|
||||||
sym->got_entry = got_entry;
|
sym->got_entry = got_entry;
|
||||||
sym->orig_address = *got_entry; // Save the original function
|
sym->orig_address = *got_entry; // Save the original function
|
||||||
@@ -280,7 +280,7 @@ PatchData moduler_create_patch(ModuleData *module) {
|
|||||||
|
|
||||||
char filename[512];
|
char filename[512];
|
||||||
size_t written =
|
size_t written =
|
||||||
hi_strncat_buf(sizeof(filename), filename, module->name, file_append);
|
hi_str_concat_buf(sizeof(filename), filename, module->name, file_append);
|
||||||
|
|
||||||
|
|
||||||
if (written == 0) {
|
if (written == 0) {
|
||||||
@@ -288,7 +288,7 @@ PatchData moduler_create_patch(ModuleData *module) {
|
|||||||
return (PatchData){0};
|
return (PatchData){0};
|
||||||
}
|
}
|
||||||
|
|
||||||
hi_file_copy(module->name, filename);
|
file_copy(module->name, filename);
|
||||||
|
|
||||||
PatchData data = {.filename = strdup(filename)};
|
PatchData data = {.filename = strdup(filename)};
|
||||||
return data;
|
return data;
|
||||||
@@ -328,7 +328,7 @@ HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
|
|||||||
patch.memreg = memory_get_module_span(memregs, patch.filename);
|
patch.memreg = memory_get_module_span(memregs, patch.filename);
|
||||||
void *patch_base = (void *)patch.memreg.region_start;
|
void *patch_base = (void *)patch.memreg.region_start;
|
||||||
|
|
||||||
HiSymbols patch_symbols;
|
VectorSymbol patch_symbols;
|
||||||
symbol_init_symbols(&patch_symbols);
|
symbol_init_symbols(&patch_symbols);
|
||||||
|
|
||||||
HiResult ret =
|
HiResult ret =
|
||||||
@@ -352,7 +352,7 @@ HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
|
|||||||
// If patch is for the same module, also collect local object symbols for
|
// If patch is for the same module, also collect local object symbols for
|
||||||
// coping those over.
|
// coping those over.
|
||||||
|
|
||||||
HiSymbols module_symbols;
|
VectorSymbol module_symbols;
|
||||||
symbol_init_symbols(&module_symbols);
|
symbol_init_symbols(&module_symbols);
|
||||||
ret = gather_patchable_symbols(&module_symbols, mod.name,
|
ret = gather_patchable_symbols(&module_symbols, mod.name,
|
||||||
(void *)mod.address);
|
(void *)mod.address);
|
||||||
@@ -364,9 +364,9 @@ HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
|
|||||||
|
|
||||||
// Copy old data to new data. Breaks with layout changes.
|
// Copy old data to new data. Breaks with layout changes.
|
||||||
for (size_t i = 0; i < sc_array_size(&module_symbols); ++i) {
|
for (size_t i = 0; i < sc_array_size(&module_symbols); ++i) {
|
||||||
HiSymbol *sym = &sc_array_at(&module_symbols, i);
|
Symbol *sym = &sc_array_at(&module_symbols, i);
|
||||||
if (sym->type == HI_SYMBOL_TYPE_OBJECT) {
|
if (sym->type == HI_SYMBOL_TYPE_OBJECT) {
|
||||||
HiSymbol *ps = symbol_find(&patch_symbols, sym);
|
Symbol *ps = symbol_find(&patch_symbols, sym);
|
||||||
if (ps) {
|
if (ps) {
|
||||||
if (ps->size >= sym->size) {
|
if (ps->size >= sym->size) {
|
||||||
memcpy(ps->address, sym->address, sym->size);
|
memcpy(ps->address, sym->address, sym->size);
|
||||||
|
|||||||
@@ -7,8 +7,10 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
size_t hi_strncat_buf(size_t buflen, char buf[buflen], const char *first, const char *second) {
|
size_t hi_str_concat_buf(size_t buflen, char buf[buflen], const char *first,
|
||||||
if (!buf) return 0;
|
const char *second) {
|
||||||
|
if (!buf)
|
||||||
|
return 0;
|
||||||
|
|
||||||
size_t first_len = strlen(first);
|
size_t first_len = strlen(first);
|
||||||
size_t second_len = strlen(second);
|
size_t second_len = strlen(second);
|
||||||
@@ -42,7 +44,7 @@ int hi_path_has_filename(const char *path, const char *filename) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
char *hi_string_from_file_dyn(const char *filename, size_t *nread,
|
char *hi_str_from_file(const char *filename, size_t *nread,
|
||||||
size_t nmax) {
|
size_t nmax) {
|
||||||
|
|
||||||
FILE *f = fopen(filename, "r");
|
FILE *f = fopen(filename, "r");
|
||||||
@@ -105,9 +107,11 @@ char *hi_string_from_file_dyn(const char *filename, size_t *nread,
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *hi_string_starts_with(const char path[static 1], size_t listn, const char *pathlist[listn]) {
|
const char *hi_str_starts_with(const char path[static 1], size_t listn,
|
||||||
|
const char *pathlist[listn]) {
|
||||||
|
|
||||||
if (!pathlist) return path;
|
if (!pathlist)
|
||||||
|
return path;
|
||||||
size_t pathlen = strlen(path);
|
size_t pathlen = strlen(path);
|
||||||
for (size_t i = 0; i < listn; ++i) {
|
for (size_t i = 0; i < listn; ++i) {
|
||||||
size_t len = MIN(strlen(pathlist[i]), pathlen);
|
size_t len = MIN(strlen(pathlist[i]), pathlen);
|
||||||
|
|||||||
@@ -6,40 +6,47 @@
|
|||||||
/**
|
/**
|
||||||
* Concatenate two strings into a buffer.
|
* Concatenate two strings into a buffer.
|
||||||
*
|
*
|
||||||
* If resulting string would be longer than buflen - 1, the resulting string in
|
* If resulting string would be longer than @a buflen - 1, the resulting string
|
||||||
* \p buf is unchanged.
|
* in
|
||||||
|
* @a buf is unchanged.
|
||||||
*
|
*
|
||||||
* @param first Null terminated character string
|
* @param first Null terminated character string
|
||||||
* @param second Null terminated character string
|
* @param second Null terminated character string
|
||||||
*/
|
*/
|
||||||
size_t hi_strncat_buf(size_t bufsize, char buf[bufsize], const char *first,
|
size_t hi_str_concat_buf(size_t bufsize, char buf[bufsize],
|
||||||
const char *second);
|
const char *first, const char *second);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy file content to a null terminated string, allocating memory while
|
* Copy file content to a null terminated string, allocating memory while
|
||||||
* reading.
|
* reading.
|
||||||
*
|
*
|
||||||
* This doesn't assume it can read file size, so it allocates memory in chunks
|
* Can be used for reading the virtual file system, where the file size isn't
|
||||||
* (default 4096 bytes) and keeps reading until 0 bytes is read. If nmax is
|
* known. If @a nmax is non-zero, that amount of bytes is allocated and read at
|
||||||
* non-zero, instead that amount of bytes is allocated and that is read.
|
* once. No more than @a nmax bytes is read in that case.
|
||||||
*
|
*
|
||||||
* In either case, the string is reallocated to match the length before
|
* In all cases, the string is reallocated to match the length before
|
||||||
* returning.
|
* returning.
|
||||||
|
*
|
||||||
* @param filename a complete pathname to the file
|
* @param filename a complete pathname to the file
|
||||||
* @param nread if not null, this will have the total amount bytes read
|
* @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
|
* @param nmax if not 0, this amount of memory in bytes is read and used as
|
||||||
* initial allocation
|
* initial allocation
|
||||||
*/
|
*/
|
||||||
char *hi_string_from_file_dyn(const char *filename, size_t *nread, size_t nmax);
|
char *hi_str_from_file(const char *filename, size_t *nread,
|
||||||
|
size_t nmax);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find if filename exists at the end of path.
|
* Return the first string from @a pathlist that is fully included in @a path.
|
||||||
|
*/
|
||||||
|
const char *hi_str_starts_with(const char path[static 1], size_t listn,
|
||||||
|
const char *pathlist[listn]);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find if @a filename exists at the end of @a path.
|
||||||
*
|
*
|
||||||
* @return 0 if not found, positive if found
|
* @return 0 if not found, positive if found
|
||||||
*/
|
*/
|
||||||
int hi_path_has_filename(const char *path, const char *filename);
|
int hi_path_has_filename(const char *path, const char *filename);
|
||||||
|
|
||||||
const char *hi_string_starts_with(const char path[static 1], size_t listn,
|
|
||||||
const char *pathlist[listn]);
|
|
||||||
|
|
||||||
#endif // HI_STRING_H_
|
#endif // HI_STRING_H_
|
||||||
|
|||||||
@@ -4,9 +4,9 @@
|
|||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
HiSymbol *symbol_find(HiSymbols *symbols, HiSymbol *symbol) {
|
Symbol *symbol_find(VectorSymbol *symbols, Symbol *symbol) {
|
||||||
for (size_t i=0; i < sc_array_size(symbols); ++i) {
|
for (size_t i=0; i < sc_array_size(symbols); ++i) {
|
||||||
HiSymbol *s = &sc_array_at(symbols, i);
|
Symbol *s = &sc_array_at(symbols, i);
|
||||||
if (strcmp(s->name, symbol->name) == 0) {
|
if (strcmp(s->name, symbol->name) == 0) {
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ HiSymbol *symbol_find(HiSymbols *symbols, HiSymbol *symbol) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
HiSymbolBind symbol_bind_from_efibind(u32 efi_bind) {
|
SymbolBind symbol_bind_from_efibind(u32 efi_bind) {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
switch (efi_bind) {
|
switch (efi_bind) {
|
||||||
case STB_LOCAL: return HI_SYMBOL_BIND_LOCAL;
|
case STB_LOCAL: return HI_SYMBOL_BIND_LOCAL;
|
||||||
@@ -25,7 +25,7 @@ HiSymbolBind symbol_bind_from_efibind(u32 efi_bind) {
|
|||||||
return ~0u;
|
return ~0u;
|
||||||
}
|
}
|
||||||
|
|
||||||
HiSymbolType symbol_type_from_efitype(u32 efi_type) {
|
SymbolType symbol_type_from_efitype(u32 efi_type) {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
switch (efi_type) {
|
switch (efi_type) {
|
||||||
case STT_NOTYPE: return HI_SYMBOL_TYPE_NOTYPE; /* Symbol type is unspecified */
|
case STT_NOTYPE: return HI_SYMBOL_TYPE_NOTYPE; /* Symbol type is unspecified */
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ typedef enum {
|
|||||||
HI_SYMBOL_BIND_LOCAL,
|
HI_SYMBOL_BIND_LOCAL,
|
||||||
HI_SYMBOL_BIND_GLOBAL,
|
HI_SYMBOL_BIND_GLOBAL,
|
||||||
HI_SYMBOL_BIND_WEAK,
|
HI_SYMBOL_BIND_WEAK,
|
||||||
} HiSymbolBind;
|
} SymbolBind;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
HI_SYMBOL_TYPE_NOTYPE,
|
HI_SYMBOL_TYPE_NOTYPE,
|
||||||
@@ -19,39 +19,41 @@ typedef enum {
|
|||||||
HI_SYMBOL_TYPE_FILE,
|
HI_SYMBOL_TYPE_FILE,
|
||||||
HI_SYMBOL_TYPE_COMMON,
|
HI_SYMBOL_TYPE_COMMON,
|
||||||
HI_SYMBOL_TYPE_TLS,
|
HI_SYMBOL_TYPE_TLS,
|
||||||
} HiSymbolType;
|
} SymbolType;
|
||||||
|
|
||||||
typedef struct HiSymbol {
|
typedef struct Symbol {
|
||||||
const char *name;
|
const char *name;
|
||||||
HiSymbolBind binding;
|
SymbolBind binding;
|
||||||
HiSymbolType type;
|
SymbolType type;
|
||||||
size_t size;
|
size_t size;
|
||||||
void *address;
|
void *address;
|
||||||
void **got_entry;
|
void **got_entry;
|
||||||
void *orig_address;
|
void *orig_address;
|
||||||
} HiSymbol;
|
} Symbol;
|
||||||
sc_array_def(HiSymbol, sym);
|
|
||||||
typedef struct sc_array_sym HiSymbols;
|
|
||||||
|
|
||||||
static inline void symbol_free(HiSymbol *symbol) { free((char *)symbol->name); }
|
sc_array_def(Symbol, sym);
|
||||||
|
typedef struct sc_array_sym VectorSymbol;
|
||||||
|
|
||||||
static inline void symbol_init_symbols(HiSymbols *symbols) {
|
static inline void symbol_free(Symbol *symbol) { free((char *)symbol->name); }
|
||||||
|
|
||||||
|
static inline void symbol_init_symbols(VectorSymbol *symbols) {
|
||||||
sc_array_init(symbols);
|
sc_array_init(symbols);
|
||||||
}
|
}
|
||||||
static inline void symbol_clear_symbols(HiSymbols *symbols) {
|
static inline void symbol_clear_symbols(VectorSymbol *symbols) {
|
||||||
for (size_t i = 0; i < sc_array_size(symbols); ++i) {
|
for (size_t i = 0; i < sc_array_size(symbols); ++i) {
|
||||||
symbol_free(&sc_array_at(symbols, i));
|
symbol_free(&sc_array_at(symbols, i));
|
||||||
}
|
}
|
||||||
sc_array_clear(symbols);
|
sc_array_clear(symbols);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void symbol_term_symbols(HiSymbols *symbols) {
|
static inline void symbol_term_symbols(VectorSymbol *symbols) {
|
||||||
symbol_clear_symbols(symbols);
|
symbol_clear_symbols(symbols);
|
||||||
sc_array_term(symbols);
|
sc_array_term(symbols);
|
||||||
}
|
}
|
||||||
|
|
||||||
HiSymbol *symbol_find(HiSymbols *symbols, HiSymbol *symbol);
|
Symbol *symbol_find(VectorSymbol *symbols, Symbol *symbol);
|
||||||
|
|
||||||
|
SymbolBind symbol_bind_from_efibind(u32 efi_bind);
|
||||||
|
SymbolType symbol_type_from_efitype(u32 efi_type);
|
||||||
|
|
||||||
HiSymbolBind symbol_bind_from_efibind(u32 efi_bind);
|
|
||||||
HiSymbolType symbol_type_from_efitype(u32 efi_type);
|
|
||||||
#endif // SYMBOLS_H_
|
#endif // SYMBOLS_H_
|
||||||
|
|||||||
Reference in New Issue
Block a user