Tomato 1.25
[tomato.git] / release / src / router / busybox / runit / runsv.c
blob6d34dc133005de7a4b6022327d5d6deb6f6ad3fc
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
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 */
31 #include <sys/poll.h>
32 #include <sys/file.h>
33 #include "libbb.h"
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);
45 #else
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)
50 ) {
51 /* Cheat */
52 gettimeofday((void*)ts, NULL);
53 ts->tv_nsec *= 1000;
54 } else {
55 extern void BUG_need_to_implement_gettimeofday_ns(void);
56 BUG_need_to_implement_gettimeofday_ns();
59 #endif
61 /* Compare possibly overflowing unsigned counters */
62 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
64 /* state */
65 #define S_DOWN 0
66 #define S_RUN 1
67 #define S_FINISH 2
68 /* ctrl */
69 #define C_NOOP 0
70 #define C_TERM 1
71 #define C_PAUSE 2
72 /* want */
73 #define W_UP 0
74 #define W_DOWN 1
75 #define W_EXIT 2
77 struct svdir {
78 int pid;
79 smallint state;
80 smallint ctrl;
81 smallint want;
82 smallint islog;
83 struct timespec start;
84 int fdlock;
85 int fdcontrol;
86 int fdcontrolwrite;
89 struct globals {
90 smallint haslog;
91 smallint sigterm;
92 smallint pidchanged;
93 struct fd_pair selfpipe;
94 struct fd_pair logpipe;
95 char *dir;
96 struct svdir svd[2];
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 )
104 #define dir (G.dir )
105 #define svd (G.svd )
106 #define INIT_G() do { \
107 pidchanged = 1; \
108 } while (0)
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)
137 sigterm = 1;
138 write(selfpipe.wr, "", 1); /* XXX */
141 static char *add_str(char *p, const char *to_add)
143 while ((*p = *to_add) != '\0') {
144 p++;
145 to_add++;
147 return p;
150 static int open_trunc_or_warn(const char *name)
152 int fd = open_trunc(name);
153 if (fd < 0)
154 bb_perror_msg("%s: warning: cannot open %s",
155 dir, name);
156 return fd;
159 static void update_status(struct svdir *s)
161 ssize_t sz;
162 int fd;
163 svstatus_t status;
165 /* pid */
166 if (pidchanged) {
167 fd = open_trunc_or_warn("supervise/pid.new");
168 if (fd < 0)
169 return;
170 if (s->pid) {
171 char spid[sizeof(int)*3 + 2];
172 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
173 write(fd, spid, size);
175 close(fd);
176 if (rename_or_warn("supervise/pid.new",
177 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
178 return;
179 pidchanged = 0;
182 /* stat */
183 fd = open_trunc_or_warn("supervise/stat.new");
184 if (fd < -1)
185 return;
188 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
189 char *p = stat_buf;
190 switch (s->state) {
191 case S_DOWN:
192 p = add_str(p, "down");
193 break;
194 case S_RUN:
195 p = add_str(p, "run");
196 break;
197 case S_FINISH:
198 p = add_str(p, "finish");
199 break;
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)
204 switch (s->want) {
205 case W_DOWN:
206 p = add_str(p, ", want down");
207 break;
208 case W_EXIT:
209 p = add_str(p, ", want exit");
210 break;
212 *p++ = '\n';
213 write(fd, stat_buf, p - stat_buf);
214 close(fd);
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)
226 status.paused = 1;
227 if (s->want == W_UP)
228 status.want = 'u';
229 else
230 status.want = 'd';
231 if (s->ctrl & C_TERM)
232 status.got_term = 1;
233 status.run_or_finish = s->state;
234 fd = open_trunc_or_warn("supervise/status.new");
235 if (fd < 0)
236 return;
237 sz = write(fd, &status, sizeof(status));
238 close(fd);
239 if (sz != sizeof(status)) {
240 warn_cannot("write supervise/status.new");
241 unlink("supervise/status.new");
242 return;
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)
250 pid_t pid;
251 int w;
252 char a[10];
253 struct stat st;
255 if (s->islog) return 0;
256 strcpy(a, "control/?");
257 a[8] = c; /* replace '?' */
258 if (stat(a, &st) == 0) {
259 if (st.st_mode & S_IXUSR) {
260 pid = vfork();
261 if (pid == -1) {
262 warn_cannot("vfork for control/?");
263 return 0;
265 if (!pid) {
266 /* child */
267 if (haslog && dup2(logpipe.wr, 1) == -1)
268 warn_cannot("setup stdout for control/?");
269 execl(a, a, (char *) NULL);
270 fatal_cannot("run control/?");
272 /* parent */
273 if (safe_waitpid(pid, &w, 0) == -1) {
274 warn_cannot("wait for child control/?");
275 return 0;
277 return !wait_exitcode(w);
279 } else {
280 if (errno != ENOENT)
281 warn_cannot("stat control/?");
283 return 0;
286 static void stopservice(struct svdir *s)
288 if (s->pid && !custom(s, 't')) {
289 kill(s->pid, SIGTERM);
290 s->ctrl |= C_TERM;
291 update_status(s);
293 if (s->want == W_DOWN) {
294 kill(s->pid, SIGCONT);
295 custom(s, 'd');
296 return;
298 if (s->want == W_EXIT) {
299 kill(s->pid, SIGCONT);
300 custom(s, 'x');
304 static void startservice(struct svdir *s)
306 int p;
307 const char *run;
309 if (s->state == S_FINISH)
310 run = "./finish";
311 else {
312 run = "./run";
313 custom(s, 'u');
316 if (s->pid != 0)
317 stopservice(s); /* should never happen */
318 while ((p = vfork()) == -1) {
319 warn_cannot("vfork, sleeping");
320 sleep(5);
322 if (p == 0) {
323 /* child */
324 if (haslog) {
325 /* NB: bug alert! right order is close, then dup2 */
326 if (s->islog) {
327 xchdir("./log");
328 close(logpipe.wr);
329 xdup2(logpipe.rd, 0);
330 } else {
331 close(logpipe.rd);
332 xdup2(logpipe.wr, 1);
335 /* Non-ignored signals revert to SIG_DFL on exec anyway */
336 /*bb_signals(0
337 + (1 << SIGCHLD)
338 + (1 << SIGTERM)
339 , SIG_DFL);*/
340 sig_unblock(SIGCHLD);
341 sig_unblock(SIGTERM);
342 execl(run, run, (char *) NULL);
343 fatal2_cannot(s->islog ? "start log/" : "start ", run);
345 /* parent */
346 if (s->state != S_FINISH) {
347 gettimeofday_ns(&s->start);
348 s->state = S_RUN;
350 s->pid = p;
351 pidchanged = 1;
352 s->ctrl = C_NOOP;
353 update_status(s);
356 static int ctrl(struct svdir *s, char c)
358 int sig;
360 switch (c) {
361 case 'd': /* down */
362 s->want = W_DOWN;
363 update_status(s);
364 if (s->pid && s->state != S_FINISH)
365 stopservice(s);
366 break;
367 case 'u': /* up */
368 s->want = W_UP;
369 update_status(s);
370 if (s->pid == 0)
371 startservice(s);
372 break;
373 case 'x': /* exit */
374 if (s->islog)
375 break;
376 s->want = W_EXIT;
377 update_status(s);
378 /* FALLTHROUGH */
379 case 't': /* sig term */
380 if (s->pid && s->state != S_FINISH)
381 stopservice(s);
382 break;
383 case 'k': /* sig kill */
384 if (s->pid && !custom(s, c))
385 kill(s->pid, SIGKILL);
386 s->state = S_DOWN;
387 break;
388 case 'p': /* sig pause */
389 if (s->pid && !custom(s, c))
390 kill(s->pid, SIGSTOP);
391 s->ctrl |= C_PAUSE;
392 update_status(s);
393 break;
394 case 'c': /* sig cont */
395 if (s->pid && !custom(s, c))
396 kill(s->pid, SIGCONT);
397 s->ctrl &= ~C_PAUSE;
398 update_status(s);
399 break;
400 case 'o': /* once */
401 s->want = W_DOWN;
402 update_status(s);
403 if (!s->pid)
404 startservice(s);
405 break;
406 case 'a': /* sig alarm */
407 sig = SIGALRM;
408 goto sendsig;
409 case 'h': /* sig hup */
410 sig = SIGHUP;
411 goto sendsig;
412 case 'i': /* sig int */
413 sig = SIGINT;
414 goto sendsig;
415 case 'q': /* sig quit */
416 sig = SIGQUIT;
417 goto sendsig;
418 case '1': /* sig usr1 */
419 sig = SIGUSR1;
420 goto sendsig;
421 case '2': /* sig usr2 */
422 sig = SIGUSR2;
423 goto sendsig;
425 return 1;
426 sendsig:
427 if (s->pid && !custom(s, c))
428 kill(s->pid, sig);
429 return 1;
432 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
433 int runsv_main(int argc UNUSED_PARAM, char **argv)
435 struct stat s;
436 int fd;
437 int r;
438 char buf[256];
440 INIT_G();
442 if (!argv[1] || argv[2])
443 bb_show_usage();
444 dir = argv[1];
446 xpiped_pair(selfpipe);
447 close_on_exec_on(selfpipe.rd);
448 close_on_exec_on(selfpipe.wr);
449 ndelay_on(selfpipe.rd);
450 ndelay_on(selfpipe.wr);
452 sig_block(SIGCHLD);
453 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
454 sig_block(SIGTERM);
455 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
457 xchdir(dir);
458 /* bss: svd[0].pid = 0; */
459 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
460 if (C_NOOP) svd[0].ctrl = C_NOOP;
461 if (W_UP) svd[0].want = W_UP;
462 /* bss: svd[0].islog = 0; */
463 /* bss: svd[1].pid = 0; */
464 gettimeofday_ns(&svd[0].start);
465 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
467 if (stat("log", &s) == -1) {
468 if (errno != ENOENT)
469 warn_cannot("stat ./log");
470 } else {
471 if (!S_ISDIR(s.st_mode)) {
472 errno = 0;
473 warn_cannot("stat log/down: log is not a directory");
474 } else {
475 haslog = 1;
476 svd[1].state = S_DOWN;
477 svd[1].ctrl = C_NOOP;
478 svd[1].want = W_UP;
479 svd[1].islog = 1;
480 gettimeofday_ns(&svd[1].start);
481 if (stat("log/down", &s) != -1)
482 svd[1].want = W_DOWN;
483 xpiped_pair(logpipe);
484 close_on_exec_on(logpipe.rd);
485 close_on_exec_on(logpipe.wr);
489 if (mkdir("supervise", 0700) == -1) {
490 r = readlink("supervise", buf, sizeof(buf));
491 if (r != -1) {
492 if (r == sizeof(buf))
493 fatal2x_cannot("readlink ./supervise", ": name too long");
494 buf[r] = 0;
495 mkdir(buf, 0700);
496 } else {
497 if ((errno != ENOENT) && (errno != EINVAL))
498 fatal_cannot("readlink ./supervise");
501 svd[0].fdlock = xopen3("log/supervise/lock"+4,
502 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
503 if (lock_exnb(svd[0].fdlock) == -1)
504 fatal_cannot("lock supervise/lock");
505 close_on_exec_on(svd[0].fdlock);
506 if (haslog) {
507 if (mkdir("log/supervise", 0700) == -1) {
508 r = readlink("log/supervise", buf, 256);
509 if (r != -1) {
510 if (r == 256)
511 fatal2x_cannot("readlink ./log/supervise", ": name too long");
512 buf[r] = 0;
513 fd = xopen(".", O_RDONLY|O_NDELAY);
514 xchdir("./log");
515 mkdir(buf, 0700);
516 if (fchdir(fd) == -1)
517 fatal_cannot("change back to service directory");
518 close(fd);
520 else {
521 if ((errno != ENOENT) && (errno != EINVAL))
522 fatal_cannot("readlink ./log/supervise");
525 svd[1].fdlock = xopen3("log/supervise/lock",
526 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
527 if (lock_ex(svd[1].fdlock) == -1)
528 fatal_cannot("lock log/supervise/lock");
529 close_on_exec_on(svd[1].fdlock);
532 mkfifo("log/supervise/control"+4, 0600);
533 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
534 close_on_exec_on(svd[0].fdcontrol);
535 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
536 close_on_exec_on(svd[0].fdcontrolwrite);
537 update_status(&svd[0]);
538 if (haslog) {
539 mkfifo("log/supervise/control", 0600);
540 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
541 close_on_exec_on(svd[1].fdcontrol);
542 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
543 close_on_exec_on(svd[1].fdcontrolwrite);
544 update_status(&svd[1]);
546 mkfifo("log/supervise/ok"+4, 0600);
547 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
548 close_on_exec_on(fd);
549 if (haslog) {
550 mkfifo("log/supervise/ok", 0600);
551 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
552 close_on_exec_on(fd);
554 for (;;) {
555 struct pollfd x[3];
556 unsigned deadline;
557 char ch;
559 if (haslog)
560 if (!svd[1].pid && svd[1].want == W_UP)
561 startservice(&svd[1]);
562 if (!svd[0].pid)
563 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
564 startservice(&svd[0]);
566 x[0].fd = selfpipe.rd;
567 x[0].events = POLLIN;
568 x[1].fd = svd[0].fdcontrol;
569 x[1].events = POLLIN;
570 /* x[2] is used only if haslog == 1 */
571 x[2].fd = svd[1].fdcontrol;
572 x[2].events = POLLIN;
573 sig_unblock(SIGTERM);
574 sig_unblock(SIGCHLD);
575 poll(x, 2 + haslog, 3600*1000);
576 sig_block(SIGTERM);
577 sig_block(SIGCHLD);
579 while (read(selfpipe.rd, &ch, 1) == 1)
580 continue;
582 for (;;) {
583 pid_t child;
584 int wstat;
586 child = wait_any_nohang(&wstat);
587 if (!child)
588 break;
589 if ((child == -1) && (errno != EINTR))
590 break;
591 if (child == svd[0].pid) {
592 svd[0].pid = 0;
593 pidchanged = 1;
594 svd[0].ctrl &=~ C_TERM;
595 if (svd[0].state != S_FINISH) {
596 fd = open_read("finish");
597 if (fd != -1) {
598 close(fd);
599 svd[0].state = S_FINISH;
600 update_status(&svd[0]);
601 continue;
604 svd[0].state = S_DOWN;
605 deadline = svd[0].start.tv_sec + 1;
606 gettimeofday_ns(&svd[0].start);
607 update_status(&svd[0]);
608 if (LESS(svd[0].start.tv_sec, deadline))
609 sleep(1);
611 if (haslog) {
612 if (child == svd[1].pid) {
613 svd[1].pid = 0;
614 pidchanged = 1;
615 svd[1].state = S_DOWN;
616 svd[1].ctrl &= ~C_TERM;
617 deadline = svd[1].start.tv_sec + 1;
618 gettimeofday_ns(&svd[1].start);
619 update_status(&svd[1]);
620 if (LESS(svd[1].start.tv_sec, deadline))
621 sleep(1);
624 } /* for (;;) */
625 if (read(svd[0].fdcontrol, &ch, 1) == 1)
626 ctrl(&svd[0], ch);
627 if (haslog)
628 if (read(svd[1].fdcontrol, &ch, 1) == 1)
629 ctrl(&svd[1], ch);
631 if (sigterm) {
632 ctrl(&svd[0], 'x');
633 sigterm = 0;
636 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
637 if (svd[1].pid == 0)
638 _exit(EXIT_SUCCESS);
639 if (svd[1].want != W_EXIT) {
640 svd[1].want = W_EXIT;
641 /* stopservice(&svd[1]); */
642 update_status(&svd[1]);
643 close(logpipe.wr);
644 close(logpipe.rd);
647 } /* for (;;) */
648 /* not reached */
649 return 0;