blob: 1ee3dda019fc75f4efccc97c0d69a945bbb8df54 [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 Vlasenko04c63862006-11-17 18:58:49 +000028/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
29/* 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;
93 int selfpipe[2];
94 int logpipe[2];
95 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 )
106#define INIT_G() \
107 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 Vlasenko04c63862006-11-17 18:58:49 +0000131static void s_child(int sig_no)
132{
133 write(selfpipe[1], "", 1);
134}
135
136static void s_term(int sig_no)
137{
138 sigterm = 1;
139 write(selfpipe[1], "", 1); /* XXX */
140}
141
142static char *add_str(char *p, const char *to_add)
143{
144 while ((*p = *to_add) != '\0') {
145 p++;
146 to_add++;
147 }
148 return p;
149}
150
151static int open_trunc_or_warn(const char *name)
152{
153 int fd = open_trunc(name);
154 if (fd < 0)
155 bb_perror_msg("%s: warning: cannot open %s",
156 dir, name);
157 return fd;
158}
159
160static int rename_or_warn(const char *old, const char *new)
161{
162 if (rename(old, new) == -1) {
163 bb_perror_msg("%s: warning: cannot rename %s to %s",
164 dir, old, new);
165 return -1;
166 }
167 return 0;
168}
169
170static void update_status(struct svdir *s)
171{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000172 ssize_t sz;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000173 int fd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000174 svstatus_t status;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000175
176 /* pid */
177 if (pidchanged) {
178 fd = open_trunc_or_warn("supervise/pid.new");
179 if (fd < 0)
180 return;
181 if (s->pid) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000182 char spid[sizeof(int)*3 + 2];
183 int size = sprintf(spid, "%u\n", (unsigned)s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000184 write(fd, spid, size);
185 }
186 close(fd);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000187 if (rename_or_warn("supervise/pid.new",
188 s->islog ? "log/supervise/pid" : "log/supervise/pid"+4))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000189 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000190 pidchanged = 0;
191 }
192
193 /* stat */
194 fd = open_trunc_or_warn("supervise/stat.new");
195 if (fd < -1)
196 return;
197
198 {
199 char stat_buf[sizeof("finish, paused, got TERM, want down\n")];
200 char *p = stat_buf;
201 switch (s->state) {
202 case S_DOWN:
203 p = add_str(p, "down");
204 break;
205 case S_RUN:
206 p = add_str(p, "run");
207 break;
208 case S_FINISH:
209 p = add_str(p, "finish");
210 break;
211 }
212 if (s->ctrl & C_PAUSE) p = add_str(p, ", paused");
213 if (s->ctrl & C_TERM) p = add_str(p, ", got TERM");
214 if (s->state != S_DOWN)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000215 switch (s->want) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000216 case W_DOWN:
217 p = add_str(p, ", want down");
218 break;
219 case W_EXIT:
220 p = add_str(p, ", want exit");
221 break;
222 }
223 *p++ = '\n';
224 write(fd, stat_buf, p - stat_buf);
225 close(fd);
226 }
227
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000228 rename_or_warn("supervise/stat.new",
229 s->islog ? "log/supervise/stat" : "log/supervise/stat"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000230
231 /* supervise compatibility */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000232 memset(&status, 0, sizeof(status));
233 status.time_be64 = SWAP_BE64(s->start.tv_sec + 0x400000000000000aULL);
234 status.time_nsec_be32 = SWAP_BE32(s->start.tv_nsec);
235 status.pid_le32 = SWAP_LE32(s->pid);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000236 if (s->ctrl & C_PAUSE)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000237 status.paused = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 if (s->want == W_UP)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000239 status.want = 'u';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000240 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000241 status.want = 'd';
Denis Vlasenko04c63862006-11-17 18:58:49 +0000242 if (s->ctrl & C_TERM)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000243 status.got_term = 1;
244 status.run_or_finish = s->state;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000245 fd = open_trunc_or_warn("supervise/status.new");
246 if (fd < 0)
247 return;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000248 sz = write(fd, &status, sizeof(status));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000249 close(fd);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000250 if (sz != sizeof(status)) {
251 warn_cannot("write supervise/status.new");
252 unlink("supervise/status.new");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000253 return;
254 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000255 rename_or_warn("supervise/status.new",
256 s->islog ? "log/supervise/status" : "log/supervise/status"+4);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000257}
258
259static unsigned custom(struct svdir *s, char c)
260{
261 int pid;
262 int w;
263 char a[10];
264 struct stat st;
265 char *prog[2];
266
267 if (s->islog) return 0;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000268 strcpy(a, "control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000269 a[8] = c;
270 if (stat(a, &st) == 0) {
271 if (st.st_mode & S_IXUSR) {
272 pid = fork();
273 if (pid == -1) {
274 warn_cannot("fork for control/?");
275 return 0;
276 }
277 if (!pid) {
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000278 if (haslog && dup2(logpipe[1], 1) == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 warn_cannot("setup stdout for control/?");
280 prog[0] = a;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000281 prog[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000282 execve(a, prog, environ);
283 fatal_cannot("run control/?");
284 }
285 while (wait_pid(&w, pid) == -1) {
286 if (errno == EINTR) continue;
287 warn_cannot("wait for child control/?");
288 return 0;
289 }
290 return !wait_exitcode(w);
291 }
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000292 } else {
293 if (errno != ENOENT)
294 warn_cannot("stat control/?");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000295 }
296 return 0;
297}
298
299static void stopservice(struct svdir *s)
300{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000301 if (s->pid && !custom(s, 't')) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000302 kill(s->pid, SIGTERM);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000303 s->ctrl |= C_TERM;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000304 update_status(s);
305 }
306 if (s->want == W_DOWN) {
307 kill(s->pid, SIGCONT);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000308 custom(s, 'd');
309 return;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000310 }
311 if (s->want == W_EXIT) {
312 kill(s->pid, SIGCONT);
313 custom(s, 'x');
314 }
315}
316
317static void startservice(struct svdir *s)
318{
319 int p;
320 char *run[2];
321
322 if (s->state == S_FINISH)
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000323 run[0] = (char*)"./finish";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000324 else {
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000325 run[0] = (char*)"./run";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000326 custom(s, 'u');
327 }
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000328 run[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000329
Denis Vlasenkoff131b92007-04-10 15:42:06 +0000330 if (s->pid != 0)
331 stopservice(s); /* should never happen */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000332 while ((p = fork()) == -1) {
333 warn_cannot("fork, sleeping");
334 sleep(5);
335 }
336 if (p == 0) {
337 /* child */
338 if (haslog) {
339 if (s->islog) {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000340 xdup2(logpipe[0], 0);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000341 close(logpipe[1]);
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000342 xchdir("./log");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000343 } else {
Denis Vlasenkoa27a11b2007-08-18 14:16:39 +0000344 xdup2(logpipe[1], 1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000345 close(logpipe[0]);
346 }
347 }
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000348 signal(SIGCHLD, SIG_DFL);
349 signal(SIGTERM, SIG_DFL);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000350 sig_unblock(SIGCHLD);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000351 sig_unblock(SIGTERM);
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000352 execvp(*run, run);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000353 fatal2_cannot(s->islog ? "start log/" : "start ", *run);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000354 }
355 if (s->state != S_FINISH) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000356 gettimeofday_ns(&s->start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000357 s->state = S_RUN;
358 }
359 s->pid = p;
360 pidchanged = 1;
361 s->ctrl = C_NOOP;
362 update_status(s);
363}
364
365static int ctrl(struct svdir *s, char c)
366{
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000367 int sig;
368
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000369 switch (c) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000370 case 'd': /* down */
371 s->want = W_DOWN;
372 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000373 if (s->pid && s->state != S_FINISH)
374 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000375 break;
376 case 'u': /* up */
377 s->want = W_UP;
378 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000379 if (s->pid == 0)
380 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000381 break;
382 case 'x': /* exit */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000383 if (s->islog)
384 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000385 s->want = W_EXIT;
386 update_status(s);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000387 /* FALLTHROUGH */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000388 case 't': /* sig term */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000389 if (s->pid && s->state != S_FINISH)
390 stopservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000391 break;
392 case 'k': /* sig kill */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000393 if (s->pid && !custom(s, c))
394 kill(s->pid, SIGKILL);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000395 s->state = S_DOWN;
396 break;
397 case 'p': /* sig pause */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000398 if (s->pid && !custom(s, c))
399 kill(s->pid, SIGSTOP);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000400 s->ctrl |= C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000401 update_status(s);
402 break;
403 case 'c': /* sig cont */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000404 if (s->pid && !custom(s, c))
405 kill(s->pid, SIGCONT);
406 if (s->ctrl & C_PAUSE)
407 s->ctrl &= ~C_PAUSE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000408 update_status(s);
409 break;
410 case 'o': /* once */
411 s->want = W_DOWN;
412 update_status(s);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000413 if (!s->pid)
414 startservice(s);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000415 break;
416 case 'a': /* sig alarm */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000417 sig = SIGALRM;
418 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000419 case 'h': /* sig hup */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000420 sig = SIGHUP;
421 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000422 case 'i': /* sig int */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000423 sig = SIGINT;
424 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000425 case 'q': /* sig quit */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000426 sig = SIGQUIT;
427 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000428 case '1': /* sig usr1 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000429 sig = SIGUSR1;
430 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000431 case '2': /* sig usr2 */
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000432 sig = SIGUSR2;
433 goto sendsig;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000434 }
435 return 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000436 sendsig:
437 if (s->pid && !custom(s, c))
438 kill(s->pid, sig);
439 return 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000440}
441
Denis Vlasenko06af2162007-02-03 17:28:39 +0000442int runsv_main(int argc, char **argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000443int runsv_main(int argc, char **argv)
444{
445 struct stat s;
446 int fd;
447 int r;
448 char buf[256];
449
Denis Vlasenkob9256052007-09-28 10:29:17 +0000450 INIT_G();
451
Denis Vlasenko45946f82007-08-20 17:27:40 +0000452 if (!argv[1] || argv[2])
453 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000454 dir = argv[1];
455
Denis Vlasenko5a6aedd2007-05-26 16:44:20 +0000456 xpipe(selfpipe);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457 coe(selfpipe[0]);
458 coe(selfpipe[1]);
459 ndelay_on(selfpipe[0]);
460 ndelay_on(selfpipe[1]);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000461
Denis Vlasenko8c783952007-01-27 22:21:52 +0000462 sig_block(SIGCHLD);
463 sig_catch(SIGCHLD, s_child);
464 sig_block(SIGTERM);
465 sig_catch(SIGTERM, s_term);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000466
467 xchdir(dir);
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000468 /* bss: svd[0].pid = 0; */
469 if (S_DOWN) svd[0].state = S_DOWN; /* otherwise already 0 (bss) */
470 if (C_NOOP) svd[0].ctrl = C_NOOP;
471 if (W_UP) svd[0].want = W_UP;
472 /* bss: svd[0].islog = 0; */
473 /* bss: svd[1].pid = 0; */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000474 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000475 if (stat("down", &s) != -1) svd[0].want = W_DOWN;
476
477 if (stat("log", &s) == -1) {
478 if (errno != ENOENT)
479 warn_cannot("stat ./log");
480 } else {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000481 if (!S_ISDIR(s.st_mode)) {
482 errno = 0;
483 warn_cannot("stat log/down: log is not a directory");
484 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000485 haslog = 1;
486 svd[1].state = S_DOWN;
487 svd[1].ctrl = C_NOOP;
488 svd[1].want = W_UP;
489 svd[1].islog = 1;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000490 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000491 if (stat("log/down", &s) != -1)
492 svd[1].want = W_DOWN;
Denis Vlasenko5a6aedd2007-05-26 16:44:20 +0000493 xpipe(logpipe);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000494 coe(logpipe[0]);
495 coe(logpipe[1]);
496 }
497 }
498
499 if (mkdir("supervise", 0700) == -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000500 r = readlink("supervise", buf, sizeof(buf));
Denis Vlasenko04c63862006-11-17 18:58:49 +0000501 if (r != -1) {
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000502 if (r == sizeof(buf))
503 fatal2x_cannot("readlink ./supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000504 buf[r] = 0;
505 mkdir(buf, 0700);
506 } else {
507 if ((errno != ENOENT) && (errno != EINVAL))
508 fatal_cannot("readlink ./supervise");
509 }
510 }
511 svd[0].fdlock = xopen3("log/supervise/lock"+4,
512 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
513 if (lock_exnb(svd[0].fdlock) == -1)
514 fatal_cannot("lock supervise/lock");
515 coe(svd[0].fdlock);
516 if (haslog) {
517 if (mkdir("log/supervise", 0700) == -1) {
518 r = readlink("log/supervise", buf, 256);
519 if (r != -1) {
520 if (r == 256)
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000521 fatal2x_cannot("readlink ./log/supervise", ": name too long");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000522 buf[r] = 0;
523 fd = xopen(".", O_RDONLY|O_NDELAY);
524 xchdir("./log");
525 mkdir(buf, 0700);
526 if (fchdir(fd) == -1)
527 fatal_cannot("change back to service directory");
528 close(fd);
529 }
530 else {
531 if ((errno != ENOENT) && (errno != EINVAL))
532 fatal_cannot("readlink ./log/supervise");
533 }
534 }
535 svd[1].fdlock = xopen3("log/supervise/lock",
536 O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
537 if (lock_ex(svd[1].fdlock) == -1)
538 fatal_cannot("lock log/supervise/lock");
539 coe(svd[1].fdlock);
540 }
541
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000542 mkfifo("log/supervise/control"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000543 svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY);
544 coe(svd[0].fdcontrol);
545 svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY);
546 coe(svd[0].fdcontrolwrite);
547 update_status(&svd[0]);
548 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000549 mkfifo("log/supervise/control", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000550 svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY);
551 coe(svd[1].fdcontrol);
552 svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY);
553 coe(svd[1].fdcontrolwrite);
554 update_status(&svd[1]);
555 }
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000556 mkfifo("log/supervise/ok"+4, 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000557 fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY);
558 coe(fd);
559 if (haslog) {
Denis Vlasenkoe2473f82007-01-27 22:22:17 +0000560 mkfifo("log/supervise/ok", 0600);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000561 fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY);
562 coe(fd);
563 }
564 for (;;) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000565 struct pollfd x[3];
566 unsigned deadline;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000567 char ch;
568
569 if (haslog)
570 if (!svd[1].pid && svd[1].want == W_UP)
571 startservice(&svd[1]);
572 if (!svd[0].pid)
573 if (svd[0].want == W_UP || svd[0].state == S_FINISH)
574 startservice(&svd[0]);
575
576 x[0].fd = selfpipe[0];
Denis Vlasenko45946f82007-08-20 17:27:40 +0000577 x[0].events = POLLIN;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000578 x[1].fd = svd[0].fdcontrol;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000579 x[1].events = POLLIN;
580 /* x[2] is used only if haslog == 1 */
581 x[2].fd = svd[1].fdcontrol;
582 x[2].events = POLLIN;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000583 sig_unblock(SIGTERM);
584 sig_unblock(SIGCHLD);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000585 poll(x, 2 + haslog, 3600*1000);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000586 sig_block(SIGTERM);
587 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000588
589 while (read(selfpipe[0], &ch, 1) == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000590 continue;
591
Denis Vlasenko04c63862006-11-17 18:58:49 +0000592 for (;;) {
593 int child;
594 int wstat;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000595
Denis Vlasenko04c63862006-11-17 18:58:49 +0000596 child = wait_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000597 if (!child)
598 break;
599 if ((child == -1) && (errno != EINTR))
600 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000601 if (child == svd[0].pid) {
602 svd[0].pid = 0;
603 pidchanged = 1;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000604 svd[0].ctrl &=~ C_TERM;
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000605 if (svd[0].state != S_FINISH) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000606 fd = open_read("finish");
607 if (fd != -1) {
608 close(fd);
609 svd[0].state = S_FINISH;
610 update_status(&svd[0]);
611 continue;
612 }
Denis Vlasenkod0762e32007-02-18 11:07:43 +0000613 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000614 svd[0].state = S_DOWN;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000615 deadline = svd[0].start.tv_sec + 1;
616 gettimeofday_ns(&svd[0].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000617 update_status(&svd[0]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000618 if (LESS(svd[0].start.tv_sec, deadline))
619 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000620 }
621 if (haslog) {
622 if (child == svd[1].pid) {
623 svd[1].pid = 0;
624 pidchanged = 1;
625 svd[1].state = S_DOWN;
Denis Vlasenko3aba6662007-03-09 22:46:06 +0000626 svd[1].ctrl &= ~C_TERM;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000627 deadline = svd[1].start.tv_sec + 1;
628 gettimeofday_ns(&svd[1].start);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000629 update_status(&svd[1]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000630 if (LESS(svd[1].start.tv_sec, deadline))
631 sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000632 }
633 }
634 }
635 if (read(svd[0].fdcontrol, &ch, 1) == 1)
636 ctrl(&svd[0], ch);
637 if (haslog)
638 if (read(svd[1].fdcontrol, &ch, 1) == 1)
639 ctrl(&svd[1], ch);
640
641 if (sigterm) {
642 ctrl(&svd[0], 'x');
643 sigterm = 0;
644 }
645
646 if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) {
647 if (svd[1].pid == 0)
648 _exit(0);
649 if (svd[1].want != W_EXIT) {
650 svd[1].want = W_EXIT;
651 /* stopservice(&svd[1]); */
652 update_status(&svd[1]);
653 close(logpipe[1]);
654 close(logpipe[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000655 }
656 }
657 }
658 /* not reached */
659 return 0;
660}