blob: 6645dc6ceb856243051787e7c769ed9d81597656 [file] [log] [blame]
Glenn L McGrath1e11c342003-05-11 14:52:39 +00001/*
2 * minit version 0.9.1 by Felix von Leitner
3 * ported to busybox by Glenn McGrath <bug1@optushome.com.au>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20#include <time.h>
21#include <signal.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <unistd.h>
26#include <limits.h>
27#include <errno.h>
28#include <sys/fcntl.h>
29#include <sys/ioctl.h>
30#include <sys/poll.h>
31#include <sys/reboot.h>
32#include <sys/socket.h>
33#include <sys/types.h>
34#include <sys/un.h>
35#include <sys/wait.h>
36#include <linux/kd.h>
37
38#include "busybox.h"
39
40#define MINITROOT "/etc/minit"
41
42static int i_am_init;
43static int infd, outfd;
44
45extern char **environ;
46
47static struct process {
48 char *name;
49 pid_t pid;
50 char respawn;
51 char circular;
52 time_t startedat;
53 int __stdin, __stdout;
54 int logservice;
55} *root;
56
57static int maxprocess = -1;
58
59static int processalloc = 0;
60
61static unsigned int fmt_ulong(char *dest, unsigned long i)
62{
63 register unsigned long len, tmp, len2;
64
65 /* first count the number of bytes needed */
66 for (len = 1, tmp = i; tmp > 9; ++len)
67 tmp /= 10;
68 if (dest)
69 for (tmp = i, dest += len, len2 = len + 1; --len2; tmp /= 10)
70 *--dest = (tmp % 10) + '0';
71 return len;
72}
73
74/* split buf into n strings that are separated by c. return n as *len.
75 * Allocate plus more slots and leave the first ofs of them alone. */
76static char **split(char *buf, int c, int *len, int plus, int ofs)
77{
78 int n = 1;
79 char **v = 0;
80 char **w;
81
82 /* step 1: count tokens */
83 char *s;
84
85 for (s = buf; *s; s++)
86 if (*s == c)
87 n++;
88 /* step 2: allocate space for pointers */
89 v = (char **) malloc((n + plus) * sizeof(char *));
90 if (!v)
91 return 0;
92 w = v + ofs;
93 *w++ = buf;
94 for (s = buf;; s++) {
95 while (*s && *s != c)
96 s++;
97 if (*s == 0)
98 break;
99 if (*s == c) {
100 *s = 0;
101 *w++ = s + 1;
102 }
103 }
104 *len = w - v;
105 return v;
106}
107
108static int openreadclose(char *fn, char **buf, unsigned long *len)
109{
110 int fd = open(fn, O_RDONLY);
111
112 if (fd < 0)
113 return -1;
114 if (!*buf) {
115 *len = lseek(fd, 0, SEEK_END);
116 lseek(fd, 0, SEEK_SET);
117 *buf = (char *) malloc(*len + 1);
118 if (!*buf) {
119 close(fd);
120 return -1;
121 }
122 }
123 *len = read(fd, *buf, *len);
124 if (*len != (unsigned long) -1)
125 (*buf)[*len] = 0;
126 return close(fd);
127}
128
129/* return index of service in process data structure or -1 if not found */
130static int findservice(char *service)
131{
132 int i;
133
134 for (i = 0; i <= maxprocess; i++) {
135 if (!strcmp(root[i].name, service))
136 return i;
137 }
138 return -1;
139}
140
141/* look up process index in data structure by PID */
142static int findbypid(pid_t pid)
143{
144 int i;
145
146 for (i = 0; i <= maxprocess; i++) {
147 if (root[i].pid == pid)
148 return i;
149 }
150 return -1;
151}
152
153/* clear circular dependency detection flags */
154static void circsweep(void)
155{
156 int i;
157
158 for (i = 0; i <= maxprocess; i++)
159 root[i].circular = 0;
160}
161
162/* add process to data structure, return index or -1 */
163static int addprocess(struct process *p)
164{
165 if (maxprocess + 1 >= processalloc) {
166 struct process *fump;
167
168 processalloc += 8;
169 if ((fump =
170 (struct process *) xrealloc(root,
171 processalloc *
172 sizeof(struct process))) == 0)
173 return -1;
174 root = fump;
175 }
176 memmove(&root[++maxprocess], p, sizeof(struct process));
177 return maxprocess;
178}
179
180/* load a service into the process data structure and return index or -1
181 * if failed */
182static int loadservice(char *service)
183{
184 struct process tmp;
185 int fd;
186
187 if (*service == 0)
188 return -1;
189 fd = findservice(service);
190 if (fd >= 0)
191 return fd;
192 if (chdir(MINITROOT) || chdir(service))
193 return -1;
194 if (!(tmp.name = strdup(service)))
195 return -1;
196 tmp.pid = 0;
197 fd = open("respawn", O_RDONLY);
198 if (fd >= 0) {
199 tmp.respawn = 1;
200 close(fd);
201 } else
202 tmp.respawn = 0;
203 tmp.startedat = 0;
204 tmp.circular = 0;
205 tmp.__stdin = 0;
206 tmp.__stdout = 1;
207 {
208 char *logservice = alloca(strlen(service) + 5);
209
210 strcpy(logservice, service);
211 strcat(logservice, "/log");
212 tmp.logservice = loadservice(logservice);
213 if (tmp.logservice >= 0) {
214 int pipefd[2];
215
216 if (pipe(pipefd))
217 return -1;
218 root[tmp.logservice].__stdin = pipefd[0];
219 tmp.__stdout = pipefd[1];
220 }
221 }
222 return (addprocess(&tmp));
223}
224
225/* usage: isup(findservice("sshd")).
226 * returns nonzero if process is up */
227static int isup(int service)
228{
229 if (service < 0)
230 return 0;
231 return (root[service].pid != 0);
232}
233
234static void opendevconsole(void)
235{
236 int fd;
237
238 if ((fd = open("/dev/console", O_RDWR | O_NOCTTY)) >= 0) {
239 dup2(fd, 0);
240 dup2(fd, 1);
241 dup2(fd, 2);
242 if (fd > 2)
243 close(fd);
244 }
245}
246
247/* called from inside the service directory, return the PID or 0 on error */
248static pid_t forkandexec(int pause_flag, int service)
249{
250 char **argv = 0;
251 int count = 0;
252 pid_t p;
253 int fd;
254 unsigned long len;
255 char *s = 0;
256 int argc;
257 char *argv0 = 0;
258
259 again:
260 switch (p = fork()) {
261 case (pid_t) - 1:
262 if (count > 3)
263 return 0;
264 sleep(++count * 2);
265 goto again;
266 case 0:
267 /* child */
268
269 if (i_am_init) {
270 ioctl(0, TIOCNOTTY, 0);
271 setsid();
272 opendevconsole();
273 tcsetpgrp(0, getpgrp());
274 }
275 close(infd);
276 close(outfd);
277 if (pause_flag) {
278 struct timespec req;
279
280 req.tv_sec = 0;
281 req.tv_nsec = 500000000;
282 nanosleep(&req, 0);
283 }
284 if (!openreadclose("params", &s, &len)) {
285 argv = split(s, '\n', &argc, 2, 1);
286 if (argv[argc - 1])
287 argv[argc - 1] = 0;
288 else
289 argv[argc] = 0;
290 } else {
291 argv = (char **) xmalloc(2 * sizeof(char *));
292 argv[1] = 0;
293 }
294 argv0 = (char *) xmalloc(PATH_MAX + 1);
295 if (!argv || !argv0)
296 goto abort;
297 if (readlink("run", argv0, PATH_MAX) < 0) {
298 if (errno != EINVAL)
299 goto abort; /* not a symbolic link */
300 argv0 = strdup("./run");
301 }
302 argv[0] = strrchr(argv0, '/');
303 if (argv[0])
304 argv[0]++;
305 else
306 argv[0] = argv0;
307 if (root[service].__stdin != 0)
308 dup2(root[service].__stdin, 0);
309 if (root[service].__stdout != 1) {
310 dup2(root[service].__stdout, 1);
311 dup2(root[service].__stdout, 2);
312 }
313 {
314 int i;
315
316 for (i = 3; i < 1024; ++i)
317 close(i);
318 }
319 execve(argv0, argv, environ);
320 _exit(0);
321 abort:
322 free(argv0);
323 free(argv);
324 _exit(0);
325 default:
326 fd = open("sync", O_RDONLY);
327 if (fd >= 0) {
328 pid_t p2;
329
330 close(fd);
331 p2 = waitpid(p, 0, 0);
332 return 1;
333 }
334 return p;
335 }
336}
337
338/* start a service, return nonzero on error */
339static int startnodep(int service, int pause_flag)
340{
341 /* step 1: see if the process is already up */
342 if (isup(service))
343 return 0;
344
345 /* step 2: fork and exec service, put PID in data structure */
346 if (chdir(MINITROOT) || chdir(root[service].name))
347 return -1;
348 root[service].startedat = time(0);
349 root[service].pid = forkandexec(pause_flag, service);
350 return root[service].pid;
351}
352
353static int startservice(int service, int pause_flag)
354{
355 int dir = -1;
356 unsigned long len;
357 char *s = 0;
358 pid_t pid;
359
360 if (service < 0)
361 return 0;
362 if (root[service].circular)
363 return 0;
364 root[service].circular = 1;
365 if (root[service].logservice >= 0)
366 startservice(root[service].logservice, pause_flag);
367 if (chdir(MINITROOT) || chdir(root[service].name))
368 return -1;
369 if ((dir = open(".", O_RDONLY)) >= 0) {
370 if (!openreadclose("depends", &s, &len)) {
371 char **deps;
372 int depc, i;
373
374 deps = split(s, '\n', &depc, 0, 0);
375 for (i = 0; i < depc; i++) {
376 int service_index;
377
378 if (deps[i][0] == '#')
379 continue;
380 service_index = loadservice(deps[i]);
381 if (service_index >= 0 && root[service_index].pid != 1)
382 startservice(service_index, 0);
383 }
384 fchdir(dir);
385 }
386 pid = startnodep(service, pause_flag);
387 close(dir);
388 dir = -1;
389 return pid;
390 }
391 return 0;
392}
393
394static void sulogin(void)
395{
396 /* exiting on an initialization failure is not a good idea for init */
397 char *argv[] = { "sulogin", 0 };
398 execve("/sbin/sulogin", argv, environ);
399 exit(1);
400}
401
402static void handlekilled(pid_t killed)
403{
404 int i;
405
406 if (killed == (pid_t) - 1) {
407 write(2, "all services exited.\n", 21);
408 exit(0);
409 }
410 if (killed == 0)
411 return;
412 i = findbypid(killed);
413 if (i >= 0) {
414 root[i].pid = 0;
415 if (root[i].respawn) {
416 circsweep();
417 startservice(i, time(0) - root[i].startedat < 1);
418 } else {
419 root[i].startedat = time(0);
420 root[i].pid = 1;
421 }
422 }
423}
424
425static void childhandler(void)
426{
427 int status;
428 pid_t killed;
429
430 do {
431 killed = waitpid(-1, &status, WNOHANG);
432 handlekilled(killed);
433 } while (killed && killed != (pid_t) - 1);
434}
435
436static volatile int dowinch = 0;
437static volatile int doint = 0;
438
439static void sigchild(int whatever)
440{
441}
442static void sigwinch(int sig)
443{
444 dowinch = 1;
445}
446static void sigint(int sig)
447{
448 doint = 1;
449}
450
451extern int minit_main(int argc, char *argv[])
452{
453 /* Schritt 1: argv[1] als Service nehmen und starten */
454 struct pollfd pfd;
455 time_t last = time(0);
456 int nfds = 1;
457 int count = 0;
458 int i;
459
460 infd = open("/etc/minit/in", O_RDWR);
461 outfd = open("/etc/minit/out", O_RDWR | O_NONBLOCK);
462 if (getpid() == 1) {
463 int fd;
464
465 i_am_init = 1;
466 reboot(0);
467 if ((fd = open("/dev/console", O_RDWR | O_NOCTTY))) {
468 ioctl(fd, KDSIGACCEPT, SIGWINCH);
469 close(fd);
470 } else
471 ioctl(0, KDSIGACCEPT, SIGWINCH);
472 }
473/* signal(SIGPWR,sighandler); don't know what to do about it */
474/* signal(SIGHUP,sighandler); ??? */
475 {
476 struct sigaction sa;
477
478 sigemptyset(&sa.sa_mask);
479 sa.sa_sigaction = 0;
480 sa.sa_flags = SA_RESTART | SA_NOCLDSTOP;
481 sa.sa_handler = sigchild;
482 sigaction(SIGCHLD, &sa, 0);
483 sa.sa_handler = sigint;
484 sigaction(SIGINT, &sa, 0); /* ctrl-alt-del */
485 sa.sa_handler = sigwinch;
486 sigaction(SIGWINCH, &sa, 0); /* keyboard request */
487 }
488 if (infd < 0 || outfd < 0) {
489 puts("minit: could not open /etc/minit/in or /etc/minit/out\n");
490 sulogin();
491 nfds = 0;
492 } else
493 pfd.fd = infd;
494 pfd.events = POLLIN;
495
496 for (i = 1; i < argc; i++) {
497 circsweep();
498 if (startservice(loadservice(argv[i]), 0))
499 count++;
500 }
501 circsweep();
502 if (!count)
503 startservice(loadservice("default"), 0);
504 for (;;) {
505 char buf[1501];
506 time_t now;
507
508 if (doint) {
509 doint = 0;
510 startservice(loadservice("ctrlaltdel"), 0);
511 }
512 if (dowinch) {
513 dowinch = 0;
514 startservice(loadservice("kbreq"), 0);
515 }
516 childhandler();
517 now = time(0);
518 if (now < last || now - last > 30) {
519 /* The system clock was reset. Compensate. */
520 long diff = last - now;
521 int j;
522
523 for (j = 0; j <= maxprocess; ++j) {
524 root[j].startedat -= diff;
525 }
526 }
527 last = now;
528 switch (poll(&pfd, nfds, 5000)) {
529 case -1:
530 if (errno == EINTR) {
531 childhandler();
532 break;
533 }
534 opendevconsole();
535 puts("poll failed!\n");
536 sulogin();
537 /* what should we do if poll fails?! */
538 break;
539 case 1:
540 i = read(infd, buf, 1500);
541 if (i > 1) {
542 pid_t pid;
543 int idx = 0;
544 int tmp;
545
546 buf[i] = 0;
547
548 if (buf[0] != 's' && ((idx = findservice(buf + 1)) < 0))
549 error:
550 write(outfd, "0", 1);
551 else {
552 switch (buf[0]) {
553 case 'p':
554 write(outfd, buf, fmt_ulong(buf, root[idx].pid));
555 break;
556 case 'r':
557 root[idx].respawn = 0;
558 goto ok;
559 case 'R':
560 root[idx].respawn = 1;
561 goto ok;
562 case 'C':
563 if (kill(root[idx].pid, 0)) { /* check if still active */
564 handlekilled(root[idx].pid); /* no!?! remove form active list */
565 goto error;
566 }
567 goto ok;
568 break;
569 case 'P':
570 {
571 unsigned char *x = buf + strlen(buf) + 1;
572 unsigned char c;
573
574 tmp = 0;
575 while ((c = *x++ - '0') < 10)
576 tmp = tmp * 10 + c;
577 }
578 if (tmp > 0) {
579 if (kill(tmp, 0))
580 goto error;
581 pid = tmp;
582 }
583 root[idx].pid = tmp;
584 goto ok;
585 case 's':
586 idx = loadservice(buf + 1);
587 if (idx < 0)
588 goto error;
589 if (root[idx].pid < 2) {
590 root[idx].pid = 0;
591 circsweep();
592 idx = startservice(idx, 0);
593 if (idx == 0) {
594 write(outfd, "0", 1);
595 break;
596 }
597 }
598 ok:
599 write(outfd, "1", 1);
600 break;
601 case 'u':
602 write(outfd, buf,
603 fmt_ulong(buf, time(0) - root[idx].startedat));
604 }
605 }
606 }
607 break;
608 default:
609 break;
610 }
611 }
612}