1 |
/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */ |
/* |
2 |
|
Copyright (c) 2001-2006, Gerrit Pape |
3 |
|
All rights reserved. |
4 |
|
|
5 |
|
Redistribution and use in source and binary forms, with or without |
6 |
|
modification, 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 |
|
|
16 |
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED |
17 |
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
18 |
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO |
19 |
|
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
20 |
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
21 |
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; |
22 |
|
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
23 |
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR |
24 |
|
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF |
25 |
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 |
|
*/ |
27 |
|
|
28 |
|
/* Taken from http://smarden.sunsite.dk/runit/sv.8.html: |
29 |
|
|
30 |
|
sv - control and manage services monitored by runsv |
31 |
|
|
32 |
|
sv [-v] [-w sec] command services |
33 |
|
/etc/init.d/service [-w sec] command |
34 |
|
|
35 |
|
The sv program reports the current status and controls the state of services |
36 |
|
monitored by the runsv(8) supervisor. |
37 |
|
|
38 |
|
services consists of one or more arguments, each argument naming a directory |
39 |
|
service used by runsv(8). If service doesn't start with a dot or slash, |
40 |
|
it is searched in the default services directory /var/service/, otherwise |
41 |
|
relative to the current directory. |
42 |
|
|
43 |
|
command is one of up, down, status, once, pause, cont, hup, alarm, interrupt, |
44 |
|
1, 2, term, kill, or exit, or start, stop, restart, shutdown, force-stop, |
45 |
|
force-reload, force-restart, force-shutdown. |
46 |
|
|
47 |
|
The sv program can be sym-linked to /etc/init.d/ to provide an LSB init |
48 |
|
script interface. The service to be controlled then is specified by the |
49 |
|
base name of the "init script". |
50 |
|
|
51 |
|
status |
52 |
|
Report the current status of the service, and the appendant log service |
53 |
|
if available, to standard output. |
54 |
|
up |
55 |
|
If the service is not running, start it. If the service stops, restart it. |
56 |
|
down |
57 |
|
If the service is running, send it the TERM signal, and the CONT signal. |
58 |
|
If ./run exits, start ./finish if it exists. After it stops, do not |
59 |
|
restart service. |
60 |
|
once |
61 |
|
If the service is not running, start it. Do not restart it if it stops. |
62 |
|
pause cont hup alarm interrupt quit 1 2 term kill |
63 |
|
If the service is running, send it the STOP, CONT, HUP, ALRM, INT, QUIT, |
64 |
|
USR1, USR2, TERM, or KILL signal respectively. |
65 |
|
exit |
66 |
|
If the service is running, send it the TERM signal, and the CONT signal. |
67 |
|
Do not restart the service. If the service is down, and no log service |
68 |
|
exists, runsv(8) exits. If the service is down and a log service exists, |
69 |
|
send the TERM signal to the log service. If the log service is down, |
70 |
|
runsv(8) exits. This command is ignored if it is given to an appendant |
71 |
|
log service. |
72 |
|
|
73 |
|
sv actually looks only at the first character of above commands. |
74 |
|
|
75 |
|
Commands compatible to LSB init script actions: |
76 |
|
|
77 |
|
status |
78 |
|
Same as status. |
79 |
|
start |
80 |
|
Same as up, but wait up to 7 seconds for the command to take effect. |
81 |
|
Then report the status or timeout. If the script ./check exists in |
82 |
|
the service directory, sv runs this script to check whether the service |
83 |
|
is up and available; it's considered to be available if ./check exits |
84 |
|
with 0. |
85 |
|
stop |
86 |
|
Same as down, but wait up to 7 seconds for the service to become down. |
87 |
|
Then report the status or timeout. |
88 |
|
restart |
89 |
|
Send the commands term, cont, and up to the service, and wait up to |
90 |
|
7 seconds for the service to restart. Then report the status or timeout. |
91 |
|
If the script ./check exists in the service directory, sv runs this script |
92 |
|
to check whether the service is up and available again; it's considered |
93 |
|
to be available if ./check exits with 0. |
94 |
|
shutdown |
95 |
|
Same as exit, but wait up to 7 seconds for the runsv(8) process |
96 |
|
to terminate. Then report the status or timeout. |
97 |
|
force-stop |
98 |
|
Same as down, but wait up to 7 seconds for the service to become down. |
99 |
|
Then report the status, and on timeout send the service the kill command. |
100 |
|
force-reload |
101 |
|
Send the service the term and cont commands, and wait up to |
102 |
|
7 seconds for the service to restart. Then report the status, |
103 |
|
and on timeout send the service the kill command. |
104 |
|
force-restart |
105 |
|
Send the service the term, cont and up commands, and wait up to |
106 |
|
7 seconds for the service to restart. Then report the status, and |
107 |
|
on timeout send the service the kill command. If the script ./check |
108 |
|
exists in the service directory, sv runs this script to check whether |
109 |
|
the service is up and available again; it's considered to be available |
110 |
|
if ./check exits with 0. |
111 |
|
force-shutdown |
112 |
|
Same as exit, but wait up to 7 seconds for the runsv(8) process to |
113 |
|
terminate. Then report the status, and on timeout send the service |
114 |
|
the kill command. |
115 |
|
|
116 |
|
Additional Commands |
117 |
|
|
118 |
|
check |
119 |
|
Check for the service to be in the state that's been requested. Wait up to |
120 |
|
7 seconds for the service to reach the requested state, then report |
121 |
|
the status or timeout. If the requested state of the service is up, |
122 |
|
and the script ./check exists in the service directory, sv runs |
123 |
|
this script to check whether the service is up and running; |
124 |
|
it's considered to be up if ./check exits with 0. |
125 |
|
|
126 |
|
Options |
127 |
|
|
128 |
|
-v |
129 |
|
wait up to 7 seconds for the command to take effect. |
130 |
|
Then report the status or timeout. |
131 |
|
-w sec |
132 |
|
Override the default timeout of 7 seconds with sec seconds. Implies -v. |
133 |
|
|
134 |
|
Environment |
135 |
|
|
136 |
|
SVDIR |
137 |
|
The environment variable $SVDIR overrides the default services directory |
138 |
|
/var/service. |
139 |
|
SVWAIT |
140 |
|
The environment variable $SVWAIT overrides the default 7 seconds to wait |
141 |
|
for a command to take effect. It is overridden by the -w option. |
142 |
|
|
143 |
|
Exit Codes |
144 |
|
sv exits 0, if the command was successfully sent to all services, and, |
145 |
|
if it was told to wait, the command has taken effect to all services. |
146 |
|
|
147 |
|
For each service that caused an error (e.g. the directory is not |
148 |
|
controlled by a runsv(8) process, or sv timed out while waiting), |
149 |
|
sv increases the exit code by one and exits non zero. The maximum |
150 |
|
is 99. sv exits 100 on error. |
151 |
|
*/ |
152 |
|
|
153 |
|
/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */ |
154 |
/* TODO: depends on runit_lib.c - review and reduce/eliminate */ |
/* TODO: depends on runit_lib.c - review and reduce/eliminate */ |
155 |
|
|
156 |
#include <sys/poll.h> |
#include <sys/poll.h> |
157 |
#include <sys/file.h> |
#include <sys/file.h> |
158 |
#include "busybox.h" |
#include "libbb.h" |
159 |
#include "runit_lib.h" |
#include "runit_lib.h" |
160 |
|
|
161 |
static char *action; |
struct globals { |
162 |
static char *acts; |
const char *acts; |
163 |
static char *varservice = "/var/service/"; |
char **service; |
164 |
static char **service; |
unsigned rc; |
165 |
static char **servicex; |
/* "Bernstein" time format: unix + 0x400000000000000aULL */ |
166 |
static unsigned services; |
uint64_t tstart, tnow; |
167 |
static unsigned rc = 0; |
svstatus_t svstatus; |
168 |
static unsigned verbose = 0; |
}; |
169 |
static unsigned long waitsec = 7; |
#define G (*(struct globals*)&bb_common_bufsiz1) |
170 |
static unsigned kll = 0; |
#define acts (G.acts ) |
171 |
static struct taia tstart, tnow, tdiff; |
#define service (G.service ) |
172 |
static struct tai tstatus; |
#define rc (G.rc ) |
173 |
|
#define tstart (G.tstart ) |
174 |
static int (*act)(char*) = 0; |
#define tnow (G.tnow ) |
175 |
static int (*cbk)(char*) = 0; |
#define svstatus (G.svstatus ) |
176 |
|
#define INIT_G() do { } while (0) |
|
static int curdir, fd, r; |
|
|
static char svstatus[20]; |
|
177 |
|
|
|
#define usage() bb_show_usage() |
|
178 |
|
|
179 |
static void fatal_cannot(char *m1) |
static void fatal_cannot(const char *m1) NORETURN; |
180 |
|
static void fatal_cannot(const char *m1) |
181 |
{ |
{ |
182 |
bb_perror_msg("fatal: cannot %s", m1); |
bb_perror_msg("fatal: can't %s", m1); |
183 |
_exit(151); |
_exit(151); |
184 |
} |
} |
185 |
|
|
186 |
static void out(char *p, char *m1) |
static void out(const char *p, const char *m1) |
187 |
{ |
{ |
188 |
printf("%s%s: %s", p, *service, m1); |
printf("%s%s: %s", p, *service, m1); |
189 |
if (errno) { |
if (errno) { |
190 |
printf(": %s", strerror(errno)); |
printf(": %s", strerror(errno)); |
191 |
} |
} |
192 |
puts(""); /* will also flush the output */ |
bb_putchar('\n'); /* will also flush the output */ |
193 |
} |
} |
194 |
|
|
|
#define FAIL "fail: " |
|
195 |
#define WARN "warning: " |
#define WARN "warning: " |
196 |
#define OK "ok: " |
#define OK "ok: " |
197 |
#define RUN "run: " |
|
198 |
#define FINISH "finish: " |
static void fail(const char *m1) |
199 |
#define DOWN "down: " |
{ |
200 |
#define TIMEOUT "timeout: " |
++rc; |
201 |
#define KILL "kill: " |
out("fail: ", m1); |
202 |
|
} |
203 |
static void fail(char *m1) { ++rc; out(FAIL, m1); } |
static void failx(const char *m1) |
204 |
static void failx(char *m1) { errno = 0; fail(m1); } |
{ |
205 |
static void warn_cannot(char *m1) { ++rc; out("warning: cannot ", m1); } |
errno = 0; |
206 |
static void warnx_cannot(char *m1) { errno = 0; warn_cannot(m1); } |
fail(m1); |
207 |
static void ok(char *m1) { errno = 0; out(OK, m1); } |
} |
208 |
|
static void warn(const char *m1) |
209 |
|
{ |
210 |
|
++rc; |
211 |
|
/* "warning: <service>: <m1>\n" */ |
212 |
|
out("warning: ", m1); |
213 |
|
} |
214 |
|
static void ok(const char *m1) |
215 |
|
{ |
216 |
|
errno = 0; |
217 |
|
out(OK, m1); |
218 |
|
} |
219 |
|
|
220 |
static int svstatus_get(void) |
static int svstatus_get(void) |
221 |
{ |
{ |
222 |
if ((fd = open_write("supervise/ok")) == -1) { |
int fd, r; |
223 |
|
|
224 |
|
fd = open_write("supervise/ok"); |
225 |
|
if (fd == -1) { |
226 |
if (errno == ENODEV) { |
if (errno == ENODEV) { |
227 |
*acts == 'x' ? ok("runsv not running") |
*acts == 'x' ? ok("runsv not running") |
228 |
: failx("runsv not running"); |
: failx("runsv not running"); |
229 |
return 0; |
return 0; |
230 |
} |
} |
231 |
warn_cannot("open supervise/ok"); |
warn("cannot open supervise/ok"); |
232 |
return -1; |
return -1; |
233 |
} |
} |
234 |
close(fd); |
close(fd); |
235 |
if ((fd = open_read("supervise/status")) == -1) { |
fd = open_read("supervise/status"); |
236 |
warn_cannot("open supervise/status"); |
if (fd == -1) { |
237 |
|
warn("cannot open supervise/status"); |
238 |
return -1; |
return -1; |
239 |
} |
} |
240 |
r = read(fd, svstatus, 20); |
r = read(fd, &svstatus, 20); |
241 |
close(fd); |
close(fd); |
242 |
switch (r) { |
switch (r) { |
243 |
case 20: break; |
case 20: |
244 |
case -1: warn_cannot("read supervise/status"); return -1; |
break; |
245 |
default: warnx_cannot("read supervise/status: bad format"); return -1; |
case -1: |
246 |
|
warn("cannot read supervise/status"); |
247 |
|
return -1; |
248 |
|
default: |
249 |
|
errno = 0; |
250 |
|
warn("cannot read supervise/status: bad format"); |
251 |
|
return -1; |
252 |
} |
} |
253 |
return 1; |
return 1; |
254 |
} |
} |
255 |
|
|
256 |
static unsigned svstatus_print(char *m) |
static unsigned svstatus_print(const char *m) |
257 |
{ |
{ |
258 |
|
int diff; |
259 |
int pid; |
int pid; |
260 |
int normallyup = 0; |
int normallyup = 0; |
261 |
struct stat s; |
struct stat s; |
262 |
|
uint64_t timestamp; |
263 |
|
|
264 |
if (stat("down", &s) == -1) { |
if (stat("down", &s) == -1) { |
265 |
if (errno != ENOENT) { |
if (errno != ENOENT) { |
268 |
} |
} |
269 |
normallyup = 1; |
normallyup = 1; |
270 |
} |
} |
271 |
pid = (unsigned char) svstatus[15]; |
pid = SWAP_LE32(svstatus.pid_le32); |
272 |
pid <<= 8; pid += (unsigned char)svstatus[14]; |
timestamp = SWAP_BE64(svstatus.time_be64); |
|
pid <<= 8; pid += (unsigned char)svstatus[13]; |
|
|
pid <<= 8; pid += (unsigned char)svstatus[12]; |
|
|
tai_unpack(svstatus, &tstatus); |
|
273 |
if (pid) { |
if (pid) { |
274 |
switch (svstatus[19]) { |
switch (svstatus.run_or_finish) { |
275 |
case 1: printf(RUN); break; |
case 1: printf("run: "); break; |
276 |
case 2: printf(FINISH); break; |
case 2: printf("finish: "); break; |
277 |
} |
} |
278 |
printf("%s: (pid %d) ", m, pid); |
printf("%s: (pid %d) ", m, pid); |
279 |
|
} else { |
280 |
|
printf("down: %s: ", m); |
281 |
} |
} |
282 |
else { |
diff = tnow - timestamp; |
283 |
printf(DOWN"%s: ", m); |
printf("%us", (diff < 0 ? 0 : diff)); |
284 |
|
if (pid) { |
285 |
|
if (!normallyup) printf(", normally down"); |
286 |
|
if (svstatus.paused) printf(", paused"); |
287 |
|
if (svstatus.want == 'd') printf(", want down"); |
288 |
|
if (svstatus.got_term) printf(", got TERM"); |
289 |
|
} else { |
290 |
|
if (normallyup) printf(", normally up"); |
291 |
|
if (svstatus.want == 'u') printf(", want up"); |
292 |
} |
} |
|
printf("%lus", (unsigned long)(tnow.sec.x < tstatus.x ? 0 : tnow.sec.x-tstatus.x)); |
|
|
if (pid && !normallyup) printf(", normally down"); |
|
|
if (!pid && normallyup) printf(", normally up"); |
|
|
if (pid && svstatus[16]) printf(", paused"); |
|
|
if (!pid && (svstatus[17] == 'u')) printf(", want up"); |
|
|
if (pid && (svstatus[17] == 'd')) printf(", want down"); |
|
|
if (pid && svstatus[18]) printf(", got TERM"); |
|
293 |
return pid ? 1 : 2; |
return pid ? 1 : 2; |
294 |
} |
} |
295 |
|
|
296 |
static int status(char *unused) |
static int status(const char *unused UNUSED_PARAM) |
297 |
{ |
{ |
298 |
|
int r; |
299 |
|
|
300 |
r = svstatus_get(); |
r = svstatus_get(); |
301 |
switch (r) { case -1: case 0: return 0; } |
switch (r) { case -1: case 0: return 0; } |
302 |
|
|
303 |
r = svstatus_print(*service); |
r = svstatus_print(*service); |
304 |
if (chdir("log") == -1) { |
if (chdir("log") == -1) { |
305 |
if (errno != ENOENT) { |
if (errno != ENOENT) { |
310 |
printf("; "); |
printf("; "); |
311 |
svstatus_print("log"); |
svstatus_print("log"); |
312 |
} |
} |
313 |
puts(""); /* will also flush the output */ |
bb_putchar('\n'); /* will also flush the output */ |
314 |
return r; |
return r; |
315 |
} |
} |
316 |
|
|
326 |
return 0; |
return 0; |
327 |
} |
} |
328 |
/* if (!(s.st_mode & S_IXUSR)) return 1; */ |
/* if (!(s.st_mode & S_IXUSR)) return 1; */ |
329 |
if ((pid = fork()) == -1) { |
prog[0] = (char*)"./check"; |
330 |
bb_perror_msg(WARN"cannot fork for %s/check", *service); |
prog[1] = NULL; |
331 |
|
pid = spawn(prog); |
332 |
|
if (pid <= 0) { |
333 |
|
bb_perror_msg(WARN"cannot %s child %s/check", "run", *service); |
334 |
return 0; |
return 0; |
335 |
} |
} |
336 |
if (!pid) { |
while (safe_waitpid(pid, &w, 0) == -1) { |
337 |
prog[0] = "./check"; |
bb_perror_msg(WARN"cannot %s child %s/check", "wait for", *service); |
|
prog[1] = 0; |
|
|
close(1); |
|
|
execve("check", prog, environ); |
|
|
bb_perror_msg(WARN"cannot run %s/check", *service); |
|
|
_exit(0); |
|
|
} |
|
|
while (wait_pid(&w, pid) == -1) { |
|
|
if (errno == EINTR) continue; |
|
|
bb_perror_msg(WARN"cannot wait for child %s/check", *service); |
|
338 |
return 0; |
return 0; |
339 |
} |
} |
340 |
return !wait_exitcode(w); |
return !wait_exitcode(w); |
341 |
} |
} |
342 |
|
|
343 |
static int check(char *a) |
static int check(const char *a) |
344 |
{ |
{ |
345 |
|
int r; |
346 |
unsigned pid; |
unsigned pid; |
347 |
|
uint64_t timestamp; |
348 |
|
|
349 |
if ((r = svstatus_get()) == -1) return -1; |
r = svstatus_get(); |
350 |
if (r == 0) { if (*a == 'x') return 1; return -1; } |
if (r == -1) |
351 |
pid = (unsigned char)svstatus[15]; |
return -1; |
352 |
pid <<= 8; pid += (unsigned char)svstatus[14]; |
if (r == 0) { |
353 |
pid <<= 8; pid += (unsigned char)svstatus[13]; |
if (*a == 'x') |
354 |
pid <<= 8; pid += (unsigned char)svstatus[12]; |
return 1; |
355 |
|
return -1; |
356 |
|
} |
357 |
|
pid = SWAP_LE32(svstatus.pid_le32); |
358 |
switch (*a) { |
switch (*a) { |
359 |
case 'x': return 0; |
case 'x': |
360 |
|
return 0; |
361 |
case 'u': |
case 'u': |
362 |
if (!pid || svstatus[19] != 1) return 0; |
if (!pid || svstatus.run_or_finish != 1) return 0; |
363 |
if (!checkscript()) return 0; |
if (!checkscript()) return 0; |
364 |
break; |
break; |
365 |
case 'd': if (pid) return 0; break; |
case 'd': |
366 |
case 'c': if (pid) if (!checkscript()) return 0; break; |
if (pid) return 0; |
367 |
|
break; |
368 |
|
case 'c': |
369 |
|
if (pid && !checkscript()) return 0; |
370 |
|
break; |
371 |
case 't': |
case 't': |
372 |
if (!pid && svstatus[17] == 'd') break; |
if (!pid && svstatus.want == 'd') break; |
373 |
tai_unpack(svstatus, &tstatus); |
timestamp = SWAP_BE64(svstatus.time_be64); |
374 |
if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript()) |
if ((tstart > timestamp) || !pid || svstatus.got_term || !checkscript()) |
375 |
return 0; |
return 0; |
376 |
break; |
break; |
377 |
case 'o': |
case 'o': |
378 |
tai_unpack(svstatus, &tstatus); |
timestamp = SWAP_BE64(svstatus.time_be64); |
379 |
if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd')) |
if ((!pid && tstart > timestamp) || (pid && svstatus.want != 'd')) |
380 |
return 0; |
return 0; |
381 |
} |
} |
382 |
printf(OK); svstatus_print(*service); puts(""); /* will also flush the output */ |
printf(OK); |
383 |
|
svstatus_print(*service); |
384 |
|
bb_putchar('\n'); /* will also flush the output */ |
385 |
return 1; |
return 1; |
386 |
} |
} |
387 |
|
|
388 |
static int control(char *a) |
static int control(const char *a) |
389 |
{ |
{ |
390 |
if (svstatus_get() <= 0) return -1; |
int fd, r; |
391 |
if (svstatus[17] == *a) return 0; |
|
392 |
if ((fd = open_write("supervise/control")) == -1) { |
if (svstatus_get() <= 0) |
393 |
|
return -1; |
394 |
|
if (svstatus.want == *a) |
395 |
|
return 0; |
396 |
|
fd = open_write("supervise/control"); |
397 |
|
if (fd == -1) { |
398 |
if (errno != ENODEV) |
if (errno != ENODEV) |
399 |
warn_cannot("open supervise/control"); |
warn("cannot open supervise/control"); |
400 |
else |
else |
401 |
*a == 'x' ? ok("runsv not running") : failx("runsv not running"); |
*a == 'x' ? ok("runsv not running") : failx("runsv not running"); |
402 |
return -1; |
return -1; |
404 |
r = write(fd, a, strlen(a)); |
r = write(fd, a, strlen(a)); |
405 |
close(fd); |
close(fd); |
406 |
if (r != strlen(a)) { |
if (r != strlen(a)) { |
407 |
warn_cannot("write to supervise/control"); |
warn("cannot write to supervise/control"); |
408 |
return -1; |
return -1; |
409 |
} |
} |
410 |
return 1; |
return 1; |
411 |
} |
} |
412 |
|
|
413 |
|
int sv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
414 |
int sv_main(int argc, char **argv) |
int sv_main(int argc, char **argv) |
415 |
{ |
{ |
416 |
unsigned opt; |
unsigned opt; |
417 |
unsigned i, want_exit; |
unsigned i, want_exit; |
418 |
char *x; |
char *x; |
419 |
|
char *action; |
420 |
for (i = strlen(*argv); i; --i) |
const char *varservice = CONFIG_SV_DEFAULT_SERVICE_DIR; |
421 |
if ((*argv)[i-1] == '/') |
unsigned services; |
422 |
break; |
char **servicex; |
423 |
*argv += i; |
unsigned waitsec = 7; |
424 |
|
smallint kll = 0; |
425 |
|
int verbose = 0; |
426 |
|
int (*act)(const char*); |
427 |
|
int (*cbk)(const char*); |
428 |
|
int curdir; |
429 |
|
|
430 |
|
INIT_G(); |
431 |
|
|
432 |
|
xfunc_error_retval = 100; |
433 |
|
|
434 |
|
x = getenv("SVDIR"); |
435 |
|
if (x) varservice = x; |
436 |
|
x = getenv("SVWAIT"); |
437 |
|
if (x) waitsec = xatou(x); |
438 |
|
|
439 |
|
opt_complementary = "w+:vv"; /* -w N, -v is a counter */ |
440 |
|
opt = getopt32(argv, "w:v", &waitsec, &verbose); |
441 |
|
argc -= optind; |
442 |
|
argv += optind; |
443 |
|
action = *argv++; |
444 |
|
if (!action || !*argv) bb_show_usage(); |
445 |
service = argv; |
service = argv; |
446 |
services = 1; |
services = argc - 1; |
|
if ((x = getenv("SVDIR"))) varservice = x; |
|
|
if ((x = getenv("SVWAIT"))) waitsec = xatoul(x); |
|
|
/* TODO: V can be handled internally by getopt_ulflags */ |
|
|
opt = getopt32(argc, argv, "w:vV", &x); |
|
|
if (opt & 1) waitsec = xatoul(x); |
|
|
if (opt & 2) verbose = 1; |
|
|
if (opt & 4) usage(); |
|
|
if (!(action = *argv++)) usage(); |
|
|
--argc; |
|
|
service = argv; services = argc; |
|
|
if (!*service) usage(); |
|
447 |
|
|
448 |
taia_now(&tnow); tstart = tnow; |
tnow = time(0) + 0x400000000000000aULL; |
449 |
if ((curdir = open_read(".")) == -1) |
tstart = tnow; |
450 |
|
curdir = open_read("."); |
451 |
|
if (curdir == -1) |
452 |
fatal_cannot("open current directory"); |
fatal_cannot("open current directory"); |
453 |
|
|
454 |
act = &control; acts = "s"; |
act = &control; |
455 |
if (verbose) cbk = ✓ |
acts = "s"; |
456 |
|
cbk = ✓ |
457 |
|
|
458 |
switch (*action) { |
switch (*action) { |
459 |
case 'x': case 'e': |
case 'x': |
460 |
acts = "x"; break; |
case 'e': |
461 |
case 'X': case 'E': |
acts = "x"; |
462 |
acts = "x"; kll = 1; cbk = ✓ break; |
if (!verbose) cbk = NULL; |
463 |
|
break; |
464 |
|
case 'X': |
465 |
|
case 'E': |
466 |
|
acts = "x"; |
467 |
|
kll = 1; |
468 |
|
break; |
469 |
case 'D': |
case 'D': |
470 |
acts = "d"; kll = 1; cbk = ✓ break; |
acts = "d"; |
471 |
|
kll = 1; |
472 |
|
break; |
473 |
case 'T': |
case 'T': |
474 |
acts = "tc"; kll = 1; cbk = ✓ break; |
acts = "tc"; |
475 |
|
kll = 1; |
476 |
|
break; |
477 |
case 'c': |
case 'c': |
478 |
if (!str_diff(action, "check")) { |
if (str_equal(action, "check")) { |
479 |
act = 0; |
act = NULL; |
480 |
acts = "c"; |
acts = "c"; |
|
cbk = ✓ |
|
481 |
break; |
break; |
482 |
} |
} |
483 |
case 'u': case 'd': case 'o': case 't': case 'p': case 'h': |
case 'u': case 'd': case 'o': case 't': case 'p': case 'h': |
484 |
case 'a': case 'i': case 'k': case 'q': case '1': case '2': |
case 'a': case 'i': case 'k': case 'q': case '1': case '2': |
485 |
action[1] = 0; acts = action; break; |
action[1] = '\0'; |
486 |
|
acts = action; |
487 |
|
if (!verbose) cbk = NULL; |
488 |
|
break; |
489 |
case 's': |
case 's': |
490 |
if (!str_diff(action, "shutdown")) { |
if (str_equal(action, "shutdown")) { |
491 |
acts = "x"; |
acts = "x"; |
|
cbk = ✓ |
|
492 |
break; |
break; |
493 |
} |
} |
494 |
if (!str_diff(action, "start")) { |
if (str_equal(action, "start")) { |
495 |
acts = "u"; |
acts = "u"; |
|
cbk = ✓ |
|
496 |
break; |
break; |
497 |
} |
} |
498 |
if (!str_diff(action, "stop")) { |
if (str_equal(action, "stop")) { |
499 |
acts = "d"; |
acts = "d"; |
|
cbk = ✓ |
|
500 |
break; |
break; |
501 |
} |
} |
502 |
act = &status; cbk = 0; break; |
/* "status" */ |
503 |
|
act = &status; |
504 |
|
cbk = NULL; |
505 |
|
break; |
506 |
case 'r': |
case 'r': |
507 |
if (!str_diff(action, "restart")) { |
if (str_equal(action, "restart")) { |
508 |
acts = "tcu"; |
acts = "tcu"; |
|
cbk = ✓ |
|
509 |
break; |
break; |
510 |
} |
} |
511 |
usage(); |
bb_show_usage(); |
512 |
case 'f': |
case 'f': |
513 |
if (!str_diff(action, "force-reload")) |
if (str_equal(action, "force-reload")) { |
514 |
{ acts = "tc"; kll = 1; cbk = ✓ break; } |
acts = "tc"; |
515 |
if (!str_diff(action, "force-restart")) |
kll = 1; |
516 |
{ acts = "tcu"; kll = 1; cbk = ✓ break; } |
break; |
517 |
if (!str_diff(action, "force-shutdown")) |
} |
518 |
{ acts = "x"; kll = 1; cbk = ✓ break; } |
if (str_equal(action, "force-restart")) { |
519 |
if (!str_diff(action, "force-stop")) |
acts = "tcu"; |
520 |
{ acts = "d"; kll = 1; cbk = ✓ break; } |
kll = 1; |
521 |
|
break; |
522 |
|
} |
523 |
|
if (str_equal(action, "force-shutdown")) { |
524 |
|
acts = "x"; |
525 |
|
kll = 1; |
526 |
|
break; |
527 |
|
} |
528 |
|
if (str_equal(action, "force-stop")) { |
529 |
|
acts = "d"; |
530 |
|
kll = 1; |
531 |
|
break; |
532 |
|
} |
533 |
default: |
default: |
534 |
usage(); |
bb_show_usage(); |
535 |
} |
} |
536 |
|
|
537 |
servicex = service; |
servicex = service; |
538 |
for (i = 0; i < services; ++i) { |
for (i = 0; i < services; ++i) { |
539 |
if ((**service != '/') && (**service != '.')) { |
if ((**service != '/') && (**service != '.')) { |
540 |
if ((chdir(varservice) == -1) || (chdir(*service) == -1)) { |
if (chdir(varservice) == -1) |
541 |
fail("cannot change to service directory"); |
goto chdir_failed_0; |
542 |
*service = 0; |
} |
543 |
} |
if (chdir(*service) == -1) { |
544 |
} else if (chdir(*service) == -1) { |
chdir_failed_0: |
545 |
fail("cannot change to service directory"); |
fail("cannot change to service directory"); |
546 |
*service = 0; |
goto nullify_service_0; |
547 |
} |
} |
548 |
if (*service) if (act && (act(acts) == -1)) *service = 0; |
if (act && (act(acts) == -1)) { |
549 |
if (fchdir(curdir) == -1) fatal_cannot("change to original directory"); |
nullify_service_0: |
550 |
|
*service = NULL; |
551 |
|
} |
552 |
|
if (fchdir(curdir) == -1) |
553 |
|
fatal_cannot("change to original directory"); |
554 |
service++; |
service++; |
555 |
} |
} |
556 |
|
|
557 |
if (*cbk) |
if (cbk) while (1) { |
558 |
for (;;) { |
int diff; |
559 |
taia_sub(&tdiff, &tnow, &tstart); |
|
560 |
service = servicex; want_exit = 1; |
diff = tnow - tstart; |
561 |
for (i = 0; i < services; ++i, ++service) { |
service = servicex; |
562 |
if (!*service) continue; |
want_exit = 1; |
563 |
if ((**service != '/') && (**service != '.')) { |
for (i = 0; i < services; ++i, ++service) { |
564 |
if ((chdir(varservice) == -1) || (chdir(*service) == -1)) { |
if (!*service) |
565 |
fail("cannot change to service directory"); |
continue; |
566 |
*service = 0; |
if ((**service != '/') && (**service != '.')) { |
567 |
} |
if (chdir(varservice) == -1) |
568 |
} else if (chdir(*service) == -1) { |
goto chdir_failed; |
569 |
fail("cannot change to service directory"); |
} |
570 |
*service = 0; |
if (chdir(*service) == -1) { |
571 |
} |
chdir_failed: |
572 |
if (*service) { if (cbk(acts) != 0) *service = 0; else want_exit = 0; } |
fail("cannot change to service directory"); |
573 |
if (*service && taia_approx(&tdiff) > waitsec) { |
goto nullify_service; |
574 |
kll ? printf(KILL) : printf(TIMEOUT); |
} |
575 |
if (svstatus_get() > 0) { svstatus_print(*service); ++rc; } |
if (cbk(acts) != 0) |
576 |
puts(""); /* will also flush the output */ |
goto nullify_service; |
577 |
if (kll) control("k"); |
want_exit = 0; |
578 |
*service = 0; |
if (diff >= waitsec) { |
579 |
|
printf(kll ? "kill: " : "timeout: "); |
580 |
|
if (svstatus_get() > 0) { |
581 |
|
svstatus_print(*service); |
582 |
|
++rc; |
583 |
} |
} |
584 |
if (fchdir(curdir) == -1) |
bb_putchar('\n'); /* will also flush the output */ |
585 |
fatal_cannot("change to original directory"); |
if (kll) |
586 |
|
control("k"); |
587 |
|
nullify_service: |
588 |
|
*service = NULL; |
589 |
} |
} |
590 |
if (want_exit) break; |
if (fchdir(curdir) == -1) |
591 |
usleep(420000); |
fatal_cannot("change to original directory"); |
|
taia_now(&tnow); |
|
592 |
} |
} |
593 |
|
if (want_exit) break; |
594 |
|
usleep(420000); |
595 |
|
tnow = time(0) + 0x400000000000000aULL; |
596 |
|
} |
597 |
return rc > 99 ? 99 : rc; |
return rc > 99 ? 99 : rc; |
598 |
} |
} |