blob: 32e4764d14df491f91fc8ffe9bd5830500ff1793 [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 Vlasenko68404f12008-03-17 09:00:54 +000092static void s_term(int sig_no ATTRIBUTE_UNUSED)
Denis Vlasenko04c63862006-11-17 18:58:49 +000093{
94 exitsoon = 1;
95}
Denis Vlasenko68404f12008-03-17 09:00:54 +000096static void s_hangup(int sig_no ATTRIBUTE_UNUSED)
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;
144 errno = 0;
145 while ((d = readdir(dir))) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000146 if (d->d_name[0] == '.')
147 continue;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000148 if (stat(d->d_name, &s) == -1) {
149 warn2_cannot("stat ", d->d_name);
150 errno = 0;
151 continue;
152 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000153 if (!S_ISDIR(s.st_mode))
154 continue;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000155 for (i = 0; i < svnum; i++) {
156 if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
157 sv[i].isgone = 0;
158 if (!sv[i].pid)
159 runsv(i, d->d_name);
160 break;
161 }
162 }
163 if (i == svnum) {
164 /* new service */
165 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
166 if (!svnew) {
167 warn3x("cannot start runsv ", d->d_name,
168 " too many services");
169 continue;
170 }
171 sv = svnew;
172 svnum++;
173 memset(&sv[i], 0, sizeof(sv[i]));
174 sv[i].ino = s.st_ino;
175 sv[i].dev = s.st_dev;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000176 /*sv[i].pid = 0;*/
177 /*sv[i].isgone = 0;*/
Denis Vlasenko04c63862006-11-17 18:58:49 +0000178 runsv(i, d->d_name);
179 check = 1;
180 }
181 }
182 if (errno) {
183 warn2_cannot("read directory ", svdir);
184 closedir(dir);
185 check = 1;
186 return;
187 }
188 closedir(dir);
189
190 /* SIGTERM removed runsv's */
191 for (i = 0; i < svnum; i++) {
192 if (!sv[i].isgone)
193 continue;
194 if (sv[i].pid)
195 kill(sv[i].pid, SIGTERM);
196 sv[i] = sv[--svnum];
197 check = 1;
198 }
199}
200
201static int setup_log(void)
202{
203 rploglen = strlen(rplog);
204 if (rploglen < 7) {
205 warnx("log must have at least seven characters");
206 return 0;
207 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000208 if (piped_pair(logpipe)) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000209 warnx("cannot create pipe for log");
210 return -1;
211 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000212 close_on_exec_on(logpipe.rd);
213 close_on_exec_on(logpipe.wr);
214 ndelay_on(logpipe.rd);
215 ndelay_on(logpipe.wr);
216 if (dup2(logpipe.wr, 2) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000217 warnx("cannot set filedescriptor for log");
218 return -1;
219 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000220 pfd[0].fd = logpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000221 pfd[0].events = POLLIN;
222 stamplog = monotonic_sec();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000223 return 1;
224}
225
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000226int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko68404f12008-03-17 09:00:54 +0000227int runsvdir_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000228{
229 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000230 dev_t last_dev = last_dev; /* for gcc */
231 ino_t last_ino = last_ino; /* for gcc */
232 time_t last_mtime = 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000233 int wstat;
234 int curdir;
235 int pid;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000236 unsigned deadline;
237 unsigned now;
238 unsigned stampcheck;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000239 char ch;
240 int i;
241
Denis Vlasenkod0a071a2008-03-17 09:33:45 +0000242 INIT_G();
243
Denis Vlasenko04c63862006-11-17 18:58:49 +0000244 argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000245 if (!*argv)
246 bb_show_usage();
247 if (argv[0][0] == '-') {
248 switch (argv[0][1]) {
249 case 'P': set_pgrp = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000250 case '-': ++argv;
251 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000252 if (!*argv)
253 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000254 }
255
Denis Vlasenko25591c32008-02-16 22:58:56 +0000256 bb_signals_recursive(1 << SIGTERM, s_term);
257 bb_signals_recursive(1 << SIGHUP, s_hangup);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258 svdir = *argv++;
259 if (argv && *argv) {
260 rplog = *argv;
261 if (setup_log() != 1) {
262 rplog = 0;
263 warnx("log service disabled");
264 }
265 }
266 curdir = open_read(".");
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000267 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000268 fatal2_cannot("open current directory", "");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000269 close_on_exec_on(curdir);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000270
Denis Vlasenko45946f82007-08-20 17:27:40 +0000271 stampcheck = monotonic_sec();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000272
273 for (;;) {
274 /* collect children */
275 for (;;) {
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000276 pid = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000277 if (pid <= 0)
278 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 for (i = 0; i < svnum; i++) {
280 if (pid == sv[i].pid) {
281 /* runsv has gone */
282 sv[i].pid = 0;
283 check = 1;
284 break;
285 }
286 }
287 }
288
Denis Vlasenko45946f82007-08-20 17:27:40 +0000289 now = monotonic_sec();
290 if ((int)(now - stampcheck) >= 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000291 /* wait at least a second */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000292 stampcheck = now + 1;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000293
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294 if (stat(svdir, &s) != -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000295 if (check || s.st_mtime != last_mtime
296 || s.st_ino != last_ino || s.st_dev != last_dev
Denis Vlasenko04c63862006-11-17 18:58:49 +0000297 ) {
298 /* svdir modified */
299 if (chdir(svdir) != -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000300 last_mtime = s.st_mtime;
301 last_dev = s.st_dev;
302 last_ino = s.st_ino;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303 check = 0;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000304 //if (now <= mtime)
305 // sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000306 runsvdir();
307 while (fchdir(curdir) == -1) {
308 warn2_cannot("change directory, pausing", "");
309 sleep(5);
310 }
311 } else
312 warn2_cannot("change directory to ", svdir);
313 }
314 } else
315 warn2_cannot("stat ", svdir);
316 }
317
318 if (rplog) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000319 if ((int)(now - stamplog) >= 0) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000320 write(logpipe.wr, ".", 1);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000321 stamplog = now + 900;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000322 }
323 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000324
Denis Vlasenko45946f82007-08-20 17:27:40 +0000325 pfd[0].revents = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000326 sig_block(SIGCHLD);
Denis Vlasenko137fbe42007-09-26 12:18:07 +0000327 deadline = (check ? 1 : 5);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000328 if (rplog)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000329 poll(pfd, 1, deadline*1000);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000330 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000331 sleep(deadline);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000332 sig_unblock(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000333
Denis Vlasenko45946f82007-08-20 17:27:40 +0000334 if (pfd[0].revents & POLLIN) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000335 while (read(logpipe.rd, &ch, 1) > 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000336 if (ch) {
337 for (i = 6; i < rploglen; i++)
338 rplog[i-1] = rplog[i];
339 rplog[rploglen-1] = ch;
340 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000341 }
342 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000343
344 switch (exitsoon) {
345 case 1:
346 _exit(0);
347 case 2:
348 for (i = 0; i < svnum; i++)
349 if (sv[i].pid)
350 kill(sv[i].pid, SIGTERM);
351 _exit(111);
352 }
353 }
354 /* not reached */
355 return 0;
356}