4 * A minimal, drop-in replacement for SysV init.
5 * This leverages OpenRC to do most of the heavy lifting, and does the minimum
6 * amount that it should--unlike other so-called inits, that have world +
7 * kitchen sink thrown in, seemingly to enlarge the attack surface. ;-)
10 * (C) 2017 Underground Software
19 #include <sys/reboot.h>
20 #include <sys/syscall.h>
22 #include <sys/types.h>
26 static const char fifoPath[] = "/dev/initctl";
27 static int shuttingDown = 0;
28 static int verbose = 0; /* N.B.: There is no mechanism to set this ATM */
29 static struct sigaction action;
30 static int freeProcessSlot = 0;
31 static pid_t processPID[1024];
32 static char * processCmd[1024];
33 static char * processName[1024];
34 static char * args[256]; /* 256 arguments ought to be enough for anybody ;-) */
38 * Find a process in the monitored process list (MPL) by PID. Returns the index
39 * of the entry in the MPL.
41 static int find_monitored_pid(pid_t pid)
45 for(i=0; i<freeProcessSlot; i++)
47 if (processPID[i] == pid)
56 * Remove the given process from the MPL by index
58 static void remove_entry(int entry)
60 free(processCmd[entry]);
61 free(processName[entry]);
62 processPID[entry] = -1;
64 if (entry == (freeProcessSlot - 1))
70 * Add a process to the MPL
72 static void add_entry(pid_t pid, const char * name, const char * command)
76 for(i=0; i<freeProcessSlot; i++)
78 if (processPID[i] == -1)
83 processCmd[i] = strdup(command);
84 processName[i] = strdup(name);
86 if (i == freeProcessSlot)
92 * Take a given space delimited string and turn it into an array of strings
93 * suitable for execv().
95 static void make_argument_list(const char * s)
102 strncpy(buf, s, 4095);
105 token = strtok_r(buf, " ", &tokSave);
107 while (token != NULL)
110 token = strtok_r(NULL, " ", &tokSave);
113 /* Argument list *must* end with a NULL entry: */
119 * Launch a command from PID 1
121 * name: The human readable name for the process to launch
122 * command: The command to launch + any parameters it needs
123 * monitor: Tells Launch whether or not this process should be monitored
124 * and respawned if it goes away (0 = no, 1 = yes)
126 static pid_t launch(const char * name, const char * command, int monitor)
131 printf("Starting %s (%smonitored)...\n", name, (monitor ? "" : "un"));
133 make_argument_list(command);
138 /* Child process gets here (if fork was successful)... */
140 execv(args[0], args);
142 /* We only get here if an error happened with execv()... Need to figure
143 * out how to handle this gracefully...
145 printf("execv() failed!\n");
150 printf("Fork failed; can't start %s!\n", name);
154 /* Parent process gets here... So add to monitor list if requested */
156 add_entry(pid, name, command);
163 * Here we do some process monitoring
164 * At some point, we'll read inittab instead of recording just our own internal
167 static void handle_sigchld(int signum)
174 /* Get PID of exiting child process(es), if any */
175 pid = waitpid(-1, NULL, WNOHANG);
177 /* Check for exit conditions... */
189 /* If we're shutting down, we don't do any more process monitoring */
193 entry = find_monitored_pid(pid);
197 /* We remove the old entry last, because it frees the name & cmd */
198 launch(processName[entry], processCmd[entry], 1);
206 * Start up the system, using the awesome power of OpenRC
208 static void init(void)
212 printf("*** init-ng v1.0.1 starting...\n");
214 pid = launch("openrc", "/sbin/rc sysinit", 0);
215 waitpid(pid, NULL, 0);
217 pid = launch("openrc", "/sbin/rc boot", 0);
218 waitpid(pid, NULL, 0);
220 pid = launch("openrc", "/sbin/rc default", 0);
221 waitpid(pid, NULL, 0);
225 static void shut_down(int cmd)
230 pid = launch("openrc", "/sbin/rc reboot", 0);
232 /* We do this because our custom signal handler will repeatedly abort this
233 * call. We probably should do a bit more checking than this, but it seems
234 * to work without any problem as is.
236 while (waitpid(pid, NULL, 0) != pid);
244 * Connect to PID 1's FIFO to send a command
246 static void do_cmd(char * cmd)
251 file = fopen(fifoPath, "w");
255 perror("do_cmd: fopen");
259 ignored = fwrite(cmd, 1, strlen(cmd), file);
264 int main(int argc, char * argv[])
266 char buf[2048], buf2[2048];
274 /* If we're not PID 1, then we're (possibly) being asked to run a command */
279 if ((strcmp(argv[1], "poweroff") == 0)
280 || (strcmp(argv[1], "restart") == 0)
281 || (strcmp(argv[1], "halt") == 0)
282 || (strcmp(argv[1], "status") == 0))
286 else if (strcmp(argv[1], "test") == 0)
288 shut_down(RB_POWER_OFF);
291 printf("Invalid command\n");
294 printf("Usage: poweroff, restart, halt, status, test\n");
299 /* We are PID EINS, so start the system using OpenRC... */
303 * N.B.: To be a drop in replacement for SysV init, we really should read
304 * and parse the entries in /etc/inittab; at least the c entries...
308 sprintf(buf, "agetty%i", i);
309 sprintf(buf2, "/sbin/agetty tty%i --noclear", i);
310 launch(buf, buf2, 1);
313 /* Install SIGCHLD signal handler to do process monitoring */
314 memset(&action, 0, sizeof(action));
315 action.sa_handler = handle_sigchld;
316 action.sa_flags = SA_SIGINFO;
317 sigaction(SIGCHLD, &action, NULL);
319 /* Create a named pipe to listen on for commands */
320 if (mkfifo(fifoPath, 0600) == -1)
322 /* This is bad... If this happens, we have no comms */
326 /* Main loop for PID 1. All commands are received and acted upon from here. */
329 /* This will block until a command is sent down the pipe... */
330 file = fopen(fifoPath, "r");
334 /* Check to see if the signal handler blew things up on us */
342 /* Grab whatever was shoved into the FIFO */
343 read = fread(buf, 1, 2047, file);
348 printf("PID1: Received \"%s\" from FIFO...\n", buf);
350 /* Now do something with it... */
351 if (strcmp(buf, "poweroff") == 0)
353 shut_down(RB_POWER_OFF);
355 else if (strcmp(buf, "restart") == 0)
357 shut_down(RB_AUTOBOOT);
359 else if (strcmp(buf, "halt") == 0)
361 shut_down(RB_HALT_SYSTEM);
363 else if (strcmp(buf, "status") == 0)
365 printf("[Status placeholder]\n");
369 /* We should never get here... */