Magellan Linux

Contents of /tags/mkinitrd-6_1_5/busybox/runit/runsv.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 899 - (show annotations) (download)
Wed Aug 5 17:52:52 2009 UTC (14 years, 10 months ago) by niro
File MIME type: text/plain
File size: 15551 byte(s)
tagged 'mkinitrd-6_1_5'
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, 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
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.
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 */
41 static void gettimeofday_ns(struct timespec *ts)
42 {
43 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
44 }
45 #else
46 static 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
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;
87 };
88
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];
97 };
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)
109
110 static void fatal2_cannot(const char *m1, const char *m2)
111 {
112 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
113 /* was exiting 111 */
114 }
115 static void fatal_cannot(const char *m)
116 {
117 fatal2_cannot(m, "");
118 /* was exiting 111 */
119 }
120 static void fatal2x_cannot(const char *m1, const char *m2)
121 {
122 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
123 /* was exiting 111 */
124 }
125 static void warn_cannot(const char *m)
126 {
127 bb_perror_msg("%s: warning: cannot %s", dir, m);
128 }
129
130 static void s_child(int sig_no UNUSED_PARAM)
131 {
132 write(selfpipe.wr, "", 1);
133 }
134
135 static void s_term(int sig_no UNUSED_PARAM)
136 {
137 sigterm = 1;
138 write(selfpipe.wr, "", 1); /* XXX */
139 }
140
141 static char *add_str(char *p, const char *to_add)
142 {
143 while ((*p = *to_add) != '\0') {
144 p++;
145 to_add++;
146 }
147 return p;
148 }
149
150 static int open_trunc_or_warn(const char *name)
151 {
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;
157 }
158
159 static void update_status(struct svdir *s)
160 {
161 ssize_t sz;
162 int fd;
163 svstatus_t status;
164
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);
174 }
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;
180 }
181
182 /* stat */
183 fd = open_trunc_or_warn("supervise/stat.new");
184 if (fd < -1)
185 return;
186
187 {
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;
200 }
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;
211 }
212 *p++ = '\n';
213 write(fd, stat_buf, p - stat_buf);
214 close(fd);
215 }
216
217 rename_or_warn("supervise/stat.new",
218 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
219
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;
243 }
244 rename_or_warn("supervise/status.new",
245 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
246 }
247
248 static unsigned custom(struct svdir *s, char c)
249 {
250 pid_t pid;
251 int w;
252 char a[10];
253 struct stat st;
254 char *prog[2];
255
256 if (s->islog) return 0;
257 strcpy(a, "control/?");
258 a[8] = c; /* replace '?' */
259 if (stat(a, &st) == 0) {
260 if (st.st_mode & S_IXUSR) {
261 pid = vfork();
262 if (pid == -1) {
263 warn_cannot("vfork for control/?");
264 return 0;
265 }
266 if (!pid) {
267 /* child */
268 if (haslog && dup2(logpipe.wr, 1) == -1)
269 warn_cannot("setup stdout for control/?");
270 prog[0] = a;
271 prog[1] = NULL;
272 execv(a, prog);
273 fatal_cannot("run control/?");
274 }
275 /* parent */
276 while (safe_waitpid(pid, &w, 0) == -1) {
277 warn_cannot("wait for child control/?");
278 return 0;
279 }
280 return !wait_exitcode(w);
281 }
282 } else {
283 if (errno != ENOENT)
284 warn_cannot("stat control/?");
285 }
286 return 0;
287 }
288
289 static void stopservice(struct svdir *s)
290 {
291 if (s->pid && !custom(s, 't')) {
292 kill(s->pid, SIGTERM);
293 s->ctrl |= C_TERM;
294 update_status(s);
295 }
296 if (s->want == W_DOWN) {
297 kill(s->pid, SIGCONT);
298 custom(s, 'd');
299 return;
300 }
301 if (s->want == W_EXIT) {
302 kill(s->pid, SIGCONT);
303 custom(s, 'x');
304 }
305 }
306
307 static void startservice(struct svdir *s)
308 {
309 int p;
310 char *run[2];
311
312 if (s->state == S_FINISH)
313 run[0] = (char*)"./finish";
314 else {
315 run[0] = (char*)"./run";
316 custom(s, 'u');
317 }
318 run[1] = NULL;
319
320 if (s->pid != 0)
321 stopservice(s); /* should never happen */
322 while ((p = vfork()) == -1) {
323 warn_cannot("vfork, sleeping");
324 sleep(5);
325 }
326 if (p == 0) {
327 /* child */
328 if (haslog) {
329 /* NB: bug alert! right order is close, then dup2 */
330 if (s->islog) {
331 xchdir("./log");
332 close(logpipe.wr);
333 xdup2(logpipe.rd, 0);
334 } else {
335 close(logpipe.rd);
336 xdup2(logpipe.wr, 1);
337 }
338 }
339 /* Non-ignored signals revert to SIG_DFL on exec anyway */
340 /*bb_signals(0
341 + (1 << SIGCHLD)
342 + (1 << SIGTERM)
343 , SIG_DFL);*/
344 sig_unblock(SIGCHLD);
345 sig_unblock(SIGTERM);
346 execvp(*run, run);
347 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
348 }
349 /* parent */
350 if (s->state != S_FINISH) {
351 gettimeofday_ns(&s->start);
352 s->state = S_RUN;
353 }
354 s->pid = p;
355 pidchanged = 1;
356 s->ctrl = C_NOOP;
357 update_status(s);
358 }
359
360 static int ctrl(struct svdir *s, char c)
361 {
362 int sig;
363
364 switch (c) {
365 case 'd': /* down */
366 s->want = W_DOWN;
367 update_status(s);
368 if (s->pid && s->state != S_FINISH)
369 stopservice(s);
370 break;
371 case 'u': /* up */
372 s->want = W_UP;
373 update_status(s);
374 if (s->pid == 0)
375 startservice(s);
376 break;
377 case 'x': /* exit */
378 if (s->islog)
379 break;
380 s->want = W_EXIT;
381 update_status(s);
382 /* FALLTHROUGH */
383 case 't': /* sig term */
384 if (s->pid && s->state != S_FINISH)
385 stopservice(s);
386 break;
387 case 'k': /* sig kill */
388 if (s->pid && !custom(s, c))
389 kill(s->pid, SIGKILL);
390 s->state = S_DOWN;
391 break;
392 case 'p': /* sig pause */
393 if (s->pid && !custom(s, c))
394 kill(s->pid, SIGSTOP);
395 s->ctrl |= C_PAUSE;
396 update_status(s);
397 break;
398 case 'c': /* sig cont */
399 if (s->pid && !custom(s, c))
400 kill(s->pid, SIGCONT);
401 if (s->ctrl & C_PAUSE)
402 s->ctrl &= ~C_PAUSE;
403 update_status(s);
404 break;
405 case 'o': /* once */
406 s->want = W_DOWN;
407 update_status(s);
408 if (!s->pid)
409 startservice(s);
410 break;
411 case 'a': /* sig alarm */
412 sig = SIGALRM;
413 goto sendsig;
414 case 'h': /* sig hup */
415 sig = SIGHUP;
416 goto sendsig;
417 case 'i': /* sig int */
418 sig = SIGINT;
419 goto sendsig;
420 case 'q': /* sig quit */
421 sig = SIGQUIT;
422 goto sendsig;
423 case '1': /* sig usr1 */
424 sig = SIGUSR1;
425 goto sendsig;
426 case '2': /* sig usr2 */
427 sig = SIGUSR2;
428 goto sendsig;
429 }
430 return 1;
431 sendsig:
432 if (s->pid && !custom(s, c))
433 kill(s->pid, sig);
434 return 1;
435 }
436
437 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
438 int runsv_main(int argc UNUSED_PARAM, char **argv)
439 {
440 struct stat s;
441 int fd;
442 int r;
443 char buf[256];
444
445 INIT_G();
446
447 if (!argv[1] || argv[2])
448 bb_show_usage();
449 dir = argv[1];
450
451 xpiped_pair(selfpipe);
452 close_on_exec_on(selfpipe.rd);
453 close_on_exec_on(selfpipe.wr);
454 ndelay_on(selfpipe.rd);
455 ndelay_on(selfpipe.wr);
456
457 sig_block(SIGCHLD);
458 bb_signals_recursive(1 << SIGCHLD, s_child);
459 sig_block(SIGTERM);
460 bb_signals_recursive(1 << SIGTERM, s_term);
461
462 xchdir(dir);
463 /* bss: svd[0].pid = 0; */
464 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
465 if (C_NOOP) svd[0].ctrl = C_NOOP;
466 if (W_UP) svd[0].want = W_UP;
467 /* bss: svd[0].islog = 0; */
468 /* bss: svd[1].pid = 0; */
469 gettimeofday_ns(&svd[0].start);
470 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
471
472 if (stat("log", &s) == -1) {
473 if (errno != ENOENT)
474 warn_cannot("stat ./log");
475 } else {
476 if (!S_ISDIR(s.st_mode)) {
477 errno = 0;
478 warn_cannot("stat log/down: log is not a directory");
479 } else {
480 haslog = 1;
481 svd[1].state = S_DOWN;
482 svd[1].ctrl = C_NOOP;
483 svd[1].want = W_UP;
484 svd[1].islog = 1;
485 gettimeofday_ns(&svd[1].start);
486 if (stat("log/down", &s) != -1)
487 svd[1].want = W_DOWN;
488 xpiped_pair(logpipe);
489 close_on_exec_on(logpipe.rd);
490 close_on_exec_on(logpipe.wr);
491 }
492 }
493
494 if (mkdir("supervise", 0700) == -1) {
495 r = readlink("supervise", buf, sizeof(buf));
496 if (r != -1) {
497 if (r == sizeof(buf))
498 fatal2x_cannot("readlink ./supervise", ": name too long");
499 buf[r] = 0;
500 mkdir(buf, 0700);
501 } else {
502 if ((errno != ENOENT) && (errno != EINVAL))
503 fatal_cannot("readlink ./supervise");
504 }
505 }
506 svd[0].fdlock = xopen3("log/supervise/lock"+4,
507 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
508 if (lock_exnb(svd[0].fdlock) == -1)
509 fatal_cannot("lock supervise/lock");
510 close_on_exec_on(svd[0].fdlock);
511 if (haslog) {
512 if (mkdir("log/supervise", 0700) == -1) {
513 r = readlink("log/supervise", buf, 256);
514 if (r != -1) {
515 if (r == 256)
516 fatal2x_cannot("readlink ./log/supervise", ": name too long");
517 buf[r] = 0;
518 fd = xopen(".", O_RDONLY|O_NDELAY);
519 xchdir("./log");
520 mkdir(buf, 0700);
521 if (fchdir(fd) == -1)
522 fatal_cannot("change back to service directory");
523 close(fd);
524 }
525 else {
526 if ((errno != ENOENT) && (errno != EINVAL))
527 fatal_cannot("readlink ./log/supervise");
528 }
529 }
530 svd[1].fdlock = xopen3("log/supervise/lock",
531 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
532 if (lock_ex(svd[1].fdlock) == -1)
533 fatal_cannot("lock log/supervise/lock");
534 close_on_exec_on(svd[1].fdlock);
535 }
536
537 mkfifo("log/supervise/control"+4, 0600);
538 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
539 close_on_exec_on(svd[0].fdcontrol);
540 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
541 close_on_exec_on(svd[0].fdcontrolwrite);
542 update_status(&svd[0]);
543 if (haslog) {
544 mkfifo("log/supervise/control", 0600);
545 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
546 close_on_exec_on(svd[1].fdcontrol);
547 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
548 close_on_exec_on(svd[1].fdcontrolwrite);
549 update_status(&svd[1]);
550 }
551 mkfifo("log/supervise/ok"+4, 0600);
552 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
553 close_on_exec_on(fd);
554 if (haslog) {
555 mkfifo("log/supervise/ok", 0600);
556 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
557 close_on_exec_on(fd);
558 }
559 for (;;) {
560 struct pollfd x[3];
561 unsigned deadline;
562 char ch;
563
564 if (haslog)
565 if (!svd[1].pid && svd[1].want == W_UP)
566 startservice(&svd[1]);
567 if (!svd[0].pid)
568 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
569 startservice(&svd[0]);
570
571 x[0].fd = selfpipe.rd;
572 x[0].events = POLLIN;
573 x[1].fd = svd[0].fdcontrol;
574 x[1].events = POLLIN;
575 /* x[2] is used only if haslog == 1 */
576 x[2].fd = svd[1].fdcontrol;
577 x[2].events = POLLIN;
578 sig_unblock(SIGTERM);
579 sig_unblock(SIGCHLD);
580 poll(x, 2 + haslog, 3600*1000);
581 sig_block(SIGTERM);
582 sig_block(SIGCHLD);
583
584 while (read(selfpipe.rd, &ch, 1) == 1)
585 continue;
586
587 for (;;) {
588 pid_t child;
589 int wstat;
590
591 child = wait_any_nohang(&wstat);
592 if (!child)
593 break;
594 if ((child == -1) && (errno != EINTR))
595 break;
596 if (child == svd[0].pid) {
597 svd[0].pid = 0;
598 pidchanged = 1;
599 svd[0].ctrl &=~ C_TERM;
600 if (svd[0].state != S_FINISH) {
601 fd = open_read("finish");
602 if (fd != -1) {
603 close(fd);
604 svd[0].state = S_FINISH;
605 update_status(&svd[0]);
606 continue;
607 }
608 }
609 svd[0].state = S_DOWN;
610 deadline = svd[0].start.tv_sec + 1;
611 gettimeofday_ns(&svd[0].start);
612 update_status(&svd[0]);
613 if (LESS(svd[0].start.tv_sec, deadline))
614 sleep(1);
615 }
616 if (haslog) {
617 if (child == svd[1].pid) {
618 svd[1].pid = 0;
619 pidchanged = 1;
620 svd[1].state = S_DOWN;
621 svd[1].ctrl &= ~C_TERM;
622 deadline = svd[1].start.tv_sec + 1;
623 gettimeofday_ns(&svd[1].start);
624 update_status(&svd[1]);
625 if (LESS(svd[1].start.tv_sec, deadline))
626 sleep(1);
627 }
628 }
629 } /* for (;;) */
630 if (read(svd[0].fdcontrol, &ch, 1) == 1)
631 ctrl(&svd[0], ch);
632 if (haslog)
633 if (read(svd[1].fdcontrol, &ch, 1) == 1)
634 ctrl(&svd[1], ch);
635
636 if (sigterm) {
637 ctrl(&svd[0], 'x');
638 sigterm = 0;
639 }
640
641 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
642 if (svd[1].pid == 0)
643 _exit(EXIT_SUCCESS);
644 if (svd[1].want != W_EXIT) {
645 svd[1].want = W_EXIT;
646 /* stopservice(&svd[1]); */
647 update_status(&svd[1]);
648 close(logpipe.wr);
649 close(logpipe.rd);
650 }
651 }
652 } /* for (;;) */
653 /* not reached */
654 return 0;
655 }