blob: 71865bd4f234e0f8ad5f90762b409ca41d402aa6 [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"
Denys Vlasenko4051a992016-12-05 13:56:40 +0100169//config:
170//config:config SVC
171//config: bool "svc"
172//config: default y
173//config: help
174//config: svc controls the state of services monitored by the runsv supervisor.
175//config: It is comaptible with daemontools command with the same name.
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200176
177//applet:IF_SV(APPLET(sv, BB_DIR_USR_BIN, BB_SUID_DROP))
Denys Vlasenko4051a992016-12-05 13:56:40 +0100178//applet:IF_SV(APPLET(svc, BB_DIR_USR_BIN, BB_SUID_DROP))
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200179
180//kbuild:lib-$(CONFIG_SV) += sv.o
Denys Vlasenko4051a992016-12-05 13:56:40 +0100181//kbuild:lib-$(CONFIG_SVC) += sv.o
Pere Orga5bc8c002011-04-11 03:29:49 +0200182
Denis Vlasenko04c63862006-11-17 18:58:49 +0000183#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000184#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200185#include "common_bufsiz.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +0000186#include "runit_lib.h"
187
Denis Vlasenkob9256052007-09-28 10:29:17 +0000188struct globals {
189 const char *acts;
190 char **service;
191 unsigned rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000192/* "Bernstein" time format: unix + 0x400000000000000aULL */
Denis Vlasenkob9256052007-09-28 10:29:17 +0000193 uint64_t tstart, tnow;
194 svstatus_t svstatus;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100195} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200196#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkob9256052007-09-28 10:29:17 +0000197#define acts (G.acts )
198#define service (G.service )
199#define rc (G.rc )
200#define tstart (G.tstart )
201#define tnow (G.tnow )
202#define svstatus (G.svstatus )
Denys Vlasenko47cfbf32016-04-21 18:18:48 +0200203#define INIT_G() do { setup_common_bufsiz(); } while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000204
Denis Vlasenko04c63862006-11-17 18:58:49 +0000205
Denys Vlasenko1d3a04a2016-11-28 01:22:57 +0100206#define str_equal(s,t) (strcmp((s), (t)) == 0)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200207
208
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000209static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000210static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000211{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000212 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000213 _exit(151);
214}
215
Denis Vlasenko322661d2007-01-29 23:43:52 +0000216static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000217{
218 printf("%s%s: %s", p, *service, m1);
219 if (errno) {
220 printf(": %s", strerror(errno));
221 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000222 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000223}
224
Denis Vlasenko04c63862006-11-17 18:58:49 +0000225#define WARN "warning: "
226#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000227
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000228static void fail(const char *m1)
229{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000230 ++rc;
231 out("fail: ", m1);
232}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000233static void failx(const char *m1)
234{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000235 errno = 0;
236 fail(m1);
237}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000238static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000239{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000240 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000241 /* "warning: <service>: <m1>\n" */
242 out("warning: ", m1);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000243}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000244static void ok(const char *m1)
245{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000246 errno = 0;
247 out(OK, m1);
248}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000249
250static int svstatus_get(void)
251{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000252 int fd, r;
253
Denys Vlasenko05e86052010-10-13 12:53:27 +0200254 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000255 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000256 if (errno == ENODEV) {
257 *acts == 'x' ? ok("runsv not running")
258 : failx("runsv not running");
259 return 0;
260 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100261 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000262 return -1;
263 }
264 close(fd);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200265 fd = open("supervise/status", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000266 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100267 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000268 return -1;
269 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000270 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000271 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000272 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000273 case 20:
274 break;
275 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100276 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000277 return -1;
278 default:
279 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100280 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000281 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000282 }
283 return 1;
284}
285
Denis Vlasenko322661d2007-01-29 23:43:52 +0000286static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000287{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000288 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000289 int pid;
290 int normallyup = 0;
291 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000292 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000293
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294 if (stat("down", &s) == -1) {
295 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100296 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000297 return 0;
298 }
299 normallyup = 1;
300 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000301 pid = SWAP_LE32(svstatus.pid_le32);
302 timestamp = SWAP_BE64(svstatus.time_be64);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303 if (pid) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000304 switch (svstatus.run_or_finish) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000305 case 1: printf("run: "); break;
306 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000307 }
308 printf("%s: (pid %d) ", m, pid);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000309 } else {
310 printf("down: %s: ", m);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000311 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000312 diff = tnow - timestamp;
313 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000314 if (pid) {
315 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000316 if (svstatus.paused) printf(", paused");
317 if (svstatus.want == 'd') printf(", want down");
318 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000319 } else {
320 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000321 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000322 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000323 return pid ? 1 : 2;
324}
325
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000326static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000327{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000328 int r;
329
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200330 if (svstatus_get() <= 0)
331 return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000332
Denis Vlasenko04c63862006-11-17 18:58:49 +0000333 r = svstatus_print(*service);
334 if (chdir("log") == -1) {
335 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100336 printf("; log: "WARN"can't change to log service directory: %s",
Denis Vlasenko04c63862006-11-17 18:58:49 +0000337 strerror(errno));
338 }
339 } else if (svstatus_get()) {
340 printf("; ");
341 svstatus_print("log");
342 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000343 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000344 return r;
345}
346
347static int checkscript(void)
348{
349 char *prog[2];
350 struct stat s;
351 int pid, w;
352
353 if (stat("check", &s) == -1) {
354 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100355 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000356 return 0;
357 }
358 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000359 prog[0] = (char*)"./check";
360 prog[1] = NULL;
361 pid = spawn(prog);
362 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100363 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000364 return 0;
365 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000366 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100367 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000368 return 0;
369 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200370 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000371}
372
Denis Vlasenko322661d2007-01-29 23:43:52 +0000373static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000374{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000375 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200376 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000377 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000378
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000379 r = svstatus_get();
380 if (r == -1)
381 return -1;
382 if (r == 0) {
383 if (*a == 'x')
384 return 1;
385 return -1;
386 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200387 pid_le32 = svstatus.pid_le32;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000388 switch (*a) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000389 case 'x':
390 return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000391 case 'u':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200392 if (!pid_le32 || svstatus.run_or_finish != 1) return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000393 if (!checkscript()) return 0;
394 break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000395 case 'd':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200396 if (pid_le32) return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000397 break;
398 case 'c':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200399 if (pid_le32 && !checkscript()) return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000400 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000401 case 't':
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200402 if (!pid_le32 && svstatus.want == 'd') break;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000403 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200404 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
Denis Vlasenko04c63862006-11-17 18:58:49 +0000405 return 0;
406 break;
407 case 'o':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000408 timestamp = SWAP_BE64(svstatus.time_be64);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200409 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
Denis Vlasenko04c63862006-11-17 18:58:49 +0000410 return 0;
411 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000412 printf(OK);
413 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000414 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000415 return 1;
416}
417
Denis Vlasenko322661d2007-01-29 23:43:52 +0000418static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000419{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200420 int fd, r, l;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000421
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200422/* Is it an optimization?
423 It causes problems with "sv o SRV; ...; sv d SRV"
424 ('d' is not passed to SRV because its .want == 'd'):
Denis Vlasenko45946f82007-08-20 17:27:40 +0000425 if (svstatus_get() <= 0)
426 return -1;
427 if (svstatus.want == *a)
428 return 0;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200429*/
Denys Vlasenko05e86052010-10-13 12:53:27 +0200430 fd = open("supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000431 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000432 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100433 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000434 else
435 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
436 return -1;
437 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200438 l = strlen(a);
439 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000440 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200441 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100442 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000443 return -1;
444 }
445 return 1;
446}
447
Denys Vlasenko4051a992016-12-05 13:56:40 +0100448//usage:#define sv_trivial_usage
449//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
450//usage:#define sv_full_usage "\n\n"
451//usage: "Control services monitored by runsv supervisor.\n"
452//usage: "Commands (only first character is enough):\n"
453//usage: "\n"
454//usage: "status: query service status\n"
455//usage: "up: if service isn't running, start it. If service stops, restart it\n"
456//usage: "once: like 'up', but if service stops, don't restart it\n"
457//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
458//usage: " if it exists. After it stops, don't restart service\n"
459//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
460//usage: " runsv exits too\n"
461//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
462//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
463static int sv(char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000464{
Denis Vlasenko04c63862006-11-17 18:58:49 +0000465 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000466 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000467 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000468 unsigned waitsec = 7;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000469 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000470 int verbose = 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000471 int (*act)(const char*);
472 int (*cbk)(const char*);
473 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000474
Denis Vlasenkob9256052007-09-28 10:29:17 +0000475 INIT_G();
476
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000477 xfunc_error_retval = 100;
478
479 x = getenv("SVDIR");
480 if (x) varservice = x;
481 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000482 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000483
Denys Vlasenko237bedd2016-07-06 21:58:02 +0200484 opt_complementary = "vv"; /* -w N, -v is a counter */
485 getopt32(argv, "w:+v", &waitsec, &verbose);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000486 argv += optind;
487 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000488 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000489
Denis Vlasenko04158e02009-02-02 10:48:06 +0000490 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000491 tstart = tnow;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200492 curdir = open(".", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000493 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000494 fatal_cannot("open current directory");
495
Denis Vlasenko322661d2007-01-29 23:43:52 +0000496 act = &control;
497 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000498 cbk = &check;
499
Denis Vlasenko04c63862006-11-17 18:58:49 +0000500 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000501 case 'x':
502 case 'e':
503 acts = "x";
504 if (!verbose) cbk = NULL;
505 break;
506 case 'X':
507 case 'E':
508 acts = "x";
509 kll = 1;
510 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000511 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000512 acts = "d";
513 kll = 1;
514 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000515 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000516 acts = "tc";
517 kll = 1;
518 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000519 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000520 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000521 act = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000522 acts = "c";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000523 break;
524 }
525 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
526 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000527 action[1] = '\0';
528 acts = action;
529 if (!verbose) cbk = NULL;
530 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000531 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000532 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000533 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000534 break;
535 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000536 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000537 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000538 break;
539 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000540 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000541 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000542 break;
543 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000544 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000545 act = &status;
546 cbk = NULL;
547 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000548 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000549 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000550 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 break;
552 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000553 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000554 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000555 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000556 acts = "tc";
557 kll = 1;
558 break;
559 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000560 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000561 acts = "tcu";
562 kll = 1;
563 break;
564 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000565 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000566 acts = "x";
567 kll = 1;
568 break;
569 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000570 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000571 acts = "d";
572 kll = 1;
573 break;
574 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000575 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000576 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000577 }
578
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200579 service = argv;
580 while ((x = *service) != NULL) {
581 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000582 if (chdir(varservice) == -1)
583 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000584 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200585 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000586 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100587 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000588 goto nullify_service_0;
589 }
590 if (act && (act(acts) == -1)) {
591 nullify_service_0:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200592 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000593 }
594 if (fchdir(curdir) == -1)
595 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000596 service++;
597 }
598
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000599 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200600 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000601 int diff;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000602
Denis Vlasenko45946f82007-08-20 17:27:40 +0000603 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200604 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000605 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200606 while ((x = *service) != NULL) {
607 if (x == (char*) -1L) /* "dead" */
608 goto next;
609 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000610 if (chdir(varservice) == -1)
611 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000612 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200613 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000614 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100615 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000616 goto nullify_service;
617 }
618 if (cbk(acts) != 0)
619 goto nullify_service;
620 want_exit = 0;
621 if (diff >= waitsec) {
622 printf(kll ? "kill: " : "timeout: ");
623 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200624 svstatus_print(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000625 ++rc;
626 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000627 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000628 if (kll)
629 control("k");
630 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200631 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000632 }
633 if (fchdir(curdir) == -1)
634 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200635 next:
636 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000637 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000638 if (want_exit) break;
639 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000640 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000641 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000642 return rc > 99 ? 99 : rc;
643}
Denys Vlasenko4051a992016-12-05 13:56:40 +0100644
645#if ENABLE_SV
646int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
647int sv_main(int argc UNUSED_PARAM, char **argv)
648{
649 return sv(argv);
650}
651#endif
652
653//usage:#define svc_trivial_usage
654//usage: "[-udopchaitkx] SERVICE_DIR..."
655//usage:#define svc_full_usage "\n\n"
656//usage: "Control services monitored by runsv supervisor"
657//usage: "\n"
658//usage: "\n"" -u If service is not running, start it; restart if it stops"
659//usage: "\n"" -d If service is running, send TERM+CONT signals; do not restart it"
660//usage: "\n"" -o Once: if service is not running, start it; do not restart it"
661//usage: "\n"" -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
662//usage: "\n"" -x Exit: runsv will exit as soon as the service is down"
663#if ENABLE_SVC
664int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
665int svc_main(int argc UNUSED_PARAM, char **argv)
666{
667 char command[2];
668 const char *optstring;
669 unsigned opts;
670
671 INIT_G();
672
673 optstring = "udopchaitkx";
674 opts = getopt32(argv, optstring);
675 argv += optind;
676 if (!argv[0] || !opts)
677 bb_show_usage();
678
679 argv -= 2;
680 if (optind > 2) {
681 argv--;
682 argv[2] = (char*)"--";
683 }
684 argv[0] = (char*)"sv";
685 argv[1] = command;
686 command[1] = '\0';
687
688 /* getopt32() was already called:
689 * reset the libc getopt() function, which keeps internal state.
690 */
691#ifdef __GLIBC__
692 optind = 0;
693#else /* BSD style */
694 optind = 1;
695 /* optreset = 1; */
696#endif
697
698 do {
699 if (opts & 1) {
700 int r;
701 command[0] = *optstring;
702 r = sv(argv);
703 if (r)
704 return 1;
705 }
706 optstring++;
707 opts >>= 1;
708 } while (opts);
709
710 return 0;
711}
712#endif