blob: 6cf5bcc29ba573d188206c2125c92a4dbe94a4c9 [file] [log] [blame]
Denis Vlasenko8a164052007-03-12 23:34:52 +00001/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
Denis Vlasenkod18f52b2008-03-02 12:53:15 +000028/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denis Vlasenko04c63862006-11-17 18:58:49 +000029/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
Pere Orga5bc8c002011-04-11 03:29:49 +020031//usage:#define runsv_trivial_usage
32//usage: "DIR"
33//usage:#define runsv_full_usage "\n\n"
34//usage: "Start and monitor a service and optionally an appendant log service"
35
Denis Vlasenko04c63862006-11-17 18:58:49 +000036#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000037#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +000038#include "runit_lib.h"
39
Denis Vlasenko7bc53602007-08-31 21:45:52 +000040#if ENABLE_MONOTONIC_SYSCALL
41#include <sys/syscall.h>
42
43/* libc has incredibly messy way of doing this,
44 * typically requiring -lrt. We just skip all this mess */
45static void gettimeofday_ns(struct timespec *ts)
46{
47 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
48}
49#else
50static void gettimeofday_ns(struct timespec *ts)
51{
Denys Vlasenkoab3964d2015-10-13 14:50:20 +020052 BUILD_BUG_ON(sizeof(struct timeval) != sizeof(struct timespec));
53 BUILD_BUG_ON(sizeof(((struct timeval*)ts)->tv_usec) != sizeof(ts->tv_nsec));
54 /* Cheat */
55 gettimeofday((void*)ts, NULL);
56 ts->tv_nsec *= 1000;
Denis Vlasenko7bc53602007-08-31 21:45:52 +000057}
58#endif
59
60/* Compare possibly overflowing unsigned counters */
61#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
62
Denis Vlasenko04c63862006-11-17 18:58:49 +000063/* state */
64#define S_DOWN 0
65#define S_RUN 1
66#define S_FINISH 2
67/* ctrl */
68#define C_NOOP 0
69#define C_TERM 1
70#define C_PAUSE 2
71/* want */
72#define W_UP 0
73#define W_DOWN 1
74#define W_EXIT 2
75
76struct svdir {
77 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000078 smallint state;
79 smallint ctrl;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +020080 smallint sd_want;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000081 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000082 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +000083 int fdlock;
84 int fdcontrol;
85 int fdcontrolwrite;
Earl Chew80a34182009-08-02 02:23:27 +020086 int wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +000087};
Denis Vlasenko04c63862006-11-17 18:58:49 +000088
Denis Vlasenkob9256052007-09-28 10:29:17 +000089struct globals {
90 smallint haslog;
91 smallint sigterm;
92 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +000093 struct fd_pair selfpipe;
94 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +000095 char *dir;
96 struct svdir svd[2];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +010097} FIX_ALIASING;
Denis Vlasenkob9256052007-09-28 10:29:17 +000098#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 )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000106#define INIT_G() do { \
107 pidchanged = 1; \
108} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000109
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000110static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000111{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200112 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000113 /* was exiting 111 */
114}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000115static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000116{
117 fatal2_cannot(m, "");
118 /* was exiting 111 */
119}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000120static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000121{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200122 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000123 /* was exiting 111 */
124}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000125static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000126{
127 bb_perror_msg("%s: warning: cannot %s", dir, m);
128}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000129
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000130static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000131{
Denis Vlasenko37188322008-02-16 13:20:56 +0000132 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000133}
134
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000135static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000136{
137 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000138 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000139}
140
Denis Vlasenko04c63862006-11-17 18:58:49 +0000141static int open_trunc_or_warn(const char *name)
142{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200143 /* Why O_NDELAY? */
144 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000145 if (fd < 0)
146 bb_perror_msg("%s: warning: cannot open %s",
147 dir, name);
148 return fd;
149}
150
Denis Vlasenko04c63862006-11-17 18:58:49 +0000151static void update_status(struct svdir *s)
152{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000153 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000154 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000155 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000156
157 /* pid */
158 if (pidchanged) {
159 fd = open_trunc_or_warn("supervise/pid.new");
160 if (fd < 0)
161 return;
162 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000163 char spid[sizeof(int)*3 + 2];
164 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000165 write(fd, spid, size);
166 }
167 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000168 if (rename_or_warn("supervise/pid.new",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100169 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000170 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000171 pidchanged = 0;
172 }
173
174 /* stat */
175 fd = open_trunc_or_warn("supervise/stat.new");
176 if (fd < -1)
177 return;
178
179 {
180 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
181 char *p = stat_buf;
182 switch (s->state) {
183 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800184 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000185 break;
186 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800187 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000188 break;
189 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800190 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000191 break;
192 }
Earl Chew80a34182009-08-02 02:23:27 +0200193 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800194 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200195 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800196 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000197 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200198 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000199 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800200 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000201 break;
202 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800203 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000204 break;
205 }
206 *p++ = '\n';
207 write(fd, stat_buf, p - stat_buf);
208 close(fd);
209 }
210
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000211 rename_or_warn("supervise/stat.new",
212 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000213
214 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000215 memset(&status, 0, sizeof(status));
216 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
217 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
218 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000219 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000220 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200221 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000222 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000223 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000224 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000225 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000226 status.got_term = 1;
227 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000228 fd = open_trunc_or_warn("supervise/status.new");
229 if (fd < 0)
230 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000231 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000232 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000233 if (sz != sizeof(status)) {
234 warn_cannot("write supervise/status.new");
235 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000236 return;
237 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000238 rename_or_warn("supervise/status.new",
239 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000240}
241
242static unsigned custom(struct svdir *s, char c)
243{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000244 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000245 int w;
246 char a[10];
247 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000248
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200249 if (s->islog)
250 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000251 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000252 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000253 if (stat(a, &st) == 0) {
254 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000255 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000256 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000257 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258 return 0;
259 }
Earl Chew80a34182009-08-02 02:23:27 +0200260 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000261 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000262 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 warn_cannot("setup stdout for control/?");
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000264 execl(a, a, (char *) NULL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000265 fatal_cannot("run control/?");
266 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000267 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000268 if (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000269 warn_cannot("wait for child control/?");
270 return 0;
271 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200272 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000273 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000274 } else {
275 if (errno != ENOENT)
276 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000277 }
278 return 0;
279}
280
281static void stopservice(struct svdir *s)
282{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000283 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000284 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000285 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000286 update_status(s);
287 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200288 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000289 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000290 custom(s, 'd');
291 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200293 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294 kill(s->pid, SIGCONT);
295 custom(s, 'x');
296 }
297}
298
299static void startservice(struct svdir *s)
300{
301 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200302 const char *arg[4];
303 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000304
Earl Chew80a34182009-08-02 02:23:27 +0200305 if (s->state == S_FINISH) {
306/* Two arguments are given to ./finish. The first one is ./run exit code,
307 * or -1 if ./run didnt exit normally. The second one is
308 * the least significant byte of the exit status as determined by waitpid;
309 * for instance it is 0 if ./run exited normally, and the signal number
310 * if ./run was terminated by a signal. If runsv cannot start ./run
311 * for some reason, the exit code is 111 and the status is 0.
312 */
313 arg[0] = "./finish";
314 arg[1] = "-1";
315 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200316 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200317 arg[1] = exitcode;
318 }
319 //arg[2] = "0";
320 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200321 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200322 //}
323 arg[3] = NULL;
324 } else {
325 arg[0] = "./run";
326 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000327 custom(s, 'u');
328 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000329
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000330 if (s->pid != 0)
331 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000332 while ((p = vfork()) == -1) {
333 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000334 sleep(5);
335 }
336 if (p == 0) {
337 /* child */
338 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000339 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000340 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000341 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000342 close(logpipe.wr);
343 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000344 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000345 close(logpipe.rd);
346 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000347 }
348 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000349 /* Non-ignored signals revert to SIG_DFL on exec anyway */
350 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000351 + (1 << SIGCHLD)
352 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000353 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000354 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000355 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200356 execv(arg[0], (char**) arg);
357 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000358 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000359 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000360 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000361 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000362 s->state = S_RUN;
363 }
364 s->pid = p;
365 pidchanged = 1;
366 s->ctrl = C_NOOP;
367 update_status(s);
368}
369
370static int ctrl(struct svdir *s, char c)
371{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000372 int sig;
373
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000374 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000375 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200376 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000377 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000378 if (s->pid && s->state != S_FINISH)
379 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000380 break;
381 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200382 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000383 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000384 if (s->pid == 0)
385 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000386 break;
387 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000388 if (s->islog)
389 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200390 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000391 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000392 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000393 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000394 if (s->pid && s->state != S_FINISH)
395 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000396 break;
397 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000398 if (s->pid && !custom(s, c))
399 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000400 s->state = S_DOWN;
401 break;
402 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000403 if (s->pid && !custom(s, c))
404 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000405 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000406 update_status(s);
407 break;
408 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000409 if (s->pid && !custom(s, c))
410 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000411 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000412 update_status(s);
413 break;
414 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200415 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000416 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000417 if (!s->pid)
418 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000419 break;
420 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000421 sig = SIGALRM;
422 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000423 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000424 sig = SIGHUP;
425 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000426 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000427 sig = SIGINT;
428 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000429 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000430 sig = SIGQUIT;
431 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000432 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000433 sig = SIGUSR1;
434 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000435 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000436 sig = SIGUSR2;
437 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000438 }
439 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000440 sendsig:
441 if (s->pid && !custom(s, c))
442 kill(s->pid, sig);
443 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000444}
445
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000446int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000447int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000448{
449 struct stat s;
450 int fd;
451 int r;
452 char buf[256];
453
Denis Vlasenkob9256052007-09-28 10:29:17 +0000454 INIT_G();
455
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100456 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457
Denis Vlasenko37188322008-02-16 13:20:56 +0000458 xpiped_pair(selfpipe);
459 close_on_exec_on(selfpipe.rd);
460 close_on_exec_on(selfpipe.wr);
461 ndelay_on(selfpipe.rd);
462 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000463
Denis Vlasenko8c783952007-01-27 22:21:52 +0000464 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000465 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000466 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000467 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000468
469 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000470 /* bss: svd[0].pid = 0; */
471 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
472 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200473 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000474 /* bss: svd[0].islog = 0; */
475 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000476 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200477 if (stat("down", &s) != -1)
478 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000479
480 if (stat("log", &s) == -1) {
481 if (errno != ENOENT)
482 warn_cannot("stat ./log");
483 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000484 if (!S_ISDIR(s.st_mode)) {
485 errno = 0;
486 warn_cannot("stat log/down: log is not a directory");
487 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000488 haslog = 1;
489 svd[1].state = S_DOWN;
490 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200491 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000492 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000493 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000494 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200495 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000496 xpiped_pair(logpipe);
497 close_on_exec_on(logpipe.rd);
498 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000499 }
500 }
501
502 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000503 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000504 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000505 if (r == sizeof(buf))
506 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000507 buf[r] = 0;
508 mkdir(buf, 0700);
509 } else {
510 if ((errno != ENOENT) && (errno != EINVAL))
511 fatal_cannot("readlink ./supervise");
512 }
513 }
514 svd[0].fdlock = xopen3("log/supervise/lock"+4,
515 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200516 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000517 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000518 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000519 if (haslog) {
520 if (mkdir("log/supervise", 0700) == -1) {
521 r = readlink("log/supervise", buf, 256);
522 if (r != -1) {
523 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000524 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000525 buf[r] = 0;
526 fd = xopen(".", O_RDONLY|O_NDELAY);
527 xchdir("./log");
528 mkdir(buf, 0700);
529 if (fchdir(fd) == -1)
530 fatal_cannot("change back to service directory");
531 close(fd);
532 }
533 else {
534 if ((errno != ENOENT) && (errno != EINVAL))
535 fatal_cannot("readlink ./log/supervise");
536 }
537 }
538 svd[1].fdlock = xopen3("log/supervise/lock",
539 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200540 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000541 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000542 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000543 }
544
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000545 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000546 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000547 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000548 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000549 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000550 update_status(&svd[0]);
551 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000552 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000554 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000555 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000556 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000557 update_status(&svd[1]);
558 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000559 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000560 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000561 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000562 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000563 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000564 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000565 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 }
567 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000568 struct pollfd x[3];
569 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000570 char ch;
571
572 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200573 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000574 startservice(&svd[1]);
575 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200576 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000577 startservice(&svd[0]);
578
Denis Vlasenko37188322008-02-16 13:20:56 +0000579 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000580 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000581 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000582 x[1].events = POLLIN;
583 /* x[2] is used only if haslog == 1 */
584 x[2].fd = svd[1].fdcontrol;
585 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000586 sig_unblock(SIGTERM);
587 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000588 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000589 sig_block(SIGTERM);
590 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000591
Denis Vlasenko37188322008-02-16 13:20:56 +0000592 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000593 continue;
594
Denis Vlasenko04c63862006-11-17 18:58:49 +0000595 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000596 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000597 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000598
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000599 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000600 if (!child)
601 break;
602 if ((child == -1) && (errno != EINTR))
603 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000604 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200605 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000606 svd[0].pid = 0;
607 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200608 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000609 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200610 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000611 if (fd != -1) {
612 close(fd);
613 svd[0].state = S_FINISH;
614 update_status(&svd[0]);
615 continue;
616 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000617 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000618 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000619 deadline = svd[0].start.tv_sec + 1;
620 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000621 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000622 if (LESS(svd[0].start.tv_sec, deadline))
623 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000624 }
625 if (haslog) {
626 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200627 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000628 svd[1].pid = 0;
629 pidchanged = 1;
630 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000631 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000632 deadline = svd[1].start.tv_sec + 1;
633 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000634 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000635 if (LESS(svd[1].start.tv_sec, deadline))
636 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000637 }
638 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000639 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000640 if (read(svd[0].fdcontrol, &ch, 1) == 1)
641 ctrl(&svd[0], ch);
642 if (haslog)
643 if (read(svd[1].fdcontrol, &ch, 1) == 1)
644 ctrl(&svd[1], ch);
645
646 if (sigterm) {
647 ctrl(&svd[0], 'x');
648 sigterm = 0;
649 }
650
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200651 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000652 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000653 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200654 if (svd[1].sd_want != W_EXIT) {
655 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000656 /* stopservice(&svd[1]); */
657 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000658 close(logpipe.wr);
659 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000660 }
661 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000662 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000663 /* not reached */
664 return 0;
665}