]> Shamusworld >> Repos - init-ng/commitdiff
Added version which conforms to openrc coding guidelines. master
authorShamus Hammons <jlhamm@acm.org>
Wed, 5 Apr 2017 01:29:14 +0000 (20:29 -0500)
committerShamus Hammons <jlhamm@acm.org>
Wed, 5 Apr 2017 01:29:14 +0000 (20:29 -0500)
init-ng.c
openrc-init.c [new file with mode: 0644]

index cc517777e68bc35c48546063338984709477bdd3..7dba91aeb3aa375fcbad2410753ba6928d9733c1 100644 (file)
--- a/init-ng.c
+++ b/init-ng.c
@@ -38,7 +38,7 @@ static char * args[256];      // 256 arguments ought to be enough for anybody ;-)
 // Find a process in the monitored process list (MPL) by PID. Returns the index
 // of the entry in the MPL.
 //
-int FindMonitoredPID(pid_t pid)
+static int FindMonitoredPID(pid_t pid)
 {
        for(int i=0; i<freeProcessSlot; i++)
        {
@@ -67,7 +67,7 @@ static void RemoveEntry(int entry)
 //
 // Add a process to the MPL
 //
-static void AddEntry(pid_t pid, char * name, char * command)
+static void AddEntry(pid_t pid, const char * name, const char * command)
 {
        int i;
 
@@ -90,7 +90,7 @@ static void AddEntry(pid_t pid, char * name, char * command)
 // Take a given space delimited string and turn it into an array of strings
 // suitable for execv().
 //
-static void MakeArgumentList(char * s)
+static void MakeArgumentList(const char * s)
 {
        char buf[4096];
        char * tokSave;
@@ -119,7 +119,7 @@ static void MakeArgumentList(char * s)
 // monitor: Tells Launch whether or not this process should be monitored
 //          and respawned if it goes away (0 = no, 1 = yes)
 //
-static pid_t Launch(char * name, char * command, int monitor)
+static pid_t Launch(const char * name, const char * command, int monitor)
 {
        if (verbose)
                printf("Starting %s (%smonitored)...\n", name, (monitor ? "" : "un"));
@@ -228,7 +228,7 @@ static void ShutDown(int cmd)
 //
 // Connect to PID 1's FIFO to send a command
 //
-static void DoCmd(char * cmd)
+static void DoCmd(const char * cmd)
 {
        FILE * file = fopen(fifoPath, "w");
 
diff --git a/openrc-init.c b/openrc-init.c
new file mode 100644 (file)
index 0000000..c31e077
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Init-NG
+ *
+ * A minimal, drop-in replacement for SysV init.
+ * This leverages OpenRC to do most of the heavy lifting, and does the minimum
+ * amount that it should--unlike other so-called inits, that have world +
+ * kitchen sink thrown in, seemingly to enlarge the attack surface. ;-)
+ *
+ * by James Hammons
+ * (C) 2017 Underground Software
+ */
+
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/reboot.h>
+#include <sys/syscall.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/wait.h>
+
+static const char fifoPath[] = "/dev/initctl";
+static int shuttingDown = 0;
+static int verbose = 0;                /* N.B.: There is no mechanism to set this ATM */
+static struct sigaction action;
+static int freeProcessSlot = 0;
+static pid_t processPID[1024];
+static char * processCmd[1024];
+static char * processName[1024];
+static char * args[256];       /* 256 arguments ought to be enough for anybody ;-) */
+
+
+/*
+ * Find a process in the monitored process list (MPL) by PID. Returns the index
+ * of the entry in the MPL.
+ */
+static int find_monitored_pid(pid_t pid)
+{
+       int i;
+
+       for(i=0; i<freeProcessSlot; i++)
+       {
+               if (processPID[i] == pid)
+                       return i;
+       }
+
+       return -1;
+}
+
+
+/*
+ * Remove the given process from the MPL by index
+ */
+static void remove_entry(int entry)
+{
+       free(processCmd[entry]);
+       free(processName[entry]);
+       processPID[entry] = -1;
+
+       if (entry == (freeProcessSlot - 1))
+               freeProcessSlot--;
+}
+
+
+/*
+ * Add a process to the MPL
+ */
+static void add_entry(pid_t pid, const char * name, const char * command)
+{
+       int i;
+
+       for(i=0; i<freeProcessSlot; i++)
+       {
+               if (processPID[i] == -1)
+                       break;
+       }
+
+       processPID[i] = pid;
+       processCmd[i] = strdup(command);
+       processName[i] = strdup(name);
+
+       if (i == freeProcessSlot)
+               freeProcessSlot++;
+}
+
+
+/*
+ * Take a given space delimited string and turn it into an array of strings
+ * suitable for execv().
+ */
+static void make_argument_list(const char * s)
+{
+       char buf[4096];
+       char * tokSave;
+       int i;
+       char * token;
+
+       strncpy(buf, s, 4095);
+       buf[4095] = 0;
+       i = 0;
+       token = strtok_r(buf, " ", &tokSave);
+
+       while (token != NULL)
+       {
+               args[i++] = token;
+               token = strtok_r(NULL, " ", &tokSave);
+       }
+
+       /* Argument list *must* end with a NULL entry: */
+       args[i] = NULL;
+}
+
+
+/*
+ * Launch a command from PID 1
+ *
+ * name:    The human readable name for the process to launch
+ * command: The command to launch + any parameters it needs
+ * monitor: Tells Launch whether or not this process should be monitored
+ *          and respawned if it goes away (0 = no, 1 = yes)
+ */
+static pid_t launch(const char * name, const char * command, int monitor)
+{
+       pid_t pid;
+
+       if (verbose)
+               printf("Starting %s (%smonitored)...\n", name, (monitor ? "" : "un"));
+
+       make_argument_list(command);
+       pid = fork();
+
+       if (pid == 0)
+       {
+               /* Child process gets here (if fork was successful)... */
+               setsid();
+               execv(args[0], args);
+
+               /* We only get here if an error happened with execv()... Need to figure
+                * out how to handle this gracefully...
+                */
+               printf("execv() failed!\n");
+               return 0;
+       }
+       else if (pid < 0)
+       {
+               printf("Fork failed; can't start %s!\n", name);
+               return pid;
+       }
+
+       /* Parent process gets here... So add to monitor list if requested */
+       if (monitor)
+               add_entry(pid, name, command);
+
+       return pid;
+}
+
+
+/*
+ * Here we do some process monitoring
+ * At some point, we'll read inittab instead of recording just our own internal
+ * processes...
+ */
+static void handle_sigchld(int signum)
+{
+       pid_t pid;
+       int entry;
+
+       while (1)
+       {
+               /* Get PID of exiting child process(es), if any */
+               pid = waitpid(-1, NULL, WNOHANG);
+
+               /* Check for exit conditions... */
+               if (pid == 0)
+                       break;
+               else if (pid == -1)
+               {
+                       if (errno == ECHILD)
+                               break;
+
+                       perror("waitpid");
+                       continue;
+               }
+
+               /* If we're shutting down, we don't do any more process monitoring */
+               if (shuttingDown)
+                       continue;
+
+               entry = find_monitored_pid(pid);
+
+               if (entry != -1)
+               {
+                       /* We remove the old entry last, because it frees the name & cmd */
+                       launch(processName[entry], processCmd[entry], 1);
+                       remove_entry(entry);
+               }
+       }
+}
+
+
+/*
+ * Start up the system, using the awesome power of OpenRC
+ */
+static void init(void)
+{
+       pid_t pid;
+
+       printf("*** init-ng v1.0.1 starting...\n");
+
+       pid = launch("openrc", "/sbin/rc sysinit", 0);
+       waitpid(pid, NULL, 0);
+
+       pid = launch("openrc", "/sbin/rc boot", 0);
+       waitpid(pid, NULL, 0);
+
+       pid = launch("openrc", "/sbin/rc default", 0);
+       waitpid(pid, NULL, 0);
+}
+
+
+static void shut_down(int cmd)
+{
+       pid_t pid;
+
+       shuttingDown = 1;
+       pid = launch("openrc", "/sbin/rc reboot", 0);
+
+       /* We do this because our custom signal handler will repeatedly abort this
+        * call. We probably should do a bit more checking than this, but it seems
+        * to work without any problem as is.
+        */
+       while (waitpid(pid, NULL, 0) != pid);
+
+       sync();
+       reboot(cmd);
+}
+
+
+/*
+ * Connect to PID 1's FIFO to send a command
+ */
+static void do_cmd(char * cmd)
+{
+       FILE * file;
+       size_t ignored;
+
+       file = fopen(fifoPath, "w");
+
+       if (file == NULL)
+       {
+               perror("do_cmd: fopen");
+               return;
+       }
+
+       ignored = fwrite(cmd, 1, strlen(cmd), file);
+       fclose(file);
+}
+
+
+int main(int argc, char * argv[])
+{
+       char buf[2048], buf2[2048];
+       pid_t pid;
+       int i;
+       FILE * file;
+       size_t read;
+
+       pid = getpid();
+
+       /* If we're not PID 1, then we're (possibly) being asked to run a command */
+       if (pid != 1)
+       {
+               if (argc > 1)
+               {
+                       if ((strcmp(argv[1], "poweroff") == 0)
+                               || (strcmp(argv[1], "restart") == 0)
+                               || (strcmp(argv[1], "halt") == 0)
+                               || (strcmp(argv[1], "status") == 0))
+                       {
+                               do_cmd(argv[1]);
+                       }
+                       else if (strcmp(argv[1], "test") == 0)
+                       {
+                               shut_down(RB_POWER_OFF);
+                       }
+                       else
+                               printf("Invalid command\n");
+               }
+               else
+                       printf("Usage: poweroff, restart, halt, status, test\n");
+
+               return 1;
+       }
+
+       /* We are PID EINS, so start the system using OpenRC... */
+       init();
+
+       /* Launch TTYs...
+        * N.B.: To be a drop in replacement for SysV init, we really should read
+        *       and parse the entries in /etc/inittab; at least the c entries...
+        */
+       for(i=1; i<=6; i++)
+       {
+               sprintf(buf, "agetty%i", i);
+               sprintf(buf2, "/sbin/agetty tty%i --noclear", i);
+               launch(buf, buf2, 1);
+       }
+
+       /* Install SIGCHLD signal handler to do process monitoring */
+       memset(&action, 0, sizeof(action));
+       action.sa_handler = handle_sigchld;
+       action.sa_flags = SA_SIGINFO;
+       sigaction(SIGCHLD, &action, NULL);
+
+       /* Create a named pipe to listen on for commands */
+       if (mkfifo(fifoPath, 0600) == -1)
+       {
+               /* This is bad... If this happens, we have no comms */
+               perror("mkfifo");
+       }
+
+       /* Main loop for PID 1. All commands are received and acted upon from here. */
+       while (1)
+       {
+               /* This will block until a command is sent down the pipe... */
+               file = fopen(fifoPath, "r");
+
+               if (file == NULL)
+               {
+                       /* Check to see if the signal handler blew things up on us */
+                       if (errno == EINTR)
+                               continue;
+
+                       perror("fopen");
+                       continue;
+               }
+
+               /* Grab whatever was shoved into the FIFO */
+               read = fread(buf, 1, 2047, file);
+               buf[read] = 0;
+               fclose(file);
+
+               if (verbose)
+                       printf("PID1: Received \"%s\" from FIFO...\n", buf);
+
+               /* Now do something with it... */
+               if (strcmp(buf, "poweroff") == 0)
+               {
+                       shut_down(RB_POWER_OFF);
+               }
+               else if (strcmp(buf, "restart") == 0)
+               {
+                       shut_down(RB_AUTOBOOT);
+               }
+               else if (strcmp(buf, "halt") == 0)
+               {
+                       shut_down(RB_HALT_SYSTEM);
+               }
+               else if (strcmp(buf, "status") == 0)
+               {
+                       printf("[Status placeholder]\n");
+               }
+       }
+
+       /* We should never get here... */
+       return 0;
+}
+