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 /* libbb candidate */
143 static char *bb_stpcpy(char *p, const char *to_add)
145 while ((*p = *to_add) != '\0') {
152 static int open_trunc_or_warn(const char *name)
154 int fd = open_trunc(name);
156 bb_perror_msg("%s: warning: cannot open %s",
161 static void update_status(struct svdir *s)
169 fd = open_trunc_or_warn("supervise/pid.new");
173 char spid[sizeof(int)*3 + 2];
174 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
175 write(fd, spid, size);
178 if (rename_or_warn("supervise/pid.new",
179 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
185 fd = open_trunc_or_warn("supervise/stat.new");
190 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
194 p = bb_stpcpy(p, "down");
197 p = bb_stpcpy(p, "run");
200 p = bb_stpcpy(p, "finish");
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) {
210 p = bb_stpcpy(p, ", want down");
213 p = bb_stpcpy(p, ", want exit");
217 write(fd, stat_buf, p - stat_buf);
221 rename_or_warn("supervise/stat.new",
222 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
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)
231 if (s->sd_want == W_UP)
235 if (s->ctrl & C_TERM)
237 status.run_or_finish = s->state;
238 fd = open_trunc_or_warn("supervise/status.new");
241 sz = write(fd, &status, sizeof(status));
243 if (sz != sizeof(status)) {
244 warn_cannot("write supervise/status.new");
245 unlink("supervise/status.new");
248 rename_or_warn("supervise/status.new",
249 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
252 static unsigned custom(struct svdir *s, char c)
261 strcpy(a, "control/?");
262 a[8] = c; /* replace '?' */
263 if (stat(a, &st) == 0) {
264 if (st.st_mode & S_IXUSR) {
267 warn_cannot("vfork for control/?");
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/?");
278 if (safe_waitpid(pid, &w, 0) == -1) {
279 warn_cannot("wait for child control/?");
282 return WEXITSTATUS(w) == 0;
286 warn_cannot("stat control/?");
291 static void stopservice(struct svdir *s)
293 if (s->pid && !custom(s, 't')) {
294 kill(s->pid, SIGTERM);
298 if (s->sd_want == W_DOWN) {
299 kill(s->pid, SIGCONT);
303 if (s->sd_want == W_EXIT) {
304 kill(s->pid, SIGCONT);
309 static void startservice(struct svdir *s)
313 char exitcode[sizeof(int)*3 + 2];
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.
325 if (WIFEXITED(s->wstat)) {
326 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
330 //if (WIFSIGNALED(s->wstat)) {
331 arg[2] = utoa(WTERMSIG(s->wstat));
341 stopservice(s); /* should never happen */
342 while ((p = vfork()) == -1) {
343 warn_cannot("vfork, sleeping");
349 /* NB: bug alert! right order is close, then dup2 */
353 xdup2(logpipe.rd, 0);
356 xdup2(logpipe.wr, 1);
359 /* Non-ignored signals revert to SIG_DFL on exec anyway */
364 sig_unblock(SIGCHLD);
365 sig_unblock(SIGTERM);
366 execv(arg[0], (char**) arg);
367 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
370 if (s->state != S_FINISH) {
371 gettimeofday_ns(&s->start);
380 static int ctrl(struct svdir *s, char c)
388 if (s->pid && s->state != S_FINISH)
403 case 't': /* sig term */
404 if (s->pid && s->state != S_FINISH)
407 case 'k': /* sig kill */
408 if (s->pid && !custom(s, c))
409 kill(s->pid, SIGKILL);
412 case 'p': /* sig pause */
413 if (s->pid && !custom(s, c))
414 kill(s->pid, SIGSTOP);
418 case 'c': /* sig cont */
419 if (s->pid && !custom(s, c))
420 kill(s->pid, SIGCONT);
430 case 'a': /* sig alarm */
433 case 'h': /* sig hup */
436 case 'i': /* sig int */
439 case 'q': /* sig quit */
442 case '1': /* sig usr1 */
445 case '2': /* sig usr2 */
451 if (s->pid && !custom(s, c))
456 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
457 int runsv_main(int argc UNUSED_PARAM, char **argv)
466 dir = single_argv(argv);
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);
475 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
477 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
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;
490 if (stat("log", &s) == -1) {
492 warn_cannot("stat ./log");
494 if (!S_ISDIR(s.st_mode)) {
496 warn_cannot("stat log/down: log is not a directory");
499 svd[1].state = S_DOWN;
500 svd[1].ctrl = C_NOOP;
501 svd[1].sd_want = W_UP;
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);
512 if (mkdir("supervise", 0700) == -1) {
513 r = readlink("supervise", buf, sizeof(buf));
515 if (r == sizeof(buf))
516 fatal2x_cannot("readlink ./supervise", ": name too long");
520 if ((errno != ENOENT) && (errno != EINVAL))
521 fatal_cannot("readlink ./supervise");
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);
530 if (mkdir("log/supervise", 0700) == -1) {
531 r = readlink("log/supervise", buf, 256);
534 fatal2x_cannot("readlink ./log/supervise", ": name too long");
536 fd = xopen(".", O_RDONLY|O_NDELAY);
539 if (fchdir(fd) == -1)
540 fatal_cannot("change back to service directory");
544 if ((errno != ENOENT) && (errno != EINVAL))
545 fatal_cannot("readlink ./log/supervise");
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);
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]);
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]);
569 mkfifo("log/supervise/ok"+4, 0600);
570 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
571 close_on_exec_on(fd);
573 mkfifo("log/supervise/ok", 0600);
574 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
575 close_on_exec_on(fd);
583 if (!svd[1].pid && svd[1].sd_want == W_UP)
584 startservice(&svd[1]);
586 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
587 startservice(&svd[0]);
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);
602 while (read(selfpipe.rd, &ch, 1) == 1)
609 child = wait_any_nohang(&wstat);
612 if ((child == -1) && (errno != EINTR))
614 if (child == svd[0].pid) {
615 svd[0].wstat = wstat;
618 svd[0].ctrl &= ~C_TERM;
619 if (svd[0].state != S_FINISH) {
620 fd = open_read("finish");
623 svd[0].state = S_FINISH;
624 update_status(&svd[0]);
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))
636 if (child == svd[1].pid) {
637 svd[0].wstat = wstat;
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))
650 if (read(svd[0].fdcontrol, &ch, 1) == 1)
653 if (read(svd[1].fdcontrol, &ch, 1) == 1)
661 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
664 if (svd[1].sd_want != W_EXIT) {
665 svd[1].sd_want = W_EXIT;
666 /* stopservice(&svd[1]); */
667 update_status(&svd[1]);