blob: 1d0809be86883b7cb4b71559e160f1e035c72ebb [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(); \
Denys Vlasenko73896622017-08-06 21:29:51 +0200208 /* need to zero out, svc calls sv() repeatedly */ \
209 memset(&G, 0, sizeof(G)); \
Denys Vlasenkob9be7802017-08-06 21:23:03 +0200210} while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000211
Denis Vlasenko04c63862006-11-17 18:58:49 +0000212
Denys Vlasenko1d3a04a2016-11-28 01:22:57 +0100213#define str_equal(s,t) (strcmp((s), (t)) == 0)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200214
215
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000216static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000217static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000218{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000219 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000220 _exit(151);
221}
222
Denis Vlasenko322661d2007-01-29 23:43:52 +0000223static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000224{
James Byrne0c632992017-05-15 21:39:51 +0200225 printf("%s%s%s: %s", p, *service, islog ? "/log" : "", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000226 if (errno) {
227 printf(": %s", strerror(errno));
228 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000229 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000230}
231
Denis Vlasenko04c63862006-11-17 18:58:49 +0000232#define WARN "warning: "
233#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000234
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000235static void fail(const char *m1)
236{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000237 ++rc;
238 out("fail: ", m1);
239}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000240static void failx(const char *m1)
241{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000242 errno = 0;
243 fail(m1);
244}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000245static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000246{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000247 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000248 /* "warning: <service>: <m1>\n" */
249 out("warning: ", m1);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000250}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000251static void ok(const char *m1)
252{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000253 errno = 0;
254 out(OK, m1);
255}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000256
257static int svstatus_get(void)
258{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000259 int fd, r;
260
Denys Vlasenko05e86052010-10-13 12:53:27 +0200261 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000262 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000263 if (errno == ENODEV) {
264 *acts == 'x' ? ok("runsv not running")
265 : failx("runsv not running");
266 return 0;
267 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100268 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000269 return -1;
270 }
271 close(fd);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200272 fd = open("supervise/status", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000273 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100274 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000275 return -1;
276 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000277 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000278 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000279 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000280 case 20:
281 break;
282 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100283 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000284 return -1;
285 default:
286 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100287 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000288 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000289 }
290 return 1;
291}
292
Denis Vlasenko322661d2007-01-29 23:43:52 +0000293static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000294{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000295 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000296 int pid;
297 int normallyup = 0;
298 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000299 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000300
Denis Vlasenko04c63862006-11-17 18:58:49 +0000301 if (stat("down", &s) == -1) {
302 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100303 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000304 return 0;
305 }
306 normallyup = 1;
307 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000308 pid = SWAP_LE32(svstatus.pid_le32);
309 timestamp = SWAP_BE64(svstatus.time_be64);
James Byrne0c632992017-05-15 21:39:51 +0200310 switch (svstatus.run_or_finish) {
311 case 0: printf("down: "); break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000312 case 1: printf("run: "); break;
313 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000314 }
James Byrne0c632992017-05-15 21:39:51 +0200315 printf("%s: ", m);
316 if (svstatus.run_or_finish)
317 printf("(pid %d) ", pid);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000318 diff = tnow - timestamp;
319 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000320 if (pid) {
321 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000322 if (svstatus.paused) printf(", paused");
323 if (svstatus.want == 'd') printf(", want down");
324 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000325 } else {
326 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000327 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000328 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000329 return pid ? 1 : 2;
330}
331
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000332static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000333{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000334 int r;
335
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200336 if (svstatus_get() <= 0)
337 return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000338
Denis Vlasenko04c63862006-11-17 18:58:49 +0000339 r = svstatus_print(*service);
James Byrne0c632992017-05-15 21:39:51 +0200340 islog = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000341 if (chdir("log") == -1) {
342 if (errno != ENOENT) {
James Byrne0c632992017-05-15 21:39:51 +0200343 printf("; ");
344 warn("can't change directory");
345 } else
346 bb_putchar('\n');
347 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000348 printf("; ");
James Byrne0c632992017-05-15 21:39:51 +0200349 if (svstatus_get()) {
350 r = svstatus_print("log");
351 bb_putchar('\n');
352 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000353 }
James Byrne0c632992017-05-15 21:39:51 +0200354 islog = 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000355 return r;
356}
357
358static int checkscript(void)
359{
360 char *prog[2];
361 struct stat s;
362 int pid, w;
363
364 if (stat("check", &s) == -1) {
365 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100366 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000367 return 0;
368 }
369 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000370 prog[0] = (char*)"./check";
371 prog[1] = NULL;
372 pid = spawn(prog);
373 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100374 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000375 return 0;
376 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000377 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100378 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000379 return 0;
380 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200381 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000382}
383
Denis Vlasenko322661d2007-01-29 23:43:52 +0000384static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000385{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000386 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200387 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000388 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000389
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000390 r = svstatus_get();
391 if (r == -1)
392 return -1;
James Byrne0c632992017-05-15 21:39:51 +0200393 while (*a) {
394 if (r == 0) {
395 if (*a == 'x')
396 return 1;
397 return -1;
398 }
399 pid_le32 = svstatus.pid_le32;
400 switch (*a) {
401 case 'x':
Denis Vlasenko04c63862006-11-17 18:58:49 +0000402 return 0;
James Byrne0c632992017-05-15 21:39:51 +0200403 case 'u':
404 if (!pid_le32 || svstatus.run_or_finish != 1)
405 return 0;
406 if (!checkscript())
407 return 0;
408 break;
409 case 'd':
410 if (pid_le32 || svstatus.run_or_finish != 0)
411 return 0;
412 break;
413 case 'C':
414 if (pid_le32 && !checkscript())
415 return 0;
416 break;
417 case 't':
418 case 'k':
419 if (!pid_le32 && svstatus.want == 'd')
420 break;
421 timestamp = SWAP_BE64(svstatus.time_be64);
422 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
423 return 0;
424 break;
425 case 'o':
426 timestamp = SWAP_BE64(svstatus.time_be64);
427 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
428 return 0;
429 break;
430 case 'p':
431 if (pid_le32 && !svstatus.paused)
432 return 0;
433 break;
434 case 'c':
435 if (pid_le32 && svstatus.paused)
436 return 0;
437 break;
438 }
439 ++a;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000440 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000441 printf(OK);
442 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000443 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000444 return 1;
445}
446
Denis Vlasenko322661d2007-01-29 23:43:52 +0000447static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000448{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200449 int fd, r, l;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000450
Denis Vlasenko45946f82007-08-20 17:27:40 +0000451 if (svstatus_get() <= 0)
452 return -1;
James Byrne0c632992017-05-15 21:39:51 +0200453 if (svstatus.want == *a && (*a != 'd' || svstatus.got_term == 1))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000454 return 0;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200455 fd = open("supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000456 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000457 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100458 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000459 else
460 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
461 return -1;
462 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200463 l = strlen(a);
464 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000465 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200466 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100467 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000468 return -1;
469 }
470 return 1;
471}
472
Denys Vlasenko4051a992016-12-05 13:56:40 +0100473//usage:#define sv_trivial_usage
474//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
475//usage:#define sv_full_usage "\n\n"
476//usage: "Control services monitored by runsv supervisor.\n"
477//usage: "Commands (only first character is enough):\n"
478//usage: "\n"
479//usage: "status: query service status\n"
480//usage: "up: if service isn't running, start it. If service stops, restart it\n"
481//usage: "once: like 'up', but if service stops, don't restart it\n"
482//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
483//usage: " if it exists. After it stops, don't restart service\n"
484//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
485//usage: " runsv exits too\n"
486//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
487//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
488static int sv(char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000489{
Denis Vlasenko04c63862006-11-17 18:58:49 +0000490 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000491 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000492 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000493 unsigned waitsec = 7;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000494 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000495 int verbose = 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000496 int (*act)(const char*);
497 int (*cbk)(const char*);
498 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000499
Denis Vlasenkob9256052007-09-28 10:29:17 +0000500 INIT_G();
501
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000502 xfunc_error_retval = 100;
503
504 x = getenv("SVDIR");
505 if (x) varservice = x;
506 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000507 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000508
Denys Vlasenko22542ec2017-08-08 21:55:02 +0200509 getopt32(argv, "^" "w:+v" "\0" "vv" /* -w N, -v is a counter */,
510 &waitsec, &verbose
511 );
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000512 argv += optind;
513 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000514 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000515
Denis Vlasenko04158e02009-02-02 10:48:06 +0000516 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000517 tstart = tnow;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200518 curdir = open(".", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000519 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000520 fatal_cannot("open current directory");
521
Denis Vlasenko322661d2007-01-29 23:43:52 +0000522 act = &control;
523 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000524 cbk = &check;
525
Denis Vlasenko04c63862006-11-17 18:58:49 +0000526 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000527 case 'x':
528 case 'e':
529 acts = "x";
530 if (!verbose) cbk = NULL;
531 break;
532 case 'X':
533 case 'E':
534 acts = "x";
535 kll = 1;
536 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000537 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000538 acts = "d";
539 kll = 1;
540 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000541 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000542 acts = "tc";
543 kll = 1;
544 break;
James Byrne0c632992017-05-15 21:39:51 +0200545 case 't':
546 if (str_equal(action, "try-restart")) {
547 acts = "tc";
548 break;
549 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000550 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000551 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000552 act = NULL;
James Byrne0c632992017-05-15 21:39:51 +0200553 acts = "C";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000554 break;
555 }
James Byrne0c632992017-05-15 21:39:51 +0200556 case 'u': case 'd': case 'o': case 'p': case 'h':
Denis Vlasenko04c63862006-11-17 18:58:49 +0000557 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000558 action[1] = '\0';
559 acts = action;
James Byrne0c632992017-05-15 21:39:51 +0200560 if (!verbose)
561 cbk = NULL;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000562 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000563 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000564 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000565 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000566 break;
567 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000568 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000569 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000570 break;
571 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000572 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000573 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000574 break;
575 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000576 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000577 act = &status;
578 cbk = NULL;
579 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000580 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000581 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000582 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000583 break;
584 }
James Byrne0c632992017-05-15 21:39:51 +0200585 if (str_equal(action, "reload")) {
586 acts = "h";
587 break;
588 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000589 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000590 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000591 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000592 acts = "tc";
593 kll = 1;
594 break;
595 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000596 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000597 acts = "tcu";
598 kll = 1;
599 break;
600 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000601 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000602 acts = "x";
603 kll = 1;
604 break;
605 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000606 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000607 acts = "d";
608 kll = 1;
609 break;
610 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000611 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000612 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000613 }
614
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200615 service = argv;
616 while ((x = *service) != NULL) {
James Byrne0c632992017-05-15 21:39:51 +0200617 if (x[0] != '/' && x[0] != '.'
618 && x[0] != '\0' && x[strlen(x) - 1] != '/'
619 ) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000620 if (chdir(varservice) == -1)
621 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000622 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200623 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000624 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100625 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000626 goto nullify_service_0;
627 }
628 if (act && (act(acts) == -1)) {
629 nullify_service_0:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200630 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000631 }
632 if (fchdir(curdir) == -1)
633 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000634 service++;
635 }
636
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000637 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200638 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000639 int diff;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000640
Denis Vlasenko45946f82007-08-20 17:27:40 +0000641 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200642 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000643 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200644 while ((x = *service) != NULL) {
645 if (x == (char*) -1L) /* "dead" */
646 goto next;
647 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000648 if (chdir(varservice) == -1)
649 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000650 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200651 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000652 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100653 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000654 goto nullify_service;
655 }
656 if (cbk(acts) != 0)
657 goto nullify_service;
658 want_exit = 0;
659 if (diff >= waitsec) {
660 printf(kll ? "kill: " : "timeout: ");
661 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200662 svstatus_print(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000663 ++rc;
664 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000665 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000666 if (kll)
667 control("k");
668 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200669 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000670 }
671 if (fchdir(curdir) == -1)
672 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200673 next:
674 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000675 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000676 if (want_exit) break;
677 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000678 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000679 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000680 return rc > 99 ? 99 : rc;
681}
Denys Vlasenko4051a992016-12-05 13:56:40 +0100682
683#if ENABLE_SV
684int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
685int sv_main(int argc UNUSED_PARAM, char **argv)
686{
687 return sv(argv);
688}
689#endif
690
691//usage:#define svc_trivial_usage
692//usage: "[-udopchaitkx] SERVICE_DIR..."
693//usage:#define svc_full_usage "\n\n"
694//usage: "Control services monitored by runsv supervisor"
695//usage: "\n"
696//usage: "\n"" -u If service is not running, start it; restart if it stops"
697//usage: "\n"" -d If service is running, send TERM+CONT signals; do not restart it"
698//usage: "\n"" -o Once: if service is not running, start it; do not restart it"
699//usage: "\n"" -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
700//usage: "\n"" -x Exit: runsv will exit as soon as the service is down"
701#if ENABLE_SVC
702int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
703int svc_main(int argc UNUSED_PARAM, char **argv)
704{
705 char command[2];
706 const char *optstring;
707 unsigned opts;
708
Denys Vlasenko4051a992016-12-05 13:56:40 +0100709 optstring = "udopchaitkx";
710 opts = getopt32(argv, optstring);
711 argv += optind;
712 if (!argv[0] || !opts)
713 bb_show_usage();
714
715 argv -= 2;
716 if (optind > 2) {
717 argv--;
718 argv[2] = (char*)"--";
719 }
720 argv[0] = (char*)"sv";
721 argv[1] = command;
722 command[1] = '\0';
723
Denys Vlasenko4051a992016-12-05 13:56:40 +0100724 do {
725 if (opts & 1) {
726 int r;
Denys Vlasenko754e9f92017-08-05 01:38:55 +0200727
Denys Vlasenko4051a992016-12-05 13:56:40 +0100728 command[0] = *optstring;
Denys Vlasenko754e9f92017-08-05 01:38:55 +0200729
730 /* getopt() was already called by getopt32():
731 * reset the libc getopt() function's internal state.
732 */
733 GETOPT_RESET();
Denys Vlasenko4051a992016-12-05 13:56:40 +0100734 r = sv(argv);
735 if (r)
736 return 1;
737 }
738 optstring++;
739 opts >>= 1;
740 } while (opts);
741
742 return 0;
743}
744#endif