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 (*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 static unsigned pmatch(const char *p
, const char *s
, unsigned len
)
282 if (c
!= *s
) return 0;
293 if (*s != '?') return 0;
301 if (*s
!= c
) return 0;
310 /*** ex fmt_ptime.[ch] ***/
313 static void fmt_time_human_30nul(char *s
)
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
)
341 gettimeofday(&tv
, NULL
);
342 sec_hi
= (0x400000000000000aULL
+ tv
.tv_sec
) >> 32;
343 tv
.tv_sec
= (time_t)(0x400000000000000aULL
) + tv
.tv_sec
;
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
);
351 bin2hex(s
, (char*)pack
, 12);
354 static void processorstart(struct logdir
*ld
)
359 if (!ld
->processor
) return;
361 warnx("processor already running", ld
->name
);
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
);
374 /* Non-ignored signals revert to SIG_DFL on exec anyway */
380 sig_unblock(SIGTERM
);
381 sig_unblock(SIGALRM
);
385 bb_error_msg(INFO
"processing: %s/%s", ld
->name
, ld
->fnsave
);
386 fd
= xopen(ld
->fnsave
, O_RDONLY
|O_NDELAY
);
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
);
391 fd
= open("state", O_RDONLY
|O_NDELAY
);
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
);
399 fd
= xopen("newstate", O_WRONLY
|O_NDELAY
|O_TRUNC
|O_CREAT
);
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 */
410 static unsigned processorstop(struct logdir
*ld
)
416 while (safe_waitpid(ld
->ppid
, &wstat
, 0) == -1)
417 pause2cannot("wait for processor", ld
->name
);
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';
429 ld
->fnsave
[26] = 'u';
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);
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
);
449 bb_error_msg(INFO
"processed: %s/%s", ld
->name
, f
);
450 while (fchdir(fdwdir
) == -1)
451 pause1cannot("change to initial working directory");
455 static void rmoldest(struct logdir
*ld
)
459 char oldest
[FMT_PTIME
];
462 oldest
[0] = 'A'; oldest
[1] = oldest
[27] = 0;
463 while (!(d
= opendir(".")))
464 pause2cannot("open directory, want rotate", ld
->name
);
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
);
473 if (strcmp(f
->d_name
, oldest
) < 0)
474 memcpy(oldest
, f
->d_name
, 27);
480 warn2("can't read directory", ld
->name
);
483 if (ld
->nmax
&& (n
> ld
->nmax
)) {
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
)
496 if (ld
->fddir
== -1) {
497 ld
->rotate_period
= 0;
501 while (!processorstop(ld
))
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';
511 ld
->fnsave
[26] = 'u';
512 ld
->fnsave
[27] = '\0';
514 fmt_time_bernstein_25(ld
->fnsave
);
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
;
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);
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
);
547 while (fchmod(ld
->fdcur
, 0644) == -1)
548 pause2cannot("set mode of current", ld
->name
);
554 while (fchdir(fdwdir
) == -1)
555 pause1cannot("change to initial working directory");
559 static int buffer_pwrite(int n
, char *s
, unsigned len
)
562 struct logdir
*ld
= &dir
[n
];
565 if (ld
->size
>= ld
->sizemax
)
567 if (len
> (ld
->sizemax
- ld
->size
))
568 len
= ld
->sizemax
- ld
->size
;
571 ////i = full_write(ld->fdcur, s, len);
572 ////if (i != -1) break;
573 i
= fwrite(s
, 1, len
, ld
->filecur
);
576 if ((errno
== ENOSPC
) && (ld
->nmin
< ld
->nmax
)) {
579 char oldest
[FMT_PTIME
];
582 while (fchdir(ld
->fddir
) == -1)
583 pause2cannot("change directory, want remove old logfile",
586 oldest
[1] = oldest
[27] = '\0';
587 while (!(d
= opendir(".")))
588 pause2cannot("open directory, want remove old logfile",
591 while ((f
= readdir(d
)))
592 if ((f
->d_name
[0] == '@') && (strlen(f
->d_name
) == 27)) {
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",
602 if (*oldest
== '@') {
603 bb_error_msg(WARNING
"out of disk space, delete: %s/%s",
606 if (unlink(oldest
) == -1) {
607 warn2("can't unlink oldest logfile", ld
->name
);
610 while (fchdir(fdwdir
) == -1)
611 pause1cannot("change to initial working directory");
616 pause2cannot("write to current", ld
->name
);
622 if (ld
->size
>= (ld
->sizemax
- linemax
))
627 static void logdir_close(struct logdir
*ld
)
632 bb_error_msg(INFO
"close: %s", ld
->name
);
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);
644 if (ld
->fdlock
== -1)
645 return; /* impossible */
649 ld
->processor
= NULL
;
652 static NOINLINE
unsigned logdir_open(struct logdir
*ld
, const char *fn
)
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
);
667 close_on_exec_on(ld
->fddir
);
668 if (fchdir(ld
->fddir
) == -1) {
670 warn2("can't change directory", (char*)fn
);
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)
678 warn2("can't lock directory", (char*)fn
);
679 while (fchdir(fdwdir
) == -1)
680 pause1cannot("change to initial working directory");
683 close_on_exec_on(ld
->fdlock
);
686 ld
->sizemax
= 1000000;
687 ld
->nmax
= ld
->nmin
= 10;
688 ld
->rotate_period
= 0;
689 ld
->name
= (char*)fn
;
692 free(ld
->inst
); ld
->inst
= NULL
;
693 free(ld
->processor
); ld
->processor
= NULL
;
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
);
702 bb_error_msg(INFO
"read: %s/config", ld
->name
);
705 np
= strchr(s
, '\n');
713 /* Filtering requires one-line buffering,
714 * resetting the "find newline" function
717 /* Add '\n'-terminated line to ld->inst */
719 int l
= asprintf(&new, "%s%s\n", ld
->inst
? ld
->inst
: "", s
);
728 static const struct suffix_mult km_suffixes
[] = {
733 ld
->sizemax
= xatou_sfx(&s
[1], km_suffixes
);
737 ld
->nmax
= xatoi_positive(&s
[1]);
740 ld
->nmin
= xatoi_positive(&s
[1]);
743 static const struct suffix_mult mh_suffixes
[] = {
746 /*{ "d", 24*60*60 },*/
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
;
761 ld
->processor
= wstrdup(s
);
767 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
770 np
= strchr(s
, '\n');
778 i
= stat("current", &st
);
780 if (st
.st_size
&& !(st
.st_mode
& S_IXUSR
)) {
781 ld
->fnsave
[25] = '.';
782 ld
->fnsave
[26] = 'u';
783 ld
->fnsave
[27] = '\0';
785 fmt_time_bernstein_25(ld
->fnsave
);
787 stat(ld
->fnsave
, &st
);
788 } while (errno
!= ENOENT
);
789 while (rename("current", ld
->fnsave
) == -1)
790 pause2cannot("rename current", ld
->name
);
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
;
800 if (errno
!= ENOENT
) {
802 warn2("can't stat current", ld
->name
);
803 while (fchdir(fdwdir
) == -1)
804 pause1cannot("change to initial working directory");
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
);
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");
828 static void logdirs_reopen(void)
834 for (l
= 0; l
< dirn
; ++l
) {
835 logdir_close(&dir
[l
]);
836 if (logdir_open(&dir
[l
], fndir
[l
]))
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
);
854 /* Used for reading stdin */
855 static int buffer_pread(/*int fd, */char *s
, unsigned len
)
861 input
.fd
= STDIN_FILENO
;
862 input
.events
= POLLIN
;
866 for (i
= 0; i
< dirn
; ++i
)
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
))
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
;
896 poll(&input
, 1, i
* 1000);
897 sigprocmask(SIG_BLOCK
, &blocked_sigset
, NULL
);
899 i
= ndelay_read(STDIN_FILENO
, s
, len
);
904 if (errno
!= EAGAIN
) {
905 warn("can't read standard input");
908 /* else: EAGAIN - normal, repeat silently */
913 linecomplete
= (s
[i
-1] == '\n');
921 if (ch
< 32 || ch
> 126)
925 for (j
= 0; replace
[j
]; ++j
) {
926 if (ch
== replace
[j
]) {
939 static void sig_term_handler(int sig_no UNUSED_PARAM
)
942 bb_error_msg(INFO
"sig%s received", "term");
946 static void sig_child_handler(int sig_no UNUSED_PARAM
)
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
) {
957 processorstop(&dir
[l
]);
964 static void sig_alarm_handler(int sig_no UNUSED_PARAM
)
967 bb_error_msg(INFO
"sig%s received", "alarm");
971 static void sig_hangup_handler(int sig_no UNUSED_PARAM
)
974 bb_error_msg(INFO
"sig%s received", "hangup");
978 static void logmatch(struct logdir
*ld
)
989 if (pmatch(s
+1, line
, linelen
))
994 if (pmatch(s
+1, line
, linelen
))
1002 int svlogd_main(int argc
, char **argv
) MAIN_EXTERNALLY_VISIBLE
;
1003 int svlogd_main(int argc
, char **argv
)
1006 ssize_t stdin_cnt
= 0;
1009 unsigned timestamp
= 0;
1013 opt_complementary
= "tt:vv";
1014 opt
= getopt32(argv
, "r:R:l:b:tv",
1015 &r
, &replace
, &l
, &b
, ×tamp
, &verbose
);
1016 if (opt
& 1) { // -r
1021 if (opt
& 2) if (!repl
) repl
= '_'; // -R
1022 if (opt
& 4) { // -l
1023 linemax
= xatou_range(l
, 0, BUFSIZ
-26);
1025 linemax
= BUFSIZ
-26;
1029 ////if (opt & 8) { // -b
1030 //// buflen = xatoi_positive(b);
1031 //// if (buflen == 0) buflen = 1024;
1033 //if (opt & 0x10) timestamp++; // -t
1034 //if (opt & 0x20) verbose++; // -v
1035 //if (timestamp > 2) timestamp = 2;
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
) {
1049 ////dir[i].btmp = xmalloc(buflen);
1050 /*dir[i].ppid = 0;*/
1052 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
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
);
1079 setvbuf(stderr
, NULL
, _IOFBF
, linelen
);
1081 /* Each iteration processes one or more lines */
1083 char stamp
[FMT_PTIME
];
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 */
1102 i
= buffer_pread(/*0, */lineptr
+ stdin_cnt
, i
);
1103 if (i
<= 0) /* EOF or error on stdin */
1106 np
= memRchr(lineptr
+ stdin_cnt
, '\n', i
);
1111 if (stdin_cnt
<= 0 && exitasap
)
1114 /* Search for '\n' (in fact, np already holds the result) */
1115 linelen
= stdin_cnt
;
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) */
1136 fmt_time_bernstein_25(stamp
);
1138 fmt_time_human_30nul(stamp
);
1141 memcpy(printptr
, stamp
, 25);
1144 for (i
= 0; i
< dirn
; ++i
) {
1145 struct logdir
*ld
= &dir
[i
];
1146 if (ld
->fddir
== -1)
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
!= '+')
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 */
1167 lineptr
[0] = ch
= '\n';
1171 linelen
= stdin_cnt
;
1172 np
= memRchr(lineptr
, '\n', stdin_cnt
);
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)
1181 if (dir
[i
].matcherr
== 'e') {
1182 ////full_write(STDERR_FILENO, lineptr, linelen);
1183 fwrite(lineptr
, 1, linelen
, stderr
);
1185 if (dir
[i
].match
!= '+')
1187 buffer_pwrite(i
, lineptr
, linelen
);
1191 stdin_cnt
-= linelen
;
1192 if (stdin_cnt
> 0) {
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
);
1199 /* Move unprocessed data to the front of line */
1200 memmove((timestamp
? line
+26 : line
), lineptr
, stdin_cnt
);
1205 for (i
= 0; i
< dirn
; ++i
) {
1207 while (!processorstop(&dir
[i
]))
1209 logdir_close(&dir
[i
]);