blob: 4dfdd3dc114d9ed7c6db973ec686b55f4eba0ccd [file] [log] [blame]
Denis Vlasenko8a164052007-03-12 23:34:52 +00001/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
Denis Vlasenkod18f52b2008-03-02 12:53:15 +000028/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denis Vlasenko04c63862006-11-17 18:58:49 +000029/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31#include <sys/poll.h>
32#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000033#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +000034#include "runit_lib.h"
35
Denis Vlasenko7bc53602007-08-31 21:45:52 +000036#if ENABLE_MONOTONIC_SYSCALL
37#include <sys/syscall.h>
38
39/* libc has incredibly messy way of doing this,
40 * typically requiring -lrt. We just skip all this mess */
41static void gettimeofday_ns(struct timespec *ts)
42{
43 syscall(__NR_clock_gettime, CLOCK_REALTIME, ts);
44}
45#else
46static void gettimeofday_ns(struct timespec *ts)
47{
48 if (sizeof(struct timeval) == sizeof(struct timespec)
49 && sizeof(((struct timeval*)ts)->tv_usec) == sizeof(ts->tv_nsec)
50 ) {
51 /* Cheat */
52 gettimeofday((void*)ts, NULL);
53 ts->tv_nsec *= 1000;
54 } else {
55 extern void BUG_need_to_implement_gettimeofday_ns(void);
56 BUG_need_to_implement_gettimeofday_ns();
57 }
58}
59#endif
60
61/* Compare possibly overflowing unsigned counters */
62#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
63
Denis Vlasenko04c63862006-11-17 18:58:49 +000064/* state */
65#define S_DOWN 0
66#define S_RUN 1
67#define S_FINISH 2
68/* ctrl */
69#define C_NOOP 0
70#define C_TERM 1
71#define C_PAUSE 2
72/* want */
73#define W_UP 0
74#define W_DOWN 1
75#define W_EXIT 2
76
77struct svdir {
78 int pid;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000079 smallint state;
80 smallint ctrl;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +020081 smallint sd_want;
Denis Vlasenko3aba6662007-03-09 22:46:06 +000082 smallint islog;
Denis Vlasenko45946f82007-08-20 17:27:40 +000083 struct timespec start;
Denis Vlasenko04c63862006-11-17 18:58:49 +000084 int fdlock;
85 int fdcontrol;
86 int fdcontrolwrite;
Earl Chew80a34182009-08-02 02:23:27 +020087 int wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +000088};
Denis Vlasenko04c63862006-11-17 18:58:49 +000089
Denis Vlasenkob9256052007-09-28 10:29:17 +000090struct globals {
91 smallint haslog;
92 smallint sigterm;
93 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +000094 struct fd_pair selfpipe;
95 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +000096 char *dir;
97 struct svdir svd[2];
98};
99#define G (*(struct globals*)&bb_common_bufsiz1)
100#define haslog (G.haslog )
101#define sigterm (G.sigterm )
102#define pidchanged (G.pidchanged )
103#define selfpipe (G.selfpipe )
104#define logpipe (G.logpipe )
105#define dir (G.dir )
106#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000107#define INIT_G() do { \
108 pidchanged = 1; \
109} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000110
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000111static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000112{
113 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
114 /* was exiting 111 */
115}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000116static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000117{
118 fatal2_cannot(m, "");
119 /* was exiting 111 */
120}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000121static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000122{
123 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
124 /* was exiting 111 */
125}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000126static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000127{
128 bb_perror_msg("%s: warning: cannot %s", dir, m);
129}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000130
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000131static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000132{
Denis Vlasenko37188322008-02-16 13:20:56 +0000133 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000134}
135
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000136static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000137{
138 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000139 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000140}
141
Earl Chew80a34182009-08-02 02:23:27 +0200142/* libbb candidate */
143static char *bb_stpcpy(char *p, const char *to_add)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000144{
145 while ((*p = *to_add) != '\0') {
146 p++;
147 to_add++;
148 }
149 return p;
150}
151
152static int open_trunc_or_warn(const char *name)
153{
154 int fd = open_trunc(name);
155 if (fd < 0)
156 bb_perror_msg("%s: warning: cannot open %s",
157 dir, name);
158 return fd;
159}
160
Denis Vlasenko04c63862006-11-17 18:58:49 +0000161static void update_status(struct svdir *s)
162{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000163 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000164 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000165 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000166
167 /* pid */
168 if (pidchanged) {
169 fd = open_trunc_or_warn("supervise/pid.new");
170 if (fd < 0)
171 return;
172 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000173 char spid[sizeof(int)*3 + 2];
174 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000175 write(fd, spid, size);
176 }
177 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000178 if (rename_or_warn("supervise/pid.new",
179 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000180 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000181 pidchanged = 0;
182 }
183
184 /* stat */
185 fd = open_trunc_or_warn("supervise/stat.new");
186 if (fd < -1)
187 return;
188
189 {
190 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
191 char *p = stat_buf;
192 switch (s->state) {
193 case S_DOWN:
Earl Chew80a34182009-08-02 02:23:27 +0200194 p = bb_stpcpy(p, "down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000195 break;
196 case S_RUN:
Earl Chew80a34182009-08-02 02:23:27 +0200197 p = bb_stpcpy(p, "run");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000198 break;
199 case S_FINISH:
Earl Chew80a34182009-08-02 02:23:27 +0200200 p = bb_stpcpy(p, "finish");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000201 break;
202 }
Earl Chew80a34182009-08-02 02:23:27 +0200203 if (s->ctrl & C_PAUSE)
204 p = bb_stpcpy(p, ", paused");
205 if (s->ctrl & C_TERM)
206 p = bb_stpcpy(p, ", got TERM");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000207 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200208 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000209 case W_DOWN:
Earl Chew80a34182009-08-02 02:23:27 +0200210 p = bb_stpcpy(p, ", want down");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000211 break;
212 case W_EXIT:
Earl Chew80a34182009-08-02 02:23:27 +0200213 p = bb_stpcpy(p, ", want exit");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000214 break;
215 }
216 *p++ = '\n';
217 write(fd, stat_buf, p - stat_buf);
218 close(fd);
219 }
220
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000221 rename_or_warn("supervise/stat.new",
222 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000223
224 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000225 memset(&status, 0, sizeof(status));
226 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
227 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
228 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000230 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200231 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000232 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000233 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000234 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000235 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000236 status.got_term = 1;
237 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 fd = open_trunc_or_warn("supervise/status.new");
239 if (fd < 0)
240 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000241 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000242 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000243 if (sz != sizeof(status)) {
244 warn_cannot("write supervise/status.new");
245 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000246 return;
247 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000248 rename_or_warn("supervise/status.new",
249 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000250}
251
252static unsigned custom(struct svdir *s, char c)
253{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000254 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000255 int w;
256 char a[10];
257 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200259 if (s->islog)
260 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000261 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000262 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 if (stat(a, &st) == 0) {
264 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000265 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000266 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000267 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000268 return 0;
269 }
Earl Chew80a34182009-08-02 02:23:27 +0200270 if (pid == 0) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000271 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000272 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000273 warn_cannot("setup stdout for control/?");
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000274 execl(a, a, (char *) NULL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275 fatal_cannot("run control/?");
276 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000277 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000278 if (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 warn_cannot("wait for child control/?");
280 return 0;
281 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200282 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000283 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000284 } else {
285 if (errno != ENOENT)
286 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000287 }
288 return 0;
289}
290
291static void stopservice(struct svdir *s)
292{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000293 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000295 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000296 update_status(s);
297 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200298 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000299 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000300 custom(s, 'd');
301 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000302 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200303 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000304 kill(s->pid, SIGCONT);
305 custom(s, 'x');
306 }
307}
308
309static void startservice(struct svdir *s)
310{
311 int p;
Earl Chew80a34182009-08-02 02:23:27 +0200312 const char *arg[4];
313 char exitcode[sizeof(int)*3 + 2];
314 char sigcode[sizeof(int)*3 + 2];
Denis Vlasenko04c63862006-11-17 18:58:49 +0000315
Earl Chew80a34182009-08-02 02:23:27 +0200316 if (s->state == S_FINISH) {
317/* Two arguments are given to ./finish. The first one is ./run exit code,
318 * or -1 if ./run didnt exit normally. The second one is
319 * the least significant byte of the exit status as determined by waitpid;
320 * for instance it is 0 if ./run exited normally, and the signal number
321 * if ./run was terminated by a signal. If runsv cannot start ./run
322 * for some reason, the exit code is 111 and the status is 0.
323 */
324 arg[0] = "./finish";
325 arg[1] = "-1";
326 if (WIFEXITED(s->wstat)) {
327 sprintf(exitcode, "%u", (int) WEXITSTATUS(s->wstat));
328 arg[1] = exitcode;
329 }
330 //arg[2] = "0";
331 //if (WIFSIGNALED(s->wstat)) {
332 sprintf(sigcode, "%u", (int) WTERMSIG(s->wstat));
333 arg[2] = sigcode;
334 //}
335 arg[3] = NULL;
336 } else {
337 arg[0] = "./run";
338 arg[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000339 custom(s, 'u');
340 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000341
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000342 if (s->pid != 0)
343 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000344 while ((p = vfork()) == -1) {
345 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000346 sleep(5);
347 }
348 if (p == 0) {
349 /* child */
350 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000351 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000352 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000353 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000354 close(logpipe.wr);
355 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000356 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000357 close(logpipe.rd);
358 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000359 }
360 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000361 /* Non-ignored signals revert to SIG_DFL on exec anyway */
362 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000363 + (1 << SIGCHLD)
364 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000365 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000366 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000367 sig_unblock(SIGTERM);
Earl Chew80a34182009-08-02 02:23:27 +0200368 execv(arg[0], (char**) arg);
369 fatal2_cannot(s->islog ? "start log/" : "start ", arg[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000370 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000371 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000372 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000373 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000374 s->state = S_RUN;
375 }
376 s->pid = p;
377 pidchanged = 1;
378 s->ctrl = C_NOOP;
379 update_status(s);
380}
381
382static int ctrl(struct svdir *s, char c)
383{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000384 int sig;
385
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000386 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000387 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200388 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000389 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000390 if (s->pid && s->state != S_FINISH)
391 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000392 break;
393 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200394 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000395 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000396 if (s->pid == 0)
397 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000398 break;
399 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000400 if (s->islog)
401 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200402 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000403 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000404 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000405 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000406 if (s->pid && s->state != S_FINISH)
407 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000408 break;
409 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000410 if (s->pid && !custom(s, c))
411 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000412 s->state = S_DOWN;
413 break;
414 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000415 if (s->pid && !custom(s, c))
416 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000417 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000418 update_status(s);
419 break;
420 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000421 if (s->pid && !custom(s, c))
422 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000423 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000424 update_status(s);
425 break;
426 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200427 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000428 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000429 if (!s->pid)
430 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000431 break;
432 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000433 sig = SIGALRM;
434 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000435 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000436 sig = SIGHUP;
437 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000438 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000439 sig = SIGINT;
440 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000441 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000442 sig = SIGQUIT;
443 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000444 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000445 sig = SIGUSR1;
446 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000447 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000448 sig = SIGUSR2;
449 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000450 }
451 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000452 sendsig:
453 if (s->pid && !custom(s, c))
454 kill(s->pid, sig);
455 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000456}
457
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000458int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000459int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000460{
461 struct stat s;
462 int fd;
463 int r;
464 char buf[256];
465
Denis Vlasenkob9256052007-09-28 10:29:17 +0000466 INIT_G();
467
Denis Vlasenko45946f82007-08-20 17:27:40 +0000468 if (!argv[1] || argv[2])
469 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000470 dir = argv[1];
471
Denis Vlasenko37188322008-02-16 13:20:56 +0000472 xpiped_pair(selfpipe);
473 close_on_exec_on(selfpipe.rd);
474 close_on_exec_on(selfpipe.wr);
475 ndelay_on(selfpipe.rd);
476 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000477
Denis Vlasenko8c783952007-01-27 22:21:52 +0000478 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000479 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000480 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000481 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000482
483 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000484 /* bss: svd[0].pid = 0; */
485 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
486 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200487 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000488 /* bss: svd[0].islog = 0; */
489 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000490 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200491 if (stat("down", &s) != -1)
492 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000493
494 if (stat("log", &s) == -1) {
495 if (errno != ENOENT)
496 warn_cannot("stat ./log");
497 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000498 if (!S_ISDIR(s.st_mode)) {
499 errno = 0;
500 warn_cannot("stat log/down: log is not a directory");
501 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000502 haslog = 1;
503 svd[1].state = S_DOWN;
504 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200505 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000506 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000507 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000508 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200509 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000510 xpiped_pair(logpipe);
511 close_on_exec_on(logpipe.rd);
512 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000513 }
514 }
515
516 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000517 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000518 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000519 if (r == sizeof(buf))
520 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000521 buf[r] = 0;
522 mkdir(buf, 0700);
523 } else {
524 if ((errno != ENOENT) && (errno != EINVAL))
525 fatal_cannot("readlink ./supervise");
526 }
527 }
528 svd[0].fdlock = xopen3("log/supervise/lock"+4,
529 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
530 if (lock_exnb(svd[0].fdlock) == -1)
531 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000532 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000533 if (haslog) {
534 if (mkdir("log/supervise", 0700) == -1) {
535 r = readlink("log/supervise", buf, 256);
536 if (r != -1) {
537 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000538 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000539 buf[r] = 0;
540 fd = xopen(".", O_RDONLY|O_NDELAY);
541 xchdir("./log");
542 mkdir(buf, 0700);
543 if (fchdir(fd) == -1)
544 fatal_cannot("change back to service directory");
545 close(fd);
546 }
547 else {
548 if ((errno != ENOENT) && (errno != EINVAL))
549 fatal_cannot("readlink ./log/supervise");
550 }
551 }
552 svd[1].fdlock = xopen3("log/supervise/lock",
553 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
554 if (lock_ex(svd[1].fdlock) == -1)
555 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000556 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000557 }
558
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000559 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000560 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000561 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000562 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000563 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000564 update_status(&svd[0]);
565 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000566 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000567 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000568 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000569 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000570 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000571 update_status(&svd[1]);
572 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000573 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000574 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000575 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000576 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000577 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000578 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000579 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000580 }
581 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000582 struct pollfd x[3];
583 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000584 char ch;
585
586 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200587 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000588 startservice(&svd[1]);
589 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200590 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000591 startservice(&svd[0]);
592
Denis Vlasenko37188322008-02-16 13:20:56 +0000593 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000594 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000595 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000596 x[1].events = POLLIN;
597 /* x[2] is used only if haslog == 1 */
598 x[2].fd = svd[1].fdcontrol;
599 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000600 sig_unblock(SIGTERM);
601 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000602 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000603 sig_block(SIGTERM);
604 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000605
Denis Vlasenko37188322008-02-16 13:20:56 +0000606 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000607 continue;
608
Denis Vlasenko04c63862006-11-17 18:58:49 +0000609 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000610 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000611 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000612
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000613 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000614 if (!child)
615 break;
616 if ((child == -1) && (errno != EINTR))
617 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000618 if (child == svd[0].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200619 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000620 svd[0].pid = 0;
621 pidchanged = 1;
Earl Chew80a34182009-08-02 02:23:27 +0200622 svd[0].ctrl &= ~C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000623 if (svd[0].state != S_FINISH) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000624 fd = open_read("finish");
625 if (fd != -1) {
626 close(fd);
627 svd[0].state = S_FINISH;
628 update_status(&svd[0]);
629 continue;
630 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000631 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000632 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000633 deadline = svd[0].start.tv_sec + 1;
634 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000635 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000636 if (LESS(svd[0].start.tv_sec, deadline))
637 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000638 }
639 if (haslog) {
640 if (child == svd[1].pid) {
Earl Chew80a34182009-08-02 02:23:27 +0200641 svd[0].wstat = wstat;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000642 svd[1].pid = 0;
643 pidchanged = 1;
644 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000645 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000646 deadline = svd[1].start.tv_sec + 1;
647 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000648 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000649 if (LESS(svd[1].start.tv_sec, deadline))
650 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000651 }
652 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000653 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000654 if (read(svd[0].fdcontrol, &ch, 1) == 1)
655 ctrl(&svd[0], ch);
656 if (haslog)
657 if (read(svd[1].fdcontrol, &ch, 1) == 1)
658 ctrl(&svd[1], ch);
659
660 if (sigterm) {
661 ctrl(&svd[0], 'x');
662 sigterm = 0;
663 }
664
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200665 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000666 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000667 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200668 if (svd[1].sd_want != W_EXIT) {
669 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000670 /* stopservice(&svd[1]); */
671 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000672 close(logpipe.wr);
673 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000674 }
675 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000676 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000677 /* not reached */
678 return 0;
679}