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 FindMonitoredPID(pid_t pid)
43 for(int i=0; i<freeProcessSlot; i++)
45 if (processPID[i] == pid)
54 // Remove the given process from the MPL by index
56 static void RemoveEntry(int entry)
58 free(processCmd[entry]);
59 free(processName[entry]);
60 processPID[entry] = -1;
62 if (entry == (freeProcessSlot - 1))
68 // Add a process to the MPL
70 static void AddEntry(pid_t pid, const char * name, const char * command)
74 for(i=0; i<freeProcessSlot; i++)
76 if (processPID[i] == -1)
81 processCmd[i] = strdup(command);
82 processName[i] = strdup(name);
84 if (i == freeProcessSlot)
90 // Take a given space delimited string and turn it into an array of strings
91 // suitable for execv().
93 static void MakeArgumentList(const char * s)
98 strncpy(buf, s, 4095);
101 char * token = strtok_r(buf, " ", &tokSave);
103 while (token != NULL)
106 token = strtok_r(NULL, " ", &tokSave);
109 // Argument list *must* end with a NULL entry:
115 // Launch a command from PID 1
117 // name: The human readable name for the process to launch
118 // command: The command to launch + any parameters it needs
119 // monitor: Tells Launch whether or not this process should be monitored
120 // and respawned if it goes away (0 = no, 1 = yes)
122 static pid_t Launch(const char * name, const char * command, int monitor)
125 printf("Starting %s (%smonitored)...\n", name, (monitor ? "" : "un"));
127 MakeArgumentList(command);
132 // Child process gets here (if fork was successful)...
134 execv(args[0], args);
136 // We only get here if an error happened with execv()... Need to figure
137 // out how to handle this gracefully...
138 printf("execv() failed!\n");
143 printf("Fork failed; can't start %s!\n", name);
147 // Parent process gets here... So add to monitor list if requested
149 AddEntry(pid, name, command);
156 // Here we do some process monitoring
157 // At some point, we'll read inittab instead of recording just our own internal
160 static void HandleSIGCHLD(int signum)
164 // Get PID of exiting child process(es), if any
165 pid_t pid = waitpid(-1, NULL, WNOHANG);
167 // Check for exit conditions...
179 // If we're shutting down, we don't do any more process monitoring
183 int entry = FindMonitoredPID(pid);
187 // We remove the old entry last, because it frees the name & cmd
188 Launch(processName[entry], processCmd[entry], 1);
196 // Start up the system, using the awesome power of OpenRC
198 static void Init(void)
200 printf("*** init-ng v1.0.1 starting...\n");
202 pid_t pid = Launch("openrc", "/sbin/rc sysinit", 0);
203 waitpid(pid, NULL, 0);
205 pid = Launch("openrc", "/sbin/rc boot", 0);
206 waitpid(pid, NULL, 0);
208 pid = Launch("openrc", "/sbin/rc default", 0);
209 waitpid(pid, NULL, 0);
213 static void ShutDown(int cmd)
216 pid_t pid = Launch("openrc", "/sbin/rc reboot", 0);
218 // We do this because our custom signal handler will repeatedly abort this
219 // call. We probably should do a bit more checking than this, but it seems
220 // to work without any problem as is.
221 while (waitpid(pid, NULL, 0) != pid);
229 // Connect to PID 1's FIFO to send a command
231 static void DoCmd(const char * cmd)
233 FILE * file = fopen(fifoPath, "w");
237 perror("DoCmd: fopen");
241 size_t ignored = fwrite(cmd, 1, strlen(cmd), file);
246 int main(int argc, char * argv[])
248 char buf[2048], buf2[2048];
249 pid_t pid = getpid();
251 // If we're not PID 1, then we're (possibly) being asked to run a command
256 if ((strcmp(argv[1], "poweroff") == 0)
257 || (strcmp(argv[1], "restart") == 0)
258 || (strcmp(argv[1], "halt") == 0)
259 || (strcmp(argv[1], "status") == 0))
263 else if (strcmp(argv[1], "test") == 0)
265 ShutDown(RB_POWER_OFF);
268 printf("Invalid command\n");
271 printf("Usage: poweroff, restart, halt, status, test\n");
276 // We are PID EINS, so start the system using OpenRC...
280 // N.B.: To be a drop in replacement for SysV init, we really should read
281 // and parse the entries in /etc/inittab; at least the c entries...
282 for(int i=1; i<=6; i++)
284 sprintf(buf, "agetty%i", i);
285 sprintf(buf2, "/sbin/agetty tty%i --noclear", i);
286 Launch(buf, buf2, 1);
289 // Install SIGCHLD signal handler to do process monitoring
290 memset(&action, 0, sizeof(action));
291 action.sa_handler = HandleSIGCHLD;
292 action.sa_flags = SA_SIGINFO;
293 sigaction(SIGCHLD, &action, NULL);
295 // Create a named pipe to listen on for commands
296 if (mkfifo(fifoPath, 0600) == -1)
298 // This is bad... If this happens, we have no comms
302 // Main loop for PID 1. All commands are received and acted upon from here.
305 // This will block until a command is sent down the pipe...
306 FILE * file = fopen(fifoPath, "r");
310 // Check to see if the signal handler blew things up on us
318 // Grab whatever was shoved into the FIFO
319 size_t read = fread(buf, 1, 2047, file);
324 printf("PID1: Received \"%s\" from FIFO...\n", buf);
326 // Now do something with it...
327 if (strcmp(buf, "poweroff") == 0)
329 ShutDown(RB_POWER_OFF);
331 else if (strcmp(buf, "restart") == 0)
333 ShutDown(RB_AUTOBOOT);
335 else if (strcmp(buf, "halt") == 0)
337 ShutDown(RB_HALT_SYSTEM);
339 else if (strcmp(buf, "status") == 0)
341 printf("[Status placeholder]\n");
345 // We should never get here...