28 |
/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */ |
/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */ |
29 |
/* TODO: depends on runit_lib.c - review and reduce/eliminate */ |
/* TODO: depends on runit_lib.c - review and reduce/eliminate */ |
30 |
|
|
31 |
|
/* |
32 |
|
Config files |
33 |
|
|
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: |
38 |
|
|
39 |
|
If the line is empty, or starts with a #, it is ignored. A line |
40 |
|
of the form |
41 |
|
|
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. |
80 |
|
|
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: |
83 |
|
|
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. |
92 |
|
|
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. |
97 |
|
|
98 |
|
Pattern Matching |
99 |
|
|
100 |
|
svlogd matches a log message against the string pattern as follows: |
101 |
|
|
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. |
108 |
|
|
109 |
|
Timestamps optionally added by svlogd are not considered part |
110 |
|
of the log message. |
111 |
|
|
112 |
|
An svlogd pattern is not a regular expression. For example consider |
113 |
|
a log message like this |
114 |
|
|
115 |
|
2005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14 |
116 |
|
|
117 |
|
The following pattern doesnt match |
118 |
|
|
119 |
|
-*pid* |
120 |
|
|
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 |
124 |
|
|
125 |
|
-*: *: pid * |
126 |
|
*/ |
127 |
|
|
128 |
#include <sys/poll.h> |
#include <sys/poll.h> |
129 |
#include <sys/file.h> |
#include <sys/file.h> |
130 |
#include "libbb.h" |
#include "libbb.h" |
169 |
int wstat; |
int wstat; |
170 |
unsigned nearest_rotate; |
unsigned nearest_rotate; |
171 |
|
|
172 |
|
void* (*memRchr)(const void *, int, size_t); |
173 |
|
|
174 |
smallint exitasap; |
smallint exitasap; |
175 |
smallint rotateasap; |
smallint rotateasap; |
176 |
smallint reopenasap; |
smallint reopenasap; |
193 |
#define fndir (G.fndir ) |
#define fndir (G.fndir ) |
194 |
#define fdwdir (G.fdwdir ) |
#define fdwdir (G.fdwdir ) |
195 |
#define wstat (G.wstat ) |
#define wstat (G.wstat ) |
196 |
|
#define memRchr (G.memRchr ) |
197 |
#define nearest_rotate (G.nearest_rotate) |
#define nearest_rotate (G.nearest_rotate) |
198 |
#define exitasap (G.exitasap ) |
#define exitasap (G.exitasap ) |
199 |
#define rotateasap (G.rotateasap ) |
#define rotateasap (G.rotateasap ) |
221 |
#define PAUSE "pausing: " |
#define PAUSE "pausing: " |
222 |
#define INFO "info: " |
#define INFO "info: " |
223 |
|
|
|
#define usage() bb_show_usage() |
|
224 |
static void fatalx(const char *m0) |
static void fatalx(const char *m0) |
225 |
{ |
{ |
226 |
bb_error_msg_and_die(FATAL"%s", m0); |
bb_error_msg_and_die(FATAL"%s", m0); |
266 |
/* NUL terminated */ |
/* NUL terminated */ |
267 |
static void fmt_time_human_30nul(char *s) |
static void fmt_time_human_30nul(char *s) |
268 |
{ |
{ |
269 |
struct tm *t; |
struct tm *ptm; |
270 |
struct timeval tv; |
struct timeval tv; |
271 |
|
|
272 |
gettimeofday(&tv, NULL); |
gettimeofday(&tv, NULL); |
273 |
t = gmtime(&(tv.tv_sec)); |
ptm = gmtime(&tv.tv_sec); |
274 |
sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000", |
sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000", |
275 |
(unsigned)(1900 + t->tm_year), |
(unsigned)(1900 + ptm->tm_year), |
276 |
(unsigned)(t->tm_mon + 1), |
(unsigned)(ptm->tm_mon + 1), |
277 |
(unsigned)(t->tm_mday), |
(unsigned)(ptm->tm_mday), |
278 |
(unsigned)(t->tm_hour), |
(unsigned)(ptm->tm_hour), |
279 |
(unsigned)(t->tm_min), |
(unsigned)(ptm->tm_min), |
280 |
(unsigned)(t->tm_sec), |
(unsigned)(ptm->tm_sec), |
281 |
(unsigned)(tv.tv_usec) |
(unsigned)(tv.tv_usec) |
282 |
); |
); |
283 |
/* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */ |
/* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */ |
322 |
while ((pid = vfork()) == -1) |
while ((pid = vfork()) == -1) |
323 |
pause2cannot("vfork for processor", ld->name); |
pause2cannot("vfork for processor", ld->name); |
324 |
if (!pid) { |
if (!pid) { |
|
char *prog[4]; |
|
325 |
int fd; |
int fd; |
326 |
|
|
327 |
/* child */ |
/* child */ |
354 |
xmove_fd(fd, 5); |
xmove_fd(fd, 5); |
355 |
|
|
356 |
// getenv("SHELL")? |
// getenv("SHELL")? |
357 |
prog[0] = (char*)"sh"; |
execl("/bin/sh", "/bin/sh" + 5, "-c", ld->processor, (char*) NULL); |
|
prog[1] = (char*)"-c"; |
|
|
prog[2] = ld->processor; |
|
|
prog[3] = NULL; |
|
|
execv("/bin/sh", prog); |
|
358 |
bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name); |
bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name); |
359 |
} |
} |
360 |
ld->fnsave[26] = sv_ch; /* ...restore */ |
ld->fnsave[26] = sv_ch; /* ...restore */ |
372 |
sig_block(SIGHUP); |
sig_block(SIGHUP); |
373 |
ld->ppid = 0; |
ld->ppid = 0; |
374 |
} |
} |
375 |
if (ld->fddir == -1) return 1; |
if (ld->fddir == -1) |
376 |
|
return 1; |
377 |
while (fchdir(ld->fddir) == -1) |
while (fchdir(ld->fddir) == -1) |
378 |
pause2cannot("change directory, want processor", ld->name); |
pause2cannot("change directory, want processor", ld->name); |
379 |
if (wait_exitcode(wstat) != 0) { |
if (WEXITSTATUS(wstat) != 0) { |
380 |
warnx("processor failed, restart", ld->name); |
warnx("processor failed, restart", ld->name); |
381 |
ld->fnsave[26] = 't'; |
ld->fnsave[26] = 't'; |
382 |
unlink(ld->fnsave); |
unlink(ld->fnsave); |
493 |
pause2cannot("rename current", ld->name); |
pause2cannot("rename current", ld->name); |
494 |
while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) |
while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) |
495 |
pause2cannot("create new current", ld->name); |
pause2cannot("create new current", ld->name); |
496 |
/* we presume this cannot fail */ |
while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) //// |
497 |
ld->filecur = fdopen(ld->fdcur, "a"); //// |
pause2cannot("create new current", ld->name); /* very unlikely */ |
498 |
setvbuf(ld->filecur, NULL, _IOFBF, linelen); //// |
setvbuf(ld->filecur, NULL, _IOFBF, linelen); //// |
499 |
close_on_exec_on(ld->fdcur); |
close_on_exec_on(ld->fdcur); |
500 |
ld->size = 0; |
ld->size = 0; |
501 |
while (fchmod(ld->fdcur, 0644) == -1) |
while (fchmod(ld->fdcur, 0644) == -1) |
502 |
pause2cannot("set mode of current", ld->name); |
pause2cannot("set mode of current", ld->name); |
503 |
|
|
504 |
rmoldest(ld); |
rmoldest(ld); |
505 |
processorstart(ld); |
processorstart(ld); |
506 |
} |
} |
603 |
ld->processor = NULL; |
ld->processor = NULL; |
604 |
} |
} |
605 |
|
|
606 |
static unsigned logdir_open(struct logdir *ld, const char *fn) |
static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn) |
607 |
{ |
{ |
608 |
char buf[128]; |
char buf[128]; |
609 |
unsigned now; |
unsigned now; |
647 |
free(ld->processor); ld->processor = NULL; |
free(ld->processor); ld->processor = NULL; |
648 |
|
|
649 |
/* read config */ |
/* read config */ |
650 |
i = open_read_close("config", buf, sizeof(buf)); |
i = open_read_close("config", buf, sizeof(buf) - 1); |
651 |
if (i < 0 && errno != ENOENT) |
if (i < 0 && errno != ENOENT) |
652 |
bb_perror_msg(WARNING"%s/config", ld->name); |
bb_perror_msg(WARNING"%s/config", ld->name); |
653 |
if (i > 0) { |
if (i > 0) { |
654 |
|
buf[i] = '\0'; |
655 |
if (verbose) |
if (verbose) |
656 |
bb_error_msg(INFO"read: %s/config", ld->name); |
bb_error_msg(INFO"read: %s/config", ld->name); |
657 |
s = buf; |
s = buf; |
664 |
case '-': |
case '-': |
665 |
case 'e': |
case 'e': |
666 |
case 'E': |
case 'E': |
667 |
|
/* Filtering requires one-line buffering, |
668 |
|
* resetting the "find newline" function |
669 |
|
* accordingly */ |
670 |
|
memRchr = memchr; |
671 |
/* Add '\n'-terminated line to ld->inst */ |
/* Add '\n'-terminated line to ld->inst */ |
672 |
while (1) { |
while (1) { |
673 |
int l = asprintf(&new, "%s%s\n", ld->inst ? : "", s); |
int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s); |
674 |
if (l >= 0 && new) |
if (l >= 0 && new) |
675 |
break; |
break; |
676 |
pause_nomem(); |
pause_nomem(); |
682 |
static const struct suffix_mult km_suffixes[] = { |
static const struct suffix_mult km_suffixes[] = { |
683 |
{ "k", 1024 }, |
{ "k", 1024 }, |
684 |
{ "m", 1024*1024 }, |
{ "m", 1024*1024 }, |
685 |
{ } |
{ "", 0 } |
686 |
}; |
}; |
687 |
ld->sizemax = xatou_sfx(&s[1], km_suffixes); |
ld->sizemax = xatou_sfx(&s[1], km_suffixes); |
688 |
break; |
break; |
698 |
{ "m", 60 }, |
{ "m", 60 }, |
699 |
{ "h", 60*60 }, |
{ "h", 60*60 }, |
700 |
/*{ "d", 24*60*60 },*/ |
/*{ "d", 24*60*60 },*/ |
701 |
{ } |
{ "", 0 } |
702 |
}; |
}; |
703 |
ld->rotate_period = xatou_sfx(&s[1], mh_suffixes); |
ld->rotate_period = xatou_sfx(&s[1], mh_suffixes); |
704 |
if (ld->rotate_period) { |
if (ld->rotate_period) { |
761 |
} |
} |
762 |
while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) |
while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1) |
763 |
pause2cannot("open current", ld->name); |
pause2cannot("open current", ld->name); |
764 |
/* we presume this cannot fail */ |
while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) |
765 |
ld->filecur = fdopen(ld->fdcur, "a"); //// |
pause2cannot("open current", ld->name); //// |
766 |
setvbuf(ld->filecur, NULL, _IOFBF, linelen); //// |
setvbuf(ld->filecur, NULL, _IOFBF, linelen); //// |
767 |
|
|
768 |
close_on_exec_on(ld->fdcur); |
close_on_exec_on(ld->fdcur); |
812 |
struct pollfd input; |
struct pollfd input; |
813 |
int i; |
int i; |
814 |
|
|
815 |
input.fd = 0; |
input.fd = STDIN_FILENO; |
816 |
input.events = POLLIN; |
input.events = POLLIN; |
817 |
|
|
818 |
do { |
do { |
956 |
int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
957 |
int svlogd_main(int argc, char **argv) |
int svlogd_main(int argc, char **argv) |
958 |
{ |
{ |
959 |
char *r,*l,*b; |
char *r, *l, *b; |
960 |
ssize_t stdin_cnt = 0; |
ssize_t stdin_cnt = 0; |
961 |
int i; |
int i; |
962 |
unsigned opt; |
unsigned opt; |
963 |
unsigned timestamp = 0; |
unsigned timestamp = 0; |
|
void* (*memRchr)(const void *, int, size_t) = memchr; |
|
964 |
|
|
965 |
INIT_G(); |
INIT_G(); |
966 |
|
|
969 |
&r, &replace, &l, &b, ×tamp, &verbose); |
&r, &replace, &l, &b, ×tamp, &verbose); |
970 |
if (opt & 1) { // -r |
if (opt & 1) { // -r |
971 |
repl = r[0]; |
repl = r[0]; |
972 |
if (!repl || r[1]) usage(); |
if (!repl || r[1]) |
973 |
|
bb_show_usage(); |
974 |
} |
} |
975 |
if (opt & 2) if (!repl) repl = '_'; // -R |
if (opt & 2) if (!repl) repl = '_'; // -R |
976 |
if (opt & 4) { // -l |
if (opt & 4) { // -l |
977 |
linemax = xatou_range(l, 0, BUFSIZ-26); |
linemax = xatou_range(l, 0, BUFSIZ-26); |
978 |
if (linemax == 0) linemax = BUFSIZ-26; |
if (linemax == 0) |
979 |
if (linemax < 256) linemax = 256; |
linemax = BUFSIZ-26; |
980 |
|
if (linemax < 256) |
981 |
|
linemax = 256; |
982 |
} |
} |
983 |
////if (opt & 8) { // -b |
////if (opt & 8) { // -b |
984 |
//// buflen = xatoi_u(b); |
//// buflen = xatoi_u(b); |
991 |
argc -= optind; |
argc -= optind; |
992 |
|
|
993 |
dirn = argc; |
dirn = argc; |
994 |
if (dirn <= 0) usage(); |
if (dirn <= 0) |
995 |
////if (buflen <= linemax) usage(); |
bb_show_usage(); |
996 |
|
////if (buflen <= linemax) bb_show_usage(); |
997 |
fdwdir = xopen(".", O_RDONLY|O_NDELAY); |
fdwdir = xopen(".", O_RDONLY|O_NDELAY); |
998 |
close_on_exec_on(fdwdir); |
close_on_exec_on(fdwdir); |
999 |
dir = xzalloc(dirn * sizeof(struct logdir)); |
dir = xzalloc(dirn * sizeof(dir[0])); |
1000 |
for (i = 0; i < dirn; ++i) { |
for (i = 0; i < dirn; ++i) { |
1001 |
dir[i].fddir = -1; |
dir[i].fddir = -1; |
1002 |
dir[i].fdcur = -1; |
dir[i].fdcur = -1; |
1016 |
sigaddset(&blocked_sigset, SIGALRM); |
sigaddset(&blocked_sigset, SIGALRM); |
1017 |
sigaddset(&blocked_sigset, SIGHUP); |
sigaddset(&blocked_sigset, SIGHUP); |
1018 |
sigprocmask(SIG_BLOCK, &blocked_sigset, NULL); |
sigprocmask(SIG_BLOCK, &blocked_sigset, NULL); |
1019 |
bb_signals_recursive(1 << SIGTERM, sig_term_handler); |
bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler); |
1020 |
bb_signals_recursive(1 << SIGCHLD, sig_child_handler); |
bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler); |
1021 |
bb_signals_recursive(1 << SIGALRM, sig_alarm_handler); |
bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler); |
1022 |
bb_signals_recursive(1 << SIGHUP, sig_hangup_handler); |
bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler); |
|
|
|
|
logdirs_reopen(); |
|
1023 |
|
|
1024 |
/* Without timestamps, we don't have to print each line |
/* Without timestamps, we don't have to print each line |
1025 |
* separately, so we can look for _last_ newline, not first, |
* separately, so we can look for _last_ newline, not first, |
1026 |
* thus batching writes */ |
* thus batching writes. If filtering is enabled in config, |
1027 |
if (!timestamp) |
* logdirs_reopen resets it to memchr. |
1028 |
memRchr = memrchr; |
*/ |
1029 |
|
memRchr = (timestamp ? memchr : memrchr); |
1030 |
|
|
1031 |
|
logdirs_reopen(); |
1032 |
|
|
1033 |
setvbuf(stderr, NULL, _IOFBF, linelen); |
setvbuf(stderr, NULL, _IOFBF, linelen); |
1034 |
|
|
1096 |
} |
} |
1097 |
for (i = 0; i < dirn; ++i) { |
for (i = 0; i < dirn; ++i) { |
1098 |
struct logdir *ld = &dir[i]; |
struct logdir *ld = &dir[i]; |
1099 |
if (ld->fddir == -1) continue; |
if (ld->fddir == -1) |
1100 |
|
continue; |
1101 |
if (ld->inst) |
if (ld->inst) |
1102 |
logmatch(ld); |
logmatch(ld); |
1103 |
if (ld->matcherr == 'e') { |
if (ld->matcherr == 'e') { |
1105 |
////full_write(STDERR_FILENO, printptr, printlen); |
////full_write(STDERR_FILENO, printptr, printlen); |
1106 |
fwrite(printptr, 1, printlen, stderr); |
fwrite(printptr, 1, printlen, stderr); |
1107 |
} |
} |
1108 |
if (ld->match != '+') continue; |
if (ld->match != '+') |
1109 |
|
continue; |
1110 |
buffer_pwrite(i, printptr, printlen); |
buffer_pwrite(i, printptr, printlen); |
1111 |
} |
} |
1112 |
|
|
1129 |
} |
} |
1130 |
/* linelen == no of chars incl. '\n' (or == stdin_cnt) */ |
/* linelen == no of chars incl. '\n' (or == stdin_cnt) */ |
1131 |
for (i = 0; i < dirn; ++i) { |
for (i = 0; i < dirn; ++i) { |
1132 |
if (dir[i].fddir == -1) continue; |
if (dir[i].fddir == -1) |
1133 |
|
continue; |
1134 |
if (dir[i].matcherr == 'e') { |
if (dir[i].matcherr == 'e') { |
1135 |
////full_write(STDERR_FILENO, lineptr, linelen); |
////full_write(STDERR_FILENO, lineptr, linelen); |
1136 |
fwrite(lineptr, 1, linelen, stderr); |
fwrite(lineptr, 1, linelen, stderr); |
1137 |
} |
} |
1138 |
if (dir[i].match != '+') continue; |
if (dir[i].match != '+') |
1139 |
|
continue; |
1140 |
buffer_pwrite(i, lineptr, linelen); |
buffer_pwrite(i, lineptr, linelen); |
1141 |
} |
} |
1142 |
} |
} |
1152 |
/* Move unprocessed data to the front of line */ |
/* Move unprocessed data to the front of line */ |
1153 |
memmove((timestamp ? line+26 : line), lineptr, stdin_cnt); |
memmove((timestamp ? line+26 : line), lineptr, stdin_cnt); |
1154 |
} |
} |
1155 |
fflush(NULL);//// |
fflush_all();//// |
1156 |
} |
} |
1157 |
|
|
1158 |
for (i = 0; i < dirn; ++i) { |
for (i = 0; i < dirn; ++i) { |
1159 |
if (dir[i].ppid) |
if (dir[i].ppid) |
1160 |
while (!processorstop(&dir[i])) |
while (!processorstop(&dir[i])) |
1161 |
/* repeat */; |
continue; |
1162 |
logdir_close(&dir[i]); |
logdir_close(&dir[i]); |
1163 |
} |
} |
1164 |
return 0; |
return 0; |