Add fs watcher
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,3 +2,4 @@ mongoose.o
|
||||
aboba
|
||||
build
|
||||
gpp1
|
||||
watcher
|
||||
|
5
.watcherignore
Normal file
5
.watcherignore
Normal file
@ -0,0 +1,5 @@
|
||||
./aboba
|
||||
./build
|
||||
./gpp1
|
||||
./watcher
|
||||
./mongoose.o
|
4
build.c
4
build.c
@ -18,7 +18,9 @@ int main(int argc, char ** argv)
|
||||
"./routes.c",
|
||||
"./baked.c",
|
||||
"./mongoose.o",
|
||||
"./gpp1"
|
||||
"./gpp1",
|
||||
"./tmpls/home.t",
|
||||
"./tmpls/page-missing.t"
|
||||
) {
|
||||
|
||||
RULE("./mongoose.o", "./mongoose/mongoose.c") {
|
||||
|
2
gebs
2
gebs
Submodule gebs updated: 61e59e6550...2f9ff2e757
194
watcher.c
Normal file
194
watcher.c
Normal file
@ -0,0 +1,194 @@
|
||||
#include <dirent.h>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <sys/inotify.h>
|
||||
|
||||
#define STB_DS_IMPLEMENTATION
|
||||
#include "stb/stb_ds.h"
|
||||
#define GEBS_NO_PREFIX
|
||||
#define GEBS_IMPLEMENTATION
|
||||
#include "gebs/gebs.h"
|
||||
|
||||
#define EVENT_BUFFER_SIZE (1 * (sizeof(struct inotify_event) + NAME_MAX + 1))
|
||||
|
||||
typedef struct {
|
||||
int key; // watch descriptor
|
||||
char *value; // the path
|
||||
} File_Map;
|
||||
|
||||
typedef struct {
|
||||
File_Map *fmap;
|
||||
int fd;
|
||||
NString_List ignored;
|
||||
} Watcher;
|
||||
|
||||
bool watcher_make(Watcher *w)
|
||||
{
|
||||
String_Builder watcherignore = {0};
|
||||
defer { sb_free(&watcherignore); }
|
||||
if (sb_read_file(&watcherignore, "./.watcherignore")) {
|
||||
sb_finish(&watcherignore);
|
||||
|
||||
char *line = strtok(watcherignore.items, "\n");
|
||||
while (line != nil) {
|
||||
char *line_copy = malloc(strlen(line)+1);
|
||||
strcpy(line_copy, line);
|
||||
list_append(&w->ignored, line_copy);
|
||||
line = strtok(nil, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
if ((w->fd = inotify_init1(IN_NONBLOCK)) < 0) {
|
||||
LOGE("inotify_init1(): %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void watcher_free(Watcher *w)
|
||||
{
|
||||
|
||||
for (size_t i = 0; i < w->ignored.count; i++) {
|
||||
free(w->ignored.items[i]);
|
||||
}
|
||||
list_free(&w->ignored);
|
||||
|
||||
for (size_t i = 0; i < hmlen(w->fmap); i++) {
|
||||
free(w->fmap[i].value);
|
||||
inotify_rm_watch(w->fd, w->fmap[i].key);
|
||||
}
|
||||
hmfree(w->fmap);
|
||||
|
||||
close(w->fd);
|
||||
}
|
||||
|
||||
void watcher_add_file1(Watcher *w, int wd, char *path)
|
||||
{
|
||||
char *path_copy = gebs_malloc(&default_allocator, strlen(path)+1);
|
||||
strcpy(path_copy, path);
|
||||
hmput(w->fmap, wd, path_copy);
|
||||
}
|
||||
|
||||
void watcher_add_file(Watcher *w, char *path)
|
||||
{
|
||||
uint32_t file_mask = IN_MODIFY | IN_DONT_FOLLOW;
|
||||
int file_wd = inotify_add_watch(w->fd, path, file_mask);
|
||||
if (file_wd < 0) {
|
||||
LOGE("Could not add %s to watcher\n", path);
|
||||
return;
|
||||
}
|
||||
|
||||
watcher_add_file1(w, file_wd, path);
|
||||
}
|
||||
|
||||
void watcher_add_all(Watcher *w, char *root)
|
||||
{
|
||||
DIR *dir;
|
||||
struct dirent *entry;
|
||||
|
||||
if ((dir = opendir(root)) == nil) {
|
||||
LOGE("Could not open dir %s\n", root);
|
||||
return;
|
||||
}
|
||||
defer { closedir(dir); }
|
||||
|
||||
while ((entry = readdir(dir)) != nil) {
|
||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
char path[PATH_MAX];
|
||||
snprintf(path, sizeof(path), "%s/%s", root, entry->d_name);
|
||||
LOGI("%s\n", path);
|
||||
|
||||
for (size_t i = 0; i < w->ignored.count; i++) {
|
||||
if (strcmp(w->ignored.items[i], path) == 0) {
|
||||
LOGI("Skipping %s\n", path);
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->d_type == DT_DIR) {
|
||||
watcher_add_all(w, path);
|
||||
} else {
|
||||
watcher_add_file(w, path);
|
||||
}
|
||||
next:
|
||||
}
|
||||
}
|
||||
|
||||
char *prog = nil;
|
||||
bool running = true;
|
||||
|
||||
void handle_sigint(int signo)
|
||||
{
|
||||
running = false;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
prog = SHIFT(&argc, &argv);
|
||||
if (argc < 2) {
|
||||
LOGI("Usage: ./watcher <dir> <command>\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *watch_dir = SHIFT(&argc, &argv);
|
||||
Cmd cmd = {0};
|
||||
defer { cmd_free(&cmd); }
|
||||
while (argc > 0) {
|
||||
cmd_append(&cmd, SHIFT(&argc, &argv));
|
||||
}
|
||||
|
||||
signal(SIGINT, &handle_sigint);
|
||||
|
||||
Watcher watcher = {0};
|
||||
if (!watcher_make(&watcher)) {
|
||||
LOGE("Failed to create the watcher\n");
|
||||
return 1;
|
||||
}
|
||||
defer { watcher_free(&watcher); }
|
||||
|
||||
watcher_add_all(&watcher, watch_dir);
|
||||
|
||||
pid_t cmd_pid = cmd_run_async(&cmd);
|
||||
|
||||
char event_buffer[EVENT_BUFFER_SIZE] __attribute__((aligned(8)));
|
||||
while (running) {
|
||||
ssize_t nread;
|
||||
if ((nread = read(watcher.fd, event_buffer, EVENT_BUFFER_SIZE)) < 0 && errno != EAGAIN) {
|
||||
LOGE("read(): %s\n", strerror(errno));
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (char *event_ptr = event_buffer; event_ptr < event_buffer + nread; ) {
|
||||
const struct inotify_event *event = (const struct inotify_event *)event_ptr;
|
||||
|
||||
ssize_t idx = hmgeti(watcher.fmap, event->wd);
|
||||
char *path = watcher.fmap[idx].value;
|
||||
|
||||
if (event->mask & IN_IGNORED) {
|
||||
hmdel(watcher.fmap, event->wd);
|
||||
watcher_add_file(&watcher, path);
|
||||
}
|
||||
|
||||
if (event->mask & (IN_MODIFY | IN_IGNORED)) {
|
||||
LOGI("%s changed (%04x)\n", path, event->mask);
|
||||
|
||||
if (cmd_pid != -1) {
|
||||
LOGI("Killing %d\n", cmd_pid);
|
||||
kill(cmd_pid, SIGKILL);
|
||||
}
|
||||
|
||||
cmd_pid = cmd_run_async(&cmd);
|
||||
}
|
||||
|
||||
event_ptr += sizeof(struct inotify_event) + event->len;
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
Reference in New Issue
Block a user