source: MondoRescue/branches/3.0/mindi-busybox/runit/svlogd.c@ 3085

Last change on this file since 3085 was 2725, checked in by Bruno Cornec, 13 years ago
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
  • Property svn:eol-style set to native
File size: 30.4 KB
Line 
1/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31/*
32Config files
33
34On startup, and after receiving a HUP signal, svlogd checks for each
35log directory log if the configuration file log/config exists,
36and if so, reads the file line by line and adjusts configuration
37for log as follows:
38
39If the line is empty, or starts with a #, it is ignored. A line
40of the form
41
42ssize
43 sets the maximum file size of current when svlogd should rotate
44 the current log file to size bytes. Default is 1000000.
45 If size is zero, svlogd doesnt rotate log files
46 You should set size to at least (2 * len).
47nnum
48 sets the number of old log files svlogd should maintain to num.
49 If svlogd sees more that num old log files in log after log file
50 rotation, it deletes the oldest one. Default is 10.
51 If num is zero, svlogd doesnt remove old log files.
52Nmin
53 sets the minimum number of old log files svlogd should maintain
54 to min. min must be less than num. If min is set, and svlogd
55 cannot write to current because the filesystem is full,
56 and it sees more than min old log files, it deletes the oldest one.
57ttimeout
58 sets the maximum age of the current log file when svlogd should
59 rotate the current log file to timeout seconds. If current
60 is timeout seconds old, and is not empty, svlogd forces log file rotation.
61!processor
62 tells svlogd to feed each recent log file through processor
63 (see above) on log file rotation. By default log files are not processed.
64ua.b.c.d[:port]
65 tells svlogd to transmit the first len characters of selected
66 log messages to the IP address a.b.c.d, port number port.
67 If port isnt set, the default port for syslog is used (514).
68 len can be set through the -l option, see below. If svlogd
69 has trouble sending udp packets, it writes error messages
70 to the log directory. Attention: logging through udp is unreliable,
71 and should be used in private networks only.
72Ua.b.c.d[:port]
73 is the same as the u line above, but the log messages are no longer
74 written to the log directory, but transmitted through udp only.
75 Error messages from svlogd concerning sending udp packages still go
76 to the log directory.
77pprefix
78 tells svlogd to prefix each line to be written to the log directory,
79 to standard error, or through UDP, with prefix.
80
81If a line starts with a -, +, e, or E, svlogd matches the first len characters
82of each log message against pattern and acts accordingly:
83
84-pattern
85 the log message is deselected.
86+pattern
87 the log message is selected.
88epattern
89 the log message is selected to be printed to standard error.
90Epattern
91 the log message is deselected to be printed to standard error.
92
93Initially each line is selected to be written to log/current. Deselected
94log messages are discarded from log. Initially each line is deselected
95to be written to standard err. Log messages selected for standard error
96are written to standard error.
97
98Pattern Matching
99
100svlogd matches a log message against the string pattern as follows:
101
102pattern is applied to the log message one character by one, starting
103with the first. A character not a star (*) and not a plus (+) matches itself.
104A plus matches the next character in pattern in the log message one
105or more times. A star before the end of pattern matches any string
106in the log message that does not include the next character in pattern.
107A star at the end of pattern matches any string.
108
109Timestamps optionally added by svlogd are not considered part
110of the log message.
111
112An svlogd pattern is not a regular expression. For example consider
113a log message like this
114
1152005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
116
117The following pattern doesnt match
118
119-*pid*
120
121because the first star matches up to the first p in tcpsvd,
122and then the match fails because i is not s. To match this
123log message, you can use a pattern like this instead
124
125-*: *: pid *
126*/
127
128#include <sys/poll.h>
129#include <sys/file.h>
130#include "libbb.h"
131#include "runit_lib.h"
132
133#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
134
135#define FMT_PTIME 30
136
137struct logdir {
138 ////char *btmp;
139 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
140 char *inst;
141 char *processor;
142 char *name;
143 unsigned size;
144 unsigned sizemax;
145 unsigned nmax;
146 unsigned nmin;
147 unsigned rotate_period;
148 int ppid;
149 int fddir;
150 int fdcur;
151 FILE* filecur; ////
152 int fdlock;
153 unsigned next_rotate;
154 char fnsave[FMT_PTIME];
155 char match;
156 char matcherr;
157};
158
159
160struct globals {
161 struct logdir *dir;
162 unsigned verbose;
163 int linemax;
164 ////int buflen;
165 int linelen;
166
167 int fdwdir;
168 char **fndir;
169 int wstat;
170 unsigned nearest_rotate;
171
172 void* (*memRchr)(const void *, int, size_t);
173
174 smallint exitasap;
175 smallint rotateasap;
176 smallint reopenasap;
177 smallint linecomplete;
178 smallint tmaxflag;
179
180 char repl;
181 const char *replace;
182 int fl_flag_0;
183 unsigned dirn;
184
185 sigset_t blocked_sigset;
186};
187#define G (*ptr_to_globals)
188#define dir (G.dir )
189#define verbose (G.verbose )
190#define linemax (G.linemax )
191#define buflen (G.buflen )
192#define linelen (G.linelen )
193#define fndir (G.fndir )
194#define fdwdir (G.fdwdir )
195#define wstat (G.wstat )
196#define memRchr (G.memRchr )
197#define nearest_rotate (G.nearest_rotate)
198#define exitasap (G.exitasap )
199#define rotateasap (G.rotateasap )
200#define reopenasap (G.reopenasap )
201#define linecomplete (G.linecomplete )
202#define tmaxflag (G.tmaxflag )
203#define repl (G.repl )
204#define replace (G.replace )
205#define blocked_sigset (G.blocked_sigset)
206#define fl_flag_0 (G.fl_flag_0 )
207#define dirn (G.dirn )
208#define INIT_G() do { \
209 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
210 linemax = 1000; \
211 /*buflen = 1024;*/ \
212 linecomplete = 1; \
213 replace = ""; \
214} while (0)
215
216#define line bb_common_bufsiz1
217
218
219#define FATAL "fatal: "
220#define WARNING "warning: "
221#define PAUSE "pausing: "
222#define INFO "info: "
223
224static void fatalx(const char *m0)
225{
226 bb_error_msg_and_die(FATAL"%s", m0);
227}
228static void warn(const char *m0)
229{
230 bb_perror_msg(WARNING"%s", m0);
231}
232static void warn2(const char *m0, const char *m1)
233{
234 bb_perror_msg(WARNING"%s: %s", m0, m1);
235}
236static void warnx(const char *m0, const char *m1)
237{
238 bb_error_msg(WARNING"%s: %s", m0, m1);
239}
240static void pause_nomem(void)
241{
242 bb_error_msg(PAUSE"out of memory");
243 sleep(3);
244}
245static void pause1cannot(const char *m0)
246{
247 bb_perror_msg(PAUSE"can't %s", m0);
248 sleep(3);
249}
250static void pause2cannot(const char *m0, const char *m1)
251{
252 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
253 sleep(3);
254}
255
256static char* wstrdup(const char *str)
257{
258 char *s;
259 while (!(s = strdup(str)))
260 pause_nomem();
261 return s;
262}
263
264static unsigned pmatch(const char *p, const char *s, unsigned len)
265{
266 for (;;) {
267 char c = *p++;
268 if (!c) return !len;
269 switch (c) {
270 case '*':
271 c = *p;
272 if (!c) return 1;
273 for (;;) {
274 if (!len) return 0;
275 if (*s == c) break;
276 ++s;
277 --len;
278 }
279 continue;
280 case '+':
281 c = *p++;
282 if (c != *s) return 0;
283 for (;;) {
284 if (!len) return 1;
285 if (*s != c) break;
286 ++s;
287 --len;
288 }
289 continue;
290 /*
291 case '?':
292 if (*p == '?') {
293 if (*s != '?') return 0;
294 ++p;
295 }
296 ++s; --len;
297 continue;
298 */
299 default:
300 if (!len) return 0;
301 if (*s != c) return 0;
302 ++s;
303 --len;
304 continue;
305 }
306 }
307 return 0;
308}
309
310/*** ex fmt_ptime.[ch] ***/
311
312/* NUL terminated */
313static void fmt_time_human_30nul(char *s)
314{
315 struct tm *ptm;
316 struct timeval tv;
317
318 gettimeofday(&tv, NULL);
319 ptm = gmtime(&tv.tv_sec);
320 sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
321 (unsigned)(1900 + ptm->tm_year),
322 (unsigned)(ptm->tm_mon + 1),
323 (unsigned)(ptm->tm_mday),
324 (unsigned)(ptm->tm_hour),
325 (unsigned)(ptm->tm_min),
326 (unsigned)(ptm->tm_sec),
327 (unsigned)(tv.tv_usec)
328 );
329 /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
330 /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
331 /* 20 (up to '.' inclusive) + 9 (not including '\0') */
332}
333
334/* NOT terminated! */
335static void fmt_time_bernstein_25(char *s)
336{
337 uint32_t pack[3];
338 struct timeval tv;
339 unsigned sec_hi;
340
341 gettimeofday(&tv, NULL);
342 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
343 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
344 tv.tv_usec *= 1000;
345 /* Network order is big-endian: most significant byte first.
346 * This is exactly what we want here */
347 pack[0] = htonl(sec_hi);
348 pack[1] = htonl(tv.tv_sec);
349 pack[2] = htonl(tv.tv_usec);
350 *s++ = '@';
351 bin2hex(s, (char*)pack, 12);
352}
353
354static void processorstart(struct logdir *ld)
355{
356 char sv_ch;
357 int pid;
358
359 if (!ld->processor) return;
360 if (ld->ppid) {
361 warnx("processor already running", ld->name);
362 return;
363 }
364
365 /* vfork'ed child trashes this byte, save... */
366 sv_ch = ld->fnsave[26];
367
368 while ((pid = vfork()) == -1)
369 pause2cannot("vfork for processor", ld->name);
370 if (!pid) {
371 int fd;
372
373 /* child */
374 /* Non-ignored signals revert to SIG_DFL on exec anyway */
375 /*bb_signals(0
376 + (1 << SIGTERM)
377 + (1 << SIGALRM)
378 + (1 << SIGHUP)
379 , SIG_DFL);*/
380 sig_unblock(SIGTERM);
381 sig_unblock(SIGALRM);
382 sig_unblock(SIGHUP);
383
384 if (verbose)
385 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
386 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
387 xmove_fd(fd, 0);
388 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
389 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
390 xmove_fd(fd, 1);
391 fd = open("state", O_RDONLY|O_NDELAY);
392 if (fd == -1) {
393 if (errno != ENOENT)
394 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
395 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
396 fd = xopen("state", O_RDONLY|O_NDELAY);
397 }
398 xmove_fd(fd, 4);
399 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
400 xmove_fd(fd, 5);
401
402// getenv("SHELL")?
403 execl(DEFAULT_SHELL, DEFAULT_SHELL_SHORT_NAME, "-c", ld->processor, (char*) NULL);
404 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
405 }
406 ld->fnsave[26] = sv_ch; /* ...restore */
407 ld->ppid = pid;
408}
409
410static unsigned processorstop(struct logdir *ld)
411{
412 char f[28];
413
414 if (ld->ppid) {
415 sig_unblock(SIGHUP);
416 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
417 pause2cannot("wait for processor", ld->name);
418 sig_block(SIGHUP);
419 ld->ppid = 0;
420 }
421 if (ld->fddir == -1)
422 return 1;
423 while (fchdir(ld->fddir) == -1)
424 pause2cannot("change directory, want processor", ld->name);
425 if (WEXITSTATUS(wstat) != 0) {
426 warnx("processor failed, restart", ld->name);
427 ld->fnsave[26] = 't';
428 unlink(ld->fnsave);
429 ld->fnsave[26] = 'u';
430 processorstart(ld);
431 while (fchdir(fdwdir) == -1)
432 pause1cannot("change to initial working directory");
433 return ld->processor ? 0 : 1;
434 }
435 ld->fnsave[26] = 't';
436 memcpy(f, ld->fnsave, 26);
437 f[26] = 's';
438 f[27] = '\0';
439 while (rename(ld->fnsave, f) == -1)
440 pause2cannot("rename processed", ld->name);
441 while (chmod(f, 0744) == -1)
442 pause2cannot("set mode of processed", ld->name);
443 ld->fnsave[26] = 'u';
444 if (unlink(ld->fnsave) == -1)
445 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
446 while (rename("newstate", "state") == -1)
447 pause2cannot("rename state", ld->name);
448 if (verbose)
449 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
450 while (fchdir(fdwdir) == -1)
451 pause1cannot("change to initial working directory");
452 return 1;
453}
454
455static void rmoldest(struct logdir *ld)
456{
457 DIR *d;
458 struct dirent *f;
459 char oldest[FMT_PTIME];
460 int n = 0;
461
462 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
463 while (!(d = opendir(".")))
464 pause2cannot("open directory, want rotate", ld->name);
465 errno = 0;
466 while ((f = readdir(d))) {
467 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
468 if (f->d_name[26] == 't') {
469 if (unlink(f->d_name) == -1)
470 warn2("can't unlink processor leftover", f->d_name);
471 } else {
472 ++n;
473 if (strcmp(f->d_name, oldest) < 0)
474 memcpy(oldest, f->d_name, 27);
475 }
476 errno = 0;
477 }
478 }
479 if (errno)
480 warn2("can't read directory", ld->name);
481 closedir(d);
482
483 if (ld->nmax && (n > ld->nmax)) {
484 if (verbose)
485 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
486 if ((*oldest == '@') && (unlink(oldest) == -1))
487 warn2("can't unlink oldest logfile", ld->name);
488 }
489}
490
491static unsigned rotate(struct logdir *ld)
492{
493 struct stat st;
494 unsigned now;
495
496 if (ld->fddir == -1) {
497 ld->rotate_period = 0;
498 return 0;
499 }
500 if (ld->ppid)
501 while (!processorstop(ld))
502 continue;
503
504 while (fchdir(ld->fddir) == -1)
505 pause2cannot("change directory, want rotate", ld->name);
506
507 /* create new filename */
508 ld->fnsave[25] = '.';
509 ld->fnsave[26] = 's';
510 if (ld->processor)
511 ld->fnsave[26] = 'u';
512 ld->fnsave[27] = '\0';
513 do {
514 fmt_time_bernstein_25(ld->fnsave);
515 errno = 0;
516 stat(ld->fnsave, &st);
517 } while (errno != ENOENT);
518
519 now = monotonic_sec();
520 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
521 ld->next_rotate = now + ld->rotate_period;
522 if (LESS(ld->next_rotate, nearest_rotate))
523 nearest_rotate = ld->next_rotate;
524 }
525
526 if (ld->size > 0) {
527 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
528 pause2cannot("fsync current logfile", ld->name);
529 while (fchmod(ld->fdcur, 0744) == -1)
530 pause2cannot("set mode of current", ld->name);
531 ////close(ld->fdcur);
532 fclose(ld->filecur);
533
534 if (verbose) {
535 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
536 ld->fnsave, ld->size);
537 }
538 while (rename("current", ld->fnsave) == -1)
539 pause2cannot("rename current", ld->name);
540 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
541 pause2cannot("create new current", ld->name);
542 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
543 pause2cannot("create new current", ld->name); /* very unlikely */
544 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
545 close_on_exec_on(ld->fdcur);
546 ld->size = 0;
547 while (fchmod(ld->fdcur, 0644) == -1)
548 pause2cannot("set mode of current", ld->name);
549
550 rmoldest(ld);
551 processorstart(ld);
552 }
553
554 while (fchdir(fdwdir) == -1)
555 pause1cannot("change to initial working directory");
556 return 1;
557}
558
559static int buffer_pwrite(int n, char *s, unsigned len)
560{
561 int i;
562 struct logdir *ld = &dir[n];
563
564 if (ld->sizemax) {
565 if (ld->size >= ld->sizemax)
566 rotate(ld);
567 if (len > (ld->sizemax - ld->size))
568 len = ld->sizemax - ld->size;
569 }
570 while (1) {
571 ////i = full_write(ld->fdcur, s, len);
572 ////if (i != -1) break;
573 i = fwrite(s, 1, len, ld->filecur);
574 if (i == len) break;
575
576 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
577 DIR *d;
578 struct dirent *f;
579 char oldest[FMT_PTIME];
580 int j = 0;
581
582 while (fchdir(ld->fddir) == -1)
583 pause2cannot("change directory, want remove old logfile",
584 ld->name);
585 oldest[0] = 'A';
586 oldest[1] = oldest[27] = '\0';
587 while (!(d = opendir(".")))
588 pause2cannot("open directory, want remove old logfile",
589 ld->name);
590 errno = 0;
591 while ((f = readdir(d)))
592 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
593 ++j;
594 if (strcmp(f->d_name, oldest) < 0)
595 memcpy(oldest, f->d_name, 27);
596 }
597 if (errno) warn2("can't read directory, want remove old logfile",
598 ld->name);
599 closedir(d);
600 errno = ENOSPC;
601 if (j > ld->nmin) {
602 if (*oldest == '@') {
603 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
604 ld->name, oldest);
605 errno = 0;
606 if (unlink(oldest) == -1) {
607 warn2("can't unlink oldest logfile", ld->name);
608 errno = ENOSPC;
609 }
610 while (fchdir(fdwdir) == -1)
611 pause1cannot("change to initial working directory");
612 }
613 }
614 }
615 if (errno)
616 pause2cannot("write to current", ld->name);
617 }
618
619 ld->size += i;
620 if (ld->sizemax)
621 if (s[i-1] == '\n')
622 if (ld->size >= (ld->sizemax - linemax))
623 rotate(ld);
624 return i;
625}
626
627static void logdir_close(struct logdir *ld)
628{
629 if (ld->fddir == -1)
630 return;
631 if (verbose)
632 bb_error_msg(INFO"close: %s", ld->name);
633 close(ld->fddir);
634 ld->fddir = -1;
635 if (ld->fdcur == -1)
636 return; /* impossible */
637 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
638 pause2cannot("fsync current logfile", ld->name);
639 while (fchmod(ld->fdcur, 0744) == -1)
640 pause2cannot("set mode of current", ld->name);
641 ////close(ld->fdcur);
642 fclose(ld->filecur);
643 ld->fdcur = -1;
644 if (ld->fdlock == -1)
645 return; /* impossible */
646 close(ld->fdlock);
647 ld->fdlock = -1;
648 free(ld->processor);
649 ld->processor = NULL;
650}
651
652static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
653{
654 char buf[128];
655 unsigned now;
656 char *new, *s, *np;
657 int i;
658 struct stat st;
659
660 now = monotonic_sec();
661
662 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
663 if (ld->fddir == -1) {
664 warn2("can't open log directory", (char*)fn);
665 return 0;
666 }
667 close_on_exec_on(ld->fddir);
668 if (fchdir(ld->fddir) == -1) {
669 logdir_close(ld);
670 warn2("can't change directory", (char*)fn);
671 return 0;
672 }
673 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
674 if ((ld->fdlock == -1)
675 || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
676 ) {
677 logdir_close(ld);
678 warn2("can't lock directory", (char*)fn);
679 while (fchdir(fdwdir) == -1)
680 pause1cannot("change to initial working directory");
681 return 0;
682 }
683 close_on_exec_on(ld->fdlock);
684
685 ld->size = 0;
686 ld->sizemax = 1000000;
687 ld->nmax = ld->nmin = 10;
688 ld->rotate_period = 0;
689 ld->name = (char*)fn;
690 ld->ppid = 0;
691 ld->match = '+';
692 free(ld->inst); ld->inst = NULL;
693 free(ld->processor); ld->processor = NULL;
694
695 /* read config */
696 i = open_read_close("config", buf, sizeof(buf) - 1);
697 if (i < 0 && errno != ENOENT)
698 bb_perror_msg(WARNING"%s/config", ld->name);
699 if (i > 0) {
700 buf[i] = '\0';
701 if (verbose)
702 bb_error_msg(INFO"read: %s/config", ld->name);
703 s = buf;
704 while (s) {
705 np = strchr(s, '\n');
706 if (np)
707 *np++ = '\0';
708 switch (s[0]) {
709 case '+':
710 case '-':
711 case 'e':
712 case 'E':
713 /* Filtering requires one-line buffering,
714 * resetting the "find newline" function
715 * accordingly */
716 memRchr = memchr;
717 /* Add '\n'-terminated line to ld->inst */
718 while (1) {
719 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
720 if (l >= 0 && new)
721 break;
722 pause_nomem();
723 }
724 free(ld->inst);
725 ld->inst = new;
726 break;
727 case 's': {
728 static const struct suffix_mult km_suffixes[] = {
729 { "k", 1024 },
730 { "m", 1024*1024 },
731 { "", 0 }
732 };
733 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
734 break;
735 }
736 case 'n':
737 ld->nmax = xatoi_positive(&s[1]);
738 break;
739 case 'N':
740 ld->nmin = xatoi_positive(&s[1]);
741 break;
742 case 't': {
743 static const struct suffix_mult mh_suffixes[] = {
744 { "m", 60 },
745 { "h", 60*60 },
746 /*{ "d", 24*60*60 },*/
747 { "", 0 }
748 };
749 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
750 if (ld->rotate_period) {
751 ld->next_rotate = now + ld->rotate_period;
752 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
753 nearest_rotate = ld->next_rotate;
754 tmaxflag = 1;
755 }
756 break;
757 }
758 case '!':
759 if (s[1]) {
760 free(ld->processor);
761 ld->processor = wstrdup(s);
762 }
763 break;
764 }
765 s = np;
766 }
767 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
768 s = ld->inst;
769 while (s) {
770 np = strchr(s, '\n');
771 if (np)
772 *np++ = '\0';
773 s = np;
774 }
775 }
776
777 /* open current */
778 i = stat("current", &st);
779 if (i != -1) {
780 if (st.st_size && !(st.st_mode & S_IXUSR)) {
781 ld->fnsave[25] = '.';
782 ld->fnsave[26] = 'u';
783 ld->fnsave[27] = '\0';
784 do {
785 fmt_time_bernstein_25(ld->fnsave);
786 errno = 0;
787 stat(ld->fnsave, &st);
788 } while (errno != ENOENT);
789 while (rename("current", ld->fnsave) == -1)
790 pause2cannot("rename current", ld->name);
791 rmoldest(ld);
792 i = -1;
793 } else {
794 /* st.st_size can be not just bigger, but WIDER!
795 * This code is safe: if st.st_size > 4GB, we select
796 * ld->sizemax (because it's "unsigned") */
797 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
798 }
799 } else {
800 if (errno != ENOENT) {
801 logdir_close(ld);
802 warn2("can't stat current", ld->name);
803 while (fchdir(fdwdir) == -1)
804 pause1cannot("change to initial working directory");
805 return 0;
806 }
807 }
808 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
809 pause2cannot("open current", ld->name);
810 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
811 pause2cannot("open current", ld->name); ////
812 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
813
814 close_on_exec_on(ld->fdcur);
815 while (fchmod(ld->fdcur, 0644) == -1)
816 pause2cannot("set mode of current", ld->name);
817
818 if (verbose) {
819 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
820 else bb_error_msg(INFO"new: %s/current", ld->name);
821 }
822
823 while (fchdir(fdwdir) == -1)
824 pause1cannot("change to initial working directory");
825 return 1;
826}
827
828static void logdirs_reopen(void)
829{
830 int l;
831 int ok = 0;
832
833 tmaxflag = 0;
834 for (l = 0; l < dirn; ++l) {
835 logdir_close(&dir[l]);
836 if (logdir_open(&dir[l], fndir[l]))
837 ok = 1;
838 }
839 if (!ok)
840 fatalx("no functional log directories");
841}
842
843/* Will look good in libbb one day */
844static ssize_t ndelay_read(int fd, void *buf, size_t count)
845{
846 if (!(fl_flag_0 & O_NONBLOCK))
847 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
848 count = safe_read(fd, buf, count);
849 if (!(fl_flag_0 & O_NONBLOCK))
850 fcntl(fd, F_SETFL, fl_flag_0);
851 return count;
852}
853
854/* Used for reading stdin */
855static int buffer_pread(/*int fd, */char *s, unsigned len)
856{
857 unsigned now;
858 struct pollfd input;
859 int i;
860
861 input.fd = STDIN_FILENO;
862 input.events = POLLIN;
863
864 do {
865 if (rotateasap) {
866 for (i = 0; i < dirn; ++i)
867 rotate(dir + i);
868 rotateasap = 0;
869 }
870 if (exitasap) {
871 if (linecomplete)
872 return 0;
873 len = 1;
874 }
875 if (reopenasap) {
876 logdirs_reopen();
877 reopenasap = 0;
878 }
879 now = monotonic_sec();
880 nearest_rotate = now + (45 * 60 + 45);
881 for (i = 0; i < dirn; ++i) {
882 if (dir[i].rotate_period) {
883 if (LESS(dir[i].next_rotate, now))
884 rotate(dir + i);
885 if (LESS(dir[i].next_rotate, nearest_rotate))
886 nearest_rotate = dir[i].next_rotate;
887 }
888 }
889
890 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
891 i = nearest_rotate - now;
892 if (i > 1000000)
893 i = 1000000;
894 if (i <= 0)
895 i = 1;
896 poll(&input, 1, i * 1000);
897 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
898
899 i = ndelay_read(STDIN_FILENO, s, len);
900 if (i >= 0)
901 break;
902 if (errno == EINTR)
903 continue;
904 if (errno != EAGAIN) {
905 warn("can't read standard input");
906 break;
907 }
908 /* else: EAGAIN - normal, repeat silently */
909 } while (!exitasap);
910
911 if (i > 0) {
912 int cnt;
913 linecomplete = (s[i-1] == '\n');
914 if (!repl)
915 return i;
916
917 cnt = i;
918 while (--cnt >= 0) {
919 char ch = *s;
920 if (ch != '\n') {
921 if (ch < 32 || ch > 126)
922 *s = repl;
923 else {
924 int j;
925 for (j = 0; replace[j]; ++j) {
926 if (ch == replace[j]) {
927 *s = repl;
928 break;
929 }
930 }
931 }
932 }
933 s++;
934 }
935 }
936 return i;
937}
938
939static void sig_term_handler(int sig_no UNUSED_PARAM)
940{
941 if (verbose)
942 bb_error_msg(INFO"sig%s received", "term");
943 exitasap = 1;
944}
945
946static void sig_child_handler(int sig_no UNUSED_PARAM)
947{
948 pid_t pid;
949 int l;
950
951 if (verbose)
952 bb_error_msg(INFO"sig%s received", "child");
953 while ((pid = wait_any_nohang(&wstat)) > 0) {
954 for (l = 0; l < dirn; ++l) {
955 if (dir[l].ppid == pid) {
956 dir[l].ppid = 0;
957 processorstop(&dir[l]);
958 break;
959 }
960 }
961 }
962}
963
964static void sig_alarm_handler(int sig_no UNUSED_PARAM)
965{
966 if (verbose)
967 bb_error_msg(INFO"sig%s received", "alarm");
968 rotateasap = 1;
969}
970
971static void sig_hangup_handler(int sig_no UNUSED_PARAM)
972{
973 if (verbose)
974 bb_error_msg(INFO"sig%s received", "hangup");
975 reopenasap = 1;
976}
977
978static void logmatch(struct logdir *ld)
979{
980 char *s;
981
982 ld->match = '+';
983 ld->matcherr = 'E';
984 s = ld->inst;
985 while (s && s[0]) {
986 switch (s[0]) {
987 case '+':
988 case '-':
989 if (pmatch(s+1, line, linelen))
990 ld->match = s[0];
991 break;
992 case 'e':
993 case 'E':
994 if (pmatch(s+1, line, linelen))
995 ld->matcherr = s[0];
996 break;
997 }
998 s += strlen(s) + 1;
999 }
1000}
1001
1002int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1003int svlogd_main(int argc, char **argv)
1004{
1005 char *r, *l, *b;
1006 ssize_t stdin_cnt = 0;
1007 int i;
1008 unsigned opt;
1009 unsigned timestamp = 0;
1010
1011 INIT_G();
1012
1013 opt_complementary = "tt:vv";
1014 opt = getopt32(argv, "r:R:l:b:tv",
1015 &r, &replace, &l, &b, &timestamp, &verbose);
1016 if (opt & 1) { // -r
1017 repl = r[0];
1018 if (!repl || r[1])
1019 bb_show_usage();
1020 }
1021 if (opt & 2) if (!repl) repl = '_'; // -R
1022 if (opt & 4) { // -l
1023 linemax = xatou_range(l, 0, BUFSIZ-26);
1024 if (linemax == 0)
1025 linemax = BUFSIZ-26;
1026 if (linemax < 256)
1027 linemax = 256;
1028 }
1029 ////if (opt & 8) { // -b
1030 //// buflen = xatoi_positive(b);
1031 //// if (buflen == 0) buflen = 1024;
1032 ////}
1033 //if (opt & 0x10) timestamp++; // -t
1034 //if (opt & 0x20) verbose++; // -v
1035 //if (timestamp > 2) timestamp = 2;
1036 argv += optind;
1037 argc -= optind;
1038
1039 dirn = argc;
1040 if (dirn <= 0)
1041 bb_show_usage();
1042 ////if (buflen <= linemax) bb_show_usage();
1043 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
1044 close_on_exec_on(fdwdir);
1045 dir = xzalloc(dirn * sizeof(dir[0]));
1046 for (i = 0; i < dirn; ++i) {
1047 dir[i].fddir = -1;
1048 dir[i].fdcur = -1;
1049 ////dir[i].btmp = xmalloc(buflen);
1050 /*dir[i].ppid = 0;*/
1051 }
1052 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
1053 fndir = argv;
1054 /* We cannot set NONBLOCK on fd #0 permanently - this setting
1055 * _isn't_ per-process! It is shared among all other processes
1056 * with the same stdin */
1057 fl_flag_0 = fcntl(0, F_GETFL);
1058
1059 sigemptyset(&blocked_sigset);
1060 sigaddset(&blocked_sigset, SIGTERM);
1061 sigaddset(&blocked_sigset, SIGCHLD);
1062 sigaddset(&blocked_sigset, SIGALRM);
1063 sigaddset(&blocked_sigset, SIGHUP);
1064 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
1065 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
1066 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
1067 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
1068 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
1069
1070 /* Without timestamps, we don't have to print each line
1071 * separately, so we can look for _last_ newline, not first,
1072 * thus batching writes. If filtering is enabled in config,
1073 * logdirs_reopen resets it to memchr.
1074 */
1075 memRchr = (timestamp ? memchr : memrchr);
1076
1077 logdirs_reopen();
1078
1079 setvbuf(stderr, NULL, _IOFBF, linelen);
1080
1081 /* Each iteration processes one or more lines */
1082 while (1) {
1083 char stamp[FMT_PTIME];
1084 char *lineptr;
1085 char *printptr;
1086 char *np;
1087 int printlen;
1088 char ch;
1089
1090 lineptr = line;
1091 if (timestamp)
1092 lineptr += 26;
1093
1094 /* lineptr[0..linemax-1] - buffer for stdin */
1095 /* (possibly has some unprocessed data from prev loop) */
1096
1097 /* Refill the buffer if needed */
1098 np = memRchr(lineptr, '\n', stdin_cnt);
1099 if (!np && !exitasap) {
1100 i = linemax - stdin_cnt; /* avail. bytes at tail */
1101 if (i >= 128) {
1102 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
1103 if (i <= 0) /* EOF or error on stdin */
1104 exitasap = 1;
1105 else {
1106 np = memRchr(lineptr + stdin_cnt, '\n', i);
1107 stdin_cnt += i;
1108 }
1109 }
1110 }
1111 if (stdin_cnt <= 0 && exitasap)
1112 break;
1113
1114 /* Search for '\n' (in fact, np already holds the result) */
1115 linelen = stdin_cnt;
1116 if (np) {
1117 print_to_nl:
1118 /* NB: starting from here lineptr may point
1119 * farther out into line[] */
1120 linelen = np - lineptr + 1;
1121 }
1122 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1123 ch = lineptr[linelen-1];
1124
1125 /* Biggest performance hit was coming from the fact
1126 * that we did not buffer writes. We were reading many lines
1127 * in one read() above, but wrote one line per write().
1128 * We are using stdio to fix that */
1129
1130 /* write out lineptr[0..linelen-1] to each log destination
1131 * (or lineptr[-26..linelen-1] if timestamping) */
1132 printlen = linelen;
1133 printptr = lineptr;
1134 if (timestamp) {
1135 if (timestamp == 1)
1136 fmt_time_bernstein_25(stamp);
1137 else /* 2: */
1138 fmt_time_human_30nul(stamp);
1139 printlen += 26;
1140 printptr -= 26;
1141 memcpy(printptr, stamp, 25);
1142 printptr[25] = ' ';
1143 }
1144 for (i = 0; i < dirn; ++i) {
1145 struct logdir *ld = &dir[i];
1146 if (ld->fddir == -1)
1147 continue;
1148 if (ld->inst)
1149 logmatch(ld);
1150 if (ld->matcherr == 'e') {
1151 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
1152 ////full_write(STDERR_FILENO, printptr, printlen);
1153 fwrite(printptr, 1, printlen, stderr);
1154 }
1155 if (ld->match != '+')
1156 continue;
1157 buffer_pwrite(i, printptr, printlen);
1158 }
1159
1160 /* If we didn't see '\n' (long input line), */
1161 /* read/write repeatedly until we see it */
1162 while (ch != '\n') {
1163 /* lineptr is emptied now, safe to use as buffer */
1164 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
1165 if (stdin_cnt <= 0) { /* EOF or error on stdin */
1166 exitasap = 1;
1167 lineptr[0] = ch = '\n';
1168 linelen = 1;
1169 stdin_cnt = 1;
1170 } else {
1171 linelen = stdin_cnt;
1172 np = memRchr(lineptr, '\n', stdin_cnt);
1173 if (np)
1174 linelen = np - lineptr + 1;
1175 ch = lineptr[linelen-1];
1176 }
1177 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1178 for (i = 0; i < dirn; ++i) {
1179 if (dir[i].fddir == -1)
1180 continue;
1181 if (dir[i].matcherr == 'e') {
1182 ////full_write(STDERR_FILENO, lineptr, linelen);
1183 fwrite(lineptr, 1, linelen, stderr);
1184 }
1185 if (dir[i].match != '+')
1186 continue;
1187 buffer_pwrite(i, lineptr, linelen);
1188 }
1189 }
1190
1191 stdin_cnt -= linelen;
1192 if (stdin_cnt > 0) {
1193 lineptr += linelen;
1194 /* If we see another '\n', we don't need to read
1195 * next piece of input: can print what we have */
1196 np = memRchr(lineptr, '\n', stdin_cnt);
1197 if (np)
1198 goto print_to_nl;
1199 /* Move unprocessed data to the front of line */
1200 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1201 }
1202 fflush_all();////
1203 }
1204
1205 for (i = 0; i < dirn; ++i) {
1206 if (dir[i].ppid)
1207 while (!processorstop(&dir[i]))
1208 continue;
1209 logdir_close(&dir[i]);
1210 }
1211 return 0;
1212}
Note: See TracBrowser for help on using the repository browser.