blob: 8384903763d1e7f7ffefb91106316871c271dfde [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
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;
50static int logpipe[2];
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
75static void s_term(int sig_no)
76{
77 exitsoon = 1;
78}
79static void s_hangup(int sig_no)
80{
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();
103 signal(SIGHUP, SIG_DFL);
104 signal(SIGTERM, SIG_DFL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000105 execvp(prog[0], prog);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000106 fatal2_cannot("start runsv ", name);
107 }
108 sv[no].pid = pid;
109}
110
111static void runsvdir(void)
112{
113 DIR *dir;
114 direntry *d;
115 int i;
116 struct stat s;
117
118 dir = opendir(".");
119 if (!dir) {
120 warn2_cannot("open directory ", svdir);
121 return;
122 }
123 for (i = 0; i < svnum; i++)
124 sv[i].isgone = 1;
125 errno = 0;
126 while ((d = readdir(dir))) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000127 if (d->d_name[0] == '.')
128 continue;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000129 if (stat(d->d_name, &s) == -1) {
130 warn2_cannot("stat ", d->d_name);
131 errno = 0;
132 continue;
133 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000134 if (!S_ISDIR(s.st_mode))
135 continue;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000136 for (i = 0; i < svnum; i++) {
137 if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
138 sv[i].isgone = 0;
139 if (!sv[i].pid)
140 runsv(i, d->d_name);
141 break;
142 }
143 }
144 if (i == svnum) {
145 /* new service */
146 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
147 if (!svnew) {
148 warn3x("cannot start runsv ", d->d_name,
149 " too many services");
150 continue;
151 }
152 sv = svnew;
153 svnum++;
154 memset(&sv[i], 0, sizeof(sv[i]));
155 sv[i].ino = s.st_ino;
156 sv[i].dev = s.st_dev;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000157 /*sv[i].pid = 0;*/
158 /*sv[i].isgone = 0;*/
Denis Vlasenko04c63862006-11-17 18:58:49 +0000159 runsv(i, d->d_name);
160 check = 1;
161 }
162 }
163 if (errno) {
164 warn2_cannot("read directory ", svdir);
165 closedir(dir);
166 check = 1;
167 return;
168 }
169 closedir(dir);
170
171 /* SIGTERM removed runsv's */
172 for (i = 0; i < svnum; i++) {
173 if (!sv[i].isgone)
174 continue;
175 if (sv[i].pid)
176 kill(sv[i].pid, SIGTERM);
177 sv[i] = sv[--svnum];
178 check = 1;
179 }
180}
181
182static int setup_log(void)
183{
184 rploglen = strlen(rplog);
185 if (rploglen < 7) {
186 warnx("log must have at least seven characters");
187 return 0;
188 }
Denis Vlasenko5a6aedd2007-05-26 16:44:20 +0000189 if (pipe(logpipe)) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000190 warnx("cannot create pipe for log");
191 return -1;
192 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000193 close_on_exec_on(logpipe[1]);
194 close_on_exec_on(logpipe[0]);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000195 ndelay_on(logpipe[0]);
196 ndelay_on(logpipe[1]);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000197 if (dup2(logpipe[1], 2) == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000198 warnx("cannot set filedescriptor for log");
199 return -1;
200 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000201 pfd[0].fd = logpipe[0];
202 pfd[0].events = POLLIN;
203 stamplog = monotonic_sec();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000204 return 1;
205}
206
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000207int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000208int runsvdir_main(int argc, char **argv)
209{
210 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000211 dev_t last_dev = last_dev; /* for gcc */
212 ino_t last_ino = last_ino; /* for gcc */
213 time_t last_mtime = 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000214 int wstat;
215 int curdir;
216 int pid;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000217 unsigned deadline;
218 unsigned now;
219 unsigned stampcheck;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000220 char ch;
221 int i;
222
223 argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000224 if (!*argv)
225 bb_show_usage();
226 if (argv[0][0] == '-') {
227 switch (argv[0][1]) {
228 case 'P': set_pgrp = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229 case '-': ++argv;
230 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000231 if (!*argv)
232 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000233 }
234
Denis Vlasenko8c783952007-01-27 22:21:52 +0000235 sig_catch(SIGTERM, s_term);
236 sig_catch(SIGHUP, s_hangup);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000237 svdir = *argv++;
238 if (argv && *argv) {
239 rplog = *argv;
240 if (setup_log() != 1) {
241 rplog = 0;
242 warnx("log service disabled");
243 }
244 }
245 curdir = open_read(".");
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000246 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000247 fatal2_cannot("open current directory", "");
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000248 close_on_exec_on(curdir);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000249
Denis Vlasenko45946f82007-08-20 17:27:40 +0000250 stampcheck = monotonic_sec();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000251
252 for (;;) {
253 /* collect children */
254 for (;;) {
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000255 pid = wait_any_nohang(&wstat);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000256 if (pid <= 0)
257 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258 for (i = 0; i < svnum; i++) {
259 if (pid == sv[i].pid) {
260 /* runsv has gone */
261 sv[i].pid = 0;
262 check = 1;
263 break;
264 }
265 }
266 }
267
Denis Vlasenko45946f82007-08-20 17:27:40 +0000268 now = monotonic_sec();
269 if ((int)(now - stampcheck) >= 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000270 /* wait at least a second */
Denis Vlasenko45946f82007-08-20 17:27:40 +0000271 stampcheck = now + 1;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000272
Denis Vlasenko04c63862006-11-17 18:58:49 +0000273 if (stat(svdir, &s) != -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000274 if (check || s.st_mtime != last_mtime
275 || s.st_ino != last_ino || s.st_dev != last_dev
Denis Vlasenko04c63862006-11-17 18:58:49 +0000276 ) {
277 /* svdir modified */
278 if (chdir(svdir) != -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000279 last_mtime = s.st_mtime;
280 last_dev = s.st_dev;
281 last_ino = s.st_ino;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000282 check = 0;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000283 //if (now <= mtime)
284 // sleep(1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000285 runsvdir();
286 while (fchdir(curdir) == -1) {
287 warn2_cannot("change directory, pausing", "");
288 sleep(5);
289 }
290 } else
291 warn2_cannot("change directory to ", svdir);
292 }
293 } else
294 warn2_cannot("stat ", svdir);
295 }
296
297 if (rplog) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000298 if ((int)(now - stamplog) >= 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000299 write(logpipe[1], ".", 1);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000300 stamplog = now + 900;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000301 }
302 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303
Denis Vlasenko45946f82007-08-20 17:27:40 +0000304 pfd[0].revents = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000305 sig_block(SIGCHLD);
Denis Vlasenko137fbe42007-09-26 12:18:07 +0000306 deadline = (check ? 1 : 5);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000307 if (rplog)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000308 poll(pfd, 1, deadline*1000);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000309 else
Denis Vlasenko45946f82007-08-20 17:27:40 +0000310 sleep(deadline);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000311 sig_unblock(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000312
Denis Vlasenko45946f82007-08-20 17:27:40 +0000313 if (pfd[0].revents & POLLIN) {
314 while (read(logpipe[0], &ch, 1) > 0) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000315 if (ch) {
316 for (i = 6; i < rploglen; i++)
317 rplog[i-1] = rplog[i];
318 rplog[rploglen-1] = ch;
319 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000320 }
321 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000322
323 switch (exitsoon) {
324 case 1:
325 _exit(0);
326 case 2:
327 for (i = 0; i < svnum; i++)
328 if (sv[i].pid)
329 kill(sv[i].pid, SIGTERM);
330 _exit(111);
331 }
332 }
333 /* not reached */
334 return 0;
335}