source: MondoRescue/branches/3.0/mindi-busybox/runit/sv.c@ 3085

Last change on this file since 3085 was 2725, checked in by Bruno Cornec, 13 years ago
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
  • Property svn:eol-style set to native
File size: 15.6 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#include <sys/poll.h>
157#include <sys/file.h>
158#include "libbb.h"
159#include "runit_lib.h"
160
161struct 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} FIX_ALIASING;
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#define str_equal(s,t) (!strcmp((s), (t)))
180
181
182static void fatal_cannot(const char *m1) NORETURN;
183static void fatal_cannot(const char *m1)
184{
185 bb_perror_msg("fatal: can't %s", m1);
186 _exit(151);
187}
188
189static void out(const char *p, const char *m1)
190{
191 printf("%s%s: %s", p, *service, m1);
192 if (errno) {
193 printf(": %s", strerror(errno));
194 }
195 bb_putchar('\n'); /* will also flush the output */
196}
197
198#define WARN "warning: "
199#define OK "ok: "
200
201static void fail(const char *m1)
202{
203 ++rc;
204 out("fail: ", m1);
205}
206static void failx(const char *m1)
207{
208 errno = 0;
209 fail(m1);
210}
211static void warn(const char *m1)
212{
213 ++rc;
214 /* "warning: <service>: <m1>\n" */
215 out("warning: ", m1);
216}
217static void ok(const char *m1)
218{
219 errno = 0;
220 out(OK, m1);
221}
222
223static int svstatus_get(void)
224{
225 int fd, r;
226
227 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
228 if (fd == -1) {
229 if (errno == ENODEV) {
230 *acts == 'x' ? ok("runsv not running")
231 : failx("runsv not running");
232 return 0;
233 }
234 warn("can't open supervise/ok");
235 return -1;
236 }
237 close(fd);
238 fd = open("supervise/status", O_RDONLY|O_NDELAY);
239 if (fd == -1) {
240 warn("can't open supervise/status");
241 return -1;
242 }
243 r = read(fd, &svstatus, 20);
244 close(fd);
245 switch (r) {
246 case 20:
247 break;
248 case -1:
249 warn("can't read supervise/status");
250 return -1;
251 default:
252 errno = 0;
253 warn("can't read supervise/status: bad format");
254 return -1;
255 }
256 return 1;
257}
258
259static unsigned svstatus_print(const char *m)
260{
261 int diff;
262 int pid;
263 int normallyup = 0;
264 struct stat s;
265 uint64_t timestamp;
266
267 if (stat("down", &s) == -1) {
268 if (errno != ENOENT) {
269 bb_perror_msg(WARN"can't stat %s/down", *service);
270 return 0;
271 }
272 normallyup = 1;
273 }
274 pid = SWAP_LE32(svstatus.pid_le32);
275 timestamp = SWAP_BE64(svstatus.time_be64);
276 if (pid) {
277 switch (svstatus.run_or_finish) {
278 case 1: printf("run: "); break;
279 case 2: printf("finish: "); break;
280 }
281 printf("%s: (pid %d) ", m, pid);
282 } else {
283 printf("down: %s: ", m);
284 }
285 diff = tnow - timestamp;
286 printf("%us", (diff < 0 ? 0 : diff));
287 if (pid) {
288 if (!normallyup) printf(", normally down");
289 if (svstatus.paused) printf(", paused");
290 if (svstatus.want == 'd') printf(", want down");
291 if (svstatus.got_term) printf(", got TERM");
292 } else {
293 if (normallyup) printf(", normally up");
294 if (svstatus.want == 'u') printf(", want up");
295 }
296 return pid ? 1 : 2;
297}
298
299static int status(const char *unused UNUSED_PARAM)
300{
301 int r;
302
303 if (svstatus_get() <= 0)
304 return 0;
305
306 r = svstatus_print(*service);
307 if (chdir("log") == -1) {
308 if (errno != ENOENT) {
309 printf("; log: "WARN"can't change to log service directory: %s",
310 strerror(errno));
311 }
312 } else if (svstatus_get()) {
313 printf("; ");
314 svstatus_print("log");
315 }
316 bb_putchar('\n'); /* will also flush the output */
317 return r;
318}
319
320static int checkscript(void)
321{
322 char *prog[2];
323 struct stat s;
324 int pid, w;
325
326 if (stat("check", &s) == -1) {
327 if (errno == ENOENT) return 1;
328 bb_perror_msg(WARN"can't stat %s/check", *service);
329 return 0;
330 }
331 /* if (!(s.st_mode & S_IXUSR)) return 1; */
332 prog[0] = (char*)"./check";
333 prog[1] = NULL;
334 pid = spawn(prog);
335 if (pid <= 0) {
336 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
337 return 0;
338 }
339 while (safe_waitpid(pid, &w, 0) == -1) {
340 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
341 return 0;
342 }
343 return WEXITSTATUS(w) == 0;
344}
345
346static int check(const char *a)
347{
348 int r;
349 unsigned pid_le32;
350 uint64_t timestamp;
351
352 r = svstatus_get();
353 if (r == -1)
354 return -1;
355 if (r == 0) {
356 if (*a == 'x')
357 return 1;
358 return -1;
359 }
360 pid_le32 = svstatus.pid_le32;
361 switch (*a) {
362 case 'x':
363 return 0;
364 case 'u':
365 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
366 if (!checkscript()) return 0;
367 break;
368 case 'd':
369 if (pid_le32) return 0;
370 break;
371 case 'c':
372 if (pid_le32 && !checkscript()) return 0;
373 break;
374 case 't':
375 if (!pid_le32 && svstatus.want == 'd') break;
376 timestamp = SWAP_BE64(svstatus.time_be64);
377 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
378 return 0;
379 break;
380 case 'o':
381 timestamp = SWAP_BE64(svstatus.time_be64);
382 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
383 return 0;
384 }
385 printf(OK);
386 svstatus_print(*service);
387 bb_putchar('\n'); /* will also flush the output */
388 return 1;
389}
390
391static int control(const char *a)
392{
393 int fd, r, l;
394
395/* Is it an optimization?
396 It causes problems with "sv o SRV; ...; sv d SRV"
397 ('d' is not passed to SRV because its .want == 'd'):
398 if (svstatus_get() <= 0)
399 return -1;
400 if (svstatus.want == *a)
401 return 0;
402*/
403 fd = open("supervise/control", O_WRONLY|O_NDELAY);
404 if (fd == -1) {
405 if (errno != ENODEV)
406 warn("can't open supervise/control");
407 else
408 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
409 return -1;
410 }
411 l = strlen(a);
412 r = write(fd, a, l);
413 close(fd);
414 if (r != l) {
415 warn("can't write to supervise/control");
416 return -1;
417 }
418 return 1;
419}
420
421int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
422int sv_main(int argc UNUSED_PARAM, char **argv)
423{
424 unsigned opt;
425 char *x;
426 char *action;
427 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
428 unsigned waitsec = 7;
429 smallint kll = 0;
430 int verbose = 0;
431 int (*act)(const char*);
432 int (*cbk)(const char*);
433 int curdir;
434
435 INIT_G();
436
437 xfunc_error_retval = 100;
438
439 x = getenv("SVDIR");
440 if (x) varservice = x;
441 x = getenv("SVWAIT");
442 if (x) waitsec = xatou(x);
443
444 opt_complementary = "w+:vv"; /* -w N, -v is a counter */
445 opt = getopt32(argv, "w:v", &waitsec, &verbose);
446 argv += optind;
447 action = *argv++;
448 if (!action || !*argv) bb_show_usage();
449
450 tnow = time(NULL) + 0x400000000000000aULL;
451 tstart = tnow;
452 curdir = open(".", O_RDONLY|O_NDELAY);
453 if (curdir == -1)
454 fatal_cannot("open current directory");
455
456 act = &control;
457 acts = "s";
458 cbk = &check;
459
460 switch (*action) {
461 case 'x':
462 case 'e':
463 acts = "x";
464 if (!verbose) cbk = NULL;
465 break;
466 case 'X':
467 case 'E':
468 acts = "x";
469 kll = 1;
470 break;
471 case 'D':
472 acts = "d";
473 kll = 1;
474 break;
475 case 'T':
476 acts = "tc";
477 kll = 1;
478 break;
479 case 'c':
480 if (str_equal(action, "check")) {
481 act = NULL;
482 acts = "c";
483 break;
484 }
485 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
486 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
487 action[1] = '\0';
488 acts = action;
489 if (!verbose) cbk = NULL;
490 break;
491 case 's':
492 if (str_equal(action, "shutdown")) {
493 acts = "x";
494 break;
495 }
496 if (str_equal(action, "start")) {
497 acts = "u";
498 break;
499 }
500 if (str_equal(action, "stop")) {
501 acts = "d";
502 break;
503 }
504 /* "status" */
505 act = &status;
506 cbk = NULL;
507 break;
508 case 'r':
509 if (str_equal(action, "restart")) {
510 acts = "tcu";
511 break;
512 }
513 bb_show_usage();
514 case 'f':
515 if (str_equal(action, "force-reload")) {
516 acts = "tc";
517 kll = 1;
518 break;
519 }
520 if (str_equal(action, "force-restart")) {
521 acts = "tcu";
522 kll = 1;
523 break;
524 }
525 if (str_equal(action, "force-shutdown")) {
526 acts = "x";
527 kll = 1;
528 break;
529 }
530 if (str_equal(action, "force-stop")) {
531 acts = "d";
532 kll = 1;
533 break;
534 }
535 default:
536 bb_show_usage();
537 }
538
539 service = argv;
540 while ((x = *service) != NULL) {
541 if (x[0] != '/' && x[0] != '.') {
542 if (chdir(varservice) == -1)
543 goto chdir_failed_0;
544 }
545 if (chdir(x) == -1) {
546 chdir_failed_0:
547 fail("can't change to service directory");
548 goto nullify_service_0;
549 }
550 if (act && (act(acts) == -1)) {
551 nullify_service_0:
552 *service = (char*) -1L; /* "dead" */
553 }
554 if (fchdir(curdir) == -1)
555 fatal_cannot("change to original directory");
556 service++;
557 }
558
559 if (cbk) while (1) {
560 int want_exit;
561 int diff;
562
563 diff = tnow - tstart;
564 service = argv;
565 want_exit = 1;
566 while ((x = *service) != NULL) {
567 if (x == (char*) -1L) /* "dead" */
568 goto next;
569 if (x[0] != '/' && x[0] != '.') {
570 if (chdir(varservice) == -1)
571 goto chdir_failed;
572 }
573 if (chdir(x) == -1) {
574 chdir_failed:
575 fail("can't change to service directory");
576 goto nullify_service;
577 }
578 if (cbk(acts) != 0)
579 goto nullify_service;
580 want_exit = 0;
581 if (diff >= waitsec) {
582 printf(kll ? "kill: " : "timeout: ");
583 if (svstatus_get() > 0) {
584 svstatus_print(x);
585 ++rc;
586 }
587 bb_putchar('\n'); /* will also flush the output */
588 if (kll)
589 control("k");
590 nullify_service:
591 *service = (char*) -1L; /* "dead" */
592 }
593 if (fchdir(curdir) == -1)
594 fatal_cannot("change to original directory");
595 next:
596 service++;
597 }
598 if (want_exit) break;
599 usleep(420000);
600 tnow = time(NULL) + 0x400000000000000aULL;
601 }
602 return rc > 99 ? 99 : rc;
603}
Note: See TracBrowser for help on using the repository browser.