blob: 8054daedff4901dd7a1ca3d043131e2cc98d9105 [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"
Denys Vlasenko0d79d772018-03-30 20:02:33 +0200166//config: depends on SV || SVC || SVOK
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200167//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.
Denys Vlasenko0d79d772018-03-30 20:02:33 +0200176//config: It is compatible with daemontools command with the same name.
177//config:
178//config:config SVOK
179//config: bool "svok"
180//config: default y
181//config: help
182//config: svok checks whether runsv supervisor is running.
183//config: It is compatible with daemontools command with the same name.
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200184
Denys Vlasenko0d79d772018-03-30 20:02:33 +0200185//applet:IF_SV( APPLET_NOEXEC(sv, sv, BB_DIR_USR_BIN, BB_SUID_DROP, sv ))
186//applet:IF_SVC( APPLET_NOEXEC(svc, svc, BB_DIR_USR_BIN, BB_SUID_DROP, svc ))
187//applet:IF_SVOK(APPLET_NOEXEC(svok, svok, BB_DIR_USR_BIN, BB_SUID_DROP, svok))
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200188
189//kbuild:lib-$(CONFIG_SV) += sv.o
Denys Vlasenko4051a992016-12-05 13:56:40 +0100190//kbuild:lib-$(CONFIG_SVC) += sv.o
Denys Vlasenko0d79d772018-03-30 20:02:33 +0200191//kbuild:lib-$(CONFIG_SVOK) += sv.o
Pere Orga5bc8c002011-04-11 03:29:49 +0200192
Denis Vlasenko04c63862006-11-17 18:58:49 +0000193#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000194#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200195#include "common_bufsiz.h"
Denis Vlasenko04c63862006-11-17 18:58:49 +0000196#include "runit_lib.h"
197
Denis Vlasenkob9256052007-09-28 10:29:17 +0000198struct globals {
199 const char *acts;
200 char **service;
201 unsigned rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000202/* "Bernstein" time format: unix + 0x400000000000000aULL */
Denis Vlasenkob9256052007-09-28 10:29:17 +0000203 uint64_t tstart, tnow;
204 svstatus_t svstatus;
Denys Vlasenkob9be7802017-08-06 21:23:03 +0200205 smallint islog;
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100206} FIX_ALIASING;
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200207#define G (*(struct globals*)bb_common_bufsiz1)
Denis Vlasenkob9256052007-09-28 10:29:17 +0000208#define acts (G.acts )
209#define service (G.service )
210#define rc (G.rc )
211#define tstart (G.tstart )
212#define tnow (G.tnow )
213#define svstatus (G.svstatus )
James Byrne0c632992017-05-15 21:39:51 +0200214#define islog (G.islog )
Denys Vlasenkob9be7802017-08-06 21:23:03 +0200215#define INIT_G() do { \
216 setup_common_bufsiz(); \
Denys Vlasenko73896622017-08-06 21:29:51 +0200217 /* need to zero out, svc calls sv() repeatedly */ \
218 memset(&G, 0, sizeof(G)); \
Denys Vlasenkob9be7802017-08-06 21:23:03 +0200219} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000220
Denis Vlasenko04c63862006-11-17 18:58:49 +0000221
Denys Vlasenko1d3a04a2016-11-28 01:22:57 +0100222#define str_equal(s,t) (strcmp((s), (t)) == 0)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200223
224
Denys Vlasenko2ab994f2018-04-06 18:26:33 +0200225#if ENABLE_SV || ENABLE_SVC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000226static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000227static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000228{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000229 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000230 _exit(151);
231}
232
Denis Vlasenko322661d2007-01-29 23:43:52 +0000233static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000234{
James Byrne0c632992017-05-15 21:39:51 +0200235 printf("%s%s%s: %s", p, *service, islog ? "/log" : "", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000236 if (errno) {
Denys Vlasenko6f97b302017-09-29 18:17:25 +0200237 printf(": "STRERROR_FMT STRERROR_ERRNO);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000238 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000239 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000240}
241
Denis Vlasenko04c63862006-11-17 18:58:49 +0000242#define WARN "warning: "
243#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000244
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000245static void fail(const char *m1)
246{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000247 ++rc;
248 out("fail: ", m1);
249}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000250static void failx(const char *m1)
251{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000252 errno = 0;
253 fail(m1);
254}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000255static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000256{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000257 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000258 /* "warning: <service>: <m1>\n" */
259 out("warning: ", m1);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000260}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000261static void ok(const char *m1)
262{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000263 errno = 0;
264 out(OK, m1);
265}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000266
267static int svstatus_get(void)
268{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000269 int fd, r;
270
Denys Vlasenko05e86052010-10-13 12:53:27 +0200271 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000272 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000273 if (errno == ENODEV) {
274 *acts == 'x' ? ok("runsv not running")
275 : failx("runsv not running");
276 return 0;
277 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100278 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000279 return -1;
280 }
281 close(fd);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200282 fd = open("supervise/status", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000283 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100284 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000285 return -1;
286 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000287 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000288 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000289 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000290 case 20:
291 break;
292 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100293 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000294 return -1;
295 default:
296 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100297 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000298 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000299 }
300 return 1;
301}
302
Denis Vlasenko322661d2007-01-29 23:43:52 +0000303static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000304{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000305 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000306 int pid;
307 int normallyup = 0;
308 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000309 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000310
Denis Vlasenko04c63862006-11-17 18:58:49 +0000311 if (stat("down", &s) == -1) {
312 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100313 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314 return 0;
315 }
316 normallyup = 1;
317 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000318 pid = SWAP_LE32(svstatus.pid_le32);
319 timestamp = SWAP_BE64(svstatus.time_be64);
James Byrne0c632992017-05-15 21:39:51 +0200320 switch (svstatus.run_or_finish) {
321 case 0: printf("down: "); break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000322 case 1: printf("run: "); break;
323 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000324 }
James Byrne0c632992017-05-15 21:39:51 +0200325 printf("%s: ", m);
326 if (svstatus.run_or_finish)
327 printf("(pid %d) ", pid);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000328 diff = tnow - timestamp;
329 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000330 if (pid) {
331 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000332 if (svstatus.paused) printf(", paused");
333 if (svstatus.want == 'd') printf(", want down");
334 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000335 } else {
336 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000337 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000338 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000339 return pid ? 1 : 2;
340}
341
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000342static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000343{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000344 int r;
345
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200346 if (svstatus_get() <= 0)
347 return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000348
Denis Vlasenko04c63862006-11-17 18:58:49 +0000349 r = svstatus_print(*service);
James Byrne0c632992017-05-15 21:39:51 +0200350 islog = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000351 if (chdir("log") == -1) {
352 if (errno != ENOENT) {
James Byrne0c632992017-05-15 21:39:51 +0200353 printf("; ");
354 warn("can't change directory");
355 } else
356 bb_putchar('\n');
357 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000358 printf("; ");
James Byrne0c632992017-05-15 21:39:51 +0200359 if (svstatus_get()) {
360 r = svstatus_print("log");
361 bb_putchar('\n');
362 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000363 }
James Byrne0c632992017-05-15 21:39:51 +0200364 islog = 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000365 return r;
366}
367
368static int checkscript(void)
369{
370 char *prog[2];
371 struct stat s;
372 int pid, w;
373
374 if (stat("check", &s) == -1) {
375 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100376 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000377 return 0;
378 }
379 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000380 prog[0] = (char*)"./check";
381 prog[1] = NULL;
382 pid = spawn(prog);
383 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100384 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000385 return 0;
386 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000387 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100388 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000389 return 0;
390 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200391 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000392}
393
Denis Vlasenko322661d2007-01-29 23:43:52 +0000394static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000395{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000396 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200397 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000398 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000399
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000400 r = svstatus_get();
401 if (r == -1)
402 return -1;
James Byrne0c632992017-05-15 21:39:51 +0200403 while (*a) {
404 if (r == 0) {
405 if (*a == 'x')
406 return 1;
407 return -1;
408 }
409 pid_le32 = svstatus.pid_le32;
410 switch (*a) {
411 case 'x':
Denis Vlasenko04c63862006-11-17 18:58:49 +0000412 return 0;
James Byrne0c632992017-05-15 21:39:51 +0200413 case 'u':
414 if (!pid_le32 || svstatus.run_or_finish != 1)
415 return 0;
416 if (!checkscript())
417 return 0;
418 break;
419 case 'd':
420 if (pid_le32 || svstatus.run_or_finish != 0)
421 return 0;
422 break;
423 case 'C':
424 if (pid_le32 && !checkscript())
425 return 0;
426 break;
427 case 't':
428 case 'k':
429 if (!pid_le32 && svstatus.want == 'd')
430 break;
431 timestamp = SWAP_BE64(svstatus.time_be64);
432 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
433 return 0;
434 break;
435 case 'o':
436 timestamp = SWAP_BE64(svstatus.time_be64);
437 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
438 return 0;
439 break;
440 case 'p':
441 if (pid_le32 && !svstatus.paused)
442 return 0;
443 break;
444 case 'c':
445 if (pid_le32 && svstatus.paused)
446 return 0;
447 break;
448 }
449 ++a;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000450 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000451 printf(OK);
452 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000453 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000454 return 1;
455}
456
Denis Vlasenko322661d2007-01-29 23:43:52 +0000457static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000458{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200459 int fd, r, l;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000460
Denis Vlasenko45946f82007-08-20 17:27:40 +0000461 if (svstatus_get() <= 0)
462 return -1;
James Byrne0c632992017-05-15 21:39:51 +0200463 if (svstatus.want == *a && (*a != 'd' || svstatus.got_term == 1))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000464 return 0;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200465 fd = open("supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000466 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000467 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100468 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000469 else
470 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
471 return -1;
472 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200473 l = strlen(a);
474 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000475 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200476 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100477 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000478 return -1;
479 }
480 return 1;
481}
482
Denys Vlasenko4051a992016-12-05 13:56:40 +0100483//usage:#define sv_trivial_usage
484//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
485//usage:#define sv_full_usage "\n\n"
486//usage: "Control services monitored by runsv supervisor.\n"
487//usage: "Commands (only first character is enough):\n"
488//usage: "\n"
489//usage: "status: query service status\n"
490//usage: "up: if service isn't running, start it. If service stops, restart it\n"
491//usage: "once: like 'up', but if service stops, don't restart it\n"
492//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
493//usage: " if it exists. After it stops, don't restart service\n"
494//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
495//usage: " runsv exits too\n"
496//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
497//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
498static int sv(char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000499{
Denis Vlasenko04c63862006-11-17 18:58:49 +0000500 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000501 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000502 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000503 unsigned waitsec = 7;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000504 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000505 int verbose = 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000506 int (*act)(const char*);
507 int (*cbk)(const char*);
508 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000509
Denis Vlasenkob9256052007-09-28 10:29:17 +0000510 INIT_G();
511
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000512 xfunc_error_retval = 100;
513
514 x = getenv("SVDIR");
515 if (x) varservice = x;
516 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000517 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000518
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200519 getopt32(argv, "^" "w:+v" "\0" "vv" /* -w N, -v is a counter */,
520 &waitsec, &verbose
521 );
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000522 argv += optind;
523 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000524 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000525
Denis Vlasenko04158e02009-02-02 10:48:06 +0000526 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000527 tstart = tnow;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200528 curdir = open(".", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000529 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000530 fatal_cannot("open current directory");
531
Denis Vlasenko322661d2007-01-29 23:43:52 +0000532 act = &control;
533 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000534 cbk = &check;
535
Denis Vlasenko04c63862006-11-17 18:58:49 +0000536 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000537 case 'x':
538 case 'e':
539 acts = "x";
540 if (!verbose) cbk = NULL;
541 break;
542 case 'X':
543 case 'E':
544 acts = "x";
545 kll = 1;
546 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000547 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000548 acts = "d";
549 kll = 1;
550 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000551 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000552 acts = "tc";
553 kll = 1;
554 break;
James Byrne0c632992017-05-15 21:39:51 +0200555 case 't':
556 if (str_equal(action, "try-restart")) {
557 acts = "tc";
558 break;
559 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000560 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000561 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000562 act = NULL;
James Byrne0c632992017-05-15 21:39:51 +0200563 acts = "C";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000564 break;
565 }
James Byrne0c632992017-05-15 21:39:51 +0200566 case 'u': case 'd': case 'o': case 'p': case 'h':
Denis Vlasenko04c63862006-11-17 18:58:49 +0000567 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000568 action[1] = '\0';
569 acts = action;
James Byrne0c632992017-05-15 21:39:51 +0200570 if (!verbose)
571 cbk = NULL;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000572 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000573 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000574 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000575 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000576 break;
577 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000578 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000579 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000580 break;
581 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000582 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000583 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000584 break;
585 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000586 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000587 act = &status;
588 cbk = NULL;
589 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000590 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000591 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000592 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000593 break;
594 }
James Byrne0c632992017-05-15 21:39:51 +0200595 if (str_equal(action, "reload")) {
596 acts = "h";
597 break;
598 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000599 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000600 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000601 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000602 acts = "tc";
603 kll = 1;
604 break;
605 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000606 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000607 acts = "tcu";
608 kll = 1;
609 break;
610 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000611 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000612 acts = "x";
613 kll = 1;
614 break;
615 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000616 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000617 acts = "d";
618 kll = 1;
619 break;
620 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000621 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000622 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000623 }
624
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200625 service = argv;
626 while ((x = *service) != NULL) {
James Byrne0c632992017-05-15 21:39:51 +0200627 if (x[0] != '/' && x[0] != '.'
Denys Vlasenko0d79d772018-03-30 20:02:33 +0200628 && !last_char_is(x, '/')
James Byrne0c632992017-05-15 21:39:51 +0200629 ) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000630 if (chdir(varservice) == -1)
631 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000632 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200633 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000634 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100635 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000636 goto nullify_service_0;
637 }
638 if (act && (act(acts) == -1)) {
639 nullify_service_0:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200640 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000641 }
642 if (fchdir(curdir) == -1)
643 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000644 service++;
645 }
646
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000647 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200648 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000649 int diff;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000650
Denis Vlasenko45946f82007-08-20 17:27:40 +0000651 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200652 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000653 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200654 while ((x = *service) != NULL) {
655 if (x == (char*) -1L) /* "dead" */
656 goto next;
657 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000658 if (chdir(varservice) == -1)
659 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000660 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200661 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000662 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100663 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000664 goto nullify_service;
665 }
666 if (cbk(acts) != 0)
667 goto nullify_service;
668 want_exit = 0;
669 if (diff >= waitsec) {
670 printf(kll ? "kill: " : "timeout: ");
671 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200672 svstatus_print(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000673 ++rc;
674 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000675 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000676 if (kll)
677 control("k");
678 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200679 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000680 }
681 if (fchdir(curdir) == -1)
682 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200683 next:
684 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000685 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000686 if (want_exit) break;
687 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000688 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000689 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000690 return rc > 99 ? 99 : rc;
691}
Denys Vlasenko2ab994f2018-04-06 18:26:33 +0200692#endif
Denys Vlasenko4051a992016-12-05 13:56:40 +0100693
694#if ENABLE_SV
695int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
696int sv_main(int argc UNUSED_PARAM, char **argv)
697{
698 return sv(argv);
699}
700#endif
701
702//usage:#define svc_trivial_usage
703//usage: "[-udopchaitkx] SERVICE_DIR..."
704//usage:#define svc_full_usage "\n\n"
705//usage: "Control services monitored by runsv supervisor"
706//usage: "\n"
707//usage: "\n"" -u If service is not running, start it; restart if it stops"
708//usage: "\n"" -d If service is running, send TERM+CONT signals; do not restart it"
709//usage: "\n"" -o Once: if service is not running, start it; do not restart it"
710//usage: "\n"" -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
711//usage: "\n"" -x Exit: runsv will exit as soon as the service is down"
712#if ENABLE_SVC
713int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
714int svc_main(int argc UNUSED_PARAM, char **argv)
715{
716 char command[2];
717 const char *optstring;
718 unsigned opts;
719
Denys Vlasenko4051a992016-12-05 13:56:40 +0100720 optstring = "udopchaitkx";
721 opts = getopt32(argv, optstring);
722 argv += optind;
723 if (!argv[0] || !opts)
724 bb_show_usage();
725
726 argv -= 2;
727 if (optind > 2) {
728 argv--;
729 argv[2] = (char*)"--";
730 }
731 argv[0] = (char*)"sv";
732 argv[1] = command;
733 command[1] = '\0';
734
Denys Vlasenko4051a992016-12-05 13:56:40 +0100735 do {
736 if (opts & 1) {
737 int r;
Denys Vlasenko754e9f92017-08-05 01:38:55 +0200738
Denys Vlasenko4051a992016-12-05 13:56:40 +0100739 command[0] = *optstring;
Denys Vlasenko754e9f92017-08-05 01:38:55 +0200740
741 /* getopt() was already called by getopt32():
742 * reset the libc getopt() function's internal state.
743 */
744 GETOPT_RESET();
Denys Vlasenko4051a992016-12-05 13:56:40 +0100745 r = sv(argv);
746 if (r)
747 return 1;
748 }
749 optstring++;
750 opts >>= 1;
751 } while (opts);
752
753 return 0;
754}
755#endif
Denys Vlasenko0d79d772018-03-30 20:02:33 +0200756
757//usage:#define svok_trivial_usage
758//usage: "SERVICE_DIR"
759//usage:#define svok_full_usage "\n\n"
760//usage: "Check whether runsv supervisor is running.\n"
761//usage: "Exit code is 0 if it does, 100 if it does not,\n"
762//usage: "111 (with error message) if SERVICE_DIR does not exist."
763#if ENABLE_SVOK
764int svok_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
765int svok_main(int argc UNUSED_PARAM, char **argv)
766{
767 const char *dir = argv[1];
768
769 if (!dir)
770 bb_show_usage();
771
772 xfunc_error_retval = 111;
773
774 /*
775 * daemontools has no concept of "default service dir", runit does.
776 * Let's act as runit.
777 */
778 if (dir[0] != '/' && dir[0] != '.'
779 && !last_char_is(dir, '/')
780 ) {
781 xchdir(CONFIG_SV_DEFAULT_SERVICE_DIR);
782 }
783
784 xchdir(dir);
785 if (open("supervise/ok", O_WRONLY) < 0) {
786 if (errno == ENOENT || errno == ENXIO)
787 return 100;
788 bb_perror_msg_and_die("can't open '%s'", "supervise/ok");
789 }
790
791 return 0;
792}
793#endif