aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt7
-rw-r--r--README.adoc6
-rw-r--r--iexec.c172
-rw-r--r--siprandom.c2
4 files changed, 182 insertions, 5 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b93fbd7..4ce01ee 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -51,6 +51,9 @@ target_link_libraries (fancontrol-ng ${project_libraries})
add_executable (priod priod.c)
target_link_libraries (priod ${project_libraries})
+add_executable (iexec iexec.c)
+target_link_libraries (iexec ${project_libraries})
+
if (WITH_GDM)
include_directories (${gdm_INCLUDE_DIRS})
add_executable (gdm-switch-user gdm-switch-user.c)
@@ -90,8 +93,8 @@ if (WITH_GDM)
install (TARGETS gdm-switch-user DESTINATION ${CMAKE_INSTALL_BINDIR})
endif (WITH_GDM)
-install (TARGETS wmstatus brightness input-switch fancontrol-ng priod siprandom
- DESTINATION ${CMAKE_INSTALL_BINDIR})
+install (TARGETS wmstatus brightness input-switch fancontrol-ng priod iexec
+ siprandom DESTINATION ${CMAKE_INSTALL_BINDIR})
install (PROGRAMS shellify DESTINATION ${CMAKE_INSTALL_BINDIR})
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
diff --git a/README.adoc b/README.adoc
index 63c919b..595d5f9 100644
--- a/README.adoc
+++ b/README.adoc
@@ -13,10 +13,12 @@ to other people as well:
- 'fancontrol-ng' is a clone of fancontrol that can handle errors on resume
from suspend instead of setting fans to maximum speed and quitting;
in general it doesn't handle everything the original does
- - 'priod' sets CPU, I/O and OOM killer priorities for new processes according
- to configuration
+ - 'priod' sets process CPU, I/O and OOM killer priorities automatically
+ according to configuration
- 'shellify' is a simple script that sets up a shell for commands like vgdb
and nmcli that are painfully lacking it
+ - 'iexec' runs a program and attempts to restart it cleanly when the
+ executable file is replaced on the disk
- 'gdm-switch-user' tells the running GDM daemon, if any, to show the switch
user screen
- 'siprandom' uses the SipHash 2-4 algorithm to produce a stream of
diff --git a/iexec.c b/iexec.c
new file mode 100644
index 0000000..a05e1df
--- /dev/null
+++ b/iexec.c
@@ -0,0 +1,172 @@
+/*
+ * iexec.c: run a program and restart on file change
+ *
+ * Copyright (c) 2017, Přemysl Janouch <p.janouch@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "config.h"
+#undef PROGRAM_NAME
+#define PROGRAM_NAME "iexec"
+#include "liberty/liberty.c"
+
+// 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 void
+handle_file_change (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;
+
+ print_debug ("file changed, killing child");
+ g_restarting = true;
+ if (kill (g_child, SIGINT))
+ print_error ("kill: %s", strerror (errno));
+ }
+}
+
+static void
+spawn (char *argv[])
+{
+ if ((g_child = fork ()) == -1)
+ exit_fatal ("fork: %s", strerror (errno));
+ else if (!g_child)
+ {
+ execvp (argv[0], argv);
+ exit_fatal ("execvp: %s", strerror (errno));
+ }
+}
+
+static bool
+check_child_death (char *argv[])
+{
+ if (waitpid (g_child, NULL, WNOHANG) != g_child)
+ return true;
+
+ if (!g_restarting)
+ {
+ 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;
+ }
+}
+
+static void
+sigchld_handler (int signum)
+{
+ // We need to have this handler so that pselect() can return EINTR
+ (void) signum;
+}
+
+int
+main (int argc, char *argv[])
+{
+ static const struct opt opts[] =
+ {
+ { '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" },
+ { 0, NULL, NULL, 0, NULL }
+ };
+
+ struct opt_handler oh = opt_handler_make (argc, argv, opts,
+ "PROGRAM [ARG...]", "Run a program and restart on file change.");
+
+ // We have to turn that off as it causes more trouble than what it's worth
+ char *nonpermuting = xstrdup_printf ("+%s", oh.opt_string);
+ free (oh.opt_string);
+ oh.opt_string = nonpermuting;
+
+ int c;
+ while ((c = opt_handler_get (&oh)) != -1)
+ switch (c)
+ {
+ case 'd':
+ g_debug_mode = true;
+ break;
+ case 'h':
+ opt_handler_usage (&oh, stdout);
+ exit (EXIT_SUCCESS);
+ case 'V':
+ printf (PROGRAM_NAME " " PROGRAM_VERSION "\n");
+ exit (EXIT_SUCCESS);
+ default:
+ print_error ("wrong options");
+ opt_handler_usage (&oh, stderr);
+ exit (EXIT_FAILURE);
+ }
+
+ if (argc == optind)
+ {
+ opt_handler_usage (&oh, stderr);
+ exit (EXIT_FAILURE);
+ }
+
+ opt_handler_free (&oh);
+ argc -= optind;
+ argv += optind;
+
+ (void) signal (SIGPIPE, SIG_IGN);
+ struct sigaction sa = { .sa_handler = sigchld_handler };
+ sigemptyset (&sa.sa_mask);
+ if (sigaction (SIGCHLD, &sa, NULL))
+ exit_fatal ("sigaction: %s", strerror (errno));
+
+ sigset_t chld, orig;
+ sigemptyset (&chld);
+ sigaddset (&chld, SIGCHLD);
+ if (sigprocmask (SIG_BLOCK, &chld, &orig))
+ exit_fatal ("sigprocmask: %s", strerror (errno));
+
+ char *path = xstrdup (argv[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)
+ exit_fatal ("inotify_add_watch: %s", strerror (errno));
+
+ free (path);
+ char *base = basename ((path = xstrdup (argv[0])));
+ spawn (argv);
+
+ do
+ {
+ 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));
+
+ free (path);
+ close (g_inotify_fd);
+ return EXIT_SUCCESS;
+}
diff --git a/siprandom.c b/siprandom.c
index 690772c..1a4d336 100644
--- a/siprandom.c
+++ b/siprandom.c
@@ -34,7 +34,7 @@ parse_program_arguments (int argc, char **argv)
};
struct opt_handler oh =
- opt_handler_make (argc, argv, opts, "CONFIG", "PRNG.");
+ opt_handler_make (argc, argv, opts, NULL, "PRNG.");
int c;
while ((c = opt_handler_get (&oh)) != -1)