blob: bca5c901c31df707aa7ef8da889d0ce4548506b2 [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 Vlasenkoc9dc2ac2007-09-27 10:08:02 +000045static struct service *sv;
Denis Vlasenko04c63862006-11-17 18:58:49 +000046static char *svdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +000047static int svnum;
Denis Vlasenko04c63862006-11-17 18:58:49 +000048static char *rplog;
49static int rploglen;
Denis Vlasenko37188322008-02-16 13:20:56 +000050static struct fd_pair logpipe;
Denis Vlasenko45946f82007-08-20 17:27:40 +000051static struct pollfd pfd[1];
52static unsigned stamplog;
53static smallint check = 1;
54static smallint exitsoon;
55static smallint set_pgrp;
Denis Vlasenko04c63862006-11-17 18:58:49 +000056
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000057static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +000058{
59 bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
60 /* was exiting 100 */
61}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000062static void warn3x(const char *m1, const char *m2, const char *m3)
Denis Vlasenko04c63862006-11-17 18:58:49 +000063{
64 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
Denis Vlasenkof7996f32007-01-11 17:20:00 +000065}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000066static void warn2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +000067{
68 warn3x("cannot ", m1, m2);
69}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000070static void warnx(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +000071{
72 warn3x(m1, "", "");
Denis Vlasenkof7996f32007-01-11 17:20:00 +000073}
Denis Vlasenko04c63862006-11-17 18:58:49 +000074
Denis Vlasenko68404f12008-03-17 09:00:54 +000075static void s_term(int sig_no ATTRIBUTE_UNUSED)
Denis Vlasenko04c63862006-11-17 18:58:49 +000076{
77 exitsoon = 1;
78}
Denis Vlasenko68404f12008-03-17 09:00:54 +000079static void s_hangup(int sig_no ATTRIBUTE_UNUSED)
Denis Vlasenko04c63862006-11-17 18:58:49 +000080{
81 exitsoon = 2;
82}
83
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000084static void runsv(int no, const char *name)
Denis Vlasenko04c63862006-11-17 18:58:49 +000085{
Denis Vlasenko45946f82007-08-20 17:27:40 +000086 pid_t pid;
87 char *prog[3];
88
89 prog[0] = (char*)"runsv";
90 prog[1] = (char*)name;
91 prog[2] = NULL;
92
93 pid = vfork();
Denis Vlasenko04c63862006-11-17 18:58:49 +000094
95 if (pid == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +000096 warn2_cannot("vfork", "");
Denis Vlasenko04c63862006-11-17 18:58:49 +000097 return;
98 }
99 if (pid == 0) {
100 /* child */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000101 if (set_pgrp)
Denis Vlasenko2856dab2007-04-01 01:18:20 +0000102 setsid();
Denis Vlasenko25591c32008-02-16 22:58:56 +0000103 bb_signals(0
104 + (1 << SIGHUP)
105 + (1 << SIGTERM)
106 , SIG_DFL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000107 execvp(prog[0], prog);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000108 fatal2_cannot("start runsv ", name);
109 }
110 sv[no].pid = pid;
111}
112
113static void runsvdir(void)
114{
115 DIR *dir;
116 direntry *d;
117 int i;
118 struct stat s;
119
120 dir = opendir(".");
121 if (!dir) {
122 warn2_cannot("open directory ", svdir);
123 return;
124 }
125 for (i = 0; i < svnum; i++)
126 sv[i].isgone = 1;
127 errno = 0;
128 while ((d = readdir(dir))) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000129 if (d->d_name[0] == '.')
130 continue;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000131 if (stat(d->d_name, &s) == -1) {
132 warn2_cannot("stat ", d->d_name);
133 errno = 0;
134 continue;
135 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000136 if (!S_ISDIR(s.st_mode))
137 continue;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000138 for (i = 0; i < svnum; i++) {
139 if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
140 sv[i].isgone = 0;
141 if (!sv[i].pid)
142 runsv(i, d->d_name);
143 break;
144 }
145 }
146 if (i == svnum) {
147 /* new service */
148 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
149 if (!svnew) {
150 warn3x("cannot start runsv ", d->d_name,
151 " too many services");
152 continue;
153 }
154 sv = svnew;
155 svnum++;
156 memset(&sv[i], 0, sizeof(sv[i]));
157 sv[i].ino = s.st_ino;
158 sv[i].dev = s.st_dev;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000159 /*sv[i].pid = 0;*/
160 /*sv[i].isgone = 0;*/
Denis Vlasenko04c63862006-11-17 18:58:49 +0000161 runsv(i, d->d_name);
162 check = 1;
163 }
164 }
165 if (errno) {
166 warn2_cannot("read directory ", svdir);
167 closedir(dir);
168 check = 1;
169 return;
170 }
171 closedir(dir);
172
173 /* SIGTERM removed runsv's */
174 for (i = 0; i < svnum; i++) {
175 if (!sv[i].isgone)
176 continue;
177 if (sv[i].pid)
178 kill(sv[i].pid, SIGTERM);
179 sv[i] = sv[--svnum];
180 check = 1;
181 }
182}
183
184static int setup_log(void)
185{
186 rploglen = strlen(rplog);
187 if (rploglen < 7) {
188 warnx("log must have at least seven characters");
189 return 0;
190 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000191 if (piped_pair(logpipe)) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000192 warnx("cannot create pipe for log");
193 return -1;
194 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000195 close_on_exec_on(logpipe.rd);
196 close_on_exec_on(logpipe.wr);
197 ndelay_on(logpipe.rd);
198 ndelay_on(logpipe.wr);
199 if (dup2(logpipe.wr, 2) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000200 warnx("cannot set filedescriptor for log");
201 return -1;
202 }
Denis Vlasenko37188322008-02-16 13:20:56 +0000203 pfd[0].fd = logpipe.rd;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000204 pfd[0].events = POLLIN;
205 stamplog = monotonic_sec();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000206 return 1;
207}
208
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000209int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko68404f12008-03-17 09:00:54 +0000210int runsvdir_main(int argc ATTRIBUTE_UNUSED, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000211{
212 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000213 dev_t last_dev = last_dev; /* for gcc */
214 ino_t last_ino = last_ino; /* for gcc */
215 time_t last_mtime = 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000216 int wstat;
217 int curdir;
218 int pid;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000219 unsigned deadline;
220 unsigned now;
221 unsigned stampcheck;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000222 char ch;
223 int i;
224
225 argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000226 if (!*argv)
227 bb_show_usage();
228 if (argv[0][0] == '-') {
229 switch (argv[0][1]) {
230 case 'P': set_pgrp = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000231 case '-': ++argv;
232 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000233 if (!*argv)
234 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000235 }
236
Denis Vlasenko25591c32008-02-16 22:58:56 +0000237 bb_signals_recursive(1 << SIGTERM, s_term);
238 bb_signals_recursive(1 << SIGHUP, s_hangup);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000239 svdir = *argv++;
240 if (argv && *argv) {
241 rplog = *argv;
242 if (setup_log() != 1) {
243 rplog = 0;
244 warnx("log service disabled");
245 }
246 }
247 curdir = open_read(".");
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000248 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000249 fatal2_cannot("open current directory", "");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000250 close_on_exec_on(curdir);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000251
Denis Vlasenko45946f82007-08-20 17:27:40 +0000252 stampcheck = monotonic_sec();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000253
254 for (;;) {
255 /* collect children */
256 for (;;) {
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000257 pid = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000258 if (pid <= 0)
259 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000260 for (i = 0; i < svnum; i++) {
261 if (pid == sv[i].pid) {
262 /* runsv has gone */
263 sv[i].pid = 0;
264 check = 1;
265 break;
266 }
267 }
268 }
269
Denis Vlasenko45946f82007-08-20 17:27:40 +0000270 now = monotonic_sec();
271 if ((int)(now - stampcheck) >= 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000272 /* wait at least a second */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000273 stampcheck = now + 1;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000274
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275 if (stat(svdir, &s) != -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000276 if (check || s.st_mtime != last_mtime
277 || s.st_ino != last_ino || s.st_dev != last_dev
Denis Vlasenko04c63862006-11-17 18:58:49 +0000278 ) {
279 /* svdir modified */
280 if (chdir(svdir) != -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000281 last_mtime = s.st_mtime;
282 last_dev = s.st_dev;
283 last_ino = s.st_ino;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000284 check = 0;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000285 //if (now <= mtime)
286 // sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000287 runsvdir();
288 while (fchdir(curdir) == -1) {
289 warn2_cannot("change directory, pausing", "");
290 sleep(5);
291 }
292 } else
293 warn2_cannot("change directory to ", svdir);
294 }
295 } else
296 warn2_cannot("stat ", svdir);
297 }
298
299 if (rplog) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000300 if ((int)(now - stamplog) >= 0) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000301 write(logpipe.wr, ".", 1);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000302 stamplog = now + 900;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303 }
304 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000305
Denis Vlasenko45946f82007-08-20 17:27:40 +0000306 pfd[0].revents = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000307 sig_block(SIGCHLD);
Denis Vlasenko137fbe42007-09-26 12:18:07 +0000308 deadline = (check ? 1 : 5);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000309 if (rplog)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000310 poll(pfd, 1, deadline*1000);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000311 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000312 sleep(deadline);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000313 sig_unblock(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314
Denis Vlasenko45946f82007-08-20 17:27:40 +0000315 if (pfd[0].revents & POLLIN) {
Denis Vlasenko37188322008-02-16 13:20:56 +0000316 while (read(logpipe.rd, &ch, 1) > 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000317 if (ch) {
318 for (i = 6; i < rploglen; i++)
319 rplog[i-1] = rplog[i];
320 rplog[rploglen-1] = ch;
321 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000322 }
323 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000324
325 switch (exitsoon) {
326 case 1:
327 _exit(0);
328 case 2:
329 for (i = 0; i < svnum; i++)
330 if (sv[i].pid)
331 kill(sv[i].pid, SIGTERM);
332 _exit(111);
333 }
334 }
335 /* not reached */
336 return 0;
337}