2 Copyright (c) 2001-2006, Gerrit Pape
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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.
28 /* Taken from http://smarden.sunsite.dk/runit/sv.8.html:
30 sv - control and manage services monitored by runsv
32 sv [-v] [-w sec] command services
33 /etc/init.d/service [-w sec] command
35 The sv program reports the current status and controls the state of services
36 monitored by the runsv(8) supervisor.
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.
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.
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".
52 Report the current status of the service, and the appendant log service
53 if available, to standard output.
55 If the service is not running, start it. If the service stops, restart it.
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
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.
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
73 sv actually looks only at the first character of above commands.
75 Commands compatible to LSB init script actions:
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
86 Same as down, but wait up to 7 seconds for the service to become down.
87 Then report the status or timeout.
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.
95 Same as exit, but wait up to 7 seconds for the runsv(8) process
96 to terminate. Then report the status or timeout.
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.
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.
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.
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
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.
129 wait up to 7 seconds for the command to take effect.
130 Then report the status or timeout.
132 Override the default timeout of 7 seconds with sec seconds. Implies -v.
137 The environment variable $SVDIR overrides the default services directory
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.
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.
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.
153 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
159 //config: sv reports the current status and controls the state of services
160 //config: monitored by the runsv supervisor.
162 //config:config SV_DEFAULT_SERVICE_DIR
163 //config: string "Default directory for services"
164 //config: default "/var/service"
165 //config: depends on SV
167 //config: Default directory for services.
168 //config: Defaults to "/var/service"
174 //config: svc controls the state of services monitored by the runsv supervisor.
175 //config: It is comaptible with daemontools command with the same name.
177 //applet:IF_SV(APPLET(sv, BB_DIR_USR_BIN, BB_SUID_DROP))
178 //applet:IF_SVC(APPLET(svc, BB_DIR_USR_BIN, BB_SUID_DROP))
180 //kbuild:lib-$(CONFIG_SV) += sv.o
181 //kbuild:lib-$(CONFIG_SVC) += sv.o
183 #include <sys/file.h>
185 #include "common_bufsiz.h"
186 #include "runit_lib.h"
192 /* "Bernstein" time format: unix + 0x400000000000000aULL */
193 uint64_t tstart, tnow;
196 #define G (*(struct globals*)bb_common_bufsiz1)
197 #define acts (G.acts )
198 #define service (G.service )
200 #define tstart (G.tstart )
201 #define tnow (G.tnow )
202 #define svstatus (G.svstatus )
203 #define INIT_G() do { setup_common_bufsiz(); } while (0)
206 #define str_equal(s,t) (strcmp((s), (t)) == 0)
209 static void fatal_cannot(const char *m1) NORETURN;
210 static void fatal_cannot(const char *m1)
212 bb_perror_msg("fatal: can't %s", m1);
216 static void out(const char *p, const char *m1)
218 printf("%s%s: %s", p, *service, m1);
220 printf(": %s", strerror(errno));
222 bb_putchar('\n'); /* will also flush the output */
225 #define WARN "warning: "
228 static void fail(const char *m1)
233 static void failx(const char *m1)
238 static void warn(const char *m1)
241 /* "warning: <service>: <m1>\n" */
242 out("warning: ", m1);
244 static void ok(const char *m1)
250 static int svstatus_get(void)
254 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
256 if (errno == ENODEV) {
257 *acts == 'x' ? ok("runsv not running")
258 : failx("runsv not running");
261 warn("can't open supervise/ok");
265 fd = open("supervise/status", O_RDONLY|O_NDELAY);
267 warn("can't open supervise/status");
270 r = read(fd, &svstatus, 20);
276 warn("can't read supervise/status");
280 warn("can't read supervise/status: bad format");
286 static unsigned svstatus_print(const char *m)
294 if (stat("down", &s) == -1) {
295 if (errno != ENOENT) {
296 bb_perror_msg(WARN"can't stat %s/down", *service);
301 pid = SWAP_LE32(svstatus.pid_le32);
302 timestamp = SWAP_BE64(svstatus.time_be64);
304 switch (svstatus.run_or_finish) {
305 case 1: printf("run: "); break;
306 case 2: printf("finish: "); break;
308 printf("%s: (pid %d) ", m, pid);
310 printf("down: %s: ", m);
312 diff = tnow - timestamp;
313 printf("%us", (diff < 0 ? 0 : diff));
315 if (!normallyup) printf(", normally down");
316 if (svstatus.paused) printf(", paused");
317 if (svstatus.want == 'd') printf(", want down");
318 if (svstatus.got_term) printf(", got TERM");
320 if (normallyup) printf(", normally up");
321 if (svstatus.want == 'u') printf(", want up");
326 static int status(const char *unused UNUSED_PARAM)
330 if (svstatus_get() <= 0)
333 r = svstatus_print(*service);
334 if (chdir("log") == -1) {
335 if (errno != ENOENT) {
336 printf("; log: "WARN"can't change to log service directory: %s",
339 } else if (svstatus_get()) {
341 svstatus_print("log");
343 bb_putchar('\n'); /* will also flush the output */
347 static int checkscript(void)
353 if (stat("check", &s) == -1) {
354 if (errno == ENOENT) return 1;
355 bb_perror_msg(WARN"can't stat %s/check", *service);
358 /* if (!(s.st_mode & S_IXUSR)) return 1; */
359 prog[0] = (char*)"./check";
363 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
366 while (safe_waitpid(pid, &w, 0) == -1) {
367 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
370 return WEXITSTATUS(w) == 0;
373 static int check(const char *a)
387 pid_le32 = svstatus.pid_le32;
392 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
393 if (!checkscript()) return 0;
396 if (pid_le32) return 0;
399 if (pid_le32 && !checkscript()) return 0;
402 if (!pid_le32 && svstatus.want == 'd') break;
403 timestamp = SWAP_BE64(svstatus.time_be64);
404 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
408 timestamp = SWAP_BE64(svstatus.time_be64);
409 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
413 svstatus_print(*service);
414 bb_putchar('\n'); /* will also flush the output */
418 static int control(const char *a)
422 /* Is it an optimization?
423 It causes problems with "sv o SRV; ...; sv d SRV"
424 ('d' is not passed to SRV because its .want == 'd'):
425 if (svstatus_get() <= 0)
427 if (svstatus.want == *a)
430 fd = open("supervise/control", O_WRONLY|O_NDELAY);
433 warn("can't open supervise/control");
435 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
442 warn("can't write to supervise/control");
448 //usage:#define sv_trivial_usage
449 //usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
450 //usage:#define sv_full_usage "\n\n"
451 //usage: "Control services monitored by runsv supervisor.\n"
452 //usage: "Commands (only first character is enough):\n"
454 //usage: "status: query service status\n"
455 //usage: "up: if service isn't running, start it. If service stops, restart it\n"
456 //usage: "once: like 'up', but if service stops, don't restart it\n"
457 //usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
458 //usage: " if it exists. After it stops, don't restart service\n"
459 //usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
460 //usage: " runsv exits too\n"
461 //usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
462 //usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
463 static int sv(char **argv)
467 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
468 unsigned waitsec = 7;
471 int (*act)(const char*);
472 int (*cbk)(const char*);
477 xfunc_error_retval = 100;
480 if (x) varservice = x;
481 x = getenv("SVWAIT");
482 if (x) waitsec = xatou(x);
484 opt_complementary = "vv"; /* -w N, -v is a counter */
485 getopt32(argv, "w:+v", &waitsec, &verbose);
488 if (!action || !*argv) bb_show_usage();
490 tnow = time(NULL) + 0x400000000000000aULL;
492 curdir = open(".", O_RDONLY|O_NDELAY);
494 fatal_cannot("open current directory");
504 if (!verbose) cbk = NULL;
520 if (str_equal(action, "check")) {
525 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
526 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
529 if (!verbose) cbk = NULL;
532 if (str_equal(action, "shutdown")) {
536 if (str_equal(action, "start")) {
540 if (str_equal(action, "stop")) {
549 if (str_equal(action, "restart")) {
555 if (str_equal(action, "force-reload")) {
560 if (str_equal(action, "force-restart")) {
565 if (str_equal(action, "force-shutdown")) {
570 if (str_equal(action, "force-stop")) {
580 while ((x = *service) != NULL) {
581 if (x[0] != '/' && x[0] != '.') {
582 if (chdir(varservice) == -1)
585 if (chdir(x) == -1) {
587 fail("can't change to service directory");
588 goto nullify_service_0;
590 if (act && (act(acts) == -1)) {
592 *service = (char*) -1L; /* "dead" */
594 if (fchdir(curdir) == -1)
595 fatal_cannot("change to original directory");
603 diff = tnow - tstart;
606 while ((x = *service) != NULL) {
607 if (x == (char*) -1L) /* "dead" */
609 if (x[0] != '/' && x[0] != '.') {
610 if (chdir(varservice) == -1)
613 if (chdir(x) == -1) {
615 fail("can't change to service directory");
616 goto nullify_service;
619 goto nullify_service;
621 if (diff >= waitsec) {
622 printf(kll ? "kill: " : "timeout: ");
623 if (svstatus_get() > 0) {
627 bb_putchar('\n'); /* will also flush the output */
631 *service = (char*) -1L; /* "dead" */
633 if (fchdir(curdir) == -1)
634 fatal_cannot("change to original directory");
638 if (want_exit) break;
640 tnow = time(NULL) + 0x400000000000000aULL;
642 return rc > 99 ? 99 : rc;
646 int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
647 int sv_main(int argc UNUSED_PARAM, char **argv)
653 //usage:#define svc_trivial_usage
654 //usage: "[-udopchaitkx] SERVICE_DIR..."
655 //usage:#define svc_full_usage "\n\n"
656 //usage: "Control services monitored by runsv supervisor"
658 //usage: "\n"" -u If service is not running, start it; restart if it stops"
659 //usage: "\n"" -d If service is running, send TERM+CONT signals; do not restart it"
660 //usage: "\n"" -o Once: if service is not running, start it; do not restart it"
661 //usage: "\n"" -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
662 //usage: "\n"" -x Exit: runsv will exit as soon as the service is down"
664 int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
665 int svc_main(int argc UNUSED_PARAM, char **argv)
668 const char *optstring;
673 optstring = "udopchaitkx";
674 opts = getopt32(argv, optstring);
676 if (!argv[0] || !opts)
682 argv[2] = (char*)"--";
684 argv[0] = (char*)"sv";
688 /* getopt32() was already called:
689 * reset the libc getopt() function, which keeps internal state.
696 command[0] = *optstring;