blob: 96ebb749d41d2a906a5070cceb77da6e73cfff4b [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
156#include <sys/poll.h>
157#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000158#include "libbb.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +0000159#include "runit_lib.h"
160
Denis Vlasenkob9256052007-09-28 10:29:17 +0000161struct globals {
162 const char *acts;
163 char **service;
164 unsigned rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000165/* "Bernstein" time format: unix + 0x400000000000000aULL */
Denis Vlasenkob9256052007-09-28 10:29:17 +0000166 uint64_t tstart, tnow;
167 svstatus_t svstatus;
168};
169#define G (*(struct globals*)&bb_common_bufsiz1)
170#define acts (G.acts )
171#define service (G.service )
172#define rc (G.rc )
173#define tstart (G.tstart )
174#define tnow (G.tnow )
175#define svstatus (G.svstatus )
176#define INIT_G() do { } while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000177
Denis Vlasenko04c63862006-11-17 18:58:49 +0000178
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000179static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000180static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000181{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000182 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000183 _exit(151);
184}
185
Denis Vlasenko322661d2007-01-29 23:43:52 +0000186static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000187{
188 printf("%s%s: %s", p, *service, m1);
189 if (errno) {
190 printf(": %s", strerror(errno));
191 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000192 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000193}
194
Denis Vlasenko04c63862006-11-17 18:58:49 +0000195#define WARN "warning: "
196#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000197
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000198static void fail(const char *m1)
199{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000200 ++rc;
201 out("fail: ", m1);
202}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000203static void failx(const char *m1)
204{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000205 errno = 0;
206 fail(m1);
207}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000208static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000209{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000210 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000211 /* "warning: <service>: <m1>\n" */
212 out("warning: ", m1);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000213}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000214static void ok(const char *m1)
215{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000216 errno = 0;
217 out(OK, m1);
218}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000219
220static int svstatus_get(void)
221{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000222 int fd, r;
223
Denis Vlasenko322661d2007-01-29 23:43:52 +0000224 fd = open_write("supervise/ok");
225 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000226 if (errno == ENODEV) {
227 *acts == 'x' ? ok("runsv not running")
228 : failx("runsv not running");
229 return 0;
230 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100231 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000232 return -1;
233 }
234 close(fd);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000235 fd = open_read("supervise/status");
236 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100237 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 return -1;
239 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000240 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000241 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000242 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000243 case 20:
244 break;
245 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100246 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000247 return -1;
248 default:
249 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100250 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000251 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000252 }
253 return 1;
254}
255
Denis Vlasenko322661d2007-01-29 23:43:52 +0000256static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000257{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000258 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259 int pid;
260 int normallyup = 0;
261 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000262 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000263
Denis Vlasenko04c63862006-11-17 18:58:49 +0000264 if (stat("down", &s) == -1) {
265 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100266 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000267 return 0;
268 }
269 normallyup = 1;
270 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000271 pid = SWAP_LE32(svstatus.pid_le32);
272 timestamp = SWAP_BE64(svstatus.time_be64);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000273 if (pid) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000274 switch (svstatus.run_or_finish) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000275 case 1: printf("run: "); break;
276 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000277 }
278 printf("%s: (pid %d) ", m, pid);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000279 } else {
280 printf("down: %s: ", m);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000281 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000282 diff = tnow - timestamp;
283 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000284 if (pid) {
285 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000286 if (svstatus.paused) printf(", paused");
287 if (svstatus.want == 'd') printf(", want down");
288 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000289 } else {
290 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000291 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000293 return pid ? 1 : 2;
294}
295
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000296static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000297{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000298 int r;
299
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200300 if (svstatus_get() <= 0)
301 return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000302
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303 r = svstatus_print(*service);
304 if (chdir("log") == -1) {
305 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100306 printf("; log: "WARN"can't change to log service directory: %s",
Denis Vlasenko04c63862006-11-17 18:58:49 +0000307 strerror(errno));
308 }
309 } else if (svstatus_get()) {
310 printf("; ");
311 svstatus_print("log");
312 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000313 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314 return r;
315}
316
317static int checkscript(void)
318{
319 char *prog[2];
320 struct stat s;
321 int pid, w;
322
323 if (stat("check", &s) == -1) {
324 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100325 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000326 return 0;
327 }
328 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000329 prog[0] = (char*)"./check";
330 prog[1] = NULL;
331 pid = spawn(prog);
332 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100333 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000334 return 0;
335 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000336 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100337 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000338 return 0;
339 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200340 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000341}
342
Denis Vlasenko322661d2007-01-29 23:43:52 +0000343static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000344{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000345 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200346 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000347 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000348
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000349 r = svstatus_get();
350 if (r == -1)
351 return -1;
352 if (r == 0) {
353 if (*a == 'x')
354 return 1;
355 return -1;
356 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200357 pid_le32 = svstatus.pid_le32;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000358 switch (*a) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000359 case 'x':
360 return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000361 case 'u':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200362 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000363 if (!checkscript()) return 0;
364 break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000365 case 'd':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200366 if (pid_le32) return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000367 break;
368 case 'c':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200369 if (pid_le32 && !checkscript()) return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000370 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000371 case 't':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200372 if (!pid_le32 && svstatus.want == 'd') break;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000373 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200374 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
Denis Vlasenko04c63862006-11-17 18:58:49 +0000375 return 0;
376 break;
377 case 'o':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000378 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200379 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000380 return 0;
381 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000382 printf(OK);
383 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000384 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000385 return 1;
386}
387
Denis Vlasenko322661d2007-01-29 23:43:52 +0000388static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000389{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200390 int fd, r, l;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000391
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200392/* Is it an optimization?
393 It causes problems with "sv o SRV; ...; sv d SRV"
394 ('d' is not passed to SRV because its .want == 'd'):
Denis Vlasenko45946f82007-08-20 17:27:40 +0000395 if (svstatus_get() <= 0)
396 return -1;
397 if (svstatus.want == *a)
398 return 0;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200399*/
Denis Vlasenko322661d2007-01-29 23:43:52 +0000400 fd = open_write("supervise/control");
401 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000402 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100403 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000404 else
405 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
406 return -1;
407 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200408 l = strlen(a);
409 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000410 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200411 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100412 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000413 return -1;
414 }
415 return 1;
416}
417
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000418int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200419int sv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000420{
421 unsigned opt;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000422 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000423 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000424 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000425 unsigned waitsec = 7;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000426 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000427 int verbose = 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000428 int (*act)(const char*);
429 int (*cbk)(const char*);
430 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000431
Denis Vlasenkob9256052007-09-28 10:29:17 +0000432 INIT_G();
433
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000434 xfunc_error_retval = 100;
435
436 x = getenv("SVDIR");
437 if (x) varservice = x;
438 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000439 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000440
Denis Vlasenko1d426652008-03-17 09:09:09 +0000441 opt_complementary = "w+:vv"; /* -w N, -v is a counter */
442 opt = getopt32(argv, "w:v", &waitsec, &verbose);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000443 argv += optind;
444 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000445 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000446
Denis Vlasenko04158e02009-02-02 10:48:06 +0000447 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000448 tstart = tnow;
449 curdir = open_read(".");
450 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000451 fatal_cannot("open current directory");
452
Denis Vlasenko322661d2007-01-29 23:43:52 +0000453 act = &control;
454 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000455 cbk = &check;
456
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000458 case 'x':
459 case 'e':
460 acts = "x";
461 if (!verbose) cbk = NULL;
462 break;
463 case 'X':
464 case 'E':
465 acts = "x";
466 kll = 1;
467 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000468 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000469 acts = "d";
470 kll = 1;
471 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000472 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000473 acts = "tc";
474 kll = 1;
475 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000476 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000477 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000478 act = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000479 acts = "c";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000480 break;
481 }
482 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
483 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000484 action[1] = '\0';
485 acts = action;
486 if (!verbose) cbk = NULL;
487 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000488 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000489 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000490 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000491 break;
492 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000493 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000494 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000495 break;
496 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000497 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000498 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000499 break;
500 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000501 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000502 act = &status;
503 cbk = NULL;
504 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000505 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000506 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000507 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000508 break;
509 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000510 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000511 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000512 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000513 acts = "tc";
514 kll = 1;
515 break;
516 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000517 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000518 acts = "tcu";
519 kll = 1;
520 break;
521 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000522 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000523 acts = "x";
524 kll = 1;
525 break;
526 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000527 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000528 acts = "d";
529 kll = 1;
530 break;
531 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000532 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000533 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000534 }
535
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200536 service = argv;
537 while ((x = *service) != NULL) {
538 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000539 if (chdir(varservice) == -1)
540 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000541 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200542 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000543 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100544 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000545 goto nullify_service_0;
546 }
547 if (act && (act(acts) == -1)) {
548 nullify_service_0:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200549 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000550 }
551 if (fchdir(curdir) == -1)
552 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000553 service++;
554 }
555
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000556 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200557 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000558 int diff;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000559
Denis Vlasenko45946f82007-08-20 17:27:40 +0000560 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200561 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000562 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200563 while ((x = *service) != NULL) {
564 if (x == (char*) -1L) /* "dead" */
565 goto next;
566 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000567 if (chdir(varservice) == -1)
568 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000569 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200570 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000571 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100572 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000573 goto nullify_service;
574 }
575 if (cbk(acts) != 0)
576 goto nullify_service;
577 want_exit = 0;
578 if (diff >= waitsec) {
579 printf(kll ? "kill: " : "timeout: ");
580 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200581 svstatus_print(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000582 ++rc;
583 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000584 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000585 if (kll)
586 control("k");
587 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200588 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000589 }
590 if (fchdir(curdir) == -1)
591 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200592 next:
593 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000594 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000595 if (want_exit) break;
596 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000597 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000598 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000599 return rc > 99 ? 99 : rc;
600}