blob: 825e9d45bbf8e4cec1c696cd17edc71eb5863db5 [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/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000173#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +0000174#include "runit_lib.h"
175
Denis Vlasenkob9256052007-09-28 10:29:17 +0000176struct globals {
177 const char *acts;
178 char **service;
179 unsigned rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000180/* "Bernstein" time format: unix + 0x400000000000000aULL */
Denis Vlasenkob9256052007-09-28 10:29:17 +0000181 uint64_t tstart, tnow;
182 svstatus_t svstatus;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100183} FIX_ALIASING;
Denis Vlasenkob9256052007-09-28 10:29:17 +0000184#define G (*(struct globals*)&bb_common_bufsiz1)
185#define acts (G.acts )
186#define service (G.service )
187#define rc (G.rc )
188#define tstart (G.tstart )
189#define tnow (G.tnow )
190#define svstatus (G.svstatus )
191#define INIT_G() do { } while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000192
Denis Vlasenko04c63862006-11-17 18:58:49 +0000193
Denys Vlasenko05e86052010-10-13 12:53:27 +0200194#define str_equal(s,t) (!strcmp((s), (t)))
195
196
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000197static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000198static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000199{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000200 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000201 _exit(151);
202}
203
Denis Vlasenko322661d2007-01-29 23:43:52 +0000204static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000205{
206 printf("%s%s: %s", p, *service, m1);
207 if (errno) {
208 printf(": %s", strerror(errno));
209 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000210 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000211}
212
Denis Vlasenko04c63862006-11-17 18:58:49 +0000213#define WARN "warning: "
214#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000215
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000216static void fail(const char *m1)
217{
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000218 ++rc;
219 out("fail: ", m1);
220}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000221static void failx(const char *m1)
222{
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000223 errno = 0;
224 fail(m1);
225}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000226static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000227{
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000228 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000229 /* "warning: <service>: <m1>\n" */
230 out("warning: ", m1);
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000231}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000232static void ok(const char *m1)
233{
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000234 errno = 0;
235 out(OK, m1);
236}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000237
238static int svstatus_get(void)
239{
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000240 int fd, r;
241
Denys Vlasenko05e86052010-10-13 12:53:27 +0200242 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000243 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000244 if (errno == ENODEV) {
245 *acts == 'x' ? ok("runsv not running")
246 : failx("runsv not running");
247 return 0;
248 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100249 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000250 return -1;
251 }
252 close(fd);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200253 fd = open("supervise/status", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000254 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100255 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000256 return -1;
257 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000258 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000260 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000261 case 20:
262 break;
263 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100264 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000265 return -1;
266 default:
267 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100268 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000269 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000270 }
271 return 1;
272}
273
Denis Vlasenko322661d2007-01-29 23:43:52 +0000274static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000276 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000277 int pid;
278 int normallyup = 0;
279 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000280 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000281
Denis Vlasenko04c63862006-11-17 18:58:49 +0000282 if (stat("down", &s) == -1) {
283 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100284 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000285 return 0;
286 }
287 normallyup = 1;
288 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000289 pid = SWAP_LE32(svstatus.pid_le32);
290 timestamp = SWAP_BE64(svstatus.time_be64);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000291 if (pid) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000292 switch (svstatus.run_or_finish) {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000293 case 1: printf("run: "); break;
294 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000295 }
296 printf("%s: (pid %d) ", m, pid);
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000297 } else {
298 printf("down: %s: ", m);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000299 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000300 diff = tnow - timestamp;
301 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000302 if (pid) {
303 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000304 if (svstatus.paused) printf(", paused");
305 if (svstatus.want == 'd') printf(", want down");
306 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000307 } else {
308 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000309 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000310 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000311 return pid ? 1 : 2;
312}
313
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000314static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000315{
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000316 int r;
317
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200318 if (svstatus_get() <= 0)
319 return 0;
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000320
Denis Vlasenko04c63862006-11-17 18:58:49 +0000321 r = svstatus_print(*service);
322 if (chdir("log") == -1) {
323 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100324 printf("; log: "WARN"can't change to log service directory: %s",
Denis Vlasenko04c63862006-11-17 18:58:49 +0000325 strerror(errno));
326 }
327 } else if (svstatus_get()) {
328 printf("; ");
329 svstatus_print("log");
330 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000331 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000332 return r;
333}
334
335static int checkscript(void)
336{
337 char *prog[2];
338 struct stat s;
339 int pid, w;
340
341 if (stat("check", &s) == -1) {
342 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100343 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000344 return 0;
345 }
346 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000347 prog[0] = (char*)"./check";
348 prog[1] = NULL;
349 pid = spawn(prog);
350 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100351 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000352 return 0;
353 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000354 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100355 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000356 return 0;
357 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200358 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000359}
360
Denis Vlasenko322661d2007-01-29 23:43:52 +0000361static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000362{
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000363 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200364 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000365 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000366
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000367 r = svstatus_get();
368 if (r == -1)
369 return -1;
370 if (r == 0) {
371 if (*a == 'x')
372 return 1;
373 return -1;
374 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200375 pid_le32 = svstatus.pid_le32;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000376 switch (*a) {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000377 case 'x':
378 return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000379 case 'u':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200380 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000381 if (!checkscript()) return 0;
382 break;
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000383 case 'd':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200384 if (pid_le32) return 0;
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000385 break;
386 case 'c':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200387 if (pid_le32 && !checkscript()) return 0;
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000388 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000389 case 't':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200390 if (!pid_le32 && svstatus.want == 'd') break;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000391 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200392 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
Denis Vlasenko04c63862006-11-17 18:58:49 +0000393 return 0;
394 break;
395 case 'o':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000396 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200397 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000398 return 0;
399 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000400 printf(OK);
401 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000402 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000403 return 1;
404}
405
Denis Vlasenko322661d2007-01-29 23:43:52 +0000406static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000407{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200408 int fd, r, l;
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000409
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200410/* Is it an optimization?
411 It causes problems with "sv o SRV; ...; sv d SRV"
412 ('d' is not passed to SRV because its .want == 'd'):
Denis Vlasenko45946f82007-08-20 17:27:40 +0000413 if (svstatus_get() <= 0)
414 return -1;
415 if (svstatus.want == *a)
416 return 0;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200417*/
Denys Vlasenko05e86052010-10-13 12:53:27 +0200418 fd = open("supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000419 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000420 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100421 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000422 else
423 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
424 return -1;
425 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200426 l = strlen(a);
427 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000428 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200429 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100430 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000431 return -1;
432 }
433 return 1;
434}
435
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000436int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200437int sv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000438{
Denis Vlasenko04c63862006-11-17 18:58:49 +0000439 char *x;
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000440 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000441 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000442 unsigned waitsec = 7;
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000443 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000444 int verbose = 0;
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000445 int (*act)(const char*);
446 int (*cbk)(const char*);
447 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000448
Denis Vlasenkob9256052007-09-28 10:29:17 +0000449 INIT_G();
450
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000451 xfunc_error_retval = 100;
452
453 x = getenv("SVDIR");
454 if (x) varservice = x;
455 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000456 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000457
Denis Vlasenko1d426652008-03-17 09:09:09 +0000458 opt_complementary = "w+:vv"; /* -w N, -v is a counter */
Denys Vlasenko60a94142011-05-13 20:57:01 +0200459 getopt32(argv, "w:v", &waitsec, &verbose);
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000460 argv += optind;
461 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000462 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000463
Denis Vlasenko04158e02009-02-02 10:48:06 +0000464 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000465 tstart = tnow;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200466 curdir = open(".", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000467 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000468 fatal_cannot("open current directory");
469
Denis Vlasenko322661d2007-01-29 23:43:52 +0000470 act = &control;
471 acts = "s";
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000472 cbk = &check;
473
Denis Vlasenko04c63862006-11-17 18:58:49 +0000474 switch (*action) {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000475 case 'x':
476 case 'e':
477 acts = "x";
478 if (!verbose) cbk = NULL;
479 break;
480 case 'X':
481 case 'E':
482 acts = "x";
483 kll = 1;
484 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000485 case 'D':
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000486 acts = "d";
487 kll = 1;
488 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000489 case 'T':
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000490 acts = "tc";
491 kll = 1;
492 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000493 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000494 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000495 act = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000496 acts = "c";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000497 break;
498 }
499 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
500 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000501 action[1] = '\0';
502 acts = action;
503 if (!verbose) cbk = NULL;
504 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000505 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000506 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000507 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000508 break;
509 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000510 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000511 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000512 break;
513 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000514 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000515 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000516 break;
517 }
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000518 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000519 act = &status;
520 cbk = NULL;
521 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000522 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000523 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000524 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000525 break;
526 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000527 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000528 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000529 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000530 acts = "tc";
531 kll = 1;
532 break;
533 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000534 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000535 acts = "tcu";
536 kll = 1;
537 break;
538 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000539 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000540 acts = "x";
541 kll = 1;
542 break;
543 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000544 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000545 acts = "d";
546 kll = 1;
547 break;
548 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000549 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000550 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 }
552
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200553 service = argv;
554 while ((x = *service) != NULL) {
555 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000556 if (chdir(varservice) == -1)
557 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000558 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200559 if (chdir(x) == -1) {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000560 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100561 fail("can't change to service directory");
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000562 goto nullify_service_0;
563 }
564 if (act && (act(acts) == -1)) {
565 nullify_service_0:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200566 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000567 }
568 if (fchdir(curdir) == -1)
569 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000570 service++;
571 }
572
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000573 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200574 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000575 int diff;
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000576
Denis Vlasenko45946f82007-08-20 17:27:40 +0000577 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200578 service = argv;
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000579 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200580 while ((x = *service) != NULL) {
581 if (x == (char*) -1L) /* "dead" */
582 goto next;
583 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000584 if (chdir(varservice) == -1)
585 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000586 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200587 if (chdir(x) == -1) {
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000588 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100589 fail("can't change to service directory");
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000590 goto nullify_service;
591 }
592 if (cbk(acts) != 0)
593 goto nullify_service;
594 want_exit = 0;
595 if (diff >= waitsec) {
596 printf(kll ? "kill: " : "timeout: ");
597 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200598 svstatus_print(x);
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000599 ++rc;
600 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000601 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000602 if (kll)
603 control("k");
604 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200605 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000606 }
607 if (fchdir(curdir) == -1)
608 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200609 next:
610 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000611 }
Denis Vlasenko7fca91a2007-02-02 01:19:09 +0000612 if (want_exit) break;
613 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000614 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000615 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000616 return rc > 99 ? 99 : rc;
617}