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";
306 if (s->pid != 0) stopservice(s); /* should never happen */
307 while ((p = fork()) == -1) {
308 warn_cannot("fork, sleeping");
315 if (dup2(logpipe[0], 0) == -1)
316 fatal_cannot("setup filedescriptor for ./log/run");
318 if (chdir("./log") == -1)
319 fatal_cannot("change directory to ./log");
321 if (dup2(logpipe[1], 1) == -1)
322 fatal_cannot("setup filedescriptor for ./run");
326 signal(SIGCHLD, SIG_DFL);
327 signal(SIGTERM, SIG_DFL);
328 sig_unblock(SIGCHLD);
329 sig_unblock(SIGTERM);
331 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
333 if (s->state != S_FINISH) {
343 static int ctrl(struct svdir *s, char c)
351 if (s->pid && s->state != S_FINISH) stopservice(s);
356 if (s->pid == 0) startservice(s);
363 case 't': /* sig term */
364 if (s->pid && s->state != S_FINISH) stopservice(s);
366 case 'k': /* sig kill */
367 if (s->pid && !custom(s, c)) kill(s->pid, SIGKILL);
370 case 'p': /* sig pause */
371 if (s->pid && !custom(s, c)) kill(s->pid, SIGSTOP);
375 case 'c': /* sig cont */
376 if (s->pid && !custom(s, c)) kill(s->pid, SIGCONT);
377 if (s->ctrl & C_PAUSE) s->ctrl &= ~C_PAUSE;
383 if (!s->pid) startservice(s);
385 case 'a': /* sig alarm */
388 case 'h': /* sig hup */
391 case 'i': /* sig int */
394 case 'q': /* sig quit */
397 case '1': /* sig usr1 */
400 case '2': /* sig usr2 */
406 if (s->pid && !custom(s, c))
411 int runsv_main(int argc, char **argv);
412 int runsv_main(int argc, char **argv)
419 if (!argv[1] || argv[2]) usage();
422 if (pipe(selfpipe) == -1) fatal_cannot("create selfpipe");
425 ndelay_on(selfpipe[0]);
426 ndelay_on(selfpipe[1]);
429 sig_catch(SIGCHLD, s_child);
431 sig_catch(SIGTERM, s_term);
434 /* bss: svd[0].pid = 0; */
435 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
436 if (C_NOOP) svd[0].ctrl = C_NOOP;
437 if (W_UP) svd[0].want = W_UP;
438 /* bss: svd[0].islog = 0; */
439 /* bss: svd[1].pid = 0; */
440 taia_now(&svd[0].start);
441 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
443 if (stat("log", &s) == -1) {
445 warn_cannot("stat ./log");
447 if (!S_ISDIR(s.st_mode))
448 warnx_cannot("stat log/down: log is not a directory");
451 svd[1].state = S_DOWN;
452 svd[1].ctrl = C_NOOP;
455 taia_now(&svd[1].start);
456 if (stat("log/down", &s) != -1)
457 svd[1].want = W_DOWN;
458 if (pipe(logpipe) == -1)
459 fatal_cannot("create log pipe");
465 if (mkdir("supervise", 0700) == -1) {
466 r = readlink("supervise", buf, sizeof(buf));
468 if (r == sizeof(buf))
469 fatal2x_cannot("readlink ./supervise", ": name too long");
473 if ((errno != ENOENT) && (errno != EINVAL))
474 fatal_cannot("readlink ./supervise");
477 svd[0].fdlock = xopen3("log/supervise/lock"+4,
478 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
479 if (lock_exnb(svd[0].fdlock) == -1)
480 fatal_cannot("lock supervise/lock");
483 if (mkdir("log/supervise", 0700) == -1) {
484 r = readlink("log/supervise", buf, 256);
487 fatal2x_cannot("readlink ./log/supervise", ": name too long");
489 fd = xopen(".", O_RDONLY|O_NDELAY);
492 if (fchdir(fd) == -1)
493 fatal_cannot("change back to service directory");
497 if ((errno != ENOENT) && (errno != EINVAL))
498 fatal_cannot("readlink ./log/supervise");
501 svd[1].fdlock = xopen3("log/supervise/lock",
502 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
503 if (lock_ex(svd[1].fdlock) == -1)
504 fatal_cannot("lock log/supervise/lock");
508 mkfifo("log/supervise/control"+4, 0600);
509 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
510 coe(svd[0].fdcontrol);
511 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
512 coe(svd[0].fdcontrolwrite);
513 update_status(&svd[0]);
515 mkfifo("log/supervise/control", 0600);
516 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
517 coe(svd[1].fdcontrol);
518 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
519 coe(svd[1].fdcontrolwrite);
520 update_status(&svd[1]);
522 mkfifo("log/supervise/ok"+4, 0600);
523 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
526 mkfifo("log/supervise/ok", 0600);
527 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
532 struct taia deadline;
537 if (!svd[1].pid && svd[1].want == W_UP)
538 startservice(&svd[1]);
540 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
541 startservice(&svd[0]);
543 x[0].fd = selfpipe[0];
544 x[0].events = IOPAUSE_READ;
545 x[1].fd = svd[0].fdcontrol;
546 x[1].events = IOPAUSE_READ;
548 x[2].fd = svd[1].fdcontrol;
549 x[2].events = IOPAUSE_READ;
552 taia_uint(&deadline, 3600);
553 taia_add(&deadline, &now, &deadline);
555 sig_unblock(SIGTERM);
556 sig_unblock(SIGCHLD);
557 iopause(x, 2+haslog, &deadline, &now);
561 while (read(selfpipe[0], &ch, 1) == 1)
567 child = wait_nohang(&wstat);
569 if ((child == -1) && (errno != EINTR)) break;
570 if (child == svd[0].pid) {
573 svd[0].ctrl &=~ C_TERM;
574 if (svd[0].state != S_FINISH) {
575 fd = open_read("finish");
578 svd[0].state = S_FINISH;
579 update_status(&svd[0]);
583 svd[0].state = S_DOWN;
584 taia_uint(&deadline, 1);
585 taia_add(&deadline, &svd[0].start, &deadline);
586 taia_now(&svd[0].start);
587 update_status(&svd[0]);
588 if (taia_less(&svd[0].start, &deadline)) sleep(1);
591 if (child == svd[1].pid) {
594 svd[1].state = S_DOWN;
595 svd[1].ctrl &= ~C_TERM;
596 taia_uint(&deadline, 1);
597 taia_add(&deadline, &svd[1].start, &deadline);
598 taia_now(&svd[1].start);
599 update_status(&svd[1]);
600 if (taia_less(&svd[1].start, &deadline)) sleep(1);
604 if (read(svd[0].fdcontrol, &ch, 1) == 1)
607 if (read(svd[1].fdcontrol, &ch, 1) == 1)
615 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
618 if (svd[1].want != W_EXIT) {
619 svd[1].want = W_EXIT;
620 /* stopservice(&svd[1]); */
621 update_status(&svd[1]);
624 //if (close(logpipe[1]) == -1)
625 // warn_cannot("close logpipe[1]");
626 //if (close(logpipe[0]) == -1)
627 // warn_cannot("close logpipe[0]");