blob: ecab8cdf5ec9241d6d03f9355ec9bae845a6abbc [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 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000383 /* Non-ignored signals revert to SIG_DFL on exec anyway */
384 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000385 + (1 << SIGCHLD)
386 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000387 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000388 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000389 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200390 execv(arg[0], (char**) arg);
391 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000392 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000393 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000394 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000395 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000396 s->state = S_RUN;
397 }
398 s->pid = p;
399 pidchanged = 1;
400 s->ctrl = C_NOOP;
401 update_status(s);
402}
403
404static int ctrl(struct svdir *s, char c)
405{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000406 int sig;
407
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000408 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000409 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200410 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000411 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200412 if (s->state == S_RUN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000413 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000414 break;
415 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200416 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000417 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200418 if (s->state == S_DOWN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000419 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000420 break;
421 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000422 if (s->islog)
423 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200424 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000425 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000426 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000427 case 't': /* sig term */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200428 if (s->state == S_RUN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000429 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000430 break;
431 case 'k': /* sig kill */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200432 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000433 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000434 s->state = S_DOWN;
435 break;
436 case 'p': /* sig pause */
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, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000439 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000440 update_status(s);
441 break;
442 case 'c': /* sig cont */
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200443 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000444 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000445 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000446 update_status(s);
447 break;
448 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200449 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000450 update_status(s);
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200451 if (s->state == S_DOWN)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000452 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000453 break;
454 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000455 sig = SIGALRM;
456 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000458 sig = SIGHUP;
459 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000460 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000461 sig = SIGINT;
462 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000463 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000464 sig = SIGQUIT;
465 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000466 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000467 sig = SIGUSR1;
468 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000469 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000470 sig = SIGUSR2;
471 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000472 }
473 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000474 sendsig:
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200475 if ((s->state == S_RUN) && !custom(s, c))
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000476 kill(s->pid, sig);
477 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000478}
479
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200480static void open_control(const char *f, struct svdir *s)
481{
482 struct stat st;
483 mkfifo(f, 0600);
484 if (stat(f, &st) == -1)
485 fatal2_cannot("stat ", f);
486 if (!S_ISFIFO(st.st_mode))
487 bb_error_msg_and_die("%s: fatal: %s exists but is not a fifo", dir, f);
488 s->fdcontrol = xopen(f, O_RDONLY|O_NDELAY);
489 close_on_exec_on(s->fdcontrol);
490 s->fdcontrolwrite = xopen(f, O_WRONLY|O_NDELAY);
491 close_on_exec_on(s->fdcontrolwrite);
492 update_status(s);
493}
494
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000495int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000496int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000497{
498 struct stat s;
499 int fd;
500 int r;
501 char buf[256];
502
Denis Vlasenkob9256052007-09-28 10:29:17 +0000503 INIT_G();
504
Denys Vlasenkoe992bae2009-11-28 15:18:53 +0100505 dir = single_argv(argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000506
Denis Vlasenko37188322008-02-16 13:20:56 +0000507 xpiped_pair(selfpipe);
508 close_on_exec_on(selfpipe.rd);
509 close_on_exec_on(selfpipe.wr);
510 ndelay_on(selfpipe.rd);
511 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000512
Denis Vlasenko8c783952007-01-27 22:21:52 +0000513 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000514 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000515 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000516 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000517
518 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000519 /* bss: svd[0].pid = 0; */
520 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
521 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200522 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000523 /* bss: svd[0].islog = 0; */
524 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000525 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200526 if (stat("down", &s) != -1)
527 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000528
529 if (stat("log", &s) == -1) {
530 if (errno != ENOENT)
531 warn_cannot("stat ./log");
532 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000533 if (!S_ISDIR(s.st_mode)) {
534 errno = 0;
535 warn_cannot("stat log/down: log is not a directory");
536 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000537 haslog = 1;
538 svd[1].state = S_DOWN;
539 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200540 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000541 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000542 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000543 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200544 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000545 xpiped_pair(logpipe);
546 close_on_exec_on(logpipe.rd);
547 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000548 }
549 }
550
551 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000552 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000554 if (r == sizeof(buf))
555 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000556 buf[r] = 0;
557 mkdir(buf, 0700);
558 } else {
559 if ((errno != ENOENT) && (errno != EINVAL))
560 fatal_cannot("readlink ./supervise");
561 }
562 }
563 svd[0].fdlock = xopen3("log/supervise/lock"+4,
564 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200565 if (flock(svd[0].fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000567 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000568 if (haslog) {
569 if (mkdir("log/supervise", 0700) == -1) {
570 r = readlink("log/supervise", buf, 256);
571 if (r != -1) {
572 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000573 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000574 buf[r] = 0;
575 fd = xopen(".", O_RDONLY|O_NDELAY);
576 xchdir("./log");
577 mkdir(buf, 0700);
578 if (fchdir(fd) == -1)
579 fatal_cannot("change back to service directory");
580 close(fd);
581 }
582 else {
583 if ((errno != ENOENT) && (errno != EINVAL))
584 fatal_cannot("readlink ./log/supervise");
585 }
586 }
587 svd[1].fdlock = xopen3("log/supervise/lock",
588 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200589 if (flock(svd[1].fdlock, LOCK_EX) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000590 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000591 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000592 }
593
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200594 open_control("log/supervise/control"+4, &svd[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000595 if (haslog) {
Denys Vlasenkobcb57642017-05-15 19:44:48 +0200596 open_control("log/supervise/control", &svd[1]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000597 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000598 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000599 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000600 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000601 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000602 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000603 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000604 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000605 }
606 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000607 struct pollfd x[3];
608 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000609 char ch;
610
611 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200612 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000613 startservice(&svd[1]);
614 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200615 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000616 startservice(&svd[0]);
617
Denis Vlasenko37188322008-02-16 13:20:56 +0000618 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000619 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000620 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000621 x[1].events = POLLIN;
622 /* x[2] is used only if haslog == 1 */
623 x[2].fd = svd[1].fdcontrol;
624 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000625 sig_unblock(SIGTERM);
626 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000627 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000628 sig_block(SIGTERM);
629 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000630
Denis Vlasenko37188322008-02-16 13:20:56 +0000631 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000632 continue;
633
Denis Vlasenko04c63862006-11-17 18:58:49 +0000634 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000635 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000636 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000637
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000638 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000639 if (!child)
640 break;
641 if ((child == -1) && (errno != EINTR))
642 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000643 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200644 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000645 svd[0].pid = 0;
646 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200647 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000648 if (svd[0].state != S_FINISH) {
Denys Vlasenko05e86052010-10-13 12:53:27 +0200649 fd = open("finish", O_RDONLY|O_NDELAY);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000650 if (fd != -1) {
651 close(fd);
652 svd[0].state = S_FINISH;
653 update_status(&svd[0]);
654 continue;
655 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000656 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000657 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000658 deadline = svd[0].start.tv_sec + 1;
659 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000660 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000661 if (LESS(svd[0].start.tv_sec, deadline))
Denys Vlasenkoec16c032020-11-29 11:37:34 +0100662 sleep1();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000663 }
664 if (haslog) {
665 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200666 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000667 svd[1].pid = 0;
668 pidchanged = 1;
669 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000670 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000671 deadline = svd[1].start.tv_sec + 1;
672 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000673 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000674 if (LESS(svd[1].start.tv_sec, deadline))
Denys Vlasenkoec16c032020-11-29 11:37:34 +0100675 sleep1();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000676 }
677 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000678 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000679 if (read(svd[0].fdcontrol, &ch, 1) == 1)
680 ctrl(&svd[0], ch);
681 if (haslog)
682 if (read(svd[1].fdcontrol, &ch, 1) == 1)
683 ctrl(&svd[1], ch);
684
685 if (sigterm) {
686 ctrl(&svd[0], 'x');
687 sigterm = 0;
688 }
689
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200690 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000691 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000692 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200693 if (svd[1].sd_want != W_EXIT) {
694 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000695 /* stopservice(&svd[1]); */
696 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000697 close(logpipe.wr);
698 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000699 }
700 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000701 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000702 /* not reached */
703 return 0;
704}