minit.c (14838B) download
1#define _GNU_SOURCE
2
3#include "minit.h"
4
5#include "write12.h"
6
7#include <alloca.h>
8#include <errno.h>
9#include <fcntl.h>
10#include <limits.h>
11#include <linux/kd.h>
12#include <poll.h>
13#include <signal.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/ioctl.h>
18#include <sys/reboot.h>
19#include <sys/socket.h>
20#include <sys/types.h>
21#include <sys/un.h>
22#include <sys/wait.h>
23#include <time.h>
24#include <unistd.h>
25
26static struct process* root;
27static int maxprocess = -1;
28static int processalloc;
29
30static char** Argv;
31
32#undef printf
33extern int printf(const char* format, ...);
34
35extern void opendevconsole();
36
37#define UPDATE
38#ifdef UPDATE
39static int doupdate;
40#endif
41
42extern int openreadclose(char* fn, char** buf, size_t* len);
43extern char** split(char* buf, int c, size_t* len, size_t plus, size_t ofs);
44
45#define HISTORY 10
46#ifdef HISTORY
47static int history[HISTORY];
48#endif
49
50/* return index of service in process data structure or -1 if not found */
51static int findservice(char* service) {
52 int i;
53 for (i = 0; i <= maxprocess; ++i) {
54 if (!strcmp(root[i].name, service))
55 return i;
56 }
57 return -1;
58}
59
60/* look up process index in data structure by PID */
61static int findbypid(pid_t pid) {
62 int i;
63 for (i = 0; i <= maxprocess; ++i) {
64 if (root[i].pid == pid)
65 return i;
66 }
67 return -1;
68}
69
70/* clear circular dependency detection flags */
71static void circsweep() {
72 int i;
73 for (i = 0; i <= maxprocess; ++i)
74 root[i].circular = 0;
75}
76
77/* add process to data structure, return index or -1 */
78static int addprocess(struct process* p) {
79 if (maxprocess + 1 >= processalloc) {
80 struct process* fump;
81 processalloc += 8;
82 if ((fump = (struct process*) realloc(root, processalloc * sizeof(struct process))) == 0) return -1;
83 root = fump;
84 }
85 memmove(&root[++maxprocess], p, sizeof(struct process));
86 return maxprocess;
87}
88
89/* load a service into the process data structure and return index or -1
90 * if failed */
91static int loadservice(char* service) {
92 struct process tmp;
93 int fd;
94 if (*service == 0) return -1;
95 fd = findservice(service);
96 if (fd >= 0) return fd;
97 if (chdir(MINITROOT) || chdir(service)) return -1;
98 if (!(tmp.name = strdup(service))) return -1;
99 tmp.pid = 0;
100 fd = open("respawn", O_RDONLY);
101 if (fd >= 0) {
102 tmp.respawn = 1;
103 close(fd);
104 } else
105 tmp.respawn = 0;
106 tmp.startedat = 0;
107 tmp.circular = 0;
108 tmp.__stdin = 0;
109 tmp.__stdout = 1;
110 {
111 char* logservice = alloca(strlen(service) + 5);
112 strcpy(logservice, service);
113 strcat(logservice, "/log");
114 tmp.logservice = loadservice(logservice);
115 if (tmp.logservice >= 0) {
116 int pipefd[2];
117 if (pipe(pipefd)) return -1;
118 fcntl(pipefd[0], F_SETFD, FD_CLOEXEC);
119 fcntl(pipefd[1], F_SETFD, FD_CLOEXEC);
120 root[tmp.logservice].__stdin = pipefd[0];
121 tmp.__stdout = pipefd[1];
122 }
123 }
124 return addprocess(&tmp);
125}
126
127/* usage: isup(findservice("sshd")).
128 * returns nonzero if process is up */
129static int isup(int service) {
130 if (service < 0) return 0;
131 return (root[service].pid != 0);
132}
133
134static int startservice(int service, int pause, int father);
135
136#undef debug
137static void handlekilled(pid_t killed) {
138 int i;
139#ifdef debug
140 {
141 char buf[50];
142 snprintf(buf, 50, " %d\n", killed);
143 write(2, buf, strlen(buf));
144 }
145#endif
146 if (killed == (pid_t) -1) {
147 static int saidso;
148 if (!saidso) {
149 write(2, "all services exited.\n", 21);
150 saidso = 1;
151 }
152 exit(0);
153 }
154 if (killed == 0) return;
155 i = findbypid(killed);
156 if (i >= 0) {
157 root[i].pid = 0;
158 if (root[i].respawn) {
159 circsweep();
160 startservice(i, time(0) - root[i].startedat < 1, root[i].father);
161 } else {
162 root[i].startedat = time(0);
163 root[i].pid = 1;
164 }
165 }
166}
167
168/* called from inside the service directory, return the PID or 0 on error */
169static pid_t forkandexec(int pause, int service) {
170 char** argv = 0;
171 int count = 0;
172 pid_t p;
173 int fd;
174 size_t len;
175 int islink;
176 char* s = 0;
177 size_t argc;
178 char* argv0 = 0;
179again:
180 switch (p = fork()) {
181 case (pid_t) -1:
182 if (count > 3) return 0;
183 sleep(++count * 2);
184 goto again;
185 case 0:
186 /* child */
187
188 if (pause) {
189 struct timespec req;
190 req.tv_sec = 0;
191 req.tv_nsec = 500000000;
192 nanosleep(&req, 0);
193 }
194 if ((fd = open("in", O_RDONLY)) != -1) {
195 if (dup2(fd, 0) != 0) {
196 __write2("dup2 failed unexpectedly.\n");
197 // This should never fail. init is run as root.
198 // If it does fail, don't exit for fear of bricking the system
199 }
200 fcntl(0, F_SETFD, 0);
201 }
202 if ((fd = open("out", O_WRONLY)) != -1) {
203 if (dup2(fd, 1) != 1 || dup2(fd, 2) != 2) {
204 __write2("dup2 failed unexpectedly.\n");
205 // This should never fail. init is run as root.
206 // If it does fail, don't exit for fear of bricking the system
207 }
208 fcntl(1, F_SETFD, 0);
209 fcntl(2, F_SETFD, 0);
210 }
211 // openreadclose and split allocate memory.
212 // We leak it here, because we are in the child process that is
213 // about to execve somebody else, at which point the OS frees all.
214 if (!openreadclose("nice", &s, &len)) {
215 int n = atoi(s);
216 nice(n);
217 s = 0;
218 }
219 if (!openreadclose("params", &s, &len)) {
220 argv = split(s, '\n', &argc, 2, 1);
221 if (argc && argv[argc - 1][0] == 0) --argc; // if params ended on newline, don't pass empty last arg
222 argv[argc] = 0;
223 } else {
224 argv = (char**) alloca(2 * sizeof(char*));
225 argv[1] = 0;
226 }
227 argv0 = (char*) alloca(PATH_MAX + 1);
228 if (!argv) _exit(1);
229 if (readlink("run", argv0, PATH_MAX) < 0) {
230 if (errno != EINVAL) _exit(1); /* not a symbolic link */
231 strcpy(argv0, "run");
232 islink = 0;
233 } else
234 islink = 1;
235 argv[0] = strrchr(argv0, '/');
236 if (argv[0])
237 argv[0]++;
238 else
239 argv[0] = argv0;
240 argv[0] = strdupa(argv[0]);
241 if (islink && argv0[0] != '/') {
242 char* c = alloca(strlen(argv0) + sizeof(MINITROOT) + strlen(root[service].name + 2));
243 char* d;
244 int fd = open(".", O_RDONLY | O_DIRECTORY);
245 stpcpy(stpcpy(stpcpy(stpcpy(c, MINITROOT "/"), root[service].name), "/"), argv0);
246 if (fd != -1) {
247 /* c is now something like /etc/minit/default/../../../var/whatever/doit
248 * attempt to clean it up a little */
249 d = strrchr(c, '/');
250 *d = 0;
251 if (chdir(c) == 0) {
252 *d = '/';
253 if (getcwd(argv0, PATH_MAX))
254 strncat(argv0, d, PATH_MAX);
255 fchdir(fd);
256 close(fd);
257 } else {
258 *d = '/';
259 argv0 = c;
260 }
261 } else
262 argv0 = c;
263 }
264 if (root[service].__stdin != 0) {
265 if (dup2(root[service].__stdin, 0) != 0) {
266 __write2("dup2 failed unexpectedly.\n");
267 // This should never fail. init is run as root.
268 // If it does fail, don't exit for fear of bricking the system
269 }
270 fcntl(0, F_SETFD, 0);
271 }
272 if (root[service].__stdout != 1) {
273 if (dup2(root[service].__stdout, 1) != 1 || dup2(root[service].__stdout, 2) != 2) {
274 __write2("dup2 failed unexpectedly.\n");
275 // This should never fail. init is run as root.
276 // If it does fail, don't exit for fear of bricking the system
277 }
278 fcntl(1, F_SETFD, 0);
279 fcntl(2, F_SETFD, 0);
280 }
281 {
282 int i;
283 for (i = 3; i < 1024; ++i) close(i);
284 }
285 chdir("root");
286 execve(argv0, argv, environ);
287 _exit(1);
288 default:
289 fd = open("sync", O_RDONLY);
290 if (fd >= 0) {
291 close(fd);
292 waitpid(p, 0, 0);
293 return 1;
294 }
295 return p;
296 }
297}
298
299/* start a service, return nonzero on error */
300static int startnodep(int service, int pause) {
301 /* step 1: see if the process is already up */
302 if (isup(service)) return 0;
303 /* step 2: fork and exec service, put PID in data structure */
304 if (chdir(MINITROOT) || chdir(root[service].name)) return -1;
305 root[service].startedat = time(0);
306 root[service].pid = forkandexec(pause, service);
307 return root[service].pid;
308}
309
310static int startservice(int service, int pause, int father) {
311 int dir = -1;
312 unsigned long len;
313 char* s = 0;
314 pid_t pid = 0;
315 if (service < 0) return 0;
316 if (root[service].circular)
317 return 0;
318 root[service].circular = 1;
319 root[service].father = father;
320#ifdef HISTORY
321 {
322 memmove(history + 1, history, sizeof(int) * ((HISTORY) -1));
323 history[0] = service;
324 }
325#endif
326 if (root[service].logservice >= 0)
327 startservice(root[service].logservice, pause, service);
328 if (chdir(MINITROOT) || chdir(root[service].name)) return -1;
329 if ((dir = open(".", O_RDONLY)) >= 0) {
330 // openreadclose allocates memory and reads the file contents into it.
331 // Need to free(s) independent of openreadclose return value
332 if (!openreadclose("depends", &s, &len)) {
333 char** deps = 0;
334 size_t depc, i;
335 deps = split(s, '\n', &depc, 0, 0);
336 for (i = 0; i < depc; i++) {
337 int Service, blacklisted, j;
338 if (deps[i][0] == '#') continue;
339 Service = loadservice(deps[i]);
340
341#if 1
342 for (j = blacklisted = 0; Argv[j]; ++j)
343 if (Argv[j][0] == '-' && !strcmp(Argv[j] + 1, deps[i])) {
344 blacklisted = 1;
345 ++Argv[j];
346 break;
347 }
348#endif
349
350 if (Service >= 0 && root[Service].pid != 1 && !blacklisted)
351 startservice(Service, 0, service);
352 }
353 fchdir(dir);
354 free(deps);
355 }
356 free(s);
357 pid = startnodep(service, pause);
358
359 close(dir);
360 dir = -1;
361 }
362 chdir(MINITROOT);
363 return pid;
364}
365
366static void _puts(const char* s) {
367 write(1, s, strlen(s));
368}
369
370static void childhandler() {
371 int status;
372 pid_t killed;
373#ifdef debug
374 write(2, "wait...", 7);
375#endif
376#ifdef UPDATE
377 if (doupdate) return;
378#endif
379
380 do {
381 killed = waitpid(-1, &status, WNOHANG);
382 handlekilled(killed);
383 } while (killed && killed != (pid_t) -1);
384}
385
386static volatile int dowinch = 0;
387static volatile int doint = 0;
388
389static void sigchild(int sig) { (void) sig; }
390
391int main(int argc, char* argv[]) {
392 /* Schritt 1: argv[1] als Service nehmen und starten */
393 int count = 0;
394 int i;
395 struct pollfd pfd;
396 time_t last = time(0);
397 int nfds = 1;
398 int outfd, infd;
399
400#ifdef HISTORY
401 for (i = 0; i < HISTORY; ++i)
402 history[i] = -1;
403#endif
404
405 Argv = argv;
406
407 infd = open(MINITROOT "/in", O_RDWR);
408 outfd = open(MINITROOT "/out", O_RDWR | O_NONBLOCK);
409
410 /* signal(SIGPWR,sighandler); don't know what to do about it */
411 /* signal(SIGHUP,sighandler); ??? */
412 {
413 static struct sigaction sa;
414 sigemptyset(&sa.sa_mask);
415 errno = 0;
416 sa.sa_sigaction = 0;
417 sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
418 sa.sa_handler = sigchild;
419 sigaction(SIGCHLD, &sa, 0);
420 sa.sa_flags = SA_RESTART;
421 if (errno) _puts("sigaction failed!\n");
422 }
423
424 if (infd < 0 || outfd < 0) {
425 _puts("minit: could not open " MINITROOT "/in or " MINITROOT "/out\n");
426 exit(1);
427 nfds = 0;
428 } else
429 pfd.fd = infd;
430 pfd.events = POLLIN;
431
432 fcntl(infd, F_SETFD, FD_CLOEXEC);
433 fcntl(outfd, F_SETFD, FD_CLOEXEC);
434
435#ifdef UPDATE
436 {
437 struct flock fl;
438 fl.l_whence = SEEK_CUR;
439 fl.l_start = 0;
440 fl.l_len = 0;
441 fl.l_pid = 0;
442 if ((0 == fcntl(infd, F_GETLK, &fl)) &&
443 (fl.l_type != F_UNLCK)) doupdate = 1;
444 }
445
446 if (!doupdate) {
447#endif
448 for (i = 1; i < argc; i++) {
449 circsweep();
450 if (startservice(loadservice(argv[i]), 0, -1)) count++;
451 }
452 circsweep();
453 if (!count) startservice(loadservice("default"), 0, -1);
454#ifdef UPDATE
455 }
456#endif
457 for (;;) {
458 int i;
459 char buf[1501];
460 time_t now;
461 if (doint) {
462 doint = 0;
463 startservice(loadservice("ctrlaltdel"), 0, -1);
464 }
465 if (dowinch) {
466 dowinch = 0;
467 startservice(loadservice("kbreq"), 0, -1);
468 }
469 childhandler();
470 now = time(0);
471 if (now < last || now - last > 30) {
472 /* The system clock was reset. Compensate. */
473 long diff = last - now;
474 int j;
475
476 for (j = 0; j <= maxprocess; ++j)
477 root[j].startedat -= diff;
478 }
479 last = now;
480 switch (poll(&pfd, nfds, 5000)) {
481 case -1:
482 if (errno == EINTR) {
483 childhandler();
484 break;
485 }
486 opendevconsole();
487 _puts("poll failed!\n");
488 exit(1);
489 /* what should we do if poll fails?! */
490 break;
491 case 1:
492 i = read(infd, buf, 1500);
493 if (i > 1) {
494 int idx = 0, tmp;
495 buf[i] = 0;
496
497/* write(1,buf,strlen(buf)); write(1,"\n",1); */
498#ifdef UPDATE
499 if (!strcmp(buf, "update")) {
500 execve("/sbin/minit", argv, environ);
501 }
502
503 if (((buf[0] != 'U') && buf[0] != 's') && ((idx = findservice(buf + 1)) < 0) && strcmp(buf, "d-"))
504#else
505 if (buf[0] != 's' && ((idx = findservice(buf + 1)) < 0) && strcmp(buf, "d-"))
506#endif
507 error:
508 write(outfd, "0", 1);
509 else {
510 switch (buf[0]) {
511 case 'p':
512 dprintf(outfd, "%d", root[idx].pid);
513 break;
514#ifdef UPDATE
515 case 'D':
516 doupdate = 1;
517 write(outfd, &root[idx], sizeof(struct process));
518 break;
519 case 'U':
520 doupdate = 1;
521 write(outfd, "1", 1);
522 if (1 == poll(&pfd, nfds, 5000)) {
523 struct process tmp;
524 if (read(infd, &tmp, sizeof tmp) != sizeof(tmp)) {
525 __write2("update failed, struct size mismatch.\n");
526 goto ok;
527 }
528 tmp.name = strdup(buf + 1);
529 addprocess(&tmp);
530 }
531 goto ok;
532#endif
533 case 'r':
534 root[idx].respawn = 0;
535 goto ok;
536 case 'R':
537 root[idx].respawn = 1;
538 goto ok;
539 case 'C':
540 if (kill(root[idx].pid, 0)) { /* check if still active */
541 handlekilled(root[idx].pid); /* no!?! remove form active list */
542 goto error;
543 }
544 goto ok;
545 case 'P': {
546 unsigned char* x = (unsigned char*) buf + strlen(buf) + 1;
547 unsigned char c;
548 tmp = 0;
549 while ((c = *x++ - '0') < 10) tmp = tmp * 10 + c;
550 }
551 if (tmp > 0) {
552 if (kill(tmp, 0)) goto error;
553 }
554 root[idx].pid = tmp;
555 goto ok;
556 case 's':
557 idx = loadservice(buf + 1);
558 if (idx < 0) goto error;
559 if (root[idx].pid < 2) {
560 root[idx].pid = 0;
561 circsweep();
562 idx = startservice(idx, 0, -1);
563 if (idx == 0) {
564 write(outfd, "0", 1);
565 break;
566 }
567 }
568 ok:
569 write(outfd, "1", 1);
570 break;
571 case 'u':
572 dprintf(outfd, "%lu", time(0) - root[idx].startedat);
573 break;
574 case 'd':
575 write(outfd, "1:", 2);
576 {
577 int i;
578 for (i = 0; i <= maxprocess; ++i) {
579 if (root[i].father == idx)
580 write(outfd, root[i].name, strlen(root[i].name) + 1);
581 }
582 write(outfd, "\0", 2);
583 }
584 break;
585 }
586 }
587 } else {
588 if (buf[0] == 'h') {
589#ifdef HISTORY
590 write(outfd, "1:", 2);
591 {
592 int i;
593 for (i = 0; i < HISTORY; ++i)
594 if (history[i] != -1)
595 write(outfd, root[history[i]].name, strlen(root[history[i]].name) + 1);
596 write(outfd, "\0", 2);
597 }
598#else
599 write(outfd, "0", 1);
600#endif
601 }
602 }
603 break;
604 default:
605#ifdef UPDATE
606 doupdate = 0;
607#endif
608 break;
609 }
610 }
611}