style fixes
[oweals/busybox.git] / runit / sv.c
1 /* 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
9 static char *action;
10 static char *acts;
11 static char *varservice = "/var/service/";
12 static char **service;
13 static char **servicex;
14 static unsigned services;
15 static unsigned rc = 0;
16 static unsigned verbose = 0;
17 static unsigned long waitsec = 7;
18 static unsigned kll = 0;
19 static struct taia tstart, tnow, tdiff;
20 static struct tai tstatus;
21
22 static int (*act)(char*) = 0;
23 static int (*cbk)(char*) = 0;
24
25 static int curdir, fd, r;
26 static char svstatus[20];
27
28 #define usage() bb_show_usage()
29
30 static void fatal_cannot(char *m1)
31 {
32         bb_perror_msg("fatal: cannot %s", m1);
33         _exit(151);
34 }
35
36 static 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
54 static void fail(char *m1) { ++rc; out(FAIL, m1); }
55 static void failx(char *m1) { errno = 0; fail(m1); }
56 static void warn_cannot(char *m1) { ++rc; out("warning: cannot ", m1); }
57 static void warnx_cannot(char *m1) { errno = 0; warn_cannot(m1); }
58 static void ok(char *m1) { errno = 0; out(OK, m1); }
59
60 static 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);
78         switch (r) {
79         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
86 static unsigned svstatus_print(char *m)
87 {
88         int pid;
89         int normallyup = 0;
90         struct stat s;
91  
92         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
124 static int status(char *unused)
125 {
126         r = svstatus_get();
127         switch (r) { case -1: case 0: return 0; }
128         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
142 static 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
174 static 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
207 static 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
227 int 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 }