Merge branch 'Teaman-ND' into Teaman-RT
[tomato.git] / release / src / router / busybox / runit / svlogd.c
blobc3ff4e9c1832aef439b4ebdd0348217e190ccd20
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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.
28 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
32 Config files
34 On startup, and after receiving a HUP signal, svlogd checks for each
35 log directory log if the configuration file log/config exists,
36 and if so, reads the file line by line and adjusts configuration
37 for log as follows:
39 If the line is empty, or starts with a #, it is ignored. A line
40 of the form
42 ssize
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).
47 nnum
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.
52 Nmin
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.
57 ttimeout
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.
64 ua.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.
72 Ua.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.
77 pprefix
78 tells svlogd to prefix each line to be written to the log directory,
79 to standard error, or through UDP, with prefix.
81 If a line starts with a -, +, e, or E, svlogd matches the first len characters
82 of each log message against pattern and acts accordingly:
84 -pattern
85 the log message is deselected.
86 +pattern
87 the log message is selected.
88 epattern
89 the log message is selected to be printed to standard error.
90 Epattern
91 the log message is deselected to be printed to standard error.
93 Initially each line is selected to be written to log/current. Deselected
94 log messages are discarded from log. Initially each line is deselected
95 to be written to standard err. Log messages selected for standard error
96 are written to standard error.
98 Pattern Matching
100 svlogd matches a log message against the string pattern as follows:
102 pattern is applied to the log message one character by one, starting
103 with the first. A character not a star (*) and not a plus (+) matches itself.
104 A plus matches the next character in pattern in the log message one
105 or more times. A star before the end of pattern matches any string
106 in the log message that does not include the next character in pattern.
107 A star at the end of pattern matches any string.
109 Timestamps optionally added by svlogd are not considered part
110 of the log message.
112 An svlogd pattern is not a regular expression. For example consider
113 a log message like this
115 2005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
117 The following pattern doesnt match
119 -*pid*
121 because the first star matches up to the first p in tcpsvd,
122 and then the match fails because i is not s. To match this
123 log message, you can use a pattern like this instead
125 -*: *: pid *
128 #include <sys/poll.h>
129 #include <sys/file.h>
130 #include "libbb.h"
131 #include "runit_lib.h"
133 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
135 #define FMT_PTIME 30
137 struct 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;
160 struct globals {
161 struct logdir *dir;
162 unsigned verbose;
163 int linemax;
164 ////int buflen;
165 int linelen;
167 int fdwdir;
168 char **fndir;
169 int wstat;
170 unsigned nearest_rotate;
172 void* (*memRchr)(const void *, int, size_t);
174 smallint exitasap;
175 smallint rotateasap;
176 smallint reopenasap;
177 smallint linecomplete;
178 smallint tmaxflag;
180 char repl;
181 const char *replace;
182 int fl_flag_0;
183 unsigned dirn;
185 sigset_t blocked_sigset;
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)
216 #define line bb_common_bufsiz1
219 #define FATAL "fatal: "
220 #define WARNING "warning: "
221 #define PAUSE "pausing: "
222 #define INFO "info: "
224 static void fatalx(const char *m0)
226 bb_error_msg_and_die(FATAL"%s", m0);
228 static void warn(const char *m0)
230 bb_perror_msg(WARNING"%s", m0);
232 static void warn2(const char *m0, const char *m1)
234 bb_perror_msg(WARNING"%s: %s", m0, m1);
236 static void warnx(const char *m0, const char *m1)
238 bb_error_msg(WARNING"%s: %s", m0, m1);
240 static void pause_nomem(void)
242 bb_error_msg(PAUSE"out of memory");
243 sleep(3);
245 static void pause1cannot(const char *m0)
247 bb_perror_msg(PAUSE"can't %s", m0);
248 sleep(3);
250 static void pause2cannot(const char *m0, const char *m1)
252 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
253 sleep(3);
256 static char* wstrdup(const char *str)
258 char *s;
259 while (!(s = strdup(str)))
260 pause_nomem();
261 return s;
264 static unsigned pmatch(const char *p, const char *s, unsigned len)
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;
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;
289 continue;
291 case '?':
292 if (*p == '?') {
293 if (*s != '?') return 0;
294 ++p;
296 ++s; --len;
297 continue;
299 default:
300 if (!len) return 0;
301 if (*s != c) return 0;
302 ++s;
303 --len;
304 continue;
307 return 0;
310 /*** ex fmt_ptime.[ch] ***/
312 /* NUL terminated */
313 static void fmt_time_human_30nul(char *s)
315 struct tm *ptm;
316 struct timeval tv;
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)
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') */
334 /* NOT terminated! */
335 static void fmt_time_bernstein_25(char *s)
337 uint32_t pack[3];
338 struct timeval tv;
339 unsigned sec_hi;
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);
354 static void processorstart(struct logdir *ld)
356 char sv_ch;
357 int pid;
359 if (!ld->processor) return;
360 if (ld->ppid) {
361 warnx("processor already running", ld->name);
362 return;
365 /* vfork'ed child trashes this byte, save... */
366 sv_ch = ld->fnsave[26];
368 while ((pid = vfork()) == -1)
369 pause2cannot("vfork for processor", ld->name);
370 if (!pid) {
371 int fd;
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);
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);
398 xmove_fd(fd, 4);
399 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
400 xmove_fd(fd, 5);
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);
406 ld->fnsave[26] = sv_ch; /* ...restore */
407 ld->ppid = pid;
410 static unsigned processorstop(struct logdir *ld)
412 char f[28];
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;
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;
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;
455 static void rmoldest(struct logdir *ld)
457 DIR *d;
458 struct dirent *f;
459 char oldest[FMT_PTIME];
460 int n = 0;
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);
476 errno = 0;
479 if (errno)
480 warn2("can't read directory", ld->name);
481 closedir(d);
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);
491 static unsigned rotate(struct logdir *ld)
493 struct stat st;
494 unsigned now;
496 if (ld->fddir == -1) {
497 ld->rotate_period = 0;
498 return 0;
500 if (ld->ppid)
501 while (!processorstop(ld))
502 continue;
504 while (fchdir(ld->fddir) == -1)
505 pause2cannot("change directory, want rotate", ld->name);
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);
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;
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);
534 if (verbose) {
535 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
536 ld->fnsave, ld->size);
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);
550 rmoldest(ld);
551 processorstart(ld);
554 while (fchdir(fdwdir) == -1)
555 pause1cannot("change to initial working directory");
556 return 1;
559 static int buffer_pwrite(int n, char *s, unsigned len)
561 int i;
562 struct logdir *ld = &dir[n];
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;
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;
576 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
577 DIR *d;
578 struct dirent *f;
579 char oldest[FMT_PTIME];
580 int j = 0;
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);
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;
610 while (fchdir(fdwdir) == -1)
611 pause1cannot("change to initial working directory");
615 if (errno)
616 pause2cannot("write to current", ld->name);
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;
627 static void logdir_close(struct logdir *ld)
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;
652 static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
654 char buf[128];
655 unsigned now;
656 char *new, *s, *np;
657 int i;
658 struct stat st;
660 now = monotonic_sec();
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;
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;
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)
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;
683 close_on_exec_on(ld->fdlock);
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;
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();
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 }
733 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
734 break;
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 }
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;
756 break;
758 case '!':
759 if (s[1]) {
760 free(ld->processor);
761 ld->processor = wstrdup(s);
763 break;
765 s = np;
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;
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;
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;
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); ////
814 close_on_exec_on(ld->fdcur);
815 while (fchmod(ld->fdcur, 0644) == -1)
816 pause2cannot("set mode of current", ld->name);
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);
823 while (fchdir(fdwdir) == -1)
824 pause1cannot("change to initial working directory");
825 return 1;
828 static void logdirs_reopen(void)
830 int l;
831 int ok = 0;
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;
839 if (!ok)
840 fatalx("no functional log directories");
843 /* Will look good in libbb one day */
844 static ssize_t ndelay_read(int fd, void *buf, size_t count)
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;
854 /* Used for reading stdin */
855 static int buffer_pread(/*int fd, */char *s, unsigned len)
857 unsigned now;
858 struct pollfd input;
859 int i;
861 input.fd = STDIN_FILENO;
862 input.events = POLLIN;
864 do {
865 if (rotateasap) {
866 for (i = 0; i < dirn; ++i)
867 rotate(dir + i);
868 rotateasap = 0;
870 if (exitasap) {
871 if (linecomplete)
872 return 0;
873 len = 1;
875 if (reopenasap) {
876 logdirs_reopen();
877 reopenasap = 0;
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;
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);
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;
908 /* else: EAGAIN - normal, repeat silently */
909 } while (!exitasap);
911 if (i > 0) {
912 int cnt;
913 linecomplete = (s[i-1] == '\n');
914 if (!repl)
915 return i;
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;
933 s++;
936 return i;
939 static void sig_term_handler(int sig_no UNUSED_PARAM)
941 if (verbose)
942 bb_error_msg(INFO"sig%s received", "term");
943 exitasap = 1;
946 static void sig_child_handler(int sig_no UNUSED_PARAM)
948 pid_t pid;
949 int l;
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;
964 static void sig_alarm_handler(int sig_no UNUSED_PARAM)
966 if (verbose)
967 bb_error_msg(INFO"sig%s received", "alarm");
968 rotateasap = 1;
971 static void sig_hangup_handler(int sig_no UNUSED_PARAM)
973 if (verbose)
974 bb_error_msg(INFO"sig%s received", "hangup");
975 reopenasap = 1;
978 static void logmatch(struct logdir *ld)
980 char *s;
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;
998 s += strlen(s) + 1;
1002 int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1003 int svlogd_main(int argc, char **argv)
1005 char *r, *l, *b;
1006 ssize_t stdin_cnt = 0;
1007 int i;
1008 unsigned opt;
1009 unsigned timestamp = 0;
1011 INIT_G();
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();
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;
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;
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;*/
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);
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);
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.
1075 memRchr = (timestamp ? memchr : memrchr);
1077 logdirs_reopen();
1079 setvbuf(stderr, NULL, _IOFBF, linelen);
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;
1090 lineptr = line;
1091 if (timestamp)
1092 lineptr += 26;
1094 /* lineptr[0..linemax-1] - buffer for stdin */
1095 /* (possibly has some unprocessed data from prev loop) */
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;
1111 if (stdin_cnt <= 0 && exitasap)
1112 break;
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;
1122 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1123 ch = lineptr[linelen-1];
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 */
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] = ' ';
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);
1155 if (ld->match != '+')
1156 continue;
1157 buffer_pwrite(i, printptr, printlen);
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];
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);
1185 if (dir[i].match != '+')
1186 continue;
1187 buffer_pwrite(i, lineptr, linelen);
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);
1202 fflush_all();////
1205 for (i = 0; i < dirn; ++i) {
1206 if (dir[i].ppid)
1207 while (!processorstop(&dir[i]))
1208 continue;
1209 logdir_close(&dir[i]);
1211 return 0;