From 047a55640ce4c194e5b99266b59d4175efeae058 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Thu, 6 Jul 2017 12:45:58 +0200 Subject: Add iexec --- CMakeLists.txt | 7 ++- README.adoc | 6 +- iexec.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ siprandom.c | 2 +- 4 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 iexec.c 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 + * + * 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 + +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) -- cgit v1.2.3