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.org/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 and
40 doesn't end with a slash, it is searched in the default services directory
41 /var/service/, otherwise 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, reload, restart, shutdown,
45 force-stop, force-reload, force-restart, force-shutdown, try-restart.
48 Report the current status of the service, and the appendant log service
49 if available, to standard output.
51 If the service is not running, start it. If the service stops, restart it.
53 If the service is running, send it the TERM signal, and the CONT signal.
54 If ./run exits, start ./finish if it exists. After it stops, do not
57 If the service is not running, start it. Do not restart it if it stops.
58 pause cont hup alarm interrupt quit 1 2 term kill
59 If the service is running, send it the STOP, CONT, HUP, ALRM, INT, QUIT,
60 USR1, USR2, TERM, or KILL signal respectively.
62 If the service is running, send it the TERM signal, and the CONT signal.
63 Do not restart the service. If the service is down, and no log service
64 exists, runsv(8) exits. If the service is down and a log service exists,
65 runsv(8) closes the standard input of the log service and waits for it to
66 terminate. If the log service is down, runsv(8) exits. This command is
67 ignored if it is given to an appendant log service.
69 sv actually looks only at the first character of above commands.
71 Commands compatible to LSB init script actions:
76 Same as up, but wait up to 7 seconds for the command to take effect.
77 Then report the status or timeout. If the script ./check exists in
78 the service directory, sv runs this script to check whether the service
79 is up and available; it's considered to be available if ./check exits
82 Same as down, but wait up to 7 seconds for the service to become down.
83 Then report the status or timeout.
85 Same as hup, and additionally report the status afterwards.
87 Send the commands term, cont, and up to the service, and wait up to
88 7 seconds for the service to restart. Then report the status or timeout.
89 If the script ./check exists in the service directory, sv runs this script
90 to check whether the service is up and available again; it's considered
91 to be available if ./check exits with 0.
93 Same as exit, but wait up to 7 seconds for the runsv(8) process
94 to terminate. Then report the status or timeout.
96 Same as down, but wait up to 7 seconds for the service to become down.
97 Then report the status, and on timeout send the service the kill command.
99 Send the service the term and cont commands, and wait up to
100 7 seconds for the service to restart. Then report the status,
101 and on timeout send the service the kill command.
103 Send the service the term, cont and up commands, and wait up to
104 7 seconds for the service to restart. Then report the status, and
105 on timeout send the service the kill command. If the script ./check
106 exists in the service directory, sv runs this script to check whether
107 the service is up and available again; it's considered to be available
108 if ./check exits with 0.
110 Same as exit, but wait up to 7 seconds for the runsv(8) process to
111 terminate. Then report the status, and on timeout send the service
114 if the service is running, send it the term and cont commands, and wait up to
115 7 seconds for the service to restart. Then report the status or timeout.
120 Check for the service to be in the state that's been requested. Wait up to
121 7 seconds for the service to reach the requested state, then report
122 the status or timeout. If the requested state of the service is up,
123 and the script ./check exists in the service directory, sv runs
124 this script to check whether the service is up and running;
125 it's considered to be up if ./check exits with 0.
130 If the command is up, down, term, once, cont, or exit, then wait up to 7
131 seconds for the command to take effect. Then report the status or timeout.
133 Override the default timeout of 7 seconds with sec seconds. Implies -v.
138 The environment variable $SVDIR overrides the default services directory
141 The environment variable $SVWAIT overrides the default 7 seconds to wait
142 for a command to take effect. It is overridden by the -w option.
145 sv exits 0, if the command was successfully sent to all services, and,
146 if it was told to wait, the command has taken effect to all services.
148 For each service that caused an error (e.g. the directory is not
149 controlled by a runsv(8) process, or sv timed out while waiting),
150 sv increases the exit code by one and exits non zero. The maximum
151 is 99. sv exits 100 on error.
154 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
157 //config: bool "sv (8.5 kb)"
160 //config: sv reports the current status and controls the state of services
161 //config: monitored by the runsv supervisor.
163 //config:config SV_DEFAULT_SERVICE_DIR
164 //config: string "Default directory for services"
165 //config: default "/var/service"
166 //config: depends on SV || SVC || SVOK
168 //config: Default directory for services.
169 //config: Defaults to "/var/service"
172 //config: bool "svc (8.4 kb)"
175 //config: svc controls the state of services monitored by the runsv supervisor.
176 //config: It is compatible with daemontools command with the same name.
179 //config: bool "svok (1.5 kb)"
182 //config: svok checks whether runsv supervisor is running.
183 //config: It is compatible with daemontools command with the same name.
185 //applet:IF_SV( APPLET_NOEXEC(sv, sv, BB_DIR_USR_BIN, BB_SUID_DROP, sv ))
186 //applet:IF_SVC( APPLET_NOEXEC(svc, svc, BB_DIR_USR_BIN, BB_SUID_DROP, svc ))
187 //applet:IF_SVOK(APPLET_NOEXEC(svok, svok, BB_DIR_USR_BIN, BB_SUID_DROP, svok))
189 //kbuild:lib-$(CONFIG_SV) += sv.o
190 //kbuild:lib-$(CONFIG_SVC) += sv.o
191 //kbuild:lib-$(CONFIG_SVOK) += sv.o
193 #include <sys/file.h>
195 #include "common_bufsiz.h"
196 #include "runit_lib.h"
202 /* "Bernstein" time format: unix + 0x400000000000000aULL */
203 uint64_t tstart, tnow;
207 #define G (*(struct globals*)bb_common_bufsiz1)
208 #define acts (G.acts )
209 #define service (G.service )
211 #define tstart (G.tstart )
212 #define tnow (G.tnow )
213 #define svstatus (G.svstatus )
214 #define islog (G.islog )
215 #define INIT_G() do { \
216 setup_common_bufsiz(); \
217 /* need to zero out, svc calls sv() repeatedly */ \
218 memset(&G, 0, sizeof(G)); \
222 #define str_equal(s,t) (strcmp((s), (t)) == 0)
225 #if ENABLE_SV || ENABLE_SVC
226 static void fatal_cannot(const char *m1) NORETURN;
227 static void fatal_cannot(const char *m1)
229 bb_perror_msg("fatal: can't %s", m1);
233 static void out(const char *p, const char *m1)
235 printf("%s%s%s: %s", p, *service, islog ? "/log" : "", m1);
237 printf(": "STRERROR_FMT STRERROR_ERRNO);
239 bb_putchar('\n'); /* will also flush the output */
242 #define WARN "warning: "
245 static void fail(const char *m1)
250 static void failx(const char *m1)
255 static void warn(const char *m1)
258 /* "warning: <service>: <m1>\n" */
259 out("warning: ", m1);
261 static void ok(const char *m1)
267 static int svstatus_get(void)
271 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
273 if (errno == ENODEV) {
274 *acts == 'x' ? ok("runsv not running")
275 : failx("runsv not running");
278 warn("can't open supervise/ok");
282 fd = open("supervise/status", O_RDONLY|O_NDELAY);
284 warn("can't open supervise/status");
287 r = read(fd, &svstatus, 20);
293 warn("can't read supervise/status");
297 warn("can't read supervise/status: bad format");
303 static unsigned svstatus_print(const char *m)
311 if (stat("down", &s) == -1) {
312 if (errno != ENOENT) {
313 bb_perror_msg(WARN"can't stat %s/down", *service);
318 pid = SWAP_LE32(svstatus.pid_le32);
319 timestamp = SWAP_BE64(svstatus.time_be64);
320 switch (svstatus.run_or_finish) {
321 case 0: printf("down: "); break;
322 case 1: printf("run: "); break;
323 case 2: printf("finish: "); break;
326 if (svstatus.run_or_finish)
327 printf("(pid %d) ", pid);
328 diff = tnow - timestamp;
329 printf("%us", (diff < 0 ? 0 : diff));
331 if (!normallyup) printf(", normally down");
332 if (svstatus.paused) printf(", paused");
333 if (svstatus.want == 'd') printf(", want down");
334 if (svstatus.got_term) printf(", got TERM");
336 if (normallyup) printf(", normally up");
337 if (svstatus.want == 'u') printf(", want up");
342 static int status(const char *unused UNUSED_PARAM)
346 if (svstatus_get() <= 0)
349 r = svstatus_print(*service);
351 if (chdir("log") == -1) {
352 if (errno != ENOENT) {
354 warn("can't change directory");
359 if (svstatus_get()) {
360 r = svstatus_print("log");
368 static int checkscript(void)
374 if (stat("check", &s) == -1) {
375 if (errno == ENOENT) return 1;
376 bb_perror_msg(WARN"can't stat %s/check", *service);
379 /* if (!(s.st_mode & S_IXUSR)) return 1; */
380 prog[0] = (char*)"./check";
384 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
387 while (safe_waitpid(pid, &w, 0) == -1) {
388 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
391 return WEXITSTATUS(w) == 0;
394 static int check(const char *a)
409 pid_le32 = svstatus.pid_le32;
414 if (!pid_le32 || svstatus.run_or_finish != 1)
420 if (pid_le32 || svstatus.run_or_finish != 0)
424 if (pid_le32 && !checkscript())
429 if (!pid_le32 && svstatus.want == 'd')
431 timestamp = SWAP_BE64(svstatus.time_be64);
432 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
436 timestamp = SWAP_BE64(svstatus.time_be64);
437 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
441 if (pid_le32 && !svstatus.paused)
445 if (pid_le32 && svstatus.paused)
452 svstatus_print(*service);
453 bb_putchar('\n'); /* will also flush the output */
457 static int control(const char *a)
461 if (svstatus_get() <= 0)
463 if (svstatus.want == *a && (*a != 'd' || svstatus.got_term == 1))
465 fd = open("supervise/control", O_WRONLY|O_NDELAY);
468 warn("can't open supervise/control");
470 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
477 warn("can't write to supervise/control");
483 //usage:#define sv_trivial_usage
484 //usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
485 //usage:#define sv_full_usage "\n\n"
486 //usage: "Control services monitored by runsv supervisor.\n"
487 //usage: "Commands (only first character is enough):\n"
489 //usage: "status: query service status\n"
490 //usage: "up: if service isn't running, start it. If service stops, restart it\n"
491 //usage: "once: like 'up', but if service stops, don't restart it\n"
492 //usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
493 //usage: " if it exists. After it stops, don't restart service\n"
494 //usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
495 //usage: " runsv exits too\n"
496 //usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
497 //usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
498 static int sv(char **argv)
502 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
503 unsigned waitsec = 7;
506 int (*act)(const char*);
507 int (*cbk)(const char*);
512 xfunc_error_retval = 100;
515 if (x) varservice = x;
516 x = getenv("SVWAIT");
517 if (x) waitsec = xatou(x);
519 getopt32(argv, "^" "w:+v" "\0" "vv" /* -w N, -v is a counter */,
524 if (!action || !*argv) bb_show_usage();
526 tnow = time(NULL) + 0x400000000000000aULL;
528 curdir = open(".", O_RDONLY|O_NDELAY);
530 fatal_cannot("open current directory");
540 if (!verbose) cbk = NULL;
556 if (str_equal(action, "try-restart")) {
561 if (str_equal(action, "check")) {
566 case 'u': case 'd': case 'o': case 'p': case 'h':
567 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
574 if (str_equal(action, "shutdown")) {
578 if (str_equal(action, "start")) {
582 if (str_equal(action, "stop")) {
591 if (str_equal(action, "restart")) {
595 if (str_equal(action, "reload")) {
601 if (str_equal(action, "force-reload")) {
606 if (str_equal(action, "force-restart")) {
611 if (str_equal(action, "force-shutdown")) {
616 if (str_equal(action, "force-stop")) {
626 while ((x = *service) != NULL) {
627 if (x[0] != '/' && x[0] != '.'
628 && !last_char_is(x, '/')
630 if (chdir(varservice) == -1)
633 if (chdir(x) == -1) {
635 fail("can't change to service directory");
636 goto nullify_service_0;
638 if (act && (act(acts) == -1)) {
640 *service = (char*) -1L; /* "dead" */
642 if (fchdir(curdir) == -1)
643 fatal_cannot("change to original directory");
651 diff = tnow - tstart;
654 while ((x = *service) != NULL) {
655 if (x == (char*) -1L) /* "dead" */
657 if (x[0] != '/' && x[0] != '.') {
658 if (chdir(varservice) == -1)
661 if (chdir(x) == -1) {
663 fail("can't change to service directory");
664 goto nullify_service;
667 goto nullify_service;
669 if (diff >= waitsec) {
670 printf(kll ? "kill: " : "timeout: ");
671 if (svstatus_get() > 0) {
675 bb_putchar('\n'); /* will also flush the output */
679 *service = (char*) -1L; /* "dead" */
681 if (fchdir(curdir) == -1)
682 fatal_cannot("change to original directory");
686 if (want_exit) break;
688 tnow = time(NULL) + 0x400000000000000aULL;
690 return rc > 99 ? 99 : rc;
695 int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
696 int sv_main(int argc UNUSED_PARAM, char **argv)
702 //usage:#define svc_trivial_usage
703 //usage: "[-udopchaitkx] SERVICE_DIR..."
704 //usage:#define svc_full_usage "\n\n"
705 //usage: "Control services monitored by runsv supervisor"
707 //usage: "\n"" -u If service is not running, start it; restart if it stops"
708 //usage: "\n"" -d If service is running, send TERM+CONT signals; do not restart it"
709 //usage: "\n"" -o Once: if service is not running, start it; do not restart it"
710 //usage: "\n"" -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
711 //usage: "\n"" -x Exit: runsv will exit as soon as the service is down"
713 int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
714 int svc_main(int argc UNUSED_PARAM, char **argv)
717 const char *optstring;
720 optstring = "udopchaitkx";
721 opts = getopt32(argv, optstring);
723 if (!argv[0] || !opts)
729 argv[2] = (char*)"--";
731 argv[0] = (char*)"sv";
739 command[0] = *optstring;
741 /* getopt() was already called by getopt32():
742 * reset the libc getopt() function's internal state.
757 //usage:#define svok_trivial_usage
758 //usage: "SERVICE_DIR"
759 //usage:#define svok_full_usage "\n\n"
760 //usage: "Check whether runsv supervisor is running.\n"
761 //usage: "Exit code is 0 if it does, 100 if it does not,\n"
762 //usage: "111 (with error message) if SERVICE_DIR does not exist."
764 int svok_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
765 int svok_main(int argc UNUSED_PARAM, char **argv)
767 const char *dir = argv[1];
772 xfunc_error_retval = 111;
775 * daemontools has no concept of "default service dir", runit does.
776 * Let's act as runit.
778 if (dir[0] != '/' && dir[0] != '.'
779 && !last_char_is(dir, '/')
781 xchdir(CONFIG_SV_DEFAULT_SERVICE_DIR);
785 if (open("supervise/ok", O_WRONLY) < 0) {
786 if (errno == ENOENT || errno == ENXIO)
788 bb_perror_msg_and_die("can't open '%s'", "supervise/ok");