blob: ad7d82cb83c92781a6c1d9b51c63886a9030d2dc [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
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020031//config: bool "runsv (7.2 kb)"
Denys Vlasenko0863e1a2015-10-19 00:41:28 +020032//config: default y
33//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020034//config: runsv starts and monitors a service and optionally an appendant log
35//config: service.
Denys Vlasenko0863e1a2015-10-19 00:41:28 +020036
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 { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200118 setup_common_bufsiz(); \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000119 pidchanged = 1; \
120} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000121
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000122static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000123{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200124 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000125 /* was exiting 111 */
126}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000127static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000128{
129 fatal2_cannot(m, "");
130 /* was exiting 111 */
131}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000132static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000133{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200134 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000135 /* was exiting 111 */
136}
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200137static void warn2_cannot(const char *m1, const char *m2)
138{
139 bb_perror_msg("%s: warning: can't %s%s", dir, m1, m2);
140}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000141static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000142{
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200143 warn2_cannot(m, "");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000144}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000145
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000146static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000147{
Denis Vlasenko37188322008-02-16 13:20:56 +0000148 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000149}
150
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000151static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000152{
153 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000154 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000155}
156
Denis Vlasenko04c63862006-11-17 18:58:49 +0000157static int open_trunc_or_warn(const char *name)
158{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200159 /* Why O_NDELAY? */
160 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000161 if (fd < 0)
162 bb_perror_msg("%s: warning: cannot open %s",
163 dir, name);
164 return fd;
165}
166
Denis Vlasenko04c63862006-11-17 18:58:49 +0000167static void update_status(struct svdir *s)
168{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000169 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000170 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000171 svstatus_t status;
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200172 const char *fstatus ="log/supervise/status";
173 const char *fstatusnew ="log/supervise/status.new";
174 const char *f_stat ="log/supervise/stat";
175 const char *fstatnew ="log/supervise/stat.new";
176 const char *fpid ="log/supervise/pid";
177 const char *fpidnew ="log/supervise/pid.new";
178
179 if (!s->islog) {
180 fstatus += 4;
181 fstatusnew += 4;
182 f_stat += 4;
183 fstatnew += 4;
184 fpid += 4;
185 fpidnew += 4;
186 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000187
188 /* pid */
189 if (pidchanged) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200190 fd = open_trunc_or_warn(fpidnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000191 if (fd < 0)
192 return;
193 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000194 char spid[sizeof(int)*3 + 2];
195 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000196 write(fd, spid, size);
197 }
198 close(fd);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200199 if (rename_or_warn(fpidnew, fpid))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000200 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000201 pidchanged = 0;
202 }
203
204 /* stat */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200205 fd = open_trunc_or_warn(fstatnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000206 if (fd < -1)
207 return;
208
209 {
210 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
211 char *p = stat_buf;
212 switch (s->state) {
213 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800214 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000215 break;
216 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800217 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000218 break;
219 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800220 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000221 break;
222 }
Earl Chew80a34182009-08-02 02:23:27 +0200223 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800224 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200225 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800226 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000227 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200228 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800230 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000231 break;
232 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800233 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000234 break;
235 }
236 *p++ = '\n';
237 write(fd, stat_buf, p - stat_buf);
238 close(fd);
239 }
240
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200241 rename_or_warn(fstatnew, f_stat);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000242
243 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000244 memset(&status, 0, sizeof(status));
245 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
246 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
247 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000248 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000249 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200250 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000251 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000252 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000253 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000254 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000255 status.got_term = 1;
256 status.run_or_finish = s->state;
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200257 fd = open_trunc_or_warn(fstatusnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258 if (fd < 0)
259 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000260 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000261 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000262 if (sz != sizeof(status)) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200263 warn2_cannot("write ", fstatusnew);
264 unlink(fstatusnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000265 return;
266 }
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200267 rename_or_warn(fstatusnew, fstatus);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000268}
269
270static unsigned custom(struct svdir *s, char c)
271{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000272 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000273 int w;
274 char a[10];
275 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000276
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200277 if (s->islog)
278 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000279 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000280 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000281 if (stat(a, &st) == 0) {
282 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000283 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000284 if (pid == -1) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200285 warn2_cannot("vfork for ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000286 return 0;
287 }
Earl Chew80a34182009-08-02 02:23:27 +0200288 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000289 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000290 if (haslog && dup2(logpipe.wr, 1) == -1)
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200291 warn2_cannot("setup stdout for ", a);
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000292 execl(a, a, (char *) NULL);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200293 fatal2_cannot("run ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000295 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000296 if (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200297 warn2_cannot("wait for child ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000298 return 0;
299 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200300 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000301 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000302 } else {
303 if (errno != ENOENT)
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200304 warn2_cannot("stat ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000305 }
306 return 0;
307}
308
309static void stopservice(struct svdir *s)
310{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000311 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000312 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000313 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314 update_status(s);
315 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200316 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000317 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000318 custom(s, 'd');
319 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000320 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200321 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000322 kill(s->pid, SIGCONT);
323 custom(s, 'x');
324 }
325}
326
327static void startservice(struct svdir *s)
328{
329 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200330 const char *arg[4];
331 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000332
Earl Chew80a34182009-08-02 02:23:27 +0200333 if (s->state == S_FINISH) {
334/* Two arguments are given to ./finish. The first one is ./run exit code,
335 * or -1 if ./run didnt exit normally. The second one is
336 * the least significant byte of the exit status as determined by waitpid;
337 * for instance it is 0 if ./run exited normally, and the signal number
338 * if ./run was terminated by a signal. If runsv cannot start ./run
339 * for some reason, the exit code is 111 and the status is 0.
340 */
341 arg[0] = "./finish";
342 arg[1] = "-1";
343 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200344 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200345 arg[1] = exitcode;
346 }
347 //arg[2] = "0";
348 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200349 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200350 //}
351 arg[3] = NULL;
352 } else {
353 arg[0] = "./run";
354 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000355 custom(s, 'u');
356 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000357
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000358 if (s->pid != 0)
359 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000360 while ((p = vfork()) == -1) {
361 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000362 sleep(5);
363 }
364 if (p == 0) {
365 /* child */
366 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000367 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000368 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000369 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000370 close(logpipe.wr);
371 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000372 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000373 close(logpipe.rd);
374 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000375 }
376 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000377 /* Non-ignored signals revert to SIG_DFL on exec anyway */
378 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000379 + (1 << SIGCHLD)
380 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000381 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000382 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000383 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200384 execv(arg[0], (char**) arg);
385 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000386 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000387 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000388 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000389 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000390 s->state = S_RUN;
391 }
392 s->pid = p;
393 pidchanged = 1;
394 s->ctrl = C_NOOP;
395 update_status(s);
396}
397
398static int ctrl(struct svdir *s, char c)
399{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000400 int sig;
401
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000402 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000403 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200404 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000405 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200406 if (s->state == S_RUN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000407 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000408 break;
409 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200410 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000411 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200412 if (s->state == S_DOWN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000413 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000414 break;
415 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000416 if (s->islog)
417 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200418 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000419 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000420 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000421 case 't': /* sig term */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200422 if (s->state == S_RUN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000423 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000424 break;
425 case 'k': /* sig kill */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200426 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000427 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000428 s->state = S_DOWN;
429 break;
430 case 'p': /* sig pause */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200431 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000432 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000433 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000434 update_status(s);
435 break;
436 case 'c': /* sig cont */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200437 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000438 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000439 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000440 update_status(s);
441 break;
442 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200443 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000444 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200445 if (s->state == S_DOWN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000446 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000447 break;
448 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000449 sig = SIGALRM;
450 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000451 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000452 sig = SIGHUP;
453 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000454 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000455 sig = SIGINT;
456 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000458 sig = SIGQUIT;
459 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000460 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000461 sig = SIGUSR1;
462 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000463 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000464 sig = SIGUSR2;
465 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000466 }
467 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000468 sendsig:
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200469 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000470 kill(s->pid, sig);
471 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000472}
473
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200474static void open_control(const char *f, struct svdir *s)
475{
476 struct stat st;
477 mkfifo(f, 0600);
478 if (stat(f, &st) == -1)
479 fatal2_cannot("stat ", f);
480 if (!S_ISFIFO(st.st_mode))
481 bb_error_msg_and_die("%s: fatal: %s exists but is not a fifo", dir, f);
482 s->fdcontrol = xopen(f, O_RDONLY|O_NDELAY);
483 close_on_exec_on(s->fdcontrol);
484 s->fdcontrolwrite = xopen(f, O_WRONLY|O_NDELAY);
485 close_on_exec_on(s->fdcontrolwrite);
486 update_status(s);
487}
488
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000489int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000490int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000491{
492 struct stat s;
493 int fd;
494 int r;
495 char buf[256];
496
Denis Vlasenkob9256052007-09-28 10:29:17 +0000497 INIT_G();
498
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100499 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000500
Denis Vlasenko37188322008-02-16 13:20:56 +0000501 xpiped_pair(selfpipe);
502 close_on_exec_on(selfpipe.rd);
503 close_on_exec_on(selfpipe.wr);
504 ndelay_on(selfpipe.rd);
505 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000506
Denis Vlasenko8c783952007-01-27 22:21:52 +0000507 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000508 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000509 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000510 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000511
512 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000513 /* bss: svd[0].pid = 0; */
514 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
515 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200516 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000517 /* bss: svd[0].islog = 0; */
518 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000519 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200520 if (stat("down", &s) != -1)
521 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000522
523 if (stat("log", &s) == -1) {
524 if (errno != ENOENT)
525 warn_cannot("stat ./log");
526 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000527 if (!S_ISDIR(s.st_mode)) {
528 errno = 0;
529 warn_cannot("stat log/down: log is not a directory");
530 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000531 haslog = 1;
532 svd[1].state = S_DOWN;
533 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200534 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000535 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000536 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000537 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200538 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000539 xpiped_pair(logpipe);
540 close_on_exec_on(logpipe.rd);
541 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000542 }
543 }
544
545 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000546 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000547 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000548 if (r == sizeof(buf))
549 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000550 buf[r] = 0;
551 mkdir(buf, 0700);
552 } else {
553 if ((errno != ENOENT) && (errno != EINVAL))
554 fatal_cannot("readlink ./supervise");
555 }
556 }
557 svd[0].fdlock = xopen3("log/supervise/lock"+4,
558 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200559 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000560 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000561 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000562 if (haslog) {
563 if (mkdir("log/supervise", 0700) == -1) {
564 r = readlink("log/supervise", buf, 256);
565 if (r != -1) {
566 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000567 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000568 buf[r] = 0;
569 fd = xopen(".", O_RDONLY|O_NDELAY);
570 xchdir("./log");
571 mkdir(buf, 0700);
572 if (fchdir(fd) == -1)
573 fatal_cannot("change back to service directory");
574 close(fd);
575 }
576 else {
577 if ((errno != ENOENT) && (errno != EINVAL))
578 fatal_cannot("readlink ./log/supervise");
579 }
580 }
581 svd[1].fdlock = xopen3("log/supervise/lock",
582 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200583 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000584 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000585 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000586 }
587
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200588 open_control("log/supervise/control"+4, &svd[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000589 if (haslog) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200590 open_control("log/supervise/control", &svd[1]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000591 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000592 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000593 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000594 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000595 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000596 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000597 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000598 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000599 }
600 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000601 struct pollfd x[3];
602 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000603 char ch;
604
605 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200606 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000607 startservice(&svd[1]);
608 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200609 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000610 startservice(&svd[0]);
611
Denis Vlasenko37188322008-02-16 13:20:56 +0000612 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000613 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000614 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000615 x[1].events = POLLIN;
616 /* x[2] is used only if haslog == 1 */
617 x[2].fd = svd[1].fdcontrol;
618 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000619 sig_unblock(SIGTERM);
620 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000621 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000622 sig_block(SIGTERM);
623 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000624
Denis Vlasenko37188322008-02-16 13:20:56 +0000625 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000626 continue;
627
Denis Vlasenko04c63862006-11-17 18:58:49 +0000628 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000629 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000630 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000631
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000632 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000633 if (!child)
634 break;
635 if ((child == -1) && (errno != EINTR))
636 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000637 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200638 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000639 svd[0].pid = 0;
640 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200641 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000642 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200643 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000644 if (fd != -1) {
645 close(fd);
646 svd[0].state = S_FINISH;
647 update_status(&svd[0]);
648 continue;
649 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000650 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000651 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000652 deadline = svd[0].start.tv_sec + 1;
653 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000654 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000655 if (LESS(svd[0].start.tv_sec, deadline))
656 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000657 }
658 if (haslog) {
659 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200660 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000661 svd[1].pid = 0;
662 pidchanged = 1;
663 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000664 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000665 deadline = svd[1].start.tv_sec + 1;
666 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000667 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000668 if (LESS(svd[1].start.tv_sec, deadline))
669 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000670 }
671 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000672 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000673 if (read(svd[0].fdcontrol, &ch, 1) == 1)
674 ctrl(&svd[0], ch);
675 if (haslog)
676 if (read(svd[1].fdcontrol, &ch, 1) == 1)
677 ctrl(&svd[1], ch);
678
679 if (sigterm) {
680 ctrl(&svd[0], 'x');
681 sigterm = 0;
682 }
683
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200684 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000685 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000686 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200687 if (svd[1].sd_want != W_EXIT) {
688 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000689 /* stopservice(&svd[1]); */
690 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000691 close(logpipe.wr);
692 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000693 }
694 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000695 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000696 /* not reached */
697 return 0;
698}