blob: 86d1818724441ebb08e6b8110ca1f1ad50269b92 [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
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000225static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000226static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000227{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000228 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000229 _exit(151);
230}
231
Denis Vlasenko322661d2007-01-29 23:43:52 +0000232static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000233{
James Byrne0c632992017-05-15 21:39:51 +0200234 printf("%s%s%s: %s", p, *service, islog ? "/log" : "", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000235 if (errno) {
Denys Vlasenko6f97b302017-09-29 18:17:25 +0200236 printf(": "STRERROR_FMT STRERROR_ERRNO);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000237 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000238 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000239}
240
Denis Vlasenko04c63862006-11-17 18:58:49 +0000241#define WARN "warning: "
242#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000243
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000244static void fail(const char *m1)
245{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000246 ++rc;
247 out("fail: ", m1);
248}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000249static void failx(const char *m1)
250{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000251 errno = 0;
252 fail(m1);
253}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000254static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000255{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000256 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000257 /* "warning: <service>: <m1>\n" */
258 out("warning: ", m1);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000259}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000260static void ok(const char *m1)
261{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000262 errno = 0;
263 out(OK, m1);
264}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000265
266static int svstatus_get(void)
267{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000268 int fd, r;
269
Denys Vlasenko05e86052010-10-13 12:53:27 +0200270 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000271 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000272 if (errno == ENODEV) {
273 *acts == 'x' ? ok("runsv not running")
274 : failx("runsv not running");
275 return 0;
276 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100277 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000278 return -1;
279 }
280 close(fd);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200281 fd = open("supervise/status", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000282 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100283 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000284 return -1;
285 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000286 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000287 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000288 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000289 case 20:
290 break;
291 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100292 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000293 return -1;
294 default:
295 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100296 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000297 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000298 }
299 return 1;
300}
301
Denis Vlasenko322661d2007-01-29 23:43:52 +0000302static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000303{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000304 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000305 int pid;
306 int normallyup = 0;
307 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000308 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000309
Denis Vlasenko04c63862006-11-17 18:58:49 +0000310 if (stat("down", &s) == -1) {
311 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100312 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000313 return 0;
314 }
315 normallyup = 1;
316 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000317 pid = SWAP_LE32(svstatus.pid_le32);
318 timestamp = SWAP_BE64(svstatus.time_be64);
James Byrne0c632992017-05-15 21:39:51 +0200319 switch (svstatus.run_or_finish) {
320 case 0: printf("down: "); break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000321 case 1: printf("run: "); break;
322 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000323 }
James Byrne0c632992017-05-15 21:39:51 +0200324 printf("%s: ", m);
325 if (svstatus.run_or_finish)
326 printf("(pid %d) ", pid);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000327 diff = tnow - timestamp;
328 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000329 if (pid) {
330 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000331 if (svstatus.paused) printf(", paused");
332 if (svstatus.want == 'd') printf(", want down");
333 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000334 } else {
335 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000336 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000337 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000338 return pid ? 1 : 2;
339}
340
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000341static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000342{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000343 int r;
344
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200345 if (svstatus_get() <= 0)
346 return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000347
Denis Vlasenko04c63862006-11-17 18:58:49 +0000348 r = svstatus_print(*service);
James Byrne0c632992017-05-15 21:39:51 +0200349 islog = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000350 if (chdir("log") == -1) {
351 if (errno != ENOENT) {
James Byrne0c632992017-05-15 21:39:51 +0200352 printf("; ");
353 warn("can't change directory");
354 } else
355 bb_putchar('\n');
356 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000357 printf("; ");
James Byrne0c632992017-05-15 21:39:51 +0200358 if (svstatus_get()) {
359 r = svstatus_print("log");
360 bb_putchar('\n');
361 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000362 }
James Byrne0c632992017-05-15 21:39:51 +0200363 islog = 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000364 return r;
365}
366
367static int checkscript(void)
368{
369 char *prog[2];
370 struct stat s;
371 int pid, w;
372
373 if (stat("check", &s) == -1) {
374 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100375 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000376 return 0;
377 }
378 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000379 prog[0] = (char*)"./check";
380 prog[1] = NULL;
381 pid = spawn(prog);
382 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100383 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000384 return 0;
385 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000386 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100387 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000388 return 0;
389 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200390 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000391}
392
Denis Vlasenko322661d2007-01-29 23:43:52 +0000393static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000394{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000395 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200396 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000397 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000398
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000399 r = svstatus_get();
400 if (r == -1)
401 return -1;
James Byrne0c632992017-05-15 21:39:51 +0200402 while (*a) {
403 if (r == 0) {
404 if (*a == 'x')
405 return 1;
406 return -1;
407 }
408 pid_le32 = svstatus.pid_le32;
409 switch (*a) {
410 case 'x':
Denis Vlasenko04c63862006-11-17 18:58:49 +0000411 return 0;
James Byrne0c632992017-05-15 21:39:51 +0200412 case 'u':
413 if (!pid_le32 || svstatus.run_or_finish != 1)
414 return 0;
415 if (!checkscript())
416 return 0;
417 break;
418 case 'd':
419 if (pid_le32 || svstatus.run_or_finish != 0)
420 return 0;
421 break;
422 case 'C':
423 if (pid_le32 && !checkscript())
424 return 0;
425 break;
426 case 't':
427 case 'k':
428 if (!pid_le32 && svstatus.want == 'd')
429 break;
430 timestamp = SWAP_BE64(svstatus.time_be64);
431 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
432 return 0;
433 break;
434 case 'o':
435 timestamp = SWAP_BE64(svstatus.time_be64);
436 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
437 return 0;
438 break;
439 case 'p':
440 if (pid_le32 && !svstatus.paused)
441 return 0;
442 break;
443 case 'c':
444 if (pid_le32 && svstatus.paused)
445 return 0;
446 break;
447 }
448 ++a;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000449 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000450 printf(OK);
451 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000452 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000453 return 1;
454}
455
Denis Vlasenko322661d2007-01-29 23:43:52 +0000456static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200458 int fd, r, l;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000459
Denis Vlasenko45946f82007-08-20 17:27:40 +0000460 if (svstatus_get() <= 0)
461 return -1;
James Byrne0c632992017-05-15 21:39:51 +0200462 if (svstatus.want == *a && (*a != 'd' || svstatus.got_term == 1))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000463 return 0;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200464 fd = open("supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000465 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000466 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100467 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000468 else
469 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
470 return -1;
471 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200472 l = strlen(a);
473 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000474 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200475 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100476 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000477 return -1;
478 }
479 return 1;
480}
481
Denys Vlasenko4051a992016-12-05 13:56:40 +0100482//usage:#define sv_trivial_usage
483//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
484//usage:#define sv_full_usage "\n\n"
485//usage: "Control services monitored by runsv supervisor.\n"
486//usage: "Commands (only first character is enough):\n"
487//usage: "\n"
488//usage: "status: query service status\n"
489//usage: "up: if service isn't running, start it. If service stops, restart it\n"
490//usage: "once: like 'up', but if service stops, don't restart it\n"
491//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
492//usage: " if it exists. After it stops, don't restart service\n"
493//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
494//usage: " runsv exits too\n"
495//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
496//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
497static int sv(char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000498{
Denis Vlasenko04c63862006-11-17 18:58:49 +0000499 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000500 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000501 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000502 unsigned waitsec = 7;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000503 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000504 int verbose = 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000505 int (*act)(const char*);
506 int (*cbk)(const char*);
507 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000508
Denis Vlasenkob9256052007-09-28 10:29:17 +0000509 INIT_G();
510
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000511 xfunc_error_retval = 100;
512
513 x = getenv("SVDIR");
514 if (x) varservice = x;
515 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000516 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000517
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200518 getopt32(argv, "^" "w:+v" "\0" "vv" /* -w N, -v is a counter */,
519 &waitsec, &verbose
520 );
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000521 argv += optind;
522 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000523 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000524
Denis Vlasenko04158e02009-02-02 10:48:06 +0000525 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000526 tstart = tnow;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200527 curdir = open(".", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000528 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000529 fatal_cannot("open current directory");
530
Denis Vlasenko322661d2007-01-29 23:43:52 +0000531 act = &control;
532 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000533 cbk = &check;
534
Denis Vlasenko04c63862006-11-17 18:58:49 +0000535 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000536 case 'x':
537 case 'e':
538 acts = "x";
539 if (!verbose) cbk = NULL;
540 break;
541 case 'X':
542 case 'E':
543 acts = "x";
544 kll = 1;
545 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000546 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000547 acts = "d";
548 kll = 1;
549 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000550 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000551 acts = "tc";
552 kll = 1;
553 break;
James Byrne0c632992017-05-15 21:39:51 +0200554 case 't':
555 if (str_equal(action, "try-restart")) {
556 acts = "tc";
557 break;
558 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000559 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000560 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000561 act = NULL;
James Byrne0c632992017-05-15 21:39:51 +0200562 acts = "C";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000563 break;
564 }
James Byrne0c632992017-05-15 21:39:51 +0200565 case 'u': case 'd': case 'o': case 'p': case 'h':
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000567 action[1] = '\0';
568 acts = action;
James Byrne0c632992017-05-15 21:39:51 +0200569 if (!verbose)
570 cbk = NULL;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000571 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000572 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000573 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000574 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000575 break;
576 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000577 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000578 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000579 break;
580 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000581 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000582 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000583 break;
584 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000585 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000586 act = &status;
587 cbk = NULL;
588 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000589 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000590 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000591 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000592 break;
593 }
James Byrne0c632992017-05-15 21:39:51 +0200594 if (str_equal(action, "reload")) {
595 acts = "h";
596 break;
597 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000598 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000599 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000600 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000601 acts = "tc";
602 kll = 1;
603 break;
604 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000605 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000606 acts = "tcu";
607 kll = 1;
608 break;
609 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000610 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000611 acts = "x";
612 kll = 1;
613 break;
614 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000615 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000616 acts = "d";
617 kll = 1;
618 break;
619 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000620 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000621 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000622 }
623
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200624 service = argv;
625 while ((x = *service) != NULL) {
James Byrne0c632992017-05-15 21:39:51 +0200626 if (x[0] != '/' && x[0] != '.'
Denys Vlasenko0d79d772018-03-30 20:02:33 +0200627 && !last_char_is(x, '/')
James Byrne0c632992017-05-15 21:39:51 +0200628 ) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000629 if (chdir(varservice) == -1)
630 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000631 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200632 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000633 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100634 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000635 goto nullify_service_0;
636 }
637 if (act && (act(acts) == -1)) {
638 nullify_service_0:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200639 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000640 }
641 if (fchdir(curdir) == -1)
642 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000643 service++;
644 }
645
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000646 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200647 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000648 int diff;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000649
Denis Vlasenko45946f82007-08-20 17:27:40 +0000650 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200651 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000652 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200653 while ((x = *service) != NULL) {
654 if (x == (char*) -1L) /* "dead" */
655 goto next;
656 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000657 if (chdir(varservice) == -1)
658 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000659 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200660 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000661 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100662 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000663 goto nullify_service;
664 }
665 if (cbk(acts) != 0)
666 goto nullify_service;
667 want_exit = 0;
668 if (diff >= waitsec) {
669 printf(kll ? "kill: " : "timeout: ");
670 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200671 svstatus_print(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000672 ++rc;
673 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000674 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000675 if (kll)
676 control("k");
677 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200678 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000679 }
680 if (fchdir(curdir) == -1)
681 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200682 next:
683 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000684 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000685 if (want_exit) break;
686 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000687 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000688 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000689 return rc > 99 ? 99 : rc;
690}
Denys Vlasenko4051a992016-12-05 13:56:40 +0100691
692#if ENABLE_SV
693int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
694int sv_main(int argc UNUSED_PARAM, char **argv)
695{
696 return sv(argv);
697}
698#endif
699
700//usage:#define svc_trivial_usage
701//usage: "[-udopchaitkx] SERVICE_DIR..."
702//usage:#define svc_full_usage "\n\n"
703//usage: "Control services monitored by runsv supervisor"
704//usage: "\n"
705//usage: "\n"" -u If service is not running, start it; restart if it stops"
706//usage: "\n"" -d If service is running, send TERM+CONT signals; do not restart it"
707//usage: "\n"" -o Once: if service is not running, start it; do not restart it"
708//usage: "\n"" -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
709//usage: "\n"" -x Exit: runsv will exit as soon as the service is down"
710#if ENABLE_SVC
711int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
712int svc_main(int argc UNUSED_PARAM, char **argv)
713{
714 char command[2];
715 const char *optstring;
716 unsigned opts;
717
Denys Vlasenko4051a992016-12-05 13:56:40 +0100718 optstring = "udopchaitkx";
719 opts = getopt32(argv, optstring);
720 argv += optind;
721 if (!argv[0] || !opts)
722 bb_show_usage();
723
724 argv -= 2;
725 if (optind > 2) {
726 argv--;
727 argv[2] = (char*)"--";
728 }
729 argv[0] = (char*)"sv";
730 argv[1] = command;
731 command[1] = '\0';
732
Denys Vlasenko4051a992016-12-05 13:56:40 +0100733 do {
734 if (opts & 1) {
735 int r;
Denys Vlasenko754e9f92017-08-05 01:38:55 +0200736
Denys Vlasenko4051a992016-12-05 13:56:40 +0100737 command[0] = *optstring;
Denys Vlasenko754e9f92017-08-05 01:38:55 +0200738
739 /* getopt() was already called by getopt32():
740 * reset the libc getopt() function's internal state.
741 */
742 GETOPT_RESET();
Denys Vlasenko4051a992016-12-05 13:56:40 +0100743 r = sv(argv);
744 if (r)
745 return 1;
746 }
747 optstring++;
748 opts >>= 1;
749 } while (opts);
750
751 return 0;
752}
753#endif
Denys Vlasenko0d79d772018-03-30 20:02:33 +0200754
755//usage:#define svok_trivial_usage
756//usage: "SERVICE_DIR"
757//usage:#define svok_full_usage "\n\n"
758//usage: "Check whether runsv supervisor is running.\n"
759//usage: "Exit code is 0 if it does, 100 if it does not,\n"
760//usage: "111 (with error message) if SERVICE_DIR does not exist."
761#if ENABLE_SVOK
762int svok_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
763int svok_main(int argc UNUSED_PARAM, char **argv)
764{
765 const char *dir = argv[1];
766
767 if (!dir)
768 bb_show_usage();
769
770 xfunc_error_retval = 111;
771
772 /*
773 * daemontools has no concept of "default service dir", runit does.
774 * Let's act as runit.
775 */
776 if (dir[0] != '/' && dir[0] != '.'
777 && !last_char_is(dir, '/')
778 ) {
779 xchdir(CONFIG_SV_DEFAULT_SERVICE_DIR);
780 }
781
782 xchdir(dir);
783 if (open("supervise/ok", O_WRONLY) < 0) {
784 if (errno == ENOENT || errno == ENXIO)
785 return 100;
786 bb_perror_msg_and_die("can't open '%s'", "supervise/ok");
787 }
788
789 return 0;
790}
791#endif