blob: 7635330e4d73037295ab77964ceeafd5a654c985 [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
Denis Vlasenko06af2162007-02-03 17:28:39 +0000254int sv_main(int argc, char **argv);
Denis Vlasenko04c63862006-11-17 18:58:49 +0000255int sv_main(int argc, char **argv)
256{
257 unsigned opt;
258 unsigned i, want_exit;
259 char *x;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000260 char *action;
261 const char *varservice = "/var/service/";
262 unsigned services;
263 char **servicex;
264 unsigned long waitsec = 7;
265 smallint kll = 0;
266 smallint verbose = 0;
267 int (*act)(const char*);
268 int (*cbk)(const char*);
269 int curdir;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000270
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000271 xfunc_error_retval = 100;
272
273 x = getenv("SVDIR");
274 if (x) varservice = x;
275 x = getenv("SVWAIT");
276 if (x) waitsec = xatoul(x);
277
278 opt = getopt32(argc, argv, "w:v", &x);
279 if (opt & 1) waitsec = xatoul(x); // -w
280 if (opt & 2) verbose = 1; // -v
281 argc -= optind;
282 argv += optind;
283 action = *argv++;
284 if (!action || !*argv) usage();
Denis Vlasenko04c63862006-11-17 18:58:49 +0000285 service = argv;
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000286 services = argc - 1;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000287
Denis Vlasenko322661d2007-01-29 23:43:52 +0000288 taia_now(&tnow);
289 tstart = tnow;
290 curdir = open_read(".");
291 if (curdir == -1)
Denis Vlasenko04c63862006-11-17 18:58:49 +0000292 fatal_cannot("open current directory");
293
Denis Vlasenko322661d2007-01-29 23:43:52 +0000294 act = &control;
295 acts = "s";
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000296 cbk = &check;
297
Denis Vlasenko04c63862006-11-17 18:58:49 +0000298 switch (*action) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000299 case 'x':
300 case 'e':
301 acts = "x";
302 if (!verbose) cbk = NULL;
303 break;
304 case 'X':
305 case 'E':
306 acts = "x";
307 kll = 1;
308 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000309 case 'D':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000310 acts = "d";
311 kll = 1;
312 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000313 case 'T':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000314 acts = "tc";
315 kll = 1;
316 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000317 case 'c':
318 if (!str_diff(action, "check")) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000319 act = NULL;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000320 acts = "c";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000321 break;
322 }
323 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
324 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000325 action[1] = '\0';
326 acts = action;
327 if (!verbose) cbk = NULL;
328 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000329 case 's':
330 if (!str_diff(action, "shutdown")) {
331 acts = "x";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000332 break;
333 }
334 if (!str_diff(action, "start")) {
335 acts = "u";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000336 break;
337 }
338 if (!str_diff(action, "stop")) {
339 acts = "d";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000340 break;
341 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000342 /* "status" */
Denis Vlasenko322661d2007-01-29 23:43:52 +0000343 act = &status;
344 cbk = NULL;
345 break;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000346 case 'r':
347 if (!str_diff(action, "restart")) {
348 acts = "tcu";
Denis Vlasenko04c63862006-11-17 18:58:49 +0000349 break;
350 }
351 usage();
352 case 'f':
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000353 if (!str_diff(action, "force-reload")) {
354 acts = "tc";
355 kll = 1;
356 break;
357 }
358 if (!str_diff(action, "force-restart")) {
359 acts = "tcu";
360 kll = 1;
361 break;
362 }
363 if (!str_diff(action, "force-shutdown")) {
364 acts = "x";
365 kll = 1;
366 break;
367 }
368 if (!str_diff(action, "force-stop")) {
369 acts = "d";
370 kll = 1;
371 break;
372 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000373 default:
374 usage();
375 }
376
377 servicex = service;
378 for (i = 0; i < services; ++i) {
379 if ((**service != '/') && (**service != '.')) {
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000380 if (chdir(varservice) == -1)
381 goto chdir_failed_0;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000382 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000383 if (chdir(*service) == -1) {
384 chdir_failed_0:
385 fail("cannot change to service directory");
386 goto nullify_service_0;
387 }
388 if (act && (act(acts) == -1)) {
389 nullify_service_0:
390 *service = NULL;
391 }
392 if (fchdir(curdir) == -1)
393 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000394 service++;
395 }
396
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000397 if (cbk) while (1) {
398 //struct taia tdiff;
399 long diff;
400
401 //taia_sub(&tdiff, &tnow, &tstart);
402 diff = tnow.sec.x - tstart.sec.x;
403 service = servicex;
404 want_exit = 1;
405 for (i = 0; i < services; ++i, ++service) {
406 if (!*service)
407 continue;
408 if ((**service != '/') && (**service != '.')) {
409 if (chdir(varservice) == -1)
410 goto chdir_failed;
Denis Vlasenko04c63862006-11-17 18:58:49 +0000411 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000412 if (chdir(*service) == -1) {
413 chdir_failed:
414 fail("cannot change to service directory");
415 goto nullify_service;
416 }
417 if (cbk(acts) != 0)
418 goto nullify_service;
419 want_exit = 0;
420 if (diff >= waitsec) {
421 printf(kll ? "kill: " : "timeout: ");
422 if (svstatus_get() > 0) {
423 svstatus_print(*service);
424 ++rc;
425 }
426 puts(""); /* will also flush the output */
427 if (kll)
428 control("k");
429 nullify_service:
430 *service = NULL;
431 }
432 if (fchdir(curdir) == -1)
433 fatal_cannot("change to original directory");
Denis Vlasenko04c63862006-11-17 18:58:49 +0000434 }
Denis Vlasenko7fca91a32007-02-02 01:19:09 +0000435 if (want_exit) break;
436 usleep(420000);
437 taia_now(&tnow);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000438 }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000439 return rc > 99 ? 99 : rc;
440}