blob: de8a0d8a46625c20e6b7cd94e996b570899741da [file] [log] [blame]
Denis Vlasenko8a164052007-03-12 23:34:52 +00001/*
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
Denis Vlasenkof223efb2007-08-03 10:58:12 +000039service used by runsv(8). If service doesn't start with a dot or slash,
Denis Vlasenko8a164052007-03-12 23:34:52 +000040it 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
Denis Vlasenkof223efb2007-08-03 10:58:12 +000075Commands compatible to LSB init script actions:
76
Denis Vlasenko8a164052007-03-12 23:34:52 +000077status
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
Denis Vlasenkof223efb2007-08-03 10:58:12 +0000109 the service is up and available again; it's considered to be available
Denis Vlasenko8a164052007-03-12 23:34:52 +0000110 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
Denis Vlasenkod18f52b2008-03-02 12:53:15 +0000153/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200154
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
Denis Vlasenko04c63862006-11-17 18:58:49 +0000173
Pere Orga5bc8c002011-04-11 03:29:49 +0200174//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
Denis Vlasenko04c63862006-11-17 18:58:49 +0000190#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000191#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +0000192#include "runit_lib.h"
193
Denis Vlasenkob9256052007-09-28 10:29:17 +0000194struct globals {
195 const char *acts;
196 char **service;
197 unsigned rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000198/* "Bernstein" time format: unix + 0x400000000000000aULL */
Denis Vlasenkob9256052007-09-28 10:29:17 +0000199 uint64_t tstart, tnow;
200 svstatus_t svstatus;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100201} FIX_ALIASING;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000202#define G (*(struct globals*)&bb_common_bufsiz1)
203#define acts (G.acts )
204#define service (G.service )
205#define rc (G.rc )
206#define tstart (G.tstart )
207#define tnow (G.tnow )
208#define svstatus (G.svstatus )
209#define INIT_G() do { } while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000210
Denis Vlasenko04c63862006-11-17 18:58:49 +0000211
Denys Vlasenko05e86052010-10-13 12:53:27 +0200212#define str_equal(s,t) (!strcmp((s), (t)))
213
214
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000215static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000216static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000217{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000218 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000219 _exit(151);
220}
221
Denis Vlasenko322661d2007-01-29 23:43:52 +0000222static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000223{
224 printf("%s%s: %s", p, *service, m1);
225 if (errno) {
226 printf(": %s", strerror(errno));
227 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000228 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229}
230
Denis Vlasenko04c63862006-11-17 18:58:49 +0000231#define WARN "warning: "
232#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000233
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000234static void fail(const char *m1)
235{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000236 ++rc;
237 out("fail: ", m1);
238}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000239static void failx(const char *m1)
240{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000241 errno = 0;
242 fail(m1);
243}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000244static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000245{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000246 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000247 /* "warning: <service>: <m1>\n" */
248 out("warning: ", m1);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000249}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000250static void ok(const char *m1)
251{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000252 errno = 0;
253 out(OK, m1);
254}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000255
256static int svstatus_get(void)
257{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000258 int fd, r;
259
Denys Vlasenko05e86052010-10-13 12:53:27 +0200260 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000261 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000262 if (errno == ENODEV) {
263 *acts == 'x' ? ok("runsv not running")
264 : failx("runsv not running");
265 return 0;
266 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100267 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000268 return -1;
269 }
270 close(fd);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200271 fd = open("supervise/status", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000272 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100273 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274 return -1;
275 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000276 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000277 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000278 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000279 case 20:
280 break;
281 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100282 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000283 return -1;
284 default:
285 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100286 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000287 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000288 }
289 return 1;
290}
291
Denis Vlasenko322661d2007-01-29 23:43:52 +0000292static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000293{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000294 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000295 int pid;
296 int normallyup = 0;
297 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000298 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000299
Denis Vlasenko04c63862006-11-17 18:58:49 +0000300 if (stat("down", &s) == -1) {
301 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100302 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303 return 0;
304 }
305 normallyup = 1;
306 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000307 pid = SWAP_LE32(svstatus.pid_le32);
308 timestamp = SWAP_BE64(svstatus.time_be64);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000309 if (pid) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000310 switch (svstatus.run_or_finish) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000311 case 1: printf("run: "); break;
312 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000313 }
314 printf("%s: (pid %d) ", m, pid);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000315 } else {
316 printf("down: %s: ", m);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000317 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000318 diff = tnow - timestamp;
319 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000320 if (pid) {
321 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000322 if (svstatus.paused) printf(", paused");
323 if (svstatus.want == 'd') printf(", want down");
324 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000325 } else {
326 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000327 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000328 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000329 return pid ? 1 : 2;
330}
331
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000332static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000333{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000334 int r;
335
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200336 if (svstatus_get() <= 0)
337 return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000338
Denis Vlasenko04c63862006-11-17 18:58:49 +0000339 r = svstatus_print(*service);
340 if (chdir("log") == -1) {
341 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100342 printf("; log: "WARN"can't change to log service directory: %s",
Denis Vlasenko04c63862006-11-17 18:58:49 +0000343 strerror(errno));
344 }
345 } else if (svstatus_get()) {
346 printf("; ");
347 svstatus_print("log");
348 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000349 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000350 return r;
351}
352
353static int checkscript(void)
354{
355 char *prog[2];
356 struct stat s;
357 int pid, w;
358
359 if (stat("check", &s) == -1) {
360 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100361 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000362 return 0;
363 }
364 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000365 prog[0] = (char*)"./check";
366 prog[1] = NULL;
367 pid = spawn(prog);
368 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100369 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000370 return 0;
371 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000372 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100373 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000374 return 0;
375 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200376 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000377}
378
Denis Vlasenko322661d2007-01-29 23:43:52 +0000379static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000380{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000381 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200382 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000383 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000384
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000385 r = svstatus_get();
386 if (r == -1)
387 return -1;
388 if (r == 0) {
389 if (*a == 'x')
390 return 1;
391 return -1;
392 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200393 pid_le32 = svstatus.pid_le32;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000394 switch (*a) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000395 case 'x':
396 return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000397 case 'u':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200398 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000399 if (!checkscript()) return 0;
400 break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000401 case 'd':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200402 if (pid_le32) return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000403 break;
404 case 'c':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200405 if (pid_le32 && !checkscript()) return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000406 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000407 case 't':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200408 if (!pid_le32 && svstatus.want == 'd') break;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000409 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200410 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
Denis Vlasenko04c63862006-11-17 18:58:49 +0000411 return 0;
412 break;
413 case 'o':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000414 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200415 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000416 return 0;
417 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000418 printf(OK);
419 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000420 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000421 return 1;
422}
423
Denis Vlasenko322661d2007-01-29 23:43:52 +0000424static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000425{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200426 int fd, r, l;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000427
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200428/* Is it an optimization?
429 It causes problems with "sv o SRV; ...; sv d SRV"
430 ('d' is not passed to SRV because its .want == 'd'):
Denis Vlasenko45946f82007-08-20 17:27:40 +0000431 if (svstatus_get() <= 0)
432 return -1;
433 if (svstatus.want == *a)
434 return 0;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200435*/
Denys Vlasenko05e86052010-10-13 12:53:27 +0200436 fd = open("supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000437 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000438 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100439 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000440 else
441 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
442 return -1;
443 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200444 l = strlen(a);
445 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000446 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200447 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100448 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000449 return -1;
450 }
451 return 1;
452}
453
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000454int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200455int sv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000456{
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000458 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000459 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000460 unsigned waitsec = 7;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000461 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000462 int verbose = 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000463 int (*act)(const char*);
464 int (*cbk)(const char*);
465 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000466
Denis Vlasenkob9256052007-09-28 10:29:17 +0000467 INIT_G();
468
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000469 xfunc_error_retval = 100;
470
471 x = getenv("SVDIR");
472 if (x) varservice = x;
473 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000474 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000475
Denis Vlasenko1d426652008-03-17 09:09:09 +0000476 opt_complementary = "w+:vv"; /* -w N, -v is a counter */
Denys Vlasenko60a94142011-05-13 20:57:01 +0200477 getopt32(argv, "w:v", &waitsec, &verbose);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000478 argv += optind;
479 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000480 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000481
Denis Vlasenko04158e02009-02-02 10:48:06 +0000482 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000483 tstart = tnow;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200484 curdir = open(".", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000485 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000486 fatal_cannot("open current directory");
487
Denis Vlasenko322661d2007-01-29 23:43:52 +0000488 act = &control;
489 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000490 cbk = &check;
491
Denis Vlasenko04c63862006-11-17 18:58:49 +0000492 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000493 case 'x':
494 case 'e':
495 acts = "x";
496 if (!verbose) cbk = NULL;
497 break;
498 case 'X':
499 case 'E':
500 acts = "x";
501 kll = 1;
502 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000503 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000504 acts = "d";
505 kll = 1;
506 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000507 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000508 acts = "tc";
509 kll = 1;
510 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000511 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000512 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000513 act = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000514 acts = "c";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000515 break;
516 }
517 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
518 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000519 action[1] = '\0';
520 acts = action;
521 if (!verbose) cbk = NULL;
522 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000523 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000524 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000525 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000526 break;
527 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000528 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000529 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000530 break;
531 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000532 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000533 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000534 break;
535 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000536 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000537 act = &status;
538 cbk = NULL;
539 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000540 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000541 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000542 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000543 break;
544 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000545 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000546 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000547 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000548 acts = "tc";
549 kll = 1;
550 break;
551 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000552 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000553 acts = "tcu";
554 kll = 1;
555 break;
556 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000557 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000558 acts = "x";
559 kll = 1;
560 break;
561 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000562 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000563 acts = "d";
564 kll = 1;
565 break;
566 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000567 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000568 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000569 }
570
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200571 service = argv;
572 while ((x = *service) != NULL) {
573 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000574 if (chdir(varservice) == -1)
575 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000576 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200577 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000578 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100579 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000580 goto nullify_service_0;
581 }
582 if (act && (act(acts) == -1)) {
583 nullify_service_0:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200584 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000585 }
586 if (fchdir(curdir) == -1)
587 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000588 service++;
589 }
590
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000591 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200592 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000593 int diff;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000594
Denis Vlasenko45946f82007-08-20 17:27:40 +0000595 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200596 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000597 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200598 while ((x = *service) != NULL) {
599 if (x == (char*) -1L) /* "dead" */
600 goto next;
601 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000602 if (chdir(varservice) == -1)
603 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000604 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200605 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000606 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100607 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000608 goto nullify_service;
609 }
610 if (cbk(acts) != 0)
611 goto nullify_service;
612 want_exit = 0;
613 if (diff >= waitsec) {
614 printf(kll ? "kill: " : "timeout: ");
615 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200616 svstatus_print(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000617 ++rc;
618 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000619 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000620 if (kll)
621 control("k");
622 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200623 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000624 }
625 if (fchdir(curdir) == -1)
626 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200627 next:
628 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000629 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000630 if (want_exit) break;
631 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000632 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000633 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000634 return rc > 99 ? 99 : rc;
635}