*: tidy up usage of char **environ
[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 Denis 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         int selfpipe[2];
94         int logpipe[2];
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)
132 {
133         write(selfpipe[1], "", 1);
134 }
135
136 static void s_term(int sig_no)
137 {
138         sigterm = 1;
139         write(selfpipe[1], "", 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 int rename_or_warn(const char *old, const char *new)
161 {
162         if (rename(old, new) == -1) {
163                 bb_perror_msg("%s: warning: cannot rename %s to %s",
164                                 dir, old, new);
165                 return -1;
166         }
167         return 0;
168 }
169
170 static void update_status(struct svdir *s)
171 {
172         ssize_t sz;
173         int fd;
174         svstatus_t status;
175
176         /* pid */
177         if (pidchanged) {
178                 fd = open_trunc_or_warn("supervise/pid.new");
179                 if (fd < 0)
180                         return;
181                 if (s->pid) {
182                         char spid[sizeof(int)*3 + 2];
183                         int size = sprintf(spid, "%u\n", (unsigned)s->pid);
184                         write(fd, spid, size);
185                 }
186                 close(fd);
187                 if (rename_or_warn("supervise/pid.new",
188                     s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
189                         return;
190                 pidchanged = 0;
191         }
192
193         /* stat */
194         fd = open_trunc_or_warn("supervise/stat.new");
195         if (fd < -1)
196                 return;
197
198         {
199                 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
200                 char *p = stat_buf;
201                 switch (s->state) {
202                 case S_DOWN:
203                         p = add_str(p, "down");
204                         break;
205                 case S_RUN:
206                         p = add_str(p, "run");
207                         break;
208                 case S_FINISH:
209                         p = add_str(p, "finish");
210                         break;
211                 }
212                 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
213                 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
214                 if (s->state != S_DOWN)
215                         switch (s->want) {
216                         case W_DOWN:
217                                 p = add_str(p, ", want down");
218                                 break;
219                         case W_EXIT:
220                                 p = add_str(p, ", want exit");
221                                 break;
222                         }
223                 *p++ = '\n';
224                 write(fd, stat_buf, p - stat_buf);
225                 close(fd);
226         }
227
228         rename_or_warn("supervise/stat.new",
229                 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
230
231         /* supervise compatibility */
232         memset(&status, 0, sizeof(status));
233         status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
234         status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
235         status.pid_le32 = SWAP_LE32(s->pid);
236         if (s->ctrl & C_PAUSE)
237                 status.paused = 1;
238         if (s->want == W_UP)
239                 status.want = 'u';
240         else
241                 status.want = 'd';
242         if (s->ctrl & C_TERM)
243                 status.got_term = 1;
244         status.run_or_finish = s->state;
245         fd = open_trunc_or_warn("supervise/status.new");
246         if (fd < 0)
247                 return;
248         sz = write(fd, &status, sizeof(status));
249         close(fd);
250         if (sz != sizeof(status)) {
251                 warn_cannot("write supervise/status.new");
252                 unlink("supervise/status.new");
253                 return;
254         }
255         rename_or_warn("supervise/status.new",
256                 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
257 }
258
259 static unsigned custom(struct svdir *s, char c)
260 {
261         int pid;
262         int w;
263         char a[10];
264         struct stat st;
265         char *prog[2];
266
267         if (s->islog) return 0;
268         strcpy(a, "control/?");
269         a[8] = c;
270         if (stat(a, &st) == 0) {
271                 if (st.st_mode & S_IXUSR) {
272                         pid = fork();
273                         if (pid == -1) {
274                                 warn_cannot("fork for control/?");
275                                 return 0;
276                         }
277                         if (!pid) {
278                                 if (haslog && dup2(logpipe[1], 1) == -1)
279                                         warn_cannot("setup stdout for control/?");
280                                 prog[0] = a;
281                                 prog[1] = NULL;
282                                 execv(a, prog);
283                                 fatal_cannot("run control/?");
284                         }
285                         while (safe_waitpid(pid, &w, 0) == -1) {
286                                 warn_cannot("wait for child control/?");
287                                 return 0;
288                         }
289                         return !wait_exitcode(w);
290                 }
291         } else {
292                 if (errno != ENOENT)
293                         warn_cannot("stat control/?");
294         }
295         return 0;
296 }
297
298 static void stopservice(struct svdir *s)
299 {
300         if (s->pid && !custom(s, 't')) {
301                 kill(s->pid, SIGTERM);
302                 s->ctrl |= C_TERM;
303                 update_status(s);
304         }
305         if (s->want == W_DOWN) {
306                 kill(s->pid, SIGCONT);
307                 custom(s, 'd');
308                 return;
309         }
310         if (s->want == W_EXIT) {
311                 kill(s->pid, SIGCONT);
312                 custom(s, 'x');
313         }
314 }
315
316 static void startservice(struct svdir *s)
317 {
318         int p;
319         char *run[2];
320
321         if (s->state == S_FINISH)
322                 run[0] = (char*)"./finish";
323         else {
324                 run[0] = (char*)"./run";
325                 custom(s, 'u');
326         }
327         run[1] = NULL;
328
329         if (s->pid != 0)
330                 stopservice(s); /* should never happen */
331         while ((p = fork()) == -1) {
332                 warn_cannot("fork, sleeping");
333                 sleep(5);
334         }
335         if (p == 0) {
336                 /* child */
337                 if (haslog) {
338                         if (s->islog) {
339                                 xdup2(logpipe[0], 0);
340                                 close(logpipe[1]);
341                                 xchdir("./log");
342                         } else {
343                                 xdup2(logpipe[1], 1);
344                                 close(logpipe[0]);
345                         }
346                 }
347                 signal(SIGCHLD, SIG_DFL);
348                 signal(SIGTERM, SIG_DFL);
349                 sig_unblock(SIGCHLD);
350                 sig_unblock(SIGTERM);
351                 execvp(*run, run);
352                 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
353         }
354         if (s->state != S_FINISH) {
355                 gettimeofday_ns(&s->start);
356                 s->state = S_RUN;
357         }
358         s->pid = p;
359         pidchanged = 1;
360         s->ctrl = C_NOOP;
361         update_status(s);
362 }
363
364 static int ctrl(struct svdir *s, char c)
365 {
366         int sig;
367
368         switch (c) {
369         case 'd': /* down */
370                 s->want = W_DOWN;
371                 update_status(s);
372                 if (s->pid && s->state != S_FINISH)
373                         stopservice(s);
374                 break;
375         case 'u': /* up */
376                 s->want = W_UP;
377                 update_status(s);
378                 if (s->pid == 0)
379                         startservice(s);
380                 break;
381         case 'x': /* exit */
382                 if (s->islog)
383                         break;
384                 s->want = W_EXIT;
385                 update_status(s);
386                 /* FALLTHROUGH */
387         case 't': /* sig term */
388                 if (s->pid && s->state != S_FINISH)
389                         stopservice(s);
390                 break;
391         case 'k': /* sig kill */
392                 if (s->pid && !custom(s, c))
393                         kill(s->pid, SIGKILL);
394                 s->state = S_DOWN;
395                 break;
396         case 'p': /* sig pause */
397                 if (s->pid && !custom(s, c))
398                         kill(s->pid, SIGSTOP);
399                 s->ctrl |= C_PAUSE;
400                 update_status(s);
401                 break;
402         case 'c': /* sig cont */
403                 if (s->pid && !custom(s, c))
404                         kill(s->pid, SIGCONT);
405                 if (s->ctrl & C_PAUSE)
406                         s->ctrl &= ~C_PAUSE;
407                 update_status(s);
408                 break;
409         case 'o': /* once */
410                 s->want = W_DOWN;
411                 update_status(s);
412                 if (!s->pid)
413                         startservice(s);
414                 break;
415         case 'a': /* sig alarm */
416                 sig = SIGALRM;
417                 goto sendsig;
418         case 'h': /* sig hup */
419                 sig = SIGHUP;
420                 goto sendsig;
421         case 'i': /* sig int */
422                 sig = SIGINT;
423                 goto sendsig;
424         case 'q': /* sig quit */
425                 sig = SIGQUIT;
426                 goto sendsig;
427         case '1': /* sig usr1 */
428                 sig = SIGUSR1;
429                 goto sendsig;
430         case '2': /* sig usr2 */
431                 sig = SIGUSR2;
432                 goto sendsig;
433         }
434         return 1;
435  sendsig:
436         if (s->pid && !custom(s, c))
437                 kill(s->pid, sig);
438         return 1;
439 }
440
441 int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
442 int runsv_main(int argc, char **argv)
443 {
444         struct stat s;
445         int fd;
446         int r;
447         char buf[256];
448
449         INIT_G();
450
451         if (!argv[1] || argv[2])
452                 bb_show_usage();
453         dir = argv[1];
454
455         xpipe(selfpipe);
456         close_on_exec_on(selfpipe[0]);
457         close_on_exec_on(selfpipe[1]);
458         ndelay_on(selfpipe[0]);
459         ndelay_on(selfpipe[1]);
460
461         sig_block(SIGCHLD);
462         sig_catch(SIGCHLD, s_child);
463         sig_block(SIGTERM);
464         sig_catch(SIGTERM, s_term);
465
466         xchdir(dir);
467         /* bss: svd[0].pid = 0; */
468         if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
469         if (C_NOOP) svd[0].ctrl = C_NOOP;
470         if (W_UP) svd[0].want = W_UP;
471         /* bss: svd[0].islog = 0; */
472         /* bss: svd[1].pid = 0; */
473         gettimeofday_ns(&svd[0].start);
474         if (stat("down", &s) != -1) svd[0].want = W_DOWN;
475
476         if (stat("log", &s) == -1) {
477                 if (errno != ENOENT)
478                         warn_cannot("stat ./log");
479         } else {
480                 if (!S_ISDIR(s.st_mode)) {
481                         errno = 0;
482                         warn_cannot("stat log/down: log is not a directory");
483                 } else {
484                         haslog = 1;
485                         svd[1].state = S_DOWN;
486                         svd[1].ctrl = C_NOOP;
487                         svd[1].want = W_UP;
488                         svd[1].islog = 1;
489                         gettimeofday_ns(&svd[1].start);
490                         if (stat("log/down", &s) != -1)
491                                 svd[1].want = W_DOWN;
492                         xpipe(logpipe);
493                         close_on_exec_on(logpipe[0]);
494                         close_on_exec_on(logpipe[1]);
495                 }
496         }
497
498         if (mkdir("supervise", 0700) == -1) {
499                 r = readlink("supervise", buf, sizeof(buf));
500                 if (r != -1) {
501                         if (r == sizeof(buf))
502                                 fatal2x_cannot("readlink ./supervise", ": name too long");
503                         buf[r] = 0;
504                         mkdir(buf, 0700);
505                 } else {
506                         if ((errno != ENOENT) && (errno != EINVAL))
507                                 fatal_cannot("readlink ./supervise");
508                 }
509         }
510         svd[0].fdlock = xopen3("log/supervise/lock"+4,
511                         O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
512         if (lock_exnb(svd[0].fdlock) == -1)
513                 fatal_cannot("lock supervise/lock");
514         close_on_exec_on(svd[0].fdlock);
515         if (haslog) {
516                 if (mkdir("log/supervise", 0700) == -1) {
517                         r = readlink("log/supervise", buf, 256);
518                         if (r != -1) {
519                                 if (r == 256)
520                                         fatal2x_cannot("readlink ./log/supervise", ": name too long");
521                                 buf[r] = 0;
522                                 fd = xopen(".", O_RDONLY|O_NDELAY);
523                                 xchdir("./log");
524                                 mkdir(buf, 0700);
525                                 if (fchdir(fd) == -1)
526                                         fatal_cannot("change back to service directory");
527                                 close(fd);
528                         }
529                         else {
530                                 if ((errno != ENOENT) && (errno != EINVAL))
531                                         fatal_cannot("readlink ./log/supervise");
532                         }
533                 }
534                 svd[1].fdlock = xopen3("log/supervise/lock",
535                                 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
536                 if (lock_ex(svd[1].fdlock) == -1)
537                         fatal_cannot("lock log/supervise/lock");
538                 close_on_exec_on(svd[1].fdlock);
539         }
540
541         mkfifo("log/supervise/control"+4, 0600);
542         svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
543         close_on_exec_on(svd[0].fdcontrol);
544         svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
545         close_on_exec_on(svd[0].fdcontrolwrite);
546         update_status(&svd[0]);
547         if (haslog) {
548                 mkfifo("log/supervise/control", 0600);
549                 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
550                 close_on_exec_on(svd[1].fdcontrol);
551                 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
552                 close_on_exec_on(svd[1].fdcontrolwrite);
553                 update_status(&svd[1]);
554         }
555         mkfifo("log/supervise/ok"+4, 0600);
556         fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
557         close_on_exec_on(fd);
558         if (haslog) {
559                 mkfifo("log/supervise/ok", 0600);
560                 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
561                 close_on_exec_on(fd);
562         }
563         for (;;) {
564                 struct pollfd x[3];
565                 unsigned deadline;
566                 char ch;
567
568                 if (haslog)
569                         if (!svd[1].pid && svd[1].want == W_UP)
570                                 startservice(&svd[1]);
571                 if (!svd[0].pid)
572                         if (svd[0].want == W_UP || svd[0].state == S_FINISH)
573                                 startservice(&svd[0]);
574
575                 x[0].fd = selfpipe[0];
576                 x[0].events = POLLIN;
577                 x[1].fd = svd[0].fdcontrol;
578                 x[1].events = POLLIN;
579                 /* x[2] is used only if haslog == 1 */
580                 x[2].fd = svd[1].fdcontrol;
581                 x[2].events = POLLIN;
582                 sig_unblock(SIGTERM);
583                 sig_unblock(SIGCHLD);
584                 poll(x, 2 + haslog, 3600*1000);
585                 sig_block(SIGTERM);
586                 sig_block(SIGCHLD);
587
588                 while (read(selfpipe[0], &ch, 1) == 1)
589                         continue;
590
591                 for (;;) {
592                         int child;
593                         int wstat;
594
595                         child = wait_any_nohang(&wstat);
596                         if (!child)
597                                 break;
598                         if ((child == -1) && (errno != EINTR))
599                                 break;
600                         if (child == svd[0].pid) {
601                                 svd[0].pid = 0;
602                                 pidchanged = 1;
603                                 svd[0].ctrl &=~ C_TERM;
604                                 if (svd[0].state != S_FINISH) {
605                                         fd = open_read("finish");
606                                         if (fd != -1) {
607                                                 close(fd);
608                                                 svd[0].state = S_FINISH;
609                                                 update_status(&svd[0]);
610                                                 continue;
611                                         }
612                                 }
613                                 svd[0].state = S_DOWN;
614                                 deadline = svd[0].start.tv_sec + 1;
615                                 gettimeofday_ns(&svd[0].start);
616                                 update_status(&svd[0]);
617                                 if (LESS(svd[0].start.tv_sec, deadline))
618                                         sleep(1);
619                         }
620                         if (haslog) {
621                                 if (child == svd[1].pid) {
622                                         svd[1].pid = 0;
623                                         pidchanged = 1;
624                                         svd[1].state = S_DOWN;
625                                         svd[1].ctrl &= ~C_TERM;
626                                         deadline = svd[1].start.tv_sec + 1;
627                                         gettimeofday_ns(&svd[1].start);
628                                         update_status(&svd[1]);
629                                         if (LESS(svd[1].start.tv_sec, deadline))
630                                                 sleep(1);
631                                 }
632                         }
633                 }
634                 if (read(svd[0].fdcontrol, &ch, 1) == 1)
635                         ctrl(&svd[0], ch);
636                 if (haslog)
637                         if (read(svd[1].fdcontrol, &ch, 1) == 1)
638                                 ctrl(&svd[1], ch);
639
640                 if (sigterm) {
641                         ctrl(&svd[0], 'x');
642                         sigterm = 0;
643                 }
644
645                 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
646                         if (svd[1].pid == 0)
647                                 _exit(0);
648                         if (svd[1].want != W_EXIT) {
649                                 svd[1].want = W_EXIT;
650                                 /* stopservice(&svd[1]); */
651                                 update_status(&svd[1]);
652                                 close(logpipe[1]);
653                                 close(logpipe[0]);
654                         }
655                 }
656         }
657         /* not reached */
658         return 0;
659 }