blob: 2af9f6604feb79211d16dda0be479ab44594feb5 [file] [log] [blame]
Denis Vlasenko04c63862006-11-17 18:58:49 +00001/* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */
2/* TODO: depends on runit_lib.c - review and reduce/eliminate */
3
4#include <sys/poll.h>
5#include <sys/file.h>
6#include "busybox.h"
7#include "runit_lib.h"
8
Denis Vlasenko322661d2007-01-29 23:43:52 +00009static const char *acts;
Denis Vlasenko04c63862006-11-17 18:58:49 +000010static char **service;
Denis Vlasenko322661d2007-01-29 23:43:52 +000011static unsigned rc;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +000012static struct taia tstart, tnow;
Denis Vlasenko04c63862006-11-17 18:58:49 +000013static char svstatus[20];
14
15#define usage() bb_show_usage()
16
Denis Vlasenko7fca91a32007-02-02 01:19:09 +000017static void fatal_cannot(const char *m1) ATTRIBUTE_NORETURN;
Denis Vlasenko322661d2007-01-29 23:43:52 +000018static void fatal_cannot(const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +000019{
20 bb_perror_msg("fatal: cannot %s", m1);
21 _exit(151);
22}
23
Denis Vlasenko322661d2007-01-29 23:43:52 +000024static void out(const char *p, const char *m1)
Denis Vlasenko04c63862006-11-17 18:58:49 +000025{
26 printf("%s%s: %s", p, *service, m1);
27 if (errno) {
28 printf(": %s", strerror(errno));
29 }
30 puts(""); /* will also flush the output */
31}
32
Denis Vlasenko04c63862006-11-17 18:58:49 +000033#define WARN "warning: "
34#define OK "ok: "
Denis Vlasenko04c63862006-11-17 18:58:49 +000035
Denis Vlasenko7fca91a32007-02-02 01:19:09 +000036static void fail(const char *m1) {
37 ++rc;
38 out("fail: ", m1);
39}
40static void failx(const char *m1) {
41 errno = 0;
42 fail(m1);
43}
44static void warn_cannot(const char *m1) {
45 ++rc;
46 out("warning: cannot ", m1);
47}
48static void warnx_cannot(const char *m1) {
49 errno = 0;
50 warn_cannot(m1);
51}
52static void ok(const char *m1) {
53 errno = 0;
54 out(OK, m1);
55}
Denis Vlasenko04c63862006-11-17 18:58:49 +000056
57static int svstatus_get(void)
58{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +000059 int fd, r;
60
Denis Vlasenko322661d2007-01-29 23:43:52 +000061 fd = open_write("supervise/ok");
62 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +000063 if (errno == ENODEV) {
64 *acts == 'x' ? ok("runsv not running")
65 : failx("runsv not running");
66 return 0;
67 }
68 warn_cannot("open supervise/ok");
69 return -1;
70 }
71 close(fd);
Denis Vlasenko322661d2007-01-29 23:43:52 +000072 fd = open_read("supervise/status");
73 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +000074 warn_cannot("open supervise/status");
75 return -1;
76 }
77 r = read(fd, svstatus, 20);
78 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000079 switch (r) {
Denis Vlasenko04c63862006-11-17 18:58:49 +000080 case 20: break;
81 case -1: warn_cannot("read supervise/status"); return -1;
82 default: warnx_cannot("read supervise/status: bad format"); return -1;
83 }
84 return 1;
85}
86
Denis Vlasenko322661d2007-01-29 23:43:52 +000087static unsigned svstatus_print(const char *m)
Denis Vlasenko04c63862006-11-17 18:58:49 +000088{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +000089 long diff;
Denis Vlasenko04c63862006-11-17 18:58:49 +000090 int pid;
91 int normallyup = 0;
92 struct stat s;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +000093 struct tai tstatus;
Denis Vlasenkof7996f32007-01-11 17:20:00 +000094
Denis Vlasenko04c63862006-11-17 18:58:49 +000095 if (stat("down", &s) == -1) {
96 if (errno != ENOENT) {
97 bb_perror_msg(WARN"cannot stat %s/down", *service);
98 return 0;
99 }
100 normallyup = 1;
101 }
102 pid = (unsigned char) svstatus[15];
103 pid <<= 8; pid += (unsigned char)svstatus[14];
104 pid <<= 8; pid += (unsigned char)svstatus[13];
105 pid <<= 8; pid += (unsigned char)svstatus[12];
106 tai_unpack(svstatus, &tstatus);
107 if (pid) {
108 switch (svstatus[19]) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000109 case 1: printf("run: "); break;
110 case 2: printf("finish: "); break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000111 }
112 printf("%s: (pid %d) ", m, pid);
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000113 } else {
114 printf("down: %s: ", m);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000115 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000116 diff = tnow.sec.x - tstatus.x;
117 printf("%lds", (diff < 0 ? 0L : diff));
118 if (pid) {
119 if (!normallyup) printf(", normally down");
120 } else {
121 if (normallyup) printf(", normally up");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000122 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000123 if (pid && svstatus[16]) printf(", paused");
124 if (!pid && (svstatus[17] == 'u')) printf(", want up");
125 if (pid && (svstatus[17] == 'd')) printf(", want down");
126 if (pid && svstatus[18]) printf(", got TERM");
127 return pid ? 1 : 2;
128}
129
Denis Vlasenko322661d2007-01-29 23:43:52 +0000130static int status(const char *unused)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000131{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000132 int r;
133
Denis Vlasenko04c63862006-11-17 18:58:49 +0000134 r = svstatus_get();
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000135 switch (r) { case -1: case 0: return 0; }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000136
Denis Vlasenko04c63862006-11-17 18:58:49 +0000137 r = svstatus_print(*service);
138 if (chdir("log") == -1) {
139 if (errno != ENOENT) {
140 printf("; log: "WARN"cannot change to log service directory: %s",
141 strerror(errno));
142 }
143 } else if (svstatus_get()) {
144 printf("; ");
145 svstatus_print("log");
146 }
147 puts(""); /* will also flush the output */
148 return r;
149}
150
151static int checkscript(void)
152{
153 char *prog[2];
154 struct stat s;
155 int pid, w;
156
157 if (stat("check", &s) == -1) {
158 if (errno == ENOENT) return 1;
159 bb_perror_msg(WARN"cannot stat %s/check", *service);
160 return 0;
161 }
162 /* if (!(s.st_mode & S_IXUSR)) return 1; */
163 if ((pid = fork()) == -1) {
164 bb_perror_msg(WARN"cannot fork for %s/check", *service);
165 return 0;
166 }
167 if (!pid) {
Denis Vlasenko322661d2007-01-29 23:43:52 +0000168 prog[0] = (char*)"./check";
169 prog[1] = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000170 close(1);
171 execve("check", prog, environ);
172 bb_perror_msg(WARN"cannot run %s/check", *service);
173 _exit(0);
174 }
175 while (wait_pid(&w, pid) == -1) {
176 if (errno == EINTR) continue;
177 bb_perror_msg(WARN"cannot wait for child %s/check", *service);
178 return 0;
179 }
180 return !wait_exitcode(w);
181}
182
Denis Vlasenko322661d2007-01-29 23:43:52 +0000183static int check(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000184{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000185 int r;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000186 unsigned pid;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000187 struct tai tstatus;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000188
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000189 r = svstatus_get();
190 if (r == -1)
191 return -1;
192 if (r == 0) {
193 if (*a == 'x')
194 return 1;
195 return -1;
196 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000197 pid = (unsigned char)svstatus[15];
198 pid <<= 8; pid += (unsigned char)svstatus[14];
199 pid <<= 8; pid += (unsigned char)svstatus[13];
200 pid <<= 8; pid += (unsigned char)svstatus[12];
201 switch (*a) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000202 case 'x':
203 return 0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000204 case 'u':
205 if (!pid || svstatus[19] != 1) return 0;
206 if (!checkscript()) return 0;
207 break;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000208 case 'd':
209 if (pid) return 0;
210 break;
211 case 'c':
212 if (pid && !checkscript()) return 0;
213 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000214 case 't':
215 if (!pid && svstatus[17] == 'd') break;
216 tai_unpack(svstatus, &tstatus);
217 if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript())
218 return 0;
219 break;
220 case 'o':
221 tai_unpack(svstatus, &tstatus);
222 if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd'))
223 return 0;
224 }
Denis Vlasenko322661d2007-01-29 23:43:52 +0000225 printf(OK);
226 svstatus_print(*service);
227 puts(""); /* will also flush the output */
Denis Vlasenko04c63862006-11-17 18:58:49 +0000228 return 1;
229}
230
Denis Vlasenko322661d2007-01-29 23:43:52 +0000231static int control(const char *a)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000232{
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000233 int fd, r;
234
Denis Vlasenko04c63862006-11-17 18:58:49 +0000235 if (svstatus_get() <= 0) return -1;
236 if (svstatus[17] == *a) return 0;
Denis Vlasenko322661d2007-01-29 23:43:52 +0000237 fd = open_write("supervise/control");
238 if (fd == -1) {
Denis Vlasenko04c63862006-11-17 18:58:49 +0000239 if (errno != ENODEV)
240 warn_cannot("open supervise/control");
241 else
242 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
243 return -1;
244 }
245 r = write(fd, a, strlen(a));
246 close(fd);
247 if (r != strlen(a)) {
248 warn_cannot("write to supervise/control");
249 return -1;
250 }
251 return 1;
252}
253
254int sv_main(int argc, char **argv)
255{
256 unsigned opt;
257 unsigned i, want_exit;
258 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000259 char *action;
260 const char *varservice = "/var/service/";
261 unsigned services;
262 char **servicex;
263 unsigned long waitsec = 7;
264 smallint kll = 0;
265 smallint verbose = 0;
266 int (*act)(const char*);
267 int (*cbk)(const char*);
268 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000269
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000270 xfunc_error_retval = 100;
271
272 x = getenv("SVDIR");
273 if (x) varservice = x;
274 x = getenv("SVWAIT");
275 if (x) waitsec = xatoul(x);
276
277 opt = getopt32(argc, argv, "w:v", &x);
278 if (opt & 1) waitsec = xatoul(x); // -w
279 if (opt & 2) verbose = 1; // -v
280 argc -= optind;
281 argv += optind;
282 action = *argv++;
283 if (!action || !*argv) usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000284 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000285 services = argc - 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000286
Denis Vlasenko322661d2007-01-29 23:43:52 +0000287 taia_now(&tnow);
288 tstart = tnow;
289 curdir = open_read(".");
290 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000291 fatal_cannot("open current directory");
292
Denis Vlasenko322661d2007-01-29 23:43:52 +0000293 act = &control;
294 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000295 cbk = &check;
296
Denis Vlasenko04c63862006-11-17 18:58:49 +0000297 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000298 case 'x':
299 case 'e':
300 acts = "x";
301 if (!verbose) cbk = NULL;
302 break;
303 case 'X':
304 case 'E':
305 acts = "x";
306 kll = 1;
307 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000308 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000309 acts = "d";
310 kll = 1;
311 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000312 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000313 acts = "tc";
314 kll = 1;
315 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000316 case 'c':
317 if (!str_diff(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000318 act = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000319 acts = "c";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000320 break;
321 }
322 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
323 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000324 action[1] = '\0';
325 acts = action;
326 if (!verbose) cbk = NULL;
327 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000328 case 's':
329 if (!str_diff(action, "shutdown")) {
330 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000331 break;
332 }
333 if (!str_diff(action, "start")) {
334 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000335 break;
336 }
337 if (!str_diff(action, "stop")) {
338 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000339 break;
340 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000341 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000342 act = &status;
343 cbk = NULL;
344 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000345 case 'r':
346 if (!str_diff(action, "restart")) {
347 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000348 break;
349 }
350 usage();
351 case 'f':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000352 if (!str_diff(action, "force-reload")) {
353 acts = "tc";
354 kll = 1;
355 break;
356 }
357 if (!str_diff(action, "force-restart")) {
358 acts = "tcu";
359 kll = 1;
360 break;
361 }
362 if (!str_diff(action, "force-shutdown")) {
363 acts = "x";
364 kll = 1;
365 break;
366 }
367 if (!str_diff(action, "force-stop")) {
368 acts = "d";
369 kll = 1;
370 break;
371 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000372 default:
373 usage();
374 }
375
376 servicex = service;
377 for (i = 0; i < services; ++i) {
378 if ((**service != '/') && (**service != '.')) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000379 if (chdir(varservice) == -1)
380 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000381 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000382 if (chdir(*service) == -1) {
383 chdir_failed_0:
384 fail("cannot change to service directory");
385 goto nullify_service_0;
386 }
387 if (act && (act(acts) == -1)) {
388 nullify_service_0:
389 *service = NULL;
390 }
391 if (fchdir(curdir) == -1)
392 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000393 service++;
394 }
395
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000396 if (cbk) while (1) {
397 //struct taia tdiff;
398 long diff;
399
400 //taia_sub(&tdiff, &tnow, &tstart);
401 diff = tnow.sec.x - tstart.sec.x;
402 service = servicex;
403 want_exit = 1;
404 for (i = 0; i < services; ++i, ++service) {
405 if (!*service)
406 continue;
407 if ((**service != '/') && (**service != '.')) {
408 if (chdir(varservice) == -1)
409 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000410 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000411 if (chdir(*service) == -1) {
412 chdir_failed:
413 fail("cannot change to service directory");
414 goto nullify_service;
415 }
416 if (cbk(acts) != 0)
417 goto nullify_service;
418 want_exit = 0;
419 if (diff >= waitsec) {
420 printf(kll ? "kill: " : "timeout: ");
421 if (svstatus_get() > 0) {
422 svstatus_print(*service);
423 ++rc;
424 }
425 puts(""); /* will also flush the output */
426 if (kll)
427 control("k");
428 nullify_service:
429 *service = NULL;
430 }
431 if (fchdir(curdir) == -1)
432 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000433 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000434 if (want_exit) break;
435 usleep(420000);
436 taia_now(&tnow);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000437 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000438 return rc > 99 ? 99 : rc;
439}