blob: 2a256a6b406b1d94f4816b59530d0380fb44069f [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"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200192#include "common_bufsiz.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +0000193#include "runit_lib.h"
194
Denis Vlasenkob9256052007-09-28 10:29:17 +0000195struct globals {
196 const char *acts;
197 char **service;
198 unsigned rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000199/* "Bernstein" time format: unix + 0x400000000000000aULL */
Denis Vlasenkob9256052007-09-28 10:29:17 +0000200 uint64_t tstart, tnow;
201 svstatus_t svstatus;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100202} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200203#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkob9256052007-09-28 10:29:17 +0000204#define acts (G.acts )
205#define service (G.service )
206#define rc (G.rc )
207#define tstart (G.tstart )
208#define tnow (G.tnow )
209#define svstatus (G.svstatus )
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200210#define INIT_G() do { setup_common_bufsiz(); } while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000211
Denis Vlasenko04c63862006-11-17 18:58:49 +0000212
Denys Vlasenko05e86052010-10-13 12:53:27 +0200213#define str_equal(s,t) (!strcmp((s), (t)))
214
215
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000216static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000217static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000218{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000219 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000220 _exit(151);
221}
222
Denis Vlasenko322661d2007-01-29 23:43:52 +0000223static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000224{
225 printf("%s%s: %s", p, *service, m1);
226 if (errno) {
227 printf(": %s", strerror(errno));
228 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000229 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000230}
231
Denis Vlasenko04c63862006-11-17 18:58:49 +0000232#define WARN "warning: "
233#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000234
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000235static void fail(const char *m1)
236{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000237 ++rc;
238 out("fail: ", m1);
239}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000240static void failx(const char *m1)
241{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000242 errno = 0;
243 fail(m1);
244}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000245static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000246{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000247 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000248 /* "warning: <service>: <m1>\n" */
249 out("warning: ", m1);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000250}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000251static void ok(const char *m1)
252{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000253 errno = 0;
254 out(OK, m1);
255}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000256
257static int svstatus_get(void)
258{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000259 int fd, r;
260
Denys Vlasenko05e86052010-10-13 12:53:27 +0200261 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000262 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 if (errno == ENODEV) {
264 *acts == 'x' ? ok("runsv not running")
265 : failx("runsv not running");
266 return 0;
267 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100268 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000269 return -1;
270 }
271 close(fd);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200272 fd = open("supervise/status", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000273 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100274 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275 return -1;
276 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000277 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000278 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000279 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000280 case 20:
281 break;
282 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100283 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000284 return -1;
285 default:
286 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100287 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000288 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000289 }
290 return 1;
291}
292
Denis Vlasenko322661d2007-01-29 23:43:52 +0000293static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000295 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000296 int pid;
297 int normallyup = 0;
298 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000299 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000300
Denis Vlasenko04c63862006-11-17 18:58:49 +0000301 if (stat("down", &s) == -1) {
302 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100303 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000304 return 0;
305 }
306 normallyup = 1;
307 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000308 pid = SWAP_LE32(svstatus.pid_le32);
309 timestamp = SWAP_BE64(svstatus.time_be64);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000310 if (pid) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000311 switch (svstatus.run_or_finish) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000312 case 1: printf("run: "); break;
313 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314 }
315 printf("%s: (pid %d) ", m, pid);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000316 } else {
317 printf("down: %s: ", m);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000318 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000319 diff = tnow - timestamp;
320 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000321 if (pid) {
322 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000323 if (svstatus.paused) printf(", paused");
324 if (svstatus.want == 'd') printf(", want down");
325 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000326 } else {
327 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000328 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000329 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000330 return pid ? 1 : 2;
331}
332
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000333static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000334{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000335 int r;
336
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200337 if (svstatus_get() <= 0)
338 return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000339
Denis Vlasenko04c63862006-11-17 18:58:49 +0000340 r = svstatus_print(*service);
341 if (chdir("log") == -1) {
342 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100343 printf("; log: "WARN"can't change to log service directory: %s",
Denis Vlasenko04c63862006-11-17 18:58:49 +0000344 strerror(errno));
345 }
346 } else if (svstatus_get()) {
347 printf("; ");
348 svstatus_print("log");
349 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000350 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000351 return r;
352}
353
354static int checkscript(void)
355{
356 char *prog[2];
357 struct stat s;
358 int pid, w;
359
360 if (stat("check", &s) == -1) {
361 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100362 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000363 return 0;
364 }
365 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000366 prog[0] = (char*)"./check";
367 prog[1] = NULL;
368 pid = spawn(prog);
369 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100370 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000371 return 0;
372 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000373 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100374 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000375 return 0;
376 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200377 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000378}
379
Denis Vlasenko322661d2007-01-29 23:43:52 +0000380static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000381{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000382 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200383 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000384 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000385
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000386 r = svstatus_get();
387 if (r == -1)
388 return -1;
389 if (r == 0) {
390 if (*a == 'x')
391 return 1;
392 return -1;
393 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200394 pid_le32 = svstatus.pid_le32;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000395 switch (*a) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000396 case 'x':
397 return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000398 case 'u':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200399 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000400 if (!checkscript()) return 0;
401 break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000402 case 'd':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200403 if (pid_le32) return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000404 break;
405 case 'c':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200406 if (pid_le32 && !checkscript()) return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000407 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000408 case 't':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200409 if (!pid_le32 && svstatus.want == 'd') break;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000410 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200411 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
Denis Vlasenko04c63862006-11-17 18:58:49 +0000412 return 0;
413 break;
414 case 'o':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000415 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200416 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000417 return 0;
418 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000419 printf(OK);
420 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000421 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000422 return 1;
423}
424
Denis Vlasenko322661d2007-01-29 23:43:52 +0000425static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000426{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200427 int fd, r, l;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000428
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200429/* Is it an optimization?
430 It causes problems with "sv o SRV; ...; sv d SRV"
431 ('d' is not passed to SRV because its .want == 'd'):
Denis Vlasenko45946f82007-08-20 17:27:40 +0000432 if (svstatus_get() <= 0)
433 return -1;
434 if (svstatus.want == *a)
435 return 0;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200436*/
Denys Vlasenko05e86052010-10-13 12:53:27 +0200437 fd = open("supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000438 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000439 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100440 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000441 else
442 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
443 return -1;
444 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200445 l = strlen(a);
446 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000447 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200448 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100449 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000450 return -1;
451 }
452 return 1;
453}
454
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000455int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200456int sv_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457{
Denis Vlasenko04c63862006-11-17 18:58:49 +0000458 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000459 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000460 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000461 unsigned waitsec = 7;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000462 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000463 int verbose = 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000464 int (*act)(const char*);
465 int (*cbk)(const char*);
466 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000467
Denis Vlasenkob9256052007-09-28 10:29:17 +0000468 INIT_G();
469
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000470 xfunc_error_retval = 100;
471
472 x = getenv("SVDIR");
473 if (x) varservice = x;
474 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000475 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000476
Denis Vlasenko1d426652008-03-17 09:09:09 +0000477 opt_complementary = "w+:vv"; /* -w N, -v is a counter */
Denys Vlasenko60a94142011-05-13 20:57:01 +0200478 getopt32(argv, "w:v", &waitsec, &verbose);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000479 argv += optind;
480 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000481 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000482
Denis Vlasenko04158e02009-02-02 10:48:06 +0000483 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000484 tstart = tnow;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200485 curdir = open(".", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000486 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000487 fatal_cannot("open current directory");
488
Denis Vlasenko322661d2007-01-29 23:43:52 +0000489 act = &control;
490 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000491 cbk = &check;
492
Denis Vlasenko04c63862006-11-17 18:58:49 +0000493 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000494 case 'x':
495 case 'e':
496 acts = "x";
497 if (!verbose) cbk = NULL;
498 break;
499 case 'X':
500 case 'E':
501 acts = "x";
502 kll = 1;
503 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000504 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000505 acts = "d";
506 kll = 1;
507 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000508 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000509 acts = "tc";
510 kll = 1;
511 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000512 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000513 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000514 act = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000515 acts = "c";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000516 break;
517 }
518 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
519 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000520 action[1] = '\0';
521 acts = action;
522 if (!verbose) cbk = NULL;
523 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000524 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000525 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000526 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000527 break;
528 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000529 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000530 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000531 break;
532 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000533 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000534 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000535 break;
536 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000537 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000538 act = &status;
539 cbk = NULL;
540 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000541 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000542 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000543 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000544 break;
545 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000546 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000547 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000548 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000549 acts = "tc";
550 kll = 1;
551 break;
552 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000553 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000554 acts = "tcu";
555 kll = 1;
556 break;
557 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000558 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000559 acts = "x";
560 kll = 1;
561 break;
562 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000563 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000564 acts = "d";
565 kll = 1;
566 break;
567 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000568 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000569 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000570 }
571
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200572 service = argv;
573 while ((x = *service) != NULL) {
574 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000575 if (chdir(varservice) == -1)
576 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000577 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200578 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000579 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100580 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000581 goto nullify_service_0;
582 }
583 if (act && (act(acts) == -1)) {
584 nullify_service_0:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200585 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000586 }
587 if (fchdir(curdir) == -1)
588 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000589 service++;
590 }
591
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000592 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200593 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000594 int diff;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000595
Denis Vlasenko45946f82007-08-20 17:27:40 +0000596 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200597 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000598 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200599 while ((x = *service) != NULL) {
600 if (x == (char*) -1L) /* "dead" */
601 goto next;
602 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000603 if (chdir(varservice) == -1)
604 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000605 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200606 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000607 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100608 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000609 goto nullify_service;
610 }
611 if (cbk(acts) != 0)
612 goto nullify_service;
613 want_exit = 0;
614 if (diff >= waitsec) {
615 printf(kll ? "kill: " : "timeout: ");
616 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200617 svstatus_print(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000618 ++rc;
619 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000620 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000621 if (kll)
622 control("k");
623 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200624 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000625 }
626 if (fchdir(curdir) == -1)
627 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200628 next:
629 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000630 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000631 if (want_exit) break;
632 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000633 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000634 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000635 return rc > 99 ? 99 : rc;
636}