blob: 8833f4c96c22239caa52360c3a35becc504a71eb [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> */
Denys Vlasenko0863e1a2015-10-19 00:41:28 +020029
30//config:config RUNSV
31//config: bool "runsv"
32//config: default y
33//config: help
34//config: runsv starts and monitors a service and optionally an appendant log
35//config: service.
36
37//applet:IF_RUNSV(APPLET(runsv, BB_DIR_USR_BIN, BB_SUID_DROP))
38
39//kbuild:lib-$(CONFIG_RUNSV) += runsv.o
Denis Vlasenko04c63862006-11-17 18:58:49 +000040
Pere Orga5bc8c002011-04-11 03:29:49 +020041//usage:#define runsv_trivial_usage
42//usage: "DIR"
43//usage:#define runsv_full_usage "\n\n"
44//usage: "Start and monitor a service and optionally an appendant log service"
45
Denis Vlasenko04c63862006-11-17 18:58:49 +000046#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000047#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +020048#include "common_bufsiz.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +000049#include "runit_lib.h"
50
Denis Vlasenko7bc53602007-08-31 21:45:52 +000051#if ENABLE_MONOTONIC_SYSCALL
52#include <sys/syscall.h>
53
54/* libc has incredibly messy way of doing this,
55 * typically requiring -lrt. We just skip all this mess */
56static void gettimeofday_ns(struct timespec *ts)
57{
58 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
59}
60#else
61static void gettimeofday_ns(struct timespec *ts)
62{
Denys Vlasenkoab3964d2015-10-13 14:50:20 +020063 BUILD_BUG_ON(sizeof(struct timeval) != sizeof(struct timespec));
64 BUILD_BUG_ON(sizeof(((struct timeval*)ts)->tv_usec) != sizeof(ts->tv_nsec));
65 /* Cheat */
66 gettimeofday((void*)ts, NULL);
67 ts->tv_nsec *= 1000;
Denis Vlasenko7bc53602007-08-31 21:45:52 +000068}
69#endif
70
71/* Compare possibly overflowing unsigned counters */
72#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
73
Denis Vlasenko04c63862006-11-17 18:58:49 +000074/* state */
75#define S_DOWN 0
76#define S_RUN 1
77#define S_FINISH 2
78/* ctrl */
79#define C_NOOP 0
80#define C_TERM 1
81#define C_PAUSE 2
82/* want */
83#define W_UP 0
84#define W_DOWN 1
85#define W_EXIT 2
86
87struct svdir {
88 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000089 smallint state;
90 smallint ctrl;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +020091 smallint sd_want;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000092 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000093 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +000094 int fdlock;
95 int fdcontrol;
96 int fdcontrolwrite;
Earl Chew80a34182009-08-02 02:23:27 +020097 int wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +000098};
Denis Vlasenko04c63862006-11-17 18:58:49 +000099
Denis Vlasenkob9256052007-09-28 10:29:17 +0000100struct globals {
101 smallint haslog;
102 smallint sigterm;
103 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +0000104 struct fd_pair selfpipe;
105 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000106 char *dir;
107 struct svdir svd[2];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100108} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200109#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkob9256052007-09-28 10:29:17 +0000110#define haslog (G.haslog )
111#define sigterm (G.sigterm )
112#define pidchanged (G.pidchanged )
113#define selfpipe (G.selfpipe )
114#define logpipe (G.logpipe )
115#define dir (G.dir )
116#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000117#define INIT_G() do { \
118 pidchanged = 1; \
119} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000120
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000121static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000122{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200123 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000124 /* was exiting 111 */
125}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000126static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000127{
128 fatal2_cannot(m, "");
129 /* was exiting 111 */
130}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000131static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000132{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200133 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000134 /* was exiting 111 */
135}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000136static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000137{
138 bb_perror_msg("%s: warning: cannot %s", dir, m);
139}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000140
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000141static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000142{
Denis Vlasenko37188322008-02-16 13:20:56 +0000143 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000144}
145
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000146static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000147{
148 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000149 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000150}
151
Denis Vlasenko04c63862006-11-17 18:58:49 +0000152static int open_trunc_or_warn(const char *name)
153{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200154 /* Why O_NDELAY? */
155 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000156 if (fd < 0)
157 bb_perror_msg("%s: warning: cannot open %s",
158 dir, name);
159 return fd;
160}
161
Denis Vlasenko04c63862006-11-17 18:58:49 +0000162static void update_status(struct svdir *s)
163{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000164 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000165 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000166 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000167
168 /* pid */
169 if (pidchanged) {
170 fd = open_trunc_or_warn("supervise/pid.new");
171 if (fd < 0)
172 return;
173 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000174 char spid[sizeof(int)*3 + 2];
175 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000176 write(fd, spid, size);
177 }
178 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000179 if (rename_or_warn("supervise/pid.new",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100180 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000181 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000182 pidchanged = 0;
183 }
184
185 /* stat */
186 fd = open_trunc_or_warn("supervise/stat.new");
187 if (fd < -1)
188 return;
189
190 {
191 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
192 char *p = stat_buf;
193 switch (s->state) {
194 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800195 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000196 break;
197 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800198 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000199 break;
200 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800201 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000202 break;
203 }
Earl Chew80a34182009-08-02 02:23:27 +0200204 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800205 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200206 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800207 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000208 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200209 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000210 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800211 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000212 break;
213 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800214 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000215 break;
216 }
217 *p++ = '\n';
218 write(fd, stat_buf, p - stat_buf);
219 close(fd);
220 }
221
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000222 rename_or_warn("supervise/stat.new",
223 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000224
225 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000226 memset(&status, 0, sizeof(status));
227 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
228 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
229 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000230 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000231 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200232 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000233 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000234 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000235 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000236 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000237 status.got_term = 1;
238 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000239 fd = open_trunc_or_warn("supervise/status.new");
240 if (fd < 0)
241 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000242 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000243 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000244 if (sz != sizeof(status)) {
245 warn_cannot("write supervise/status.new");
246 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000247 return;
248 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000249 rename_or_warn("supervise/status.new",
250 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000251}
252
253static unsigned custom(struct svdir *s, char c)
254{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000255 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000256 int w;
257 char a[10];
258 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200260 if (s->islog)
261 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000262 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000263 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000264 if (stat(a, &st) == 0) {
265 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000266 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000267 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000268 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000269 return 0;
270 }
Earl Chew80a34182009-08-02 02:23:27 +0200271 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000272 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000273 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274 warn_cannot("setup stdout for control/?");
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000275 execl(a, a, (char *) NULL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000276 fatal_cannot("run control/?");
277 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000278 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000279 if (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000280 warn_cannot("wait for child control/?");
281 return 0;
282 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200283 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000284 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000285 } else {
286 if (errno != ENOENT)
287 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000288 }
289 return 0;
290}
291
292static void stopservice(struct svdir *s)
293{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000294 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000295 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000296 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000297 update_status(s);
298 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200299 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000300 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000301 custom(s, 'd');
302 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200304 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000305 kill(s->pid, SIGCONT);
306 custom(s, 'x');
307 }
308}
309
310static void startservice(struct svdir *s)
311{
312 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200313 const char *arg[4];
314 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000315
Earl Chew80a34182009-08-02 02:23:27 +0200316 if (s->state == S_FINISH) {
317/* Two arguments are given to ./finish. The first one is ./run exit code,
318 * or -1 if ./run didnt exit normally. The second one is
319 * the least significant byte of the exit status as determined by waitpid;
320 * for instance it is 0 if ./run exited normally, and the signal number
321 * if ./run was terminated by a signal. If runsv cannot start ./run
322 * for some reason, the exit code is 111 and the status is 0.
323 */
324 arg[0] = "./finish";
325 arg[1] = "-1";
326 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200327 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200328 arg[1] = exitcode;
329 }
330 //arg[2] = "0";
331 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200332 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200333 //}
334 arg[3] = NULL;
335 } else {
336 arg[0] = "./run";
337 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000338 custom(s, 'u');
339 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000340
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000341 if (s->pid != 0)
342 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000343 while ((p = vfork()) == -1) {
344 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000345 sleep(5);
346 }
347 if (p == 0) {
348 /* child */
349 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000350 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000351 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000352 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000353 close(logpipe.wr);
354 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000355 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000356 close(logpipe.rd);
357 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000358 }
359 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000360 /* Non-ignored signals revert to SIG_DFL on exec anyway */
361 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000362 + (1 << SIGCHLD)
363 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000364 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000365 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000366 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200367 execv(arg[0], (char**) arg);
368 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000369 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000370 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000371 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000372 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000373 s->state = S_RUN;
374 }
375 s->pid = p;
376 pidchanged = 1;
377 s->ctrl = C_NOOP;
378 update_status(s);
379}
380
381static int ctrl(struct svdir *s, char c)
382{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000383 int sig;
384
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000385 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000386 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200387 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000388 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000389 if (s->pid && s->state != S_FINISH)
390 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000391 break;
392 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200393 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000394 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000395 if (s->pid == 0)
396 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000397 break;
398 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000399 if (s->islog)
400 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200401 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000402 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000403 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000404 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000405 if (s->pid && s->state != S_FINISH)
406 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000407 break;
408 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000409 if (s->pid && !custom(s, c))
410 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000411 s->state = S_DOWN;
412 break;
413 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000414 if (s->pid && !custom(s, c))
415 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000416 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000417 update_status(s);
418 break;
419 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000420 if (s->pid && !custom(s, c))
421 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000422 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000423 update_status(s);
424 break;
425 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200426 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000427 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000428 if (!s->pid)
429 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000430 break;
431 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000432 sig = SIGALRM;
433 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000434 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000435 sig = SIGHUP;
436 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000437 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000438 sig = SIGINT;
439 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000440 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000441 sig = SIGQUIT;
442 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000443 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000444 sig = SIGUSR1;
445 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000446 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000447 sig = SIGUSR2;
448 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000449 }
450 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000451 sendsig:
452 if (s->pid && !custom(s, c))
453 kill(s->pid, sig);
454 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000455}
456
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000457int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000458int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000459{
460 struct stat s;
461 int fd;
462 int r;
463 char buf[256];
464
Denis Vlasenkob9256052007-09-28 10:29:17 +0000465 INIT_G();
466
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100467 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000468
Denis Vlasenko37188322008-02-16 13:20:56 +0000469 xpiped_pair(selfpipe);
470 close_on_exec_on(selfpipe.rd);
471 close_on_exec_on(selfpipe.wr);
472 ndelay_on(selfpipe.rd);
473 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000474
Denis Vlasenko8c783952007-01-27 22:21:52 +0000475 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000476 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000477 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000478 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000479
480 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000481 /* bss: svd[0].pid = 0; */
482 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
483 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200484 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000485 /* bss: svd[0].islog = 0; */
486 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000487 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200488 if (stat("down", &s) != -1)
489 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000490
491 if (stat("log", &s) == -1) {
492 if (errno != ENOENT)
493 warn_cannot("stat ./log");
494 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000495 if (!S_ISDIR(s.st_mode)) {
496 errno = 0;
497 warn_cannot("stat log/down: log is not a directory");
498 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000499 haslog = 1;
500 svd[1].state = S_DOWN;
501 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200502 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000503 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000504 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000505 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200506 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000507 xpiped_pair(logpipe);
508 close_on_exec_on(logpipe.rd);
509 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000510 }
511 }
512
513 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000514 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000515 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000516 if (r == sizeof(buf))
517 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000518 buf[r] = 0;
519 mkdir(buf, 0700);
520 } else {
521 if ((errno != ENOENT) && (errno != EINVAL))
522 fatal_cannot("readlink ./supervise");
523 }
524 }
525 svd[0].fdlock = xopen3("log/supervise/lock"+4,
526 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200527 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000528 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000529 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000530 if (haslog) {
531 if (mkdir("log/supervise", 0700) == -1) {
532 r = readlink("log/supervise", buf, 256);
533 if (r != -1) {
534 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000535 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000536 buf[r] = 0;
537 fd = xopen(".", O_RDONLY|O_NDELAY);
538 xchdir("./log");
539 mkdir(buf, 0700);
540 if (fchdir(fd) == -1)
541 fatal_cannot("change back to service directory");
542 close(fd);
543 }
544 else {
545 if ((errno != ENOENT) && (errno != EINVAL))
546 fatal_cannot("readlink ./log/supervise");
547 }
548 }
549 svd[1].fdlock = xopen3("log/supervise/lock",
550 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200551 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000552 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000553 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000554 }
555
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000556 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000557 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000558 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000559 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000560 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000561 update_status(&svd[0]);
562 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000563 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000564 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000565 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000567 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000568 update_status(&svd[1]);
569 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000570 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000571 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000572 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000573 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000574 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000575 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000576 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000577 }
578 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000579 struct pollfd x[3];
580 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000581 char ch;
582
583 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200584 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000585 startservice(&svd[1]);
586 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200587 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000588 startservice(&svd[0]);
589
Denis Vlasenko37188322008-02-16 13:20:56 +0000590 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000591 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000592 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000593 x[1].events = POLLIN;
594 /* x[2] is used only if haslog == 1 */
595 x[2].fd = svd[1].fdcontrol;
596 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000597 sig_unblock(SIGTERM);
598 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000599 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000600 sig_block(SIGTERM);
601 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000602
Denis Vlasenko37188322008-02-16 13:20:56 +0000603 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000604 continue;
605
Denis Vlasenko04c63862006-11-17 18:58:49 +0000606 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000607 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000608 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000609
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000610 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000611 if (!child)
612 break;
613 if ((child == -1) && (errno != EINTR))
614 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000615 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200616 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000617 svd[0].pid = 0;
618 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200619 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000620 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200621 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000622 if (fd != -1) {
623 close(fd);
624 svd[0].state = S_FINISH;
625 update_status(&svd[0]);
626 continue;
627 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000628 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000629 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000630 deadline = svd[0].start.tv_sec + 1;
631 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000632 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000633 if (LESS(svd[0].start.tv_sec, deadline))
634 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000635 }
636 if (haslog) {
637 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200638 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000639 svd[1].pid = 0;
640 pidchanged = 1;
641 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000642 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000643 deadline = svd[1].start.tv_sec + 1;
644 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000645 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000646 if (LESS(svd[1].start.tv_sec, deadline))
647 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000648 }
649 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000650 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000651 if (read(svd[0].fdcontrol, &ch, 1) == 1)
652 ctrl(&svd[0], ch);
653 if (haslog)
654 if (read(svd[1].fdcontrol, &ch, 1) == 1)
655 ctrl(&svd[1], ch);
656
657 if (sigterm) {
658 ctrl(&svd[0], 'x');
659 sigterm = 0;
660 }
661
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200662 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000663 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000664 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200665 if (svd[1].sd_want != W_EXIT) {
666 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000667 /* stopservice(&svd[1]); */
668 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000669 close(logpipe.wr);
670 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000671 }
672 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000673 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000674 /* not reached */
675 return 0;
676}