source: MondoRescue/branches/3.3/mindi-busybox/runit/sv.c@ 3621

Last change on this file since 3621 was 3621, checked in by Bruno Cornec, 7 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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