Initial public busybox upstream commit
[busybox4maemo] / 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() \
107         do { \
108                 pidchanged = 1; \
109         } while (0)
110
111 static 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 }
116 static void fatal_cannot(const char *m)
117 {
118         fatal2_cannot(m, "");
119         /* was exiting 111 */
120 }
121 static 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 }
126 static void warn_cannot(const char *m)
127 {
128         bb_perror_msg("%s: warning: cannot %s", dir, m);
129 }
130
131 static void s_child(int sig_no ATTRIBUTE_UNUSED)
132 {
133         write(selfpipe.wr, "", 1);
134 }
135
136 static void s_term(int sig_no ATTRIBUTE_UNUSED)
137 {
138         sigterm = 1;
139         write(selfpipe.wr, "", 1); /* XXX */
140 }
141
142 static char *add_str(char *p, const char *to_add)
143 {
144         while ((*p = *to_add) != '\0') {
145                 p++;
146                 to_add++;
147         }
148         return p;
149 }
150
151 static int open_trunc_or_warn(const char *name)
152 {
153         int fd = open_trunc(name);
154         if (fd < 0)
155                 bb_perror_msg("%s: warning: cannot open %s",
156                                 dir, name);
157         return fd;
158 }
159
160 static void update_status(struct svdir *s)
161 {
162         ssize_t sz;
163         int fd;
164         svstatus_t status;
165
166         /* pid */
167         if (pidchanged) {
168                 fd = open_trunc_or_warn("supervise/pid.new");
169                 if (fd < 0)
170                         return;
171                 if (s->pid) {
172                         char spid[sizeof(int)*3 + 2];
173                         int size = sprintf(spid, "%u\n", (unsigned)s->pid);
174                         write(fd, spid, size);
175                 }
176                 close(fd);
177                 if (rename_or_warn("supervise/pid.new",
178                     s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
179                         return;
180                 pidchanged = 0;
181         }
182
183         /* stat */
184         fd = open_trunc_or_warn("supervise/stat.new");
185         if (fd < -1)
186                 return;
187
188         {
189                 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
190                 char *p = stat_buf;
191                 switch (s->state) {
192                 case S_DOWN:
193                         p = add_str(p, "down");
194                         break;
195                 case S_RUN:
196                         p = add_str(p, "run");
197                         break;
198                 case S_FINISH:
199                         p = add_str(p, "finish");
200                         break;
201                 }
202                 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
203                 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
204                 if (s->state != S_DOWN)
205                         switch (s->want) {
206                         case W_DOWN:
207                                 p = add_str(p, ", want down");
208                                 break;
209                         case W_EXIT:
210                                 p = add_str(p, ", want exit");
211                                 break;
212                         }
213                 *p++ = '\n';
214                 write(fd, stat_buf, p - stat_buf);
215                 close(fd);
216         }
217
218         rename_or_warn("supervise/stat.new",
219                 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
220
221         /* supervise compatibility */
222         memset(&status, 0, sizeof(status));
223         status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
224         status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
225         status.pid_le32 = SWAP_LE32(s->pid);
226         if (s->ctrl & C_PAUSE)
227                 status.paused = 1;
228         if (s->want == W_UP)
229                 status.want = 'u';
230         else
231                 status.want = 'd';
232         if (s->ctrl & C_TERM)
233                 status.got_term = 1;
234         status.run_or_finish = s->state;
235         fd = open_trunc_or_warn("supervise/status.new");
236         if (fd < 0)
237                 return;
238         sz = write(fd, &status, sizeof(status));
239         close(fd);
240         if (sz != sizeof(status)) {
241                 warn_cannot("write supervise/status.new");
242                 unlink("supervise/status.new");
243                 return;
244         }
245         rename_or_warn("supervise/status.new",
246                 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
247 }
248
249 static unsigned custom(struct svdir *s, char c)
250 {
251         int pid;
252         int w;
253         char a[10];
254         struct stat st;
255         char *prog[2];
256
257         if (s->islog) return 0;
258         strcpy(a, "control/?");
259         a[8] = c; /* replace '?' */
260         if (stat(a, &st) == 0) {
261                 if (st.st_mode & S_IXUSR) {
262                         pid = vfork();
263                         if (pid == -1) {
264                                 warn_cannot("vfork for control/?");
265                                 return 0;
266                         }
267                         if (!pid) {
268                                 /* child */
269                                 if (haslog && dup2(logpipe.wr, 1) == -1)
270                                         warn_cannot("setup stdout for control/?");
271                                 prog[0] = a;
272                                 prog[1] = NULL;
273                                 execv(a, prog);
274                                 fatal_cannot("run control/?");
275                         }
276                         /* parent */
277                         while (safe_waitpid(pid, &w, 0) == -1) {
278                                 warn_cannot("wait for child control/?");
279                                 return 0;
280                         }
281                         return !wait_exitcode(w);
282                 }
283         } else {
284                 if (errno != ENOENT)
285                         warn_cannot("stat control/?");
286         }
287         return 0;
288 }
289
290 static void stopservice(struct svdir *s)
291 {
292         if (s->pid && !custom(s, 't')) {
293                 kill(s->pid, SIGTERM);
294                 s->ctrl |= C_TERM;
295                 update_status(s);
296         }
297         if (s->want == W_DOWN) {
298                 kill(s->pid, SIGCONT);
299                 custom(s, 'd');
300                 return;
301         }
302         if (s->want == W_EXIT) {
303                 kill(s->pid, SIGCONT);
304                 custom(s, 'x');
305         }
306 }
307
308 static void startservice(struct svdir *s)
309 {
310         int p;
311         char *run[2];
312
313         if (s->state == S_FINISH)
314                 run[0] = (char*)"./finish";
315         else {
316                 run[0] = (char*)"./run";
317                 custom(s, 'u');
318         }
319         run[1] = NULL;
320
321         if (s->pid != 0)
322                 stopservice(s); /* should never happen */
323         while ((p = vfork()) == -1) {
324                 warn_cannot("vfork, sleeping");
325                 sleep(5);
326         }
327         if (p == 0) {
328                 /* child */
329                 if (haslog) {
330                         /* NB: bug alert! right order is close, then dup2 */
331                         if (s->islog) {
332                                 xchdir("./log");
333                                 close(logpipe.wr);
334                                 xdup2(logpipe.rd, 0);
335                         } else {
336                                 close(logpipe.rd);
337                                 xdup2(logpipe.wr, 1);
338                         }
339                 }
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 ATTRIBUTE_UNUSED, 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                         int 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(0);
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 }