diff options
author | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2006-11-16 02:27:24 +0000 |
---|---|---|
committer | vda <vda@69ca8d6d-28ef-0310-b511-8ec308f3f277> | 2006-11-16 02:27:24 +0000 |
commit | 39c36b524f83cae7c64bdd2d91c59f30d9cb6243 (patch) | |
tree | 3a5f790604219213666cbf5cccd747c02528912c /runit/svlogd.c | |
parent | 2ec77e221f3354928e99e307932dc4ae812dc433 (diff) | |
download | busybox-w32-39c36b524f83cae7c64bdd2d91c59f30d9cb6243.tar.gz busybox-w32-39c36b524f83cae7c64bdd2d91c59f30d9cb6243.tar.bz2 busybox-w32-39c36b524f83cae7c64bdd2d91c59f30d9cb6243.zip |
svlogd: new applet. +9k. Still too big, but it was 12k yesterday.
git-svn-id: svn://busybox.net/trunk/busybox@16535 69ca8d6d-28ef-0310-b511-8ec308f3f277
Diffstat (limited to 'runit/svlogd.c')
-rw-r--r-- | runit/svlogd.c | 880 |
1 files changed, 880 insertions, 0 deletions
diff --git a/runit/svlogd.c b/runit/svlogd.c new file mode 100644 index 000000000..8e012d239 --- /dev/null +++ b/runit/svlogd.c | |||
@@ -0,0 +1,880 @@ | |||
1 | /* | ||
2 | Copyright (c) 2001-2006, Gerrit Pape | ||
3 | All rights reserved. | ||
4 | |||
5 | Redistribution and use in source and binary forms, with or without | ||
6 | modification, are permitted provided that the following conditions are met: | ||
7 | |||
8 | 1. Redistributions of source code must retain the above copyright notice, | ||
9 | this list of conditions and the following disclaimer. | ||
10 | 2. Redistributions in binary form must reproduce the above copyright | ||
11 | notice, this list of conditions and the following disclaimer in the | ||
12 | documentation and/or other materials provided with the distribution. | ||
13 | 3. The name of the author may not be used to endorse or promote products | ||
14 | derived from this software without specific prior written permission. | ||
15 | |||
16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | ||
17 | WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | ||
18 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | ||
19 | EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
20 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | ||
21 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | ||
22 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | ||
23 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | ||
24 | OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | ||
25 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | */ | ||
27 | |||
28 | /* Busyboxed by Denis Vlasenko <vda.linux@googlemail.com> */ | ||
29 | /* TODO: depends on runit_lib.c - review and reduce/eliminate */ | ||
30 | |||
31 | #include <sys/poll.h> | ||
32 | #include <sys/file.h> | ||
33 | #include "busybox.h" | ||
34 | #include "runit_lib.h" | ||
35 | |||
36 | static unsigned verbose; | ||
37 | static int linemax = 1000; | ||
38 | static int buflen = 1024; | ||
39 | static int linelen; | ||
40 | |||
41 | static char **fndir; | ||
42 | static int fdwdir; | ||
43 | static int wstat; | ||
44 | static struct taia trotate; | ||
45 | |||
46 | static char *line; | ||
47 | static unsigned exitasap; | ||
48 | static unsigned rotateasap; | ||
49 | static unsigned reopenasap; | ||
50 | static unsigned linecomplete = 1; | ||
51 | static unsigned tmaxflag; | ||
52 | static iopause_fd in; | ||
53 | |||
54 | static const char *replace = ""; | ||
55 | static char repl; | ||
56 | |||
57 | static struct logdir { | ||
58 | char *btmp; | ||
59 | /* pattern list to match, in "aa\0bb\0\cc\0\0" form */ | ||
60 | char *inst; | ||
61 | char *processor; | ||
62 | char *name; | ||
63 | unsigned size; | ||
64 | unsigned sizemax; | ||
65 | unsigned nmax; | ||
66 | unsigned nmin; | ||
67 | /* int (not long) because of taia_uint() usage: */ | ||
68 | unsigned tmax; | ||
69 | int ppid; | ||
70 | int fddir; | ||
71 | int fdcur; | ||
72 | int fdlock; | ||
73 | struct taia trotate; | ||
74 | char fnsave[FMT_PTIME]; | ||
75 | char match; | ||
76 | char matcherr; | ||
77 | } *dir; | ||
78 | static unsigned dirn = 0; | ||
79 | |||
80 | #define FATAL "fatal: " | ||
81 | #define WARNING "warning: " | ||
82 | #define PAUSE "pausing: " | ||
83 | #define INFO "info: " | ||
84 | |||
85 | #define usage() bb_show_usage() | ||
86 | static void fatalx(char *m0) | ||
87 | { | ||
88 | bb_error_msg_and_die(FATAL"%s", m0); | ||
89 | } | ||
90 | static void warn(char *m0) { | ||
91 | bb_perror_msg(WARNING"%s", m0); | ||
92 | } | ||
93 | static void warn2(char *m0, char *m1) | ||
94 | { | ||
95 | bb_perror_msg(WARNING"%s: %s", m0, m1); | ||
96 | } | ||
97 | static void warnx(char *m0, char *m1) | ||
98 | { | ||
99 | bb_error_msg(WARNING"%s: %s", m0, m1); | ||
100 | } | ||
101 | static void pause_nomem(void) | ||
102 | { | ||
103 | bb_error_msg(PAUSE"out of memory"); sleep(3); | ||
104 | } | ||
105 | static void pause1cannot(char *m0) | ||
106 | { | ||
107 | bb_perror_msg(PAUSE"cannot %s", m0); sleep(3); | ||
108 | } | ||
109 | static void pause2cannot(char *m0, char *m1) | ||
110 | { | ||
111 | bb_perror_msg(PAUSE"cannot %s %s", m0, m1); | ||
112 | sleep(3); | ||
113 | } | ||
114 | |||
115 | static char* wstrdup(const char *str) | ||
116 | { | ||
117 | char *s; | ||
118 | while (!(s = strdup(str))) pause_nomem(); | ||
119 | return s; | ||
120 | } | ||
121 | |||
122 | static unsigned processorstart(struct logdir *ld) | ||
123 | { | ||
124 | int pid; | ||
125 | |||
126 | if (!ld->processor) return 0; | ||
127 | if (ld->ppid) { | ||
128 | warnx("processor already running", ld->name); | ||
129 | return 0; | ||
130 | } | ||
131 | while ((pid = fork()) == -1) | ||
132 | pause2cannot("fork for processor", ld->name); | ||
133 | if (!pid) { | ||
134 | char *prog[4]; | ||
135 | int fd; | ||
136 | |||
137 | /* child */ | ||
138 | sig_uncatch(sig_term); | ||
139 | sig_uncatch(sig_alarm); | ||
140 | sig_uncatch(sig_hangup); | ||
141 | sig_unblock(sig_term); | ||
142 | sig_unblock(sig_alarm); | ||
143 | sig_unblock(sig_hangup); | ||
144 | |||
145 | if (verbose) | ||
146 | bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave); | ||
147 | fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY); | ||
148 | if (fd_move(0, fd) == -1) | ||
149 | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); | ||
150 | ld->fnsave[26] = 't'; | ||
151 | fd = xopen3(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT, 0644); | ||
152 | if (fd_move(1, fd) == -1) | ||
153 | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); | ||
154 | fd = open_read("state"); | ||
155 | if (fd == -1) { | ||
156 | if (errno != ENOENT) | ||
157 | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name); | ||
158 | close(xopen3("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT, 0644)); | ||
159 | fd = xopen("state", O_RDONLY|O_NDELAY); | ||
160 | } | ||
161 | if (fd_move(4, fd) == -1) | ||
162 | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); | ||
163 | fd = xopen3("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT, 0644); | ||
164 | if (fd_move(5, fd) == -1) | ||
165 | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "move filedescriptor for", ld->name); | ||
166 | |||
167 | prog[0] = "sh"; | ||
168 | prog[1] = "-c"; | ||
169 | prog[2] = ld->processor; | ||
170 | prog[3] = '\0'; | ||
171 | execve("/bin/sh", prog, environ); | ||
172 | bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name); | ||
173 | } | ||
174 | ld->ppid = pid; | ||
175 | return 1; | ||
176 | } | ||
177 | |||
178 | static unsigned processorstop(struct logdir *ld) | ||
179 | { | ||
180 | char f[28]; | ||
181 | |||
182 | if (ld->ppid) { | ||
183 | sig_unblock(sig_hangup); | ||
184 | while (wait_pid(&wstat, ld->ppid) == -1) | ||
185 | pause2cannot("wait for processor", ld->name); | ||
186 | sig_block(sig_hangup); | ||
187 | ld->ppid = 0; | ||
188 | } | ||
189 | if (ld->fddir == -1) return 1; | ||
190 | while (fchdir(ld->fddir) == -1) | ||
191 | pause2cannot("change directory, want processor", ld->name); | ||
192 | if (wait_exitcode(wstat) != 0) { | ||
193 | warnx("processor failed, restart", ld->name); | ||
194 | ld->fnsave[26] = 't'; | ||
195 | unlink(ld->fnsave); | ||
196 | ld->fnsave[26] = 'u'; | ||
197 | processorstart(ld); | ||
198 | while (fchdir(fdwdir) == -1) | ||
199 | pause1cannot("change to initial working directory"); | ||
200 | return ld->processor ? 0 : 1; | ||
201 | } | ||
202 | ld->fnsave[26] = 't'; | ||
203 | memcpy(f, ld->fnsave, 26); | ||
204 | f[26] = 's'; | ||
205 | f[27] = '\0'; | ||
206 | while (rename(ld->fnsave, f) == -1) | ||
207 | pause2cannot("rename processed", ld->name); | ||
208 | while (chmod(f, 0744) == -1) | ||
209 | pause2cannot("set mode of processed", ld->name); | ||
210 | ld->fnsave[26] = 'u'; | ||
211 | if (unlink(ld->fnsave) == -1) | ||
212 | bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave); | ||
213 | while (rename("newstate", "state") == -1) | ||
214 | pause2cannot("rename state", ld->name); | ||
215 | if (verbose) bb_error_msg(INFO"processed: %s/%s", ld->name, f); | ||
216 | while (fchdir(fdwdir) == -1) | ||
217 | pause1cannot("change to initial working directory"); | ||
218 | return 1; | ||
219 | } | ||
220 | |||
221 | static void rmoldest(struct logdir *ld) | ||
222 | { | ||
223 | DIR *d; | ||
224 | struct dirent *f; | ||
225 | char oldest[FMT_PTIME]; | ||
226 | int n = 0; | ||
227 | |||
228 | oldest[0] = 'A'; oldest[1] = oldest[27] = 0; | ||
229 | while (!(d = opendir("."))) | ||
230 | pause2cannot("open directory, want rotate", ld->name); | ||
231 | errno = 0; | ||
232 | while ((f = readdir(d))) { | ||
233 | if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) { | ||
234 | if (f->d_name[26] == 't') { | ||
235 | if (unlink(f->d_name) == -1) | ||
236 | warn2("cannot unlink processor leftover", f->d_name); | ||
237 | } else { | ||
238 | ++n; | ||
239 | if (strcmp(f->d_name, oldest) < 0) | ||
240 | memcpy(oldest, f->d_name, 27); | ||
241 | } | ||
242 | errno = 0; | ||
243 | } | ||
244 | } | ||
245 | if (errno) warn2("cannot read directory", ld->name); | ||
246 | closedir(d); | ||
247 | |||
248 | if (ld->nmax && (n > ld->nmax)) { | ||
249 | if (verbose) bb_error_msg(INFO"delete: %s/%s", ld->name, oldest); | ||
250 | if ((*oldest == '@') && (unlink(oldest) == -1)) | ||
251 | warn2("cannot unlink oldest logfile", ld->name); | ||
252 | } | ||
253 | } | ||
254 | |||
255 | static unsigned rotate(struct logdir *ld) | ||
256 | { | ||
257 | struct stat st; | ||
258 | struct taia now; | ||
259 | |||
260 | if (ld->fddir == -1) { | ||
261 | ld->tmax = 0; | ||
262 | return 0; | ||
263 | } | ||
264 | if (ld->ppid) | ||
265 | while(!processorstop(ld)) | ||
266 | /* wait */; | ||
267 | |||
268 | while (fchdir(ld->fddir) == -1) | ||
269 | pause2cannot("change directory, want rotate", ld->name); | ||
270 | |||
271 | /* create new filename */ | ||
272 | ld->fnsave[25] = '.'; | ||
273 | ld->fnsave[26] = 's'; | ||
274 | if (ld->processor) | ||
275 | ld->fnsave[26] = 'u'; | ||
276 | ld->fnsave[27] = '\0'; | ||
277 | do { | ||
278 | taia_now(&now); | ||
279 | fmt_taia(ld->fnsave, &now); | ||
280 | errno = 0; | ||
281 | } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT)); | ||
282 | |||
283 | if (ld->tmax && taia_less(&ld->trotate, &now)) { | ||
284 | taia_uint(&ld->trotate, ld->tmax); | ||
285 | taia_add(&ld->trotate, &now, &ld->trotate); | ||
286 | if (taia_less(&ld->trotate, &trotate)) | ||
287 | trotate = ld->trotate; | ||
288 | } | ||
289 | |||
290 | if (ld->size > 0) { | ||
291 | while (fsync(ld->fdcur) == -1) | ||
292 | pause2cannot("fsync current logfile", ld->name); | ||
293 | while (fchmod(ld->fdcur, 0744) == -1) | ||
294 | pause2cannot("set mode of current", ld->name); | ||
295 | close(ld->fdcur); | ||
296 | if (verbose) { | ||
297 | bb_error_msg(INFO"rename: %s/current %s %u", ld->name, | ||
298 | ld->fnsave, ld->size); | ||
299 | } | ||
300 | while (rename("current", ld->fnsave) == -1) | ||
301 | pause2cannot("rename current", ld->name); | ||
302 | while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) | ||
303 | pause2cannot("create new current", ld->name); | ||
304 | coe(ld->fdcur); | ||
305 | ld->size = 0; | ||
306 | while (fchmod(ld->fdcur, 0644) == -1) | ||
307 | pause2cannot("set mode of current", ld->name); | ||
308 | rmoldest(ld); | ||
309 | processorstart(ld); | ||
310 | } | ||
311 | |||
312 | while (fchdir(fdwdir) == -1) | ||
313 | pause1cannot("change to initial working directory"); | ||
314 | return 1; | ||
315 | } | ||
316 | |||
317 | static int buffer_pwrite(int n, char *s, unsigned len) | ||
318 | { | ||
319 | int i; | ||
320 | struct logdir *ld = &dir[n]; | ||
321 | |||
322 | if (ld->sizemax) { | ||
323 | if (ld->size >= ld->sizemax) | ||
324 | rotate(ld); | ||
325 | if (len > (ld->sizemax - ld->size)) | ||
326 | len = ld->sizemax - ld->size; | ||
327 | } | ||
328 | while ((i = write(ld->fdcur, s, len)) == -1) { | ||
329 | if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) { | ||
330 | DIR *d; | ||
331 | struct dirent *f; | ||
332 | char oldest[FMT_PTIME]; | ||
333 | int j = 0; | ||
334 | |||
335 | while (fchdir(ld->fddir) == -1) | ||
336 | pause2cannot("change directory, want remove old logfile", | ||
337 | ld->name); | ||
338 | oldest[0] = 'A'; | ||
339 | oldest[1] = oldest[27] = '\0'; | ||
340 | while (!(d = opendir("."))) | ||
341 | pause2cannot("open directory, want remove old logfile", | ||
342 | ld->name); | ||
343 | errno = 0; | ||
344 | while ((f = readdir(d))) | ||
345 | if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) { | ||
346 | ++j; | ||
347 | if (strcmp(f->d_name, oldest) < 0) | ||
348 | memcpy(oldest, f->d_name, 27); | ||
349 | } | ||
350 | if (errno) warn2("cannot read directory, want remove old logfile", | ||
351 | ld->name); | ||
352 | closedir(d); | ||
353 | errno = ENOSPC; | ||
354 | if (j > ld->nmin) { | ||
355 | if (*oldest == '@') { | ||
356 | bb_error_msg(WARNING"out of disk space, delete: %s/%s", | ||
357 | ld->name, oldest); | ||
358 | errno = 0; | ||
359 | if (unlink(oldest) == -1) { | ||
360 | warn2("cannot unlink oldest logfile", ld->name); | ||
361 | errno = ENOSPC; | ||
362 | } | ||
363 | while (fchdir(fdwdir) == -1) | ||
364 | pause1cannot("change to initial working directory"); | ||
365 | } | ||
366 | } | ||
367 | } | ||
368 | if (errno) pause2cannot("write to current", ld->name); | ||
369 | } | ||
370 | |||
371 | ld->size += i; | ||
372 | if (ld->sizemax) | ||
373 | if (s[i-1] == '\n') | ||
374 | if (ld->size >= (ld->sizemax - linemax)) | ||
375 | rotate(ld); | ||
376 | return i; | ||
377 | } | ||
378 | |||
379 | static void logdir_close(struct logdir *ld) | ||
380 | { | ||
381 | if (ld->fddir == -1) | ||
382 | return; | ||
383 | if (verbose) | ||
384 | bb_error_msg(INFO"close: %s", ld->name); | ||
385 | close(ld->fddir); | ||
386 | ld->fddir = -1; | ||
387 | if (ld->fdcur == -1) | ||
388 | return; /* impossible */ | ||
389 | while (fsync(ld->fdcur) == -1) | ||
390 | pause2cannot("fsync current logfile", ld->name); | ||
391 | while (fchmod(ld->fdcur, 0744) == -1) | ||
392 | pause2cannot("set mode of current", ld->name); | ||
393 | close(ld->fdcur); | ||
394 | ld->fdcur = -1; | ||
395 | if (ld->fdlock == -1) | ||
396 | return; /* impossible */ | ||
397 | close(ld->fdlock); | ||
398 | ld->fdlock = -1; | ||
399 | free(ld->processor); | ||
400 | ld->processor = NULL; | ||
401 | } | ||
402 | |||
403 | static unsigned logdir_open(struct logdir *ld, const char *fn) | ||
404 | { | ||
405 | char buf[128]; | ||
406 | struct taia now; | ||
407 | char *new, *s, *np; | ||
408 | int i; | ||
409 | struct stat st; | ||
410 | |||
411 | ld->fddir = open(fn, O_RDONLY|O_NDELAY); | ||
412 | if (ld->fddir == -1) { | ||
413 | warn2("cannot open log directory", (char*)fn); | ||
414 | return 0; | ||
415 | } | ||
416 | coe(ld->fddir); | ||
417 | if (fchdir(ld->fddir) == -1) { | ||
418 | logdir_close(ld); | ||
419 | warn2("cannot change directory", (char*)fn); | ||
420 | return 0; | ||
421 | } | ||
422 | ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600); | ||
423 | if ((ld->fdlock == -1) | ||
424 | || (lock_exnb(ld->fdlock) == -1) | ||
425 | ) { | ||
426 | logdir_close(ld); | ||
427 | warn2("cannot lock directory", (char*)fn); | ||
428 | while (fchdir(fdwdir) == -1) | ||
429 | pause1cannot("change to initial working directory"); | ||
430 | return 0; | ||
431 | } | ||
432 | coe(ld->fdlock); | ||
433 | |||
434 | ld->size = 0; | ||
435 | ld->sizemax = 1000000; | ||
436 | ld->nmax = ld->nmin = 10; | ||
437 | ld->tmax = 0; | ||
438 | ld->name = (char*)fn; | ||
439 | ld->ppid = 0; | ||
440 | ld->match = '+'; | ||
441 | free(ld->inst); ld->inst = NULL; | ||
442 | free(ld->processor); ld->processor = NULL; | ||
443 | |||
444 | /* read config */ | ||
445 | i = open_read_close("config", buf, sizeof(buf)); | ||
446 | if (i < 0) | ||
447 | warn2("cannot read config", ld->name); | ||
448 | if (i > 0) { | ||
449 | if (verbose) bb_error_msg(INFO"read: %s/config", ld->name); | ||
450 | s = buf; | ||
451 | while (s) { | ||
452 | np = strchr(s, '\n'); | ||
453 | if (np) *np++ = '\0'; | ||
454 | switch (s[0]) { | ||
455 | case '+': | ||
456 | case '-': | ||
457 | case 'e': | ||
458 | case 'E': | ||
459 | while (1) { | ||
460 | int l = asprintf(&new, "%s%s\n", ld->inst?:"", s); | ||
461 | if (l >= 0 && new) break; | ||
462 | pause_nomem(); | ||
463 | } | ||
464 | free(ld->inst); | ||
465 | ld->inst = new; | ||
466 | break; | ||
467 | case 's': { | ||
468 | static const struct suffix_mult km_suffixes[] = { | ||
469 | { "k", 1024 }, | ||
470 | { "m", 1024*1024 }, | ||
471 | { NULL, 0 } | ||
472 | }; | ||
473 | ld->sizemax = xatou_sfx(&s[1], km_suffixes); | ||
474 | break; | ||
475 | } | ||
476 | case 'n': | ||
477 | ld->nmax = xatoi_u(&s[1]); | ||
478 | break; | ||
479 | case 'N': | ||
480 | ld->nmin = xatoi_u(&s[1]); | ||
481 | break; | ||
482 | case 't': { | ||
483 | static const struct suffix_mult mh_suffixes[] = { | ||
484 | { "m", 60 }, | ||
485 | { "h", 60*60 }, | ||
486 | /*{ "d", 24*60*60 },*/ | ||
487 | { NULL, 0 } | ||
488 | }; | ||
489 | ld->tmax = xatou_sfx(&s[1], mh_suffixes); | ||
490 | if (ld->tmax) { | ||
491 | taia_uint(&ld->trotate, ld->tmax); | ||
492 | taia_add(&ld->trotate, &now, &ld->trotate); | ||
493 | if (!tmaxflag || taia_less(&ld->trotate, &trotate)) | ||
494 | trotate = ld->trotate; | ||
495 | tmaxflag = 1; | ||
496 | } | ||
497 | break; | ||
498 | } | ||
499 | case '!': | ||
500 | if (s[1]) { | ||
501 | free(ld->processor); | ||
502 | ld->processor = wstrdup(s); | ||
503 | } | ||
504 | break; | ||
505 | } | ||
506 | s = np; | ||
507 | } | ||
508 | /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */ | ||
509 | s = ld->inst; | ||
510 | while (s) { | ||
511 | np = strchr(s, '\n'); | ||
512 | if (np) *np++ = '\0'; | ||
513 | s = np; | ||
514 | } | ||
515 | } | ||
516 | |||
517 | /* open current */ | ||
518 | i = stat("current", &st); | ||
519 | if (i != -1) { | ||
520 | if (st.st_size && ! (st.st_mode & S_IXUSR)) { | ||
521 | ld->fnsave[25] = '.'; | ||
522 | ld->fnsave[26] = 'u'; | ||
523 | ld->fnsave[27] = '\0'; | ||
524 | do { | ||
525 | taia_now(&now); | ||
526 | fmt_taia(ld->fnsave, &now); | ||
527 | errno = 0; | ||
528 | } while ((stat(ld->fnsave, &st) != -1) || (errno != ENOENT)); | ||
529 | while (rename("current", ld->fnsave) == -1) | ||
530 | pause2cannot("rename current", ld->name); | ||
531 | rmoldest(ld); | ||
532 | i = -1; | ||
533 | } else { | ||
534 | /* Be paranoid: st.st_size can be not just bigger, but WIDER! */ | ||
535 | /* (bug in original svlogd. remove this comment when fixed there) */ | ||
536 | ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size; | ||
537 | } | ||
538 | } else { | ||
539 | if (errno != ENOENT) { | ||
540 | logdir_close(ld); | ||
541 | warn2("cannot stat current", ld->name); | ||
542 | while (fchdir(fdwdir) == -1) | ||
543 | pause1cannot("change to initial working directory"); | ||
544 | return 0; | ||
545 | } | ||
546 | } | ||
547 | while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) | ||
548 | pause2cannot("open current", ld->name); | ||
549 | coe(ld->fdcur); | ||
550 | while (fchmod(ld->fdcur, 0644) == -1) | ||
551 | pause2cannot("set mode of current", ld->name); | ||
552 | |||
553 | if (verbose) { | ||
554 | if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name); | ||
555 | else bb_error_msg(INFO"new: %s/current", ld->name); | ||
556 | } | ||
557 | |||
558 | while (fchdir(fdwdir) == -1) | ||
559 | pause1cannot("change to initial working directory"); | ||
560 | return 1; | ||
561 | } | ||
562 | |||
563 | static void logdirs_reopen(void) | ||
564 | { | ||
565 | struct taia now; | ||
566 | int l; | ||
567 | int ok = 0; | ||
568 | |||
569 | tmaxflag = 0; | ||
570 | taia_now(&now); | ||
571 | for (l = 0; l < dirn; ++l) { | ||
572 | logdir_close(&dir[l]); | ||
573 | if (logdir_open(&dir[l], fndir[l])) ok = 1; | ||
574 | } | ||
575 | if (!ok) fatalx("no functional log directories"); | ||
576 | } | ||
577 | |||
578 | /* Used for reading stdin */ | ||
579 | static int buffer_pread(int fd, char *s, unsigned len) | ||
580 | { | ||
581 | struct taia now; | ||
582 | int i; | ||
583 | |||
584 | if (rotateasap) { | ||
585 | for (i = 0; i < dirn; ++i) | ||
586 | rotate(dir+i); | ||
587 | rotateasap = 0; | ||
588 | } | ||
589 | if (exitasap) { | ||
590 | if (linecomplete) | ||
591 | return 0; | ||
592 | len = 1; | ||
593 | } | ||
594 | if (reopenasap) { | ||
595 | logdirs_reopen(); | ||
596 | reopenasap = 0; | ||
597 | } | ||
598 | taia_now(&now); | ||
599 | taia_uint(&trotate, 2744); | ||
600 | taia_add(&trotate, &now, &trotate); | ||
601 | for (i = 0; i < dirn; ++i) | ||
602 | if (dir[i].tmax) { | ||
603 | if (taia_less(&dir[i].trotate, &now)) | ||
604 | rotate(dir+i); | ||
605 | if (taia_less(&dir[i].trotate, &trotate)) | ||
606 | trotate = dir[i].trotate; | ||
607 | } | ||
608 | |||
609 | while (1) { | ||
610 | /* Comment? */ | ||
611 | sig_unblock(sig_term); | ||
612 | sig_unblock(sig_child); | ||
613 | sig_unblock(sig_alarm); | ||
614 | sig_unblock(sig_hangup); | ||
615 | iopause(&in, 1, &trotate, &now); | ||
616 | sig_block(sig_term); | ||
617 | sig_block(sig_child); | ||
618 | sig_block(sig_alarm); | ||
619 | sig_block(sig_hangup); | ||
620 | i = safe_read(fd, s, len); | ||
621 | if (i >= 0) break; | ||
622 | if (errno != EAGAIN) { | ||
623 | warn("cannot read standard input"); | ||
624 | break; | ||
625 | } | ||
626 | /* else: EAGAIN - normal, repeat silently */ | ||
627 | } | ||
628 | |||
629 | if (i > 0) { | ||
630 | int cnt; | ||
631 | linecomplete = (s[i-1] == '\n'); | ||
632 | if (!repl) return i; | ||
633 | |||
634 | cnt = i; | ||
635 | while (--cnt >= 0) { | ||
636 | char ch = *s; | ||
637 | if (ch != '\n') { | ||
638 | if (ch < 32 || ch > 126) | ||
639 | *s = repl; | ||
640 | else { | ||
641 | int j; | ||
642 | for (j = 0; replace[j]; ++j) { | ||
643 | if (ch == replace[j]) { | ||
644 | *s = repl; | ||
645 | break; | ||
646 | } | ||
647 | } | ||
648 | } | ||
649 | } | ||
650 | s++; | ||
651 | } | ||
652 | } | ||
653 | return i; | ||
654 | } | ||
655 | |||
656 | |||
657 | static void sig_term_handler(int sig_no) | ||
658 | { | ||
659 | if (verbose) | ||
660 | bb_error_msg(INFO"sig%s received", "term"); | ||
661 | exitasap = 1; | ||
662 | } | ||
663 | |||
664 | static void sig_child_handler(int sig_no) | ||
665 | { | ||
666 | int pid, l; | ||
667 | |||
668 | if (verbose) | ||
669 | bb_error_msg(INFO"sig%s received", "child"); | ||
670 | while ((pid = wait_nohang(&wstat)) > 0) | ||
671 | for (l = 0; l < dirn; ++l) | ||
672 | if (dir[l].ppid == pid) { | ||
673 | dir[l].ppid = 0; | ||
674 | processorstop(&dir[l]); | ||
675 | break; | ||
676 | } | ||
677 | } | ||
678 | |||
679 | static void sig_alarm_handler(int sig_no) | ||
680 | { | ||
681 | if (verbose) | ||
682 | bb_error_msg(INFO"sig%s received", "alarm"); | ||
683 | rotateasap = 1; | ||
684 | } | ||
685 | |||
686 | static void sig_hangup_handler(int sig_no) | ||
687 | { | ||
688 | if (verbose) | ||
689 | bb_error_msg(INFO"sig%s received", "hangup"); | ||
690 | reopenasap = 1; | ||
691 | } | ||
692 | |||
693 | static void logmatch(struct logdir *ld) | ||
694 | { | ||
695 | char *s; | ||
696 | |||
697 | ld->match = '+'; | ||
698 | ld->matcherr = 'E'; | ||
699 | s = ld->inst; | ||
700 | while (s && s[0]) { | ||
701 | switch (s[0]) { | ||
702 | case '+': | ||
703 | case '-': | ||
704 | if (pmatch(s+1, line, linelen)) | ||
705 | ld->match = s[0]; | ||
706 | break; | ||
707 | case 'e': | ||
708 | case 'E': | ||
709 | if (pmatch(s+1, line, linelen)) | ||
710 | ld->matcherr = s[0]; | ||
711 | break; | ||
712 | } | ||
713 | s += strlen(s) + 1; | ||
714 | } | ||
715 | } | ||
716 | |||
717 | int svlogd_main(int argc, char **argv) | ||
718 | { | ||
719 | struct taia now; | ||
720 | char *r,*l,*b; | ||
721 | ssize_t stdin_cnt = 0; | ||
722 | int i; | ||
723 | unsigned opt; | ||
724 | unsigned timestamp = 0; | ||
725 | |||
726 | opt_complementary = "tt:vv"; | ||
727 | opt = getopt32(argc, argv, "r:R:l:b:tv", | ||
728 | &r, &replace, &l, &b, ×tamp, &verbose); | ||
729 | if (opt & 1) { // -r | ||
730 | repl = r[0]; | ||
731 | if (!repl || r[1]) usage(); | ||
732 | } | ||
733 | if (opt & 2) if (!repl) repl = '_'; // -R | ||
734 | if (opt & 4) { // -l | ||
735 | linemax = xatou_range(l, 0, 1000); | ||
736 | if (linemax == 0) linemax = 1000; | ||
737 | if (linemax < 256) linemax = 256; | ||
738 | } | ||
739 | if (opt & 8) { // -b | ||
740 | buflen = xatoi_u(b); | ||
741 | if (buflen == 0) buflen = 1024; | ||
742 | } | ||
743 | //if (opt & 0x10) timestamp++; // -t | ||
744 | //if (opt & 0x20) verbose++; // -v | ||
745 | if (timestamp > 2) timestamp = 2; | ||
746 | argv += optind; | ||
747 | argc -= optind; | ||
748 | |||
749 | dirn = argc; | ||
750 | if (dirn <= 0) usage(); | ||
751 | if (buflen <= linemax) usage(); | ||
752 | fdwdir = xopen(".", O_RDONLY|O_NDELAY); | ||
753 | coe(fdwdir); | ||
754 | dir = xmalloc(dirn * sizeof(struct logdir)); | ||
755 | for (i = 0; i < dirn; ++i) { | ||
756 | dir[i].fddir = -1; | ||
757 | dir[i].fdcur = -1; | ||
758 | dir[i].btmp = xmalloc(buflen); | ||
759 | dir[i].ppid = 0; | ||
760 | } | ||
761 | line = xmalloc(linemax + (timestamp ? 26 : 0)); | ||
762 | fndir = argv; | ||
763 | in.fd = 0; | ||
764 | in.events = IOPAUSE_READ; | ||
765 | ndelay_on(in.fd); | ||
766 | |||
767 | sig_block(sig_term); | ||
768 | sig_block(sig_child); | ||
769 | sig_block(sig_alarm); | ||
770 | sig_block(sig_hangup); | ||
771 | sig_catch(sig_term, sig_term_handler); | ||
772 | sig_catch(sig_child, sig_child_handler); | ||
773 | sig_catch(sig_alarm, sig_alarm_handler); | ||
774 | sig_catch(sig_hangup, sig_hangup_handler); | ||
775 | |||
776 | logdirs_reopen(); | ||
777 | |||
778 | /* Each iteration processes one line */ | ||
779 | while (1) { | ||
780 | int printlen; | ||
781 | char *lineptr = line; | ||
782 | char *np; | ||
783 | char ch; | ||
784 | |||
785 | /* Prepare timestamp if needed */ | ||
786 | if (timestamp) { | ||
787 | char stamp[FMT_PTIME]; | ||
788 | taia_now(&now); | ||
789 | switch (timestamp) { | ||
790 | case 1: | ||
791 | fmt_taia(stamp, &now); | ||
792 | break; | ||
793 | default: /* case 2: */ | ||
794 | fmt_ptime(stamp, &now); | ||
795 | break; | ||
796 | } | ||
797 | memcpy(line, stamp, 25); | ||
798 | line[25] = ' '; | ||
799 | lineptr += 26; | ||
800 | } | ||
801 | |||
802 | /* lineptr[0..linemax-1] - buffer for stdin */ | ||
803 | /* (possibly has some unprocessed data from prev loop) */ | ||
804 | |||
805 | /* Refill the buffer if needed */ | ||
806 | np = memchr(lineptr, '\n', stdin_cnt); | ||
807 | i = linemax - stdin_cnt; /* avail. bytes at tail */ | ||
808 | if (i >= 128 && !exitasap && !np) { | ||
809 | int sz = buffer_pread(0, lineptr + stdin_cnt, i); | ||
810 | if (sz <= 0) /* EOF or error on stdin */ | ||
811 | exitasap = 1; | ||
812 | else { | ||
813 | stdin_cnt += sz; | ||
814 | np = memchr(lineptr, '\n', stdin_cnt); | ||
815 | } | ||
816 | } | ||
817 | if (stdin_cnt <= 0 && exitasap) | ||
818 | break; | ||
819 | |||
820 | /* Search for '\n' (in fact, np already holds the result) */ | ||
821 | linelen = stdin_cnt; | ||
822 | if (np) linelen = np - lineptr + 1; | ||
823 | /* linelen == no of chars incl. '\n' (or == stdin_cnt) */ | ||
824 | ch = lineptr[linelen-1]; | ||
825 | |||
826 | printlen = linelen + (timestamp ? 26 : 0); | ||
827 | /* write out line[0..printlen-1] to each log destination */ | ||
828 | for (i = 0; i < dirn; ++i) { | ||
829 | struct logdir *ld = &dir[i]; | ||
830 | if (ld->fddir == -1) continue; | ||
831 | if (ld->inst) | ||
832 | logmatch(ld); | ||
833 | if (ld->matcherr == 'e') { | ||
834 | fprintf(stderr, "%.*s%s", | ||
835 | printlen, line, | ||
836 | (ch != '\n') ? "...\n" : "" | ||
837 | ); | ||
838 | } | ||
839 | if (ld->match != '+') continue; | ||
840 | buffer_pwrite(i, line, printlen); | ||
841 | } | ||
842 | |||
843 | /* If we didn't see '\n' (long input line), */ | ||
844 | /* read/write repeatedly until we see it */ | ||
845 | while (ch != '\n') { | ||
846 | /* lineptr is emptied now, safe to use as buffer */ | ||
847 | stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax); | ||
848 | if (stdin_cnt <= 0) { /* EOF or error on stdin */ | ||
849 | lineptr[0] = ch = '\n'; | ||
850 | linelen = 1; | ||
851 | exitasap = 1; | ||
852 | stdin_cnt = 1; | ||
853 | } else { | ||
854 | linelen = stdin_cnt; | ||
855 | np = memchr(lineptr, '\n', stdin_cnt); | ||
856 | if (np) linelen = np - lineptr + 1; | ||
857 | ch = lineptr[linelen-1]; | ||
858 | } | ||
859 | /* linelen == no of chars incl. '\n' (or == stdin_cnt) */ | ||
860 | for (i = 0; i < dirn; ++i) { | ||
861 | if (dir[i].fddir == -1) continue; | ||
862 | if (dir[i].match != '+') continue; | ||
863 | buffer_pwrite(i, lineptr, linelen); | ||
864 | } | ||
865 | } | ||
866 | |||
867 | /* Move unprocessed data to the front of line */ | ||
868 | stdin_cnt -= linelen; | ||
869 | if (stdin_cnt > 0) /* TODO: slow if buffer is big */ | ||
870 | memmove(lineptr, &lineptr[linelen], stdin_cnt); | ||
871 | } | ||
872 | |||
873 | for (i = 0; i < dirn; ++i) { | ||
874 | if (dir[i].ppid) | ||
875 | while (!processorstop(&dir[i])) | ||
876 | /* repeat */; | ||
877 | logdir_close(&dir[i]); | ||
878 | } | ||
879 | _exit(0); | ||
880 | } | ||