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 #include <sys/poll.h>
129 #include <sys/file.h>
131 #include "runit_lib.h"
133 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
139 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
147 unsigned rotate_period;
153 unsigned next_rotate;
154 char fnsave[FMT_PTIME];
170 unsigned nearest_rotate;
172 void* (*memRchr)(const void *, int, size_t);
177 smallint linecomplete;
185 sigset_t blocked_sigset;
187 #define G (*(struct globals*)ptr_to_globals)
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))); \
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");
245 static void pause1cannot(const char *m0)
247 bb_perror_msg(PAUSE"can't %s", m0);
250 static void pause2cannot(const char *m0, const char *m1)
252 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
256 static char* wstrdup(const char *str)
259 while (!(s = strdup(str)))
264 /*** ex fmt_ptime.[ch] ***/
267 static void fmt_time_human_30nul(char *s)
272 gettimeofday(&tv, NULL);
273 t = gmtime(&(tv.tv_sec));
274 sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
275 (unsigned)(1900 + t->tm_year),
276 (unsigned)(t->tm_mon + 1),
277 (unsigned)(t->tm_mday),
278 (unsigned)(t->tm_hour),
279 (unsigned)(t->tm_min),
280 (unsigned)(t->tm_sec),
281 (unsigned)(tv.tv_usec)
283 /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
284 /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
285 /* 20 (up to '.' inclusive) + 9 (not including '\0') */
288 /* NOT terminated! */
289 static void fmt_time_bernstein_25(char *s)
295 gettimeofday(&tv, NULL);
296 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
297 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
299 /* Network order is big-endian: most significant byte first.
300 * This is exactly what we want here */
301 pack[0] = htonl(sec_hi);
302 pack[1] = htonl(tv.tv_sec);
303 pack[2] = htonl(tv.tv_usec);
305 bin2hex(s, (char*)pack, 12);
308 static void processorstart(struct logdir *ld)
313 if (!ld->processor) return;
315 warnx("processor already running", ld->name);
319 /* vfork'ed child trashes this byte, save... */
320 sv_ch = ld->fnsave[26];
322 while ((pid = vfork()) == -1)
323 pause2cannot("vfork for processor", ld->name);
328 /* Non-ignored signals revert to SIG_DFL on exec anyway */
334 sig_unblock(SIGTERM);
335 sig_unblock(SIGALRM);
339 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
340 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
342 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
343 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
345 fd = open_read("state");
348 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
349 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
350 fd = xopen("state", O_RDONLY|O_NDELAY);
353 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
357 execl("/bin/sh", "/bin/sh" + 5, "-c", ld->processor, (char*) NULL);
358 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
360 ld->fnsave[26] = sv_ch; /* ...restore */
364 static unsigned processorstop(struct logdir *ld)
370 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
371 pause2cannot("wait for processor", ld->name);
377 while (fchdir(ld->fddir) == -1)
378 pause2cannot("change directory, want processor", ld->name);
379 if (WEXITSTATUS(wstat) != 0) {
380 warnx("processor failed, restart", ld->name);
381 ld->fnsave[26] = 't';
383 ld->fnsave[26] = 'u';
385 while (fchdir(fdwdir) == -1)
386 pause1cannot("change to initial working directory");
387 return ld->processor ? 0 : 1;
389 ld->fnsave[26] = 't';
390 memcpy(f, ld->fnsave, 26);
393 while (rename(ld->fnsave, f) == -1)
394 pause2cannot("rename processed", ld->name);
395 while (chmod(f, 0744) == -1)
396 pause2cannot("set mode of processed", ld->name);
397 ld->fnsave[26] = 'u';
398 if (unlink(ld->fnsave) == -1)
399 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
400 while (rename("newstate", "state") == -1)
401 pause2cannot("rename state", ld->name);
403 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
404 while (fchdir(fdwdir) == -1)
405 pause1cannot("change to initial working directory");
409 static void rmoldest(struct logdir *ld)
413 char oldest[FMT_PTIME];
416 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
417 while (!(d = opendir(".")))
418 pause2cannot("open directory, want rotate", ld->name);
420 while ((f = readdir(d))) {
421 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
422 if (f->d_name[26] == 't') {
423 if (unlink(f->d_name) == -1)
424 warn2("can't unlink processor leftover", f->d_name);
427 if (strcmp(f->d_name, oldest) < 0)
428 memcpy(oldest, f->d_name, 27);
434 warn2("can't read directory", ld->name);
437 if (ld->nmax && (n > ld->nmax)) {
439 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
440 if ((*oldest == '@') && (unlink(oldest) == -1))
441 warn2("can't unlink oldest logfile", ld->name);
445 static unsigned rotate(struct logdir *ld)
450 if (ld->fddir == -1) {
451 ld->rotate_period = 0;
455 while (!processorstop(ld))
458 while (fchdir(ld->fddir) == -1)
459 pause2cannot("change directory, want rotate", ld->name);
461 /* create new filename */
462 ld->fnsave[25] = '.';
463 ld->fnsave[26] = 's';
465 ld->fnsave[26] = 'u';
466 ld->fnsave[27] = '\0';
468 fmt_time_bernstein_25(ld->fnsave);
470 stat(ld->fnsave, &st);
471 } while (errno != ENOENT);
473 now = monotonic_sec();
474 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
475 ld->next_rotate = now + ld->rotate_period;
476 if (LESS(ld->next_rotate, nearest_rotate))
477 nearest_rotate = ld->next_rotate;
481 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
482 pause2cannot("fsync current logfile", ld->name);
483 while (fchmod(ld->fdcur, 0744) == -1)
484 pause2cannot("set mode of current", ld->name);
485 ////close(ld->fdcur);
489 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
490 ld->fnsave, ld->size);
492 while (rename("current", ld->fnsave) == -1)
493 pause2cannot("rename current", ld->name);
494 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
495 pause2cannot("create new current", ld->name);
496 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
497 pause2cannot("create new current", ld->name); /* very unlikely */
498 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
499 close_on_exec_on(ld->fdcur);
501 while (fchmod(ld->fdcur, 0644) == -1)
502 pause2cannot("set mode of current", ld->name);
508 while (fchdir(fdwdir) == -1)
509 pause1cannot("change to initial working directory");
513 static int buffer_pwrite(int n, char *s, unsigned len)
516 struct logdir *ld = &dir[n];
519 if (ld->size >= ld->sizemax)
521 if (len > (ld->sizemax - ld->size))
522 len = ld->sizemax - ld->size;
525 ////i = full_write(ld->fdcur, s, len);
526 ////if (i != -1) break;
527 i = fwrite(s, 1, len, ld->filecur);
530 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
533 char oldest[FMT_PTIME];
536 while (fchdir(ld->fddir) == -1)
537 pause2cannot("change directory, want remove old logfile",
540 oldest[1] = oldest[27] = '\0';
541 while (!(d = opendir(".")))
542 pause2cannot("open directory, want remove old logfile",
545 while ((f = readdir(d)))
546 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
548 if (strcmp(f->d_name, oldest) < 0)
549 memcpy(oldest, f->d_name, 27);
551 if (errno) warn2("can't read directory, want remove old logfile",
556 if (*oldest == '@') {
557 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
560 if (unlink(oldest) == -1) {
561 warn2("can't unlink oldest logfile", ld->name);
564 while (fchdir(fdwdir) == -1)
565 pause1cannot("change to initial working directory");
570 pause2cannot("write to current", ld->name);
576 if (ld->size >= (ld->sizemax - linemax))
581 static void logdir_close(struct logdir *ld)
586 bb_error_msg(INFO"close: %s", ld->name);
590 return; /* impossible */
591 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
592 pause2cannot("fsync current logfile", ld->name);
593 while (fchmod(ld->fdcur, 0744) == -1)
594 pause2cannot("set mode of current", ld->name);
595 ////close(ld->fdcur);
598 if (ld->fdlock == -1)
599 return; /* impossible */
603 ld->processor = NULL;
606 static unsigned logdir_open(struct logdir *ld, const char *fn)
614 now = monotonic_sec();
616 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
617 if (ld->fddir == -1) {
618 warn2("can't open log directory", (char*)fn);
621 close_on_exec_on(ld->fddir);
622 if (fchdir(ld->fddir) == -1) {
624 warn2("can't change directory", (char*)fn);
627 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
628 if ((ld->fdlock == -1)
629 || (lock_exnb(ld->fdlock) == -1)
632 warn2("can't lock directory", (char*)fn);
633 while (fchdir(fdwdir) == -1)
634 pause1cannot("change to initial working directory");
637 close_on_exec_on(ld->fdlock);
640 ld->sizemax = 1000000;
641 ld->nmax = ld->nmin = 10;
642 ld->rotate_period = 0;
643 ld->name = (char*)fn;
646 free(ld->inst); ld->inst = NULL;
647 free(ld->processor); ld->processor = NULL;
650 i = open_read_close("config", buf, sizeof(buf) - 1);
651 if (i < 0 && errno != ENOENT)
652 bb_perror_msg(WARNING"%s/config", ld->name);
656 bb_error_msg(INFO"read: %s/config", ld->name);
659 np = strchr(s, '\n');
667 /* Filtering requires one-line buffering,
668 * resetting the "find newline" function
671 /* Add '\n'-terminated line to ld->inst */
673 int l = asprintf(&new, "%s%s\n", ld->inst ? : "", s);
682 static const struct suffix_mult km_suffixes[] = {
687 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
691 ld->nmax = xatoi_u(&s[1]);
694 ld->nmin = xatoi_u(&s[1]);
697 static const struct suffix_mult mh_suffixes[] = {
700 /*{ "d", 24*60*60 },*/
703 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
704 if (ld->rotate_period) {
705 ld->next_rotate = now + ld->rotate_period;
706 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
707 nearest_rotate = ld->next_rotate;
715 ld->processor = wstrdup(s);
721 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
724 np = strchr(s, '\n');
732 i = stat("current", &st);
734 if (st.st_size && !(st.st_mode & S_IXUSR)) {
735 ld->fnsave[25] = '.';
736 ld->fnsave[26] = 'u';
737 ld->fnsave[27] = '\0';
739 fmt_time_bernstein_25(ld->fnsave);
741 stat(ld->fnsave, &st);
742 } while (errno != ENOENT);
743 while (rename("current", ld->fnsave) == -1)
744 pause2cannot("rename current", ld->name);
748 /* st.st_size can be not just bigger, but WIDER!
749 * This code is safe: if st.st_size > 4GB, we select
750 * ld->sizemax (because it's "unsigned") */
751 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
754 if (errno != ENOENT) {
756 warn2("can't stat current", ld->name);
757 while (fchdir(fdwdir) == -1)
758 pause1cannot("change to initial working directory");
762 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
763 pause2cannot("open current", ld->name);
764 /* we presume this cannot fail */
765 ld->filecur = fdopen(ld->fdcur, "a"); ////
766 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
768 close_on_exec_on(ld->fdcur);
769 while (fchmod(ld->fdcur, 0644) == -1)
770 pause2cannot("set mode of current", ld->name);
773 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
774 else bb_error_msg(INFO"new: %s/current", ld->name);
777 while (fchdir(fdwdir) == -1)
778 pause1cannot("change to initial working directory");
782 static void logdirs_reopen(void)
788 for (l = 0; l < dirn; ++l) {
789 logdir_close(&dir[l]);
790 if (logdir_open(&dir[l], fndir[l]))
794 fatalx("no functional log directories");
797 /* Will look good in libbb one day */
798 static ssize_t ndelay_read(int fd, void *buf, size_t count)
800 if (!(fl_flag_0 & O_NONBLOCK))
801 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
802 count = safe_read(fd, buf, count);
803 if (!(fl_flag_0 & O_NONBLOCK))
804 fcntl(fd, F_SETFL, fl_flag_0);
808 /* Used for reading stdin */
809 static int buffer_pread(/*int fd, */char *s, unsigned len)
815 input.fd = STDIN_FILENO;
816 input.events = POLLIN;
820 for (i = 0; i < dirn; ++i)
833 now = monotonic_sec();
834 nearest_rotate = now + (45 * 60 + 45);
835 for (i = 0; i < dirn; ++i) {
836 if (dir[i].rotate_period) {
837 if (LESS(dir[i].next_rotate, now))
839 if (LESS(dir[i].next_rotate, nearest_rotate))
840 nearest_rotate = dir[i].next_rotate;
844 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
845 i = nearest_rotate - now;
850 poll(&input, 1, i * 1000);
851 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
853 i = ndelay_read(STDIN_FILENO, s, len);
858 if (errno != EAGAIN) {
859 warn("can't read standard input");
862 /* else: EAGAIN - normal, repeat silently */
867 linecomplete = (s[i-1] == '\n');
875 if (ch < 32 || ch > 126)
879 for (j = 0; replace[j]; ++j) {
880 if (ch == replace[j]) {
893 static void sig_term_handler(int sig_no UNUSED_PARAM)
896 bb_error_msg(INFO"sig%s received", "term");
900 static void sig_child_handler(int sig_no UNUSED_PARAM)
906 bb_error_msg(INFO"sig%s received", "child");
907 while ((pid = wait_any_nohang(&wstat)) > 0) {
908 for (l = 0; l < dirn; ++l) {
909 if (dir[l].ppid == pid) {
911 processorstop(&dir[l]);
918 static void sig_alarm_handler(int sig_no UNUSED_PARAM)
921 bb_error_msg(INFO"sig%s received", "alarm");
925 static void sig_hangup_handler(int sig_no UNUSED_PARAM)
928 bb_error_msg(INFO"sig%s received", "hangup");
932 static void logmatch(struct logdir *ld)
943 if (pmatch(s+1, line, linelen))
948 if (pmatch(s+1, line, linelen))
956 int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
957 int svlogd_main(int argc, char **argv)
960 ssize_t stdin_cnt = 0;
963 unsigned timestamp = 0;
967 opt_complementary = "tt:vv";
968 opt = getopt32(argv, "r:R:l:b:tv",
969 &r, &replace, &l, &b, ×tamp, &verbose);
975 if (opt & 2) if (!repl) repl = '_'; // -R
977 linemax = xatou_range(l, 0, BUFSIZ-26);
983 ////if (opt & 8) { // -b
984 //// buflen = xatoi_u(b);
985 //// if (buflen == 0) buflen = 1024;
987 //if (opt & 0x10) timestamp++; // -t
988 //if (opt & 0x20) verbose++; // -v
989 //if (timestamp > 2) timestamp = 2;
996 ////if (buflen <= linemax) bb_show_usage();
997 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
998 close_on_exec_on(fdwdir);
999 dir = xzalloc(dirn * sizeof(dir[0]));
1000 for (i = 0; i < dirn; ++i) {
1003 ////dir[i].btmp = xmalloc(buflen);
1004 /*dir[i].ppid = 0;*/
1006 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
1008 /* We cannot set NONBLOCK on fd #0 permanently - this setting
1009 * _isn't_ per-process! It is shared among all other processes
1010 * with the same stdin */
1011 fl_flag_0 = fcntl(0, F_GETFL);
1013 sigemptyset(&blocked_sigset);
1014 sigaddset(&blocked_sigset, SIGTERM);
1015 sigaddset(&blocked_sigset, SIGCHLD);
1016 sigaddset(&blocked_sigset, SIGALRM);
1017 sigaddset(&blocked_sigset, SIGHUP);
1018 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
1019 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
1020 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
1021 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
1022 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
1024 /* Without timestamps, we don't have to print each line
1025 * separately, so we can look for _last_ newline, not first,
1026 * thus batching writes. If filtering is enabled in config,
1027 * logdirs_reopen resets it to memchr.
1029 memRchr = (timestamp ? memchr : memrchr);
1033 setvbuf(stderr, NULL, _IOFBF, linelen);
1035 /* Each iteration processes one or more lines */
1037 char stamp[FMT_PTIME];
1048 /* lineptr[0..linemax-1] - buffer for stdin */
1049 /* (possibly has some unprocessed data from prev loop) */
1051 /* Refill the buffer if needed */
1052 np = memRchr(lineptr, '\n', stdin_cnt);
1053 if (!np && !exitasap) {
1054 i = linemax - stdin_cnt; /* avail. bytes at tail */
1056 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
1057 if (i <= 0) /* EOF or error on stdin */
1060 np = memRchr(lineptr + stdin_cnt, '\n', i);
1065 if (stdin_cnt <= 0 && exitasap)
1068 /* Search for '\n' (in fact, np already holds the result) */
1069 linelen = stdin_cnt;
1071 print_to_nl: /* NB: starting from here lineptr may point
1072 * farther out into line[] */
1073 linelen = np - lineptr + 1;
1075 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1076 ch = lineptr[linelen-1];
1078 /* Biggest performance hit was coming from the fact
1079 * that we did not buffer writes. We were reading many lines
1080 * in one read() above, but wrote one line per write().
1081 * We are using stdio to fix that */
1083 /* write out lineptr[0..linelen-1] to each log destination
1084 * (or lineptr[-26..linelen-1] if timestamping) */
1089 fmt_time_bernstein_25(stamp);
1091 fmt_time_human_30nul(stamp);
1094 memcpy(printptr, stamp, 25);
1097 for (i = 0; i < dirn; ++i) {
1098 struct logdir *ld = &dir[i];
1099 if (ld->fddir == -1)
1103 if (ld->matcherr == 'e') {
1104 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
1105 ////full_write(STDERR_FILENO, printptr, printlen);
1106 fwrite(printptr, 1, printlen, stderr);
1108 if (ld->match != '+')
1110 buffer_pwrite(i, printptr, printlen);
1113 /* If we didn't see '\n' (long input line), */
1114 /* read/write repeatedly until we see it */
1115 while (ch != '\n') {
1116 /* lineptr is emptied now, safe to use as buffer */
1117 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
1118 if (stdin_cnt <= 0) { /* EOF or error on stdin */
1120 lineptr[0] = ch = '\n';
1124 linelen = stdin_cnt;
1125 np = memRchr(lineptr, '\n', stdin_cnt);
1127 linelen = np - lineptr + 1;
1128 ch = lineptr[linelen-1];
1130 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1131 for (i = 0; i < dirn; ++i) {
1132 if (dir[i].fddir == -1)
1134 if (dir[i].matcherr == 'e') {
1135 ////full_write(STDERR_FILENO, lineptr, linelen);
1136 fwrite(lineptr, 1, linelen, stderr);
1138 if (dir[i].match != '+')
1140 buffer_pwrite(i, lineptr, linelen);
1144 stdin_cnt -= linelen;
1145 if (stdin_cnt > 0) {
1147 /* If we see another '\n', we don't need to read
1148 * next piece of input: can print what we have */
1149 np = memRchr(lineptr, '\n', stdin_cnt);
1152 /* Move unprocessed data to the front of line */
1153 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1158 for (i = 0; i < dirn; ++i) {
1160 while (!processorstop(&dir[i]))
1162 logdir_close(&dir[i]);