Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/runit/sv.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 984 - (show annotations) (download)
Sun May 30 11:32:42 2010 UTC (13 years, 11 months ago) by niro
File MIME type: text/plain
File size: 15818 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, 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
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /* Taken from http://smarden.sunsite.dk/runit/sv.8.html:
29
30 sv - control and manage services monitored by runsv
31
32 sv [-v] [-w sec] command services
33 /etc/init.d/service [-w sec] command
34
35 The sv program reports the current status and controls the state of services
36 monitored by the runsv(8) supervisor.
37
38 services consists of one or more arguments, each argument naming a directory
39 service used by runsv(8). If service doesn't start with a dot or slash,
40 it is searched in the default services directory /var/service/, otherwise
41 relative to the current directory.
42
43 command is one of up, down, status, once, pause, cont, hup, alarm, interrupt,
44 1, 2, term, kill, or exit, or start, stop, restart, shutdown, force-stop,
45 force-reload, force-restart, force-shutdown.
46
47 The sv program can be sym-linked to /etc/init.d/ to provide an LSB init
48 script interface. The service to be controlled then is specified by the
49 base name of the "init script".
50
51 status
52 Report the current status of the service, and the appendant log service
53 if available, to standard output.
54 up
55 If the service is not running, start it. If the service stops, restart it.
56 down
57 If the service is running, send it the TERM signal, and the CONT signal.
58 If ./run exits, start ./finish if it exists. After it stops, do not
59 restart service.
60 once
61 If the service is not running, start it. Do not restart it if it stops.
62 pause cont hup alarm interrupt quit 1 2 term kill
63 If the service is running, send it the STOP, CONT, HUP, ALRM, INT, QUIT,
64 USR1, USR2, TERM, or KILL signal respectively.
65 exit
66 If the service is running, send it the TERM signal, and the CONT signal.
67 Do not restart the service. If the service is down, and no log service
68 exists, runsv(8) exits. If the service is down and a log service exists,
69 send the TERM signal to the log service. If the log service is down,
70 runsv(8) exits. This command is ignored if it is given to an appendant
71 log service.
72
73 sv actually looks only at the first character of above commands.
74
75 Commands compatible to LSB init script actions:
76
77 status
78 Same as status.
79 start
80 Same as up, but wait up to 7 seconds for the command to take effect.
81 Then report the status or timeout. If the script ./check exists in
82 the service directory, sv runs this script to check whether the service
83 is up and available; it's considered to be available if ./check exits
84 with 0.
85 stop
86 Same as down, but wait up to 7 seconds for the service to become down.
87 Then report the status or timeout.
88 restart
89 Send the commands term, cont, and up to the service, and wait up to
90 7 seconds for the service to restart. Then report the status or timeout.
91 If the script ./check exists in the service directory, sv runs this script
92 to check whether the service is up and available again; it's considered
93 to be available if ./check exits with 0.
94 shutdown
95 Same as exit, but wait up to 7 seconds for the runsv(8) process
96 to terminate. Then report the status or timeout.
97 force-stop
98 Same as down, but wait up to 7 seconds for the service to become down.
99 Then report the status, and on timeout send the service the kill command.
100 force-reload
101 Send the service the term and cont commands, and wait up to
102 7 seconds for the service to restart. Then report the status,
103 and on timeout send the service the kill command.
104 force-restart
105 Send the service the term, cont and up commands, and wait up to
106 7 seconds for the service to restart. Then report the status, and
107 on timeout send the service the kill command. If the script ./check
108 exists in the service directory, sv runs this script to check whether
109 the service is up and available again; it's considered to be available
110 if ./check exits with 0.
111 force-shutdown
112 Same as exit, but wait up to 7 seconds for the runsv(8) process to
113 terminate. Then report the status, and on timeout send the service
114 the kill command.
115
116 Additional Commands
117
118 check
119 Check for the service to be in the state that's been requested. Wait up to
120 7 seconds for the service to reach the requested state, then report
121 the status or timeout. If the requested state of the service is up,
122 and the script ./check exists in the service directory, sv runs
123 this script to check whether the service is up and running;
124 it's considered to be up if ./check exits with 0.
125
126 Options
127
128 -v
129 wait up to 7 seconds for the command to take effect.
130 Then report the status or timeout.
131 -w sec
132 Override the default timeout of 7 seconds with sec seconds. Implies -v.
133
134 Environment
135
136 SVDIR
137 The environment variable $SVDIR overrides the default services directory
138 /var/service.
139 SVWAIT
140 The environment variable $SVWAIT overrides the default 7 seconds to wait
141 for a command to take effect. It is overridden by the -w option.
142
143 Exit Codes
144 sv exits 0, if the command was successfully sent to all services, and,
145 if it was told to wait, the command has taken effect to all services.
146
147 For each service that caused an error (e.g. the directory is not
148 controlled by a runsv(8) process, or sv timed out while waiting),
149 sv increases the exit code by one and exits non zero. The maximum
150 is 99. sv exits 100 on error.
151 */
152
153 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
154 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
155
156 #include <sys/poll.h>
157 #include <sys/file.h>
158 #include "libbb.h"
159 #include "runit_lib.h"
160
161 struct globals {
162 const char *acts;
163 char **service;
164 unsigned rc;
165 /* "Bernstein" time format: unix + 0x400000000000000aULL */
166 uint64_t tstart, tnow;
167 svstatus_t svstatus;
168 };
169 #define G (*(struct globals*)&bb_common_bufsiz1)
170 #define acts (G.acts )
171 #define service (G.service )
172 #define rc (G.rc )
173 #define tstart (G.tstart )
174 #define tnow (G.tnow )
175 #define svstatus (G.svstatus )
176 #define INIT_G() do { } while (0)
177
178
179 static void fatal_cannot(const char *m1) NORETURN;
180 static void fatal_cannot(const char *m1)
181 {
182 bb_perror_msg("fatal: can't %s", m1);
183 _exit(151);
184 }
185
186 static void out(const char *p, const char *m1)
187 {
188 printf("%s%s: %s", p, *service, m1);
189 if (errno) {
190 printf(": %s", strerror(errno));
191 }
192 bb_putchar('\n'); /* will also flush the output */
193 }
194
195 #define WARN "warning: "
196 #define OK "ok: "
197
198 static void fail(const char *m1)
199 {
200 ++rc;
201 out("fail: ", m1);
202 }
203 static void failx(const char *m1)
204 {
205 errno = 0;
206 fail(m1);
207 }
208 static void warn(const char *m1)
209 {
210 ++rc;
211 /* "warning: <service>: <m1>\n" */
212 out("warning: ", m1);
213 }
214 static void ok(const char *m1)
215 {
216 errno = 0;
217 out(OK, m1);
218 }
219
220 static int svstatus_get(void)
221 {
222 int fd, r;
223
224 fd = open_write("supervise/ok");
225 if (fd == -1) {
226 if (errno == ENODEV) {
227 *acts == 'x' ? ok("runsv not running")
228 : failx("runsv not running");
229 return 0;
230 }
231 warn("can't open supervise/ok");
232 return -1;
233 }
234 close(fd);
235 fd = open_read("supervise/status");
236 if (fd == -1) {
237 warn("can't open supervise/status");
238 return -1;
239 }
240 r = read(fd, &svstatus, 20);
241 close(fd);
242 switch (r) {
243 case 20:
244 break;
245 case -1:
246 warn("can't read supervise/status");
247 return -1;
248 default:
249 errno = 0;
250 warn("can't read supervise/status: bad format");
251 return -1;
252 }
253 return 1;
254 }
255
256 static unsigned svstatus_print(const char *m)
257 {
258 int diff;
259 int pid;
260 int normallyup = 0;
261 struct stat s;
262 uint64_t timestamp;
263
264 if (stat("down", &s) == -1) {
265 if (errno != ENOENT) {
266 bb_perror_msg(WARN"can't stat %s/down", *service);
267 return 0;
268 }
269 normallyup = 1;
270 }
271 pid = SWAP_LE32(svstatus.pid_le32);
272 timestamp = SWAP_BE64(svstatus.time_be64);
273 if (pid) {
274 switch (svstatus.run_or_finish) {
275 case 1: printf("run: "); break;
276 case 2: printf("finish: "); break;
277 }
278 printf("%s: (pid %d) ", m, pid);
279 } else {
280 printf("down: %s: ", m);
281 }
282 diff = tnow - timestamp;
283 printf("%us", (diff < 0 ? 0 : diff));
284 if (pid) {
285 if (!normallyup) printf(", normally down");
286 if (svstatus.paused) printf(", paused");
287 if (svstatus.want == 'd') printf(", want down");
288 if (svstatus.got_term) printf(", got TERM");
289 } else {
290 if (normallyup) printf(", normally up");
291 if (svstatus.want == 'u') printf(", want up");
292 }
293 return pid ? 1 : 2;
294 }
295
296 static int status(const char *unused UNUSED_PARAM)
297 {
298 int r;
299
300 if (svstatus_get() <= 0)
301 return 0;
302
303 r = svstatus_print(*service);
304 if (chdir("log") == -1) {
305 if (errno != ENOENT) {
306 printf("; log: "WARN"can't change to log service directory: %s",
307 strerror(errno));
308 }
309 } else if (svstatus_get()) {
310 printf("; ");
311 svstatus_print("log");
312 }
313 bb_putchar('\n'); /* will also flush the output */
314 return r;
315 }
316
317 static int checkscript(void)
318 {
319 char *prog[2];
320 struct stat s;
321 int pid, w;
322
323 if (stat("check", &s) == -1) {
324 if (errno == ENOENT) return 1;
325 bb_perror_msg(WARN"can't stat %s/check", *service);
326 return 0;
327 }
328 /* if (!(s.st_mode & S_IXUSR)) return 1; */
329 prog[0] = (char*)"./check";
330 prog[1] = NULL;
331 pid = spawn(prog);
332 if (pid <= 0) {
333 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
334 return 0;
335 }
336 while (safe_waitpid(pid, &w, 0) == -1) {
337 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
338 return 0;
339 }
340 return WEXITSTATUS(w) == 0;
341 }
342
343 static int check(const char *a)
344 {
345 int r;
346 unsigned pid_le32;
347 uint64_t timestamp;
348
349 r = svstatus_get();
350 if (r == -1)
351 return -1;
352 if (r == 0) {
353 if (*a == 'x')
354 return 1;
355 return -1;
356 }
357 pid_le32 = svstatus.pid_le32;
358 switch (*a) {
359 case 'x':
360 return 0;
361 case 'u':
362 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
363 if (!checkscript()) return 0;
364 break;
365 case 'd':
366 if (pid_le32) return 0;
367 break;
368 case 'c':
369 if (pid_le32 && !checkscript()) return 0;
370 break;
371 case 't':
372 if (!pid_le32 && svstatus.want == 'd') break;
373 timestamp = SWAP_BE64(svstatus.time_be64);
374 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
375 return 0;
376 break;
377 case 'o':
378 timestamp = SWAP_BE64(svstatus.time_be64);
379 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
380 return 0;
381 }
382 printf(OK);
383 svstatus_print(*service);
384 bb_putchar('\n'); /* will also flush the output */
385 return 1;
386 }
387
388 static int control(const char *a)
389 {
390 int fd, r, l;
391
392 /* Is it an optimization?
393 It causes problems with "sv o SRV; ...; sv d SRV"
394 ('d' is not passed to SRV because its .want == 'd'):
395 if (svstatus_get() <= 0)
396 return -1;
397 if (svstatus.want == *a)
398 return 0;
399 */
400 fd = open_write("supervise/control");
401 if (fd == -1) {
402 if (errno != ENODEV)
403 warn("can't open supervise/control");
404 else
405 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
406 return -1;
407 }
408 l = strlen(a);
409 r = write(fd, a, l);
410 close(fd);
411 if (r != l) {
412 warn("can't write to supervise/control");
413 return -1;
414 }
415 return 1;
416 }
417
418 int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
419 int sv_main(int argc UNUSED_PARAM, char **argv)
420 {
421 unsigned opt;
422 char *x;
423 char *action;
424 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
425 unsigned waitsec = 7;
426 smallint kll = 0;
427 int verbose = 0;
428 int (*act)(const char*);
429 int (*cbk)(const char*);
430 int curdir;
431
432 INIT_G();
433
434 xfunc_error_retval = 100;
435
436 x = getenv("SVDIR");
437 if (x) varservice = x;
438 x = getenv("SVWAIT");
439 if (x) waitsec = xatou(x);
440
441 opt_complementary = "w+:vv"; /* -w N, -v is a counter */
442 opt = getopt32(argv, "w:v", &waitsec, &verbose);
443 argv += optind;
444 action = *argv++;
445 if (!action || !*argv) bb_show_usage();
446
447 tnow = time(NULL) + 0x400000000000000aULL;
448 tstart = tnow;
449 curdir = open_read(".");
450 if (curdir == -1)
451 fatal_cannot("open current directory");
452
453 act = &control;
454 acts = "s";
455 cbk = &check;
456
457 switch (*action) {
458 case 'x':
459 case 'e':
460 acts = "x";
461 if (!verbose) cbk = NULL;
462 break;
463 case 'X':
464 case 'E':
465 acts = "x";
466 kll = 1;
467 break;
468 case 'D':
469 acts = "d";
470 kll = 1;
471 break;
472 case 'T':
473 acts = "tc";
474 kll = 1;
475 break;
476 case 'c':
477 if (str_equal(action, "check")) {
478 act = NULL;
479 acts = "c";
480 break;
481 }
482 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
483 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
484 action[1] = '\0';
485 acts = action;
486 if (!verbose) cbk = NULL;
487 break;
488 case 's':
489 if (str_equal(action, "shutdown")) {
490 acts = "x";
491 break;
492 }
493 if (str_equal(action, "start")) {
494 acts = "u";
495 break;
496 }
497 if (str_equal(action, "stop")) {
498 acts = "d";
499 break;
500 }
501 /* "status" */
502 act = &status;
503 cbk = NULL;
504 break;
505 case 'r':
506 if (str_equal(action, "restart")) {
507 acts = "tcu";
508 break;
509 }
510 bb_show_usage();
511 case 'f':
512 if (str_equal(action, "force-reload")) {
513 acts = "tc";
514 kll = 1;
515 break;
516 }
517 if (str_equal(action, "force-restart")) {
518 acts = "tcu";
519 kll = 1;
520 break;
521 }
522 if (str_equal(action, "force-shutdown")) {
523 acts = "x";
524 kll = 1;
525 break;
526 }
527 if (str_equal(action, "force-stop")) {
528 acts = "d";
529 kll = 1;
530 break;
531 }
532 default:
533 bb_show_usage();
534 }
535
536 service = argv;
537 while ((x = *service) != NULL) {
538 if (x[0] != '/' && x[0] != '.') {
539 if (chdir(varservice) == -1)
540 goto chdir_failed_0;
541 }
542 if (chdir(x) == -1) {
543 chdir_failed_0:
544 fail("can't change to service directory");
545 goto nullify_service_0;
546 }
547 if (act && (act(acts) == -1)) {
548 nullify_service_0:
549 *service = (char*) -1L; /* "dead" */
550 }
551 if (fchdir(curdir) == -1)
552 fatal_cannot("change to original directory");
553 service++;
554 }
555
556 if (cbk) while (1) {
557 int want_exit;
558 int diff;
559
560 diff = tnow - tstart;
561 service = argv;
562 want_exit = 1;
563 while ((x = *service) != NULL) {
564 if (x == (char*) -1L) /* "dead" */
565 goto next;
566 if (x[0] != '/' && x[0] != '.') {
567 if (chdir(varservice) == -1)
568 goto chdir_failed;
569 }
570 if (chdir(x) == -1) {
571 chdir_failed:
572 fail("can't change to service directory");
573 goto nullify_service;
574 }
575 if (cbk(acts) != 0)
576 goto nullify_service;
577 want_exit = 0;
578 if (diff >= waitsec) {
579 printf(kll ? "kill: " : "timeout: ");
580 if (svstatus_get() > 0) {
581 svstatus_print(x);
582 ++rc;
583 }
584 bb_putchar('\n'); /* will also flush the output */
585 if (kll)
586 control("k");
587 nullify_service:
588 *service = (char*) -1L; /* "dead" */
589 }
590 if (fchdir(curdir) == -1)
591 fatal_cannot("change to original directory");
592 next:
593 service++;
594 }
595 if (want_exit) break;
596 usleep(420000);
597 tnow = time(NULL) + 0x400000000000000aULL;
598 }
599 return rc > 99 ? 99 : rc;
600 }