1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31#include <sys/poll.h>
32#include <sys/file.h>
33#include "libbb.h"
34#include "runit_lib.h"
35
36#define MAXSERVICES 1000
37
38
39#define CHECK_DEVNO_TOO 0
40
41struct service {
42#if CHECK_DEVNO_TOO
43 dev_t dev;
44#endif
45 ino_t ino;
46 pid_t pid;
47 smallint isgone;
48};
49
50struct globals {
51 struct service *sv;
52 char *svdir;
53 int svnum;
54#if ENABLE_FEATURE_RUNSVDIR_LOG
55 char *rplog;
56 int rploglen;
57 struct fd_pair logpipe;
58 struct pollfd pfd[1];
59 unsigned stamplog;
60#endif
61};
62#define G (*(struct globals*)&bb_common_bufsiz1)
63#define sv (G.sv )
64#define svdir (G.svdir )
65#define svnum (G.svnum )
66#define rplog (G.rplog )
67#define rploglen (G.rploglen )
68#define logpipe (G.logpipe )
69#define pfd (G.pfd )
70#define stamplog (G.stamplog )
71#define INIT_G() do { \
72} while (0)
73
74static void fatal2_cannot(const char *m1, const char *m2)
75{
76 bb_perror_msg_and_die("%s: fatal: cannot %s%s", svdir, m1, m2);
77
78}
79static void warn3x(const char *m1, const char *m2, const char *m3)
80{
81 bb_error_msg("%s: warning: %s%s%s", svdir, m1, m2, m3);
82}
83static void warn2_cannot(const char *m1, const char *m2)
84{
85 warn3x("cannot ", m1, m2);
86}
87#if ENABLE_FEATURE_RUNSVDIR_LOG
88static void warnx(const char *m1)
89{
90 warn3x(m1, "", "");
91}
92#endif
93
94
95static NOINLINE pid_t runsv(const char *name)
96{
97 pid_t pid;
98
99
100 if (bb_got_signal)
101 return 0;
102
103 pid = vfork();
104 if (pid == -1) {
105 warn2_cannot("vfork", "");
106 return 0;
107 }
108 if (pid == 0) {
109
110 if (option_mask32 & 1)
111 setsid();
112
113
114
115
116#if 0
117 bb_signals(0
118 | (1 << SIGHUP)
119 | (1 << SIGTERM)
120 , SIG_DFL);
121#endif
122 execlp("runsv", "runsv", name, NULL);
123 fatal2_cannot("start runsv ", name);
124 }
125 return pid;
126}
127
128
129static NOINLINE int do_rescan(void)
130{
131 DIR *dir;
132 direntry *d;
133 int i;
134 struct stat s;
135 int need_rescan = 0;
136
137 dir = opendir(".");
138 if (!dir) {
139 warn2_cannot("open directory ", svdir);
140 return 1;
141 }
142 for (i = 0; i < svnum; i++)
143 sv[i].isgone = 1;
144
145 while (1) {
146 errno = 0;
147 d = readdir(dir);
148 if (!d)
149 break;
150 if (d->d_name[0] == '.')
151 continue;
152 if (stat(d->d_name, &s) == -1) {
153 warn2_cannot("stat ", d->d_name);
154 continue;
155 }
156 if (!S_ISDIR(s.st_mode))
157 continue;
158
159 for (i = 0; i < svnum; i++) {
160 if ((sv[i].ino == s.st_ino)
161#if CHECK_DEVNO_TOO
162 && (sv[i].dev == s.st_dev)
163#endif
164 ) {
165 if (sv[i].pid == 0)
166 goto run_ith_sv;
167 sv[i].isgone = 0;
168 goto next_dentry;
169 }
170 }
171 {
172 struct service *svnew = realloc(sv, (i+1) * sizeof(*sv));
173 if (!svnew) {
174 warn2_cannot("start runsv ", d->d_name);
175 need_rescan = 1;
176 continue;
177 }
178 sv = svnew;
179 svnum++;
180#if CHECK_DEVNO_TOO
181 sv[i].dev = s.st_dev;
182#endif
183 sv[i].ino = s.st_ino;
184 run_ith_sv:
185 sv[i].pid = runsv(d->d_name);
186 sv[i].isgone = 0;
187 }
188 next_dentry: ;
189 }
190 i = errno;
191 closedir(dir);
192 if (i) {
193 warn2_cannot("read directory ", svdir);
194 return 1;
195 }
196
197
198
199 for (i = 0; i < svnum; i++) {
200 if (!sv[i].isgone)
201 continue;
202 if (sv[i].pid)
203 kill(sv[i].pid, SIGTERM);
204 svnum--;
205 sv[i] = sv[svnum];
206 i--;
207 }
208 return need_rescan;
209}
210
211int runsvdir_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
212int runsvdir_main(int argc UNUSED_PARAM, char **argv)
213{
214 struct stat s;
215 dev_t last_dev = last_dev;
216 ino_t last_ino = last_ino;
217 time_t last_mtime = 0;
218 int wstat;
219 int curdir;
220 pid_t pid;
221 unsigned deadline;
222 unsigned now;
223 unsigned stampcheck;
224 int i;
225 int need_rescan = 1;
226 char *opt_s_argv[3];
227
228 INIT_G();
229
230 opt_complementary = "-1";
231 opt_s_argv[0] = NULL;
232 opt_s_argv[2] = NULL;
233 getopt32(argv, "Ps:", &opt_s_argv[0]);
234 argv += optind;
235
236 bb_signals(0
237 | (1 << SIGTERM)
238 | (1 << SIGHUP)
239
240
241
242
243
244
245
246
247 | (getpid() == 1 ? ((1 << SIGUSR1) | (1 << SIGUSR2)) : 0)
248 , record_signo);
249 svdir = *argv++;
250
251#if ENABLE_FEATURE_RUNSVDIR_LOG
252
253 if (*argv) {
254 rplog = *argv;
255 rploglen = strlen(rplog);
256 if (rploglen < 7) {
257 warnx("log must have at least seven characters");
258 } else if (piped_pair(logpipe)) {
259 warnx("cannot create pipe for log");
260 } else {
261 close_on_exec_on(logpipe.rd);
262 close_on_exec_on(logpipe.wr);
263 ndelay_on(logpipe.rd);
264 ndelay_on(logpipe.wr);
265 if (dup2(logpipe.wr, 2) == -1) {
266 warnx("cannot set filedescriptor for log");
267 } else {
268 pfd[0].fd = logpipe.rd;
269 pfd[0].events = POLLIN;
270 stamplog = monotonic_sec();
271 goto run;
272 }
273 }
274 rplog = NULL;
275 warnx("log service disabled");
276 }
277 run:
278#endif
279 curdir = open_read(".");
280 if (curdir == -1)
281 fatal2_cannot("open current directory", "");
282 close_on_exec_on(curdir);
283
284 stampcheck = monotonic_sec();
285
286 for (;;) {
287
288 for (;;) {
289 pid = wait_any_nohang(&wstat);
290 if (pid <= 0)
291 break;
292 for (i = 0; i < svnum; i++) {
293 if (pid == sv[i].pid) {
294
295 sv[i].pid = 0;
296 need_rescan = 1;
297 }
298 }
299 }
300
301 now = monotonic_sec();
302 if ((int)(now - stampcheck) >= 0) {
303
304 stampcheck = now + 1;
305
306 if (stat(svdir, &s) != -1) {
307 if (need_rescan || s.st_mtime != last_mtime
308 || s.st_ino != last_ino || s.st_dev != last_dev
309 ) {
310
311 if (chdir(svdir) != -1) {
312 last_mtime = s.st_mtime;
313 last_dev = s.st_dev;
314 last_ino = s.st_ino;
315
316
317 need_rescan = do_rescan();
318 while (fchdir(curdir) == -1) {
319 warn2_cannot("change directory, pausing", "");
320 sleep(5);
321 }
322 } else {
323 warn2_cannot("change directory to ", svdir);
324 }
325 }
326 } else {
327 warn2_cannot("stat ", svdir);
328 }
329 }
330
331#if ENABLE_FEATURE_RUNSVDIR_LOG
332 if (rplog) {
333 if ((int)(now - stamplog) >= 0) {
334 write(logpipe.wr, ".", 1);
335 stamplog = now + 900;
336 }
337 }
338 pfd[0].revents = 0;
339#endif
340 deadline = (need_rescan ? 1 : 5);
341 sig_block(SIGCHLD);
342#if ENABLE_FEATURE_RUNSVDIR_LOG
343 if (rplog)
344 poll(pfd, 1, deadline*1000);
345 else
346#endif
347 sleep(deadline);
348 sig_unblock(SIGCHLD);
349
350#if ENABLE_FEATURE_RUNSVDIR_LOG
351 if (pfd[0].revents & POLLIN) {
352 char ch;
353 while (read(logpipe.rd, &ch, 1) > 0) {
354 if (ch < ' ')
355 ch = ' ';
356 for (i = 6; i < rploglen; i++)
357 rplog[i-1] = rplog[i];
358 rplog[rploglen-1] = ch;
359 }
360 }
361#endif
362 if (!bb_got_signal)
363 continue;
364
365
366
367
368 if (opt_s_argv[0]) {
369
370 opt_s_argv[1] = utoa(bb_got_signal);
371 pid = spawn(opt_s_argv);
372 if (pid > 0) {
373
374
375 while (wait(NULL) != pid)
376 continue;
377 }
378 }
379
380 switch (bb_got_signal) {
381 case SIGHUP:
382 for (i = 0; i < svnum; i++)
383 if (sv[i].pid)
384 kill(sv[i].pid, SIGTERM);
385
386 default:
387
388 if (getpid() == 1)
389 break;
390 return (SIGHUP == bb_got_signal) ? 111 : EXIT_SUCCESS;
391 }
392
393 bb_got_signal = 0;
394 }
395}
396