blob: faa31d4faee944e76bf906fd31dad81c60c0f622 [file] [log] [blame]
Denis Vlasenko8a164052007-03-12 23:34:52 +00001/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
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
157//config: bool "sv"
158//config: default y
159//config: help
160//config: sv reports the current status and controls the state of services
161//config: monitored by the runsv supervisor.
162//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
168//config: Default directory for services.
169//config: Defaults to "/var/service"
Denys Vlasenko4051a992016-12-05 13:56:40 +0100170//config:
171//config:config SVC
172//config: bool "svc"
173//config: default y
174//config: help
175//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
178//applet:IF_SV(APPLET(sv, BB_DIR_USR_BIN, BB_SUID_DROP))
Denys Vlasenko548620c2016-12-08 12:24:48 +0100179//applet:IF_SVC(APPLET(svc, BB_DIR_USR_BIN, BB_SUID_DROP))
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;
James Byrne0c632992017-05-15 21:39:51 +0200196 unsigned 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 Vlasenko47cfbf32016-04-21 18:18:48 +0200206#define INIT_G() do { setup_common_bufsiz(); } while (0)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000207
Denis Vlasenko04c63862006-11-17 18:58:49 +0000208
Denys Vlasenko1d3a04a2016-11-28 01:22:57 +0100209#define str_equal(s,t) (strcmp((s), (t)) == 0)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200210
211
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000212static void fatal_cannot(const char *m1) NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000213static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000214{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000215 bb_perror_msg("fatal: can't %s", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000216 _exit(151);
217}
218
Denis Vlasenko322661d2007-01-29 23:43:52 +0000219static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000220{
James Byrne0c632992017-05-15 21:39:51 +0200221 printf("%s%s%s: %s", p, *service, islog ? "/log" : "", m1);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000222 if (errno) {
223 printf(": %s", strerror(errno));
224 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000225 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000226}
227
Denis Vlasenko04c63862006-11-17 18:58:49 +0000228#define WARN "warning: "
229#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +0000230
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000231static void fail(const char *m1)
232{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000233 ++rc;
234 out("fail: ", m1);
235}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000236static void failx(const char *m1)
237{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000238 errno = 0;
239 fail(m1);
240}
Denis Vlasenko45946f82007-08-20 17:27:40 +0000241static void warn(const char *m1)
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000242{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000243 ++rc;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000244 /* "warning: <service>: <m1>\n" */
245 out("warning: ", m1);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000246}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000247static void ok(const char *m1)
248{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000249 errno = 0;
250 out(OK, m1);
251}
Denis Vlasenko04c63862006-11-17 18:58:49 +0000252
253static int svstatus_get(void)
254{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000255 int fd, r;
256
Denys Vlasenko05e86052010-10-13 12:53:27 +0200257 fd = open("supervise/ok", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000258 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000259 if (errno == ENODEV) {
260 *acts == 'x' ? ok("runsv not running")
261 : failx("runsv not running");
262 return 0;
263 }
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100264 warn("can't open supervise/ok");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000265 return -1;
266 }
267 close(fd);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200268 fd = open("supervise/status", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000269 if (fd == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100270 warn("can't open supervise/status");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000271 return -1;
272 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000273 r = read(fd, &svstatus, 20);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000274 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000275 switch (r) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000276 case 20:
277 break;
278 case -1:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100279 warn("can't read supervise/status");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000280 return -1;
281 default:
282 errno = 0;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100283 warn("can't read supervise/status: bad format");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000284 return -1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000285 }
286 return 1;
287}
288
Denis Vlasenko322661d2007-01-29 23:43:52 +0000289static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000290{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000291 int diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 int pid;
293 int normallyup = 0;
294 struct stat s;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000295 uint64_t timestamp;
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000296
Denis Vlasenko04c63862006-11-17 18:58:49 +0000297 if (stat("down", &s) == -1) {
298 if (errno != ENOENT) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100299 bb_perror_msg(WARN"can't stat %s/down", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000300 return 0;
301 }
302 normallyup = 1;
303 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000304 pid = SWAP_LE32(svstatus.pid_le32);
305 timestamp = SWAP_BE64(svstatus.time_be64);
James Byrne0c632992017-05-15 21:39:51 +0200306 switch (svstatus.run_or_finish) {
307 case 0: printf("down: "); break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000308 case 1: printf("run: "); break;
309 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000310 }
James Byrne0c632992017-05-15 21:39:51 +0200311 printf("%s: ", m);
312 if (svstatus.run_or_finish)
313 printf("(pid %d) ", pid);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000314 diff = tnow - timestamp;
315 printf("%us", (diff < 0 ? 0 : diff));
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000316 if (pid) {
317 if (!normallyup) printf(", normally down");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000318 if (svstatus.paused) printf(", paused");
319 if (svstatus.want == 'd') printf(", want down");
320 if (svstatus.got_term) printf(", got TERM");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000321 } else {
322 if (normallyup) printf(", normally up");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000323 if (svstatus.want == 'u') printf(", want up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000324 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000325 return pid ? 1 : 2;
326}
327
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000328static int status(const char *unused UNUSED_PARAM)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000329{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000330 int r;
331
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200332 if (svstatus_get() <= 0)
333 return 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000334
Denis Vlasenko04c63862006-11-17 18:58:49 +0000335 r = svstatus_print(*service);
James Byrne0c632992017-05-15 21:39:51 +0200336 islog = 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000337 if (chdir("log") == -1) {
338 if (errno != ENOENT) {
James Byrne0c632992017-05-15 21:39:51 +0200339 printf("; ");
340 warn("can't change directory");
341 } else
342 bb_putchar('\n');
343 } else {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000344 printf("; ");
James Byrne0c632992017-05-15 21:39:51 +0200345 if (svstatus_get()) {
346 r = svstatus_print("log");
347 bb_putchar('\n');
348 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000349 }
James Byrne0c632992017-05-15 21:39:51 +0200350 islog = 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000351 return r;
352}
353
354static int checkscript(void)
355{
356 char *prog[2];
357 struct stat s;
358 int pid, w;
359
360 if (stat("check", &s) == -1) {
361 if (errno == ENOENT) return 1;
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100362 bb_perror_msg(WARN"can't stat %s/check", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000363 return 0;
364 }
365 /* if (!(s.st_mode & S_IXUSR)) return 1; */
Denis Vlasenko53091ec2007-03-26 13:35:09 +0000366 prog[0] = (char*)"./check";
367 prog[1] = NULL;
368 pid = spawn(prog);
369 if (pid <= 0) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100370 bb_perror_msg(WARN"can't %s child %s/check", "run", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000371 return 0;
372 }
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000373 while (safe_waitpid(pid, &w, 0) == -1) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100374 bb_perror_msg(WARN"can't %s child %s/check", "wait for", *service);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000375 return 0;
376 }
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200377 return WEXITSTATUS(w) == 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000378}
379
Denis Vlasenko322661d2007-01-29 23:43:52 +0000380static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000381{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000382 int r;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200383 unsigned pid_le32;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000384 uint64_t timestamp;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000385
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000386 r = svstatus_get();
387 if (r == -1)
388 return -1;
James Byrne0c632992017-05-15 21:39:51 +0200389 while (*a) {
390 if (r == 0) {
391 if (*a == 'x')
392 return 1;
393 return -1;
394 }
395 pid_le32 = svstatus.pid_le32;
396 switch (*a) {
397 case 'x':
Denis Vlasenko04c63862006-11-17 18:58:49 +0000398 return 0;
James Byrne0c632992017-05-15 21:39:51 +0200399 case 'u':
400 if (!pid_le32 || svstatus.run_or_finish != 1)
401 return 0;
402 if (!checkscript())
403 return 0;
404 break;
405 case 'd':
406 if (pid_le32 || svstatus.run_or_finish != 0)
407 return 0;
408 break;
409 case 'C':
410 if (pid_le32 && !checkscript())
411 return 0;
412 break;
413 case 't':
414 case 'k':
415 if (!pid_le32 && svstatus.want == 'd')
416 break;
417 timestamp = SWAP_BE64(svstatus.time_be64);
418 if ((tstart > timestamp) || !pid_le32 || svstatus.got_term || !checkscript())
419 return 0;
420 break;
421 case 'o':
422 timestamp = SWAP_BE64(svstatus.time_be64);
423 if ((!pid_le32 && tstart > timestamp) || (pid_le32 && svstatus.want != 'd'))
424 return 0;
425 break;
426 case 'p':
427 if (pid_le32 && !svstatus.paused)
428 return 0;
429 break;
430 case 'c':
431 if (pid_le32 && svstatus.paused)
432 return 0;
433 break;
434 }
435 ++a;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000436 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000437 printf(OK);
438 svstatus_print(*service);
Denis Vlasenko4daad902007-09-27 10:20:47 +0000439 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000440 return 1;
441}
442
Denis Vlasenko322661d2007-01-29 23:43:52 +0000443static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000444{
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200445 int fd, r, l;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000446
Denis Vlasenko45946f82007-08-20 17:27:40 +0000447 if (svstatus_get() <= 0)
448 return -1;
James Byrne0c632992017-05-15 21:39:51 +0200449 if (svstatus.want == *a && (*a != 'd' || svstatus.got_term == 1))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000450 return 0;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200451 fd = open("supervise/control", O_WRONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000452 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000453 if (errno != ENODEV)
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100454 warn("can't open supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000455 else
456 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
457 return -1;
458 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200459 l = strlen(a);
460 r = write(fd, a, l);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000461 close(fd);
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200462 if (r != l) {
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100463 warn("can't write to supervise/control");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000464 return -1;
465 }
466 return 1;
467}
468
Denys Vlasenko4051a992016-12-05 13:56:40 +0100469//usage:#define sv_trivial_usage
470//usage: "[-v] [-w SEC] CMD SERVICE_DIR..."
471//usage:#define sv_full_usage "\n\n"
472//usage: "Control services monitored by runsv supervisor.\n"
473//usage: "Commands (only first character is enough):\n"
474//usage: "\n"
475//usage: "status: query service status\n"
476//usage: "up: if service isn't running, start it. If service stops, restart it\n"
477//usage: "once: like 'up', but if service stops, don't restart it\n"
478//usage: "down: send TERM and CONT signals. If ./run exits, start ./finish\n"
479//usage: " if it exists. After it stops, don't restart service\n"
480//usage: "exit: send TERM and CONT signals to service and log service. If they exit,\n"
481//usage: " runsv exits too\n"
482//usage: "pause, cont, hup, alarm, interrupt, quit, 1, 2, term, kill: send\n"
483//usage: "STOP, CONT, HUP, ALRM, INT, QUIT, USR1, USR2, TERM, KILL signal to service"
484static int sv(char **argv)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000485{
Denis Vlasenko04c63862006-11-17 18:58:49 +0000486 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000487 char *action;
Denis Vlasenkof068b3e2008-11-06 23:07:42 +0000488 const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000489 unsigned waitsec = 7;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000490 smallint kll = 0;
Denis Vlasenko1d426652008-03-17 09:09:09 +0000491 int verbose = 0;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000492 int (*act)(const char*);
493 int (*cbk)(const char*);
494 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000495
Denis Vlasenkob9256052007-09-28 10:29:17 +0000496 INIT_G();
497
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000498 xfunc_error_retval = 100;
499
500 x = getenv("SVDIR");
501 if (x) varservice = x;
502 x = getenv("SVWAIT");
Denis Vlasenko45946f82007-08-20 17:27:40 +0000503 if (x) waitsec = xatou(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000504
Denys Vlasenko237bedd2016-07-06 21:58:02 +0200505 opt_complementary = "vv"; /* -w N, -v is a counter */
506 getopt32(argv, "w:+v", &waitsec, &verbose);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000507 argv += optind;
508 action = *argv++;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000509 if (!action || !*argv) bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000510
Denis Vlasenko04158e02009-02-02 10:48:06 +0000511 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000512 tstart = tnow;
Denys Vlasenko05e86052010-10-13 12:53:27 +0200513 curdir = open(".", O_RDONLY|O_NDELAY);
Denis Vlasenko322661d2007-01-29 23:43:52 +0000514 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000515 fatal_cannot("open current directory");
516
Denis Vlasenko322661d2007-01-29 23:43:52 +0000517 act = &control;
518 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000519 cbk = &check;
520
Denis Vlasenko04c63862006-11-17 18:58:49 +0000521 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000522 case 'x':
523 case 'e':
524 acts = "x";
525 if (!verbose) cbk = NULL;
526 break;
527 case 'X':
528 case 'E':
529 acts = "x";
530 kll = 1;
531 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000532 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000533 acts = "d";
534 kll = 1;
535 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000536 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000537 acts = "tc";
538 kll = 1;
539 break;
James Byrne0c632992017-05-15 21:39:51 +0200540 case 't':
541 if (str_equal(action, "try-restart")) {
542 acts = "tc";
543 break;
544 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000545 case 'c':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000546 if (str_equal(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000547 act = NULL;
James Byrne0c632992017-05-15 21:39:51 +0200548 acts = "C";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000549 break;
550 }
James Byrne0c632992017-05-15 21:39:51 +0200551 case 'u': case 'd': case 'o': case 'p': case 'h':
Denis Vlasenko04c63862006-11-17 18:58:49 +0000552 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000553 action[1] = '\0';
554 acts = action;
James Byrne0c632992017-05-15 21:39:51 +0200555 if (!verbose)
556 cbk = NULL;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000557 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000558 case 's':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000559 if (str_equal(action, "shutdown")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000560 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000561 break;
562 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000563 if (str_equal(action, "start")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000564 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000565 break;
566 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000567 if (str_equal(action, "stop")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000568 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000569 break;
570 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000571 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000572 act = &status;
573 cbk = NULL;
574 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000575 case 'r':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000576 if (str_equal(action, "restart")) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000577 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000578 break;
579 }
James Byrne0c632992017-05-15 21:39:51 +0200580 if (str_equal(action, "reload")) {
581 acts = "h";
582 break;
583 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000584 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000585 case 'f':
Denis Vlasenko45946f82007-08-20 17:27:40 +0000586 if (str_equal(action, "force-reload")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000587 acts = "tc";
588 kll = 1;
589 break;
590 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000591 if (str_equal(action, "force-restart")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000592 acts = "tcu";
593 kll = 1;
594 break;
595 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000596 if (str_equal(action, "force-shutdown")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000597 acts = "x";
598 kll = 1;
599 break;
600 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000601 if (str_equal(action, "force-stop")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000602 acts = "d";
603 kll = 1;
604 break;
605 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000606 default:
Denis Vlasenko45946f82007-08-20 17:27:40 +0000607 bb_show_usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000608 }
609
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200610 service = argv;
611 while ((x = *service) != NULL) {
James Byrne0c632992017-05-15 21:39:51 +0200612 if (x[0] != '/' && x[0] != '.'
613 && x[0] != '\0' && x[strlen(x) - 1] != '/'
614 ) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000615 if (chdir(varservice) == -1)
616 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000617 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200618 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000619 chdir_failed_0:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100620 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000621 goto nullify_service_0;
622 }
623 if (act && (act(acts) == -1)) {
624 nullify_service_0:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200625 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000626 }
627 if (fchdir(curdir) == -1)
628 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000629 service++;
630 }
631
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000632 if (cbk) while (1) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200633 int want_exit;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000634 int diff;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000635
Denis Vlasenko45946f82007-08-20 17:27:40 +0000636 diff = tnow - tstart;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200637 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000638 want_exit = 1;
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200639 while ((x = *service) != NULL) {
640 if (x == (char*) -1L) /* "dead" */
641 goto next;
642 if (x[0] != '/' && x[0] != '.') {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000643 if (chdir(varservice) == -1)
644 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000645 }
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200646 if (chdir(x) == -1) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000647 chdir_failed:
Denys Vlasenko6331cf02009-11-13 09:08:27 +0100648 fail("can't change to service directory");
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000649 goto nullify_service;
650 }
651 if (cbk(acts) != 0)
652 goto nullify_service;
653 want_exit = 0;
654 if (diff >= waitsec) {
655 printf(kll ? "kill: " : "timeout: ");
656 if (svstatus_get() > 0) {
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200657 svstatus_print(x);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000658 ++rc;
659 }
Denis Vlasenko4daad902007-09-27 10:20:47 +0000660 bb_putchar('\n'); /* will also flush the output */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000661 if (kll)
662 control("k");
663 nullify_service:
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200664 *service = (char*) -1L; /* "dead" */
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000665 }
666 if (fchdir(curdir) == -1)
667 fatal_cannot("change to original directory");
Denys Vlasenkob3ba9e22009-07-15 00:24:08 +0200668 next:
669 service++;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000670 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000671 if (want_exit) break;
672 usleep(420000);
Denis Vlasenko04158e02009-02-02 10:48:06 +0000673 tnow = time(NULL) + 0x400000000000000aULL;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000674 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000675 return rc > 99 ? 99 : rc;
676}
Denys Vlasenko4051a992016-12-05 13:56:40 +0100677
678#if ENABLE_SV
679int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
680int sv_main(int argc UNUSED_PARAM, char **argv)
681{
682 return sv(argv);
683}
684#endif
685
686//usage:#define svc_trivial_usage
687//usage: "[-udopchaitkx] SERVICE_DIR..."
688//usage:#define svc_full_usage "\n\n"
689//usage: "Control services monitored by runsv supervisor"
690//usage: "\n"
691//usage: "\n"" -u If service is not running, start it; restart if it stops"
692//usage: "\n"" -d If service is running, send TERM+CONT signals; do not restart it"
693//usage: "\n"" -o Once: if service is not running, start it; do not restart it"
694//usage: "\n"" -pchaitk Send STOP, CONT, HUP, ALRM, INT, TERM, KILL signal to service"
695//usage: "\n"" -x Exit: runsv will exit as soon as the service is down"
696#if ENABLE_SVC
697int svc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
698int svc_main(int argc UNUSED_PARAM, char **argv)
699{
700 char command[2];
701 const char *optstring;
702 unsigned opts;
703
704 INIT_G();
705
706 optstring = "udopchaitkx";
707 opts = getopt32(argv, optstring);
708 argv += optind;
709 if (!argv[0] || !opts)
710 bb_show_usage();
711
712 argv -= 2;
713 if (optind > 2) {
714 argv--;
715 argv[2] = (char*)"--";
716 }
717 argv[0] = (char*)"sv";
718 argv[1] = command;
719 command[1] = '\0';
720
721 /* getopt32() was already called:
722 * reset the libc getopt() function, which keeps internal state.
723 */
Kaarle Ritvanen835ad3a2017-04-12 00:58:46 +0300724 GETOPT_RESET();
Denys Vlasenko4051a992016-12-05 13:56:40 +0100725
726 do {
727 if (opts & 1) {
728 int r;
729 command[0] = *optstring;
730 r = sv(argv);
731 if (r)
732 return 1;
733 }
734 optstring++;
735 opts >>= 1;
736 } while (opts);
737
738 return 0;
739}
740#endif