2 Copyright (c) 2001-2006, Gerrit Pape
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 */
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
39 If the line is empty, or starts with a #, it is ignored. A line
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).
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.
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.
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.
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.
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.
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
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:
85 the log message is deselected.
87 the log message is selected.
89 the log message is selected to be printed to standard error.
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.
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
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
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
128 //usage:#define svlogd_trivial_usage
129 //usage: "[-ttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..."
130 //usage:#define svlogd_full_usage "\n\n"
131 //usage: "Continuously read log data from stdin and write to rotated log files in DIRs"
133 //usage: "\n""DIR/config file modifies behavior:"
134 //usage: "\n""sSIZE - when to rotate logs"
135 //usage: "\n""nNUM - number of files to retain"
136 /*usage: "\n""NNUM - min number files to retain" - confusing */
137 /*usage: "\n""tSEC - rotate file if it get SEC seconds old" - confusing */
138 //usage: "\n""!PROG - process rotated log with PROG"
139 /*usage: "\n""uIPADDR - send log over UDP" - unsupported */
140 /*usage: "\n""UIPADDR - send log over UDP and DONT log" - unsupported */
141 /*usage: "\n""pPFX - prefix each line with PFX" - unsupported */
142 //usage: "\n""+,-PATTERN - (de)select line for logging"
143 //usage: "\n""E,ePATTERN - (de)select line for stderr"
145 #include <sys/file.h>
147 #include "runit_lib.h"
149 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
155 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
163 unsigned rotate_period;
169 unsigned next_rotate;
170 char fnsave[FMT_PTIME];
186 unsigned nearest_rotate;
188 void* (*memRchr)(const void *, int, size_t);
194 smallint linecomplete;
202 sigset_t blocked_sigset;
204 #define G (*ptr_to_globals)
206 #define verbose (G.verbose )
207 #define linemax (G.linemax )
208 #define buflen (G.buflen )
209 #define linelen (G.linelen )
210 #define fndir (G.fndir )
211 #define fdwdir (G.fdwdir )
212 #define wstat (G.wstat )
213 #define memRchr (G.memRchr )
214 #define nearest_rotate (G.nearest_rotate)
215 #define exitasap (G.exitasap )
216 #define rotateasap (G.rotateasap )
217 #define reopenasap (G.reopenasap )
218 #define linecomplete (G.linecomplete )
219 #define tmaxflag (G.tmaxflag )
220 #define repl (G.repl )
221 #define replace (G.replace )
222 #define blocked_sigset (G.blocked_sigset)
223 #define fl_flag_0 (G.fl_flag_0 )
224 #define dirn (G.dirn )
225 #define INIT_G() do { \
226 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
233 #define line bb_common_bufsiz1
236 #define FATAL "fatal: "
237 #define WARNING "warning: "
238 #define PAUSE "pausing: "
239 #define INFO "info: "
241 static void fatalx(const char *m0)
243 bb_error_msg_and_die(FATAL"%s", m0);
245 static void warn(const char *m0)
247 bb_perror_msg(WARNING"%s", m0);
249 static void warn2(const char *m0, const char *m1)
251 bb_perror_msg(WARNING"%s: %s", m0, m1);
253 static void warnx(const char *m0, const char *m1)
255 bb_error_msg(WARNING"%s: %s", m0, m1);
257 static void pause_nomem(void)
259 bb_error_msg(PAUSE"out of memory");
262 static void pause1cannot(const char *m0)
264 bb_perror_msg(PAUSE"can't %s", m0);
267 static void pause2cannot(const char *m0, const char *m1)
269 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
273 static char* wstrdup(const char *str)
276 while (!(s = strdup(str)))
281 static unsigned pmatch(const char *p, const char *s, unsigned len)
299 if (c != *s) return 0;
310 if (*s != '?') return 0;
318 if (*s != c) return 0;
327 /*** ex fmt_ptime.[ch] ***/
330 static void fmt_time_human_30nul(char *s)
335 gettimeofday(&tv, NULL);
336 ptm = gmtime(&tv.tv_sec);
337 sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
338 (unsigned)(1900 + ptm->tm_year),
339 (unsigned)(ptm->tm_mon + 1),
340 (unsigned)(ptm->tm_mday),
341 (unsigned)(ptm->tm_hour),
342 (unsigned)(ptm->tm_min),
343 (unsigned)(ptm->tm_sec),
344 (unsigned)(tv.tv_usec)
346 /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
347 /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
348 /* 20 (up to '.' inclusive) + 9 (not including '\0') */
351 /* NOT terminated! */
352 static void fmt_time_bernstein_25(char *s)
358 gettimeofday(&tv, NULL);
359 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
360 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
362 /* Network order is big-endian: most significant byte first.
363 * This is exactly what we want here */
364 pack[0] = htonl(sec_hi);
365 pack[1] = htonl(tv.tv_sec);
366 pack[2] = htonl(tv.tv_usec);
368 bin2hex(s, (char*)pack, 12);
371 static void processorstart(struct logdir *ld)
376 if (!ld->processor) return;
378 warnx("processor already running", ld->name);
382 /* vfork'ed child trashes this byte, save... */
383 sv_ch = ld->fnsave[26];
386 G.shell = xstrdup(get_shell_name());
388 while ((pid = vfork()) == -1)
389 pause2cannot("vfork for processor", ld->name);
394 /* Non-ignored signals revert to SIG_DFL on exec anyway */
400 sig_unblock(SIGTERM);
401 sig_unblock(SIGALRM);
405 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
406 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
408 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
409 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
411 fd = open("state", O_RDONLY|O_NDELAY);
414 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
415 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
416 fd = xopen("state", O_RDONLY|O_NDELAY);
419 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
422 execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL);
423 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
425 ld->fnsave[26] = sv_ch; /* ...restore */
429 static unsigned processorstop(struct logdir *ld)
435 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
436 pause2cannot("wait for processor", ld->name);
442 while (fchdir(ld->fddir) == -1)
443 pause2cannot("change directory, want processor", ld->name);
444 if (WEXITSTATUS(wstat) != 0) {
445 warnx("processor failed, restart", ld->name);
446 ld->fnsave[26] = 't';
448 ld->fnsave[26] = 'u';
450 while (fchdir(fdwdir) == -1)
451 pause1cannot("change to initial working directory");
452 return ld->processor ? 0 : 1;
454 ld->fnsave[26] = 't';
455 memcpy(f, ld->fnsave, 26);
458 while (rename(ld->fnsave, f) == -1)
459 pause2cannot("rename processed", ld->name);
460 while (chmod(f, 0744) == -1)
461 pause2cannot("set mode of processed", ld->name);
462 ld->fnsave[26] = 'u';
463 if (unlink(ld->fnsave) == -1)
464 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
465 while (rename("newstate", "state") == -1)
466 pause2cannot("rename state", ld->name);
468 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
469 while (fchdir(fdwdir) == -1)
470 pause1cannot("change to initial working directory");
474 static void rmoldest(struct logdir *ld)
478 char oldest[FMT_PTIME];
481 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
482 while (!(d = opendir(".")))
483 pause2cannot("open directory, want rotate", ld->name);
485 while ((f = readdir(d))) {
486 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
487 if (f->d_name[26] == 't') {
488 if (unlink(f->d_name) == -1)
489 warn2("can't unlink processor leftover", f->d_name);
492 if (strcmp(f->d_name, oldest) < 0)
493 memcpy(oldest, f->d_name, 27);
499 warn2("can't read directory", ld->name);
502 if (ld->nmax && (n > ld->nmax)) {
504 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
505 if ((*oldest == '@') && (unlink(oldest) == -1))
506 warn2("can't unlink oldest logfile", ld->name);
510 static unsigned rotate(struct logdir *ld)
515 if (ld->fddir == -1) {
516 ld->rotate_period = 0;
520 while (!processorstop(ld))
523 while (fchdir(ld->fddir) == -1)
524 pause2cannot("change directory, want rotate", ld->name);
526 /* create new filename */
527 ld->fnsave[25] = '.';
528 ld->fnsave[26] = 's';
530 ld->fnsave[26] = 'u';
531 ld->fnsave[27] = '\0';
533 fmt_time_bernstein_25(ld->fnsave);
535 stat(ld->fnsave, &st);
536 } while (errno != ENOENT);
538 now = monotonic_sec();
539 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
540 ld->next_rotate = now + ld->rotate_period;
541 if (LESS(ld->next_rotate, nearest_rotate))
542 nearest_rotate = ld->next_rotate;
546 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
547 pause2cannot("fsync current logfile", ld->name);
548 while (fchmod(ld->fdcur, 0744) == -1)
549 pause2cannot("set mode of current", ld->name);
550 ////close(ld->fdcur);
554 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
555 ld->fnsave, ld->size);
557 while (rename("current", ld->fnsave) == -1)
558 pause2cannot("rename current", ld->name);
559 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
560 pause2cannot("create new current", ld->name);
561 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
562 pause2cannot("create new current", ld->name); /* very unlikely */
563 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
564 close_on_exec_on(ld->fdcur);
566 while (fchmod(ld->fdcur, 0644) == -1)
567 pause2cannot("set mode of current", ld->name);
573 while (fchdir(fdwdir) == -1)
574 pause1cannot("change to initial working directory");
578 static int buffer_pwrite(int n, char *s, unsigned len)
581 struct logdir *ld = &dir[n];
584 if (ld->size >= ld->sizemax)
586 if (len > (ld->sizemax - ld->size))
587 len = ld->sizemax - ld->size;
590 ////i = full_write(ld->fdcur, s, len);
591 ////if (i != -1) break;
592 i = fwrite(s, 1, len, ld->filecur);
595 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
598 char oldest[FMT_PTIME];
601 while (fchdir(ld->fddir) == -1)
602 pause2cannot("change directory, want remove old logfile",
605 oldest[1] = oldest[27] = '\0';
606 while (!(d = opendir(".")))
607 pause2cannot("open directory, want remove old logfile",
610 while ((f = readdir(d)))
611 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
613 if (strcmp(f->d_name, oldest) < 0)
614 memcpy(oldest, f->d_name, 27);
616 if (errno) warn2("can't read directory, want remove old logfile",
621 if (*oldest == '@') {
622 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
625 if (unlink(oldest) == -1) {
626 warn2("can't unlink oldest logfile", ld->name);
629 while (fchdir(fdwdir) == -1)
630 pause1cannot("change to initial working directory");
635 pause2cannot("write to current", ld->name);
641 if (ld->size >= (ld->sizemax - linemax))
646 static void logdir_close(struct logdir *ld)
651 bb_error_msg(INFO"close: %s", ld->name);
655 return; /* impossible */
656 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
657 pause2cannot("fsync current logfile", ld->name);
658 while (fchmod(ld->fdcur, 0744) == -1)
659 pause2cannot("set mode of current", ld->name);
660 ////close(ld->fdcur);
663 if (ld->fdlock == -1)
664 return; /* impossible */
668 ld->processor = NULL;
671 static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
679 now = monotonic_sec();
681 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
682 if (ld->fddir == -1) {
683 warn2("can't open log directory", (char*)fn);
686 close_on_exec_on(ld->fddir);
687 if (fchdir(ld->fddir) == -1) {
689 warn2("can't change directory", (char*)fn);
692 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
693 if ((ld->fdlock == -1)
694 || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
697 warn2("can't lock directory", (char*)fn);
698 while (fchdir(fdwdir) == -1)
699 pause1cannot("change to initial working directory");
702 close_on_exec_on(ld->fdlock);
705 ld->sizemax = 1000000;
706 ld->nmax = ld->nmin = 10;
707 ld->rotate_period = 0;
708 ld->name = (char*)fn;
711 free(ld->inst); ld->inst = NULL;
712 free(ld->processor); ld->processor = NULL;
715 i = open_read_close("config", buf, sizeof(buf) - 1);
716 if (i < 0 && errno != ENOENT)
717 bb_perror_msg(WARNING"%s/config", ld->name);
721 bb_error_msg(INFO"read: %s/config", ld->name);
724 np = strchr(s, '\n');
732 /* Filtering requires one-line buffering,
733 * resetting the "find newline" function
736 /* Add '\n'-terminated line to ld->inst */
738 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
747 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
751 ld->nmax = xatoi_positive(&s[1]);
754 ld->nmin = xatoi_positive(&s[1]);
757 static const struct suffix_mult mh_suffixes[] = {
760 /*{ "d", 24*60*60 },*/
763 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
764 if (ld->rotate_period) {
765 ld->next_rotate = now + ld->rotate_period;
766 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
767 nearest_rotate = ld->next_rotate;
775 ld->processor = wstrdup(s);
781 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
784 np = strchr(s, '\n');
792 i = stat("current", &st);
794 if (st.st_size && !(st.st_mode & S_IXUSR)) {
795 ld->fnsave[25] = '.';
796 ld->fnsave[26] = 'u';
797 ld->fnsave[27] = '\0';
799 fmt_time_bernstein_25(ld->fnsave);
801 stat(ld->fnsave, &st);
802 } while (errno != ENOENT);
803 while (rename("current", ld->fnsave) == -1)
804 pause2cannot("rename current", ld->name);
808 /* st.st_size can be not just bigger, but WIDER!
809 * This code is safe: if st.st_size > 4GB, we select
810 * ld->sizemax (because it's "unsigned") */
811 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
814 if (errno != ENOENT) {
816 warn2("can't stat current", ld->name);
817 while (fchdir(fdwdir) == -1)
818 pause1cannot("change to initial working directory");
822 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
823 pause2cannot("open current", ld->name);
824 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
825 pause2cannot("open current", ld->name); ////
826 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
828 close_on_exec_on(ld->fdcur);
829 while (fchmod(ld->fdcur, 0644) == -1)
830 pause2cannot("set mode of current", ld->name);
833 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
834 else bb_error_msg(INFO"new: %s/current", ld->name);
837 while (fchdir(fdwdir) == -1)
838 pause1cannot("change to initial working directory");
842 static void logdirs_reopen(void)
848 for (l = 0; l < dirn; ++l) {
849 logdir_close(&dir[l]);
850 if (logdir_open(&dir[l], fndir[l]))
854 fatalx("no functional log directories");
857 /* Will look good in libbb one day */
858 static ssize_t ndelay_read(int fd, void *buf, size_t count)
860 if (!(fl_flag_0 & O_NONBLOCK))
861 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
862 count = safe_read(fd, buf, count);
863 if (!(fl_flag_0 & O_NONBLOCK))
864 fcntl(fd, F_SETFL, fl_flag_0);
868 /* Used for reading stdin */
869 static int buffer_pread(/*int fd, */char *s, unsigned len)
875 input.fd = STDIN_FILENO;
876 input.events = POLLIN;
880 for (i = 0; i < dirn; ++i)
893 now = monotonic_sec();
894 nearest_rotate = now + (45 * 60 + 45);
895 for (i = 0; i < dirn; ++i) {
896 if (dir[i].rotate_period) {
897 if (LESS(dir[i].next_rotate, now))
899 if (LESS(dir[i].next_rotate, nearest_rotate))
900 nearest_rotate = dir[i].next_rotate;
904 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
905 i = nearest_rotate - now;
910 poll(&input, 1, i * 1000);
911 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
913 i = ndelay_read(STDIN_FILENO, s, len);
918 if (errno != EAGAIN) {
919 warn("can't read standard input");
922 /* else: EAGAIN - normal, repeat silently */
927 linecomplete = (s[i-1] == '\n');
935 if (ch < 32 || ch > 126)
939 for (j = 0; replace[j]; ++j) {
940 if (ch == replace[j]) {
953 static void sig_term_handler(int sig_no UNUSED_PARAM)
956 bb_error_msg(INFO"sig%s received", "term");
960 static void sig_child_handler(int sig_no UNUSED_PARAM)
966 bb_error_msg(INFO"sig%s received", "child");
967 while ((pid = wait_any_nohang(&wstat)) > 0) {
968 for (l = 0; l < dirn; ++l) {
969 if (dir[l].ppid == pid) {
971 processorstop(&dir[l]);
978 static void sig_alarm_handler(int sig_no UNUSED_PARAM)
981 bb_error_msg(INFO"sig%s received", "alarm");
985 static void sig_hangup_handler(int sig_no UNUSED_PARAM)
988 bb_error_msg(INFO"sig%s received", "hangup");
992 static void logmatch(struct logdir *ld)
1003 if (pmatch(s+1, line, linelen))
1008 if (pmatch(s+1, line, linelen))
1009 ld->matcherr = s[0];
1016 int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1017 int svlogd_main(int argc, char **argv)
1020 ssize_t stdin_cnt = 0;
1023 unsigned timestamp = 0;
1027 opt_complementary = "tt:vv";
1028 opt = getopt32(argv, "r:R:l:b:tv",
1029 &r, &replace, &l, &b, ×tamp, &verbose);
1030 if (opt & 1) { // -r
1035 if (opt & 2) if (!repl) repl = '_'; // -R
1036 if (opt & 4) { // -l
1037 linemax = xatou_range(l, 0, BUFSIZ-26);
1039 linemax = BUFSIZ-26;
1043 ////if (opt & 8) { // -b
1044 //// buflen = xatoi_positive(b);
1045 //// if (buflen == 0) buflen = 1024;
1047 //if (opt & 0x10) timestamp++; // -t
1048 //if (opt & 0x20) verbose++; // -v
1049 //if (timestamp > 2) timestamp = 2;
1056 ////if (buflen <= linemax) bb_show_usage();
1057 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
1058 close_on_exec_on(fdwdir);
1059 dir = xzalloc(dirn * sizeof(dir[0]));
1060 for (i = 0; i < dirn; ++i) {
1063 ////dir[i].btmp = xmalloc(buflen);
1064 /*dir[i].ppid = 0;*/
1066 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
1068 /* We cannot set NONBLOCK on fd #0 permanently - this setting
1069 * _isn't_ per-process! It is shared among all other processes
1070 * with the same stdin */
1071 fl_flag_0 = fcntl(0, F_GETFL);
1073 sigemptyset(&blocked_sigset);
1074 sigaddset(&blocked_sigset, SIGTERM);
1075 sigaddset(&blocked_sigset, SIGCHLD);
1076 sigaddset(&blocked_sigset, SIGALRM);
1077 sigaddset(&blocked_sigset, SIGHUP);
1078 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
1079 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
1080 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
1081 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
1082 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
1084 /* Without timestamps, we don't have to print each line
1085 * separately, so we can look for _last_ newline, not first,
1086 * thus batching writes. If filtering is enabled in config,
1087 * logdirs_reopen resets it to memchr.
1089 memRchr = (timestamp ? memchr : memrchr);
1093 setvbuf(stderr, NULL, _IOFBF, linelen);
1095 /* Each iteration processes one or more lines */
1097 char stamp[FMT_PTIME];
1108 /* lineptr[0..linemax-1] - buffer for stdin */
1109 /* (possibly has some unprocessed data from prev loop) */
1111 /* Refill the buffer if needed */
1112 np = memRchr(lineptr, '\n', stdin_cnt);
1113 if (!np && !exitasap) {
1114 i = linemax - stdin_cnt; /* avail. bytes at tail */
1116 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
1117 if (i <= 0) /* EOF or error on stdin */
1120 np = memRchr(lineptr + stdin_cnt, '\n', i);
1125 if (stdin_cnt <= 0 && exitasap)
1128 /* Search for '\n' (in fact, np already holds the result) */
1129 linelen = stdin_cnt;
1132 /* NB: starting from here lineptr may point
1133 * farther out into line[] */
1134 linelen = np - lineptr + 1;
1136 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1137 ch = lineptr[linelen-1];
1139 /* Biggest performance hit was coming from the fact
1140 * that we did not buffer writes. We were reading many lines
1141 * in one read() above, but wrote one line per write().
1142 * We are using stdio to fix that */
1144 /* write out lineptr[0..linelen-1] to each log destination
1145 * (or lineptr[-26..linelen-1] if timestamping) */
1150 fmt_time_bernstein_25(stamp);
1152 fmt_time_human_30nul(stamp);
1155 memcpy(printptr, stamp, 25);
1158 for (i = 0; i < dirn; ++i) {
1159 struct logdir *ld = &dir[i];
1160 if (ld->fddir == -1)
1164 if (ld->matcherr == 'e') {
1165 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
1166 ////full_write(STDERR_FILENO, printptr, printlen);
1167 fwrite(printptr, 1, printlen, stderr);
1169 if (ld->match != '+')
1171 buffer_pwrite(i, printptr, printlen);
1174 /* If we didn't see '\n' (long input line), */
1175 /* read/write repeatedly until we see it */
1176 while (ch != '\n') {
1177 /* lineptr is emptied now, safe to use as buffer */
1178 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
1179 if (stdin_cnt <= 0) { /* EOF or error on stdin */
1181 lineptr[0] = ch = '\n';
1185 linelen = stdin_cnt;
1186 np = memRchr(lineptr, '\n', stdin_cnt);
1188 linelen = np - lineptr + 1;
1189 ch = lineptr[linelen-1];
1191 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1192 for (i = 0; i < dirn; ++i) {
1193 if (dir[i].fddir == -1)
1195 if (dir[i].matcherr == 'e') {
1196 ////full_write(STDERR_FILENO, lineptr, linelen);
1197 fwrite(lineptr, 1, linelen, stderr);
1199 if (dir[i].match != '+')
1201 buffer_pwrite(i, lineptr, linelen);
1205 stdin_cnt -= linelen;
1206 if (stdin_cnt > 0) {
1208 /* If we see another '\n', we don't need to read
1209 * next piece of input: can print what we have */
1210 np = memRchr(lineptr, '\n', stdin_cnt);
1213 /* Move unprocessed data to the front of line */
1214 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1219 for (i = 0; i < dirn; ++i) {
1221 while (!processorstop(&dir[i]))
1223 logdir_close(&dir[i]);