diff options
author | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2006-11-17 18:58:49 +0000 |
---|---|---|
committer | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2006-11-17 18:58:49 +0000 |
commit | 5e930fe52997747f9c5127a4c34fff0ca1057222 (patch) | |
tree | ca62c4d72fe5799be5ba36810e1c214e0cb8a49f /runit/sv.c | |
parent | e89087e8ac2bbd0d510f9291f8bd704059f4e48b (diff) | |
download | busybox-w32-5e930fe52997747f9c5127a4c34fff0ca1057222.tar.gz busybox-w32-5e930fe52997747f9c5127a4c34fff0ca1057222.tar.bz2 busybox-w32-5e930fe52997747f9c5127a4c34fff0ca1057222.zip |
forgot about avn add... :(
git-svn-id: svn://busybox.net/trunk/busybox@16570 69ca8d6d-28ef-0310-b511-8ec308f3f277
Diffstat (limited to '')
-rw-r--r-- | runit/sv.c | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/runit/sv.c b/runit/sv.c new file mode 100644 index 000000000..819f31419 --- /dev/null +++ b/runit/sv.c | |||
@@ -0,0 +1,360 @@ | |||
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 = ✓ | ||
257 | switch (*action) { | ||
258 | case 'x': case 'e': | ||
259 | acts = "x"; break; | ||
260 | case 'X': case 'E': | ||
261 | acts = "x"; kll = 1; cbk = ✓ break; | ||
262 | case 'D': | ||
263 | acts = "d"; kll = 1; cbk = ✓ break; | ||
264 | case 'T': | ||
265 | acts = "tc"; kll = 1; cbk = ✓ break; | ||
266 | case 'c': | ||
267 | if (!str_diff(action, "check")) { | ||
268 | act = 0; | ||
269 | acts = "c"; | ||
270 | cbk = ✓ | ||
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 = ✓ | ||
280 | break; | ||
281 | } | ||
282 | if (!str_diff(action, "start")) { | ||
283 | acts = "u"; | ||
284 | cbk = ✓ | ||
285 | break; | ||
286 | } | ||
287 | if (!str_diff(action, "stop")) { | ||
288 | acts = "d"; | ||
289 | cbk = ✓ | ||
290 | break; | ||
291 | } | ||
292 | act = &status; cbk = 0; break; | ||
293 | case 'r': | ||
294 | if (!str_diff(action, "restart")) { | ||
295 | acts = "tcu"; | ||
296 | cbk = ✓ | ||
297 | break; | ||
298 | } | ||
299 | usage(); | ||
300 | case 'f': | ||
301 | if (!str_diff(action, "force-reload")) | ||
302 | { acts = "tc"; kll = 1; cbk = ✓ break; } | ||
303 | if (!str_diff(action, "force-restart")) | ||
304 | { acts = "tcu"; kll = 1; cbk = ✓ break; } | ||
305 | if (!str_diff(action, "force-shutdown")) | ||
306 | { acts = "x"; kll = 1; cbk = ✓ break; } | ||
307 | if (!str_diff(action, "force-stop")) | ||
308 | { acts = "d"; kll = 1; cbk = ✓ 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 | } | ||