blob: aaa4c470cd3104319c276375186aae76bb3ae708 [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
36#define MAXSERVICES 1000
37
Denis Vlasenko45946f82007-08-20 17:27:40 +000038struct service {
39 dev_t dev;
40 ino_t ino;
41 pid_t pid;
42 smallint isgone;
43};
44
Denis Vlasenkod0a071a2008-03-17 09:33:45 +000045struct globals {
46 struct service *sv;
47 char *svdir;
48 char *rplog;
49 int svnum;
50 int rploglen;
51 struct fd_pair logpipe;
52 struct pollfd pfd[1];
53 unsigned stamplog;
54 smallint check; /* = 1; */
55 smallint exitsoon;
56 smallint set_pgrp;
57};
58#define G (*(struct globals*)&bb_common_bufsiz1)
59#define sv (G.sv )
60#define svdir (G.svdir )
61#define rplog (G.rplog )
62#define svnum (G.svnum )
63#define rploglen (G.rploglen )
64#define logpipe (G.logpipe )
65#define pfd (G.pfd )
66#define stamplog (G.stamplog )
67#define check (G.check )
68#define exitsoon (G.exitsoon )
69#define set_pgrp (G.set_pgrp )
70#define INIT_G() do { \
71 check = 1; \
72} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +000073
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000074static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +000075{
76 bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
77 /* was exiting 100 */
78}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000079static void warn3x(const char *m1, const char *m2, const char *m3)
Denis Vlasenko04c63862006-11-17 18:58:49 +000080{
81 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
Denis Vlasenkof7996f32007-01-11 17:20:00 +000082}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000083static void warn2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +000084{
85 warn3x("cannot ", m1, m2);
86}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000087static void warnx(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +000088{
89 warn3x(m1, "", "");
Denis Vlasenkof7996f32007-01-11 17:20:00 +000090}
Denis Vlasenko04c63862006-11-17 18:58:49 +000091
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000092static void s_term(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +000093{
94 exitsoon = 1;
95}
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000096static void s_hangup(int sig_no UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +000097{
98 exitsoon = 2;
99}
100
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000101static void runsv(int no, const char *name)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000102{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000103 pid_t pid;
104 char *prog[3];
105
106 prog[0] = (char*)"runsv";
107 prog[1] = (char*)name;
108 prog[2] = NULL;
109
110 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000111
112 if (pid == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000113 warn2_cannot("vfork", "");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000114 return;
115 }
116 if (pid == 0) {
117 /* child */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000118 if (set_pgrp)
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000119 setsid();
Denis Vlasenko25591c32008-02-16 22:58:56 +0000120 bb_signals(0
121 + (1 << SIGHUP)
122 + (1 << SIGTERM)
123 , SIG_DFL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000124 execvp(prog[0], prog);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000125 fatal2_cannot("start runsv ", name);
126 }
127 sv[no].pid = pid;
128}
129
130static void runsvdir(void)
131{
132 DIR *dir;
133 direntry *d;
134 int i;
135 struct stat s;
136
137 dir = opendir(".");
138 if (!dir) {
139 warn2_cannot("open directory ", svdir);
140 return;
141 }
142 for (i = 0; i < svnum; i++)
143 sv[i].isgone = 1;
Denis Vlasenko8f740942008-05-31 07:00:33 +0000144
145 while (1) {
146 errno = 0;
147 d = readdir(dir);
148 if (!d)
149 break;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000150 if (d->d_name[0] == '.')
151 continue;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000152 if (stat(d->d_name, &s) == -1) {
153 warn2_cannot("stat ", d->d_name);
154 errno = 0;
155 continue;
156 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000157 if (!S_ISDIR(s.st_mode))
158 continue;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000159 for (i = 0; i < svnum; i++) {
160 if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
161 sv[i].isgone = 0;
162 if (!sv[i].pid)
163 runsv(i, d->d_name);
164 break;
165 }
166 }
167 if (i == svnum) {
168 /* new service */
169 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
170 if (!svnew) {
171 warn3x("cannot start runsv ", d->d_name,
172 " too many services");
173 continue;
174 }
175 sv = svnew;
176 svnum++;
177 memset(&sv[i], 0, sizeof(sv[i]));
178 sv[i].ino = s.st_ino;
179 sv[i].dev = s.st_dev;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000180 /*sv[i].pid = 0;*/
181 /*sv[i].isgone = 0;*/
Denis Vlasenko04c63862006-11-17 18:58:49 +0000182 runsv(i, d->d_name);
183 check = 1;
184 }
185 }
186 if (errno) {
187 warn2_cannot("read directory ", svdir);
188 closedir(dir);
189 check = 1;
190 return;
191 }
192 closedir(dir);
193
194 /* SIGTERM removed runsv's */
195 for (i = 0; i < svnum; i++) {
196 if (!sv[i].isgone)
197 continue;
198 if (sv[i].pid)
199 kill(sv[i].pid, SIGTERM);
200 sv[i] = sv[--svnum];
Denis Vlasenko8f740942008-05-31 07:00:33 +0000201/* BUG? we deleted sv[i] by copying over sv[last], but we will not check this newly-copied one! */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000202 check = 1;
203 }
204}
205
206static int setup_log(void)
207{
208 rploglen = strlen(rplog);
209 if (rploglen < 7) {
210 warnx("log must have at least seven characters");
211 return 0;
212 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000213 if (piped_pair(logpipe)) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000214 warnx("cannot create pipe for log");
215 return -1;
216 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000217 close_on_exec_on(logpipe.rd);
218 close_on_exec_on(logpipe.wr);
219 ndelay_on(logpipe.rd);
220 ndelay_on(logpipe.wr);
221 if (dup2(logpipe.wr, 2) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000222 warnx("cannot set filedescriptor for log");
223 return -1;
224 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000225 pfd[0].fd = logpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000226 pfd[0].events = POLLIN;
227 stamplog = monotonic_sec();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000228 return 1;
229}
230
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000231int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000232int runsvdir_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000233{
234 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000235 dev_t last_dev = last_dev; /* for gcc */
236 ino_t last_ino = last_ino; /* for gcc */
237 time_t last_mtime = 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 int wstat;
239 int curdir;
240 int pid;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000241 unsigned deadline;
242 unsigned now;
243 unsigned stampcheck;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000244 char ch;
245 int i;
246
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000247 INIT_G();
248
Denis Vlasenko04c63862006-11-17 18:58:49 +0000249 argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000250 if (!*argv)
251 bb_show_usage();
252 if (argv[0][0] == '-') {
253 switch (argv[0][1]) {
254 case 'P': set_pgrp = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000255 case '-': ++argv;
256 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000257 if (!*argv)
258 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259 }
260
Denis Vlasenko25591c32008-02-16 22:58:56 +0000261 bb_signals_recursive(1 << SIGTERM, s_term);
262 bb_signals_recursive(1 << SIGHUP, s_hangup);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 svdir = *argv++;
264 if (argv && *argv) {
265 rplog = *argv;
266 if (setup_log() != 1) {
267 rplog = 0;
268 warnx("log service disabled");
269 }
270 }
271 curdir = open_read(".");
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000272 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000273 fatal2_cannot("open current directory", "");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000274 close_on_exec_on(curdir);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275
Denis Vlasenko45946f82007-08-20 17:27:40 +0000276 stampcheck = monotonic_sec();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000277
278 for (;;) {
279 /* collect children */
280 for (;;) {
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000281 pid = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000282 if (pid <= 0)
283 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000284 for (i = 0; i < svnum; i++) {
285 if (pid == sv[i].pid) {
286 /* runsv has gone */
287 sv[i].pid = 0;
288 check = 1;
289 break;
290 }
291 }
292 }
293
Denis Vlasenko45946f82007-08-20 17:27:40 +0000294 now = monotonic_sec();
295 if ((int)(now - stampcheck) >= 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000296 /* wait at least a second */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000297 stampcheck = now + 1;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000298
Denis Vlasenko04c63862006-11-17 18:58:49 +0000299 if (stat(svdir, &s) != -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000300 if (check || s.st_mtime != last_mtime
301 || s.st_ino != last_ino || s.st_dev != last_dev
Denis Vlasenko04c63862006-11-17 18:58:49 +0000302 ) {
303 /* svdir modified */
304 if (chdir(svdir) != -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000305 last_mtime = s.st_mtime;
306 last_dev = s.st_dev;
307 last_ino = s.st_ino;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000308 check = 0;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000309 //if (now <= mtime)
310 // sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000311 runsvdir();
312 while (fchdir(curdir) == -1) {
313 warn2_cannot("change directory, pausing", "");
314 sleep(5);
315 }
316 } else
317 warn2_cannot("change directory to ", svdir);
318 }
319 } else
320 warn2_cannot("stat ", svdir);
321 }
322
323 if (rplog) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000324 if ((int)(now - stamplog) >= 0) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000325 write(logpipe.wr, ".", 1);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000326 stamplog = now + 900;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000327 }
328 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000329
Denis Vlasenko45946f82007-08-20 17:27:40 +0000330 pfd[0].revents = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000331 sig_block(SIGCHLD);
Denis Vlasenko137fbe42007-09-26 12:18:07 +0000332 deadline = (check ? 1 : 5);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000333 if (rplog)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000334 poll(pfd, 1, deadline*1000);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000335 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000336 sleep(deadline);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000337 sig_unblock(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000338
Denis Vlasenko45946f82007-08-20 17:27:40 +0000339 if (pfd[0].revents & POLLIN) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000340 while (read(logpipe.rd, &ch, 1) > 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000341 if (ch) {
342 for (i = 6; i < rploglen; i++)
343 rplog[i-1] = rplog[i];
344 rplog[rploglen-1] = ch;
345 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000346 }
347 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000348
349 switch (exitsoon) {
350 case 1:
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +0000351 _exit(EXIT_SUCCESS);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000352 case 2:
353 for (i = 0; i < svnum; i++)
354 if (sv[i].pid)
355 kill(sv[i].pid, SIGTERM);
356 _exit(111);
357 }
358 }
359 /* not reached */
360 return 0;
361}