source: MondoRescue/branches/2.2.9/mindi-busybox/runit/runsv.c@ 2725

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