Compare commits
4 Commits
c291e4b6ac
...
7e30dfb6f0
Author | SHA1 | Date | |
---|---|---|---|
7e30dfb6f0 | |||
4cc1baf429 | |||
957aed63a8 | |||
59b78ebc5c |
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2015 - 2021, Přemysl Eric Janouch <p@janouch.name>
|
Copyright (c) 2015 - 2023, 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
119
iexec.c
@ -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, 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
|
* 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,34 +24,54 @@
|
|||||||
// 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 pid_t g_child;
|
static struct
|
||||||
static bool g_restarting = false;
|
{
|
||||||
static int g_inotify_fd, g_inotify_wd;
|
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_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]; ssize_t len; const struct inotify_event *e;
|
char buf[4096];
|
||||||
while ((len = read (g_inotify_fd, buf, sizeof buf)) > 0)
|
ssize_t len = 0;
|
||||||
for (char *ptr = buf; ptr < buf + len; ptr += sizeof *e + e->len)
|
struct inotify_event *e = NULL;
|
||||||
{
|
while ((len = read (g.inotify_fd, buf, sizeof buf)) > 0)
|
||||||
e = (const struct inotify_event *) buf;
|
for (char *ptr = buf; ptr < buf + len; ptr += sizeof *e + e->len)
|
||||||
if (e->wd != g_inotify_wd || strcmp (e->name, base))
|
handle_inotify_event ((e = (struct inotify_event *) buf), 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
|
||||||
@ -64,23 +84,22 @@ spawn (char *argv[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
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;
|
return true;
|
||||||
|
|
||||||
if (!g_restarting)
|
g.child = 0;
|
||||||
|
if (!g.killing)
|
||||||
{
|
{
|
||||||
print_debug ("child died on its own, not respawning");
|
print_debug ("child died on its own, not respawning");
|
||||||
return false;
|
return g.exits;
|
||||||
}
|
|
||||||
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
|
||||||
@ -93,8 +112,11 @@ 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" },
|
||||||
@ -111,6 +133,12 @@ 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;
|
||||||
@ -136,6 +164,9 @@ 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);
|
||||||
@ -148,27 +179,33 @@ 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 = 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));
|
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,
|
||||||
dirname (path), IN_MOVED_TO | IN_CLOSE_WRITE)) < 0)
|
dir, 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 (argv[0])));
|
char *base = basename ((path = xstrdup (target)));
|
||||||
spawn (argv);
|
g.respawn = true;
|
||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
fd_set r; FD_SET (g_inotify_fd, &r);
|
if (g.respawn)
|
||||||
(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 (argv));
|
while (check_child_death ());
|
||||||
|
|
||||||
free (path);
|
free (path);
|
||||||
close (g_inotify_fd);
|
xclose (g.inotify_fd);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
2
liberty
2
liberty
@ -1 +1 @@
|
|||||||
Subproject commit 782a9a5977bd5f2101e8808b94d659fe52e2490a
|
Subproject commit d01a1ff0348174f91bb2d3ba53145cc2c9f50a7f
|
Loading…
x
Reference in New Issue
Block a user