1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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
55
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
66 gettimeofday((void*)ts, NULL);
67 ts->tv_nsec *= 1000;
68}
69#endif
70
71
72#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
73
74
75#define S_DOWN 0
76#define S_RUN 1
77#define S_FINISH 2
78
79#define C_NOOP 0
80#define C_TERM 1
81#define C_PAUSE 2
82
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
126}
127static void fatal_cannot(const char *m)
128{
129 fatal2_cannot(m, "");
130
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
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);
151}
152
153static int open_trunc_or_warn(const char *name)
154{
155
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
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
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
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;
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
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
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
319
320
321
322
323
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
332
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);
344 while ((p = vfork()) == -1) {
345 warn_cannot("vfork, sleeping");
346 sleep(5);
347 }
348 if (p == 0) {
349
350 if (haslog) {
351
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
362
363
364
365
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
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':
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':
394 s->sd_want = W_UP;
395 update_status(s);
396 if (s->pid == 0)
397 startservice(s);
398 break;
399 case 'x':
400 if (s->islog)
401 break;
402 s->sd_want = W_EXIT;
403 update_status(s);
404
405 case 't':
406 if (s->pid && s->state != S_FINISH)
407 stopservice(s);
408 break;
409 case 'k':
410 if (s->pid && !custom(s, c))
411 kill(s->pid, SIGKILL);
412 s->state = S_DOWN;
413 break;
414 case 'p':
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':
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':
427 s->sd_want = W_DOWN;
428 update_status(s);
429 if (!s->pid)
430 startservice(s);
431 break;
432 case 'a':
433 sig = SIGALRM;
434 goto sendsig;
435 case 'h':
436 sig = SIGHUP;
437 goto sendsig;
438 case 'i':
439 sig = SIGINT;
440 goto sendsig;
441 case 'q':
442 sig = SIGQUIT;
443 goto sendsig;
444 case '1':
445 sig = SIGUSR1;
446 goto sendsig;
447 case '2':
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
483 if (S_DOWN) svd[0].state = S_DOWN;
484 if (C_NOOP) svd[0].ctrl = C_NOOP;
485 if (W_UP) svd[0].sd_want = W_UP;
486
487
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
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 }
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
669 update_status(&svd[1]);
670 close(logpipe.wr);
671 close(logpipe.rd);
672 }
673 }
674 }
675
676 return 0;
677}
678