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 static int selfpipe[2];
62 static struct svdir svd[2];
64 static smallint sigterm;
65 static smallint haslog;
66 static smallint pidchanged = 1;
67 static int logpipe[2];
70 #define usage() bb_show_usage()
72 static void fatal2_cannot(const char *m1, const char *m2)
74 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
77 static void fatal_cannot(const char *m)
82 static void fatal2x_cannot(const char *m1, const char *m2)
84 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
87 static void warn_cannot(const char *m)
89 bb_perror_msg("%s: warning: cannot %s", dir, m);
91 static void warnx_cannot(const char *m)
93 bb_error_msg("%s: warning: cannot %s", dir, m);
96 static void s_child(int sig_no)
98 write(selfpipe[1], "", 1);
101 static void s_term(int sig_no)
104 write(selfpipe[1], "", 1); /* XXX */
107 static char *add_str(char *p, const char *to_add)
109 while ((*p = *to_add) != '\0') {
116 static int open_trunc_or_warn(const char *name)
118 int fd = open_trunc(name);
120 bb_perror_msg("%s: warning: cannot open %s",
125 static int rename_or_warn(const char *old, const char *new)
127 if (rename(old, new) == -1) {
128 bb_perror_msg("%s: warning: cannot rename %s to %s",
135 static void update_status(struct svdir *s)
143 fd = open_trunc_or_warn("supervise/pid.new");
147 char spid[sizeof(int)*3 + 2];
148 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
149 write(fd, spid, size);
152 if (rename_or_warn("supervise/pid.new",
153 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
159 fd = open_trunc_or_warn("supervise/stat.new");
164 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
168 p = add_str(p, "down");
171 p = add_str(p, "run");
174 p = add_str(p, "finish");
177 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
178 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
179 if (s->state != S_DOWN)
182 p = add_str(p, ", want down");
185 p = add_str(p, ", want exit");
189 write(fd, stat_buf, p - stat_buf);
193 rename_or_warn("supervise/stat.new",
194 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
196 /* supervise compatibility */
197 taia_pack(status, &s->start);
198 l = (unsigned long)s->pid;
199 status[12] = l; l >>=8;
200 status[13] = l; l >>=8;
201 status[14] = l; l >>=8;
203 if (s->ctrl & C_PAUSE)
211 if (s->ctrl & C_TERM)
215 status[19] = s->state;
216 fd = open_trunc_or_warn("supervise/status.new");
219 l = write(fd, status, sizeof(status));
221 warn_cannot("write supervise/status.new");
223 unlink("supervise/status.new");
227 if (l < sizeof(status)) {
228 warnx_cannot("write supervise/status.new: partial write");
231 rename_or_warn("supervise/status.new",
232 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
235 static unsigned custom(struct svdir *s, char c)
243 if (s->islog) return 0;
244 strcpy(a, "control/?");
246 if (stat(a, &st) == 0) {
247 if (st.st_mode & S_IXUSR) {
250 warn_cannot("fork for control/?");
254 if (haslog && dup2(logpipe[1], 1) == -1)
255 warn_cannot("setup stdout for control/?");
258 execve(a, prog, environ);
259 fatal_cannot("run control/?");
261 while (wait_pid(&w, pid) == -1) {
262 if (errno == EINTR) continue;
263 warn_cannot("wait for child control/?");
266 return !wait_exitcode(w);
270 warn_cannot("stat control/?");
275 static void stopservice(struct svdir *s)
277 if (s->pid && !custom(s, 't')) {
278 kill(s->pid, SIGTERM);
282 if (s->want == W_DOWN) {
283 kill(s->pid, SIGCONT);
287 if (s->want == W_EXIT) {
288 kill(s->pid, SIGCONT);
293 static void startservice(struct svdir *s)
298 if (s->state == S_FINISH)
299 run[0] = (char*)"./finish";
301 run[0] = (char*)"./run";
307 stopservice(s); /* should never happen */
308 while ((p = fork()) == -1) {
309 warn_cannot("fork, sleeping");
316 if (dup2(logpipe[0], 0) == -1)
317 fatal_cannot("setup filedescriptor for ./log/run");
319 if (chdir("./log") == -1)
320 fatal_cannot("change directory to ./log");
322 if (dup2(logpipe[1], 1) == -1)
323 fatal_cannot("setup filedescriptor for ./run");
327 signal(SIGCHLD, SIG_DFL);
328 signal(SIGTERM, SIG_DFL);
329 sig_unblock(SIGCHLD);
330 sig_unblock(SIGTERM);
332 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
334 if (s->state != S_FINISH) {
344 static int ctrl(struct svdir *s, char c)
352 if (s->pid && s->state != S_FINISH) stopservice(s);
357 if (s->pid == 0) startservice(s);
364 case 't': /* sig term */
365 if (s->pid && s->state != S_FINISH) stopservice(s);
367 case 'k': /* sig kill */
368 if (s->pid && !custom(s, c)) kill(s->pid, SIGKILL);
371 case 'p': /* sig pause */
372 if (s->pid && !custom(s, c)) kill(s->pid, SIGSTOP);
376 case 'c': /* sig cont */
377 if (s->pid && !custom(s, c)) kill(s->pid, SIGCONT);
378 if (s->ctrl & C_PAUSE) s->ctrl &= ~C_PAUSE;
384 if (!s->pid) startservice(s);
386 case 'a': /* sig alarm */
389 case 'h': /* sig hup */
392 case 'i': /* sig int */
395 case 'q': /* sig quit */
398 case '1': /* sig usr1 */
401 case '2': /* sig usr2 */
407 if (s->pid && !custom(s, c))
412 int runsv_main(int argc, char **argv);
413 int runsv_main(int argc, char **argv)
420 if (!argv[1] || argv[2]) usage();
423 if (pipe(selfpipe) == -1) fatal_cannot("create selfpipe");
426 ndelay_on(selfpipe[0]);
427 ndelay_on(selfpipe[1]);
430 sig_catch(SIGCHLD, s_child);
432 sig_catch(SIGTERM, s_term);
435 /* bss: svd[0].pid = 0; */
436 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
437 if (C_NOOP) svd[0].ctrl = C_NOOP;
438 if (W_UP) svd[0].want = W_UP;
439 /* bss: svd[0].islog = 0; */
440 /* bss: svd[1].pid = 0; */
441 taia_now(&svd[0].start);
442 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
444 if (stat("log", &s) == -1) {
446 warn_cannot("stat ./log");
448 if (!S_ISDIR(s.st_mode))
449 warnx_cannot("stat log/down: log is not a directory");
452 svd[1].state = S_DOWN;
453 svd[1].ctrl = C_NOOP;
456 taia_now(&svd[1].start);
457 if (stat("log/down", &s) != -1)
458 svd[1].want = W_DOWN;
459 if (pipe(logpipe) == -1)
460 fatal_cannot("create log pipe");
466 if (mkdir("supervise", 0700) == -1) {
467 r = readlink("supervise", buf, sizeof(buf));
469 if (r == sizeof(buf))
470 fatal2x_cannot("readlink ./supervise", ": name too long");
474 if ((errno != ENOENT) && (errno != EINVAL))
475 fatal_cannot("readlink ./supervise");
478 svd[0].fdlock = xopen3("log/supervise/lock"+4,
479 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
480 if (lock_exnb(svd[0].fdlock) == -1)
481 fatal_cannot("lock supervise/lock");
484 if (mkdir("log/supervise", 0700) == -1) {
485 r = readlink("log/supervise", buf, 256);
488 fatal2x_cannot("readlink ./log/supervise", ": name too long");
490 fd = xopen(".", O_RDONLY|O_NDELAY);
493 if (fchdir(fd) == -1)
494 fatal_cannot("change back to service directory");
498 if ((errno != ENOENT) && (errno != EINVAL))
499 fatal_cannot("readlink ./log/supervise");
502 svd[1].fdlock = xopen3("log/supervise/lock",
503 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
504 if (lock_ex(svd[1].fdlock) == -1)
505 fatal_cannot("lock log/supervise/lock");
509 mkfifo("log/supervise/control"+4, 0600);
510 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
511 coe(svd[0].fdcontrol);
512 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
513 coe(svd[0].fdcontrolwrite);
514 update_status(&svd[0]);
516 mkfifo("log/supervise/control", 0600);
517 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
518 coe(svd[1].fdcontrol);
519 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
520 coe(svd[1].fdcontrolwrite);
521 update_status(&svd[1]);
523 mkfifo("log/supervise/ok"+4, 0600);
524 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
527 mkfifo("log/supervise/ok", 0600);
528 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
533 struct taia deadline;
538 if (!svd[1].pid && svd[1].want == W_UP)
539 startservice(&svd[1]);
541 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
542 startservice(&svd[0]);
544 x[0].fd = selfpipe[0];
545 x[0].events = IOPAUSE_READ;
546 x[1].fd = svd[0].fdcontrol;
547 x[1].events = IOPAUSE_READ;
549 x[2].fd = svd[1].fdcontrol;
550 x[2].events = IOPAUSE_READ;
553 taia_uint(&deadline, 3600);
554 taia_add(&deadline, &now, &deadline);
556 sig_unblock(SIGTERM);
557 sig_unblock(SIGCHLD);
558 iopause(x, 2+haslog, &deadline, &now);
562 while (read(selfpipe[0], &ch, 1) == 1)
568 child = wait_nohang(&wstat);
570 if ((child == -1) && (errno != EINTR)) break;
571 if (child == svd[0].pid) {
574 svd[0].ctrl &=~ C_TERM;
575 if (svd[0].state != S_FINISH) {
576 fd = open_read("finish");
579 svd[0].state = S_FINISH;
580 update_status(&svd[0]);
584 svd[0].state = S_DOWN;
585 taia_uint(&deadline, 1);
586 taia_add(&deadline, &svd[0].start, &deadline);
587 taia_now(&svd[0].start);
588 update_status(&svd[0]);
589 if (taia_less(&svd[0].start, &deadline)) sleep(1);
592 if (child == svd[1].pid) {
595 svd[1].state = S_DOWN;
596 svd[1].ctrl &= ~C_TERM;
597 taia_uint(&deadline, 1);
598 taia_add(&deadline, &svd[1].start, &deadline);
599 taia_now(&svd[1].start);
600 update_status(&svd[1]);
601 if (taia_less(&svd[1].start, &deadline)) sleep(1);
605 if (read(svd[0].fdcontrol, &ch, 1) == 1)
608 if (read(svd[1].fdcontrol, &ch, 1) == 1)
616 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
619 if (svd[1].want != W_EXIT) {
620 svd[1].want = W_EXIT;
621 /* stopservice(&svd[1]); */
622 update_status(&svd[1]);
625 //if (close(logpipe[1]) == -1)
626 // warn_cannot("close logpipe[1]");
627 //if (close(logpipe[0]) == -1)
628 // warn_cannot("close logpipe[0]");