blob: 054053619a24cd2308f00842cc50ba7ee6d29ac9 [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
9static char *action;
10static char *acts;
11static char *varservice = "/var/service/";
12static char **service;
13static char **servicex;
14static unsigned services;
15static unsigned rc = 0;
16static unsigned verbose = 0;
17static unsigned long waitsec = 7;
18static unsigned kll = 0;
19static struct taia tstart, tnow, tdiff;
20static struct tai tstatus;
21
22static int (*act)(char*) = 0;
23static int (*cbk)(char*) = 0;
24
25static int curdir, fd, r;
26static char svstatus[20];
27
28#define usage() bb_show_usage()
29
30static void fatal_cannot(char *m1)
31{
32 bb_perror_msg("fatal: cannot %s", m1);
33 _exit(151);
34}
35
36static void out(char *p, char *m1)
37{
38 printf("%s%s: %s", p, *service, m1);
39 if (errno) {
40 printf(": %s", strerror(errno));
41 }
42 puts(""); /* will also flush the output */
43}
44
45#define FAIL "fail: "
46#define WARN "warning: "
47#define OK "ok: "
48#define RUN "run: "
49#define FINISH "finish: "
50#define DOWN "down: "
51#define TIMEOUT "timeout: "
52#define KILL "kill: "
53
54static void fail(char *m1) { ++rc; out(FAIL, m1); }
55static void failx(char *m1) { errno = 0; fail(m1); }
56static void warn_cannot(char *m1) { ++rc; out("warning: cannot ", m1); }
57static void warnx_cannot(char *m1) { errno = 0; warn_cannot(m1); }
58static void ok(char *m1) { errno = 0; out(OK, m1); }
59
60static int svstatus_get(void)
61{
62 if ((fd = open_write("supervise/ok")) == -1) {
63 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);
72 if ((fd = open_read("supervise/status")) == -1) {
73 warn_cannot("open supervise/status");
74 return -1;
75 }
76 r = read(fd, svstatus, 20);
77 close(fd);
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000078 switch (r) {
Denis Vlasenko04c63862006-11-17 18:58:49 +000079 case 20: break;
80 case -1: warn_cannot("read supervise/status"); return -1;
81 default: warnx_cannot("read supervise/status: bad format"); return -1;
82 }
83 return 1;
84}
85
86static unsigned svstatus_print(char *m)
87{
88 int pid;
89 int normallyup = 0;
90 struct stat s;
Denis Vlasenkof7996f32007-01-11 17:20:00 +000091
Denis Vlasenko04c63862006-11-17 18:58:49 +000092 if (stat("down", &s) == -1) {
93 if (errno != ENOENT) {
94 bb_perror_msg(WARN"cannot stat %s/down", *service);
95 return 0;
96 }
97 normallyup = 1;
98 }
99 pid = (unsigned char) svstatus[15];
100 pid <<= 8; pid += (unsigned char)svstatus[14];
101 pid <<= 8; pid += (unsigned char)svstatus[13];
102 pid <<= 8; pid += (unsigned char)svstatus[12];
103 tai_unpack(svstatus, &tstatus);
104 if (pid) {
105 switch (svstatus[19]) {
106 case 1: printf(RUN); break;
107 case 2: printf(FINISH); break;
108 }
109 printf("%s: (pid %d) ", m, pid);
110 }
111 else {
112 printf(DOWN"%s: ", m);
113 }
114 printf("%lus", (unsigned long)(tnow.sec.x < tstatus.x ? 0 : tnow.sec.x-tstatus.x));
115 if (pid && !normallyup) printf(", normally down");
116 if (!pid && normallyup) printf(", normally up");
117 if (pid && svstatus[16]) printf(", paused");
118 if (!pid && (svstatus[17] == 'u')) printf(", want up");
119 if (pid && (svstatus[17] == 'd')) printf(", want down");
120 if (pid && svstatus[18]) printf(", got TERM");
121 return pid ? 1 : 2;
122}
123
124static int status(char *unused)
125{
126 r = svstatus_get();
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000127 switch (r) { case -1: case 0: return 0; }
Denis Vlasenko04c63862006-11-17 18:58:49 +0000128 r = svstatus_print(*service);
129 if (chdir("log") == -1) {
130 if (errno != ENOENT) {
131 printf("; log: "WARN"cannot change to log service directory: %s",
132 strerror(errno));
133 }
134 } else if (svstatus_get()) {
135 printf("; ");
136 svstatus_print("log");
137 }
138 puts(""); /* will also flush the output */
139 return r;
140}
141
142static int checkscript(void)
143{
144 char *prog[2];
145 struct stat s;
146 int pid, w;
147
148 if (stat("check", &s) == -1) {
149 if (errno == ENOENT) return 1;
150 bb_perror_msg(WARN"cannot stat %s/check", *service);
151 return 0;
152 }
153 /* if (!(s.st_mode & S_IXUSR)) return 1; */
154 if ((pid = fork()) == -1) {
155 bb_perror_msg(WARN"cannot fork for %s/check", *service);
156 return 0;
157 }
158 if (!pid) {
159 prog[0] = "./check";
160 prog[1] = 0;
161 close(1);
162 execve("check", prog, environ);
163 bb_perror_msg(WARN"cannot run %s/check", *service);
164 _exit(0);
165 }
166 while (wait_pid(&w, pid) == -1) {
167 if (errno == EINTR) continue;
168 bb_perror_msg(WARN"cannot wait for child %s/check", *service);
169 return 0;
170 }
171 return !wait_exitcode(w);
172}
173
174static int check(char *a)
175{
176 unsigned pid;
177
178 if ((r = svstatus_get()) == -1) return -1;
179 if (r == 0) { if (*a == 'x') return 1; return -1; }
180 pid = (unsigned char)svstatus[15];
181 pid <<= 8; pid += (unsigned char)svstatus[14];
182 pid <<= 8; pid += (unsigned char)svstatus[13];
183 pid <<= 8; pid += (unsigned char)svstatus[12];
184 switch (*a) {
185 case 'x': return 0;
186 case 'u':
187 if (!pid || svstatus[19] != 1) return 0;
188 if (!checkscript()) return 0;
189 break;
190 case 'd': if (pid) return 0; break;
191 case 'c': if (pid) if (!checkscript()) return 0; break;
192 case 't':
193 if (!pid && svstatus[17] == 'd') break;
194 tai_unpack(svstatus, &tstatus);
195 if ((tstart.sec.x > tstatus.x) || !pid || svstatus[18] || !checkscript())
196 return 0;
197 break;
198 case 'o':
199 tai_unpack(svstatus, &tstatus);
200 if ((!pid && tstart.sec.x > tstatus.x) || (pid && svstatus[17] != 'd'))
201 return 0;
202 }
203 printf(OK); svstatus_print(*service); puts(""); /* will also flush the output */
204 return 1;
205}
206
207static int control(char *a)
208{
209 if (svstatus_get() <= 0) return -1;
210 if (svstatus[17] == *a) return 0;
211 if ((fd = open_write("supervise/control")) == -1) {
212 if (errno != ENODEV)
213 warn_cannot("open supervise/control");
214 else
215 *a == 'x' ? ok("runsv not running") : failx("runsv not running");
216 return -1;
217 }
218 r = write(fd, a, strlen(a));
219 close(fd);
220 if (r != strlen(a)) {
221 warn_cannot("write to supervise/control");
222 return -1;
223 }
224 return 1;
225}
226
227int sv_main(int argc, char **argv)
228{
229 unsigned opt;
230 unsigned i, want_exit;
231 char *x;
232
233 for (i = strlen(*argv); i; --i)
234 if ((*argv)[i-1] == '/')
235 break;
236 *argv += i;
237 service = argv;
238 services = 1;
239 if ((x = getenv("SVDIR"))) varservice = x;
240 if ((x = getenv("SVWAIT"))) waitsec = xatoul(x);
241 /* TODO: V can be handled internally by getopt_ulflags */
242 opt = getopt32(argc, argv, "w:vV", &x);
243 if (opt & 1) waitsec = xatoul(x);
244 if (opt & 2) verbose = 1;
245 if (opt & 4) usage();
246 if (!(action = *argv++)) usage();
247 --argc;
248 service = argv; services = argc;
249 if (!*service) usage();
250
251 taia_now(&tnow); tstart = tnow;
252 if ((curdir = open_read(".")) == -1)
253 fatal_cannot("open current directory");
254
255 act = &control; acts = "s";
256 if (verbose) cbk = &check;
257 switch (*action) {
258 case 'x': case 'e':
259 acts = "x"; break;
260 case 'X': case 'E':
261 acts = "x"; kll = 1; cbk = &check; break;
262 case 'D':
263 acts = "d"; kll = 1; cbk = &check; break;
264 case 'T':
265 acts = "tc"; kll = 1; cbk = &check; break;
266 case 'c':
267 if (!str_diff(action, "check")) {
268 act = 0;
269 acts = "c";
270 cbk = &check;
271 break;
272 }
273 case 'u': case 'd': case 'o': case 't': case 'p': case 'h':
274 case 'a': case 'i': case 'k': case 'q': case '1': case '2':
275 action[1] = 0; acts = action; break;
276 case 's':
277 if (!str_diff(action, "shutdown")) {
278 acts = "x";
279 cbk = &check;
280 break;
281 }
282 if (!str_diff(action, "start")) {
283 acts = "u";
284 cbk = &check;
285 break;
286 }
287 if (!str_diff(action, "stop")) {
288 acts = "d";
289 cbk = &check;
290 break;
291 }
292 act = &status; cbk = 0; break;
293 case 'r':
294 if (!str_diff(action, "restart")) {
295 acts = "tcu";
296 cbk = &check;
297 break;
298 }
299 usage();
300 case 'f':
301 if (!str_diff(action, "force-reload"))
302 { acts = "tc"; kll = 1; cbk = &check; break; }
303 if (!str_diff(action, "force-restart"))
304 { acts = "tcu"; kll = 1; cbk = &check; break; }
305 if (!str_diff(action, "force-shutdown"))
306 { acts = "x"; kll = 1; cbk = &check; break; }
307 if (!str_diff(action, "force-stop"))
308 { acts = "d"; kll = 1; cbk = &check; break; }
309 default:
310 usage();
311 }
312
313 servicex = service;
314 for (i = 0; i < services; ++i) {
315 if ((**service != '/') && (**service != '.')) {
316 if ((chdir(varservice) == -1) || (chdir(*service) == -1)) {
317 fail("cannot change to service directory");
318 *service = 0;
319 }
320 } else if (chdir(*service) == -1) {
321 fail("cannot change to service directory");
322 *service = 0;
323 }
324 if (*service) if (act && (act(acts) == -1)) *service = 0;
325 if (fchdir(curdir) == -1) fatal_cannot("change to original directory");
326 service++;
327 }
328
329 if (*cbk)
330 for (;;) {
331 taia_sub(&tdiff, &tnow, &tstart);
332 service = servicex; want_exit = 1;
333 for (i = 0; i < services; ++i, ++service) {
334 if (!*service) continue;
335 if ((**service != '/') && (**service != '.')) {
336 if ((chdir(varservice) == -1) || (chdir(*service) == -1)) {
337 fail("cannot change to service directory");
338 *service = 0;
339 }
340 } else if (chdir(*service) == -1) {
341 fail("cannot change to service directory");
342 *service = 0;
343 }
344 if (*service) { if (cbk(acts) != 0) *service = 0; else want_exit = 0; }
345 if (*service && taia_approx(&tdiff) > waitsec) {
346 kll ? printf(KILL) : printf(TIMEOUT);
347 if (svstatus_get() > 0) { svstatus_print(*service); ++rc; }
348 puts(""); /* will also flush the output */
349 if (kll) control("k");
350 *service = 0;
351 }
352 if (fchdir(curdir) == -1)
353 fatal_cannot("change to original directory");
354 }
355 if (want_exit) break;
356 usleep(420000);
357 taia_now(&tnow);
358 }
359 return rc > 99 ? 99 : rc;
360}