9b65e1
From 6d1619802269db2f62c6b51b03a049c3d3a23a2c Mon Sep 17 00:00:00 2001
9b65e1
From: Frantisek Kluknavsky <fkluknav@redhat.com>
9b65e1
Date: Mon, 21 Aug 2017 16:44:19 +0200
9b65e1
Subject: [PATCH] rhel6 features
9b65e1
9b65e1
---
9b65e1
 dumb-init.c | 199 +++++++++++++++++++++++++++++++++++++++++++++++++++---------
9b65e1
 1 file changed, 171 insertions(+), 28 deletions(-)
9b65e1
9b65e1
diff --git a/dumb-init.c b/dumb-init.c
9b65e1
index 65e69ef..3893b8c 100644
9b65e1
--- a/dumb-init.c
9b65e1
+++ b/dumb-init.c
9b65e1
@@ -19,6 +19,8 @@
9b65e1
 #include <sys/types.h>
9b65e1
 #include <sys/wait.h>
9b65e1
 #include <unistd.h>
9b65e1
+#include <dirent.h>
9b65e1
+#include <ctype.h>
9b65e1
 #include "VERSION.h"
9b65e1
 
9b65e1
 #define PRINTERR(...) do { \
9b65e1
@@ -34,14 +36,18 @@
9b65e1
 // Signals we care about are numbered from 1 to 31, inclusive.
9b65e1
 // (32 and above are real-time signals.)
9b65e1
 // TODO: this is likely not portable outside of Linux, or on strange architectures
9b65e1
-#define MAXSIG 31
9b65e1
+// EDIT: we actually care about some real-time signals as well
9b65e1
+// SIGRTMAX is not a constant, use 64
9b65e1
+#define MAXSIG 64
9b65e1
 
9b65e1
 // Indices are one-indexed (signal 1 is at index 1). Index zero is unused.
9b65e1
 int signal_rewrite[MAXSIG + 1] = {[0 ... MAXSIG] = -1};
9b65e1
+char *signal_action[MAXSIG + 1] = {[0 ... MAXSIG] = ""};
9b65e1
 
9b65e1
 pid_t child_pid = -1;
9b65e1
 char debug = 0;
9b65e1
 char use_setsid = 1;
9b65e1
+static char survive_bereaving = 0;
9b65e1
 
