*: add -Wunused-parameter; fix resulting breakage
[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() \
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;
260         if (stat(a, &st) == 0) {
261                 if (st.st_mode & S_IXUSR) {
262                         pid = fork();
263                         if (pid == -1) {
264                                 warn_cannot("fork for control/?");
265                                 return 0;
266                         }
267                         if (!pid) {
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                         while (safe_waitpid(pid, &w, 0) == -1) {
276                                 warn_cannot("wait for child control/?");
277                                 return 0;
278                         }
279                         return !wait_exitcode(w);
280                 }
281         } else {
282                 if (errno != ENOENT)
283                         warn_cannot("stat control/?");
284         }
285         return 0;
286 }
287
288 static void stopservice(struct svdir *s)
289 {
290         if (s->pid && !custom(s, 't')) {
291                 kill(s->pid, SIGTERM);
292                 s->ctrl |= C_TERM;
293                 update_status(s);
294         }
295         if (s->want == W_DOWN) {
296                 kill(s->pid, SIGCONT);
297                 custom(s, 'd');
298                 return;
299         }
300         if (s->want == W_EXIT) {
301                 kill(s->pid, SIGCONT);
302                 custom(s, 'x');
303         }
304 }
305
306 static void startservice(struct svdir *s)
307 {
308         int p;
309         char *run[2];
310
311         if (s->state == S_FINISH)
312                 run[0] = (char*)"./finish";
313         else {
314                 run[0] = (char*)"./run";
315                 custom(s, 'u');
316         }
317         run[1] = NULL;
318
319         if (s->pid != 0)
320                 stopservice(s); /* should never happen */
321         while ((p = fork()) == -1) {
322                 warn_cannot("fork, sleeping");
323                 sleep(5);
324         }
325         if (p == 0) {
326                 /* child */
327                 if (haslog) {
328                         /* NB: bug alert! right order is close, then dup2 */
329                         if (s->islog) {
330                                 xchdir("./log");
331                                 close(logpipe.wr);
332                                 xdup2(logpipe.rd, 0);
333                         } else {
334                                 close(logpipe.rd);
335                                 xdup2(logpipe.wr, 1);
336                         }
337                 }
338                 bb_signals(0
339                         + (1 << SIGCHLD)
340                         + (1 << SIGTERM)
341                         , SIG_DFL);
342                 sig_unblock(SIGCHLD);
343                 sig_unblock(SIGTERM);
344                 execvp(*run, run);
345                 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
346         }
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 ATTRIBUTE_UNUSED, 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(1 << SIGCHLD, s_child);
456         sig_block(SIGTERM);
457         bb_signals_recursive(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                         int 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(0);
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 }