Contents of /trunk/mkinitrd-magellan/busybox/runit/runsv.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
File MIME type: text/plain
File size: 13611 byte(s)
Sat Sep 1 22:45:15 2007 UTC (17 years ago) by niro
File MIME type: text/plain
File size: 13611 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
1 | /* 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 | static int selfpipe[2]; |
10 | |
11 | /* state */ |
12 | #define S_DOWN 0 |
13 | #define S_RUN 1 |
14 | #define S_FINISH 2 |
15 | /* ctrl */ |
16 | #define C_NOOP 0 |
17 | #define C_TERM 1 |
18 | #define C_PAUSE 2 |
19 | /* want */ |
20 | #define W_UP 0 |
21 | #define W_DOWN 1 |
22 | #define W_EXIT 2 |
23 | |
24 | struct svdir { |
25 | int pid; |
26 | int state; |
27 | int ctrl; |
28 | int want; |
29 | struct taia start; |
30 | int fdlock; |
31 | int fdcontrol; |
32 | int fdcontrolwrite; |
33 | int islog; |
34 | }; |
35 | static struct svdir svd[2]; |
36 | |
37 | static int sigterm = 0; |
38 | static int haslog = 0; |
39 | static int pidchanged = 1; |
40 | static int logpipe[2]; |
41 | static char *dir; |
42 | |
43 | #define usage() bb_show_usage() |
44 | |
45 | static void fatal2_cannot(char *m1, char *m2) |
46 | { |
47 | bb_perror_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2); |
48 | /* was exiting 111 */ |
49 | } |
50 | static void fatal_cannot(char *m) |
51 | { |
52 | fatal2_cannot(m, ""); |
53 | /* was exiting 111 */ |
54 | } |
55 | static void fatal2x_cannot(char *m1, char *m2) |
56 | { |
57 | bb_error_msg_and_die("%s: fatal: cannot %s%s", dir, m1, m2); |
58 | /* was exiting 111 */ |
59 | } |
60 | static void warn_cannot(char *m) |
61 | { |
62 | bb_perror_msg("%s: warning: cannot %s", dir, m); |
63 | } |
64 | static void warnx_cannot(char *m) |
65 | { |
66 | bb_error_msg("%s: warning: cannot %s", dir, m); |
67 | } |
68 | |
69 | static void stopservice(struct svdir *); |
70 | |
71 | static void s_child(int sig_no) |
72 | { |
73 | write(selfpipe[1], "", 1); |
74 | } |
75 | |
76 | static void s_term(int sig_no) |
77 | { |
78 | sigterm = 1; |
79 | write(selfpipe[1], "", 1); /* XXX */ |
80 | } |
81 | |
82 | static char *add_str(char *p, const char *to_add) |
83 | { |
84 | while ((*p = *to_add) != '\0') { |
85 | p++; |
86 | to_add++; |
87 | } |
88 | return p; |
89 | } |
90 | |
91 | static int open_trunc_or_warn(const char *name) |
92 | { |
93 | int fd = open_trunc(name); |
94 | if (fd < 0) |
95 | bb_perror_msg("%s: warning: cannot open %s", |
96 | dir, name); |
97 | return fd; |
98 | } |
99 | |
100 | static int rename_or_warn(const char *old, const char *new) |
101 | { |
102 | if (rename(old, new) == -1) { |
103 | bb_perror_msg("%s: warning: cannot rename %s to %s", |
104 | dir, old, new); |
105 | return -1; |
106 | } |
107 | return 0; |
108 | } |
109 | |
110 | static void update_status(struct svdir *s) |
111 | { |
112 | unsigned long l; |
113 | int fd; |
114 | char status[20]; |
115 | |
116 | /* pid */ |
117 | if (pidchanged) { |
118 | fd = open_trunc_or_warn("supervise/pid.new"); |
119 | if (fd < 0) |
120 | return; |
121 | if (s->pid) { |
122 | char spid[sizeof(s->pid)*3 + 2]; |
123 | int size = sprintf(spid, "%d\n", s->pid); |
124 | write(fd, spid, size); |
125 | } |
126 | close(fd); |
127 | if (s->islog) { |
128 | if (rename_or_warn("supervise/pid.new", "log/supervise/pid")) |
129 | return; |
130 | } else if (rename_or_warn("supervise/pid.new", "supervise/pid")) { |
131 | return; |
132 | } |
133 | pidchanged = 0; |
134 | } |
135 | |
136 | /* stat */ |
137 | fd = open_trunc_or_warn("supervise/stat.new"); |
138 | if (fd < -1) |
139 | return; |
140 | |
141 | { |
142 | char stat_buf[sizeof("finish, paused, got TERM, want down\n")]; |
143 | char *p = stat_buf; |
144 | switch (s->state) { |
145 | case S_DOWN: |
146 | p = add_str(p, "down"); |
147 | break; |
148 | case S_RUN: |
149 | p = add_str(p, "run"); |
150 | break; |
151 | case S_FINISH: |
152 | p = add_str(p, "finish"); |
153 | break; |
154 | } |
155 | if (s->ctrl & C_PAUSE) p = add_str(p, ", paused"); |
156 | if (s->ctrl & C_TERM) p = add_str(p, ", got TERM"); |
157 | if (s->state != S_DOWN) |
158 | switch (s->want) { |
159 | case W_DOWN: |
160 | p = add_str(p, ", want down"); |
161 | break; |
162 | case W_EXIT: |
163 | p = add_str(p, ", want exit"); |
164 | break; |
165 | } |
166 | *p++ = '\n'; |
167 | write(fd, stat_buf, p - stat_buf); |
168 | close(fd); |
169 | } |
170 | |
171 | if (s->islog) { |
172 | rename_or_warn("supervise/stat.new", "log/supervise/stat"); |
173 | } else { |
174 | rename_or_warn("supervise/stat.new", "log/supervise/stat"+4); |
175 | } |
176 | |
177 | /* supervise compatibility */ |
178 | taia_pack(status, &s->start); |
179 | l = (unsigned long)s->pid; |
180 | status[12] = l; l >>=8; |
181 | status[13] = l; l >>=8; |
182 | status[14] = l; l >>=8; |
183 | status[15] = l; |
184 | if (s->ctrl & C_PAUSE) |
185 | status[16] = 1; |
186 | else |
187 | status[16] = 0; |
188 | if (s->want == W_UP) |
189 | status[17] = 'u'; |
190 | else |
191 | status[17] = 'd'; |
192 | if (s->ctrl & C_TERM) |
193 | status[18] = 1; |
194 | else |
195 | status[18] = 0; |
196 | status[19] = s->state; |
197 | fd = open_trunc_or_warn("supervise/status.new"); |
198 | if (fd < 0) |
199 | return; |
200 | l = write(fd, status, sizeof status); |
201 | if (l < 0) { |
202 | warn_cannot("write supervise/status.new"); |
203 | close(fd); |
204 | unlink("supervise/status.new"); |
205 | return; |
206 | } |
207 | close(fd); |
208 | if (l < sizeof status) { |
209 | warnx_cannot("write supervise/status.new: partial write"); |
210 | return; |
211 | } |
212 | if (s->islog) { |
213 | rename_or_warn("supervise/status.new", "log/supervise/status"); |
214 | } else { |
215 | rename_or_warn("supervise/status.new", "log/supervise/status"+4); |
216 | } |
217 | } |
218 | |
219 | static unsigned custom(struct svdir *s, char c) |
220 | { |
221 | int pid; |
222 | int w; |
223 | char a[10]; |
224 | struct stat st; |
225 | char *prog[2]; |
226 | |
227 | if (s->islog) return 0; |
228 | memcpy(a, "control/?", 10); |
229 | a[8] = c; |
230 | if (stat(a, &st) == 0) { |
231 | if (st.st_mode & S_IXUSR) { |
232 | pid = fork(); |
233 | if (pid == -1) { |
234 | warn_cannot("fork for control/?"); |
235 | return 0; |
236 | } |
237 | if (!pid) { |
238 | if (haslog && fd_copy(1, logpipe[1]) == -1) |
239 | warn_cannot("setup stdout for control/?"); |
240 | prog[0] = a; |
241 | prog[1] = 0; |
242 | execve(a, prog, environ); |
243 | fatal_cannot("run control/?"); |
244 | } |
245 | while (wait_pid(&w, pid) == -1) { |
246 | if (errno == EINTR) continue; |
247 | warn_cannot("wait for child control/?"); |
248 | return 0; |
249 | } |
250 | return !wait_exitcode(w); |
251 | } |
252 | } |
253 | else { |
254 | if (errno == ENOENT) return 0; |
255 | warn_cannot("stat control/?"); |
256 | } |
257 | return 0; |
258 | } |
259 | |
260 | static void stopservice(struct svdir *s) |
261 | { |
262 | if (s->pid && ! custom(s, 't')) { |
263 | kill(s->pid, SIGTERM); |
264 | s->ctrl |=C_TERM; |
265 | update_status(s); |
266 | } |
267 | if (s->want == W_DOWN) { |
268 | kill(s->pid, SIGCONT); |
269 | custom(s, 'd'); return; |
270 | } |
271 | if (s->want == W_EXIT) { |
272 | kill(s->pid, SIGCONT); |
273 | custom(s, 'x'); |
274 | } |
275 | } |
276 | |
277 | static void startservice(struct svdir *s) |
278 | { |
279 | int p; |
280 | char *run[2]; |
281 | |
282 | if (s->state == S_FINISH) |
283 | run[0] = "./finish"; |
284 | else { |
285 | run[0] = "./run"; |
286 | custom(s, 'u'); |
287 | } |
288 | run[1] = 0; |
289 | |
290 | if (s->pid != 0) stopservice(s); /* should never happen */ |
291 | while ((p = fork()) == -1) { |
292 | warn_cannot("fork, sleeping"); |
293 | sleep(5); |
294 | } |
295 | if (p == 0) { |
296 | /* child */ |
297 | if (haslog) { |
298 | if (s->islog) { |
299 | if (fd_copy(0, logpipe[0]) == -1) |
300 | fatal_cannot("setup filedescriptor for ./log/run"); |
301 | close(logpipe[1]); |
302 | if (chdir("./log") == -1) |
303 | fatal_cannot("change directory to ./log"); |
304 | } else { |
305 | if (fd_copy(1, logpipe[1]) == -1) |
306 | fatal_cannot("setup filedescriptor for ./run"); |
307 | close(logpipe[0]); |
308 | } |
309 | } |
310 | sig_uncatch(sig_child); |
311 | sig_unblock(sig_child); |
312 | sig_uncatch(sig_term); |
313 | sig_unblock(sig_term); |
314 | execve(*run, run, environ); |
315 | if (s->islog) |
316 | fatal2_cannot("start log/", *run); |
317 | else |
318 | fatal2_cannot("start ", *run); |
319 | } |
320 | if (s->state != S_FINISH) { |
321 | taia_now(&s->start); |
322 | s->state = S_RUN; |
323 | } |
324 | s->pid = p; |
325 | pidchanged = 1; |
326 | s->ctrl = C_NOOP; |
327 | update_status(s); |
328 | } |
329 | |
330 | static int ctrl(struct svdir *s, char c) |
331 | { |
332 | switch (c) { |
333 | case 'd': /* down */ |
334 | s->want = W_DOWN; |
335 | update_status(s); |
336 | if (s->pid && s->state != S_FINISH) stopservice(s); |
337 | break; |
338 | case 'u': /* up */ |
339 | s->want = W_UP; |
340 | update_status(s); |
341 | if (s->pid == 0) startservice(s); |
342 | break; |
343 | case 'x': /* exit */ |
344 | if (s->islog) break; |
345 | s->want = W_EXIT; |
346 | update_status(s); |
347 | if (s->pid && s->state != S_FINISH) stopservice(s); |
348 | break; |
349 | case 't': /* sig term */ |
350 | if (s->pid && s->state != S_FINISH) stopservice(s); |
351 | break; |
352 | case 'k': /* sig kill */ |
353 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGKILL); |
354 | s->state = S_DOWN; |
355 | break; |
356 | case 'p': /* sig pause */ |
357 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGSTOP); |
358 | s->ctrl |=C_PAUSE; |
359 | update_status(s); |
360 | break; |
361 | case 'c': /* sig cont */ |
362 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGCONT); |
363 | if (s->ctrl & C_PAUSE) s->ctrl &=~C_PAUSE; |
364 | update_status(s); |
365 | break; |
366 | case 'o': /* once */ |
367 | s->want = W_DOWN; |
368 | update_status(s); |
369 | if (!s->pid) startservice(s); |
370 | break; |
371 | case 'a': /* sig alarm */ |
372 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGALRM); |
373 | break; |
374 | case 'h': /* sig hup */ |
375 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGHUP); |
376 | break; |
377 | case 'i': /* sig int */ |
378 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGINT); |
379 | break; |
380 | case 'q': /* sig quit */ |
381 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGQUIT); |
382 | break; |
383 | case '1': /* sig usr1 */ |
384 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGUSR1); |
385 | break; |
386 | case '2': /* sig usr2 */ |
387 | if (s->pid && ! custom(s, c)) kill(s->pid, SIGUSR2); |
388 | break; |
389 | } |
390 | return 1; |
391 | } |
392 | |
393 | int runsv_main(int argc, char **argv) |
394 | { |
395 | struct stat s; |
396 | int fd; |
397 | int r; |
398 | char buf[256]; |
399 | |
400 | if (!argv[1] || argv[2]) usage(); |
401 | dir = argv[1]; |
402 | |
403 | if (pipe(selfpipe) == -1) fatal_cannot("create selfpipe"); |
404 | coe(selfpipe[0]); |
405 | coe(selfpipe[1]); |
406 | ndelay_on(selfpipe[0]); |
407 | ndelay_on(selfpipe[1]); |
408 | |
409 | sig_block(sig_child); |
410 | sig_catch(sig_child, s_child); |
411 | sig_block(sig_term); |
412 | sig_catch(sig_term, s_term); |
413 | |
414 | xchdir(dir); |
415 | svd[0].pid = 0; |
416 | svd[0].state = S_DOWN; |
417 | svd[0].ctrl = C_NOOP; |
418 | svd[0].want = W_UP; |
419 | svd[0].islog = 0; |
420 | svd[1].pid = 0; |
421 | taia_now(&svd[0].start); |
422 | if (stat("down", &s) != -1) svd[0].want = W_DOWN; |
423 | |
424 | if (stat("log", &s) == -1) { |
425 | if (errno != ENOENT) |
426 | warn_cannot("stat ./log"); |
427 | } else { |
428 | if (!S_ISDIR(s.st_mode)) |
429 | warnx_cannot("stat log/down: log is not a directory"); |
430 | else { |
431 | haslog = 1; |
432 | svd[1].state = S_DOWN; |
433 | svd[1].ctrl = C_NOOP; |
434 | svd[1].want = W_UP; |
435 | svd[1].islog = 1; |
436 | taia_now(&svd[1].start); |
437 | if (stat("log/down", &s) != -1) |
438 | svd[1].want = W_DOWN; |
439 | if (pipe(logpipe) == -1) |
440 | fatal_cannot("create log pipe"); |
441 | coe(logpipe[0]); |
442 | coe(logpipe[1]); |
443 | } |
444 | } |
445 | |
446 | if (mkdir("supervise", 0700) == -1) { |
447 | r = readlink("supervise", buf, 256); |
448 | if (r != -1) { |
449 | if (r == 256) |
450 | fatal2x_cannot("readlink ./supervise: ", "name too long"); |
451 | buf[r] = 0; |
452 | mkdir(buf, 0700); |
453 | } else { |
454 | if ((errno != ENOENT) && (errno != EINVAL)) |
455 | fatal_cannot("readlink ./supervise"); |
456 | } |
457 | } |
458 | svd[0].fdlock = xopen3("log/supervise/lock"+4, |
459 | O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); |
460 | if (lock_exnb(svd[0].fdlock) == -1) |
461 | fatal_cannot("lock supervise/lock"); |
462 | coe(svd[0].fdlock); |
463 | if (haslog) { |
464 | if (mkdir("log/supervise", 0700) == -1) { |
465 | r = readlink("log/supervise", buf, 256); |
466 | if (r != -1) { |
467 | if (r == 256) |
468 | fatal2x_cannot("readlink ./log/supervise: ", "name too long"); |
469 | buf[r] = 0; |
470 | fd = xopen(".", O_RDONLY|O_NDELAY); |
471 | xchdir("./log"); |
472 | mkdir(buf, 0700); |
473 | if (fchdir(fd) == -1) |
474 | fatal_cannot("change back to service directory"); |
475 | close(fd); |
476 | } |
477 | else { |
478 | if ((errno != ENOENT) && (errno != EINVAL)) |
479 | fatal_cannot("readlink ./log/supervise"); |
480 | } |
481 | } |
482 | svd[1].fdlock = xopen3("log/supervise/lock", |
483 | O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); |
484 | if (lock_ex(svd[1].fdlock) == -1) |
485 | fatal_cannot("lock log/supervise/lock"); |
486 | coe(svd[1].fdlock); |
487 | } |
488 | |
489 | fifo_make("log/supervise/control"+4, 0600); |
490 | svd[0].fdcontrol = xopen("log/supervise/control"+4, O_RDONLY|O_NDELAY); |
491 | coe(svd[0].fdcontrol); |
492 | svd[0].fdcontrolwrite = xopen("log/supervise/control"+4, O_WRONLY|O_NDELAY); |
493 | coe(svd[0].fdcontrolwrite); |
494 | update_status(&svd[0]); |
495 | if (haslog) { |
496 | fifo_make("log/supervise/control", 0600); |
497 | svd[1].fdcontrol = xopen("log/supervise/control", O_RDONLY|O_NDELAY); |
498 | coe(svd[1].fdcontrol); |
499 | svd[1].fdcontrolwrite = xopen("log/supervise/control", O_WRONLY|O_NDELAY); |
500 | coe(svd[1].fdcontrolwrite); |
501 | update_status(&svd[1]); |
502 | } |
503 | fifo_make("log/supervise/ok"+4, 0600); |
504 | fd = xopen("log/supervise/ok"+4, O_RDONLY|O_NDELAY); |
505 | coe(fd); |
506 | if (haslog) { |
507 | fifo_make("log/supervise/ok", 0600); |
508 | fd = xopen("log/supervise/ok", O_RDONLY|O_NDELAY); |
509 | coe(fd); |
510 | } |
511 | for (;;) { |
512 | iopause_fd x[3]; |
513 | struct taia deadline; |
514 | struct taia now; |
515 | char ch; |
516 | |
517 | if (haslog) |
518 | if (!svd[1].pid && svd[1].want == W_UP) |
519 | startservice(&svd[1]); |
520 | if (!svd[0].pid) |
521 | if (svd[0].want == W_UP || svd[0].state == S_FINISH) |
522 | startservice(&svd[0]); |
523 | |
524 | x[0].fd = selfpipe[0]; |
525 | x[0].events = IOPAUSE_READ; |
526 | x[1].fd = svd[0].fdcontrol; |
527 | x[1].events = IOPAUSE_READ; |
528 | if (haslog) { |
529 | x[2].fd = svd[1].fdcontrol; |
530 | x[2].events = IOPAUSE_READ; |
531 | } |
532 | taia_now(&now); |
533 | taia_uint(&deadline, 3600); |
534 | taia_add(&deadline, &now, &deadline); |
535 | |
536 | sig_unblock(sig_term); |
537 | sig_unblock(sig_child); |
538 | iopause(x, 2+haslog, &deadline, &now); |
539 | sig_block(sig_term); |
540 | sig_block(sig_child); |
541 | |
542 | while (read(selfpipe[0], &ch, 1) == 1) |
543 | ; |
544 | for (;;) { |
545 | int child; |
546 | int wstat; |
547 | |
548 | child = wait_nohang(&wstat); |
549 | if (!child) break; |
550 | if ((child == -1) && (errno != EINTR)) break; |
551 | if (child == svd[0].pid) { |
552 | svd[0].pid = 0; |
553 | pidchanged = 1; |
554 | svd[0].ctrl &=~C_TERM; |
555 | if (svd[0].state != S_FINISH) |
556 | fd = open_read("finish"); |
557 | if (fd != -1) { |
558 | close(fd); |
559 | svd[0].state = S_FINISH; |
560 | update_status(&svd[0]); |
561 | continue; |
562 | } |
563 | svd[0].state = S_DOWN; |
564 | taia_uint(&deadline, 1); |
565 | taia_add(&deadline, &svd[0].start, &deadline); |
566 | taia_now(&svd[0].start); |
567 | update_status(&svd[0]); |
568 | if (taia_less(&svd[0].start, &deadline)) sleep(1); |
569 | } |
570 | if (haslog) { |
571 | if (child == svd[1].pid) { |
572 | svd[1].pid = 0; |
573 | pidchanged = 1; |
574 | svd[1].state = S_DOWN; |
575 | svd[1].ctrl &=~C_TERM; |
576 | taia_uint(&deadline, 1); |
577 | taia_add(&deadline, &svd[1].start, &deadline); |
578 | taia_now(&svd[1].start); |
579 | update_status(&svd[1]); |
580 | if (taia_less(&svd[1].start, &deadline)) sleep(1); |
581 | } |
582 | } |
583 | } |
584 | if (read(svd[0].fdcontrol, &ch, 1) == 1) |
585 | ctrl(&svd[0], ch); |
586 | if (haslog) |
587 | if (read(svd[1].fdcontrol, &ch, 1) == 1) |
588 | ctrl(&svd[1], ch); |
589 | |
590 | if (sigterm) { |
591 | ctrl(&svd[0], 'x'); |
592 | sigterm = 0; |
593 | } |
594 | |
595 | if (svd[0].want == W_EXIT && svd[0].state == S_DOWN) { |
596 | if (svd[1].pid == 0) |
597 | _exit(0); |
598 | if (svd[1].want != W_EXIT) { |
599 | svd[1].want = W_EXIT; |
600 | /* stopservice(&svd[1]); */ |
601 | update_status(&svd[1]); |
602 | close(logpipe[1]); |
603 | close(logpipe[0]); |
604 | //if (close(logpipe[1]) == -1) |
605 | // warn_cannot("close logpipe[1]"); |
606 | //if (close(logpipe[0]) == -1) |
607 | // warn_cannot("close logpipe[0]"); |
608 | } |
609 | } |
610 | } |
611 | /* not reached */ |
612 | return 0; |
613 | } |