f70b51390f5c79fe893d154c55bfa21e6094d155
[oweals/busybox.git] / runit / runsv.c
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8    1. Redistributions of source code must retain the above copyright notice,
9       this list of conditions and the following disclaimer.
10    2. Redistributions in binary form must reproduce the above copyright
11       notice, this list of conditions and the following disclaimer in the
12       documentation and/or other materials provided with the distribution.
13    3. The name of the author may not be used to endorse or promote products
14       derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /* Busyboxed by 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 static int selfpipe[2];
37
38 /* state */
39 #define S_DOWN 0
40 #define S_RUN 1
41 #define S_FINISH 2
42 /* ctrl */
43 #define C_NOOP 0
44 #define C_TERM 1
45 #define C_PAUSE 2
46 /* want */
47 #define W_UP 0
48 #define W_DOWN 1
49 #define W_EXIT 2
50
51 struct svdir {
52         int pid;
53         smallint state;
54         smallint ctrl;
55         smallint want;
56         smallint islog;
57         struct taia start;
58         int fdlock;
59         int fdcontrol;
60         int fdcontrolwrite;
61 };
62 static struct svdir svd[2];
63
64 static smallint sigterm;
65 static smallint haslog;
66 static smallint pidchanged = 1;
67 static int logpipe[2];
68 static char *dir;
69
70 #define usage() bb_show_usage()
71
72 static void fatal2_cannot(const char *m1, const char *m2)
73 {
74         bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
75         /* was exiting 111 */
76 }
77 static void fatal_cannot(const char *m)
78 {
79         fatal2_cannot(m, "");
80         /* was exiting 111 */
81 }
82 static void fatal2x_cannot(const char *m1, const char *m2)
83 {
84         bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
85         /* was exiting 111 */
86 }
87 static void warn_cannot(const char *m)
88 {
89         bb_perror_msg("%s: warning: cannot %s", dir, m);
90 }
91 static void warnx_cannot(const char *m)
92 {
93         bb_error_msg("%s: warning: cannot %s", dir, m);
94 }
95
96 static void s_child(int sig_no)
97 {
98         write(selfpipe[1], "", 1);
99 }
100
101 static void s_term(int sig_no)
102 {
103         sigterm = 1;
104         write(selfpipe[1], "", 1); /* XXX */
105 }
106
107 static char *add_str(char *p, const char *to_add)
108 {
109         while ((*p = *to_add) != '\0') {
110                 p++;
111                 to_add++;
112         }
113         return p;
114 }
115
116 static int open_trunc_or_warn(const char *name)
117 {
118         int fd = open_trunc(name);
119         if (fd < 0)
120                 bb_perror_msg("%s: warning: cannot open %s",
121                                 dir, name);
122         return fd;
123 }
124
125 static int rename_or_warn(const char *old, const char *new)
126 {
127         if (rename(old, new) == -1) {
128                 bb_perror_msg("%s: warning: cannot rename %s to %s",
129                                 dir, old, new);
130                 return -1;
131         }
132         return 0;
133 }
134
135 static void update_status(struct svdir *s)
136 {
137         unsigned long l;
138         int fd;
139         char status[20];
140
141         /* pid */
142         if (pidchanged) {
143                 fd = open_trunc_or_warn("supervise/pid.new");
144                 if (fd < 0)
145                         return;
146                 if (s->pid) {
147                         char spid[sizeof(int)*3 + 2];
148                         int size = sprintf(spid, "%u\n", (unsigned)s->pid);
149                         write(fd, spid, size);
150                 }
151                 close(fd);
152                 if (rename_or_warn("supervise/pid.new",
153                     s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
154                         return;
155                 pidchanged = 0;
156         }
157
158         /* stat */
159         fd = open_trunc_or_warn("supervise/stat.new");
160         if (fd < -1)
161                 return;
162
163         {
164                 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
165                 char *p = stat_buf;
166                 switch (s->state) {
167                 case S_DOWN:
168                         p = add_str(p, "down");
169                         break;
170                 case S_RUN:
171                         p = add_str(p, "run");
172                         break;
173                 case S_FINISH:
174                         p = add_str(p, "finish");
175                         break;
176                 }
177                 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
178                 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
179                 if (s->state != S_DOWN)
180                         switch (s->want) {
181                         case W_DOWN:
182                                 p = add_str(p, ", want down");
183                                 break;
184                         case W_EXIT:
185                                 p = add_str(p, ", want exit");
186                                 break;
187                         }
188                 *p++ = '\n';
189                 write(fd, stat_buf, p - stat_buf);
190                 close(fd);
191         }
192
193         rename_or_warn("supervise/stat.new",
194                 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
195
196         /* supervise compatibility */
197         taia_pack(status, &s->start);
198         l = (unsigned long)s->pid;
199         status[12] = l; l >>=8;
200         status[13] = l; l >>=8;
201         status[14] = l; l >>=8;
202         status[15] = l;
203         if (s->ctrl & C_PAUSE)
204                 status[16] = 1;
205         else
206                 status[16] = 0;
207         if (s->want == W_UP)
208                 status[17] = 'u';
209         else
210                 status[17] = 'd';
211         if (s->ctrl & C_TERM)
212                 status[18] = 1;
213         else
214                 status[18] = 0;
215         status[19] = s->state;
216         fd = open_trunc_or_warn("supervise/status.new");
217         if (fd < 0)
218                 return;
219         l = write(fd, status, sizeof(status));
220         if (l < 0) {
221                 warn_cannot("write supervise/status.new");
222                 close(fd);
223                 unlink("supervise/status.new");
224                 return;
225         }
226         close(fd);
227         if (l < sizeof(status)) {
228                 warnx_cannot("write supervise/status.new: partial write");
229                 return;
230         }
231         rename_or_warn("supervise/status.new",
232                 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
233 }
234
235 static unsigned custom(struct svdir *s, char c)
236 {
237         int pid;
238         int w;
239         char a[10];
240         struct stat st;
241         char *prog[2];
242
243         if (s->islog) return 0;
244         strcpy(a, "control/?");
245         a[8] = c;
246         if (stat(a, &st) == 0) {
247                 if (st.st_mode & S_IXUSR) {
248                         pid = fork();
249                         if (pid == -1) {
250                                 warn_cannot("fork for control/?");
251                                 return 0;
252                         }
253                         if (!pid) {
254                                 if (haslog && dup2(logpipe[1], 1) == -1)
255                                         warn_cannot("setup stdout for control/?");
256                                 prog[0] = a;
257                                 prog[1] = NULL;
258                                 execve(a, prog, environ);
259                                 fatal_cannot("run control/?");
260                         }
261                         while (wait_pid(&w, pid) == -1) {
262                                 if (errno == EINTR) continue;
263                                 warn_cannot("wait for child control/?");
264                                 return 0;
265                         }
266                         return !wait_exitcode(w);
267                 }
268         } else {
269                 if (errno != ENOENT)
270                         warn_cannot("stat control/?");
271         }
272         return 0;
273 }
274
275 static void stopservice(struct svdir *s)
276 {
277         if (s->pid && !custom(s, 't')) {
278                 kill(s->pid, SIGTERM);
279                 s->ctrl |= C_TERM;
280                 update_status(s);
281         }
282         if (s->want == W_DOWN) {
283                 kill(s->pid, SIGCONT);
284                 custom(s, 'd');
285                 return;
286         }
287         if (s->want == W_EXIT) {
288                 kill(s->pid, SIGCONT);
289                 custom(s, 'x');
290         }
291 }
292
293 static void startservice(struct svdir *s)
294 {
295         int p;
296         char *run[2];
297
298         if (s->state == S_FINISH)
299                 run[0] = (char*)"./finish";
300         else {
301                 run[0] = (char*)"./run";
302                 custom(s, 'u');
303         }
304         run[1] = NULL;
305
306         if (s->pid != 0)
307                 stopservice(s); /* should never happen */
308         while ((p = fork()) == -1) {
309                 warn_cannot("fork, sleeping");
310                 sleep(5);
311         }
312         if (p == 0) {
313                 /* child */
314                 if (haslog) {
315                         if (s->islog) {
316                                 xdup2(logpipe[0], 0);
317                                 close(logpipe[1]);
318                                 xchdir("./log");
319                         } else {
320                                 xdup2(logpipe[1], 1);
321                                 close(logpipe[0]);
322                         }
323                 }
324                 signal(SIGCHLD, SIG_DFL);
325                 signal(SIGTERM, SIG_DFL);
326                 sig_unblock(SIGCHLD);
327                 sig_unblock(SIGTERM);
328                 execvp(*run, run);
329                 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
330         }
331         if (s->state != S_FINISH) {
332                 taia_now(&s->start);
333                 s->state = S_RUN;
334         }
335         s->pid = p;
336         pidchanged = 1;
337         s->ctrl = C_NOOP;
338         update_status(s);
339 }
340
341 static int ctrl(struct svdir *s, char c)
342 {
343         int sig;
344
345         switch (c) {
346         case 'd': /* down */
347                 s->want = W_DOWN;
348                 update_status(s);
349                 if (s->pid && s->state != S_FINISH) stopservice(s);
350                 break;
351         case 'u': /* up */
352                 s->want = W_UP;
353                 update_status(s);
354                 if (s->pid == 0) startservice(s);
355                 break;
356         case 'x': /* exit */
357                 if (s->islog) break;
358                 s->want = W_EXIT;
359                 update_status(s);
360                 /* FALLTHROUGH */
361         case 't': /* sig term */
362                 if (s->pid && s->state != S_FINISH) stopservice(s);
363                 break;
364         case 'k': /* sig kill */
365                 if (s->pid && !custom(s, c)) kill(s->pid, SIGKILL);
366                 s->state = S_DOWN;
367                 break;
368         case 'p': /* sig pause */
369                 if (s->pid && !custom(s, c)) kill(s->pid, SIGSTOP);
370                 s->ctrl |= C_PAUSE;
371                 update_status(s);
372                 break;
373         case 'c': /* sig cont */
374                 if (s->pid && !custom(s, c)) kill(s->pid, SIGCONT);
375                 if (s->ctrl & C_PAUSE) s->ctrl &= ~C_PAUSE;
376                 update_status(s);
377                 break;
378         case 'o': /* once */
379                 s->want = W_DOWN;
380                 update_status(s);
381                 if (!s->pid) startservice(s);
382                 break;
383         case 'a': /* sig alarm */
384                 sig = SIGALRM;
385                 goto sendsig;
386         case 'h': /* sig hup */
387                 sig = SIGHUP;
388                 goto sendsig;
389         case 'i': /* sig int */
390                 sig = SIGINT;
391                 goto sendsig;
392         case 'q': /* sig quit */
393                 sig = SIGQUIT;
394                 goto sendsig;
395         case '1': /* sig usr1 */
396                 sig = SIGUSR1;
397                 goto sendsig;
398         case '2': /* sig usr2 */
399                 sig = SIGUSR2;
400                 goto sendsig;
401         }
402         return 1;
403  sendsig:
404         if (s->pid && !custom(s, c))
405                 kill(s->pid, sig);
406         return 1;
407 }
408
409 int runsv_main(int argc, char **argv);
410 int runsv_main(int argc, char **argv)
411 {
412         struct stat s;
413         int fd;
414         int r;
415         char buf[256];
416
417         if (!argv[1] || argv[2]) usage();
418         dir = argv[1];
419
420         xpipe(selfpipe);
421         coe(selfpipe[0]);
422         coe(selfpipe[1]);
423         ndelay_on(selfpipe[0]);
424         ndelay_on(selfpipe[1]);
425
426         sig_block(SIGCHLD);
427         sig_catch(SIGCHLD, s_child);
428         sig_block(SIGTERM);
429         sig_catch(SIGTERM, s_term);
430
431         xchdir(dir);
432         /* bss: svd[0].pid = 0; */
433         if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
434         if (C_NOOP) svd[0].ctrl = C_NOOP;
435         if (W_UP) svd[0].want = W_UP;
436         /* bss: svd[0].islog = 0; */
437         /* bss: svd[1].pid = 0; */
438         taia_now(&svd[0].start);
439         if (stat("down", &s) != -1) svd[0].want = W_DOWN;
440
441         if (stat("log", &s) == -1) {
442                 if (errno != ENOENT)
443                         warn_cannot("stat ./log");
444         } else {
445                 if (!S_ISDIR(s.st_mode))
446                         warnx_cannot("stat log/down: log is not a directory");
447                 else {
448                         haslog = 1;
449                         svd[1].state = S_DOWN;
450                         svd[1].ctrl = C_NOOP;
451                         svd[1].want = W_UP;
452                         svd[1].islog = 1;
453                         taia_now(&svd[1].start);
454                         if (stat("log/down", &s) != -1)
455                                 svd[1].want = W_DOWN;
456                         xpipe(logpipe);
457                         coe(logpipe[0]);
458                         coe(logpipe[1]);
459                 }
460         }
461
462         if (mkdir("supervise", 0700) == -1) {
463                 r = readlink("supervise", buf, sizeof(buf));
464                 if (r != -1) {
465                         if (r == sizeof(buf))
466                                 fatal2x_cannot("readlink ./supervise", ": name too long");
467                         buf[r] = 0;
468                         mkdir(buf, 0700);
469                 } else {
470                         if ((errno != ENOENT) && (errno != EINVAL))
471                                 fatal_cannot("readlink ./supervise");
472                 }
473         }
474         svd[0].fdlock = xopen3("log/supervise/lock"+4,
475                         O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
476         if (lock_exnb(svd[0].fdlock) == -1)
477                 fatal_cannot("lock supervise/lock");
478         coe(svd[0].fdlock);
479         if (haslog) {
480                 if (mkdir("log/supervise", 0700) == -1) {
481                         r = readlink("log/supervise", buf, 256);
482                         if (r != -1) {
483                                 if (r == 256)
484                                         fatal2x_cannot("readlink ./log/supervise", ": name too long");
485                                 buf[r] = 0;
486                                 fd = xopen(".", O_RDONLY|O_NDELAY);
487                                 xchdir("./log");
488                                 mkdir(buf, 0700);
489                                 if (fchdir(fd) == -1)
490                                         fatal_cannot("change back to service directory");
491                                 close(fd);
492                         }
493                         else {
494                                 if ((errno != ENOENT) && (errno != EINVAL))
495                                         fatal_cannot("readlink ./log/supervise");
496                         }
497                 }
498                 svd[1].fdlock = xopen3("log/supervise/lock",
499                                 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
500                 if (lock_ex(svd[1].fdlock) == -1)
501                         fatal_cannot("lock log/supervise/lock");
502                 coe(svd[1].fdlock);
503         }
504
505         mkfifo("log/supervise/control"+4, 0600);
506         svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
507         coe(svd[0].fdcontrol);
508         svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
509         coe(svd[0].fdcontrolwrite);
510         update_status(&svd[0]);
511         if (haslog) {
512                 mkfifo("log/supervise/control", 0600);
513                 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
514                 coe(svd[1].fdcontrol);
515                 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
516                 coe(svd[1].fdcontrolwrite);
517                 update_status(&svd[1]);
518         }
519         mkfifo("log/supervise/ok"+4, 0600);
520         fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
521         coe(fd);
522         if (haslog) {
523                 mkfifo("log/supervise/ok", 0600);
524                 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
525                 coe(fd);
526         }
527         for (;;) {
528                 iopause_fd x[3];
529                 struct taia deadline;
530                 struct taia now;
531                 char ch;
532
533                 if (haslog)
534                         if (!svd[1].pid && svd[1].want == W_UP)
535                                 startservice(&svd[1]);
536                 if (!svd[0].pid)
537                         if (svd[0].want == W_UP || svd[0].state == S_FINISH)
538                                 startservice(&svd[0]);
539
540                 x[0].fd = selfpipe[0];
541                 x[0].events = IOPAUSE_READ;
542                 x[1].fd = svd[0].fdcontrol;
543                 x[1].events = IOPAUSE_READ;
544                 if (haslog) {
545                         x[2].fd = svd[1].fdcontrol;
546                         x[2].events = IOPAUSE_READ;
547                 }
548                 taia_now(&now);
549                 taia_uint(&deadline, 3600);
550                 taia_add(&deadline, &now, &deadline);
551
552                 sig_unblock(SIGTERM);
553                 sig_unblock(SIGCHLD);
554                 iopause(x, 2+haslog, &deadline, &now);
555                 sig_block(SIGTERM);
556                 sig_block(SIGCHLD);
557
558                 while (read(selfpipe[0], &ch, 1) == 1)
559                         ;
560                 for (;;) {
561                         int child;
562                         int wstat;
563
564                         child = wait_nohang(&wstat);
565                         if (!child) break;
566                         if ((child == -1) && (errno != EINTR)) break;
567                         if (child == svd[0].pid) {
568                                 svd[0].pid = 0;
569                                 pidchanged = 1;
570                                 svd[0].ctrl &=~ C_TERM;
571                                 if (svd[0].state != S_FINISH) {
572                                         fd = open_read("finish");
573                                         if (fd != -1) {
574                                                 close(fd);
575                                                 svd[0].state = S_FINISH;
576                                                 update_status(&svd[0]);
577                                                 continue;
578                                         }
579                                 }
580                                 svd[0].state = S_DOWN;
581                                 taia_uint(&deadline, 1);
582                                 taia_add(&deadline, &svd[0].start, &deadline);
583                                 taia_now(&svd[0].start);
584                                 update_status(&svd[0]);
585                                 if (taia_less(&svd[0].start, &deadline)) sleep(1);
586                         }
587                         if (haslog) {
588                                 if (child == svd[1].pid) {
589                                         svd[1].pid = 0;
590                                         pidchanged = 1;
591                                         svd[1].state = S_DOWN;
592                                         svd[1].ctrl &= ~C_TERM;
593                                         taia_uint(&deadline, 1);
594                                         taia_add(&deadline, &svd[1].start, &deadline);
595                                         taia_now(&svd[1].start);
596                                         update_status(&svd[1]);
597                                         if (taia_less(&svd[1].start, &deadline)) sleep(1);
598                                 }
599                         }
600                 }
601                 if (read(svd[0].fdcontrol, &ch, 1) == 1)
602                         ctrl(&svd[0], ch);
603                 if (haslog)
604                         if (read(svd[1].fdcontrol, &ch, 1) == 1)
605                                 ctrl(&svd[1], ch);
606
607                 if (sigterm) {
608                         ctrl(&svd[0], 'x');
609                         sigterm = 0;
610                 }
611
612                 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
613                         if (svd[1].pid == 0)
614                                 _exit(0);
615                         if (svd[1].want != W_EXIT) {
616                                 svd[1].want = W_EXIT;
617                                 /* stopservice(&svd[1]); */
618                                 update_status(&svd[1]);
619                                 close(logpipe[1]);
620                                 close(logpipe[0]);
621                                 //if (close(logpipe[1]) == -1)
622                                 //      warn_cannot("close logpipe[1]");
623                                 //if (close(logpipe[0]) == -1)
624                                 //      warn_cannot("close logpipe[0]");
625                         }
626                 }
627         }
628         /* not reached */
629         return 0;
630 }