4155b8f43b552b2aa84d89e68ff9835bf225c6fa
[oweals/busybox.git] / runit / runsv.c
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
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;
264                         }
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/?");
271                         }
272                         /* parent */
273                         if (safe_waitpid(pid, &w, 0) == -1) {
274                                 warn_cannot("wait for child control/?");
275                                 return 0;
276                         }
277                         return !wait_exitcode(w);
278                 }
279         } else {
280                 if (errno != ENOENT)
281                         warn_cannot("stat control/?");
282         }
283         return 0;
284 }
285
286 static void stopservice(struct svdir *s)
287 {
288         if (s->pid && !custom(s, 't')) {
289                 kill(s->pid, SIGTERM);
290                 s->ctrl |= C_TERM;
291                 update_status(s);
292         }
293         if (s->want == W_DOWN) {
294                 kill(s->pid, SIGCONT);
295                 custom(s, 'd');
296                 return;
297         }
298         if (s->want == W_EXIT) {
299                 kill(s->pid, SIGCONT);
300                 custom(s, 'x');
301         }
302 }
303
304 static void startservice(struct svdir *s)
305 {
306         int p;
307         char *run[2];
308
309         if (s->state == S_FINISH)
310                 run[0] = (char*)"./finish";
311         else {
312                 run[0] = (char*)"./run";
313                 custom(s, 'u');
314         }
315         run[1] = NULL;
316
317         if (s->pid != 0)
318                 stopservice(s); /* should never happen */
319         while ((p = vfork()) == -1) {
320                 warn_cannot("vfork, sleeping");
321                 sleep(5);
322         }
323         if (p == 0) {
324                 /* child */
325                 if (haslog) {
326                         /* NB: bug alert! right order is close, then dup2 */
327                         if (s->islog) {
328                                 xchdir("./log");
329                                 close(logpipe.wr);
330                                 xdup2(logpipe.rd, 0);
331                         } else {
332                                 close(logpipe.rd);
333                                 xdup2(logpipe.wr, 1);
334                         }
335                 }
336                 /* Non-ignored signals revert to SIG_DFL on exec anyway */
337                 /*bb_signals(0
338                         + (1 << SIGCHLD)
339                         + (1 << SIGTERM)
340                         , SIG_DFL);*/
341                 sig_unblock(SIGCHLD);
342                 sig_unblock(SIGTERM);
343                 execvp(*run, run);
344                 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
345         }
346         /* parent */
347         if (s->state != S_FINISH) {
348                 gettimeofday_ns(&s->start);
349                 s->state = S_RUN;
350         }
351         s->pid = p;
352         pidchanged = 1;
353         s->ctrl = C_NOOP;
354         update_status(s);
355 }
356
357 static int ctrl(struct svdir *s, char c)
358 {
359         int sig;
360
361         switch (c) {
362         case 'd': /* down */
363                 s->want = W_DOWN;
364                 update_status(s);
365                 if (s->pid && s->state != S_FINISH)
366                         stopservice(s);
367                 break;
368         case 'u': /* up */
369                 s->want = W_UP;
370                 update_status(s);
371                 if (s->pid == 0)
372                         startservice(s);
373                 break;
374         case 'x': /* exit */
375                 if (s->islog)
376                         break;
377                 s->want = W_EXIT;
378                 update_status(s);
379                 /* FALLTHROUGH */
380         case 't': /* sig term */
381                 if (s->pid && s->state != S_FINISH)
382                         stopservice(s);
383                 break;
384         case 'k': /* sig kill */
385                 if (s->pid && !custom(s, c))
386                         kill(s->pid, SIGKILL);
387                 s->state = S_DOWN;
388                 break;
389         case 'p': /* sig pause */
390                 if (s->pid && !custom(s, c))
391                         kill(s->pid, SIGSTOP);
392                 s->ctrl |= C_PAUSE;
393                 update_status(s);
394                 break;
395         case 'c': /* sig cont */
396                 if (s->pid && !custom(s, c))
397                         kill(s->pid, SIGCONT);
398                 if (s->ctrl & C_PAUSE)
399                         s->ctrl &= ~C_PAUSE;
400                 update_status(s);
401                 break;
402         case 'o': /* once */
403                 s->want = W_DOWN;
404                 update_status(s);
405                 if (!s->pid)
406                         startservice(s);
407                 break;
408         case 'a': /* sig alarm */
409                 sig = SIGALRM;
410                 goto sendsig;
411         case 'h': /* sig hup */
412                 sig = SIGHUP;
413                 goto sendsig;
414         case 'i': /* sig int */
415                 sig = SIGINT;
416                 goto sendsig;
417         case 'q': /* sig quit */
418                 sig = SIGQUIT;
419                 goto sendsig;
420         case '1': /* sig usr1 */
421                 sig = SIGUSR1;
422                 goto sendsig;
423         case '2': /* sig usr2 */
424                 sig = SIGUSR2;
425                 goto sendsig;
426         }
427         return 1;
428  sendsig:
429         if (s->pid && !custom(s, c))
430                 kill(s->pid, sig);
431         return 1;
432 }
433
434 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
435 int runsv_main(int argc UNUSED_PARAM, char **argv)
436 {
437         struct stat s;
438         int fd;
439         int r;
440         char buf[256];
441
442         INIT_G();
443
444         if (!argv[1] || argv[2])
445                 bb_show_usage();
446         dir = argv[1];
447
448         xpiped_pair(selfpipe);
449         close_on_exec_on(selfpipe.rd);
450         close_on_exec_on(selfpipe.wr);
451         ndelay_on(selfpipe.rd);
452         ndelay_on(selfpipe.wr);
453
454         sig_block(SIGCHLD);
455         bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
456         sig_block(SIGTERM);
457         bb_signals_recursive_norestart(1 << SIGTERM, s_term);
458
459         xchdir(dir);
460         /* bss: svd[0].pid = 0; */
461         if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
462         if (C_NOOP) svd[0].ctrl = C_NOOP;
463         if (W_UP) svd[0].want = W_UP;
464         /* bss: svd[0].islog = 0; */
465         /* bss: svd[1].pid = 0; */
466         gettimeofday_ns(&svd[0].start);
467         if (stat("down", &s) != -1) svd[0].want = W_DOWN;
468
469         if (stat("log", &s) == -1) {
470                 if (errno != ENOENT)
471                         warn_cannot("stat ./log");
472         } else {
473                 if (!S_ISDIR(s.st_mode)) {
474                         errno = 0;
475                         warn_cannot("stat log/down: log is not a directory");
476                 } else {
477                         haslog = 1;
478                         svd[1].state = S_DOWN;
479                         svd[1].ctrl = C_NOOP;
480                         svd[1].want = W_UP;
481                         svd[1].islog = 1;
482                         gettimeofday_ns(&svd[1].start);
483                         if (stat("log/down", &s) != -1)
484                                 svd[1].want = W_DOWN;
485                         xpiped_pair(logpipe);
486                         close_on_exec_on(logpipe.rd);
487                         close_on_exec_on(logpipe.wr);
488                 }
489         }
490
491         if (mkdir("supervise", 0700) == -1) {
492                 r = readlink("supervise", buf, sizeof(buf));
493                 if (r != -1) {
494                         if (r == sizeof(buf))
495                                 fatal2x_cannot("readlink ./supervise", ": name too long");
496                         buf[r] = 0;
497                         mkdir(buf, 0700);
498                 } else {
499                         if ((errno != ENOENT) && (errno != EINVAL))
500                                 fatal_cannot("readlink ./supervise");
501                 }
502         }
503         svd[0].fdlock = xopen3("log/supervise/lock"+4,
504                         O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
505         if (lock_exnb(svd[0].fdlock) == -1)
506                 fatal_cannot("lock supervise/lock");
507         close_on_exec_on(svd[0].fdlock);
508         if (haslog) {
509                 if (mkdir("log/supervise", 0700) == -1) {
510                         r = readlink("log/supervise", buf, 256);
511                         if (r != -1) {
512                                 if (r == 256)
513                                         fatal2x_cannot("readlink ./log/supervise", ": name too long");
514                                 buf[r] = 0;
515                                 fd = xopen(".", O_RDONLY|O_NDELAY);
516                                 xchdir("./log");
517                                 mkdir(buf, 0700);
518                                 if (fchdir(fd) == -1)
519                                         fatal_cannot("change back to service directory");
520                                 close(fd);
521                         }
522                         else {
523                                 if ((errno != ENOENT) && (errno != EINVAL))
524                                         fatal_cannot("readlink ./log/supervise");
525                         }
526                 }
527                 svd[1].fdlock = xopen3("log/supervise/lock",
528                                 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
529                 if (lock_ex(svd[1].fdlock) == -1)
530                         fatal_cannot("lock log/supervise/lock");
531                 close_on_exec_on(svd[1].fdlock);
532         }
533
534         mkfifo("log/supervise/control"+4, 0600);
535         svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
536         close_on_exec_on(svd[0].fdcontrol);
537         svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
538         close_on_exec_on(svd[0].fdcontrolwrite);
539         update_status(&svd[0]);
540         if (haslog) {
541                 mkfifo("log/supervise/control", 0600);
542                 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
543                 close_on_exec_on(svd[1].fdcontrol);
544                 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
545                 close_on_exec_on(svd[1].fdcontrolwrite);
546                 update_status(&svd[1]);
547         }
548         mkfifo("log/supervise/ok"+4, 0600);
549         fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
550         close_on_exec_on(fd);
551         if (haslog) {
552                 mkfifo("log/supervise/ok", 0600);
553                 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
554                 close_on_exec_on(fd);
555         }
556         for (;;) {
557                 struct pollfd x[3];
558                 unsigned deadline;
559                 char ch;
560
561                 if (haslog)
562                         if (!svd[1].pid && svd[1].want == W_UP)
563                                 startservice(&svd[1]);
564                 if (!svd[0].pid)
565                         if (svd[0].want == W_UP || svd[0].state == S_FINISH)
566                                 startservice(&svd[0]);
567
568                 x[0].fd = selfpipe.rd;
569                 x[0].events = POLLIN;
570                 x[1].fd = svd[0].fdcontrol;
571                 x[1].events = POLLIN;
572                 /* x[2] is used only if haslog == 1 */
573                 x[2].fd = svd[1].fdcontrol;
574                 x[2].events = POLLIN;
575                 sig_unblock(SIGTERM);
576                 sig_unblock(SIGCHLD);
577                 poll(x, 2 + haslog, 3600*1000);
578                 sig_block(SIGTERM);
579                 sig_block(SIGCHLD);
580
581                 while (read(selfpipe.rd, &ch, 1) == 1)
582                         continue;
583
584                 for (;;) {
585                         pid_t child;
586                         int wstat;
587
588                         child = wait_any_nohang(&wstat);
589                         if (!child)
590                                 break;
591                         if ((child == -1) && (errno != EINTR))
592                                 break;
593                         if (child == svd[0].pid) {
594                                 svd[0].pid = 0;
595                                 pidchanged = 1;
596                                 svd[0].ctrl &=~ C_TERM;
597                                 if (svd[0].state != S_FINISH) {
598                                         fd = open_read("finish");
599                                         if (fd != -1) {
600                                                 close(fd);
601                                                 svd[0].state = S_FINISH;
602                                                 update_status(&svd[0]);
603                                                 continue;
604                                         }
605                                 }
606                                 svd[0].state = S_DOWN;
607                                 deadline = svd[0].start.tv_sec + 1;
608                                 gettimeofday_ns(&svd[0].start);
609                                 update_status(&svd[0]);
610                                 if (LESS(svd[0].start.tv_sec, deadline))
611                                         sleep(1);
612                         }
613                         if (haslog) {
614                                 if (child == svd[1].pid) {
615                                         svd[1].pid = 0;
616                                         pidchanged = 1;
617                                         svd[1].state = S_DOWN;
618                                         svd[1].ctrl &= ~C_TERM;
619                                         deadline = svd[1].start.tv_sec + 1;
620                                         gettimeofday_ns(&svd[1].start);
621                                         update_status(&svd[1]);
622                                         if (LESS(svd[1].start.tv_sec, deadline))
623                                                 sleep(1);
624                                 }
625                         }
626                 } /* for (;;) */
627                 if (read(svd[0].fdcontrol, &ch, 1) == 1)
628                         ctrl(&svd[0], ch);
629                 if (haslog)
630                         if (read(svd[1].fdcontrol, &ch, 1) == 1)
631                                 ctrl(&svd[1], ch);
632
633                 if (sigterm) {
634                         ctrl(&svd[0], 'x');
635                         sigterm = 0;
636                 }
637
638                 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
639                         if (svd[1].pid == 0)
640                                 _exit(EXIT_SUCCESS);
641                         if (svd[1].want != W_EXIT) {
642                                 svd[1].want = W_EXIT;
643                                 /* stopservice(&svd[1]); */
644                                 update_status(&svd[1]);
645                                 close(logpipe.wr);
646                                 close(logpipe.rd);
647                         }
648                 }
649         } /* for (;;) */
650         /* not reached */
651         return 0;
652 }