From: Mattia Cabrini Date: Sun, 14 Apr 2024 10:44:58 +0000 (+0200) Subject: Initial commit X-Git-Url: https://git.theboydaily.dev/mattia?a=commitdiff_plain;h=3f62dca3e517f68632ca4f7111f994822f2e6445;p=run_at_change.git Initial commit --- 3f62dca3e517f68632ca4f7111f994822f2e6445 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..278bf20 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Mattia Cabrini + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3529b6a --- /dev/null +++ b/Makefile @@ -0,0 +1,16 @@ +build: clear + gcc -lc main.c -o main + +debug: clear + gcc -lc main.c -g -o main + +clear: + rm -f main + +all: build + +run: build + ./main main.c "cat main.c" + +run-debug: debug + gdb ./main main.c "cat main.c" diff --git a/README.md b/README.md new file mode 100644 index 0000000..b6e3ff7 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# Run @ Change + +Runs a command when a file changes. diff --git a/main.c b/main.c new file mode 100644 index 0000000..624b5ad --- /dev/null +++ b/main.c @@ -0,0 +1,181 @@ +/* +MIT License + +Copyright (c) 2023 Mattia Cabrini + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include +#include +#include + +#include +#include + +typedef int bool; + +const int true = 1; +const int false = 0; + +const char* init_watch(int *fd, int *wd, char *path) { + *fd = inotify_init(); + + if (*fd == -1) { + return "could not init inotify watch"; + } + + *wd = inotify_add_watch(*fd, path, IN_MODIFY | IN_IGNORED); + + if (*wd == -1) { + close(*fd); + return "could not add a watch"; + } + + return NULL; +} + +void close_watch(int *fd, int *wd) { + if (*wd >= 0) { + inotify_rm_watch(*fd, *wd); + *wd = -1; + } + + if(*fd >= 0) { + close(*fd); + *fd = -1; + } +} + +bool reg_file_exists(char *path, ino_t *st_ino) { + struct stat s; + + int sres = stat(path, &s); + + if (sres == -1) { + return false; + } + + bool isreg = S_ISREG(s.st_mode); + + if (st_ino != NULL) { + *st_ino = s.st_ino; + } + + return isreg; +} + +bool read_inotify_e(int fd, struct inotify_event *e) { + size_t sz_base_ev_struct = sizeof(struct inotify_event); + size_t sz_buf = sizeof(struct inotify_event) + NAME_MAX + 1; + + size_t bytes_read = read(fd, e, sz_base_ev_struct); + + if (bytes_read != sz_base_ev_struct) { + return false; + } + + if (e->len > 0) { + bytes_read = read(fd, e->name, e->len); + + if (bytes_read != e->len) { + return false; + } + } + + return true; +} + +// argv[1]: file path +// argv[2]: command +int main(int argc, char **argv) { + const char *err; + char *file_path; + + int fd; + int wd; + ino_t st_ino; + + if (argc < 3) { + printf("Usage: run_at_change file_path comm...\n"); + exit(1); + } + + file_path = argv[1]; + + printf("monitoring %s\n", file_path); + + err = init_watch(&fd, &wd, file_path); + + if (err != NULL) { + fprintf(stderr, "%s\n", err); + exit(1); + } + + if (!reg_file_exists(file_path, &st_ino)) { + fprintf(stderr, "the file %s does not exist\n", file_path); + exit(1); + } + + size_t sz_buf = sizeof(struct inotify_event) + NAME_MAX + 1; + struct inotify_event *e = (struct inotify_event *) malloc(sz_buf); + + while(read_inotify_e(fd, e)) { + if (e->wd != wd) { + fprintf(stderr, "weird... e->wd != wd\n"); + continue; + } + + if (e->mask & IN_MODIFY == 0) { + fprintf(stderr, "weird... e->mask & IN_MODIFY == 0\n"); + continue; + } + + if(e->mask & IN_MODIFY) { + printf("changed\n"); + system(argv[2]); + } + + if(e->mask & IN_IGNORED) { + printf("~~~ deleted\n"); + ino_t old_ino = st_ino; + + if (reg_file_exists(file_path, &st_ino)) { + printf("~~~ file still exists\n"); + printf("~~~ ino %d :---> %d\n", old_ino, st_ino); + + close_watch(&fd, &wd); + + err = init_watch(&fd, &wd, file_path); + + if (err != NULL) { + fprintf(stderr, "%s\n", err); + exit(1); + } + } + + system(argv[2]); + } + } + + close_watch(&fd, &wd); + + return fd == 0 ? 1 : 0; +}