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;
93 struct fd_pair selfpipe;
94 struct fd_pair logpipe;
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 )
106 #define INIT_G() do { \
110 static void fatal2_cannot(const char *m1, const char *m2)
112 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
113 /* was exiting 111 */
115 static void fatal_cannot(const char *m)
117 fatal2_cannot(m, "");
118 /* was exiting 111 */
120 static void fatal2x_cannot(const char *m1, const char *m2)
122 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
123 /* was exiting 111 */
125 static void warn_cannot(const char *m)
127 bb_perror_msg("%s: warning: cannot %s", dir, m);
130 static void s_child(int sig_no UNUSED_PARAM)
132 write(selfpipe.wr, "", 1);
135 static void s_term(int sig_no UNUSED_PARAM)
138 write(selfpipe.wr, "", 1); /* XXX */
141 static char *add_str(char *p, const char *to_add)
143 while ((*p = *to_add) != '\0') {
150 static int open_trunc_or_warn(const char *name)
152 int fd = open_trunc(name);
154 bb_perror_msg("%s: warning: cannot open %s",
159 static void update_status(struct svdir *s)
167 fd = open_trunc_or_warn("supervise/pid.new");
171 char spid[sizeof(int)*3 + 2];
172 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
173 write(fd, spid, size);
176 if (rename_or_warn("supervise/pid.new",
177 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
183 fd = open_trunc_or_warn("supervise/stat.new");
188 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
192 p = add_str(p, "down");
195 p = add_str(p, "run");
198 p = add_str(p, "finish");
201 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
202 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
203 if (s->state != S_DOWN)
206 p = add_str(p, ", want down");
209 p = add_str(p, ", want exit");
213 write(fd, stat_buf, p - stat_buf);
217 rename_or_warn("supervise/stat.new",
218 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
220 /* supervise compatibility */
221 memset(&status, 0, sizeof(status));
222 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
223 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
224 status.pid_le32 = SWAP_LE32(s->pid);
225 if (s->ctrl & C_PAUSE)
231 if (s->ctrl & C_TERM)
233 status.run_or_finish = s->state;
234 fd = open_trunc_or_warn("supervise/status.new");
237 sz = write(fd, &status, sizeof(status));
239 if (sz != sizeof(status)) {
240 warn_cannot("write supervise/status.new");
241 unlink("supervise/status.new");
244 rename_or_warn("supervise/status.new",
245 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
248 static unsigned custom(struct svdir *s, char c)
257 strcpy(a, "control/?");
258 a[8] = c; /* replace '?' */
259 if (stat(a, &st) == 0) {
260 if (st.st_mode & S_IXUSR) {
263 warn_cannot("vfork for control/?");
268 if (haslog && dup2(logpipe.wr, 1) == -1)
269 warn_cannot("setup stdout for control/?");
270 execl(a, a, (char *) NULL);
271 fatal_cannot("run control/?");
274 if (safe_waitpid(pid, &w, 0) == -1) {
275 warn_cannot("wait for child control/?");
278 return WEXITSTATUS(w) == 0;
282 warn_cannot("stat control/?");
287 static void stopservice(struct svdir *s)
289 if (s->pid && !custom(s, 't')) {
290 kill(s->pid, SIGTERM);
294 if (s->want == W_DOWN) {
295 kill(s->pid, SIGCONT);
299 if (s->want == W_EXIT) {
300 kill(s->pid, SIGCONT);
305 static void startservice(struct svdir *s)
310 if (s->state == S_FINISH)
318 stopservice(s); /* should never happen */
319 while ((p = vfork()) == -1) {
320 warn_cannot("vfork, sleeping");
326 /* NB: bug alert! right order is close, then dup2 */
330 xdup2(logpipe.rd, 0);
333 xdup2(logpipe.wr, 1);
336 /* Non-ignored signals revert to SIG_DFL on exec anyway */
341 sig_unblock(SIGCHLD);
342 sig_unblock(SIGTERM);
343 execl(run, run, (char *) NULL);
344 fatal2_cannot(s->islog ? "start log/" : "start ", run);
347 if (s->state != S_FINISH) {
348 gettimeofday_ns(&s->start);
357 static int ctrl(struct svdir *s, char c)
365 if (s->pid && s->state != S_FINISH)
380 case 't': /* sig term */
381 if (s->pid && s->state != S_FINISH)
384 case 'k': /* sig kill */
385 if (s->pid && !custom(s, c))
386 kill(s->pid, SIGKILL);
389 case 'p': /* sig pause */
390 if (s->pid && !custom(s, c))
391 kill(s->pid, SIGSTOP);
395 case 'c': /* sig cont */
396 if (s->pid && !custom(s, c))
397 kill(s->pid, SIGCONT);
407 case 'a': /* sig alarm */
410 case 'h': /* sig hup */
413 case 'i': /* sig int */
416 case 'q': /* sig quit */
419 case '1': /* sig usr1 */
422 case '2': /* sig usr2 */
428 if (s->pid && !custom(s, c))
433 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
434 int runsv_main(int argc UNUSED_PARAM, char **argv)
443 if (!argv[1] || argv[2])
447 xpiped_pair(selfpipe);
448 close_on_exec_on(selfpipe.rd);
449 close_on_exec_on(selfpipe.wr);
450 ndelay_on(selfpipe.rd);
451 ndelay_on(selfpipe.wr);
454 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
456 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
459 /* bss: svd[0].pid = 0; */
460 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
461 if (C_NOOP) svd[0].ctrl = C_NOOP;
462 if (W_UP) svd[0].want = W_UP;
463 /* bss: svd[0].islog = 0; */
464 /* bss: svd[1].pid = 0; */
465 gettimeofday_ns(&svd[0].start);
466 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
468 if (stat("log", &s) == -1) {
470 warn_cannot("stat ./log");
472 if (!S_ISDIR(s.st_mode)) {
474 warn_cannot("stat log/down: log is not a directory");
477 svd[1].state = S_DOWN;
478 svd[1].ctrl = C_NOOP;
481 gettimeofday_ns(&svd[1].start);
482 if (stat("log/down", &s) != -1)
483 svd[1].want = W_DOWN;
484 xpiped_pair(logpipe);
485 close_on_exec_on(logpipe.rd);
486 close_on_exec_on(logpipe.wr);
490 if (mkdir("supervise", 0700) == -1) {
491 r = readlink("supervise", buf, sizeof(buf));
493 if (r == sizeof(buf))
494 fatal2x_cannot("readlink ./supervise", ": name too long");
498 if ((errno != ENOENT) && (errno != EINVAL))
499 fatal_cannot("readlink ./supervise");
502 svd[0].fdlock = xopen3("log/supervise/lock"+4,
503 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
504 if (lock_exnb(svd[0].fdlock) == -1)
505 fatal_cannot("lock supervise/lock");
506 close_on_exec_on(svd[0].fdlock);
508 if (mkdir("log/supervise", 0700) == -1) {
509 r = readlink("log/supervise", buf, 256);
512 fatal2x_cannot("readlink ./log/supervise", ": name too long");
514 fd = xopen(".", O_RDONLY|O_NDELAY);
517 if (fchdir(fd) == -1)
518 fatal_cannot("change back to service directory");
522 if ((errno != ENOENT) && (errno != EINVAL))
523 fatal_cannot("readlink ./log/supervise");
526 svd[1].fdlock = xopen3("log/supervise/lock",
527 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
528 if (lock_ex(svd[1].fdlock) == -1)
529 fatal_cannot("lock log/supervise/lock");
530 close_on_exec_on(svd[1].fdlock);
533 mkfifo("log/supervise/control"+4, 0600);
534 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
535 close_on_exec_on(svd[0].fdcontrol);
536 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
537 close_on_exec_on(svd[0].fdcontrolwrite);
538 update_status(&svd[0]);
540 mkfifo("log/supervise/control", 0600);
541 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
542 close_on_exec_on(svd[1].fdcontrol);
543 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
544 close_on_exec_on(svd[1].fdcontrolwrite);
545 update_status(&svd[1]);
547 mkfifo("log/supervise/ok"+4, 0600);
548 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
549 close_on_exec_on(fd);
551 mkfifo("log/supervise/ok", 0600);
552 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
553 close_on_exec_on(fd);
561 if (!svd[1].pid && svd[1].want == W_UP)
562 startservice(&svd[1]);
564 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
565 startservice(&svd[0]);
567 x[0].fd = selfpipe.rd;
568 x[0].events = POLLIN;
569 x[1].fd = svd[0].fdcontrol;
570 x[1].events = POLLIN;
571 /* x[2] is used only if haslog == 1 */
572 x[2].fd = svd[1].fdcontrol;
573 x[2].events = POLLIN;
574 sig_unblock(SIGTERM);
575 sig_unblock(SIGCHLD);
576 poll(x, 2 + haslog, 3600*1000);
580 while (read(selfpipe.rd, &ch, 1) == 1)
587 child = wait_any_nohang(&wstat);
590 if ((child == -1) && (errno != EINTR))
592 if (child == svd[0].pid) {
595 svd[0].ctrl &=~ C_TERM;
596 if (svd[0].state != S_FINISH) {
597 fd = open_read("finish");
600 svd[0].state = S_FINISH;
601 update_status(&svd[0]);
605 svd[0].state = S_DOWN;
606 deadline = svd[0].start.tv_sec + 1;
607 gettimeofday_ns(&svd[0].start);
608 update_status(&svd[0]);
609 if (LESS(svd[0].start.tv_sec, deadline))
613 if (child == svd[1].pid) {
616 svd[1].state = S_DOWN;
617 svd[1].ctrl &= ~C_TERM;
618 deadline = svd[1].start.tv_sec + 1;
619 gettimeofday_ns(&svd[1].start);
620 update_status(&svd[1]);
621 if (LESS(svd[1].start.tv_sec, deadline))
626 if (read(svd[0].fdcontrol, &ch, 1) == 1)
629 if (read(svd[1].fdcontrol, &ch, 1) == 1)
637 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
640 if (svd[1].want != W_EXIT) {
641 svd[1].want = W_EXIT;
642 /* stopservice(&svd[1]); */
643 update_status(&svd[1]);