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.
78 Same as up, but wait up to 7 seconds for the command to take effect.
79 Then report the status or timeout. If the script ./check exists in
80 the service directory, sv runs this script to check whether the service
81 is up and available; it's considered to be available if ./check exits
84 Same as down, but wait up to 7 seconds for the service to become down.
85 Then report the status or timeout.
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
117 Check for the service to be in the state that's been requested. Wait up to
118 7 seconds for the service to reach the requested state, then report
119 the status or timeout. If the requested state of the service is up,
120 and the script ./check exists in the service directory, sv runs
121 this script to check whether the service is up and running;
122 it's considered to be up if ./check exits with 0.
127 wait up to 7 seconds for the command to take effect.
128 Then report the status or timeout.
130 Override the default timeout of 7 seconds with sec seconds. Implies -v.
135 The environment variable $SVDIR overrides the default services directory
138 The environment variable $SVWAIT overrides the default 7 seconds to wait
139 for a command to take effect. It is overridden by the -w option.
142 sv exits 0, if the command was successfully sent to all services, and,
143 if it was told to wait, the command has taken effect to all services.
145 For each service that caused an error (e.g. the directory is not
146 controlled by a runsv(8) process, or sv timed out while waiting),
147 sv increases the exit code by one and exits non zero. The maximum
148 is 99. sv exits 100 on error.
151 /* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
152 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
154 #include <sys/poll.h>
155 #include <sys/file.h>
157 #include "runit_lib.h"
159 static const char *acts;
160 static char **service;
162 static struct taia tstart, tnow;
163 static char svstatus[20];
165 #define usage() bb_show_usage()
167 static void fatal_cannot(const char *m1) ATTRIBUTE_NORETURN;
168 static void fatal_cannot(const char *m1)
170 bb_perror_msg("fatal: cannot %s", m1);
174 static void out(const char *p, const char *m1)
176 printf("%s%s: %s", p, *service, m1);
178 printf(": %s", strerror(errno));
180 puts(""); /* will also flush the output */
183 #define WARN "warning: "
186 static void fail(const char *m1) {
190 static void failx(const char *m1) {
194 static void warn_cannot(const char *m1) {
196 out("warning: cannot ", m1);
198 static void warnx_cannot(const char *m1) {
202 static void ok(const char *m1) {
207 static int svstatus_get(void)
211 fd = open_write("supervise/ok");
213 if (errno == ENODEV) {
214 *acts == 'x' ? ok("runsv not running")
215 : failx("runsv not running");
218 warn_cannot("open supervise/ok");
222 fd = open_read("supervise/status");
224 warn_cannot("open supervise/status");
227 r = read(fd, svstatus, 20);
231 case -1: warn_cannot("read supervise/status"); return -1;
232 default: warnx_cannot("read supervise/status: bad format"); return -1;
237 static unsigned svstatus_print(const char *m)
245 if (stat("down", &s) == -1) {
246 if (errno != ENOENT) {
247 bb_perror_msg(WARN"cannot stat %s/down", *service);
252 pid = (unsigned char) svstatus[15];
253 pid <<= 8; pid += (unsigned char)svstatus[14];
254 pid <<= 8; pid += (unsigned char)svstatus[13];
255 pid <<= 8; pid += (unsigned char)svstatus[12];
256 tai_unpack(svstatus, &tstatus);
258 switch (svstatus[19]) {
259 case 1: printf("run: "); break;
260 case 2: printf("finish: "); break;
262 printf("%s: (pid %d) ", m, pid);
264 printf("down: %s: ", m);
266 diff = tnow.sec.x - tstatus.x;
267 printf("%lds", (diff < 0 ? 0L : diff));
269 if (!normallyup) printf(", normally down");
270 if (svstatus[16]) printf(", paused");
271 if (svstatus[17] == 'd') printf(", want down");
272 if (svstatus[18]) printf(", got TERM");
274 if (normallyup) printf(", normally up");
275 if (svstatus[17] == 'u') printf(", want up");
280 static int status(const char *unused)
285 switch (r) { case -1: case 0: return 0; }
287 r = svstatus_print(*service);
288 if (chdir("log") == -1) {
289 if (errno != ENOENT) {
290 printf("; log: "WARN"cannot change to log service directory: %s",
293 } else if (svstatus_get()) {
295 svstatus_print("log");
297 puts(""); /* will also flush the output */
301 static int checkscript(void)
307 if (stat("check", &s) == -1) {
308 if (errno == ENOENT) return 1;
309 bb_perror_msg(WARN"cannot stat %s/check", *service);
312 /* if (!(s.st_mode & S_IXUSR)) return 1; */
313 prog[0] = (char*)"./check";
317 bb_perror_msg(WARN"cannot %s child %s/check", "run", *service);
320 while (wait_pid(&w, pid) == -1) {
321 if (errno == EINTR) continue;
322 bb_perror_msg(WARN"cannot %s child %s/check", "wait for", *service);
325 return !wait_exitcode(w);
328 static int check(const char *a)
342 pid = (unsigned char)svstatus[15];
343 pid <<= 8; pid += (unsigned char)svstatus[14];
344 pid <<= 8; pid += (unsigned char)svstatus[13];
345 pid <<= 8; pid += (unsigned char)svstatus[12];
350 if (!pid || svstatus[19] != 1) return 0;
351 if (!checkscript()) return 0;
357 if (pid && !checkscript()) return 0;
360 if (!pid && svstatus[17] == 'd') break;
361 tai_unpack(svstatus, &tstatus);
362 if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript())
366 tai_unpack(svstatus, &tstatus);
367 if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd'))
371 svstatus_print(*service);
372 puts(""); /* will also flush the output */
376 static int control(const char *a)
380 if (svstatus_get() <= 0) return -1;
381 if (svstatus[17] == *a) return 0;
382 fd = open_write("supervise/control");
385 warn_cannot("open supervise/control");
387 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
390 r = write(fd, a, strlen(a));
392 if (r != strlen(a)) {
393 warn_cannot("write to supervise/control");
399 int sv_main(int argc, char **argv);
400 int sv_main(int argc, char **argv)
403 unsigned i, want_exit;
406 const char *varservice = "/var/service/";
409 unsigned long waitsec = 7;
411 smallint verbose = 0;
412 int (*act)(const char*);
413 int (*cbk)(const char*);
416 xfunc_error_retval = 100;
419 if (x) varservice = x;
420 x = getenv("SVWAIT");
421 if (x) waitsec = xatoul(x);
423 opt = getopt32(argc, argv, "w:v", &x);
424 if (opt & 1) waitsec = xatoul(x); // -w
425 if (opt & 2) verbose = 1; // -v
429 if (!action || !*argv) usage();
435 curdir = open_read(".");
437 fatal_cannot("open current directory");
447 if (!verbose) cbk = NULL;
463 if (!str_diff(action, "check")) {
468 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
469 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
472 if (!verbose) cbk = NULL;
475 if (!str_diff(action, "shutdown")) {
479 if (!str_diff(action, "start")) {
483 if (!str_diff(action, "stop")) {
492 if (!str_diff(action, "restart")) {
498 if (!str_diff(action, "force-reload")) {
503 if (!str_diff(action, "force-restart")) {
508 if (!str_diff(action, "force-shutdown")) {
513 if (!str_diff(action, "force-stop")) {
523 for (i = 0; i < services; ++i) {
524 if ((**service != '/') && (**service != '.')) {
525 if (chdir(varservice) == -1)
528 if (chdir(*service) == -1) {
530 fail("cannot change to service directory");
531 goto nullify_service_0;
533 if (act && (act(acts) == -1)) {
537 if (fchdir(curdir) == -1)
538 fatal_cannot("change to original directory");
546 //taia_sub(&tdiff, &tnow, &tstart);
547 diff = tnow.sec.x - tstart.sec.x;
550 for (i = 0; i < services; ++i, ++service) {
553 if ((**service != '/') && (**service != '.')) {
554 if (chdir(varservice) == -1)
557 if (chdir(*service) == -1) {
559 fail("cannot change to service directory");
560 goto nullify_service;
563 goto nullify_service;
565 if (diff >= waitsec) {
566 printf(kll ? "kill: " : "timeout: ");
567 if (svstatus_get() > 0) {
568 svstatus_print(*service);
571 puts(""); /* will also flush the output */
577 if (fchdir(curdir) == -1)
578 fatal_cannot("change to original directory");
580 if (want_exit) break;
584 return rc > 99 ? 99 : rc;