blob: 2d2b5db3147b5c7ef9ae2df9c88019da10966e36 [file] [log] [blame]
Denis Vlasenko04c63862006-11-17 18:58:49 +00001/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
2/* TODO: depends on runit_lib.c - review and reduce/eliminate */
3
4#include <sys/poll.h>
5#include <sys/file.h>
6#include "busybox.h"
7#include "runit_lib.h"
8
9#define MAXSERVICES 1000
10
11static char *svdir;
12static unsigned long dev;
13static unsigned long ino;
14static struct service {
15 unsigned long dev;
16 unsigned long ino;
17 int pid;
18 int isgone;
19} *sv;
20static int svnum;
21static int check = 1;
22static char *rplog;
23static int rploglen;
24static int logpipe[2];
25static iopause_fd io[1];
26static struct taia stamplog;
27static int exitsoon;
28static int pgrp;
29
30#define usage() bb_show_usage()
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000031static void fatal2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +000032{
33 bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
34 /* was exiting 100 */
35}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000036static void warn3x(const char *m1, const char *m2, const char *m3)
Denis Vlasenko04c63862006-11-17 18:58:49 +000037{
38 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
Denis Vlasenkof7996f32007-01-11 17:20:00 +000039}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000040static void warn2_cannot(const char *m1, const char *m2)
Denis Vlasenko04c63862006-11-17 18:58:49 +000041{
42 warn3x("cannot ", m1, m2);
43}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000044static void warnx(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +000045{
46 warn3x(m1, "", "");
Denis Vlasenkof7996f32007-01-11 17:20:00 +000047}
Denis Vlasenko04c63862006-11-17 18:58:49 +000048
49static void s_term(int sig_no)
50{
51 exitsoon = 1;
52}
53static void s_hangup(int sig_no)
54{
55 exitsoon = 2;
56}
57
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000058static void runsv(int no, const char *name)
Denis Vlasenko04c63862006-11-17 18:58:49 +000059{
60 int pid = fork();
61
62 if (pid == -1) {
63 warn2_cannot("fork for ", name);
64 return;
65 }
66 if (pid == 0) {
67 /* child */
68 char *prog[3];
69
Denis Vlasenkoab2aea42007-01-29 22:51:58 +000070 prog[0] = (char*)"runsv";
71 prog[1] = (char*)name;
Denis Vlasenkoe2473f82007-01-27 22:22:17 +000072 prog[2] = NULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +000073 sig_uncatch(SIGHUP);
74 sig_uncatch(SIGTERM);
Denis Vlasenko04c63862006-11-17 18:58:49 +000075 if (pgrp) setsid();
Denis Vlasenko1d76f432007-02-06 01:20:12 +000076 BB_EXECVP(prog[0], prog);
Denis Vlasenko04c63862006-11-17 18:58:49 +000077 //pathexec_run(*prog, prog, (char* const*)environ);
78 fatal2_cannot("start runsv ", name);
79 }
80 sv[no].pid = pid;
81}
82
83static void runsvdir(void)
84{
85 DIR *dir;
86 direntry *d;
87 int i;
88 struct stat s;
89
90 dir = opendir(".");
91 if (!dir) {
92 warn2_cannot("open directory ", svdir);
93 return;
94 }
95 for (i = 0; i < svnum; i++)
96 sv[i].isgone = 1;
97 errno = 0;
98 while ((d = readdir(dir))) {
99 if (d->d_name[0] == '.') continue;
100 if (stat(d->d_name, &s) == -1) {
101 warn2_cannot("stat ", d->d_name);
102 errno = 0;
103 continue;
104 }
105 if (!S_ISDIR(s.st_mode)) continue;
106 for (i = 0; i < svnum; i++) {
107 if ((sv[i].ino == s.st_ino) && (sv[i].dev == s.st_dev)) {
108 sv[i].isgone = 0;
109 if (!sv[i].pid)
110 runsv(i, d->d_name);
111 break;
112 }
113 }
114 if (i == svnum) {
115 /* new service */
116 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
117 if (!svnew) {
118 warn3x("cannot start runsv ", d->d_name,
119 " too many services");
120 continue;
121 }
122 sv = svnew;
123 svnum++;
124 memset(&sv[i], 0, sizeof(sv[i]));
125 sv[i].ino = s.st_ino;
126 sv[i].dev = s.st_dev;
127 //sv[i].pid = 0;
128 //sv[i].isgone = 0;
129 runsv(i, d->d_name);
130 check = 1;
131 }
132 }
133 if (errno) {
134 warn2_cannot("read directory ", svdir);
135 closedir(dir);
136 check = 1;
137 return;
138 }
139 closedir(dir);
140
141 /* SIGTERM removed runsv's */
142 for (i = 0; i < svnum; i++) {
143 if (!sv[i].isgone)
144 continue;
145 if (sv[i].pid)
146 kill(sv[i].pid, SIGTERM);
147 sv[i] = sv[--svnum];
148 check = 1;
149 }
150}
151
152static int setup_log(void)
153{
154 rploglen = strlen(rplog);
155 if (rploglen < 7) {
156 warnx("log must have at least seven characters");
157 return 0;
158 }
159 if (pipe(logpipe) == -1) {
160 warnx("cannot create pipe for log");
161 return -1;
162 }
163 coe(logpipe[1]);
164 coe(logpipe[0]);
165 ndelay_on(logpipe[0]);
166 ndelay_on(logpipe[1]);
167 if (fd_copy(2, logpipe[1]) == -1) {
168 warnx("cannot set filedescriptor for log");
169 return -1;
170 }
171 io[0].fd = logpipe[0];
172 io[0].events = IOPAUSE_READ;
173 taia_now(&stamplog);
174 return 1;
175}
176
Denis Vlasenko06af2162007-02-03 17:28:39 +0000177int runsvdir_main(int argc, char **argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000178int runsvdir_main(int argc, char **argv)
179{
180 struct stat s;
181 time_t mtime = 0;
182 int wstat;
183 int curdir;
184 int pid;
185 struct taia deadline;
186 struct taia now;
187 struct taia stampcheck;
188 char ch;
189 int i;
190
191 argv++;
192 if (!argv || !*argv) usage();
193 if (**argv == '-') {
194 switch (*(*argv + 1)) {
195 case 'P': pgrp = 1;
196 case '-': ++argv;
197 }
198 if (!argv || !*argv) usage();
199 }
200
Denis Vlasenko8c783952007-01-27 22:21:52 +0000201 sig_catch(SIGTERM, s_term);
202 sig_catch(SIGHUP, s_hangup);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000203 svdir = *argv++;
204 if (argv && *argv) {
205 rplog = *argv;
206 if (setup_log() != 1) {
207 rplog = 0;
208 warnx("log service disabled");
209 }
210 }
211 curdir = open_read(".");
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000212 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000213 fatal2_cannot("open current directory", "");
214 coe(curdir);
215
216 taia_now(&stampcheck);
217
218 for (;;) {
219 /* collect children */
220 for (;;) {
221 pid = wait_nohang(&wstat);
222 if (pid <= 0) break;
223 for (i = 0; i < svnum; i++) {
224 if (pid == sv[i].pid) {
225 /* runsv has gone */
226 sv[i].pid = 0;
227 check = 1;
228 break;
229 }
230 }
231 }
232
233 taia_now(&now);
234 if (now.sec.x < (stampcheck.sec.x - 3)) {
235 /* time warp */
236 warnx("time warp: resetting time stamp");
237 taia_now(&stampcheck);
238 taia_now(&now);
239 if (rplog) taia_now(&stamplog);
240 }
241 if (taia_less(&now, &stampcheck) == 0) {
242 /* wait at least a second */
243 taia_uint(&deadline, 1);
244 taia_add(&stampcheck, &now, &deadline);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000245
Denis Vlasenko04c63862006-11-17 18:58:49 +0000246 if (stat(svdir, &s) != -1) {
247 if (check || s.st_mtime != mtime
248 || s.st_ino != ino || s.st_dev != dev
249 ) {
250 /* svdir modified */
251 if (chdir(svdir) != -1) {
252 mtime = s.st_mtime;
253 dev = s.st_dev;
254 ino = s.st_ino;
255 check = 0;
256 if (now.sec.x <= (4611686018427387914ULL + (uint64_t)mtime))
257 sleep(1);
258 runsvdir();
259 while (fchdir(curdir) == -1) {
260 warn2_cannot("change directory, pausing", "");
261 sleep(5);
262 }
263 } else
264 warn2_cannot("change directory to ", svdir);
265 }
266 } else
267 warn2_cannot("stat ", svdir);
268 }
269
270 if (rplog) {
271 if (taia_less(&now, &stamplog) == 0) {
272 write(logpipe[1], ".", 1);
273 taia_uint(&deadline, 900);
274 taia_add(&stamplog, &now, &deadline);
275 }
276 }
277 taia_uint(&deadline, check ? 1 : 5);
278 taia_add(&deadline, &now, &deadline);
279
Denis Vlasenko8c783952007-01-27 22:21:52 +0000280 sig_block(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000281 if (rplog)
282 iopause(io, 1, &deadline, &now);
283 else
284 iopause(0, 0, &deadline, &now);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000285 sig_unblock(SIGCHLD);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000286
287 if (rplog && (io[0].revents | IOPAUSE_READ))
288 while (read(logpipe[0], &ch, 1) > 0)
289 if (ch) {
290 for (i = 6; i < rploglen; i++)
291 rplog[i-1] = rplog[i];
292 rplog[rploglen-1] = ch;
293 }
294
295 switch (exitsoon) {
296 case 1:
297 _exit(0);
298 case 2:
299 for (i = 0; i < svnum; i++)
300 if (sv[i].pid)
301 kill(sv[i].pid, SIGTERM);
302 _exit(111);
303 }
304 }
305 /* not reached */
306 return 0;
307}