diff options
Diffstat (limited to 'iexec.c')
-rw-r--r-- | iexec.c | 113 |
1 files changed, 75 insertions, 38 deletions
@@ -1,7 +1,7 @@ /* * iexec.c: run a program and restart on file change * - * Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name> + * Copyright (c) 2017 - 2023, Přemysl Eric Janouch <p@janouch.name> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted. @@ -24,34 +24,54 @@ // This can also work on BSD if someone puts in the effort to support kqueue #include <sys/inotify.h> -static pid_t g_child; -static bool g_restarting = false; -static int g_inotify_fd, g_inotify_wd; +static struct +{ + pid_t child; ///< Watched child or 0 + bool exits; ///< Don't restart child when it exits + bool respawn; ///< Respawn child ASAP + bool killing; ///< Waiting for child to die + int inotify_fd, inotify_wd; +} +g; +// Note that this program doesn't queue up file-based restarts static void -handle_file_change (const char *base) +handle_inotify_event (const struct inotify_event *e, const char *base) { - char buf[4096]; ssize_t len; const struct inotify_event *e; - while ((len = read (g_inotify_fd, buf, sizeof buf)) > 0) - for (char *ptr = buf; ptr < buf + len; ptr += sizeof *e + e->len) - { - e = (const struct inotify_event *) buf; - if (e->wd != g_inotify_wd || strcmp (e->name, base)) - continue; + if (e->wd != g.inotify_wd || strcmp (e->name, base)) + return; + if (g.child) + { print_debug ("file changed, killing child"); - g_restarting = true; - if (kill (g_child, SIGINT)) + if (kill (g.child, SIGINT)) print_error ("kill: %s", strerror (errno)); + g.killing = true; + } + else + { + print_debug ("file changed, respawning"); + g.respawn = true; } } static void +handle_file_change (const char *base) +{ + char buf[4096]; + ssize_t len = 0; + struct inotify_event *e = NULL; + while ((len = read (g.inotify_fd, buf, sizeof buf)) > 0) + for (char *ptr = buf; ptr < buf + len; ptr += sizeof *e + e->len) + handle_inotify_event ((e = (struct inotify_event *) buf), base); +} + +static void spawn (char *argv[]) { - if ((g_child = fork ()) == -1) + if ((g.child = fork ()) == -1) exit_fatal ("fork: %s", strerror (errno)); - else if (g_child) + else if (g.child) return; // A linker can create spurious CLOSE_WRITEs, wait until it's executable @@ -64,23 +84,22 @@ spawn (char *argv[]) } static bool -check_child_death (char *argv[]) +check_child_death (void) { - if (waitpid (g_child, NULL, WNOHANG) != g_child) + int status = 0; + if (waitpid (g.child, &status, WNOHANG) != g.child) return true; - if (!g_restarting) + g.child = 0; + if (!g.killing) { print_debug ("child died on its own, not respawning"); - return false; - } - else - { - print_debug ("child died on request, respawning"); - spawn (argv); - g_restarting = false; - return true; + return g.exits; } + + g.killing = false; + print_debug ("child died on request, respawning"); + return g.respawn = true; } static void @@ -93,8 +112,11 @@ sigchld_handler (int signum) int main (int argc, char *argv[]) { + const char *target = NULL; static const struct opt opts[] = { + { 'f', "file", "PATH", 0, "watch this path rather than the program" }, + { 'e', "exits", NULL, 0, "allow the program to exit on its own" }, { 'd', "debug", NULL, 0, "run in debug mode" }, { 'h', "help", NULL, 0, "display this help and exit" }, { 'V', "version", NULL, 0, "output version information and exit" }, @@ -111,6 +133,12 @@ main (int argc, char *argv[]) while ((c = opt_handler_get (&oh)) != -1) switch (c) { + case 'f': + target = optarg; + break; + case 'e': + g.exits = true; + break; case 'd': g_debug_mode = true; break; @@ -136,6 +164,9 @@ main (int argc, char *argv[]) argc -= optind; argv += optind; + if (!target) + target = argv[0]; + (void) signal (SIGPIPE, SIG_IGN); struct sigaction sa = { .sa_handler = sigchld_handler }; sigemptyset (&sa.sa_mask); @@ -148,27 +179,33 @@ main (int argc, char *argv[]) if (sigprocmask (SIG_BLOCK, &chld, &orig)) exit_fatal ("sigprocmask: %s", strerror (errno)); - char *path = xstrdup (argv[0]); + char *path = NULL; + char *dir = dirname ((path = xstrdup (target))); - if ((g_inotify_fd = inotify_init1 (IN_NONBLOCK)) < 0) + if ((g.inotify_fd = inotify_init1 (IN_NONBLOCK)) < 0) exit_fatal ("inotify_init1: %s", strerror (errno)); - if ((g_inotify_wd = inotify_add_watch (g_inotify_fd, - dirname (path), IN_MOVED_TO | IN_CLOSE_WRITE)) < 0) + if ((g.inotify_wd = inotify_add_watch (g.inotify_fd, + dir, IN_MOVED_TO | IN_CLOSE_WRITE)) < 0) exit_fatal ("inotify_add_watch: %s", strerror (errno)); free (path); - char *base = basename ((path = xstrdup (argv[0]))); - spawn (argv); - + char *base = basename ((path = xstrdup (target))); + g.respawn = true; do { - fd_set r; FD_SET (g_inotify_fd, &r); - (void) pselect (g_inotify_fd + 1, &r, NULL, NULL, NULL, &orig); + if (g.respawn) + { + spawn (argv); + g.respawn = false; + } + + fd_set r; FD_SET (g.inotify_fd, &r); + (void) pselect (g.inotify_fd + 1, &r, NULL, NULL, NULL, &orig); handle_file_change (base); } - while (check_child_death (argv)); + while (check_child_death ()); free (path); - close (g_inotify_fd); + xclose (g.inotify_fd); return EXIT_SUCCESS; } |