9b65e1
 int translate_signal(int signum) {
9b65e1
     if (signum <= 0 || signum > MAXSIG) {
9b65e1
@@ -57,17 +63,75 @@ int translate_signal(int signum) {
9b65e1
     }
9b65e1
 }
9b65e1
 
9b65e1
+void do_action(int signum) {
9b65e1
+    DEBUG("Action for signal %d: running %s\n", signum, signal_action[signum]);
9b65e1
+    int child_pid = fork();
9b65e1
+    if (child_pid < 0) {
9b65e1
+        PRINTERR("Unable to fork. Exiting.\n");
9b65e1
+        exit(1);
9b65e1
+    } else if (child_pid == 0) {
9b65e1
+        /* child */
9b65e1
+        sigset_t all_signals;
9b65e1
+        sigfillset(&all_signals);
9b65e1
+        sigprocmask(SIG_UNBLOCK, &all_signals, NULL);
9b65e1
+        execlp("/bin/bash", "/bin/bash", "-c", signal_action[signum], (char *)NULL);
9b65e1
+
9b65e1
+        // if this point is reached, exec failed, so we should exit nonzero
9b65e1
+        PRINTERR("Could not exec %s: %s\n", signal_action[signum], strerror(errno));
9b65e1
+        exit(1);
9b65e1
+    }
9b65e1
+}
9b65e1
+
9b65e1
 void forward_signal(int signum) {
9b65e1
-    signum = translate_signal(signum);
9b65e1
-    if (signum != -1) {
9b65e1
-        kill(use_setsid ? -child_pid : child_pid, signum);
9b65e1
-        DEBUG("Forwarded signal %d to children.\n", signum);
9b65e1
+    int new_signum = translate_signal(signum);
9b65e1
+    if (new_signum == -2) {
9b65e1
+        do_action(signum);
9b65e1
+    } else if (new_signum != -1) {
9b65e1
+        kill(use_setsid ? -child_pid : child_pid, new_signum);
9b65e1
+        DEBUG("Forwarded signal %d to children.\n", new_signum);
9b65e1
     } else {
9b65e1
-        DEBUG("Not forwarding signal %d to children (ignored).\n", signum);
9b65e1
+        DEBUG("Not forwarding signal %d to children (ignored).\n", new_signum);
9b65e1
     }
9b65e1
 }
9b65e1
 
9b65e1
 /*
9b65e1
+ * Read /proc and see if there are processes except init(PIDs)
9b65e1
+ */
9b65e1
+signed int process_count() {
9b65e1
+    DIR *dp;
9b65e1
+    struct dirent *ep;
9b65e1
+    char nonnumber;
9b65e1
+    signed int count = 0;
9b65e1
+
9b65e1
+    dp = opendir ("/proc");
9b65e1
+    if (dp != NULL)
9b65e1
+    {
9b65e1
+        while ((ep = readdir (dp)) != NULL) {
9b65e1
+            nonnumber = 0;
9b65e1
+            for (int i = 0; ep->d_name[i] != 0; ++i) {
9b65e1
+                if (!isdigit(ep->d_name[i])) {
9b65e1
+                    nonnumber = 1;
9b65e1
+                    break;
9b65e1
+                }
9b65e1
+            }
9b65e1
+            if (!nonnumber) {
9b65e1
+                DEBUG("/proc/%s is a process\n", ep->d_name);
9b65e1
+                ++count;
9b65e1
+                if (count > 1) {
9b65e1
+                    closedir(dp);
9b65e1
+                    return 2; //2 is enough, do not count further
9b65e1
+                }
9b65e1
+            }
9b65e1
+        }
9b65e1
+        closedir(dp);
9b65e1
+    } else {
9b65e1
+        PRINTERR("Could not open /proc.\n");
9b65e1
+        return -1;
9b65e1
+    }
9b65e1
+    return count;
9b65e1
+}
9b65e1
+
9b65e1
+/*
9b65e1
  * The dumb-init signal handler.
9b65e1
  *
9b65e1
  * The main job of this signal handler is to forward signals along to our child
9b65e1
@@ -88,6 +152,7 @@ void forward_signal(int signum) {
9b65e1
  *
9b65e1
 */
9b65e1
 void handle_signal(int signum) {
9b65e1
+    static char bereaved = 0;
9b65e1
     DEBUG("Received signal %d.\n", signum);
9b65e1
     if (signum == SIGCHLD) {
9b65e1
         int status, exit_status;
9b65e1
@@ -103,11 +168,26 @@ void handle_signal(int signum) {
9b65e1
             }
9b65e1
 
9b65e1
             if (killed_pid == child_pid) {
9b65e1
-                forward_signal(SIGTERM);  // send SIGTERM to any remaining children
9b65e1
-                DEBUG("Child exited with status %d. Goodbye.\n", exit_status);
9b65e1
-                exit(exit_status);
9b65e1
+                bereaved = 1;
9b65e1
+                if (!survive_bereaving) {
9b65e1
+                    forward_signal(SIGTERM);  // send SIGTERM to any remaining children
9b65e1
+                    DEBUG("Child exited with status %d. Goodbye.\n", exit_status);
9b65e1
+                    exit(exit_status);
9b65e1
+                } else {
9b65e1
+                    DEBUG("Child exited with status %d. Stay alive for your grandchildren.\n", exit_status);
9b65e1
+                }
9b65e1
             }
9b65e1
         }
9b65e1
+         
9b65e1
+        if ((bereaved == 1) && survive_bereaving) {
9b65e1
+            signed int pc = process_count();
9b65e1
+            DEBUG("Process count: %d\n", pc);
9b65e1
+            if (pc <= 1) {
9b65e1
+                DEBUG("No process left, exitting.\n");
9b65e1
+                exit(0);
9b65e1
+            }
9b65e1
+        }
9b65e1
+
9b65e1
     } else {
9b65e1
         forward_signal(signum);
9b65e1
         if (signum == SIGTSTP || signum == SIGTTOU || signum == SIGTTIN) {
9b65e1
@@ -126,15 +206,21 @@ void print_help(char *argv[]) {
9b65e1
         "It is designed to run as PID1 in minimal container environments.\n"
9b65e1
         "\n"
9b65e1
         "Optional arguments:\n"
9b65e1
-        "   -c, --single-child   Run in single-child mode.\n"
9b65e1
-        "                        In this mode, signals are only proxied to the\n"
9b65e1
-        "                        direct child and not any of its descendants.\n"
9b65e1
-        "   -r, --rewrite s:r    Rewrite received signal s to new signal r before proxying.\n"
9b65e1
-        "                        To ignore (not proxy) a signal, rewrite it to 0.\n"
9b65e1
-        "                        This option can be specified multiple times.\n"
9b65e1
-        "   -v, --verbose        Print debugging information to stderr.\n"
9b65e1
-        "   -h, --help           Print this help message and exit.\n"
9b65e1
-        "   -V, --version        Print the current version and exit.\n"
9b65e1
+        "   -c, --single-child      Run in single-child mode.\n"
9b65e1
+        "                           In this mode, signals are only proxied to the\n"
9b65e1
+        "                           direct child and not any of its descendants.\n"
9b65e1
+        "   -b, --survive-bereaving Do not quit when the direct child dies.\n"
9b65e1
+        "   -r, --rewrite s:r       Rewrite received signal s to new signal r before proxying.\n"
9b65e1
+        "                           To ignore (not proxy) a signal, rewrite it to 0.\n"
9b65e1
+        "                           To rewrite all signals, rewrite (otherwise nonexistent) signal 0.\n"
9b65e1
+        "                           (Useful to ignore all signals, use '--rewrite 0:0').\n"
9b65e1
+        "                           This option can be specified multiple times.\n"
9b65e1
+        "   -a, --action s:exe      Run exe after receiving sinal s.\n"
9b65e1
+        "                           For example, -a '2:echo hi there'.\n"
9b65e1
+        "                           This option can be specified multiple times.\n"
9b65e1
+        "   -v, --verbose           Print debugging information to stderr.\n"
9b65e1
+        "   -h, --help              Print this help message and exit.\n"
9b65e1
+        "   -V, --version           Print the current version and exit.\n"
9b65e1
         "\n"
9b65e1
         "Full help is available online at https://github.com/Yelp/dumb-init\n",
9b65e1
         VERSION,
9b65e1
@@ -146,11 +232,24 @@ void print_rewrite_signum_help() {
9b65e1
     fprintf(
9b65e1
         stderr,
9b65e1
         "Usage: -r option takes <signum>:<signum>, where <signum> "
9b65e1
+        "is between 0 and %d.\n"
9b65e1
+        "This option can be specified multiple times.\n"
9b65e1
+        "Use --help for full usage.\n",
9b65e1
+        MAXSIG
9b65e1
+    );
9b65e1
+    exit(1);
9b65e1
+}
9b65e1
+
9b65e1
+void print_action_help() {
9b65e1
+    fprintf(
9b65e1
+        stderr,
9b65e1
+        "Usage: -a option takes <signum>:<path>, where <signum> "
9b65e1
         "is between 1 and %d.\n"
9b65e1
         "This option can be specified multiple times.\n"
9b65e1
         "Use --help for full usage.\n",
9b65e1
         MAXSIG
9b65e1
     );
9b65e1
+
9b65e1
     exit(1);
9b65e1
 }
9b65e1
 
9b65e1
@@ -158,15 +257,36 @@ void parse_rewrite_signum(char *arg) {
9b65e1
     int signum, replacement;
9b65e1
     if (
9b65e1
         sscanf(arg, "%d:%d", &signum, &replacement) == 2 &&
9b65e1
-        (signum >= 1 && signum <= MAXSIG) &&
9b65e1
+        (signum >= 0 && signum <= MAXSIG) &&
9b65e1
         (replacement >= 0 && replacement <= MAXSIG)
9b65e1
     ) {
9b65e1
-        signal_rewrite[signum] = replacement;
9b65e1
+        if (signum == 0) {
9b65e1
+            for (int i = 0; i <= MAXSIG; ++i) {
9b65e1
+                signal_rewrite[i] = replacement;
9b65e1
+            }
9b65e1
+        } else {
9b65e1
+            signal_rewrite[signum] = replacement;
9b65e1
+        }
9b65e1
     } else {
9b65e1
         print_rewrite_signum_help();
9b65e1
     }
9b65e1
 }
9b65e1
 
9b65e1
+void parse_action(char *arg) {
9b65e1
+    int signum;
9b65e1
+    int status;
9b65e1
+    int position;
9b65e1
+    if (
9b65e1
+        (status = sscanf(arg, "%d:%n", &signum, &position)) == 1 &&
9b65e1
+        (signum >= 0 && signum <= MAXSIG)
9b65e1
+    ) {
9b65e1
+        DEBUG("signal action: %d, position %d\n", signum, position);
9b65e1
+        signal_action[signum] = &(arg[position]);
9b65e1
+        signal_rewrite[signum] = -2;
9b65e1
+    } else {
9b65e1
+        print_action_help();
9b65e1
+    }
9b65e1
+}
9b65e1
 void set_rewrite_to_sigstop_if_not_defined(int signum) {
9b65e1
     if (signal_rewrite[signum] == -1)
9b65e1
         signal_rewrite[signum] = SIGSTOP;
9b65e1
@@ -175,14 +295,16 @@ void set_rewrite_to_sigstop_if_not_defined(int signum) {
9b65e1
 char **parse_command(int argc, char *argv[]) {
9b65e1
     int opt;
9b65e1
     struct option long_options[] = {
9b65e1
-        {"help",         no_argument,       NULL, 'h'},
9b65e1
-        {"single-child", no_argument,       NULL, 'c'},
9b65e1
-        {"rewrite",      required_argument, NULL, 'r'},
9b65e1
-        {"verbose",      no_argument,       NULL, 'v'},
9b65e1
-        {"version",      no_argument,       NULL, 'V'},
9b65e1
+        {"help",             no_argument,       NULL, 'h'},
9b65e1
+        {"single-child",     no_argument,       NULL, 'c'},
9b65e1
+        {"rewrite",          required_argument, NULL, 'r'},
9b65e1
+        {"verbose",          no_argument,       NULL, 'v'},
9b65e1
+        {"version",          no_argument,       NULL, 'V'},
9b65e1
+        {"survive-bereaving",no_argument,       NULL, 'b'},
9b65e1
+        {"action",           required_argument, NULL, 'a'},
9b65e1
         {NULL,                     0,       NULL,   0},
9b65e1
     };
9b65e1
-    while ((opt = getopt_long(argc, argv, "+hvVcr:", long_options, NULL)) != -1) {
9b65e1
+    while ((opt = getopt_long(argc, argv, "+hvVcbr:a:", long_options, NULL)) != -1) {
9b65e1
         switch (opt) {
9b65e1
             case 'h':
9b65e1
                 print_help(argv);
9b65e1
@@ -199,7 +321,14 @@ char **parse_command(int argc, char *argv[]) {
9b65e1
             case 'r':
9b65e1
                 parse_rewrite_signum(optarg);
9b65e1
                 break;
9b65e1
+            case 'a':
9b65e1
+                parse_action(optarg);
9b65e1
+                break;
9b65e1
+            case 'b':
9b65e1
+                survive_bereaving = 1;
9b65e1
+                break;
9b65e1
             default:
9b65e1
+                PRINTERR("Error while parsing arguments.\n");
9b65e1
                 exit(1);
9b65e1
         }
9b65e1
     }
9b65e1
@@ -295,8 +424,22 @@ int main(int argc, char *argv[]) {
9b65e1
         /* parent */
9b65e1
         DEBUG("Child spawned with PID %d.\n", child_pid);
9b65e1
         for (;;) {
9b65e1
-            int signum;
9b65e1
-            sigwait(&all_signals, &signum);
9b65e1
+            struct timespec timeout = {1, 0};
9b65e1
+            int signum = sigtimedwait(&all_signals, NULL, &timeout);
9b65e1
+            if (signum == -1) {
9b65e1
+                switch (errno) {
9b65e1
+                    case EINVAL: 
9b65e1
+                        PRINTERR("Invalid timeout, report this as a bug!\n");
9b65e1
+                        exit(1);
9b65e1
+                    case EINTR:
9b65e1
+                        PRINTERR("Wait interrupted by a signal. This should never happen. Report this as a bug!\n");
9b65e1
+                        exit(1);
9b65e1
+                    case EAGAIN:
9b65e1
+                        //pretend timeout to be SIGCHLD, check if we want to continue running
9b65e1
+                        signum = SIGCHLD;
9b65e1
+                        DEBUG("Heartbeat...\n");
9b65e1
+                }
9b65e1
+            }
9b65e1
             handle_signal(signum);
9b65e1
         }
9b65e1
     }
9b65e1
-- 
9b65e1
2.13.3
9b65e1