blob: 61ea240ff8678f3cd95822cc38813a3ffa3dd7c0 [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
Denys Vlasenko95f79532017-08-02 14:26:33 +020016THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
Denis Vlasenko8a164052007-03-12 23:34:52 +000017WARRANTIES, 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 Vlasenkob097a842018-12-28 03:20:17 +010031//config: bool "runsv (7.8 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
Denis Vlasenko7bc53602007-08-31 21:45:52 +000054static void gettimeofday_ns(struct timespec *ts)
55{
Denys Vlasenkobe5a5052019-10-24 16:26:55 +020056 clock_gettime(CLOCK_REALTIME, ts);
Denis Vlasenko7bc53602007-08-31 21:45:52 +000057}
58#else
59static void gettimeofday_ns(struct timespec *ts)
60{
Denys Vlasenko77a51a22020-12-29 16:53:11 +010061 if (sizeof(struct timeval) == sizeof(struct timespec)
62 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
63 ) {
64 /* Cheat */
Denys Vlasenko3c13da32020-12-30 23:48:01 +010065 xgettimeofday((void*)ts);
Denys Vlasenko77a51a22020-12-29 16:53:11 +010066 ts->tv_nsec *= 1000;
67 } else {
68 /* For example, musl has "incompatible" layouts */
69 struct timeval tv;
Denys Vlasenko3c13da32020-12-30 23:48:01 +010070 xgettimeofday(&tv);
Denys Vlasenko77a51a22020-12-29 16:53:11 +010071 ts->tv_sec = tv.tv_sec;
72 ts->tv_nsec = tv.tv_usec * 1000;
73 }
Denis Vlasenko7bc53602007-08-31 21:45:52 +000074}
75#endif
76
77/* Compare possibly overflowing unsigned counters */
78#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
79
Denis Vlasenko04c63862006-11-17 18:58:49 +000080/* state */
81#define S_DOWN 0
82#define S_RUN 1
83#define S_FINISH 2
84/* ctrl */
85#define C_NOOP 0
86#define C_TERM 1
87#define C_PAUSE 2
88/* want */
89#define W_UP 0
90#define W_DOWN 1
91#define W_EXIT 2
92
93struct svdir {
94 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000095 smallint state;
96 smallint ctrl;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +020097 smallint sd_want;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000098 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000099 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000100 int fdlock;
101 int fdcontrol;
102 int fdcontrolwrite;
Earl Chew80a34182009-08-02 02:23:27 +0200103 int wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000104};
Denis Vlasenko04c63862006-11-17 18:58:49 +0000105
Denis Vlasenkob9256052007-09-28 10:29:17 +0000106struct globals {
107 smallint haslog;
108 smallint sigterm;
109 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +0000110 struct fd_pair selfpipe;
111 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000112 char *dir;
113 struct svdir svd[2];
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100114} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200115#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkob9256052007-09-28 10:29:17 +0000116#define haslog (G.haslog )
117#define sigterm (G.sigterm )
118#define pidchanged (G.pidchanged )
119#define selfpipe (G.selfpipe )
120#define logpipe (G.logpipe )
121#define dir (G.dir )
122#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000123#define INIT_G() do { \
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200124 setup_common_bufsiz(); \
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000125 pidchanged = 1; \
126} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000127
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000128static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000129{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200130 bb_perror_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000131 /* was exiting 111 */
132}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000133static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000134{
135 fatal2_cannot(m, "");
136 /* was exiting 111 */
137}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000138static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000139{
Denys Vlasenko7f3a2a22015-10-08 11:24:44 +0200140 bb_error_msg_and_die("%s: fatal: can't %s%s", dir, m1, m2);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000141 /* was exiting 111 */
142}
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200143static void warn2_cannot(const char *m1, const char *m2)
144{
145 bb_perror_msg("%s: warning: can't %s%s", dir, m1, m2);
146}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000147static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000148{
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200149 warn2_cannot(m, "");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000150}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000151
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000152static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000153{
Denis Vlasenko37188322008-02-16 13:20:56 +0000154 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000155}
156
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000157static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000158{
159 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000160 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000161}
162
Denis Vlasenko04c63862006-11-17 18:58:49 +0000163static int open_trunc_or_warn(const char *name)
164{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200165 /* Why O_NDELAY? */
166 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000167 if (fd < 0)
168 bb_perror_msg("%s: warning: cannot open %s",
169 dir, name);
170 return fd;
171}
172
Denis Vlasenko04c63862006-11-17 18:58:49 +0000173static void update_status(struct svdir *s)
174{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000175 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000176 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000177 svstatus_t status;
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200178 const char *fstatus ="log/supervise/status";
179 const char *fstatusnew ="log/supervise/status.new";
180 const char *f_stat ="log/supervise/stat";
181 const char *fstatnew ="log/supervise/stat.new";
182 const char *fpid ="log/supervise/pid";
183 const char *fpidnew ="log/supervise/pid.new";
184
185 if (!s->islog) {
186 fstatus += 4;
187 fstatusnew += 4;
188 f_stat += 4;
189 fstatnew += 4;
190 fpid += 4;
191 fpidnew += 4;
192 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000193
194 /* pid */
195 if (pidchanged) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200196 fd = open_trunc_or_warn(fpidnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000197 if (fd < 0)
198 return;
199 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000200 char spid[sizeof(int)*3 + 2];
201 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000202 write(fd, spid, size);
203 }
204 close(fd);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200205 if (rename_or_warn(fpidnew, fpid))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000206 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000207 pidchanged = 0;
208 }
209
210 /* stat */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200211 fd = open_trunc_or_warn(fstatnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000212 if (fd < -1)
213 return;
214
215 {
216 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
217 char *p = stat_buf;
218 switch (s->state) {
219 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800220 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000221 break;
222 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800223 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000224 break;
225 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800226 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000227 break;
228 }
Earl Chew80a34182009-08-02 02:23:27 +0200229 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800230 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200231 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800232 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000233 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200234 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000235 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800236 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000237 break;
238 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800239 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000240 break;
241 }
242 *p++ = '\n';
243 write(fd, stat_buf, p - stat_buf);
244 close(fd);
245 }
246
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200247 rename_or_warn(fstatnew, f_stat);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000248
249 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000250 memset(&status, 0, sizeof(status));
251 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
252 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
253 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000254 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000255 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200256 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000257 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000259 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000260 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000261 status.got_term = 1;
262 status.run_or_finish = s->state;
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200263 fd = open_trunc_or_warn(fstatusnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000264 if (fd < 0)
265 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000266 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000267 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000268 if (sz != sizeof(status)) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200269 warn2_cannot("write ", fstatusnew);
270 unlink(fstatusnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000271 return;
272 }
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200273 rename_or_warn(fstatusnew, fstatus);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274}
275
276static unsigned custom(struct svdir *s, char c)
277{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000278 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 int w;
280 char a[10];
281 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000282
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200283 if (s->islog)
284 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000285 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000286 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000287 if (stat(a, &st) == 0) {
288 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000289 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000290 if (pid == -1) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200291 warn2_cannot("vfork for ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 return 0;
293 }
Earl Chew80a34182009-08-02 02:23:27 +0200294 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000295 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000296 if (haslog && dup2(logpipe.wr, 1) == -1)
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200297 warn2_cannot("setup stdout for ", a);
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000298 execl(a, a, (char *) NULL);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200299 fatal2_cannot("run ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000300 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000301 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000302 if (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200303 warn2_cannot("wait for child ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000304 return 0;
305 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200306 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000307 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000308 } else {
309 if (errno != ENOENT)
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200310 warn2_cannot("stat ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000311 }
312 return 0;
313}
314
315static void stopservice(struct svdir *s)
316{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000317 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000318 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000319 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000320 update_status(s);
321 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200322 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000323 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000324 custom(s, 'd');
325 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000326 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200327 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000328 kill(s->pid, SIGCONT);
329 custom(s, 'x');
330 }
331}
332
333static void startservice(struct svdir *s)
334{
335 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200336 const char *arg[4];
337 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000338
Earl Chew80a34182009-08-02 02:23:27 +0200339 if (s->state == S_FINISH) {
340/* Two arguments are given to ./finish. The first one is ./run exit code,
341 * or -1 if ./run didnt exit normally. The second one is
342 * the least significant byte of the exit status as determined by waitpid;
343 * for instance it is 0 if ./run exited normally, and the signal number
344 * if ./run was terminated by a signal. If runsv cannot start ./run
345 * for some reason, the exit code is 111 and the status is 0.
346 */
347 arg[0] = "./finish";
348 arg[1] = "-1";
349 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200350 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200351 arg[1] = exitcode;
352 }
353 //arg[2] = "0";
354 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200355 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200356 //}
357 arg[3] = NULL;
358 } else {
359 arg[0] = "./run";
360 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000361 custom(s, 'u');
362 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000363
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000364 if (s->pid != 0)
365 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000366 while ((p = vfork()) == -1) {
367 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000368 sleep(5);
369 }
370 if (p == 0) {
371 /* child */
372 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000373 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000374 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000375 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000376 close(logpipe.wr);
377 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000378 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000379 close(logpipe.rd);
380 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000381 }
382 }
Denys Vlasenkod3e10902021-06-05 15:24:04 +0200383 /* Non-ignored signals revert to SIG_DFL on exec anyway.
384 * But we can get signals BEFORE execl(), this is unlikely
385 * but wouldn't be good...
386 */
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000387 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000388 + (1 << SIGCHLD)
389 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000390 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000391 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000392 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200393 execv(arg[0], (char**) arg);
394 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000395 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000396 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000397 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000398 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000399 s->state = S_RUN;
400 }
401 s->pid = p;
402 pidchanged = 1;
403 s->ctrl = C_NOOP;
404 update_status(s);
405}
406
407static int ctrl(struct svdir *s, char c)
408{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000409 int sig;
410
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000411 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000412 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200413 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000414 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200415 if (s->state == S_RUN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000416 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000417 break;
418 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200419 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000420 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200421 if (s->state == S_DOWN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000422 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000423 break;
424 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000425 if (s->islog)
426 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200427 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000428 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000429 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000430 case 't': /* sig term */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200431 if (s->state == S_RUN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000432 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000433 break;
434 case 'k': /* sig kill */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200435 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000436 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000437 s->state = S_DOWN;
438 break;
439 case 'p': /* sig pause */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200440 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000441 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000442 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000443 update_status(s);
444 break;
445 case 'c': /* sig cont */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200446 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000447 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000448 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000449 update_status(s);
450 break;
451 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200452 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000453 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200454 if (s->state == S_DOWN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000455 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000456 break;
457 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000458 sig = SIGALRM;
459 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000460 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000461 sig = SIGHUP;
462 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000463 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000464 sig = SIGINT;
465 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000466 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000467 sig = SIGQUIT;
468 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000469 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000470 sig = SIGUSR1;
471 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000472 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000473 sig = SIGUSR2;
474 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000475 }
476 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000477 sendsig:
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200478 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000479 kill(s->pid, sig);
480 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000481}
482
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200483static void open_control(const char *f, struct svdir *s)
484{
485 struct stat st;
486 mkfifo(f, 0600);
487 if (stat(f, &st) == -1)
488 fatal2_cannot("stat ", f);
489 if (!S_ISFIFO(st.st_mode))
490 bb_error_msg_and_die("%s: fatal: %s exists but is not a fifo", dir, f);
491 s->fdcontrol = xopen(f, O_RDONLY|O_NDELAY);
492 close_on_exec_on(s->fdcontrol);
493 s->fdcontrolwrite = xopen(f, O_WRONLY|O_NDELAY);
494 close_on_exec_on(s->fdcontrolwrite);
495 update_status(s);
496}
497
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000498int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000499int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000500{
501 struct stat s;
502 int fd;
503 int r;
504 char buf[256];
505
Denis Vlasenkob9256052007-09-28 10:29:17 +0000506 INIT_G();
507
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100508 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000509
Denis Vlasenko37188322008-02-16 13:20:56 +0000510 xpiped_pair(selfpipe);
511 close_on_exec_on(selfpipe.rd);
512 close_on_exec_on(selfpipe.wr);
513 ndelay_on(selfpipe.rd);
514 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000515
Denis Vlasenko8c783952007-01-27 22:21:52 +0000516 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000517 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000518 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000519 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000520
521 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000522 /* bss: svd[0].pid = 0; */
523 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
524 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200525 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000526 /* bss: svd[0].islog = 0; */
527 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000528 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200529 if (stat("down", &s) != -1)
530 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000531
532 if (stat("log", &s) == -1) {
533 if (errno != ENOENT)
534 warn_cannot("stat ./log");
535 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000536 if (!S_ISDIR(s.st_mode)) {
537 errno = 0;
538 warn_cannot("stat log/down: log is not a directory");
539 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000540 haslog = 1;
541 svd[1].state = S_DOWN;
542 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200543 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000544 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000545 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000546 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200547 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000548 xpiped_pair(logpipe);
549 close_on_exec_on(logpipe.rd);
550 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 }
552 }
553
554 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000555 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000556 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000557 if (r == sizeof(buf))
558 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000559 buf[r] = 0;
560 mkdir(buf, 0700);
561 } else {
562 if ((errno != ENOENT) && (errno != EINVAL))
563 fatal_cannot("readlink ./supervise");
564 }
565 }
566 svd[0].fdlock = xopen3("log/supervise/lock"+4,
567 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200568 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000569 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000570 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000571 if (haslog) {
572 if (mkdir("log/supervise", 0700) == -1) {
573 r = readlink("log/supervise", buf, 256);
574 if (r != -1) {
575 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000576 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000577 buf[r] = 0;
578 fd = xopen(".", O_RDONLY|O_NDELAY);
579 xchdir("./log");
580 mkdir(buf, 0700);
581 if (fchdir(fd) == -1)
582 fatal_cannot("change back to service directory");
583 close(fd);
584 }
585 else {
586 if ((errno != ENOENT) && (errno != EINVAL))
587 fatal_cannot("readlink ./log/supervise");
588 }
589 }
590 svd[1].fdlock = xopen3("log/supervise/lock",
591 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200592 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000593 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000594 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000595 }
596
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200597 open_control("log/supervise/control"+4, &svd[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000598 if (haslog) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200599 open_control("log/supervise/control", &svd[1]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000600 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000601 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000602 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000603 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000604 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000605 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000606 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000607 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000608 }
609 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000610 struct pollfd x[3];
611 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000612 char ch;
613
614 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200615 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000616 startservice(&svd[1]);
617 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200618 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000619 startservice(&svd[0]);
620
Denis Vlasenko37188322008-02-16 13:20:56 +0000621 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000622 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000623 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000624 x[1].events = POLLIN;
625 /* x[2] is used only if haslog == 1 */
626 x[2].fd = svd[1].fdcontrol;
627 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000628 sig_unblock(SIGTERM);
629 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000630 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000631 sig_block(SIGTERM);
632 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000633
Denis Vlasenko37188322008-02-16 13:20:56 +0000634 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000635 continue;
636
Denis Vlasenko04c63862006-11-17 18:58:49 +0000637 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000638 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000639 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000640
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000641 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000642 if (!child)
643 break;
644 if ((child == -1) && (errno != EINTR))
645 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000646 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200647 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000648 svd[0].pid = 0;
649 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200650 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000651 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200652 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000653 if (fd != -1) {
654 close(fd);
655 svd[0].state = S_FINISH;
656 update_status(&svd[0]);
657 continue;
658 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000659 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000660 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000661 deadline = svd[0].start.tv_sec + 1;
662 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000663 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000664 if (LESS(svd[0].start.tv_sec, deadline))
Denys Vlasenkoec16c032020-11-29 11:37:34 +0100665 sleep1();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000666 }
667 if (haslog) {
668 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200669 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000670 svd[1].pid = 0;
671 pidchanged = 1;
672 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000673 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000674 deadline = svd[1].start.tv_sec + 1;
675 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000676 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000677 if (LESS(svd[1].start.tv_sec, deadline))
Denys Vlasenkoec16c032020-11-29 11:37:34 +0100678 sleep1();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000679 }
680 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000681 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000682 if (read(svd[0].fdcontrol, &ch, 1) == 1)
683 ctrl(&svd[0], ch);
684 if (haslog)
685 if (read(svd[1].fdcontrol, &ch, 1) == 1)
686 ctrl(&svd[1], ch);
687
688 if (sigterm) {
689 ctrl(&svd[0], 'x');
690 sigterm = 0;
691 }
692
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200693 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000694 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000695 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200696 if (svd[1].sd_want != W_EXIT) {
697 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000698 /* stopservice(&svd[1]); */
699 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000700 close(logpipe.wr);
701 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000702 }
703 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000704 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000705 /* not reached */
706 return 0;
707}