source: MondoRescue/branches/3.2/mindi-busybox/runit/sv.c@ 3232

Last change on this file since 3232 was 3232, checked in by Bruno Cornec, 10 years ago
  • Update mindi-busybox to 1.21.1
  • Property svn:eol-style set to native
File size: 16.5 KB
Line 
1/*
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
28/* Taken from http://smarden.sunsite.dk/runit/sv.8.html:
29
30sv - control and manage services monitored by runsv
31
32sv [-v] [-w sec] command services
33/etc/init.d/service [-w sec] command
34
35The sv program reports the current status and controls the state of services
36monitored by the runsv(8) supervisor.
37
38services consists of one or more arguments, each argument naming a directory
39service used by runsv(8). If service doesn't start with a dot or slash,
40it is searched in the default services directory /var/service/, otherwise
41relative to the current directory.
42
43command is one of up, down, status, once, pause, cont, hup, alarm, interrupt,
441, 2, term, kill, or exit, or start, stop, restart, shutdown, force-stop,
45force-reload, force-restart, force-shutdown.
46
47The sv program can be sym-linked to /etc/init.d/ to provide an LSB init
48script interface. The service to be controlled then is specified by the
49base name of the "init script".
50
51status
52 Report the current status of the service, and the appendant log service
53 if available, to standard output.
54up
55 If the service is not running, start it. If the service stops, restart it.
56down
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.
60once
61 If the service is not running, start it. Do not restart it if it stops.
62pause 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.
65exit
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
73sv actually looks only at the first character of above commands.
74
75Commands compatible to LSB init script actions:
76
77status
78 Same as status.
79start
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.
85stop
86 Same as down, but wait up to 7 seconds for the service to become down.
87 Then report the status or timeout.
88restart
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.
94shutdown
95 Same as exit, but wait up to 7 seconds for the runsv(8) process
96 to terminate. Then report the status or timeout.
97force-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.
100force-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.
104force-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.
111force-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
116Additional Commands
117
118check
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
126Options
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
134Environment
135
136SVDIR
137 The environment variable $SVDIR overrides the default services directory
138 /var/service.
139SVWAIT
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
143Exit 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//usage:#define sv_trivial_usage
157//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
158//usage:#define sv_full_usage "\n\n"
159//usage: "Control services monitored by runsv supervisor.\n"
160//usage: "Commands (only first character is enough):\n"
161//usage: "\n"
162//usage: "status: query service status\n"
163//usage: "up: if service isn't running, start it. If service stops, restart it\n"
164//usage: "once: like 'up', but if service stops, don't restart it\n"
165//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
166//usage: " if it exists. After it stops, don't restart service\n"
167//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
168//usage: " runsv exits too\n"
169//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
170//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
171
172#include <sys/poll.h>
173#include <sys/file.h>
174#include "libbb.h"
175#include "runit_lib.h"
176
177struct globals {
178 const char *acts;
179 char **service;
180 unsigned rc;
181/* "Bernstein" time format: unix + 0x400000000000000aULL */
182 uint64_t tstart, tnow;
183 svstatus_t svstatus;
184} FIX_ALIASING;
185#define G (*(struct globals*)&bb_common_bufsiz1)
186#define acts (G.acts )
187#define service (G.service )
188#define rc (G.rc )
189#define tstart (G.tstart )
190#define tnow (G.tnow )
191#define svstatus (G.svstatus )
192#define INIT_G() do { } while (0)
193
194
195#define str_equal(s,t) (!strcmp((s), (t)))
196
197
198static void fatal_cannot(const char *m1) NORETURN;
199static void fatal_cannot(const char *m1)
200{
201 bb_perror_msg("fatal: can't %s", m1);
202 _exit(151);
203}
204
205static void out(const char *p, const char *m1)
206{
207 printf("%s%s: %s", p, *service, m1);
208 if (errno) {
209 printf(": %s", strerror(errno));
210 }
211 bb_putchar('\n'); /* will also flush the output */
212}
213
214#define WARN "warning: "
215#define OK "ok: "
216
217static void fail(const char *m1)
218{
219 ++rc;
220 out("fail: ", m1);
221}
222static void failx(const char *m1)
223{
224 errno = 0;
225 fail(m1);
226}
227static void warn(const char *m1)
228{
229 ++rc;
230 /* "warning: <service>: <m1>\n" */
231 out("warning: ", m1);
232}
233static void ok(const char *m1)
234{
235 errno = 0;
236 out(OK, m1);
237}
238
239static int svstatus_get(void)
240{
241 int fd, r;
242
243 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
244 if (fd == -1) {
245 if (errno == ENODEV) {
246 *acts == 'x' ? ok("runsv not running")
247 : failx("runsv not running");
248 return 0;
249 }
250 warn("can't open supervise/ok");
251 return -1;
252 }
253 close(fd);
254 fd = open("supervise/status", O_RDONLY|O_NDELAY);
255 if (fd == -1) {
256 warn("can't open supervise/status");
257 return -1;
258 }
259 r = read(fd, &svstatus, 20);
260 close(fd);
261 switch (r) {
262 case 20:
263 break;
264 case -1:
265 warn("can't read supervise/status");
266 return -1;
267 default:
268 errno = 0;
269 warn("can't read supervise/status: bad format");
270 return -1;
271 }
272 return 1;
273}
274
275static unsigned svstatus_print(const char *m)
276{
277 int diff;
278 int pid;
279 int normallyup = 0;
280 struct stat s;
281 uint64_t timestamp;
282
283 if (stat("down", &s) == -1) {
284 if (errno != ENOENT) {
285 bb_perror_msg(WARN"can't stat %s/down", *service);
286 return 0;
287 }
288 normallyup = 1;
289 }
290 pid = SWAP_LE32(svstatus.pid_le32);
291 timestamp = SWAP_BE64(svstatus.time_be64);
292 if (pid) {
293 switch (svstatus.run_or_finish) {
294 case 1: printf("run: "); break;
295 case 2: printf("finish: "); break;
296 }
297 printf("%s: (pid %d) ", m, pid);
298 } else {
299 printf("down: %s: ", m);
300 }
301 diff = tnow - timestamp;
302 printf("%us", (diff < 0 ? 0 : diff));
303 if (pid) {
304 if (!normallyup) printf(", normally down");
305 if (svstatus.paused) printf(", paused");
306 if (svstatus.want == 'd') printf(", want down");
307 if (svstatus.got_term) printf(", got TERM");
308 } else {
309 if (normallyup) printf(", normally up");
310 if (svstatus.want == 'u') printf(", want up");
311 }
312 return pid ? 1 : 2;
313}
314
315static int status(const char *unused UNUSED_PARAM)
316{
317 int r;
318
319 if (svstatus_get() <= 0)
320 return 0;
321
322 r = svstatus_print(*service);
323 if (chdir("log") == -1) {
324 if (errno != ENOENT) {
325 printf("; log: "WARN"can't change to log service directory: %s",
326 strerror(errno));
327 }
328 } else if (svstatus_get()) {
329 printf("; ");
330 svstatus_print("log");
331 }
332 bb_putchar('\n'); /* will also flush the output */
333 return r;
334}
335
336static int checkscript(void)
337{
338 char *prog[2];
339 struct stat s;
340 int pid, w;
341
342 if (stat("check", &s) == -1) {
343 if (errno == ENOENT) return 1;
344 bb_perror_msg(WARN"can't stat %s/check", *service);
345 return 0;
346 }
347 /* if (!(s.st_mode & S_IXUSR)) return 1; */
348 prog[0] = (char*)"./check";
349 prog[1] = NULL;
350 pid = spawn(prog);
351 if (pid <= 0) {
352 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
353 return 0;
354 }
355 while (safe_waitpid(pid, &w, 0) == -1) {
356 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
357 return 0;
358 }
359 return WEXITSTATUS(w) == 0;
360}
361
362static int check(const char *a)
363{
364 int r;
365 unsigned pid_le32;
366 uint64_t timestamp;
367
368 r = svstatus_get();
369 if (r == -1)
370 return -1;
371 if (r == 0) {
372 if (*a == 'x')
373 return 1;
374 return -1;
375 }
376 pid_le32 = svstatus.pid_le32;
377 switch (*a) {
378 case 'x':
379 return 0;
380 case 'u':
381 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
382 if (!checkscript()) return 0;
383 break;
384 case 'd':
385 if (pid_le32) return 0;
386 break;
387 case 'c':
388 if (pid_le32 && !checkscript()) return 0;
389 break;
390 case 't':
391 if (!pid_le32 && svstatus.want == 'd') break;
392 timestamp = SWAP_BE64(svstatus.time_be64);
393 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
394 return 0;
395 break;
396 case 'o':
397 timestamp = SWAP_BE64(svstatus.time_be64);
398 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
399 return 0;
400 }
401 printf(OK);
402 svstatus_print(*service);
403 bb_putchar('\n'); /* will also flush the output */
404 return 1;
405}
406
407static int control(const char *a)
408{
409 int fd, r, l;
410
411/* Is it an optimization?
412 It causes problems with "sv o SRV; ...; sv d SRV"
413 ('d' is not passed to SRV because its .want == 'd'):
414 if (svstatus_get() <= 0)
415 return -1;
416 if (svstatus.want == *a)
417 return 0;
418*/
419 fd = open("supervise/control", O_WRONLY|O_NDELAY);
420 if (fd == -1) {
421 if (errno != ENODEV)
422 warn("can't open supervise/control");
423 else
424 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
425 return -1;
426 }
427 l = strlen(a);
428 r = write(fd, a, l);
429 close(fd);
430 if (r != l) {
431 warn("can't write to supervise/control");
432 return -1;
433 }
434 return 1;
435}
436
437int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
438int sv_main(int argc UNUSED_PARAM, char **argv)
439{
440 char *x;
441 char *action;
442 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
443 unsigned waitsec = 7;
444 smallint kll = 0;
445 int verbose = 0;
446 int (*act)(const char*);
447 int (*cbk)(const char*);
448 int curdir;
449
450 INIT_G();
451
452 xfunc_error_retval = 100;
453
454 x = getenv("SVDIR");
455 if (x) varservice = x;
456 x = getenv("SVWAIT");
457 if (x) waitsec = xatou(x);
458
459 opt_complementary = "w+:vv"; /* -w N, -v is a counter */
460 getopt32(argv, "w:v", &waitsec, &verbose);
461 argv += optind;
462 action = *argv++;
463 if (!action || !*argv) bb_show_usage();
464
465 tnow = time(NULL) + 0x400000000000000aULL;
466 tstart = tnow;
467 curdir = open(".", O_RDONLY|O_NDELAY);
468 if (curdir == -1)
469 fatal_cannot("open current directory");
470
471 act = &control;
472 acts = "s";
473 cbk = &check;
474
475 switch (*action) {
476 case 'x':
477 case 'e':
478 acts = "x";
479 if (!verbose) cbk = NULL;
480 break;
481 case 'X':
482 case 'E':
483 acts = "x";
484 kll = 1;
485 break;
486 case 'D':
487 acts = "d";
488 kll = 1;
489 break;
490 case 'T':
491 acts = "tc";
492 kll = 1;
493 break;
494 case 'c':
495 if (str_equal(action, "check")) {
496 act = NULL;
497 acts = "c";
498 break;
499 }
500 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
501 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
502 action[1] = '\0';
503 acts = action;
504 if (!verbose) cbk = NULL;
505 break;
506 case 's':
507 if (str_equal(action, "shutdown")) {
508 acts = "x";
509 break;
510 }
511 if (str_equal(action, "start")) {
512 acts = "u";
513 break;
514 }
515 if (str_equal(action, "stop")) {
516 acts = "d";
517 break;
518 }
519 /* "status" */
520 act = &status;
521 cbk = NULL;
522 break;
523 case 'r':
524 if (str_equal(action, "restart")) {
525 acts = "tcu";
526 break;
527 }
528 bb_show_usage();
529 case 'f':
530 if (str_equal(action, "force-reload")) {
531 acts = "tc";
532 kll = 1;
533 break;
534 }
535 if (str_equal(action, "force-restart")) {
536 acts = "tcu";
537 kll = 1;
538 break;
539 }
540 if (str_equal(action, "force-shutdown")) {
541 acts = "x";
542 kll = 1;
543 break;
544 }
545 if (str_equal(action, "force-stop")) {
546 acts = "d";
547 kll = 1;
548 break;
549 }
550 default:
551 bb_show_usage();
552 }
553
554 service = argv;
555 while ((x = *service) != NULL) {
556 if (x[0] != '/' && x[0] != '.') {
557 if (chdir(varservice) == -1)
558 goto chdir_failed_0;
559 }
560 if (chdir(x) == -1) {
561 chdir_failed_0:
562 fail("can't change to service directory");
563 goto nullify_service_0;
564 }
565 if (act && (act(acts) == -1)) {
566 nullify_service_0:
567 *service = (char*) -1L; /* "dead" */
568 }
569 if (fchdir(curdir) == -1)
570 fatal_cannot("change to original directory");
571 service++;
572 }
573
574 if (cbk) while (1) {
575 int want_exit;
576 int diff;
577
578 diff = tnow - tstart;
579 service = argv;
580 want_exit = 1;
581 while ((x = *service) != NULL) {
582 if (x == (char*) -1L) /* "dead" */
583 goto next;
584 if (x[0] != '/' && x[0] != '.') {
585 if (chdir(varservice) == -1)
586 goto chdir_failed;
587 }
588 if (chdir(x) == -1) {
589 chdir_failed:
590 fail("can't change to service directory");
591 goto nullify_service;
592 }
593 if (cbk(acts) != 0)
594 goto nullify_service;
595 want_exit = 0;
596 if (diff >= waitsec) {
597 printf(kll ? "kill: " : "timeout: ");
598 if (svstatus_get() > 0) {
599 svstatus_print(x);
600 ++rc;
601 }
602 bb_putchar('\n'); /* will also flush the output */
603 if (kll)
604 control("k");
605 nullify_service:
606 *service = (char*) -1L; /* "dead" */
607 }
608 if (fchdir(curdir) == -1)
609 fatal_cannot("change to original directory");
610 next:
611 service++;
612 }
613 if (want_exit) break;
614 usleep(420000);
615 tnow = time(NULL) + 0x400000000000000aULL;
616 }
617 return rc > 99 ? 99 : rc;
618}
Note: See TracBrowser for help on using the repository browser.