stop using non-standard macro, use WEXITSTATUS instead
[platform/upstream/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)
256                 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                                 execl(a, a, (char *) NULL);
271                                 fatal_cannot("run control/?");
272                         }
273                         /* parent */
274                         if (safe_waitpid(pid, &w, 0) == -1) {
275                                 warn_cannot("wait for child control/?");
276                                 return 0;
277                         }
278                         return WEXITSTATUS(w) == 0;
279                 }
280         } else {
281                 if (errno != ENOENT)
282                         warn_cannot("stat control/?");
283         }
284         return 0;
285 }
286
287 static void stopservice(struct svdir *s)
288 {
289         if (s->pid && !custom(s, 't')) {
290                 kill(s->pid, SIGTERM);
291                 s->ctrl |= C_TERM;
292                 update_status(s);
293         }
294         if (s->want == W_DOWN) {
295                 kill(s->pid, SIGCONT);
296                 custom(s, 'd');
297                 return;
298         }
299         if (s->want == W_EXIT) {
300                 kill(s->pid, SIGCONT);
301                 custom(s, 'x');
302         }
303 }
304
305 static void startservice(struct svdir *s)
306 {
307         int p;
308         const char *run;
309
310         if (s->state == S_FINISH)
311                 run = "./finish";
312         else {
313                 run = "./run";
314                 custom(s, 'u');
315         }
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                 execl(run, run, (char *) NULL);
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                 s->ctrl &= ~C_PAUSE;
399                 update_status(s);
400                 break;
401         case 'o': /* once */
402                 s->want = W_DOWN;
403                 update_status(s);
404                 if (!s->pid)
405                         startservice(s);
406                 break;
407         case 'a': /* sig alarm */
408                 sig = SIGALRM;
409                 goto sendsig;
410         case 'h': /* sig hup */
411                 sig = SIGHUP;
412                 goto sendsig;
413         case 'i': /* sig int */
414                 sig = SIGINT;
415                 goto sendsig;
416         case 'q': /* sig quit */
417                 sig = SIGQUIT;
418                 goto sendsig;
419         case '1': /* sig usr1 */
420                 sig = SIGUSR1;
421                 goto sendsig;
422         case '2': /* sig usr2 */
423                 sig = SIGUSR2;
424                 goto sendsig;
425         }
426         return 1;
427  sendsig:
428         if (s->pid && !custom(s, c))
429                 kill(s->pid, sig);
430         return 1;
431 }
432
433 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
434 int runsv_main(int argc UNUSED_PARAM, char **argv)
435 {
436         struct stat s;
437         int fd;
438         int r;
439         char buf[256];
440
441         INIT_G();
442
443         if (!argv[1] || argv[2])
444                 bb_show_usage();
445         dir = argv[1];
446
447         xpiped_pair(selfpipe);
448         close_on_exec_on(selfpipe.rd);
449         close_on_exec_on(selfpipe.wr);
450         ndelay_on(selfpipe.rd);
451         ndelay_on(selfpipe.wr);
452
453         sig_block(SIGCHLD);
454         bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
455         sig_block(SIGTERM);
456         bb_signals_recursive_norestart(1 << SIGTERM, s_term);
457
458         xchdir(dir);
459         /* bss: svd[0].pid = 0; */
460         if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
461         if (C_NOOP) svd[0].ctrl = C_NOOP;
462         if (W_UP) svd[0].want = W_UP;
463         /* bss: svd[0].islog = 0; */
464         /* bss: svd[1].pid = 0; */
465         gettimeofday_ns(&svd[0].start);
466         if (stat("down", &s) != -1) svd[0].want = W_DOWN;
467
468         if (stat("log", &s) == -1) {
469                 if (errno != ENOENT)
470                         warn_cannot("stat ./log");
471         } else {
472                 if (!S_ISDIR(s.st_mode)) {
473                         errno = 0;
474                         warn_cannot("stat log/down: log is not a directory");
475                 } else {
476                         haslog = 1;
477                         svd[1].state = S_DOWN;
478                         svd[1].ctrl = C_NOOP;
479                         svd[1].want = W_UP;
480                         svd[1].islog = 1;
481                         gettimeofday_ns(&svd[1].start);
482                         if (stat("log/down", &s) != -1)
483                                 svd[1].want = W_DOWN;
484                         xpiped_pair(logpipe);
485                         close_on_exec_on(logpipe.rd);
486                         close_on_exec_on(logpipe.wr);
487                 }
488         }
489
490         if (mkdir("supervise", 0700) == -1) {
491                 r = readlink("supervise", buf, sizeof(buf));
492                 if (r != -1) {
493                         if (r == sizeof(buf))
494                                 fatal2x_cannot("readlink ./supervise", ": name too long");
495                         buf[r] = 0;
496                         mkdir(buf, 0700);
497                 } else {
498                         if ((errno != ENOENT) && (errno != EINVAL))
499                                 fatal_cannot("readlink ./supervise");
500                 }
501         }
502         svd[0].fdlock = xopen3("log/supervise/lock"+4,
503                         O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
504         if (lock_exnb(svd[0].fdlock) == -1)
505                 fatal_cannot("lock supervise/lock");
506         close_on_exec_on(svd[0].fdlock);
507         if (haslog) {
508                 if (mkdir("log/supervise", 0700) == -1) {
509                         r = readlink("log/supervise", buf, 256);
510                         if (r != -1) {
511                                 if (r == 256)
512                                         fatal2x_cannot("readlink ./log/supervise", ": name too long");
513                                 buf[r] = 0;
514                                 fd = xopen(".", O_RDONLY|O_NDELAY);
515                                 xchdir("./log");
516                                 mkdir(buf, 0700);
517                                 if (fchdir(fd) == -1)
518                                         fatal_cannot("change back to service directory");
519                                 close(fd);
520                         }
521                         else {
522                                 if ((errno != ENOENT) && (errno != EINVAL))
523                                         fatal_cannot("readlink ./log/supervise");
524                         }
525                 }
526                 svd[1].fdlock = xopen3("log/supervise/lock",
527                                 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
528                 if (lock_ex(svd[1].fdlock) == -1)
529                         fatal_cannot("lock log/supervise/lock");
530                 close_on_exec_on(svd[1].fdlock);
531         }
532
533         mkfifo("log/supervise/control"+4, 0600);
534         svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
535         close_on_exec_on(svd[0].fdcontrol);
536         svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
537         close_on_exec_on(svd[0].fdcontrolwrite);
538         update_status(&svd[0]);
539         if (haslog) {
540                 mkfifo("log/supervise/control", 0600);
541                 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
542                 close_on_exec_on(svd[1].fdcontrol);
543                 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
544                 close_on_exec_on(svd[1].fdcontrolwrite);
545                 update_status(&svd[1]);
546         }
547         mkfifo("log/supervise/ok"+4, 0600);
548         fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
549         close_on_exec_on(fd);
550         if (haslog) {
551                 mkfifo("log/supervise/ok", 0600);
552                 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
553                 close_on_exec_on(fd);
554         }
555         for (;;) {
556                 struct pollfd x[3];
557                 unsigned deadline;
558                 char ch;
559
560                 if (haslog)
561                         if (!svd[1].pid && svd[1].want == W_UP)
562                                 startservice(&svd[1]);
563                 if (!svd[0].pid)
564                         if (svd[0].want == W_UP || svd[0].state == S_FINISH)
565                                 startservice(&svd[0]);
566
567                 x[0].fd = selfpipe.rd;
568                 x[0].events = POLLIN;
569                 x[1].fd = svd[0].fdcontrol;
570                 x[1].events = POLLIN;
571                 /* x[2] is used only if haslog == 1 */
572                 x[2].fd = svd[1].fdcontrol;
573                 x[2].events = POLLIN;
574                 sig_unblock(SIGTERM);
575                 sig_unblock(SIGCHLD);
576                 poll(x, 2 + haslog, 3600*1000);
577                 sig_block(SIGTERM);
578                 sig_block(SIGCHLD);
579
580                 while (read(selfpipe.rd, &ch, 1) == 1)
581                         continue;
582
583                 for (;;) {
584                         pid_t child;
585                         int wstat;
586
587                         child = wait_any_nohang(&wstat);
588                         if (!child)
589                                 break;
590                         if ((child == -1) && (errno != EINTR))
591                                 break;
592                         if (child == svd[0].pid) {
593                                 svd[0].pid = 0;
594                                 pidchanged = 1;
595                                 svd[0].ctrl &=~ C_TERM;
596                                 if (svd[0].state != S_FINISH) {
597                                         fd = open_read("finish");
598                                         if (fd != -1) {
599                                                 close(fd);
600                                                 svd[0].state = S_FINISH;
601                                                 update_status(&svd[0]);
602                                                 continue;
603                                         }
604                                 }
605                                 svd[0].state = S_DOWN;
606                                 deadline = svd[0].start.tv_sec + 1;
607                                 gettimeofday_ns(&svd[0].start);
608                                 update_status(&svd[0]);
609                                 if (LESS(svd[0].start.tv_sec, deadline))
610                                         sleep(1);
611                         }
612                         if (haslog) {
613                                 if (child == svd[1].pid) {
614                                         svd[1].pid = 0;
615                                         pidchanged = 1;
616                                         svd[1].state = S_DOWN;
617                                         svd[1].ctrl &= ~C_TERM;
618                                         deadline = svd[1].start.tv_sec + 1;
619                                         gettimeofday_ns(&svd[1].start);
620                                         update_status(&svd[1]);
621                                         if (LESS(svd[1].start.tv_sec, deadline))
622                                                 sleep(1);
623                                 }
624                         }
625                 } /* for (;;) */
626                 if (read(svd[0].fdcontrol, &ch, 1) == 1)
627                         ctrl(&svd[0], ch);
628                 if (haslog)
629                         if (read(svd[1].fdcontrol, &ch, 1) == 1)
630                                 ctrl(&svd[1], ch);
631
632                 if (sigterm) {
633                         ctrl(&svd[0], 'x');
634                         sigterm = 0;
635                 }
636
637                 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
638                         if (svd[1].pid == 0)
639                                 _exit(EXIT_SUCCESS);
640                         if (svd[1].want != W_EXIT) {
641                                 svd[1].want = W_EXIT;
642                                 /* stopservice(&svd[1]); */
643                                 update_status(&svd[1]);
644                                 close(logpipe.wr);
645                                 close(logpipe.rd);
646                         }
647                 }
648         } /* for (;;) */
649         /* not reached */
650         return 0;
651 }