2 Copyright (c) 2001-2006, Gerrit Pape
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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.
28 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
34 #include "runit_lib.h"
36 #if ENABLE_MONOTONIC_SYSCALL
37 #include <sys/syscall.h>
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)
43 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
46 static void gettimeofday_ns(struct timespec *ts)
48 if (sizeof(struct timeval) == sizeof(struct timespec)
49 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
52 gettimeofday((void*)ts, NULL);
55 extern void BUG_need_to_implement_gettimeofday_ns(void);
56 BUG_need_to_implement_gettimeofday_ns();
61 /* Compare possibly overflowing unsigned counters */
62 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
83 struct timespec start;
94 struct fd_pair selfpipe;
95 struct fd_pair logpipe;
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 )
107 #define INIT_G() do { \
111 static void fatal2_cannot(const char *m1, const char *m2)
113 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
114 /* was exiting 111 */
116 static void fatal_cannot(const char *m)
118 fatal2_cannot(m, "");
119 /* was exiting 111 */
121 static void fatal2x_cannot(const char *m1, const char *m2)
123 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
124 /* was exiting 111 */
126 static void warn_cannot(const char *m)
128 bb_perror_msg("%s: warning: cannot %s", dir, m);
131 static void s_child(int sig_no UNUSED_PARAM)
133 write(selfpipe.wr, "", 1);
136 static void s_term(int sig_no UNUSED_PARAM)
139 write(selfpipe.wr, "", 1); /* XXX */
142 static int open_trunc_or_warn(const char *name)
145 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
147 bb_perror_msg("%s: warning: cannot open %s",
152 static void update_status(struct svdir *s)
160 fd = open_trunc_or_warn("supervise/pid.new");
164 char spid[sizeof(int)*3 + 2];
165 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
166 write(fd, spid, size);
169 if (rename_or_warn("supervise/pid.new",
170 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
176 fd = open_trunc_or_warn("supervise/stat.new");
181 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
185 p = stpcpy(p, "down");
188 p = stpcpy(p, "run");
191 p = stpcpy(p, "finish");
194 if (s->ctrl & C_PAUSE)
195 p = stpcpy(p, ", paused");
196 if (s->ctrl & C_TERM)
197 p = stpcpy(p, ", got TERM");
198 if (s->state != S_DOWN)
199 switch (s->sd_want) {
201 p = stpcpy(p, ", want down");
204 p = stpcpy(p, ", want exit");
208 write(fd, stat_buf, p - stat_buf);
212 rename_or_warn("supervise/stat.new",
213 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
215 /* supervise compatibility */
216 memset(&status, 0, sizeof(status));
217 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
218 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
219 status.pid_le32 = SWAP_LE32(s->pid);
220 if (s->ctrl & C_PAUSE)
222 if (s->sd_want == W_UP)
226 if (s->ctrl & C_TERM)
228 status.run_or_finish = s->state;
229 fd = open_trunc_or_warn("supervise/status.new");
232 sz = write(fd, &status, sizeof(status));
234 if (sz != sizeof(status)) {
235 warn_cannot("write supervise/status.new");
236 unlink("supervise/status.new");
239 rename_or_warn("supervise/status.new",
240 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
243 static unsigned custom(struct svdir *s, char c)
252 strcpy(a, "control/?");
253 a[8] = c; /* replace '?' */
254 if (stat(a, &st) == 0) {
255 if (st.st_mode & S_IXUSR) {
258 warn_cannot("vfork for control/?");
263 if (haslog && dup2(logpipe.wr, 1) == -1)
264 warn_cannot("setup stdout for control/?");
265 execl(a, a, (char *) NULL);
266 fatal_cannot("run control/?");
269 if (safe_waitpid(pid, &w, 0) == -1) {
270 warn_cannot("wait for child control/?");
273 return WEXITSTATUS(w) == 0;
277 warn_cannot("stat control/?");
282 static void stopservice(struct svdir *s)
284 if (s->pid && !custom(s, 't')) {
285 kill(s->pid, SIGTERM);
289 if (s->sd_want == W_DOWN) {
290 kill(s->pid, SIGCONT);
294 if (s->sd_want == W_EXIT) {
295 kill(s->pid, SIGCONT);
300 static void startservice(struct svdir *s)
304 char exitcode[sizeof(int)*3 + 2];
306 if (s->state == S_FINISH) {
307 /* Two arguments are given to ./finish. The first one is ./run exit code,
308 * or -1 if ./run didnt exit normally. The second one is
309 * the least significant byte of the exit status as determined by waitpid;
310 * for instance it is 0 if ./run exited normally, and the signal number
311 * if ./run was terminated by a signal. If runsv cannot start ./run
312 * for some reason, the exit code is 111 and the status is 0.
316 if (WIFEXITED(s->wstat)) {
317 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
321 //if (WIFSIGNALED(s->wstat)) {
322 arg[2] = utoa(WTERMSIG(s->wstat));
332 stopservice(s); /* should never happen */
333 while ((p = vfork()) == -1) {
334 warn_cannot("vfork, sleeping");
340 /* NB: bug alert! right order is close, then dup2 */
344 xdup2(logpipe.rd, 0);
347 xdup2(logpipe.wr, 1);
350 /* Non-ignored signals revert to SIG_DFL on exec anyway */
355 sig_unblock(SIGCHLD);
356 sig_unblock(SIGTERM);
357 execv(arg[0], (char**) arg);
358 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
361 if (s->state != S_FINISH) {
362 gettimeofday_ns(&s->start);
371 static int ctrl(struct svdir *s, char c)
379 if (s->pid && s->state != S_FINISH)
394 case 't': /* sig term */
395 if (s->pid && s->state != S_FINISH)
398 case 'k': /* sig kill */
399 if (s->pid && !custom(s, c))
400 kill(s->pid, SIGKILL);
403 case 'p': /* sig pause */
404 if (s->pid && !custom(s, c))
405 kill(s->pid, SIGSTOP);
409 case 'c': /* sig cont */
410 if (s->pid && !custom(s, c))
411 kill(s->pid, SIGCONT);
421 case 'a': /* sig alarm */
424 case 'h': /* sig hup */
427 case 'i': /* sig int */
430 case 'q': /* sig quit */
433 case '1': /* sig usr1 */
436 case '2': /* sig usr2 */
442 if (s->pid && !custom(s, c))
447 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
448 int runsv_main(int argc UNUSED_PARAM, char **argv)
457 dir = single_argv(argv);
459 xpiped_pair(selfpipe);
460 close_on_exec_on(selfpipe.rd);
461 close_on_exec_on(selfpipe.wr);
462 ndelay_on(selfpipe.rd);
463 ndelay_on(selfpipe.wr);
466 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
468 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
471 /* bss: svd[0].pid = 0; */
472 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
473 if (C_NOOP) svd[0].ctrl = C_NOOP;
474 if (W_UP) svd[0].sd_want = W_UP;
475 /* bss: svd[0].islog = 0; */
476 /* bss: svd[1].pid = 0; */
477 gettimeofday_ns(&svd[0].start);
478 if (stat("down", &s) != -1)
479 svd[0].sd_want = W_DOWN;
481 if (stat("log", &s) == -1) {
483 warn_cannot("stat ./log");
485 if (!S_ISDIR(s.st_mode)) {
487 warn_cannot("stat log/down: log is not a directory");
490 svd[1].state = S_DOWN;
491 svd[1].ctrl = C_NOOP;
492 svd[1].sd_want = W_UP;
494 gettimeofday_ns(&svd[1].start);
495 if (stat("log/down", &s) != -1)
496 svd[1].sd_want = W_DOWN;
497 xpiped_pair(logpipe);
498 close_on_exec_on(logpipe.rd);
499 close_on_exec_on(logpipe.wr);
503 if (mkdir("supervise", 0700) == -1) {
504 r = readlink("supervise", buf, sizeof(buf));
506 if (r == sizeof(buf))
507 fatal2x_cannot("readlink ./supervise", ": name too long");
511 if ((errno != ENOENT) && (errno != EINVAL))
512 fatal_cannot("readlink ./supervise");
515 svd[0].fdlock = xopen3("log/supervise/lock"+4,
516 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
517 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
518 fatal_cannot("lock supervise/lock");
519 close_on_exec_on(svd[0].fdlock);
521 if (mkdir("log/supervise", 0700) == -1) {
522 r = readlink("log/supervise", buf, 256);
525 fatal2x_cannot("readlink ./log/supervise", ": name too long");
527 fd = xopen(".", O_RDONLY|O_NDELAY);
530 if (fchdir(fd) == -1)
531 fatal_cannot("change back to service directory");
535 if ((errno != ENOENT) && (errno != EINVAL))
536 fatal_cannot("readlink ./log/supervise");
539 svd[1].fdlock = xopen3("log/supervise/lock",
540 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
541 if (flock(svd[1].fdlock, LOCK_EX) == -1)
542 fatal_cannot("lock log/supervise/lock");
543 close_on_exec_on(svd[1].fdlock);
546 mkfifo("log/supervise/control"+4, 0600);
547 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
548 close_on_exec_on(svd[0].fdcontrol);
549 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
550 close_on_exec_on(svd[0].fdcontrolwrite);
551 update_status(&svd[0]);
553 mkfifo("log/supervise/control", 0600);
554 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
555 close_on_exec_on(svd[1].fdcontrol);
556 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
557 close_on_exec_on(svd[1].fdcontrolwrite);
558 update_status(&svd[1]);
560 mkfifo("log/supervise/ok"+4, 0600);
561 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
562 close_on_exec_on(fd);
564 mkfifo("log/supervise/ok", 0600);
565 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
566 close_on_exec_on(fd);
574 if (!svd[1].pid && svd[1].sd_want == W_UP)
575 startservice(&svd[1]);
577 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
578 startservice(&svd[0]);
580 x[0].fd = selfpipe.rd;
581 x[0].events = POLLIN;
582 x[1].fd = svd[0].fdcontrol;
583 x[1].events = POLLIN;
584 /* x[2] is used only if haslog == 1 */
585 x[2].fd = svd[1].fdcontrol;
586 x[2].events = POLLIN;
587 sig_unblock(SIGTERM);
588 sig_unblock(SIGCHLD);
589 poll(x, 2 + haslog, 3600*1000);
593 while (read(selfpipe.rd, &ch, 1) == 1)
600 child = wait_any_nohang(&wstat);
603 if ((child == -1) && (errno != EINTR))
605 if (child == svd[0].pid) {
606 svd[0].wstat = wstat;
609 svd[0].ctrl &= ~C_TERM;
610 if (svd[0].state != S_FINISH) {
611 fd = open("finish", O_RDONLY|O_NDELAY);
614 svd[0].state = S_FINISH;
615 update_status(&svd[0]);
619 svd[0].state = S_DOWN;
620 deadline = svd[0].start.tv_sec + 1;
621 gettimeofday_ns(&svd[0].start);
622 update_status(&svd[0]);
623 if (LESS(svd[0].start.tv_sec, deadline))
627 if (child == svd[1].pid) {
628 svd[0].wstat = wstat;
631 svd[1].state = S_DOWN;
632 svd[1].ctrl &= ~C_TERM;
633 deadline = svd[1].start.tv_sec + 1;
634 gettimeofday_ns(&svd[1].start);
635 update_status(&svd[1]);
636 if (LESS(svd[1].start.tv_sec, deadline))
641 if (read(svd[0].fdcontrol, &ch, 1) == 1)
644 if (read(svd[1].fdcontrol, &ch, 1) == 1)
652 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
655 if (svd[1].sd_want != W_EXIT) {
656 svd[1].sd_want = W_EXIT;
657 /* stopservice(&svd[1]); */
658 update_status(&svd[1]);