blob: e93e25a4debabc77405fc049af971aabae1f2146 [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;
Denis Vlasenko04c63862006-11-17 18:58:49 +000087};
Denis Vlasenko04c63862006-11-17 18:58:49 +000088
Denis Vlasenkob9256052007-09-28 10:29:17 +000089struct globals {
90 smallint haslog;
91 smallint sigterm;
92 smallint pidchanged;
Denis Vlasenko37188322008-02-16 13:20:56 +000093 struct fd_pair selfpipe;
94 struct fd_pair logpipe;
Denis Vlasenkob9256052007-09-28 10:29:17 +000095 char *dir;
96 struct svdir svd[2];
97};
98#define G (*(struct globals*)&bb_common_bufsiz1)
99#define haslog (G.haslog )
100#define sigterm (G.sigterm )
101#define pidchanged (G.pidchanged )
102#define selfpipe (G.selfpipe )
103#define logpipe (G.logpipe )
104#define dir (G.dir )
105#define svd (G.svd )
Denis Vlasenko7049ff82008-06-25 09:53:17 +0000106#define INIT_G() do { \
107 pidchanged = 1; \
108} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000109
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000110static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000111{
112 bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
113 /* was exiting 111 */
114}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000115static void fatal_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000116{
117 fatal2_cannot(m, "");
118 /* was exiting 111 */
119}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000120static void fatal2x_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000121{
122 bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2);
123 /* was exiting 111 */
124}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000125static void warn_cannot(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000126{
127 bb_perror_msg("%s: warning: cannot %s", dir, m);
128}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000129
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000130static void s_child(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000131{
Denis Vlasenko37188322008-02-16 13:20:56 +0000132 write(selfpipe.wr, "", 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000133}
134
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000135static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000136{
137 sigterm = 1;
Denis Vlasenko37188322008-02-16 13:20:56 +0000138 write(selfpipe.wr, "", 1); /* XXX */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000139}
140
141static char *add_str(char *p, const char *to_add)
142{
143 while ((*p = *to_add) != '\0') {
144 p++;
145 to_add++;
146 }
147 return p;
148}
149
150static int open_trunc_or_warn(const char *name)
151{
152 int fd = open_trunc(name);
153 if (fd < 0)
154 bb_perror_msg("%s: warning: cannot open %s",
155 dir, name);
156 return fd;
157}
158
Denis Vlasenko04c63862006-11-17 18:58:49 +0000159static void update_status(struct svdir *s)
160{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000161 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000162 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000163 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000164
165 /* pid */
166 if (pidchanged) {
167 fd = open_trunc_or_warn("supervise/pid.new");
168 if (fd < 0)
169 return;
170 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000171 char spid[sizeof(int)*3 + 2];
172 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000173 write(fd, spid, size);
174 }
175 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000176 if (rename_or_warn("supervise/pid.new",
177 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000178 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000179 pidchanged = 0;
180 }
181
182 /* stat */
183 fd = open_trunc_or_warn("supervise/stat.new");
184 if (fd < -1)
185 return;
186
187 {
188 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
189 char *p = stat_buf;
190 switch (s->state) {
191 case S_DOWN:
192 p = add_str(p, "down");
193 break;
194 case S_RUN:
195 p = add_str(p, "run");
196 break;
197 case S_FINISH:
198 p = add_str(p, "finish");
199 break;
200 }
201 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
202 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
203 if (s->state != S_DOWN)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200204 switch (s->sd_want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000205 case W_DOWN:
206 p = add_str(p, ", want down");
207 break;
208 case W_EXIT:
209 p = add_str(p, ", want exit");
210 break;
211 }
212 *p++ = '\n';
213 write(fd, stat_buf, p - stat_buf);
214 close(fd);
215 }
216
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000217 rename_or_warn("supervise/stat.new",
218 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000219
220 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000221 memset(&status, 0, sizeof(status));
222 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
223 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
224 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000225 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000226 status.paused = 1;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200227 if (s->sd_want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000228 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000230 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000231 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000232 status.got_term = 1;
233 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000234 fd = open_trunc_or_warn("supervise/status.new");
235 if (fd < 0)
236 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000237 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000239 if (sz != sizeof(status)) {
240 warn_cannot("write supervise/status.new");
241 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000242 return;
243 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000244 rename_or_warn("supervise/status.new",
245 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000246}
247
248static unsigned custom(struct svdir *s, char c)
249{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000250 pid_t pid;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000251 int w;
252 char a[10];
253 struct stat st;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000254
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200255 if (s->islog)
256 return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000257 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000258 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259 if (stat(a, &st) == 0) {
260 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000261 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000262 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000263 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000264 return 0;
265 }
266 if (!pid) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000267 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000268 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000269 warn_cannot("setup stdout for control/?");
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000270 execl(a, a, (char *) NULL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000271 fatal_cannot("run control/?");
272 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000273 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000274 if (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275 warn_cannot("wait for child control/?");
276 return 0;
277 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200278 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000280 } else {
281 if (errno != ENOENT)
282 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000283 }
284 return 0;
285}
286
287static void stopservice(struct svdir *s)
288{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000289 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000290 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000291 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 update_status(s);
293 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200294 if (s->sd_want == W_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000295 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000296 custom(s, 'd');
297 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000298 }
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200299 if (s->sd_want == W_EXIT) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000300 kill(s->pid, SIGCONT);
301 custom(s, 'x');
302 }
303}
304
305static void startservice(struct svdir *s)
306{
307 int p;
Denis Vlasenko31773b72009-02-26 12:38:01 +0000308 const char *run;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000309
310 if (s->state == S_FINISH)
Denis Vlasenko31773b72009-02-26 12:38:01 +0000311 run = "./finish";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000312 else {
Denis Vlasenko31773b72009-02-26 12:38:01 +0000313 run = "./run";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314 custom(s, 'u');
315 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000316
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000317 if (s->pid != 0)
318 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000319 while ((p = vfork()) == -1) {
320 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000321 sleep(5);
322 }
323 if (p == 0) {
324 /* child */
325 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000326 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000327 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000328 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000329 close(logpipe.wr);
330 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000331 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000332 close(logpipe.rd);
333 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000334 }
335 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000336 /* Non-ignored signals revert to SIG_DFL on exec anyway */
337 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000338 + (1 << SIGCHLD)
339 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000340 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000341 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000342 sig_unblock(SIGTERM);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000343 execl(run, run, (char *) NULL);
344 fatal2_cannot(s->islog ? "start log/" : "start ", run);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000345 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000346 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000347 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000348 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000349 s->state = S_RUN;
350 }
351 s->pid = p;
352 pidchanged = 1;
353 s->ctrl = C_NOOP;
354 update_status(s);
355}
356
357static int ctrl(struct svdir *s, char c)
358{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000359 int sig;
360
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000361 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000362 case 'd': /* down */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200363 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000364 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000365 if (s->pid && s->state != S_FINISH)
366 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000367 break;
368 case 'u': /* up */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200369 s->sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000370 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000371 if (s->pid == 0)
372 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000373 break;
374 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000375 if (s->islog)
376 break;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200377 s->sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000378 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000379 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000380 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000381 if (s->pid && s->state != S_FINISH)
382 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000383 break;
384 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000385 if (s->pid && !custom(s, c))
386 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000387 s->state = S_DOWN;
388 break;
389 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000390 if (s->pid && !custom(s, c))
391 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000392 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000393 update_status(s);
394 break;
395 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000396 if (s->pid && !custom(s, c))
397 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000398 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000399 update_status(s);
400 break;
401 case 'o': /* once */
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200402 s->sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000403 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000404 if (!s->pid)
405 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000406 break;
407 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000408 sig = SIGALRM;
409 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000410 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000411 sig = SIGHUP;
412 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000413 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000414 sig = SIGINT;
415 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000416 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000417 sig = SIGQUIT;
418 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000419 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000420 sig = SIGUSR1;
421 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000422 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000423 sig = SIGUSR2;
424 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000425 }
426 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000427 sendsig:
428 if (s->pid && !custom(s, c))
429 kill(s->pid, sig);
430 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000431}
432
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000433int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000434int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000435{
436 struct stat s;
437 int fd;
438 int r;
439 char buf[256];
440
Denis Vlasenkob9256052007-09-28 10:29:17 +0000441 INIT_G();
442
Denis Vlasenko45946f82007-08-20 17:27:40 +0000443 if (!argv[1] || argv[2])
444 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000445 dir = argv[1];
446
Denis Vlasenko37188322008-02-16 13:20:56 +0000447 xpiped_pair(selfpipe);
448 close_on_exec_on(selfpipe.rd);
449 close_on_exec_on(selfpipe.wr);
450 ndelay_on(selfpipe.rd);
451 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000452
Denis Vlasenko8c783952007-01-27 22:21:52 +0000453 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000454 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000455 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000456 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457
458 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000459 /* bss: svd[0].pid = 0; */
460 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
461 if (C_NOOP) svd[0].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200462 if (W_UP) svd[0].sd_want = W_UP;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000463 /* bss: svd[0].islog = 0; */
464 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000465 gettimeofday_ns(&svd[0].start);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200466 if (stat("down", &s) != -1)
467 svd[0].sd_want = W_DOWN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000468
469 if (stat("log", &s) == -1) {
470 if (errno != ENOENT)
471 warn_cannot("stat ./log");
472 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000473 if (!S_ISDIR(s.st_mode)) {
474 errno = 0;
475 warn_cannot("stat log/down: log is not a directory");
476 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000477 haslog = 1;
478 svd[1].state = S_DOWN;
479 svd[1].ctrl = C_NOOP;
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200480 svd[1].sd_want = W_UP;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000481 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000482 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000483 if (stat("log/down", &s) != -1)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200484 svd[1].sd_want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000485 xpiped_pair(logpipe);
486 close_on_exec_on(logpipe.rd);
487 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000488 }
489 }
490
491 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000492 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000493 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000494 if (r == sizeof(buf))
495 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000496 buf[r] = 0;
497 mkdir(buf, 0700);
498 } else {
499 if ((errno != ENOENT) && (errno != EINVAL))
500 fatal_cannot("readlink ./supervise");
501 }
502 }
503 svd[0].fdlock = xopen3("log/supervise/lock"+4,
504 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
505 if (lock_exnb(svd[0].fdlock) == -1)
506 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000507 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000508 if (haslog) {
509 if (mkdir("log/supervise", 0700) == -1) {
510 r = readlink("log/supervise", buf, 256);
511 if (r != -1) {
512 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000513 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000514 buf[r] = 0;
515 fd = xopen(".", O_RDONLY|O_NDELAY);
516 xchdir("./log");
517 mkdir(buf, 0700);
518 if (fchdir(fd) == -1)
519 fatal_cannot("change back to service directory");
520 close(fd);
521 }
522 else {
523 if ((errno != ENOENT) && (errno != EINVAL))
524 fatal_cannot("readlink ./log/supervise");
525 }
526 }
527 svd[1].fdlock = xopen3("log/supervise/lock",
528 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
529 if (lock_ex(svd[1].fdlock) == -1)
530 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000531 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000532 }
533
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000534 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000535 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000536 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000537 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000538 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000539 update_status(&svd[0]);
540 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000541 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000542 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000543 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000544 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000545 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000546 update_status(&svd[1]);
547 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000548 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000549 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000550 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000552 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000554 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000555 }
556 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000557 struct pollfd x[3];
558 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000559 char ch;
560
561 if (haslog)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200562 if (!svd[1].pid && svd[1].sd_want == W_UP)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000563 startservice(&svd[1]);
564 if (!svd[0].pid)
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200565 if (svd[0].sd_want == W_UP || svd[0].state == S_FINISH)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 startservice(&svd[0]);
567
Denis Vlasenko37188322008-02-16 13:20:56 +0000568 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000569 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000570 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000571 x[1].events = POLLIN;
572 /* x[2] is used only if haslog == 1 */
573 x[2].fd = svd[1].fdcontrol;
574 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000575 sig_unblock(SIGTERM);
576 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000577 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000578 sig_block(SIGTERM);
579 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000580
Denis Vlasenko37188322008-02-16 13:20:56 +0000581 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000582 continue;
583
Denis Vlasenko04c63862006-11-17 18:58:49 +0000584 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000585 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000586 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000587
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000588 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000589 if (!child)
590 break;
591 if ((child == -1) && (errno != EINTR))
592 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000593 if (child == svd[0].pid) {
594 svd[0].pid = 0;
595 pidchanged = 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000596 svd[0].ctrl &=~ C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000597 if (svd[0].state != S_FINISH) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000598 fd = open_read("finish");
599 if (fd != -1) {
600 close(fd);
601 svd[0].state = S_FINISH;
602 update_status(&svd[0]);
603 continue;
604 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000605 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000606 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000607 deadline = svd[0].start.tv_sec + 1;
608 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000609 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000610 if (LESS(svd[0].start.tv_sec, deadline))
611 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000612 }
613 if (haslog) {
614 if (child == svd[1].pid) {
615 svd[1].pid = 0;
616 pidchanged = 1;
617 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000618 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000619 deadline = svd[1].start.tv_sec + 1;
620 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000621 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000622 if (LESS(svd[1].start.tv_sec, deadline))
623 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000624 }
625 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000626 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000627 if (read(svd[0].fdcontrol, &ch, 1) == 1)
628 ctrl(&svd[0], ch);
629 if (haslog)
630 if (read(svd[1].fdcontrol, &ch, 1) == 1)
631 ctrl(&svd[1], ch);
632
633 if (sigterm) {
634 ctrl(&svd[0], 'x');
635 sigterm = 0;
636 }
637
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200638 if (svd[0].sd_want == W_EXIT && svd[0].state == S_DOWN) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000639 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000640 _exit(EXIT_SUCCESS);
Denys Vlasenkoe46601d2009-07-15 00:21:49 +0200641 if (svd[1].sd_want != W_EXIT) {
642 svd[1].sd_want = W_EXIT;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000643 /* stopservice(&svd[1]); */
644 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000645 close(logpipe.wr);
646 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000647 }
648 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000649 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000650 /* not reached */
651 return 0;
652}