blob: 5b01c875c1ae1215db9add5635a2f5801979c0af [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> */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000154/* TODO: depends on runit_lib.c - review and reduce/eliminate */
155
Pere Orga5bc8c002011-04-11 03:29:49 +0200156//usage:#define sv_trivial_usage
157//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
158//usage:#define sv_full_usage "\n\n"
159//usage: "Control services monitored by runsv supervisor.\n"
160//usage: "Commands (only first character is enough):\n"
161//usage: "\n"
162//usage: "status: query service status\n"
163//usage: "up: if service isn't running, start it. If service stops, restart it\n"
164//usage: "once: like 'up', but if service stops, don't restart it\n"
165//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
166//usage: " if it exists. After it stops, don't restart service\n"
167//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
168//usage: " runsv exits too\n"
169//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
170//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
171
Denis Vlasenko04c63862006-11-17 18:58:49 +0000172#include <sys/poll.h>
173#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000174#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +0000175#include "runit_lib.h"
176
Denis Vlasenkob9256052007-09-28 10:29:17 +0000177struct globals {
178 const char *acts;
179 char **service;
180 unsigned rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000181/* "Bernstein" time format: unix + 0x400000000000000aULL */
Denis Vlasenkob9256052007-09-28 10:29:17 +0000182 uint64_t tstart, tnow;
183 svstatus_t svstatus;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100184} FIX_ALIASING;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000185#define G (*(struct globals*)&bb_common_bufsiz1)
186#define acts (G.acts )
187#define service (G.service )
188#define rc (G.rc )
189#define tstart (G.tstart )
190#define tnow (G.tnow )
191#define svstatus (G.svstatus )
192#define INIT_G() do { } while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000193
Denis Vlasenko04c63862006-11-17 18:58:49 +0000194
Denys Vlasenko05e86052010-10-13 12:53:27 +0200195#define str_equal(s,t) (!strcmp((s), (t)))
196
197
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000198static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000199static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000200{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000201 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000202 _exit(151);
203}
204
Denis Vlasenko322661d2007-01-29 23:43:52 +0000205static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000206{
207 printf("%s%s: %s", p, *service, m1);
208 if (errno) {
209 printf(": %s", strerror(errno));
210 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000211 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000212}
213
Denis Vlasenko04c63862006-11-17 18:58:49 +0000214#define WARN "warning: "
215#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000216
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000217static void fail(const char *m1)
218{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000219 ++rc;
220 out("fail: ", m1);
221}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000222static void failx(const char *m1)
223{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000224 errno = 0;
225 fail(m1);
226}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000227static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000228{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000229 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000230 /* "warning: <service>: <m1>\n" */
231 out("warning: ", m1);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000232}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000233static void ok(const char *m1)
234{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000235 errno = 0;
236 out(OK, m1);
237}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238
239static int svstatus_get(void)
240{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000241 int fd, r;
242
Denys Vlasenko05e86052010-10-13 12:53:27 +0200243 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000244 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000245 if (errno == ENODEV) {
246 *acts == 'x' ? ok("runsv not running")
247 : failx("runsv not running");
248 return 0;
249 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100250 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000251 return -1;
252 }
253 close(fd);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200254 fd = open("supervise/status", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000255 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100256 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000257 return -1;
258 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000259 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000260 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000261 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000262 case 20:
263 break;
264 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100265 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000266 return -1;
267 default:
268 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100269 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000270 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000271 }
272 return 1;
273}
274
Denis Vlasenko322661d2007-01-29 23:43:52 +0000275static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000276{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000277 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000278 int pid;
279 int normallyup = 0;
280 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000281 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000282
Denis Vlasenko04c63862006-11-17 18:58:49 +0000283 if (stat("down", &s) == -1) {
284 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100285 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000286 return 0;
287 }
288 normallyup = 1;
289 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000290 pid = SWAP_LE32(svstatus.pid_le32);
291 timestamp = SWAP_BE64(svstatus.time_be64);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 if (pid) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000293 switch (svstatus.run_or_finish) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000294 case 1: printf("run: "); break;
295 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000296 }
297 printf("%s: (pid %d) ", m, pid);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000298 } else {
299 printf("down: %s: ", m);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000300 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000301 diff = tnow - timestamp;
302 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000303 if (pid) {
304 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000305 if (svstatus.paused) printf(", paused");
306 if (svstatus.want == 'd') printf(", want down");
307 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000308 } else {
309 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000310 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000311 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000312 return pid ? 1 : 2;
313}
314
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000315static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000316{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000317 int r;
318
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200319 if (svstatus_get() <= 0)
320 return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000321
Denis Vlasenko04c63862006-11-17 18:58:49 +0000322 r = svstatus_print(*service);
323 if (chdir("log") == -1) {
324 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100325 printf("; log: "WARN"can't change to log service directory: %s",
Denis Vlasenko04c63862006-11-17 18:58:49 +0000326 strerror(errno));
327 }
328 } else if (svstatus_get()) {
329 printf("; ");
330 svstatus_print("log");
331 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000332 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000333 return r;
334}
335
336static int checkscript(void)
337{
338 char *prog[2];
339 struct stat s;
340 int pid, w;
341
342 if (stat("check", &s) == -1) {
343 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100344 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000345 return 0;
346 }
347 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000348 prog[0] = (char*)"./check";
349 prog[1] = NULL;
350 pid = spawn(prog);
351 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100352 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000353 return 0;
354 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000355 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100356 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000357 return 0;
358 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200359 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000360}
361
Denis Vlasenko322661d2007-01-29 23:43:52 +0000362static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000363{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000364 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200365 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000366 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000367
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000368 r = svstatus_get();
369 if (r == -1)
370 return -1;
371 if (r == 0) {
372 if (*a == 'x')
373 return 1;
374 return -1;
375 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200376 pid_le32 = svstatus.pid_le32;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000377 switch (*a) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000378 case 'x':
379 return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000380 case 'u':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200381 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000382 if (!checkscript()) return 0;
383 break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000384 case 'd':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200385 if (pid_le32) return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000386 break;
387 case 'c':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200388 if (pid_le32 && !checkscript()) return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000389 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000390 case 't':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200391 if (!pid_le32 && svstatus.want == 'd') break;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000392 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200393 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
Denis Vlasenko04c63862006-11-17 18:58:49 +0000394 return 0;
395 break;
396 case 'o':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000397 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200398 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000399 return 0;
400 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000401 printf(OK);
402 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000403 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000404 return 1;
405}
406
Denis Vlasenko322661d2007-01-29 23:43:52 +0000407static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000408{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200409 int fd, r, l;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000410
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200411/* Is it an optimization?
412 It causes problems with "sv o SRV; ...; sv d SRV"
413 ('d' is not passed to SRV because its .want == 'd'):
Denis Vlasenko45946f82007-08-20 17:27:40 +0000414 if (svstatus_get() <= 0)
415 return -1;
416 if (svstatus.want == *a)
417 return 0;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200418*/
Denys Vlasenko05e86052010-10-13 12:53:27 +0200419 fd = open("supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000420 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000421 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100422 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000423 else
424 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
425 return -1;
426 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200427 l = strlen(a);
428 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000429 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200430 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100431 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000432 return -1;
433 }
434 return 1;
435}
436
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000437int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200438int sv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000439{
Denis Vlasenko04c63862006-11-17 18:58:49 +0000440 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000441 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000442 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000443 unsigned waitsec = 7;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000444 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000445 int verbose = 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000446 int (*act)(const char*);
447 int (*cbk)(const char*);
448 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000449
Denis Vlasenkob9256052007-09-28 10:29:17 +0000450 INIT_G();
451
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000452 xfunc_error_retval = 100;
453
454 x = getenv("SVDIR");
455 if (x) varservice = x;
456 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000457 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000458
Denis Vlasenko1d426652008-03-17 09:09:09 +0000459 opt_complementary = "w+:vv"; /* -w N, -v is a counter */
Denys Vlasenko60a94142011-05-13 20:57:01 +0200460 getopt32(argv, "w:v", &waitsec, &verbose);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000461 argv += optind;
462 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000463 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000464
Denis Vlasenko04158e02009-02-02 10:48:06 +0000465 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000466 tstart = tnow;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200467 curdir = open(".", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000468 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000469 fatal_cannot("open current directory");
470
Denis Vlasenko322661d2007-01-29 23:43:52 +0000471 act = &control;
472 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000473 cbk = &check;
474
Denis Vlasenko04c63862006-11-17 18:58:49 +0000475 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000476 case 'x':
477 case 'e':
478 acts = "x";
479 if (!verbose) cbk = NULL;
480 break;
481 case 'X':
482 case 'E':
483 acts = "x";
484 kll = 1;
485 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000486 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000487 acts = "d";
488 kll = 1;
489 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000490 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000491 acts = "tc";
492 kll = 1;
493 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000494 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000495 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000496 act = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000497 acts = "c";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000498 break;
499 }
500 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
501 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000502 action[1] = '\0';
503 acts = action;
504 if (!verbose) cbk = NULL;
505 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000506 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000507 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000508 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000509 break;
510 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000511 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000512 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000513 break;
514 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000515 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000516 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000517 break;
518 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000519 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000520 act = &status;
521 cbk = NULL;
522 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000523 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000524 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000525 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000526 break;
527 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000528 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000529 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000530 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000531 acts = "tc";
532 kll = 1;
533 break;
534 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000535 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000536 acts = "tcu";
537 kll = 1;
538 break;
539 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000540 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000541 acts = "x";
542 kll = 1;
543 break;
544 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000545 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000546 acts = "d";
547 kll = 1;
548 break;
549 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000550 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000551 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000552 }
553
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200554 service = argv;
555 while ((x = *service) != NULL) {
556 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000557 if (chdir(varservice) == -1)
558 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000559 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200560 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000561 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100562 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000563 goto nullify_service_0;
564 }
565 if (act && (act(acts) == -1)) {
566 nullify_service_0:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200567 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000568 }
569 if (fchdir(curdir) == -1)
570 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000571 service++;
572 }
573
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000574 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200575 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000576 int diff;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000577
Denis Vlasenko45946f82007-08-20 17:27:40 +0000578 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200579 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000580 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200581 while ((x = *service) != NULL) {
582 if (x == (char*) -1L) /* "dead" */
583 goto next;
584 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000585 if (chdir(varservice) == -1)
586 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000587 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200588 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000589 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100590 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000591 goto nullify_service;
592 }
593 if (cbk(acts) != 0)
594 goto nullify_service;
595 want_exit = 0;
596 if (diff >= waitsec) {
597 printf(kll ? "kill: " : "timeout: ");
598 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200599 svstatus_print(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000600 ++rc;
601 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000602 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000603 if (kll)
604 control("k");
605 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200606 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000607 }
608 if (fchdir(curdir) == -1)
609 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200610 next:
611 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000612 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000613 if (want_exit) break;
614 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000615 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000616 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000617 return rc > 99 ? 99 : rc;
618}