Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/runit/runsv.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 984 - (show annotations) (download)
Sun May 30 11:32:42 2010 UTC (13 years, 11 months ago) by niro
File MIME type: text/plain
File size: 16354 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31 #include <sys/poll.h>
32 #include <sys/file.h>
33 #include "libbb.h"
34 #include "runit_lib.h"
35
36 #if ENABLE_MONOTONIC_SYSCALL
37 #include <sys/syscall.h>
38
39 /* libc has incredibly messy way of doing this,
40 * typically requiring -lrt. We just skip all this mess */
41 static void gettimeofday_ns(struct timespec *ts)
42 {
43 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
44 }
45 #else
46 static void gettimeofday_ns(struct timespec *ts)
47 {
48 if (sizeof(struct timeval) == sizeof(struct timespec)
49 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
50 ) {
51 /* Cheat */
52 gettimeofday((void*)ts, NULL);
53 ts->tv_nsec *= 1000;
54 } else {
55 extern void BUG_need_to_implement_gettimeofday_ns(void);
56 BUG_need_to_implement_gettimeofday_ns();
57 }
58 }
59 #endif
60
61 /* Compare possibly overflowing unsigned counters */
62 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
63
64 /* state */
65 #define S_DOWN 0
66 #define S_RUN 1
67 #define S_FINISH 2
68 /* ctrl */
69 #define C_NOOP 0
70 #define C_TERM 1
71 #define C_PAUSE 2
72 /* want */
73 #define W_UP 0
74 #define W_DOWN 1
75 #define W_EXIT 2
76
77 struct svdir {
78 int pid;
79 smallint state;
80 smallint ctrl;
81 smallint sd_want;
82 smallint islog;
83 struct timespec start;
84 int fdlock;
85 int fdcontrol;
86 int fdcontrolwrite;
87 int wstat;
88 };
89
90 struct globals {
91 smallint haslog;
92 smallint sigterm;
93 smallint pidchanged;
94 struct fd_pair selfpipe;
95 struct fd_pair logpipe;
96 char *dir;
97 struct svdir svd[2];
98 };
99 #define G (*(struct globals*)&bb_common_bufsiz1)
100 #define haslog (G.haslog )
101 #define sigterm (G.sigterm )
102 #define pidchanged (G.pidchanged )
103 #define selfpipe (G.selfpipe )
104 #define logpipe (G.logpipe )
105 #define dir (G.dir )
106 #define svd (G.svd )
107 #define INIT_G() do { \
108 pidchanged = 1; \
109 } while (0)
110
111 static void fatal2_cannot(const char *m1, const char *m2)
112 {
113 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
114 /* was exiting 111 */
115 }
116 static void fatal_cannot(const char *m)
117 {
118 fatal2_cannot(m, "");
119 /* was exiting 111 */
120 }
121 static void fatal2x_cannot(const char *m1, const char *m2)
122 {
123 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
124 /* was exiting 111 */
125 }
126 static void warn_cannot(const char *m)
127 {
128 bb_perror_msg("%s: warning: cannot %s", dir, m);
129 }
130
131 static void s_child(int sig_no UNUSED_PARAM)
132 {
133 write(selfpipe.wr, "", 1);
134 }
135
136 static void s_term(int sig_no UNUSED_PARAM)
137 {
138 sigterm = 1;
139 write(selfpipe.wr, "", 1); /* XXX */
140 }
141
142 /* libbb candidate */
143 static char *bb_stpcpy(char *p, const char *to_add)
144 {
145 while ((*p = *to_add) != '\0') {
146 p++;
147 to_add++;
148 }
149 return p;
150 }
151
152 static int open_trunc_or_warn(const char *name)
153 {
154 int fd = open_trunc(name);
155 if (fd < 0)
156 bb_perror_msg("%s: warning: cannot open %s",
157 dir, name);
158 return fd;
159 }
160
161 static void update_status(struct svdir *s)
162 {
163 ssize_t sz;
164 int fd;
165 svstatus_t status;
166
167 /* pid */
168 if (pidchanged) {
169 fd = open_trunc_or_warn("supervise/pid.new");
170 if (fd < 0)
171 return;
172 if (s->pid) {
173 char spid[sizeof(int)*3 + 2];
174 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
175 write(fd, spid, size);
176 }
177 close(fd);
178 if (rename_or_warn("supervise/pid.new",
179 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
180 return;
181 pidchanged = 0;
182 }
183
184 /* stat */
185 fd = open_trunc_or_warn("supervise/stat.new");
186 if (fd < -1)
187 return;
188
189 {
190 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
191 char *p = stat_buf;
192 switch (s->state) {
193 case S_DOWN:
194 p = bb_stpcpy(p, "down");
195 break;
196 case S_RUN:
197 p = bb_stpcpy(p, "run");
198 break;
199 case S_FINISH:
200 p = bb_stpcpy(p, "finish");
201 break;
202 }
203 if (s->ctrl & C_PAUSE)
204 p = bb_stpcpy(p, ", paused");
205 if (s->ctrl & C_TERM)
206 p = bb_stpcpy(p, ", got TERM");
207 if (s->state != S_DOWN)
208 switch (s->sd_want) {
209 case W_DOWN:
210 p = bb_stpcpy(p, ", want down");
211 break;
212 case W_EXIT:
213 p = bb_stpcpy(p, ", want exit");
214 break;
215 }
216 *p++ = '\n';
217 write(fd, stat_buf, p - stat_buf);
218 close(fd);
219 }
220
221 rename_or_warn("supervise/stat.new",
222 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
223
224 /* supervise compatibility */
225 memset(&status, 0, sizeof(status));
226 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
227 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
228 status.pid_le32 = SWAP_LE32(s->pid);
229 if (s->ctrl & C_PAUSE)
230 status.paused = 1;
231 if (s->sd_want == W_UP)
232 status.want = 'u';
233 else
234 status.want = 'd';
235 if (s->ctrl & C_TERM)
236 status.got_term = 1;
237 status.run_or_finish = s->state;
238 fd = open_trunc_or_warn("supervise/status.new");
239 if (fd < 0)
240 return;
241 sz = write(fd, &status, sizeof(status));
242 close(fd);
243 if (sz != sizeof(status)) {
244 warn_cannot("write supervise/status.new");
245 unlink("supervise/status.new");
246 return;
247 }
248 rename_or_warn("supervise/status.new",
249 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
250 }
251
252 static unsigned custom(struct svdir *s, char c)
253 {
254 pid_t pid;
255 int w;
256 char a[10];
257 struct stat st;
258
259 if (s->islog)
260 return 0;
261 strcpy(a, "control/?");
262 a[8] = c; /* replace '?' */
263 if (stat(a, &st) == 0) {
264 if (st.st_mode & S_IXUSR) {
265 pid = vfork();
266 if (pid == -1) {
267 warn_cannot("vfork for control/?");
268 return 0;
269 }
270 if (pid == 0) {
271 /* child */
272 if (haslog && dup2(logpipe.wr, 1) == -1)
273 warn_cannot("setup stdout for control/?");
274 execl(a, a, (char *) NULL);
275 fatal_cannot("run control/?");
276 }
277 /* parent */
278 if (safe_waitpid(pid, &w, 0) == -1) {
279 warn_cannot("wait for child control/?");
280 return 0;
281 }
282 return WEXITSTATUS(w) == 0;
283 }
284 } else {
285 if (errno != ENOENT)
286 warn_cannot("stat control/?");
287 }
288 return 0;
289 }
290
291 static void stopservice(struct svdir *s)
292 {
293 if (s->pid && !custom(s, 't')) {
294 kill(s->pid, SIGTERM);
295 s->ctrl |= C_TERM;
296 update_status(s);
297 }
298 if (s->sd_want == W_DOWN) {
299 kill(s->pid, SIGCONT);
300 custom(s, 'd');
301 return;
302 }
303 if (s->sd_want == W_EXIT) {
304 kill(s->pid, SIGCONT);
305 custom(s, 'x');
306 }
307 }
308
309 static void startservice(struct svdir *s)
310 {
311 int p;
312 const char *arg[4];
313 char exitcode[sizeof(int)*3 + 2];
314
315 if (s->state == S_FINISH) {
316 /* Two arguments are given to ./finish. The first one is ./run exit code,
317 * or -1 if ./run didnt exit normally. The second one is
318 * the least significant byte of the exit status as determined by waitpid;
319 * for instance it is 0 if ./run exited normally, and the signal number
320 * if ./run was terminated by a signal. If runsv cannot start ./run
321 * for some reason, the exit code is 111 and the status is 0.
322 */
323 arg[0] = "./finish";
324 arg[1] = "-1";
325 if (WIFEXITED(s->wstat)) {
326 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
327 arg[1] = exitcode;
328 }
329 //arg[2] = "0";
330 //if (WIFSIGNALED(s->wstat)) {
331 arg[2] = utoa(WTERMSIG(s->wstat));
332 //}
333 arg[3] = NULL;
334 } else {
335 arg[0] = "./run";
336 arg[1] = NULL;
337 custom(s, 'u');
338 }
339
340 if (s->pid != 0)
341 stopservice(s); /* should never happen */
342 while ((p = vfork()) == -1) {
343 warn_cannot("vfork, sleeping");
344 sleep(5);
345 }
346 if (p == 0) {
347 /* child */
348 if (haslog) {
349 /* NB: bug alert! right order is close, then dup2 */
350 if (s->islog) {
351 xchdir("./log");
352 close(logpipe.wr);
353 xdup2(logpipe.rd, 0);
354 } else {
355 close(logpipe.rd);
356 xdup2(logpipe.wr, 1);
357 }
358 }
359 /* Non-ignored signals revert to SIG_DFL on exec anyway */
360 /*bb_signals(0
361 + (1 << SIGCHLD)
362 + (1 << SIGTERM)
363 , SIG_DFL);*/
364 sig_unblock(SIGCHLD);
365 sig_unblock(SIGTERM);
366 execv(arg[0], (char**) arg);
367 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
368 }
369 /* parent */
370 if (s->state != S_FINISH) {
371 gettimeofday_ns(&s->start);
372 s->state = S_RUN;
373 }
374 s->pid = p;
375 pidchanged = 1;
376 s->ctrl = C_NOOP;
377 update_status(s);
378 }
379
380 static int ctrl(struct svdir *s, char c)
381 {
382 int sig;
383
384 switch (c) {
385 case 'd': /* down */
386 s->sd_want = W_DOWN;
387 update_status(s);
388 if (s->pid && s->state != S_FINISH)
389 stopservice(s);
390 break;
391 case 'u': /* up */
392 s->sd_want = W_UP;
393 update_status(s);
394 if (s->pid == 0)
395 startservice(s);
396 break;
397 case 'x': /* exit */
398 if (s->islog)
399 break;
400 s->sd_want = W_EXIT;
401 update_status(s);
402 /* FALLTHROUGH */
403 case 't': /* sig term */
404 if (s->pid && s->state != S_FINISH)
405 stopservice(s);
406 break;
407 case 'k': /* sig kill */
408 if (s->pid && !custom(s, c))
409 kill(s->pid, SIGKILL);
410 s->state = S_DOWN;
411 break;
412 case 'p': /* sig pause */
413 if (s->pid && !custom(s, c))
414 kill(s->pid, SIGSTOP);
415 s->ctrl |= C_PAUSE;
416 update_status(s);
417 break;
418 case 'c': /* sig cont */
419 if (s->pid && !custom(s, c))
420 kill(s->pid, SIGCONT);
421 s->ctrl &= ~C_PAUSE;
422 update_status(s);
423 break;
424 case 'o': /* once */
425 s->sd_want = W_DOWN;
426 update_status(s);
427 if (!s->pid)
428 startservice(s);
429 break;
430 case 'a': /* sig alarm */
431 sig = SIGALRM;
432 goto sendsig;
433 case 'h': /* sig hup */
434 sig = SIGHUP;
435 goto sendsig;
436 case 'i': /* sig int */
437 sig = SIGINT;
438 goto sendsig;
439 case 'q': /* sig quit */
440 sig = SIGQUIT;
441 goto sendsig;
442 case '1': /* sig usr1 */
443 sig = SIGUSR1;
444 goto sendsig;
445 case '2': /* sig usr2 */
446 sig = SIGUSR2;
447 goto sendsig;
448 }
449 return 1;
450 sendsig:
451 if (s->pid && !custom(s, c))
452 kill(s->pid, sig);
453 return 1;
454 }
455
456 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
457 int runsv_main(int argc UNUSED_PARAM, char **argv)
458 {
459 struct stat s;
460 int fd;
461 int r;
462 char buf[256];
463
464 INIT_G();
465
466 dir = single_argv(argv);
467
468 xpiped_pair(selfpipe);
469 close_on_exec_on(selfpipe.rd);
470 close_on_exec_on(selfpipe.wr);
471 ndelay_on(selfpipe.rd);
472 ndelay_on(selfpipe.wr);
473
474 sig_block(SIGCHLD);
475 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
476 sig_block(SIGTERM);
477 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
478
479 xchdir(dir);
480 /* bss: svd[0].pid = 0; */
481 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
482 if (C_NOOP) svd[0].ctrl = C_NOOP;
483 if (W_UP) svd[0].sd_want = W_UP;
484 /* bss: svd[0].islog = 0; */
485 /* bss: svd[1].pid = 0; */
486 gettimeofday_ns(&svd[0].start);
487 if (stat("down", &s) != -1)
488 svd[0].sd_want = W_DOWN;
489
490 if (stat("log", &s) == -1) {
491 if (errno != ENOENT)
492 warn_cannot("stat ./log");
493 } else {
494 if (!S_ISDIR(s.st_mode)) {
495 errno = 0;
496 warn_cannot("stat log/down: log is not a directory");
497 } else {
498 haslog = 1;
499 svd[1].state = S_DOWN;
500 svd[1].ctrl = C_NOOP;
501 svd[1].sd_want = W_UP;
502 svd[1].islog = 1;
503 gettimeofday_ns(&svd[1].start);
504 if (stat("log/down", &s) != -1)
505 svd[1].sd_want = W_DOWN;
506 xpiped_pair(logpipe);
507 close_on_exec_on(logpipe.rd);
508 close_on_exec_on(logpipe.wr);
509 }
510 }
511
512 if (mkdir("supervise", 0700) == -1) {
513 r = readlink("supervise", buf, sizeof(buf));
514 if (r != -1) {
515 if (r == sizeof(buf))
516 fatal2x_cannot("readlink ./supervise", ": name too long");
517 buf[r] = 0;
518 mkdir(buf, 0700);
519 } else {
520 if ((errno != ENOENT) && (errno != EINVAL))
521 fatal_cannot("readlink ./supervise");
522 }
523 }
524 svd[0].fdlock = xopen3("log/supervise/lock"+4,
525 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
526 if (lock_exnb(svd[0].fdlock) == -1)
527 fatal_cannot("lock supervise/lock");
528 close_on_exec_on(svd[0].fdlock);
529 if (haslog) {
530 if (mkdir("log/supervise", 0700) == -1) {
531 r = readlink("log/supervise", buf, 256);
532 if (r != -1) {
533 if (r == 256)
534 fatal2x_cannot("readlink ./log/supervise", ": name too long");
535 buf[r] = 0;
536 fd = xopen(".", O_RDONLY|O_NDELAY);
537 xchdir("./log");
538 mkdir(buf, 0700);
539 if (fchdir(fd) == -1)
540 fatal_cannot("change back to service directory");
541 close(fd);
542 }
543 else {
544 if ((errno != ENOENT) && (errno != EINVAL))
545 fatal_cannot("readlink ./log/supervise");
546 }
547 }
548 svd[1].fdlock = xopen3("log/supervise/lock",
549 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
550 if (lock_ex(svd[1].fdlock) == -1)
551 fatal_cannot("lock log/supervise/lock");
552 close_on_exec_on(svd[1].fdlock);
553 }
554
555 mkfifo("log/supervise/control"+4, 0600);
556 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
557 close_on_exec_on(svd[0].fdcontrol);
558 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
559 close_on_exec_on(svd[0].fdcontrolwrite);
560 update_status(&svd[0]);
561 if (haslog) {
562 mkfifo("log/supervise/control", 0600);
563 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
564 close_on_exec_on(svd[1].fdcontrol);
565 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
566 close_on_exec_on(svd[1].fdcontrolwrite);
567 update_status(&svd[1]);
568 }
569 mkfifo("log/supervise/ok"+4, 0600);
570 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
571 close_on_exec_on(fd);
572 if (haslog) {
573 mkfifo("log/supervise/ok", 0600);
574 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
575 close_on_exec_on(fd);
576 }
577 for (;;) {
578 struct pollfd x[3];
579 unsigned deadline;
580 char ch;
581
582 if (haslog)
583 if (!svd[1].pid && svd[1].sd_want == W_UP)
584 startservice(&svd[1]);
585 if (!svd[0].pid)
586 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
587 startservice(&svd[0]);
588
589 x[0].fd = selfpipe.rd;
590 x[0].events = POLLIN;
591 x[1].fd = svd[0].fdcontrol;
592 x[1].events = POLLIN;
593 /* x[2] is used only if haslog == 1 */
594 x[2].fd = svd[1].fdcontrol;
595 x[2].events = POLLIN;
596 sig_unblock(SIGTERM);
597 sig_unblock(SIGCHLD);
598 poll(x, 2 + haslog, 3600*1000);
599 sig_block(SIGTERM);
600 sig_block(SIGCHLD);
601
602 while (read(selfpipe.rd, &ch, 1) == 1)
603 continue;
604
605 for (;;) {
606 pid_t child;
607 int wstat;
608
609 child = wait_any_nohang(&wstat);
610 if (!child)
611 break;
612 if ((child == -1) && (errno != EINTR))
613 break;
614 if (child == svd[0].pid) {
615 svd[0].wstat = wstat;
616 svd[0].pid = 0;
617 pidchanged = 1;
618 svd[0].ctrl &= ~C_TERM;
619 if (svd[0].state != S_FINISH) {
620 fd = open_read("finish");
621 if (fd != -1) {
622 close(fd);
623 svd[0].state = S_FINISH;
624 update_status(&svd[0]);
625 continue;
626 }
627 }
628 svd[0].state = S_DOWN;
629 deadline = svd[0].start.tv_sec + 1;
630 gettimeofday_ns(&svd[0].start);
631 update_status(&svd[0]);
632 if (LESS(svd[0].start.tv_sec, deadline))
633 sleep(1);
634 }
635 if (haslog) {
636 if (child == svd[1].pid) {
637 svd[0].wstat = wstat;
638 svd[1].pid = 0;
639 pidchanged = 1;
640 svd[1].state = S_DOWN;
641 svd[1].ctrl &= ~C_TERM;
642 deadline = svd[1].start.tv_sec + 1;
643 gettimeofday_ns(&svd[1].start);
644 update_status(&svd[1]);
645 if (LESS(svd[1].start.tv_sec, deadline))
646 sleep(1);
647 }
648 }
649 } /* for (;;) */
650 if (read(svd[0].fdcontrol, &ch, 1) == 1)
651 ctrl(&svd[0], ch);
652 if (haslog)
653 if (read(svd[1].fdcontrol, &ch, 1) == 1)
654 ctrl(&svd[1], ch);
655
656 if (sigterm) {
657 ctrl(&svd[0], 'x');
658 sigterm = 0;
659 }
660
661 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
662 if (svd[1].pid == 0)
663 _exit(EXIT_SUCCESS);
664 if (svd[1].sd_want != W_EXIT) {
665 svd[1].sd_want = W_EXIT;
666 /* stopservice(&svd[1]); */
667 update_status(&svd[1]);
668 close(logpipe.wr);
669 close(logpipe.rd);
670 }
671 }
672 } /* for (;;) */
673 /* not reached */
674 return 0;
675 }