blob: 6d34dc133005de7a4b6022327d5d6deb6f6ad3fc [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;
81 smallint want;
82 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)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000204 switch (s->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;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000227 if (s->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
255 if (s->islog) return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000256 strcpy(a, "control/?");
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000257 a[8] = c; /* replace '?' */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258 if (stat(a, &st) == 0) {
259 if (st.st_mode & S_IXUSR) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000260 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000261 if (pid == -1) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000262 warn_cannot("vfork for control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 return 0;
264 }
265 if (!pid) {
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000266 /* child */
Denis Vlasenko37188322008-02-16 13:20:56 +0000267 if (haslog && dup2(logpipe.wr, 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000268 warn_cannot("setup stdout for control/?");
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000269 execl(a, a, (char *) NULL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000270 fatal_cannot("run control/?");
271 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000272 /* parent */
Denis Vlasenkof09f4e02009-02-26 12:29:59 +0000273 if (safe_waitpid(pid, &w, 0) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274 warn_cannot("wait for child control/?");
275 return 0;
276 }
277 return !wait_exitcode(w);
278 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000279 } else {
280 if (errno != ENOENT)
281 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000282 }
283 return 0;
284}
285
286static void stopservice(struct svdir *s)
287{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000288 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000289 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000290 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000291 update_status(s);
292 }
293 if (s->want == W_DOWN) {
294 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000295 custom(s, 'd');
296 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000297 }
298 if (s->want == W_EXIT) {
299 kill(s->pid, SIGCONT);
300 custom(s, 'x');
301 }
302}
303
304static void startservice(struct svdir *s)
305{
306 int p;
Denis Vlasenko31773b72009-02-26 12:38:01 +0000307 const char *run;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000308
309 if (s->state == S_FINISH)
Denis Vlasenko31773b72009-02-26 12:38:01 +0000310 run = "./finish";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000311 else {
Denis Vlasenko31773b72009-02-26 12:38:01 +0000312 run = "./run";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000313 custom(s, 'u');
314 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000315
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000316 if (s->pid != 0)
317 stopservice(s); /* should never happen */
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000318 while ((p = vfork()) == -1) {
319 warn_cannot("vfork, sleeping");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000320 sleep(5);
321 }
322 if (p == 0) {
323 /* child */
324 if (haslog) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000325 /* NB: bug alert! right order is close, then dup2 */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000326 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000327 xchdir("./log");
Denis Vlasenko37188322008-02-16 13:20:56 +0000328 close(logpipe.wr);
329 xdup2(logpipe.rd, 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000330 } else {
Denis Vlasenko37188322008-02-16 13:20:56 +0000331 close(logpipe.rd);
332 xdup2(logpipe.wr, 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000333 }
334 }
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000335 /* Non-ignored signals revert to SIG_DFL on exec anyway */
336 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000337 + (1 << SIGCHLD)
338 + (1 << SIGTERM)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000339 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000340 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000341 sig_unblock(SIGTERM);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000342 execl(run, run, (char *) NULL);
343 fatal2_cannot(s->islog ? "start log/" : "start ", run);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000344 }
Denis Vlasenko592d4fe2008-03-17 09:19:26 +0000345 /* parent */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000346 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000347 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000348 s->state = S_RUN;
349 }
350 s->pid = p;
351 pidchanged = 1;
352 s->ctrl = C_NOOP;
353 update_status(s);
354}
355
356static int ctrl(struct svdir *s, char c)
357{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000358 int sig;
359
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000360 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000361 case 'd': /* down */
362 s->want = W_DOWN;
363 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000364 if (s->pid && s->state != S_FINISH)
365 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000366 break;
367 case 'u': /* up */
368 s->want = W_UP;
369 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000370 if (s->pid == 0)
371 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000372 break;
373 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000374 if (s->islog)
375 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000376 s->want = W_EXIT;
377 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000378 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000379 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000380 if (s->pid && s->state != S_FINISH)
381 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000382 break;
383 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000384 if (s->pid && !custom(s, c))
385 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000386 s->state = S_DOWN;
387 break;
388 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000389 if (s->pid && !custom(s, c))
390 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000391 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000392 update_status(s);
393 break;
394 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000395 if (s->pid && !custom(s, c))
396 kill(s->pid, SIGCONT);
Denis Vlasenko31773b72009-02-26 12:38:01 +0000397 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000398 update_status(s);
399 break;
400 case 'o': /* once */
401 s->want = W_DOWN;
402 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000403 if (!s->pid)
404 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000405 break;
406 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000407 sig = SIGALRM;
408 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000409 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000410 sig = SIGHUP;
411 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000412 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000413 sig = SIGINT;
414 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000415 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000416 sig = SIGQUIT;
417 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000418 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000419 sig = SIGUSR1;
420 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000421 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000422 sig = SIGUSR2;
423 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000424 }
425 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000426 sendsig:
427 if (s->pid && !custom(s, c))
428 kill(s->pid, sig);
429 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000430}
431
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000432int runsv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000433int runsv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000434{
435 struct stat s;
436 int fd;
437 int r;
438 char buf[256];
439
Denis Vlasenkob9256052007-09-28 10:29:17 +0000440 INIT_G();
441
Denis Vlasenko45946f82007-08-20 17:27:40 +0000442 if (!argv[1] || argv[2])
443 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000444 dir = argv[1];
445
Denis Vlasenko37188322008-02-16 13:20:56 +0000446 xpiped_pair(selfpipe);
447 close_on_exec_on(selfpipe.rd);
448 close_on_exec_on(selfpipe.wr);
449 ndelay_on(selfpipe.rd);
450 ndelay_on(selfpipe.wr);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000451
Denis Vlasenko8c783952007-01-27 22:21:52 +0000452 sig_block(SIGCHLD);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000453 bb_signals_recursive_norestart(1 << SIGCHLD, s_child);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000454 sig_block(SIGTERM);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +0000455 bb_signals_recursive_norestart(1 << SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000456
457 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000458 /* bss: svd[0].pid = 0; */
459 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
460 if (C_NOOP) svd[0].ctrl = C_NOOP;
461 if (W_UP) svd[0].want = W_UP;
462 /* bss: svd[0].islog = 0; */
463 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000464 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000465 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
466
467 if (stat("log", &s) == -1) {
468 if (errno != ENOENT)
469 warn_cannot("stat ./log");
470 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000471 if (!S_ISDIR(s.st_mode)) {
472 errno = 0;
473 warn_cannot("stat log/down: log is not a directory");
474 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000475 haslog = 1;
476 svd[1].state = S_DOWN;
477 svd[1].ctrl = C_NOOP;
478 svd[1].want = W_UP;
479 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000480 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000481 if (stat("log/down", &s) != -1)
482 svd[1].want = W_DOWN;
Denis Vlasenko37188322008-02-16 13:20:56 +0000483 xpiped_pair(logpipe);
484 close_on_exec_on(logpipe.rd);
485 close_on_exec_on(logpipe.wr);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000486 }
487 }
488
489 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000490 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000491 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000492 if (r == sizeof(buf))
493 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000494 buf[r] = 0;
495 mkdir(buf, 0700);
496 } else {
497 if ((errno != ENOENT) && (errno != EINVAL))
498 fatal_cannot("readlink ./supervise");
499 }
500 }
501 svd[0].fdlock = xopen3("log/supervise/lock"+4,
502 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
503 if (lock_exnb(svd[0].fdlock) == -1)
504 fatal_cannot("lock supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000505 close_on_exec_on(svd[0].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000506 if (haslog) {
507 if (mkdir("log/supervise", 0700) == -1) {
508 r = readlink("log/supervise", buf, 256);
509 if (r != -1) {
510 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000511 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000512 buf[r] = 0;
513 fd = xopen(".", O_RDONLY|O_NDELAY);
514 xchdir("./log");
515 mkdir(buf, 0700);
516 if (fchdir(fd) == -1)
517 fatal_cannot("change back to service directory");
518 close(fd);
519 }
520 else {
521 if ((errno != ENOENT) && (errno != EINVAL))
522 fatal_cannot("readlink ./log/supervise");
523 }
524 }
525 svd[1].fdlock = xopen3("log/supervise/lock",
526 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
527 if (lock_ex(svd[1].fdlock) == -1)
528 fatal_cannot("lock log/supervise/lock");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000529 close_on_exec_on(svd[1].fdlock);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000530 }
531
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000532 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000533 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000534 close_on_exec_on(svd[0].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000535 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000536 close_on_exec_on(svd[0].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000537 update_status(&svd[0]);
538 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000539 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000540 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000541 close_on_exec_on(svd[1].fdcontrol);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000542 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000543 close_on_exec_on(svd[1].fdcontrolwrite);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000544 update_status(&svd[1]);
545 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000546 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000547 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000548 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000549 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000550 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000552 close_on_exec_on(fd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 }
554 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000555 struct pollfd x[3];
556 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000557 char ch;
558
559 if (haslog)
560 if (!svd[1].pid && svd[1].want == W_UP)
561 startservice(&svd[1]);
562 if (!svd[0].pid)
563 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
564 startservice(&svd[0]);
565
Denis Vlasenko37188322008-02-16 13:20:56 +0000566 x[0].fd = selfpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000567 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000568 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000569 x[1].events = POLLIN;
570 /* x[2] is used only if haslog == 1 */
571 x[2].fd = svd[1].fdcontrol;
572 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000573 sig_unblock(SIGTERM);
574 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000575 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000576 sig_block(SIGTERM);
577 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000578
Denis Vlasenko37188322008-02-16 13:20:56 +0000579 while (read(selfpipe.rd, &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000580 continue;
581
Denis Vlasenko04c63862006-11-17 18:58:49 +0000582 for (;;) {
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000583 pid_t child;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000584 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000585
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000586 child = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000587 if (!child)
588 break;
589 if ((child == -1) && (errno != EINTR))
590 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000591 if (child == svd[0].pid) {
592 svd[0].pid = 0;
593 pidchanged = 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000594 svd[0].ctrl &=~ C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000595 if (svd[0].state != S_FINISH) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000596 fd = open_read("finish");
597 if (fd != -1) {
598 close(fd);
599 svd[0].state = S_FINISH;
600 update_status(&svd[0]);
601 continue;
602 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000603 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000604 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000605 deadline = svd[0].start.tv_sec + 1;
606 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000607 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000608 if (LESS(svd[0].start.tv_sec, deadline))
609 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000610 }
611 if (haslog) {
612 if (child == svd[1].pid) {
613 svd[1].pid = 0;
614 pidchanged = 1;
615 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000616 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000617 deadline = svd[1].start.tv_sec + 1;
618 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000619 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000620 if (LESS(svd[1].start.tv_sec, deadline))
621 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000622 }
623 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000624 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000625 if (read(svd[0].fdcontrol, &ch, 1) == 1)
626 ctrl(&svd[0], ch);
627 if (haslog)
628 if (read(svd[1].fdcontrol, &ch, 1) == 1)
629 ctrl(&svd[1], ch);
630
631 if (sigterm) {
632 ctrl(&svd[0], 'x');
633 sigterm = 0;
634 }
635
636 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
637 if (svd[1].pid == 0)
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000638 _exit(EXIT_SUCCESS);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000639 if (svd[1].want != W_EXIT) {
640 svd[1].want = W_EXIT;
641 /* stopservice(&svd[1]); */
642 update_status(&svd[1]);
Denis Vlasenko37188322008-02-16 13:20:56 +0000643 close(logpipe.wr);
644 close(logpipe.rd);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000645 }
646 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000647 } /* for (;;) */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000648 /* not reached */
649 return 0;
650}