Contents of /trunk/mkinitrd-magellan/busybox/runit/sv.c
Parent Directory
|
Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 9210 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 9210 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
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 | } |