From 7e30dfb6f0194d83bc78b67664fbf3231cc8d2bd Mon Sep 17 00:00:00 2001
From: Přemysl Eric Janouch 
Date: Mon, 19 Jun 2023 18:57:52 +0200
Subject: iexec: enable not exitting together with the child
---
 iexec.c | 83 +++++++++++++++++++++++++++++++++++++++++------------------------
 1 file changed, 53 insertions(+), 30 deletions(-)
diff --git a/iexec.c b/iexec.c
index 5e4c403..562d49d 100644
--- a/iexec.c
+++ b/iexec.c
@@ -24,20 +24,35 @@
 // This can also work on BSD if someone puts in the effort to support kqueue
 #include 
 
-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_inotify_event (const struct inotify_event *e, const char *base)
 {
-	if (e->wd != g_inotify_wd || strcmp (e->name, base))
+	if (e->wd != g.inotify_wd || strcmp (e->name, base))
 		return;
 
-	print_debug ("file changed, killing child");
-	g_restarting = true;
-	if (g_child >= 0 && kill (g_child, SIGINT))
-		print_error ("kill: %s", strerror (errno));
+	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
@@ -46,7 +61,7 @@ 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)
+	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);
 }
@@ -54,9 +69,9 @@ handle_file_change (const char *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
@@ -69,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
@@ -102,6 +116,7 @@ main (int argc, char *argv[])
 	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" },
@@ -121,6 +136,9 @@ main (int argc, char *argv[])
 	case 'f':
 		target = optarg;
 		break;
+	case 'e':
+		g.exits = true;
+		break;
 	case 'd':
 		g_debug_mode = true;
 		break;
@@ -164,25 +182,30 @@ main (int argc, char *argv[])
 	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,
+	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 (target)));
-	spawn (argv);
-
+	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;
 }
-- 
cgit v1.2.3-70-g09d2