blob: 630f1f37e2debf5654d15d032388438ab0e1b560 [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
Denys Vlasenko95f79532017-08-02 14:26:33 +020016THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
Denis Vlasenko8a164052007-03-12 23:34:52 +000017WARRANTIES, 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
James Byrne0c632992017-05-15 21:39:51 +020028/* Taken from http://smarden.org/runit/sv.8.html:
Denis Vlasenko8a164052007-03-12 23:34:52 +000029
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
James Byrne0c632992017-05-15 21:39:51 +020039service used by runsv(8). If service doesn't start with a dot or slash and
40doesn't end with a slash, it is searched in the default services directory
41/var/service/, otherwise relative to the current directory.
Denis Vlasenko8a164052007-03-12 23:34:52 +000042
43command is one of up, down, status, once, pause, cont, hup, alarm, interrupt,
James Byrne0c632992017-05-15 21:39:51 +0200441, 2, term, kill, or exit, or start, stop, reload, restart, shutdown,
45force-stop, force-reload, force-restart, force-shutdown, try-restart.
Denis Vlasenko8a164052007-03-12 23:34:52 +000046
47status
48 Report the current status of the service, and the appendant log service
49 if available, to standard output.
50up
51 If the service is not running, start it. If the service stops, restart it.
52down
53 If the service is running, send it the TERM signal, and the CONT signal.
54 If ./run exits, start ./finish if it exists. After it stops, do not
55 restart service.
56once
57 If the service is not running, start it. Do not restart it if it stops.
58pause cont hup alarm interrupt quit 1 2 term kill
59 If the service is running, send it the STOP, CONT, HUP, ALRM, INT, QUIT,
60 USR1, USR2, TERM, or KILL signal respectively.
61exit
62 If the service is running, send it the TERM signal, and the CONT signal.
63 Do not restart the service. If the service is down, and no log service
64 exists, runsv(8) exits. If the service is down and a log service exists,
James Byrne0c632992017-05-15 21:39:51 +020065 runsv(8) closes the standard input of the log service and waits for it to
66 terminate. If the log service is down, runsv(8) exits. This command is
67 ignored if it is given to an appendant log service.
Denis Vlasenko8a164052007-03-12 23:34:52 +000068
69sv actually looks only at the first character of above commands.
70
Denis Vlasenkof223efb2007-08-03 10:58:12 +000071Commands compatible to LSB init script actions:
72
Denis Vlasenko8a164052007-03-12 23:34:52 +000073status
74 Same as status.
75start
76 Same as up, but wait up to 7 seconds for the command to take effect.
77 Then report the status or timeout. If the script ./check exists in
78 the service directory, sv runs this script to check whether the service
79 is up and available; it's considered to be available if ./check exits
80 with 0.
81stop
82 Same as down, but wait up to 7 seconds for the service to become down.
83 Then report the status or timeout.
James Byrne0c632992017-05-15 21:39:51 +020084reload
85 Same as hup, and additionally report the status afterwards.
Denis Vlasenko8a164052007-03-12 23:34:52 +000086restart
87 Send the commands term, cont, and up to the service, and wait up to
88 7 seconds for the service to restart. Then report the status or timeout.
89 If the script ./check exists in the service directory, sv runs this script
90 to check whether the service is up and available again; it's considered
91 to be available if ./check exits with 0.
92shutdown
93 Same as exit, but wait up to 7 seconds for the runsv(8) process
94 to terminate. Then report the status or timeout.
95force-stop
96 Same as down, but wait up to 7 seconds for the service to become down.
97 Then report the status, and on timeout send the service the kill command.
98force-reload
99 Send the service the term and cont commands, and wait up to
100 7 seconds for the service to restart. Then report the status,
101 and on timeout send the service the kill command.
102force-restart
103 Send the service the term, cont and up commands, and wait up to
104 7 seconds for the service to restart. Then report the status, and
105 on timeout send the service the kill command. If the script ./check
106 exists in the service directory, sv runs this script to check whether
Denis Vlasenkof223efb2007-08-03 10:58:12 +0000107 the service is up and available again; it's considered to be available
Denis Vlasenko8a164052007-03-12 23:34:52 +0000108 if ./check exits with 0.
109force-shutdown
110 Same as exit, but wait up to 7 seconds for the runsv(8) process to
111 terminate. Then report the status, and on timeout send the service
112 the kill command.
James Byrne0c632992017-05-15 21:39:51 +0200113try-restart
114 if the service is running, send it the term and cont commands, and wait up to
115 7 seconds for the service to restart. Then report the status or timeout.
Denis Vlasenko8a164052007-03-12 23:34:52 +0000116
117Additional Commands
118
119check
120 Check for the service to be in the state that's been requested. Wait up to
121 7 seconds for the service to reach the requested state, then report
122 the status or timeout. If the requested state of the service is up,
123 and the script ./check exists in the service directory, sv runs
124 this script to check whether the service is up and running;
125 it's considered to be up if ./check exits with 0.
126
127Options
128
129-v
James Byrne0c632992017-05-15 21:39:51 +0200130 If the command is up, down, term, once, cont, or exit, then wait up to 7
131 seconds for the command to take effect. Then report the status or timeout.
Denis Vlasenko8a164052007-03-12 23:34:52 +0000132-w sec
133 Override the default timeout of 7 seconds with sec seconds. Implies -v.
134
135Environment
136
137SVDIR
138 The environment variable $SVDIR overrides the default services directory
139 /var/service.
140SVWAIT
141 The environment variable $SVWAIT overrides the default 7 seconds to wait
142 for a command to take effect. It is overridden by the -w option.
143
144Exit Codes
145 sv exits 0, if the command was successfully sent to all services, and,
146 if it was told to wait, the command has taken effect to all services.
147
148 For each service that caused an error (e.g. the directory is not
149 controlled by a runsv(8) process, or sv timed out while waiting),
150 sv increases the exit code by one and exits non zero. The maximum
151 is 99. sv exits 100 on error.
152*/
153
Denis Vlasenkod18f52b2008-03-02 12:53:15 +0000154/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200155
156//config:config SV
Denys Vlasenko4eed2c62017-07-18 22:01:24 +0200157//config: bool "sv (7.8 kb)"
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200158//config: default y
159//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200160//config: sv reports the current status and controls the state of services
161//config: monitored by the runsv supervisor.
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200162//config:
163//config:config SV_DEFAULT_SERVICE_DIR
164//config: string "Default directory for services"
165//config: default "/var/service"
166//config: depends on SV
167//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200168//config: Default directory for services.
169//config: Defaults to "/var/service"
Denys Vlasenko4051a992016-12-05 13:56:40 +0100170//config:
171//config:config SVC
Denys Vlasenko4eed2c62017-07-18 22:01:24 +0200172//config: bool "svc (7.8 kb)"
Denys Vlasenko4051a992016-12-05 13:56:40 +0100173//config: default y
174//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200175//config: svc controls the state of services monitored by the runsv supervisor.
176//config: It is comaptible with daemontools command with the same name.
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200177
Denys Vlasenkoa453ca52017-08-05 01:42:08 +0200178//applet:IF_SV( APPLET_NOEXEC(sv, sv, BB_DIR_USR_BIN, BB_SUID_DROP, sv ))
179//applet:IF_SVC(APPLET_NOEXEC(svc, svc, BB_DIR_USR_BIN, BB_SUID_DROP, svc))
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200180
181//kbuild:lib-$(CONFIG_SV) += sv.o
Denys Vlasenko4051a992016-12-05 13:56:40 +0100182//kbuild:lib-$(CONFIG_SVC) += sv.o
Pere Orga5bc8c002011-04-11 03:29:49 +0200183
Denis Vlasenko04c63862006-11-17 18:58:49 +0000184#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000185#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200186#include "common_bufsiz.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +0000187#include "runit_lib.h"
188
Denis Vlasenkob9256052007-09-28 10:29:17 +0000189struct globals {
190 const char *acts;
191 char **service;
192 unsigned rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000193/* "Bernstein" time format: unix + 0x400000000000000aULL */
Denis Vlasenkob9256052007-09-28 10:29:17 +0000194 uint64_t tstart, tnow;
195 svstatus_t svstatus;
Denys Vlasenkob9be7802017-08-06 21:23:03 +0200196 smallint islog;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100197} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200198#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkob9256052007-09-28 10:29:17 +0000199#define acts (G.acts )
200#define service (G.service )
201#define rc (G.rc )
202#define tstart (G.tstart )
203#define tnow (G.tnow )
204#define svstatus (G.svstatus )
James Byrne0c632992017-05-15 21:39:51 +0200205#define islog (G.islog )
Denys Vlasenkob9be7802017-08-06 21:23:03 +0200206#define INIT_G() do { \
207 setup_common_bufsiz(); \
208 /* need to zero out, we are NOEXEC */ \
209 rc = EXIT_SUCCESS; \
210 islog = 0; \
211 /* other fields need not be zero */ \
212} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000213
Denis Vlasenko04c63862006-11-17 18:58:49 +0000214
Denys Vlasenko1d3a04a2016-11-28 01:22:57 +0100215#define str_equal(s,t) (strcmp((s), (t)) == 0)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200216
217
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000218static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000219static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000220{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000221 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000222 _exit(151);
223}
224
Denis Vlasenko322661d2007-01-29 23:43:52 +0000225static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000226{
James Byrne0c632992017-05-15 21:39:51 +0200227 printf("%s%s%s: %s", p, *service, islog ? "/log" : "", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000228 if (errno) {
229 printf(": %s", strerror(errno));
230 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000231 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000232}
233
Denis Vlasenko04c63862006-11-17 18:58:49 +0000234#define WARN "warning: "
235#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000236
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000237static void fail(const char *m1)
238{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000239 ++rc;
240 out("fail: ", m1);
241}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000242static void failx(const char *m1)
243{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000244 errno = 0;
245 fail(m1);
246}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000247static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000248{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000249 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000250 /* "warning: <service>: <m1>\n" */
251 out("warning: ", m1);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000252}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000253static void ok(const char *m1)
254{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000255 errno = 0;
256 out(OK, m1);
257}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000258
259static int svstatus_get(void)
260{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000261 int fd, r;
262
Denys Vlasenko05e86052010-10-13 12:53:27 +0200263 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000264 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000265 if (errno == ENODEV) {
266 *acts == 'x' ? ok("runsv not running")
267 : failx("runsv not running");
268 return 0;
269 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100270 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000271 return -1;
272 }
273 close(fd);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200274 fd = open("supervise/status", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000275 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100276 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000277 return -1;
278 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000279 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000280 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000281 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000282 case 20:
283 break;
284 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100285 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000286 return -1;
287 default:
288 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100289 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000290 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000291 }
292 return 1;
293}
294
Denis Vlasenko322661d2007-01-29 23:43:52 +0000295static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000296{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000297 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000298 int pid;
299 int normallyup = 0;
300 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000301 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000302
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303 if (stat("down", &s) == -1) {
304 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100305 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000306 return 0;
307 }
308 normallyup = 1;
309 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000310 pid = SWAP_LE32(svstatus.pid_le32);
311 timestamp = SWAP_BE64(svstatus.time_be64);
James Byrne0c632992017-05-15 21:39:51 +0200312 switch (svstatus.run_or_finish) {
313 case 0: printf("down: "); break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000314 case 1: printf("run: "); break;
315 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000316 }
James Byrne0c632992017-05-15 21:39:51 +0200317 printf("%s: ", m);
318 if (svstatus.run_or_finish)
319 printf("(pid %d) ", pid);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000320 diff = tnow - timestamp;
321 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000322 if (pid) {
323 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000324 if (svstatus.paused) printf(", paused");
325 if (svstatus.want == 'd') printf(", want down");
326 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000327 } else {
328 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000329 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000330 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000331 return pid ? 1 : 2;
332}
333
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000334static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000335{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000336 int r;
337
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200338 if (svstatus_get() <= 0)
339 return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000340
Denis Vlasenko04c63862006-11-17 18:58:49 +0000341 r = svstatus_print(*service);
James Byrne0c632992017-05-15 21:39:51 +0200342 islog = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000343 if (chdir("log") == -1) {
344 if (errno != ENOENT) {
James Byrne0c632992017-05-15 21:39:51 +0200345 printf("; ");
346 warn("can't change directory");
347 } else
348 bb_putchar('\n');
349 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000350 printf("; ");
James Byrne0c632992017-05-15 21:39:51 +0200351 if (svstatus_get()) {
352 r = svstatus_print("log");
353 bb_putchar('\n');
354 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000355 }
James Byrne0c632992017-05-15 21:39:51 +0200356 islog = 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000357 return r;
358}
359
360static int checkscript(void)
361{
362 char *prog[2];
363 struct stat s;
364 int pid, w;
365
366 if (stat("check", &s) == -1) {
367 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100368 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000369 return 0;
370 }
371 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000372 prog[0] = (char*)"./check";
373 prog[1] = NULL;
374 pid = spawn(prog);
375 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100376 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000377 return 0;
378 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000379 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100380 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000381 return 0;
382 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200383 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000384}
385
Denis Vlasenko322661d2007-01-29 23:43:52 +0000386static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000387{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000388 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200389 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000390 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000391
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000392 r = svstatus_get();
393 if (r == -1)
394 return -1;
James Byrne0c632992017-05-15 21:39:51 +0200395 while (*a) {
396 if (r == 0) {
397 if (*a == 'x')
398 return 1;
399 return -1;
400 }
401 pid_le32 = svstatus.pid_le32;
402 switch (*a) {
403 case 'x':
Denis Vlasenko04c63862006-11-17 18:58:49 +0000404 return 0;
James Byrne0c632992017-05-15 21:39:51 +0200405 case 'u':
406 if (!pid_le32 || svstatus.run_or_finish != 1)
407 return 0;
408 if (!checkscript())
409 return 0;
410 break;
411 case 'd':
412 if (pid_le32 || svstatus.run_or_finish != 0)
413 return 0;
414 break;
415 case 'C':
416 if (pid_le32 && !checkscript())
417 return 0;
418 break;
419 case 't':
420 case 'k':
421 if (!pid_le32 && svstatus.want == 'd')
422 break;
423 timestamp = SWAP_BE64(svstatus.time_be64);
424 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
425 return 0;
426 break;
427 case 'o':
428 timestamp = SWAP_BE64(svstatus.time_be64);
429 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
430 return 0;
431 break;
432 case 'p':
433 if (pid_le32 && !svstatus.paused)
434 return 0;
435 break;
436 case 'c':
437 if (pid_le32 && svstatus.paused)
438 return 0;
439 break;
440 }
441 ++a;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000442 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000443 printf(OK);
444 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000445 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000446 return 1;
447}
448
Denis Vlasenko322661d2007-01-29 23:43:52 +0000449static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000450{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200451 int fd, r, l;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000452
Denis Vlasenko45946f82007-08-20 17:27:40 +0000453 if (svstatus_get() <= 0)
454 return -1;
James Byrne0c632992017-05-15 21:39:51 +0200455 if (svstatus.want == *a && (*a != 'd' || svstatus.got_term == 1))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000456 return 0;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200457 fd = open("supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000458 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000459 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100460 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000461 else
462 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
463 return -1;
464 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200465 l = strlen(a);
466 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000467 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200468 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100469 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000470 return -1;
471 }
472 return 1;
473}
474
Denys Vlasenko4051a992016-12-05 13:56:40 +0100475//usage:#define sv_trivial_usage
476//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
477//usage:#define sv_full_usage "\n\n"
478//usage: "Control services monitored by runsv supervisor.\n"
479//usage: "Commands (only first character is enough):\n"
480//usage: "\n"
481//usage: "status: query service status\n"
482//usage: "up: if service isn't running, start it. If service stops, restart it\n"
483//usage: "once: like 'up', but if service stops, don't restart it\n"
484//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
485//usage: " if it exists. After it stops, don't restart service\n"
486//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
487//usage: " runsv exits too\n"
488//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
489//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
490static int sv(char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000491{
Denis Vlasenko04c63862006-11-17 18:58:49 +0000492 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000493 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000494 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000495 unsigned waitsec = 7;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000496 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000497 int verbose = 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000498 int (*act)(const char*);
499 int (*cbk)(const char*);
500 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000501
Denis Vlasenkob9256052007-09-28 10:29:17 +0000502 INIT_G();
503
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000504 xfunc_error_retval = 100;
505
506 x = getenv("SVDIR");
507 if (x) varservice = x;
508 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000509 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000510
Denys Vlasenko237bedd2016-07-06 21:58:02 +0200511 opt_complementary = "vv"; /* -w N, -v is a counter */
512 getopt32(argv, "w:+v", &waitsec, &verbose);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000513 argv += optind;
514 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000515 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000516
Denis Vlasenko04158e02009-02-02 10:48:06 +0000517 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000518 tstart = tnow;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200519 curdir = open(".", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000520 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000521 fatal_cannot("open current directory");
522
Denis Vlasenko322661d2007-01-29 23:43:52 +0000523 act = &control;
524 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000525 cbk = &check;
526
Denis Vlasenko04c63862006-11-17 18:58:49 +0000527 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000528 case 'x':
529 case 'e':
530 acts = "x";
531 if (!verbose) cbk = NULL;
532 break;
533 case 'X':
534 case 'E':
535 acts = "x";
536 kll = 1;
537 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000538 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000539 acts = "d";
540 kll = 1;
541 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000542 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000543 acts = "tc";
544 kll = 1;
545 break;
James Byrne0c632992017-05-15 21:39:51 +0200546 case 't':
547 if (str_equal(action, "try-restart")) {
548 acts = "tc";
549 break;
550 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000552 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000553 act = NULL;
James Byrne0c632992017-05-15 21:39:51 +0200554 acts = "C";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000555 break;
556 }
James Byrne0c632992017-05-15 21:39:51 +0200557 case 'u': case 'd': case 'o': case 'p': case 'h':
Denis Vlasenko04c63862006-11-17 18:58:49 +0000558 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000559 action[1] = '\0';
560 acts = action;
James Byrne0c632992017-05-15 21:39:51 +0200561 if (!verbose)
562 cbk = NULL;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000563 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000564 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000565 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000567 break;
568 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000569 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000570 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000571 break;
572 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000573 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000574 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000575 break;
576 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000577 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000578 act = &status;
579 cbk = NULL;
580 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000581 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000582 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000583 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000584 break;
585 }
James Byrne0c632992017-05-15 21:39:51 +0200586 if (str_equal(action, "reload")) {
587 acts = "h";
588 break;
589 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000590 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000591 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000592 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000593 acts = "tc";
594 kll = 1;
595 break;
596 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000597 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000598 acts = "tcu";
599 kll = 1;
600 break;
601 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000602 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000603 acts = "x";
604 kll = 1;
605 break;
606 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000607 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000608 acts = "d";
609 kll = 1;
610 break;
611 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000612 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000613 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000614 }
615
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200616 service = argv;
617 while ((x = *service) != NULL) {
James Byrne0c632992017-05-15 21:39:51 +0200618 if (x[0] != '/' && x[0] != '.'
619 && x[0] != '\0' && x[strlen(x) - 1] != '/'
620 ) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000621 if (chdir(varservice) == -1)
622 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000623 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200624 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000625 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100626 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000627 goto nullify_service_0;
628 }
629 if (act && (act(acts) == -1)) {
630 nullify_service_0:
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");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000635 service++;
636 }
637
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000638 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200639 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000640 int diff;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000641
Denis Vlasenko45946f82007-08-20 17:27:40 +0000642 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200643 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000644 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200645 while ((x = *service) != NULL) {
646 if (x == (char*) -1L) /* "dead" */
647 goto next;
648 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000649 if (chdir(varservice) == -1)
650 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000651 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200652 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000653 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100654 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000655 goto nullify_service;
656 }
657 if (cbk(acts) != 0)
658 goto nullify_service;
659 want_exit = 0;
660 if (diff >= waitsec) {
661 printf(kll ? "kill: " : "timeout: ");
662 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200663 svstatus_print(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000664 ++rc;
665 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000666 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000667 if (kll)
668 control("k");
669 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200670 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000671 }
672 if (fchdir(curdir) == -1)
673 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200674 next:
675 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000676 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000677 if (want_exit) break;
678 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000679 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000680 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000681 return rc > 99 ? 99 : rc;
682}
Denys Vlasenko4051a992016-12-05 13:56:40 +0100683
684#if ENABLE_SV
685int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
686int sv_main(int argc UNUSED_PARAM, char **argv)
687{
688 return sv(argv);
689}
690#endif
691
692//usage:#define svc_trivial_usage
693//usage: "[-udopchaitkx] SERVICE_DIR..."
694//usage:#define svc_full_usage "\n\n"
695//usage: "Control services monitored by runsv supervisor"
696//usage: "\n"
697//usage: "\n"" -u If service is not running, start it; restart if it stops"
698//usage: "\n"" -d If service is running, send TERM+CONT signals; do not restart it"
699//usage: "\n"" -o Once: if service is not running, start it; do not restart it"
700//usage: "\n"" -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
701//usage: "\n"" -x Exit: runsv will exit as soon as the service is down"
702#if ENABLE_SVC
703int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
704int svc_main(int argc UNUSED_PARAM, char **argv)
705{
706 char command[2];
707 const char *optstring;
708 unsigned opts;
709
710 INIT_G();
711
712 optstring = "udopchaitkx";
713 opts = getopt32(argv, optstring);
714 argv += optind;
715 if (!argv[0] || !opts)
716 bb_show_usage();
717
718 argv -= 2;
719 if (optind > 2) {
720 argv--;
721 argv[2] = (char*)"--";
722 }
723 argv[0] = (char*)"sv";
724 argv[1] = command;
725 command[1] = '\0';
726
Denys Vlasenko4051a992016-12-05 13:56:40 +0100727 do {
728 if (opts & 1) {
729 int r;
Denys Vlasenko754e9f92017-08-05 01:38:55 +0200730
Denys Vlasenko4051a992016-12-05 13:56:40 +0100731 command[0] = *optstring;
Denys Vlasenko754e9f92017-08-05 01:38:55 +0200732
733 /* getopt() was already called by getopt32():
734 * reset the libc getopt() function's internal state.
735 */
736 GETOPT_RESET();
Denys Vlasenko4051a992016-12-05 13:56:40 +0100737 r = sv(argv);
738 if (r)
739 return 1;
740 }
741 optstring++;
742 opts >>= 1;
743 } while (opts);
744
745 return 0;
746}
747#endif