Compare commits

..

No commits in common. "7e30dfb6f0194d83bc78b67664fbf3231cc8d2bd" and "c291e4b6ac5cfedd72fcdbb938a29d41e28fc855" have entirely different histories.

3 changed files with 43 additions and 80 deletions

View File

@ -1,4 +1,4 @@
Copyright (c) 2015 - 2023, Přemysl Eric Janouch <p@janouch.name> Copyright (c) 2015 - 2021, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted. purpose with or without fee is hereby granted.

119
iexec.c
View File

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

@ -1 +1 @@
Subproject commit d01a1ff0348174f91bb2d3ba53145cc2c9f50a7f Subproject commit 782a9a5977bd5f2101e8808b94d659fe52e2490a