]> Shamusworld >> Repos - init-ng/blob - init-ng.c
Added version which conforms to openrc coding guidelines.
[init-ng] / init-ng.c
1 //
2 // Init-NG
3 //
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. ;-)
8 //
9 // by James Hammons
10 // (C) 2017 Underground Software
11 //
12
13 #include <unistd.h>
14 #include <errno.h>
15 #include <signal.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/reboot.h>
20 #include <sys/syscall.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <sys/un.h>
24 #include <sys/wait.h>
25
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 ;-)
35
36
37 //
38 // Find a process in the monitored process list (MPL) by PID. Returns the index
39 // of the entry in the MPL.
40 //
41 static int FindMonitoredPID(pid_t pid)
42 {
43         for(int i=0; i<freeProcessSlot; i++)
44         {
45                 if (processPID[i] == pid)
46                         return i;
47         }
48
49         return -1;
50 }
51
52
53 //
54 // Remove the given process from the MPL by index
55 //
56 static void RemoveEntry(int entry)
57 {
58         free(processCmd[entry]);
59         free(processName[entry]);
60         processPID[entry] = -1;
61
62         if (entry == (freeProcessSlot - 1))
63                 freeProcessSlot--;
64 }
65
66
67 //
68 // Add a process to the MPL
69 //
70 static void AddEntry(pid_t pid, const char * name, const char * command)
71 {
72         int i;
73
74         for(i=0; i<freeProcessSlot; i++)
75         {
76                 if (processPID[i] == -1)
77                         break;
78         }
79
80         processPID[i] = pid;
81         processCmd[i] = strdup(command);
82         processName[i] = strdup(name);
83
84         if (i == freeProcessSlot)
85                 freeProcessSlot++;
86 }
87
88
89 //
90 // Take a given space delimited string and turn it into an array of strings
91 // suitable for execv().
92 //
93 static void MakeArgumentList(const char * s)
94 {
95         char buf[4096];
96         char * tokSave;
97
98         strncpy(buf, s, 4095);
99         buf[4095] = 0;
100         int i = 0;
101         char * token = strtok_r(buf, " ", &tokSave);
102
103         while (token != NULL)
104         {
105                 args[i++] = token;
106                 token = strtok_r(NULL, " ", &tokSave);
107         }
108
109         // Argument list *must* end with a NULL entry:
110         args[i] = NULL;
111 }
112
113
114 //
115 // Launch a command from PID 1
116 //
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)
121 //
122 static pid_t Launch(const char * name, const char * command, int monitor)
123 {
124         if (verbose)
125                 printf("Starting %s (%smonitored)...\n", name, (monitor ? "" : "un"));
126
127         MakeArgumentList(command);
128         pid_t pid = fork();
129
130         if (pid == 0)
131         {
132                 // Child process gets here (if fork was successful)...
133                 setsid();
134                 execv(args[0], args);
135
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");
139                 return 0;
140         }
141         else if (pid < 0)
142         {
143                 printf("Fork failed; can't start %s!\n", name);
144                 return pid;
145         }
146
147         // Parent process gets here... So add to monitor list if requested
148         if (monitor)
149                 AddEntry(pid, name, command);
150
151         return pid;
152 }
153
154
155 //
156 // Here we do some process monitoring
157 // At some point, we'll read inittab instead of recording just our own internal
158 // processes...
159 //
160 static void HandleSIGCHLD(int signum)
161 {
162         while (1)
163         {
164                 // Get PID of exiting child process(es), if any
165                 pid_t pid = waitpid(-1, NULL, WNOHANG);
166
167                 // Check for exit conditions...
168                 if (pid == 0)
169                         break;
170                 else if (pid == -1)
171                 {
172                         if (errno == ECHILD)
173                                 break;
174
175                         perror("waitpid");
176                         continue;
177                 }
178
179                 // If we're shutting down, we don't do any more process monitoring
180                 if (shuttingDown)
181                         continue;
182
183                 int entry = FindMonitoredPID(pid);
184
185                 if (entry != -1)
186                 {
187                         // We remove the old entry last, because it frees the name & cmd
188                         Launch(processName[entry], processCmd[entry], 1);
189                         RemoveEntry(entry);
190                 }
191         }
192 }
193
194
195 //
196 // Start up the system, using the awesome power of OpenRC
197 //
198 static void Init(void)
199 {
200         printf("*** init-ng v1.0.1 starting...\n");
201
202         pid_t pid = Launch("openrc", "/sbin/rc sysinit", 0);
203         waitpid(pid, NULL, 0);
204
205         pid = Launch("openrc", "/sbin/rc boot", 0);
206         waitpid(pid, NULL, 0);
207
208         pid = Launch("openrc", "/sbin/rc default", 0);
209         waitpid(pid, NULL, 0);
210 }
211
212
213 static void ShutDown(int cmd)
214 {
215         shuttingDown = 1;
216         pid_t pid = Launch("openrc", "/sbin/rc reboot", 0);
217
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);
222
223         sync();
224         reboot(cmd);
225 }
226
227
228 //
229 // Connect to PID 1's FIFO to send a command
230 //
231 static void DoCmd(const char * cmd)
232 {
233         FILE * file = fopen(fifoPath, "w");
234
235         if (file == NULL)
236         {
237                 perror("DoCmd: fopen");
238                 return;
239         }
240
241         size_t ignored = fwrite(cmd, 1, strlen(cmd), file);
242         fclose(file);
243 }
244
245
246 int main(int argc, char * argv[])
247 {
248         char buf[2048], buf2[2048];
249         pid_t pid = getpid();
250
251         // If we're not PID 1, then we're (possibly) being asked to run a command
252         if (pid != 1)
253         {
254                 if (argc > 1)
255                 {
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))
260                         {
261                                 DoCmd(argv[1]);
262                         }
263                         else if (strcmp(argv[1], "test") == 0)
264                         {
265                                 ShutDown(RB_POWER_OFF);
266                         }
267                         else
268                                 printf("Invalid command\n");
269                 }
270                 else
271                         printf("Usage: poweroff, restart, halt, status, test\n");
272
273                 return 1;
274         }
275
276         // We are PID EINS, so start the system using OpenRC...
277         Init();
278
279         // Launch TTYs...
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++)
283         {
284                 sprintf(buf, "agetty%i", i);
285                 sprintf(buf2, "/sbin/agetty tty%i --noclear", i);
286                 Launch(buf, buf2, 1);
287         }
288
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);
294
295         // Create a named pipe to listen on for commands
296         if (mkfifo(fifoPath, 0600) == -1)
297         {
298                 // This is bad... If this happens, we have no comms
299                 perror("mkfifo");
300         }
301
302         // Main loop for PID 1. All commands are received and acted upon from here.
303         while (1)
304         {
305                 // This will block until a command is sent down the pipe...
306                 FILE * file = fopen(fifoPath, "r");
307
308                 if (file == NULL)
309                 {
310                         // Check to see if the signal handler blew things up on us
311                         if (errno == EINTR)
312                                 continue;
313
314                         perror("fopen");
315                         continue;
316                 }
317
318                 // Grab whatever was shoved into the FIFO
319                 size_t read = fread(buf, 1, 2047, file);
320                 buf[read] = 0;
321                 fclose(file);
322
323                 if (verbose)
324                         printf("PID1: Received \"%s\" from FIFO...\n", buf);
325
326                 // Now do something with it...
327                 if (strcmp(buf, "poweroff") == 0)
328                 {
329                         ShutDown(RB_POWER_OFF);
330                 }
331                 else if (strcmp(buf, "restart") == 0)
332                 {
333                         ShutDown(RB_AUTOBOOT);
334                 }
335                 else if (strcmp(buf, "halt") == 0)
336                 {
337                         ShutDown(RB_HALT_SYSTEM);
338                 }
339                 else if (strcmp(buf, "status") == 0)
340                 {
341                         printf("[Status placeholder]\n");
342                 }
343         }
344
345         // We should never get here...
346         return 0;
347 }
348