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 Denis 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;
98 #define G (*(struct globals*)&bb_common_bufsiz1)
99 #define haslog (G.haslog )
100 #define sigterm (G.sigterm )
101 #define pidchanged (G.pidchanged )
102 #define selfpipe (G.selfpipe )
103 #define logpipe (G.logpipe )
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)
133 write(selfpipe[1], "", 1);
136 static void s_term(int sig_no)
139 write(selfpipe[1], "", 1); /* XXX */
142 static char *add_str(char *p, const char *to_add)
144 while ((*p = *to_add) != '\0') {
151 static int open_trunc_or_warn(const char *name)
153 int fd = open_trunc(name);
155 bb_perror_msg("%s: warning: cannot open %s",
160 static int rename_or_warn(const char *old, const char *new)
162 if (rename(old, new) == -1) {
163 bb_perror_msg("%s: warning: cannot rename %s to %s",
170 static void update_status(struct svdir *s)
178 fd = open_trunc_or_warn("supervise/pid.new");
182 char spid[sizeof(int)*3 + 2];
183 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
184 write(fd, spid, size);
187 if (rename_or_warn("supervise/pid.new",
188 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
194 fd = open_trunc_or_warn("supervise/stat.new");
199 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
203 p = add_str(p, "down");
206 p = add_str(p, "run");
209 p = add_str(p, "finish");
212 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
213 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
214 if (s->state != S_DOWN)
217 p = add_str(p, ", want down");
220 p = add_str(p, ", want exit");
224 write(fd, stat_buf, p - stat_buf);
228 rename_or_warn("supervise/stat.new",
229 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
231 /* supervise compatibility */
232 memset(&status, 0, sizeof(status));
233 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
234 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
235 status.pid_le32 = SWAP_LE32(s->pid);
236 if (s->ctrl & C_PAUSE)
242 if (s->ctrl & C_TERM)
244 status.run_or_finish = s->state;
245 fd = open_trunc_or_warn("supervise/status.new");
248 sz = write(fd, &status, sizeof(status));
250 if (sz != sizeof(status)) {
251 warn_cannot("write supervise/status.new");
252 unlink("supervise/status.new");
255 rename_or_warn("supervise/status.new",
256 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
259 static unsigned custom(struct svdir *s, char c)
267 if (s->islog) return 0;
268 strcpy(a, "control/?");
270 if (stat(a, &st) == 0) {
271 if (st.st_mode & S_IXUSR) {
274 warn_cannot("fork for control/?");
278 if (haslog && dup2(logpipe[1], 1) == -1)
279 warn_cannot("setup stdout for control/?");
282 execve(a, prog, environ);
283 fatal_cannot("run control/?");
285 while (wait_pid(&w, pid) == -1) {
286 if (errno == EINTR) continue;
287 warn_cannot("wait for child control/?");
290 return !wait_exitcode(w);
294 warn_cannot("stat control/?");
299 static void stopservice(struct svdir *s)
301 if (s->pid && !custom(s, 't')) {
302 kill(s->pid, SIGTERM);
306 if (s->want == W_DOWN) {
307 kill(s->pid, SIGCONT);
311 if (s->want == W_EXIT) {
312 kill(s->pid, SIGCONT);
317 static void startservice(struct svdir *s)
322 if (s->state == S_FINISH)
323 run[0] = (char*)"./finish";
325 run[0] = (char*)"./run";
331 stopservice(s); /* should never happen */
332 while ((p = fork()) == -1) {
333 warn_cannot("fork, sleeping");
340 xdup2(logpipe[0], 0);
344 xdup2(logpipe[1], 1);
348 signal(SIGCHLD, SIG_DFL);
349 signal(SIGTERM, SIG_DFL);
350 sig_unblock(SIGCHLD);
351 sig_unblock(SIGTERM);
353 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
355 if (s->state != S_FINISH) {
356 gettimeofday_ns(&s->start);
365 static int ctrl(struct svdir *s, char c)
373 if (s->pid && s->state != S_FINISH)
388 case 't': /* sig term */
389 if (s->pid && s->state != S_FINISH)
392 case 'k': /* sig kill */
393 if (s->pid && !custom(s, c))
394 kill(s->pid, SIGKILL);
397 case 'p': /* sig pause */
398 if (s->pid && !custom(s, c))
399 kill(s->pid, SIGSTOP);
403 case 'c': /* sig cont */
404 if (s->pid && !custom(s, c))
405 kill(s->pid, SIGCONT);
406 if (s->ctrl & C_PAUSE)
416 case 'a': /* sig alarm */
419 case 'h': /* sig hup */
422 case 'i': /* sig int */
425 case 'q': /* sig quit */
428 case '1': /* sig usr1 */
431 case '2': /* sig usr2 */
437 if (s->pid && !custom(s, c))
442 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
443 int runsv_main(int argc, char **argv)
452 if (!argv[1] || argv[2])
457 close_on_exec_on(selfpipe[0]);
458 close_on_exec_on(selfpipe[1]);
459 ndelay_on(selfpipe[0]);
460 ndelay_on(selfpipe[1]);
463 sig_catch(SIGCHLD, s_child);
465 sig_catch(SIGTERM, s_term);
468 /* bss: svd[0].pid = 0; */
469 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
470 if (C_NOOP) svd[0].ctrl = C_NOOP;
471 if (W_UP) svd[0].want = W_UP;
472 /* bss: svd[0].islog = 0; */
473 /* bss: svd[1].pid = 0; */
474 gettimeofday_ns(&svd[0].start);
475 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
477 if (stat("log", &s) == -1) {
479 warn_cannot("stat ./log");
481 if (!S_ISDIR(s.st_mode)) {
483 warn_cannot("stat log/down: log is not a directory");
486 svd[1].state = S_DOWN;
487 svd[1].ctrl = C_NOOP;
490 gettimeofday_ns(&svd[1].start);
491 if (stat("log/down", &s) != -1)
492 svd[1].want = W_DOWN;
494 close_on_exec_on(logpipe[0]);
495 close_on_exec_on(logpipe[1]);
499 if (mkdir("supervise", 0700) == -1) {
500 r = readlink("supervise", buf, sizeof(buf));
502 if (r == sizeof(buf))
503 fatal2x_cannot("readlink ./supervise", ": name too long");
507 if ((errno != ENOENT) && (errno != EINVAL))
508 fatal_cannot("readlink ./supervise");
511 svd[0].fdlock = xopen3("log/supervise/lock"+4,
512 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
513 if (lock_exnb(svd[0].fdlock) == -1)
514 fatal_cannot("lock supervise/lock");
515 close_on_exec_on(svd[0].fdlock);
517 if (mkdir("log/supervise", 0700) == -1) {
518 r = readlink("log/supervise", buf, 256);
521 fatal2x_cannot("readlink ./log/supervise", ": name too long");
523 fd = xopen(".", O_RDONLY|O_NDELAY);
526 if (fchdir(fd) == -1)
527 fatal_cannot("change back to service directory");
531 if ((errno != ENOENT) && (errno != EINVAL))
532 fatal_cannot("readlink ./log/supervise");
535 svd[1].fdlock = xopen3("log/supervise/lock",
536 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
537 if (lock_ex(svd[1].fdlock) == -1)
538 fatal_cannot("lock log/supervise/lock");
539 close_on_exec_on(svd[1].fdlock);
542 mkfifo("log/supervise/control"+4, 0600);
543 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
544 close_on_exec_on(svd[0].fdcontrol);
545 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
546 close_on_exec_on(svd[0].fdcontrolwrite);
547 update_status(&svd[0]);
549 mkfifo("log/supervise/control", 0600);
550 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
551 close_on_exec_on(svd[1].fdcontrol);
552 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
553 close_on_exec_on(svd[1].fdcontrolwrite);
554 update_status(&svd[1]);
556 mkfifo("log/supervise/ok"+4, 0600);
557 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
558 close_on_exec_on(fd);
560 mkfifo("log/supervise/ok", 0600);
561 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
562 close_on_exec_on(fd);
570 if (!svd[1].pid && svd[1].want == W_UP)
571 startservice(&svd[1]);
573 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
574 startservice(&svd[0]);
576 x[0].fd = selfpipe[0];
577 x[0].events = POLLIN;
578 x[1].fd = svd[0].fdcontrol;
579 x[1].events = POLLIN;
580 /* x[2] is used only if haslog == 1 */
581 x[2].fd = svd[1].fdcontrol;
582 x[2].events = POLLIN;
583 sig_unblock(SIGTERM);
584 sig_unblock(SIGCHLD);
585 poll(x, 2 + haslog, 3600*1000);
589 while (read(selfpipe[0], &ch, 1) == 1)
596 child = wait_nohang(&wstat);
599 if ((child == -1) && (errno != EINTR))
601 if (child == svd[0].pid) {
604 svd[0].ctrl &=~ C_TERM;
605 if (svd[0].state != S_FINISH) {
606 fd = open_read("finish");
609 svd[0].state = S_FINISH;
610 update_status(&svd[0]);
614 svd[0].state = S_DOWN;
615 deadline = svd[0].start.tv_sec + 1;
616 gettimeofday_ns(&svd[0].start);
617 update_status(&svd[0]);
618 if (LESS(svd[0].start.tv_sec, deadline))
622 if (child == svd[1].pid) {
625 svd[1].state = S_DOWN;
626 svd[1].ctrl &= ~C_TERM;
627 deadline = svd[1].start.tv_sec + 1;
628 gettimeofday_ns(&svd[1].start);
629 update_status(&svd[1]);
630 if (LESS(svd[1].start.tv_sec, deadline))
635 if (read(svd[0].fdcontrol, &ch, 1) == 1)
638 if (read(svd[1].fdcontrol, &ch, 1) == 1)
646 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
649 if (svd[1].want != W_EXIT) {
650 svd[1].want = W_EXIT;
651 /* stopservice(&svd[1]); */
652 update_status(&svd[1]);