blob: 6ad6bf46ecf53cd2828249201432fd5e66e67d27 [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
Denys Vlasenko5a72b0c2021-06-05 16:36:21 +0200152/* SIGCHLD/TERM handler is reentrancy-safe because they are unmasked
Denys Vlasenko5dadd492021-06-05 16:20:05 +0200153 * only over poll() call, not over memory allocations
154 * or printouts. Do not need to save/restore errno either,
155 * as poll() error is not checked there.
156 */
Denys Vlasenko5a72b0c2021-06-05 16:36:21 +0200157static void s_chld_term(int sig_no)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000158{
Denys Vlasenko5a72b0c2021-06-05 16:36:21 +0200159 if (sig_no == SIGTERM)
160 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000161 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000162}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000163
Denis Vlasenko04c63862006-11-17 18:58:49 +0000164static int open_trunc_or_warn(const char *name)
165{
Denys Vlasenkoe09bff32010-09-05 19:28:29 +0200166 /* Why O_NDELAY? */
167 int fd = open(name, O_WRONLY | O_NDELAY | O_TRUNC | O_CREAT, 0644);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000168 if (fd < 0)
169 bb_perror_msg("%s: warning: cannot open %s",
170 dir, name);
171 return fd;
172}
173
Denis Vlasenko04c63862006-11-17 18:58:49 +0000174static void update_status(struct svdir *s)
175{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000176 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000177 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000178 svstatus_t status;
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200179 const char *fstatus ="log/supervise/status";
180 const char *fstatusnew ="log/supervise/status.new";
181 const char *f_stat ="log/supervise/stat";
182 const char *fstatnew ="log/supervise/stat.new";
183 const char *fpid ="log/supervise/pid";
184 const char *fpidnew ="log/supervise/pid.new";
185
186 if (!s->islog) {
187 fstatus += 4;
188 fstatusnew += 4;
189 f_stat += 4;
190 fstatnew += 4;
191 fpid += 4;
192 fpidnew += 4;
193 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000194
195 /* pid */
196 if (pidchanged) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200197 fd = open_trunc_or_warn(fpidnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000198 if (fd < 0)
199 return;
200 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000201 char spid[sizeof(int)*3 + 2];
202 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000203 write(fd, spid, size);
204 }
205 close(fd);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200206 if (rename_or_warn(fpidnew, fpid))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000207 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000208 pidchanged = 0;
209 }
210
211 /* stat */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200212 fd = open_trunc_or_warn(fstatnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000213 if (fd < -1)
214 return;
215
216 {
217 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
218 char *p = stat_buf;
219 switch (s->state) {
220 case S_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800221 p = stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000222 break;
223 case S_RUN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800224 p = stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000225 break;
226 case S_FINISH:
Dan Fandrichdc506762011-02-12 22:26:57 -0800227 p = stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000228 break;
229 }
Earl Chew80a34182009-08-02 02:23:27 +0200230 if (s->ctrl & C_PAUSE)
Dan Fandrichdc506762011-02-12 22:26:57 -0800231 p = stpcpy(p, ", paused");
Earl Chew80a34182009-08-02 02:23:27 +0200232 if (s->ctrl & C_TERM)
Dan Fandrichdc506762011-02-12 22:26:57 -0800233 p = stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000234 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200235 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000236 case W_DOWN:
Dan Fandrichdc506762011-02-12 22:26:57 -0800237 p = stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 break;
239 case W_EXIT:
Dan Fandrichdc506762011-02-12 22:26:57 -0800240 p = stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000241 break;
242 }
243 *p++ = '\n';
244 write(fd, stat_buf, p - stat_buf);
245 close(fd);
246 }
247
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200248 rename_or_warn(fstatnew, f_stat);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000249
250 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000251 memset(&status, 0, sizeof(status));
252 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
253 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
254 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000255 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000256 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200257 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000258 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000260 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000261 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000262 status.got_term = 1;
263 status.run_or_finish = s->state;
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200264 fd = open_trunc_or_warn(fstatusnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000265 if (fd < 0)
266 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000267 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000268 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000269 if (sz != sizeof(status)) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200270 warn2_cannot("write ", fstatusnew);
271 unlink(fstatusnew);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000272 return;
273 }
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200274 rename_or_warn(fstatusnew, fstatus);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275}
276
277static unsigned custom(struct svdir *s, char c)
278{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000279 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000280 int w;
281 char a[10];
282 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000283
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200284 if (s->islog)
285 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000286 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000287 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000288 if (stat(a, &st) == 0) {
289 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000290 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000291 if (pid == -1) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200292 warn2_cannot("vfork for ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000293 return 0;
294 }
Earl Chew80a34182009-08-02 02:23:27 +0200295 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000296 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000297 if (haslog && dup2(logpipe.wr, 1) == -1)
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200298 warn2_cannot("setup stdout for ", a);
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000299 execl(a, a, (char *) NULL);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200300 fatal2_cannot("run ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000301 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000302 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000303 if (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200304 warn2_cannot("wait for child ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000305 return 0;
306 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200307 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000308 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000309 } else {
310 if (errno != ENOENT)
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200311 warn2_cannot("stat ", a);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000312 }
313 return 0;
314}
315
316static void stopservice(struct svdir *s)
317{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000318 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000319 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000320 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000321 update_status(s);
322 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200323 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000324 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000325 custom(s, 'd');
326 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000327 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200328 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000329 kill(s->pid, SIGCONT);
330 custom(s, 'x');
331 }
332}
333
334static void startservice(struct svdir *s)
335{
336 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200337 const char *arg[4];
338 char exitcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000339
Earl Chew80a34182009-08-02 02:23:27 +0200340 if (s->state == S_FINISH) {
341/* Two arguments are given to ./finish. The first one is ./run exit code,
342 * or -1 if ./run didnt exit normally. The second one is
343 * the least significant byte of the exit status as determined by waitpid;
344 * for instance it is 0 if ./run exited normally, and the signal number
345 * if ./run was terminated by a signal. If runsv cannot start ./run
346 * for some reason, the exit code is 111 and the status is 0.
347 */
348 arg[0] = "./finish";
349 arg[1] = "-1";
350 if (WIFEXITED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200351 *utoa_to_buf(WEXITSTATUS(s->wstat), exitcode, sizeof(exitcode)) = '\0';
Earl Chew80a34182009-08-02 02:23:27 +0200352 arg[1] = exitcode;
353 }
354 //arg[2] = "0";
355 //if (WIFSIGNALED(s->wstat)) {
Denys Vlasenko7bb346f2009-10-06 22:09:50 +0200356 arg[2] = utoa(WTERMSIG(s->wstat));
Earl Chew80a34182009-08-02 02:23:27 +0200357 //}
358 arg[3] = NULL;
359 } else {
360 arg[0] = "./run";
361 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000362 custom(s, 'u');
363 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000364
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000365 if (s->pid != 0)
366 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000367 while ((p = vfork()) == -1) {
368 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000369 sleep(5);
370 }
371 if (p == 0) {
372 /* child */
373 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000374 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000375 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000376 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000377 close(logpipe.wr);
378 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000379 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000380 close(logpipe.rd);
381 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000382 }
383 }
Denys Vlasenko5dadd492021-06-05 16:20:05 +0200384 /* Non-ignored signals revert to SIG_DFL on exec.
385 * But we can get signals BEFORE execl(), unlikely as that may be.
386 * SIGCHLD is safe (would merely write to selfpipe),
387 * but SIGTERM would set sigterm = 1 (with vfork, we affect parent).
388 * Avoid that.
Denys Vlasenkod3e10902021-06-05 15:24:04 +0200389 */
Denys Vlasenko5dadd492021-06-05 16:20:05 +0200390 /*signal(SIGCHLD, SIG_DFL);*/
391 signal(SIGTERM, SIG_DFL);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000392 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000393 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200394 execv(arg[0], (char**) arg);
395 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000396 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000397 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000398 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000399 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000400 s->state = S_RUN;
401 }
402 s->pid = p;
403 pidchanged = 1;
404 s->ctrl = C_NOOP;
405 update_status(s);
406}
407
408static int ctrl(struct svdir *s, char c)
409{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000410 int sig;
411
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000412 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000413 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200414 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000415 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200416 if (s->state == S_RUN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000417 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000418 break;
419 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200420 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000421 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200422 if (s->state == S_DOWN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000423 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000424 break;
425 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000426 if (s->islog)
427 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200428 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000429 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000430 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000431 case 't': /* sig term */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200432 if (s->state == S_RUN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000433 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000434 break;
435 case 'k': /* sig kill */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200436 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000437 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000438 s->state = S_DOWN;
439 break;
440 case 'p': /* sig pause */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200441 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000442 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000443 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000444 update_status(s);
445 break;
446 case 'c': /* sig cont */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200447 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000448 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000449 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000450 update_status(s);
451 break;
452 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200453 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000454 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200455 if (s->state == S_DOWN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000456 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457 break;
458 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000459 sig = SIGALRM;
460 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000461 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000462 sig = SIGHUP;
463 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000464 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000465 sig = SIGINT;
466 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000467 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000468 sig = SIGQUIT;
469 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000470 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000471 sig = SIGUSR1;
472 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000473 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000474 sig = SIGUSR2;
475 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000476 }
477 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000478 sendsig:
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200479 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000480 kill(s->pid, sig);
481 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000482}
483
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200484static void open_control(const char *f, struct svdir *s)
485{
486 struct stat st;
487 mkfifo(f, 0600);
488 if (stat(f, &st) == -1)
489 fatal2_cannot("stat ", f);
490 if (!S_ISFIFO(st.st_mode))
491 bb_error_msg_and_die("%s: fatal: %s exists but is not a fifo", dir, f);
492 s->fdcontrol = xopen(f, O_RDONLY|O_NDELAY);
493 close_on_exec_on(s->fdcontrol);
494 s->fdcontrolwrite = xopen(f, O_WRONLY|O_NDELAY);
495 close_on_exec_on(s->fdcontrolwrite);
496 update_status(s);
497}
498
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000499int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000500int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000501{
502 struct stat s;
503 int fd;
504 int r;
505 char buf[256];
506
Denis Vlasenkob9256052007-09-28 10:29:17 +0000507 INIT_G();
508
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100509 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000510
Denis Vlasenko37188322008-02-16 13:20:56 +0000511 xpiped_pair(selfpipe);
512 close_on_exec_on(selfpipe.rd);
513 close_on_exec_on(selfpipe.wr);
514 ndelay_on(selfpipe.rd);
515 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000516
Denis Vlasenko8c783952007-01-27 22:21:52 +0000517 sig_block(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000518 sig_block(SIGTERM);
Denys Vlasenko5dadd492021-06-05 16:20:05 +0200519 /* No particular reason why we don't set SA_RESTART
520 * (poll() wouldn't restart regardless of that flag),
521 * we just follow what runit-2.1.2 does:
522 */
Denys Vlasenko5a72b0c2021-06-05 16:36:21 +0200523 bb_signals_norestart(0
524 + (1 << SIGCHLD)
525 + (1 << SIGTERM)
526 , s_chld_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000527
528 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000529 /* bss: svd[0].pid = 0; */
530 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
531 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200532 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000533 /* bss: svd[0].islog = 0; */
534 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000535 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200536 if (stat("down", &s) != -1)
537 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000538
539 if (stat("log", &s) == -1) {
540 if (errno != ENOENT)
541 warn_cannot("stat ./log");
542 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000543 if (!S_ISDIR(s.st_mode)) {
544 errno = 0;
545 warn_cannot("stat log/down: log is not a directory");
546 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000547 haslog = 1;
548 svd[1].state = S_DOWN;
549 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200550 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000552 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200554 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000555 xpiped_pair(logpipe);
556 close_on_exec_on(logpipe.rd);
557 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000558 }
559 }
560
561 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000562 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000563 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000564 if (r == sizeof(buf))
565 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 buf[r] = 0;
567 mkdir(buf, 0700);
568 } else {
569 if ((errno != ENOENT) && (errno != EINVAL))
570 fatal_cannot("readlink ./supervise");
571 }
572 }
573 svd[0].fdlock = xopen3("log/supervise/lock"+4,
574 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200575 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000576 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000577 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000578 if (haslog) {
579 if (mkdir("log/supervise", 0700) == -1) {
580 r = readlink("log/supervise", buf, 256);
581 if (r != -1) {
582 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000583 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000584 buf[r] = 0;
585 fd = xopen(".", O_RDONLY|O_NDELAY);
586 xchdir("./log");
587 mkdir(buf, 0700);
588 if (fchdir(fd) == -1)
589 fatal_cannot("change back to service directory");
590 close(fd);
591 }
592 else {
593 if ((errno != ENOENT) && (errno != EINVAL))
594 fatal_cannot("readlink ./log/supervise");
595 }
596 }
597 svd[1].fdlock = xopen3("log/supervise/lock",
598 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200599 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000600 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000601 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000602 }
603
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200604 open_control("log/supervise/control"+4, &svd[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000605 if (haslog) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200606 open_control("log/supervise/control", &svd[1]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000607 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000608 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000609 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000610 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000611 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000612 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000613 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000614 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000615 }
616 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000617 struct pollfd x[3];
618 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000619 char ch;
620
621 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200622 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000623 startservice(&svd[1]);
624 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200625 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000626 startservice(&svd[0]);
627
Denis Vlasenko37188322008-02-16 13:20:56 +0000628 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000629 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000630 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000631 x[1].events = POLLIN;
632 /* x[2] is used only if haslog == 1 */
633 x[2].fd = svd[1].fdcontrol;
634 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000635 sig_unblock(SIGTERM);
636 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000637 poll(x, 2 + haslog, 3600*1000);
Denys Vlasenko5dadd492021-06-05 16:20:05 +0200638 /* NB: signal handlers can trash errno of poll() */
Denis Vlasenko8c783952007-01-27 22:21:52 +0000639 sig_block(SIGTERM);
640 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000641
Denis Vlasenko37188322008-02-16 13:20:56 +0000642 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000643 continue;
644
Denis Vlasenko04c63862006-11-17 18:58:49 +0000645 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000646 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000647 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000648
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000649 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000650 if (!child)
651 break;
652 if ((child == -1) && (errno != EINTR))
653 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000654 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200655 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000656 svd[0].pid = 0;
657 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200658 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000659 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200660 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000661 if (fd != -1) {
662 close(fd);
663 svd[0].state = S_FINISH;
664 update_status(&svd[0]);
665 continue;
666 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000667 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000668 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000669 deadline = svd[0].start.tv_sec + 1;
670 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000671 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000672 if (LESS(svd[0].start.tv_sec, deadline))
Denys Vlasenkoec16c032020-11-29 11:37:34 +0100673 sleep1();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000674 }
675 if (haslog) {
676 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200677 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000678 svd[1].pid = 0;
679 pidchanged = 1;
680 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000681 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000682 deadline = svd[1].start.tv_sec + 1;
683 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000684 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000685 if (LESS(svd[1].start.tv_sec, deadline))
Denys Vlasenkoec16c032020-11-29 11:37:34 +0100686 sleep1();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000687 }
688 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000689 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000690 if (read(svd[0].fdcontrol, &ch, 1) == 1)
691 ctrl(&svd[0], ch);
692 if (haslog)
693 if (read(svd[1].fdcontrol, &ch, 1) == 1)
694 ctrl(&svd[1], ch);
695
696 if (sigterm) {
697 ctrl(&svd[0], 'x');
698 sigterm = 0;
699 }
700
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200701 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000702 if (svd[1].pid == 0)
Denys Vlasenkodb5546c2022-01-05 22:16:06 +0100703 _exit_SUCCESS();
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200704 if (svd[1].sd_want != W_EXIT) {
705 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000706 /* stopservice(&svd[1]); */
707 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000708 close(logpipe.wr);
709 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000710 }
711 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000712 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000713 /* not reached */
714 return 0;
715}