diff --git a/.gitignore b/.gitignore index 974ff5d..6a173c5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ mongoose.o aboba build gpp1 +watcher diff --git a/.watcherignore b/.watcherignore new file mode 100644 index 0000000..7558cc3 --- /dev/null +++ b/.watcherignore @@ -0,0 +1,5 @@ +./aboba +./build +./gpp1 +./watcher +./mongoose.o diff --git a/build.c b/build.c index b3bcbca..44c0fb6 100644 --- a/build.c +++ b/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") { diff --git a/gebs b/gebs index 61e59e6..2f9ff2e 160000 --- a/gebs +++ b/gebs @@ -1 +1 @@ -Subproject commit 61e59e655098b29951c3c7eaf5b18a2968be0b38 +Subproject commit 2f9ff2e757341c6639cc0e4f435eb58e617456eb diff --git a/watcher.c b/watcher.c new file mode 100644 index 0000000..ccf47e4 --- /dev/null +++ b/watcher.c @@ -0,0 +1,194 @@ +#include +#include +#include +#include + +#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 \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; +}