source: MondoRescue/branches/3.3/mindi-busybox/runit/runsv.c@ 3621

Last change on this file since 3621 was 3621, checked in by Bruno Cornec, 7 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 16.2 KB
Line 
1/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
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.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29
30//config:config RUNSV
31//config: bool "runsv"
32//config: default y
33//config: help
34//config: runsv starts and monitors a service and optionally an appendant log
35//config: service.
36
37//applet:IF_RUNSV(APPLET(runsv, BB_DIR_USR_BIN, BB_SUID_DROP))
38
39//kbuild:lib-$(CONFIG_RUNSV) += runsv.o
40
41//usage:#define runsv_trivial_usage
42//usage: "DIR"
43//usage:#define runsv_full_usage "\n\n"
44//usage: "Start and monitor a service and optionally an appendant log service"
45
46#include <sys/file.h>
47#include "libbb.h"
48#include "common_bufsiz.h"
49#include "runit_lib.h"
50
51#if ENABLE_MONOTONIC_SYSCALL
52#include <sys/syscall.h>
53
54/* libc has incredibly messy way of doing this,
55 * typically requiring -lrt. We just skip all this mess */
56static void gettimeofday_ns(struct timespec *ts)
57{
58 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
59}
60#else
61static void gettimeofday_ns(struct timespec *ts)
62{
63 BUILD_BUG_ON(sizeof(struct timeval) != sizeof(struct timespec));
64 BUILD_BUG_ON(sizeof(((struct timeval*)ts)->tv_usec) != sizeof(ts->tv_nsec));
65 /* Cheat */
66 gettimeofday((void*)ts, NULL);
67 ts->tv_nsec *= 1000;
68}
69#endif
70
71/* Compare possibly overflowing unsigned counters */
72#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
73
74/* state */
75#define S_DOWN 0
76#define S_RUN 1
77#define S_FINISH 2
78/* ctrl */
79#define C_NOOP 0
80#define C_TERM 1
81#define C_PAUSE 2
82/* want */
83#define W_UP 0
84#define W_DOWN 1
85#define W_EXIT 2
86
87struct svdir {
88 int pid;
89 smallint state;
90 smallint ctrl;
91 smallint sd_want;
92 smallint islog;
93 struct timespec start;
94 int fdlock;
95 int fdcontrol;
96 int fdcontrolwrite;
97 int wstat;
98};
99
100struct globals {
101 smallint haslog;
102 smallint sigterm;
103 smallint pidchanged;
104 struct fd_pair selfpipe;
105 struct fd_pair logpipe;
106 char *dir;
107 struct svdir svd[2];
108} FIX_ALIASING;
109#define G (*(struct globals*)bb_common_bufsiz1)
110#define haslog (G.haslog )
111#define sigterm (G.sigterm )
112#define pidchanged (G.pidchanged )
113#define selfpipe (G.selfpipe )
114#define logpipe (G.logpipe )
115#define dir (G.dir )
116#define svd (G.svd )
117#define INIT_G() do { \
118 setup_common_bufsiz(); \
119 pidchanged = 1; \
120} while (0)
121
122static void fatal2_cannot(const char *m1, const char *m2)
123{
124 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
125 /* was exiting 111 */
126}
127static void fatal_cannot(const char *m)
128{
129 fatal2_cannot(m, "");
130 /* was exiting 111 */
131}
132static void fatal2x_cannot(const char *m1, const char *m2)
133{
134 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
135 /* was exiting 111 */
136}
137static void warn_cannot(const char *m)
138{
139 bb_perror_msg("%s: warning: cannot %s", dir, m);
140}
141
142static void s_child(int sig_no UNUSED_PARAM)
143{
144 write(selfpipe.wr, "", 1);
145}
146
147static void s_term(int sig_no UNUSED_PARAM)
148{
149 sigterm = 1;
150 write(selfpipe.wr, "", 1); /* XXX */
151}
152
153static int open_trunc_or_warn(const char *name)
154{
155 /* Why O_NDELAY? */
156 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
157 if (fd < 0)
158 bb_perror_msg("%s: warning: cannot open %s",
159 dir, name);
160 return fd;
161}
162
163static void update_status(struct svdir *s)
164{
165 ssize_t sz;
166 int fd;
167 svstatus_t status;
168
169 /* pid */
170 if (pidchanged) {
171 fd = open_trunc_or_warn("supervise/pid.new");
172 if (fd < 0)
173 return;
174 if (s->pid) {
175 char spid[sizeof(int)*3 + 2];
176 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
177 write(fd, spid, size);
178 }
179 close(fd);
180 if (rename_or_warn("supervise/pid.new",
181 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
182 return;
183 pidchanged = 0;
184 }
185
186 /* stat */
187 fd = open_trunc_or_warn("supervise/stat.new");
188 if (fd < -1)
189 return;
190
191 {
192 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
193 char *p = stat_buf;
194 switch (s->state) {
195 case S_DOWN:
196 p = stpcpy(p, "down");
197 break;
198 case S_RUN:
199 p = stpcpy(p, "run");
200 break;
201 case S_FINISH:
202 p = stpcpy(p, "finish");
203 break;
204 }
205 if (s->ctrl & C_PAUSE)
206 p = stpcpy(p, ", paused");
207 if (s->ctrl & C_TERM)
208 p = stpcpy(p, ", got TERM");
209 if (s->state != S_DOWN)
210 switch (s->sd_want) {
211 case W_DOWN:
212 p = stpcpy(p, ", want down");
213 break;
214 case W_EXIT:
215 p = stpcpy(p, ", want exit");
216 break;
217 }
218 *p++ = '\n';
219 write(fd, stat_buf, p - stat_buf);
220 close(fd);
221 }
222
223 rename_or_warn("supervise/stat.new",
224 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
225
226 /* supervise compatibility */
227 memset(&status, 0, sizeof(status));
228 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
229 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
230 status.pid_le32 = SWAP_LE32(s->pid);
231 if (s->ctrl & C_PAUSE)
232 status.paused = 1;
233 if (s->sd_want == W_UP)
234 status.want = 'u';
235 else
236 status.want = 'd';
237 if (s->ctrl & C_TERM)
238 status.got_term = 1;
239 status.run_or_finish = s->state;
240 fd = open_trunc_or_warn("supervise/status.new");
241 if (fd < 0)
242 return;
243 sz = write(fd, &status, sizeof(status));
244 close(fd);
245 if (sz != sizeof(status)) {
246 warn_cannot("write supervise/status.new");
247 unlink("supervise/status.new");
248 return;
249 }
250 rename_or_warn("supervise/status.new",
251 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
252}
253
254static unsigned custom(struct svdir *s, char c)
255{
256 pid_t pid;
257 int w;
258 char a[10];
259 struct stat st;
260
261 if (s->islog)
262 return 0;
263 strcpy(a, "control/?");
264 a[8] = c; /* replace '?' */
265 if (stat(a, &st) == 0) {
266 if (st.st_mode & S_IXUSR) {
267 pid = vfork();
268 if (pid == -1) {
269 warn_cannot("vfork for control/?");
270 return 0;
271 }
272 if (pid == 0) {
273 /* child */
274 if (haslog && dup2(logpipe.wr, 1) == -1)
275 warn_cannot("setup stdout for control/?");
276 execl(a, a, (char *) NULL);
277 fatal_cannot("run control/?");
278 }
279 /* parent */
280 if (safe_waitpid(pid, &w, 0) == -1) {
281 warn_cannot("wait for child control/?");
282 return 0;
283 }
284 return WEXITSTATUS(w) == 0;
285 }
286 } else {
287 if (errno != ENOENT)
288 warn_cannot("stat control/?");
289 }
290 return 0;
291}
292
293static void stopservice(struct svdir *s)
294{
295 if (s->pid && !custom(s, 't')) {
296 kill(s->pid, SIGTERM);
297 s->ctrl |= C_TERM;
298 update_status(s);
299 }
300 if (s->sd_want == W_DOWN) {
301 kill(s->pid, SIGCONT);
302 custom(s, 'd');
303 return;
304 }
305 if (s->sd_want == W_EXIT) {
306 kill(s->pid, SIGCONT);
307 custom(s, 'x');
308 }
309}
310
311static void startservice(struct svdir *s)
312{
313 int p;
314 const char *arg[4];
315 char exitcode[sizeof(int)*3 + 2];
316
317 if (s->state == S_FINISH) {
318/* Two arguments are given to ./finish. The first one is ./run exit code,
319 * or -1 if ./run didnt exit normally. The second one is
320 * the least significant byte of the exit status as determined by waitpid;
321 * for instance it is 0 if ./run exited normally, and the signal number
322 * if ./run was terminated by a signal. If runsv cannot start ./run
323 * for some reason, the exit code is 111 and the status is 0.
324 */
325 arg[0] = "./finish";
326 arg[1] = "-1";
327 if (WIFEXITED(s->wstat)) {
328 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
329 arg[1] = exitcode;
330 }
331 //arg[2] = "0";
332 //if (WIFSIGNALED(s->wstat)) {
333 arg[2] = utoa(WTERMSIG(s->wstat));
334 //}
335 arg[3] = NULL;
336 } else {
337 arg[0] = "./run";
338 arg[1] = NULL;
339 custom(s, 'u');
340 }
341
342 if (s->pid != 0)
343 stopservice(s); /* should never happen */
344 while ((p = vfork()) == -1) {
345 warn_cannot("vfork, sleeping");
346 sleep(5);
347 }
348 if (p == 0) {
349 /* child */
350 if (haslog) {
351 /* NB: bug alert! right order is close, then dup2 */
352 if (s->islog) {
353 xchdir("./log");
354 close(logpipe.wr);
355 xdup2(logpipe.rd, 0);
356 } else {
357 close(logpipe.rd);
358 xdup2(logpipe.wr, 1);
359 }
360 }
361 /* Non-ignored signals revert to SIG_DFL on exec anyway */
362 /*bb_signals(0
363 + (1 << SIGCHLD)
364 + (1 << SIGTERM)
365 , SIG_DFL);*/
366 sig_unblock(SIGCHLD);
367 sig_unblock(SIGTERM);
368 execv(arg[0], (char**) arg);
369 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
370 }
371 /* parent */
372 if (s->state != S_FINISH) {
373 gettimeofday_ns(&s->start);
374 s->state = S_RUN;
375 }
376 s->pid = p;
377 pidchanged = 1;
378 s->ctrl = C_NOOP;
379 update_status(s);
380}
381
382static int ctrl(struct svdir *s, char c)
383{
384 int sig;
385
386 switch (c) {
387 case 'd': /* down */
388 s->sd_want = W_DOWN;
389 update_status(s);
390 if (s->pid && s->state != S_FINISH)
391 stopservice(s);
392 break;
393 case 'u': /* up */
394 s->sd_want = W_UP;
395 update_status(s);
396 if (s->pid == 0)
397 startservice(s);
398 break;
399 case 'x': /* exit */
400 if (s->islog)
401 break;
402 s->sd_want = W_EXIT;
403 update_status(s);
404 /* FALLTHROUGH */
405 case 't': /* sig term */
406 if (s->pid && s->state != S_FINISH)
407 stopservice(s);
408 break;
409 case 'k': /* sig kill */
410 if (s->pid && !custom(s, c))
411 kill(s->pid, SIGKILL);
412 s->state = S_DOWN;
413 break;
414 case 'p': /* sig pause */
415 if (s->pid && !custom(s, c))
416 kill(s->pid, SIGSTOP);
417 s->ctrl |= C_PAUSE;
418 update_status(s);
419 break;
420 case 'c': /* sig cont */
421 if (s->pid && !custom(s, c))
422 kill(s->pid, SIGCONT);
423 s->ctrl &= ~C_PAUSE;
424 update_status(s);
425 break;
426 case 'o': /* once */
427 s->sd_want = W_DOWN;
428 update_status(s);
429 if (!s->pid)
430 startservice(s);
431 break;
432 case 'a': /* sig alarm */
433 sig = SIGALRM;
434 goto sendsig;
435 case 'h': /* sig hup */
436 sig = SIGHUP;
437 goto sendsig;
438 case 'i': /* sig int */
439 sig = SIGINT;
440 goto sendsig;
441 case 'q': /* sig quit */
442 sig = SIGQUIT;
443 goto sendsig;
444 case '1': /* sig usr1 */
445 sig = SIGUSR1;
446 goto sendsig;
447 case '2': /* sig usr2 */
448 sig = SIGUSR2;
449 goto sendsig;
450 }
451 return 1;
452 sendsig:
453 if (s->pid && !custom(s, c))
454 kill(s->pid, sig);
455 return 1;
456}
457
458int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
459int runsv_main(int argc UNUSED_PARAM, char **argv)
460{
461 struct stat s;
462 int fd;
463 int r;
464 char buf[256];
465
466 INIT_G();
467
468 dir = single_argv(argv);
469
470 xpiped_pair(selfpipe);
471 close_on_exec_on(selfpipe.rd);
472 close_on_exec_on(selfpipe.wr);
473 ndelay_on(selfpipe.rd);
474 ndelay_on(selfpipe.wr);
475
476 sig_block(SIGCHLD);
477 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
478 sig_block(SIGTERM);
479 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
480
481 xchdir(dir);
482 /* bss: svd[0].pid = 0; */
483 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
484 if (C_NOOP) svd[0].ctrl = C_NOOP;
485 if (W_UP) svd[0].sd_want = W_UP;
486 /* bss: svd[0].islog = 0; */
487 /* bss: svd[1].pid = 0; */
488 gettimeofday_ns(&svd[0].start);
489 if (stat("down", &s) != -1)
490 svd[0].sd_want = W_DOWN;
491
492 if (stat("log", &s) == -1) {
493 if (errno != ENOENT)
494 warn_cannot("stat ./log");
495 } else {
496 if (!S_ISDIR(s.st_mode)) {
497 errno = 0;
498 warn_cannot("stat log/down: log is not a directory");
499 } else {
500 haslog = 1;
501 svd[1].state = S_DOWN;
502 svd[1].ctrl = C_NOOP;
503 svd[1].sd_want = W_UP;
504 svd[1].islog = 1;
505 gettimeofday_ns(&svd[1].start);
506 if (stat("log/down", &s) != -1)
507 svd[1].sd_want = W_DOWN;
508 xpiped_pair(logpipe);
509 close_on_exec_on(logpipe.rd);
510 close_on_exec_on(logpipe.wr);
511 }
512 }
513
514 if (mkdir("supervise", 0700) == -1) {
515 r = readlink("supervise", buf, sizeof(buf));
516 if (r != -1) {
517 if (r == sizeof(buf))
518 fatal2x_cannot("readlink ./supervise", ": name too long");
519 buf[r] = 0;
520 mkdir(buf, 0700);
521 } else {
522 if ((errno != ENOENT) && (errno != EINVAL))
523 fatal_cannot("readlink ./supervise");
524 }
525 }
526 svd[0].fdlock = xopen3("log/supervise/lock"+4,
527 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
528 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
529 fatal_cannot("lock supervise/lock");
530 close_on_exec_on(svd[0].fdlock);
531 if (haslog) {
532 if (mkdir("log/supervise", 0700) == -1) {
533 r = readlink("log/supervise", buf, 256);
534 if (r != -1) {
535 if (r == 256)
536 fatal2x_cannot("readlink ./log/supervise", ": name too long");
537 buf[r] = 0;
538 fd = xopen(".", O_RDONLY|O_NDELAY);
539 xchdir("./log");
540 mkdir(buf, 0700);
541 if (fchdir(fd) == -1)
542 fatal_cannot("change back to service directory");
543 close(fd);
544 }
545 else {
546 if ((errno != ENOENT) && (errno != EINVAL))
547 fatal_cannot("readlink ./log/supervise");
548 }
549 }
550 svd[1].fdlock = xopen3("log/supervise/lock",
551 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
552 if (flock(svd[1].fdlock, LOCK_EX) == -1)
553 fatal_cannot("lock log/supervise/lock");
554 close_on_exec_on(svd[1].fdlock);
555 }
556
557 mkfifo("log/supervise/control"+4, 0600);
558 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
559 close_on_exec_on(svd[0].fdcontrol);
560 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
561 close_on_exec_on(svd[0].fdcontrolwrite);
562 update_status(&svd[0]);
563 if (haslog) {
564 mkfifo("log/supervise/control", 0600);
565 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
566 close_on_exec_on(svd[1].fdcontrol);
567 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
568 close_on_exec_on(svd[1].fdcontrolwrite);
569 update_status(&svd[1]);
570 }
571 mkfifo("log/supervise/ok"+4, 0600);
572 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
573 close_on_exec_on(fd);
574 if (haslog) {
575 mkfifo("log/supervise/ok", 0600);
576 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
577 close_on_exec_on(fd);
578 }
579 for (;;) {
580 struct pollfd x[3];
581 unsigned deadline;
582 char ch;
583
584 if (haslog)
585 if (!svd[1].pid && svd[1].sd_want == W_UP)
586 startservice(&svd[1]);
587 if (!svd[0].pid)
588 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
589 startservice(&svd[0]);
590
591 x[0].fd = selfpipe.rd;
592 x[0].events = POLLIN;
593 x[1].fd = svd[0].fdcontrol;
594 x[1].events = POLLIN;
595 /* x[2] is used only if haslog == 1 */
596 x[2].fd = svd[1].fdcontrol;
597 x[2].events = POLLIN;
598 sig_unblock(SIGTERM);
599 sig_unblock(SIGCHLD);
600 poll(x, 2 + haslog, 3600*1000);
601 sig_block(SIGTERM);
602 sig_block(SIGCHLD);
603
604 while (read(selfpipe.rd, &ch, 1) == 1)
605 continue;
606
607 for (;;) {
608 pid_t child;
609 int wstat;
610
611 child = wait_any_nohang(&wstat);
612 if (!child)
613 break;
614 if ((child == -1) && (errno != EINTR))
615 break;
616 if (child == svd[0].pid) {
617 svd[0].wstat = wstat;
618 svd[0].pid = 0;
619 pidchanged = 1;
620 svd[0].ctrl &= ~C_TERM;
621 if (svd[0].state != S_FINISH) {
622 fd = open("finish", O_RDONLY|O_NDELAY);
623 if (fd != -1) {
624 close(fd);
625 svd[0].state = S_FINISH;
626 update_status(&svd[0]);
627 continue;
628 }
629 }
630 svd[0].state = S_DOWN;
631 deadline = svd[0].start.tv_sec + 1;
632 gettimeofday_ns(&svd[0].start);
633 update_status(&svd[0]);
634 if (LESS(svd[0].start.tv_sec, deadline))
635 sleep(1);
636 }
637 if (haslog) {
638 if (child == svd[1].pid) {
639 svd[0].wstat = wstat;
640 svd[1].pid = 0;
641 pidchanged = 1;
642 svd[1].state = S_DOWN;
643 svd[1].ctrl &= ~C_TERM;
644 deadline = svd[1].start.tv_sec + 1;
645 gettimeofday_ns(&svd[1].start);
646 update_status(&svd[1]);
647 if (LESS(svd[1].start.tv_sec, deadline))
648 sleep(1);
649 }
650 }
651 } /* for (;;) */
652 if (read(svd[0].fdcontrol, &ch, 1) == 1)
653 ctrl(&svd[0], ch);
654 if (haslog)
655 if (read(svd[1].fdcontrol, &ch, 1) == 1)
656 ctrl(&svd[1], ch);
657
658 if (sigterm) {
659 ctrl(&svd[0], 'x');
660 sigterm = 0;
661 }
662
663 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
664 if (svd[1].pid == 0)
665 _exit(EXIT_SUCCESS);
666 if (svd[1].sd_want != W_EXIT) {
667 svd[1].sd_want = W_EXIT;
668 /* stopservice(&svd[1]); */
669 update_status(&svd[1]);
670 close(logpipe.wr);
671 close(logpipe.rd);
672 }
673 }
674 } /* for (;;) */
675 /* not reached */
676 return 0;
677}
Note: See TracBrowser for help on using the repository browser.