blob: c080b9acc703a7053c3fae5ac162327e3935f37b [file] [log] [blame]
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
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.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
Denis Vlasenkod18f52b2008-03-02 12:53:15 +000028/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denis Vlasenko83ea6432006-11-16 02:27:24 +000029/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
Denys Vlasenkoaebb7422009-08-02 00:55:49 +020031/*
32Config files
33
34On startup, and after receiving a HUP signal, svlogd checks for each
35log directory log if the configuration file log/config exists,
36and if so, reads the file line by line and adjusts configuration
37for log as follows:
38
39If the line is empty, or starts with a #, it is ignored. A line
40of the form
41
42ssize
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).
47nnum
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.
52Nmin
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.
57ttimeout
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.
64ua.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.
72Ua.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.
77pprefix
78 tells svlogd to prefix each line to be written to the log directory,
79 to standard error, or through UDP, with prefix.
80
81If a line starts with a -, +, e, or E, svlogd matches the first len characters
82of 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.
88epattern
89 the log message is selected to be printed to standard error.
90Epattern
91 the log message is deselected to be printed to standard error.
92
93Initially each line is selected to be written to log/current. Deselected
94log messages are discarded from log. Initially each line is deselected
95to be written to standard err. Log messages selected for standard error
96are written to standard error.
97
98Pattern Matching
99
100svlogd matches a log message against the string pattern as follows:
101
102pattern is applied to the log message one character by one, starting
103with the first. A character not a star (*) and not a plus (+) matches itself.
104A plus matches the next character in pattern in the log message one
105or more times. A star before the end of pattern matches any string
106in the log message that does not include the next character in pattern.
107A star at the end of pattern matches any string.
108
109Timestamps optionally added by svlogd are not considered part
110of the log message.
111
112An svlogd pattern is not a regular expression. For example consider
113a log message like this
114
1152005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
116
117The following pattern doesnt match
118
119-*pid*
120
121because the first star matches up to the first p in tcpsvd,
122and then the match fails because i is not s. To match this
123log message, you can use a pattern like this instead
124
125-*: *: pid *
126*/
127
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100128//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"
132//usage: "\n"
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"
144
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000145#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000146#include "libbb.h"
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000147#include "runit_lib.h"
148
Denis Vlasenko45946f82007-08-20 17:27:40 +0000149#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
150
151#define FMT_PTIME 30
152
Denis Vlasenko339936b2007-10-05 22:11:06 +0000153struct logdir {
Denis Vlasenko64392902007-02-03 00:53:43 +0000154 ////char *btmp;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000155 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
156 char *inst;
157 char *processor;
158 char *name;
159 unsigned size;
160 unsigned sizemax;
161 unsigned nmax;
162 unsigned nmin;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000163 unsigned rotate_period;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000164 int ppid;
165 int fddir;
166 int fdcur;
Denis Vlasenko64392902007-02-03 00:53:43 +0000167 FILE* filecur; ////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000168 int fdlock;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000169 unsigned next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000170 char fnsave[FMT_PTIME];
171 char match;
172 char matcherr;
Denis Vlasenko339936b2007-10-05 22:11:06 +0000173};
174
175
176struct globals {
177 struct logdir *dir;
178 unsigned verbose;
179 int linemax;
180 ////int buflen;
181 int linelen;
182
183 int fdwdir;
184 char **fndir;
185 int wstat;
186 unsigned nearest_rotate;
187
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200188 void* (*memRchr)(const void *, int, size_t);
Denys Vlasenko681efe22011-03-08 21:00:36 +0100189 char *shell;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200190
Denis Vlasenko339936b2007-10-05 22:11:06 +0000191 smallint exitasap;
192 smallint rotateasap;
193 smallint reopenasap;
194 smallint linecomplete;
195 smallint tmaxflag;
196
197 char repl;
198 const char *replace;
199 int fl_flag_0;
200 unsigned dirn;
201
202 sigset_t blocked_sigset;
203};
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100204#define G (*ptr_to_globals)
Denis Vlasenko339936b2007-10-05 22:11:06 +0000205#define dir (G.dir )
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 )
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200213#define memRchr (G.memRchr )
Denis Vlasenko339936b2007-10-05 22:11:06 +0000214#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 { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000226 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denis Vlasenko339936b2007-10-05 22:11:06 +0000227 linemax = 1000; \
228 /*buflen = 1024;*/ \
229 linecomplete = 1; \
230 replace = ""; \
231} while (0)
232
233#define line bb_common_bufsiz1
234
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000235
236#define FATAL "fatal: "
237#define WARNING "warning: "
238#define PAUSE "pausing: "
239#define INFO "info: "
240
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000241static void fatalx(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000242{
243 bb_error_msg_and_die(FATAL"%s", m0);
244}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000245static void warn(const char *m0)
246{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000247 bb_perror_msg(WARNING"%s", m0);
248}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000249static void warn2(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000250{
251 bb_perror_msg(WARNING"%s: %s", m0, m1);
252}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000253static void warnx(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000254{
255 bb_error_msg(WARNING"%s: %s", m0, m1);
256}
257static void pause_nomem(void)
258{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000259 bb_error_msg(PAUSE"out of memory");
260 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000261}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000262static void pause1cannot(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000263{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000264 bb_perror_msg(PAUSE"can't %s", m0);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000265 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000266}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000267static void pause2cannot(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000268{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000269 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000270 sleep(3);
271}
272
273static char* wstrdup(const char *str)
274{
275 char *s;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000276 while (!(s = strdup(str)))
277 pause_nomem();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000278 return s;
279}
280
Denys Vlasenko05e86052010-10-13 12:53:27 +0200281static unsigned pmatch(const char *p, const char *s, unsigned len)
282{
283 for (;;) {
284 char c = *p++;
285 if (!c) return !len;
286 switch (c) {
287 case '*':
288 c = *p;
289 if (!c) return 1;
290 for (;;) {
291 if (!len) return 0;
292 if (*s == c) break;
293 ++s;
294 --len;
295 }
296 continue;
297 case '+':
298 c = *p++;
299 if (c != *s) return 0;
300 for (;;) {
301 if (!len) return 1;
302 if (*s != c) break;
303 ++s;
304 --len;
305 }
306 continue;
307 /*
308 case '?':
309 if (*p == '?') {
310 if (*s != '?') return 0;
311 ++p;
312 }
313 ++s; --len;
314 continue;
315 */
316 default:
317 if (!len) return 0;
318 if (*s != c) return 0;
319 ++s;
320 --len;
321 continue;
322 }
323 }
324 return 0;
325}
326
Denis Vlasenko45946f82007-08-20 17:27:40 +0000327/*** ex fmt_ptime.[ch] ***/
328
329/* NUL terminated */
330static void fmt_time_human_30nul(char *s)
331{
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100332 struct tm *ptm;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000333 struct timeval tv;
334
335 gettimeofday(&tv, NULL);
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100336 ptm = gmtime(&tv.tv_sec);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000337 sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100338 (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),
Denis Vlasenko45946f82007-08-20 17:27:40 +0000344 (unsigned)(tv.tv_usec)
345 );
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') */
349}
350
351/* NOT terminated! */
352static void fmt_time_bernstein_25(char *s)
353{
354 uint32_t pack[3];
355 struct timeval tv;
356 unsigned sec_hi;
357
358 gettimeofday(&tv, NULL);
359 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
360 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
361 tv.tv_usec *= 1000;
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);
367 *s++ = '@';
368 bin2hex(s, (char*)pack, 12);
369}
370
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000371static void processorstart(struct logdir *ld)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000372{
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000373 char sv_ch;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000374 int pid;
375
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000376 if (!ld->processor) return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000377 if (ld->ppid) {
378 warnx("processor already running", ld->name);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000379 return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000380 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000381
382 /* vfork'ed child trashes this byte, save... */
383 sv_ch = ld->fnsave[26];
384
Denys Vlasenko681efe22011-03-08 21:00:36 +0100385 if (!G.shell)
386 G.shell = xstrdup(get_shell_name());
387
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000388 while ((pid = vfork()) == -1)
389 pause2cannot("vfork for processor", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000390 if (!pid) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000391 int fd;
392
393 /* child */
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000394 /* Non-ignored signals revert to SIG_DFL on exec anyway */
395 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000396 + (1 << SIGTERM)
397 + (1 << SIGALRM)
398 + (1 << SIGHUP)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000399 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000400 sig_unblock(SIGTERM);
401 sig_unblock(SIGALRM);
402 sig_unblock(SIGHUP);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000403
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000404 if (verbose)
405 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
406 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000407 xmove_fd(fd, 0);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000408 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000409 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000410 xmove_fd(fd, 1);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200411 fd = open("state", O_RDONLY|O_NDELAY);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000412 if (fd == -1) {
413 if (errno != ENOENT)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000414 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000415 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000416 fd = xopen("state", O_RDONLY|O_NDELAY);
417 }
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000418 xmove_fd(fd, 4);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000419 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000420 xmove_fd(fd, 5);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000421
Denys Vlasenko681efe22011-03-08 21:00:36 +0100422 execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000423 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000424 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000425 ld->fnsave[26] = sv_ch; /* ...restore */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000426 ld->ppid = pid;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000427}
428
429static unsigned processorstop(struct logdir *ld)
430{
431 char f[28];
432
433 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000434 sig_unblock(SIGHUP);
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000435 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000436 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000437 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000438 ld->ppid = 0;
439 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200440 if (ld->fddir == -1)
441 return 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000442 while (fchdir(ld->fddir) == -1)
443 pause2cannot("change directory, want processor", ld->name);
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200444 if (WEXITSTATUS(wstat) != 0) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000445 warnx("processor failed, restart", ld->name);
446 ld->fnsave[26] = 't';
447 unlink(ld->fnsave);
448 ld->fnsave[26] = 'u';
449 processorstart(ld);
450 while (fchdir(fdwdir) == -1)
451 pause1cannot("change to initial working directory");
452 return ld->processor ? 0 : 1;
453 }
454 ld->fnsave[26] = 't';
455 memcpy(f, ld->fnsave, 26);
456 f[26] = 's';
457 f[27] = '\0';
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)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000464 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000465 while (rename("newstate", "state") == -1)
466 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000467 if (verbose)
468 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000469 while (fchdir(fdwdir) == -1)
470 pause1cannot("change to initial working directory");
471 return 1;
472}
473
474static void rmoldest(struct logdir *ld)
475{
476 DIR *d;
477 struct dirent *f;
478 char oldest[FMT_PTIME];
479 int n = 0;
480
481 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
482 while (!(d = opendir(".")))
483 pause2cannot("open directory, want rotate", ld->name);
484 errno = 0;
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)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000489 warn2("can't unlink processor leftover", f->d_name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000490 } else {
491 ++n;
492 if (strcmp(f->d_name, oldest) < 0)
493 memcpy(oldest, f->d_name, 27);
494 }
495 errno = 0;
496 }
497 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000498 if (errno)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000499 warn2("can't read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000500 closedir(d);
501
502 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000503 if (verbose)
504 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000505 if ((*oldest == '@') && (unlink(oldest) == -1))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000506 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000507 }
508}
509
510static unsigned rotate(struct logdir *ld)
511{
512 struct stat st;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000513 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000514
515 if (ld->fddir == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000516 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000517 return 0;
518 }
519 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000520 while (!processorstop(ld))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000521 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000522
523 while (fchdir(ld->fddir) == -1)
524 pause2cannot("change directory, want rotate", ld->name);
525
526 /* create new filename */
527 ld->fnsave[25] = '.';
528 ld->fnsave[26] = 's';
529 if (ld->processor)
530 ld->fnsave[26] = 'u';
531 ld->fnsave[27] = '\0';
532 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000533 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000534 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000535 stat(ld->fnsave, &st);
536 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000537
Denis Vlasenko45946f82007-08-20 17:27:40 +0000538 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;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000543 }
544
545 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000546 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000547 pause2cannot("fsync current logfile", ld->name);
548 while (fchmod(ld->fdcur, 0744) == -1)
549 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000550 ////close(ld->fdcur);
551 fclose(ld->filecur);
552
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000553 if (verbose) {
554 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
555 ld->fnsave, ld->size);
556 }
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);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200561 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
562 pause2cannot("create new current", ld->name); /* very unlikely */
Denis Vlasenko64392902007-02-03 00:53:43 +0000563 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000564 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000565 ld->size = 0;
566 while (fchmod(ld->fdcur, 0644) == -1)
567 pause2cannot("set mode of current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200568
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000569 rmoldest(ld);
570 processorstart(ld);
571 }
572
573 while (fchdir(fdwdir) == -1)
574 pause1cannot("change to initial working directory");
575 return 1;
576}
577
578static int buffer_pwrite(int n, char *s, unsigned len)
579{
580 int i;
581 struct logdir *ld = &dir[n];
582
583 if (ld->sizemax) {
584 if (ld->size >= ld->sizemax)
585 rotate(ld);
586 if (len > (ld->sizemax - ld->size))
587 len = ld->sizemax - ld->size;
588 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000589 while (1) {
590 ////i = full_write(ld->fdcur, s, len);
591 ////if (i != -1) break;
592 i = fwrite(s, 1, len, ld->filecur);
593 if (i == len) break;
594
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000595 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
596 DIR *d;
597 struct dirent *f;
598 char oldest[FMT_PTIME];
599 int j = 0;
600
601 while (fchdir(ld->fddir) == -1)
602 pause2cannot("change directory, want remove old logfile",
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100603 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000604 oldest[0] = 'A';
605 oldest[1] = oldest[27] = '\0';
606 while (!(d = opendir(".")))
607 pause2cannot("open directory, want remove old logfile",
Tanguy Pruvot823694d2012-11-18 13:20:29 +0100608 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000609 errno = 0;
610 while ((f = readdir(d)))
611 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
612 ++j;
613 if (strcmp(f->d_name, oldest) < 0)
614 memcpy(oldest, f->d_name, 27);
615 }
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000616 if (errno) warn2("can't read directory, want remove old logfile",
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000617 ld->name);
618 closedir(d);
619 errno = ENOSPC;
620 if (j > ld->nmin) {
621 if (*oldest == '@') {
622 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
623 ld->name, oldest);
624 errno = 0;
625 if (unlink(oldest) == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000626 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000627 errno = ENOSPC;
628 }
629 while (fchdir(fdwdir) == -1)
630 pause1cannot("change to initial working directory");
631 }
632 }
633 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000634 if (errno)
635 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000636 }
637
638 ld->size += i;
639 if (ld->sizemax)
640 if (s[i-1] == '\n')
641 if (ld->size >= (ld->sizemax - linemax))
642 rotate(ld);
643 return i;
644}
645
646static void logdir_close(struct logdir *ld)
647{
648 if (ld->fddir == -1)
649 return;
650 if (verbose)
651 bb_error_msg(INFO"close: %s", ld->name);
652 close(ld->fddir);
653 ld->fddir = -1;
654 if (ld->fdcur == -1)
655 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000656 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000657 pause2cannot("fsync current logfile", ld->name);
658 while (fchmod(ld->fdcur, 0744) == -1)
659 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000660 ////close(ld->fdcur);
661 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000662 ld->fdcur = -1;
663 if (ld->fdlock == -1)
664 return; /* impossible */
665 close(ld->fdlock);
666 ld->fdlock = -1;
667 free(ld->processor);
668 ld->processor = NULL;
669}
670
Denys Vlasenkoa7bb3c12009-10-08 12:28:08 +0200671static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000672{
673 char buf[128];
Denis Vlasenko45946f82007-08-20 17:27:40 +0000674 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000675 char *new, *s, *np;
676 int i;
677 struct stat st;
678
Denis Vlasenko45946f82007-08-20 17:27:40 +0000679 now = monotonic_sec();
680
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000681 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
682 if (ld->fddir == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000683 warn2("can't open log directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000684 return 0;
685 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000686 close_on_exec_on(ld->fddir);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000687 if (fchdir(ld->fddir) == -1) {
688 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000689 warn2("can't change directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000690 return 0;
691 }
692 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
693 if ((ld->fdlock == -1)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200694 || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000695 ) {
696 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000697 warn2("can't lock directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000698 while (fchdir(fdwdir) == -1)
699 pause1cannot("change to initial working directory");
700 return 0;
701 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000702 close_on_exec_on(ld->fdlock);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000703
704 ld->size = 0;
705 ld->sizemax = 1000000;
706 ld->nmax = ld->nmin = 10;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000707 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000708 ld->name = (char*)fn;
709 ld->ppid = 0;
710 ld->match = '+';
711 free(ld->inst); ld->inst = NULL;
712 free(ld->processor); ld->processor = NULL;
713
714 /* read config */
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200715 i = open_read_close("config", buf, sizeof(buf) - 1);
Denis Vlasenkof223efb2007-08-03 10:58:12 +0000716 if (i < 0 && errno != ENOENT)
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000717 bb_perror_msg(WARNING"%s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000718 if (i > 0) {
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200719 buf[i] = '\0';
Denis Vlasenko45946f82007-08-20 17:27:40 +0000720 if (verbose)
721 bb_error_msg(INFO"read: %s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000722 s = buf;
723 while (s) {
724 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000725 if (np)
726 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000727 switch (s[0]) {
728 case '+':
729 case '-':
730 case 'e':
731 case 'E':
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200732 /* Filtering requires one-line buffering,
733 * resetting the "find newline" function
734 * accordingly */
735 memRchr = memchr;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000736 /* Add '\n'-terminated line to ld->inst */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000737 while (1) {
Denys Vlasenko90a99042009-09-06 02:36:23 +0200738 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000739 if (l >= 0 && new)
740 break;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000741 pause_nomem();
742 }
743 free(ld->inst);
744 ld->inst = new;
745 break;
746 case 's': {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000747 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
748 break;
749 }
750 case 'n':
Denys Vlasenko77832482010-08-12 14:14:45 +0200751 ld->nmax = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000752 break;
753 case 'N':
Denys Vlasenko77832482010-08-12 14:14:45 +0200754 ld->nmin = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000755 break;
756 case 't': {
757 static const struct suffix_mult mh_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000758 { "m", 60 },
759 { "h", 60*60 },
760 /*{ "d", 24*60*60 },*/
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200761 { "", 0 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000762 };
Denis Vlasenko45946f82007-08-20 17:27:40 +0000763 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;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000768 tmaxflag = 1;
769 }
770 break;
771 }
772 case '!':
773 if (s[1]) {
774 free(ld->processor);
775 ld->processor = wstrdup(s);
776 }
777 break;
778 }
779 s = np;
780 }
781 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
782 s = ld->inst;
783 while (s) {
784 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000785 if (np)
786 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000787 s = np;
788 }
789 }
790
791 /* open current */
792 i = stat("current", &st);
793 if (i != -1) {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000794 if (st.st_size && !(st.st_mode & S_IXUSR)) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000795 ld->fnsave[25] = '.';
796 ld->fnsave[26] = 'u';
797 ld->fnsave[27] = '\0';
798 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000799 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000800 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000801 stat(ld->fnsave, &st);
802 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000803 while (rename("current", ld->fnsave) == -1)
804 pause2cannot("rename current", ld->name);
805 rmoldest(ld);
806 i = -1;
807 } else {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000808 /* 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") */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000811 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
812 }
813 } else {
814 if (errno != ENOENT) {
815 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000816 warn2("can't stat current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000817 while (fchdir(fdwdir) == -1)
818 pause1cannot("change to initial working directory");
819 return 0;
820 }
821 }
822 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
823 pause2cannot("open current", ld->name);
Denys Vlasenkoa7ccdee2009-11-15 23:28:11 +0100824 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
825 pause2cannot("open current", ld->name); ////
Denis Vlasenko64392902007-02-03 00:53:43 +0000826 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
827
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000828 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000829 while (fchmod(ld->fdcur, 0644) == -1)
830 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000831
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000832 if (verbose) {
833 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
834 else bb_error_msg(INFO"new: %s/current", ld->name);
835 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000836
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000837 while (fchdir(fdwdir) == -1)
838 pause1cannot("change to initial working directory");
839 return 1;
840}
841
842static void logdirs_reopen(void)
843{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000844 int l;
845 int ok = 0;
846
847 tmaxflag = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000848 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000849 logdir_close(&dir[l]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000850 if (logdir_open(&dir[l], fndir[l]))
851 ok = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000852 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000853 if (!ok)
854 fatalx("no functional log directories");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000855}
856
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000857/* Will look good in libbb one day */
858static ssize_t ndelay_read(int fd, void *buf, size_t count)
859{
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);
865 return count;
866}
867
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000868/* Used for reading stdin */
Denis Vlasenko5d61e712007-09-27 10:09:59 +0000869static int buffer_pread(/*int fd, */char *s, unsigned len)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000870{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000871 unsigned now;
872 struct pollfd input;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000873 int i;
874
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200875 input.fd = STDIN_FILENO;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000876 input.events = POLLIN;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000877
Denis Vlasenkob9528352007-05-06 01:37:21 +0000878 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000879 if (rotateasap) {
880 for (i = 0; i < dirn; ++i)
881 rotate(dir + i);
882 rotateasap = 0;
883 }
884 if (exitasap) {
885 if (linecomplete)
886 return 0;
887 len = 1;
888 }
889 if (reopenasap) {
890 logdirs_reopen();
891 reopenasap = 0;
892 }
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))
898 rotate(dir + i);
899 if (LESS(dir[i].next_rotate, nearest_rotate))
900 nearest_rotate = dir[i].next_rotate;
901 }
902 }
903
Denis Vlasenko339936b2007-10-05 22:11:06 +0000904 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000905 i = nearest_rotate - now;
906 if (i > 1000000)
907 i = 1000000;
908 if (i <= 0)
909 i = 1;
910 poll(&input, 1, i * 1000);
Denis Vlasenko339936b2007-10-05 22:11:06 +0000911 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000912
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000913 i = ndelay_read(STDIN_FILENO, s, len);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000914 if (i >= 0)
915 break;
916 if (errno == EINTR)
917 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000918 if (errno != EAGAIN) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000919 warn("can't read standard input");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000920 break;
921 }
922 /* else: EAGAIN - normal, repeat silently */
Denis Vlasenkob9528352007-05-06 01:37:21 +0000923 } while (!exitasap);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000924
925 if (i > 0) {
926 int cnt;
927 linecomplete = (s[i-1] == '\n');
Denis Vlasenko45946f82007-08-20 17:27:40 +0000928 if (!repl)
929 return i;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000930
931 cnt = i;
932 while (--cnt >= 0) {
933 char ch = *s;
934 if (ch != '\n') {
935 if (ch < 32 || ch > 126)
936 *s = repl;
937 else {
938 int j;
939 for (j = 0; replace[j]; ++j) {
940 if (ch == replace[j]) {
941 *s = repl;
942 break;
943 }
944 }
945 }
946 }
947 s++;
948 }
949 }
950 return i;
951}
952
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000953static void sig_term_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000954{
955 if (verbose)
956 bb_error_msg(INFO"sig%s received", "term");
957 exitasap = 1;
958}
959
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000960static void sig_child_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000961{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000962 pid_t pid;
963 int l;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000964
965 if (verbose)
966 bb_error_msg(INFO"sig%s received", "child");
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000967 while ((pid = wait_any_nohang(&wstat)) > 0) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000968 for (l = 0; l < dirn; ++l) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000969 if (dir[l].ppid == pid) {
970 dir[l].ppid = 0;
971 processorstop(&dir[l]);
972 break;
973 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000974 }
975 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000976}
977
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000978static void sig_alarm_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000979{
980 if (verbose)
981 bb_error_msg(INFO"sig%s received", "alarm");
982 rotateasap = 1;
983}
984
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000985static void sig_hangup_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000986{
987 if (verbose)
988 bb_error_msg(INFO"sig%s received", "hangup");
989 reopenasap = 1;
990}
991
992static void logmatch(struct logdir *ld)
993{
994 char *s;
995
996 ld->match = '+';
997 ld->matcherr = 'E';
998 s = ld->inst;
999 while (s && s[0]) {
1000 switch (s[0]) {
1001 case '+':
1002 case '-':
1003 if (pmatch(s+1, line, linelen))
1004 ld->match = s[0];
1005 break;
1006 case 'e':
1007 case 'E':
1008 if (pmatch(s+1, line, linelen))
1009 ld->matcherr = s[0];
1010 break;
1011 }
1012 s += strlen(s) + 1;
1013 }
1014}
1015
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001016int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001017int svlogd_main(int argc, char **argv)
1018{
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001019 char *r, *l, *b;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001020 ssize_t stdin_cnt = 0;
1021 int i;
1022 unsigned opt;
1023 unsigned timestamp = 0;
1024
Denis Vlasenko339936b2007-10-05 22:11:06 +00001025 INIT_G();
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001026
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001027 opt_complementary = "tt:vv";
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001028 opt = getopt32(argv, "r:R:l:b:tv",
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001029 &r, &replace, &l, &b, &timestamp, &verbose);
1030 if (opt & 1) { // -r
1031 repl = r[0];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001032 if (!repl || r[1])
1033 bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001034 }
1035 if (opt & 2) if (!repl) repl = '_'; // -R
1036 if (opt & 4) { // -l
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001037 linemax = xatou_range(l, 0, BUFSIZ-26);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001038 if (linemax == 0)
1039 linemax = BUFSIZ-26;
1040 if (linemax < 256)
1041 linemax = 256;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001042 }
Denis Vlasenko64392902007-02-03 00:53:43 +00001043 ////if (opt & 8) { // -b
Denys Vlasenko77832482010-08-12 14:14:45 +02001044 //// buflen = xatoi_positive(b);
Denis Vlasenko64392902007-02-03 00:53:43 +00001045 //// if (buflen == 0) buflen = 1024;
1046 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001047 //if (opt & 0x10) timestamp++; // -t
1048 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001049 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001050 argv += optind;
1051 argc -= optind;
1052
1053 dirn = argc;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001054 if (dirn <= 0)
1055 bb_show_usage();
1056 ////if (buflen <= linemax) bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001057 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +00001058 close_on_exec_on(fdwdir);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001059 dir = xzalloc(dirn * sizeof(dir[0]));
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001060 for (i = 0; i < dirn; ++i) {
1061 dir[i].fddir = -1;
1062 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001063 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenkob9528352007-05-06 01:37:21 +00001064 /*dir[i].ppid = 0;*/
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001065 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001066 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001067 fndir = argv;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001068 /* 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 */
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001071 fl_flag_0 = fcntl(0, F_GETFL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001072
Denis Vlasenko339936b2007-10-05 22:11:06 +00001073 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);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +00001079 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);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001083
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001084 /* Without timestamps, we don't have to print each line
1085 * separately, so we can look for _last_ newline, not first,
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001086 * thus batching writes. If filtering is enabled in config,
1087 * logdirs_reopen resets it to memchr.
1088 */
1089 memRchr = (timestamp ? memchr : memrchr);
1090
1091 logdirs_reopen();
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001092
Denis Vlasenko64392902007-02-03 00:53:43 +00001093 setvbuf(stderr, NULL, _IOFBF, linelen);
1094
Denis Vlasenko4e1715f2007-01-28 14:51:32 +00001095 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001096 while (1) {
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001097 char stamp[FMT_PTIME];
1098 char *lineptr;
1099 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001100 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001101 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001102 char ch;
1103
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001104 lineptr = line;
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001105 if (timestamp)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001106 lineptr += 26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001107
1108 /* lineptr[0..linemax-1] - buffer for stdin */
1109 /* (possibly has some unprocessed data from prev loop) */
1110
1111 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001112 np = memRchr(lineptr, '\n', stdin_cnt);
1113 if (!np && !exitasap) {
1114 i = linemax - stdin_cnt; /* avail. bytes at tail */
1115 if (i >= 128) {
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001116 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001117 if (i <= 0) /* EOF or error on stdin */
1118 exitasap = 1;
1119 else {
1120 np = memRchr(lineptr + stdin_cnt, '\n', i);
1121 stdin_cnt += i;
1122 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001123 }
1124 }
1125 if (stdin_cnt <= 0 && exitasap)
1126 break;
1127
1128 /* Search for '\n' (in fact, np already holds the result) */
1129 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001130 if (np) {
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02001131 print_to_nl:
1132 /* NB: starting from here lineptr may point
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001133 * farther out into line[] */
1134 linelen = np - lineptr + 1;
1135 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001136 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1137 ch = lineptr[linelen-1];
1138
Denis Vlasenko64392902007-02-03 00:53:43 +00001139 /* 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 */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001143
1144 /* write out lineptr[0..linelen-1] to each log destination
1145 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001146 printlen = linelen;
1147 printptr = lineptr;
1148 if (timestamp) {
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001149 if (timestamp == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +00001150 fmt_time_bernstein_25(stamp);
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001151 else /* 2: */
Denis Vlasenko45946f82007-08-20 17:27:40 +00001152 fmt_time_human_30nul(stamp);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001153 printlen += 26;
1154 printptr -= 26;
1155 memcpy(printptr, stamp, 25);
1156 printptr[25] = ' ';
1157 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001158 for (i = 0; i < dirn; ++i) {
1159 struct logdir *ld = &dir[i];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001160 if (ld->fddir == -1)
1161 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001162 if (ld->inst)
1163 logmatch(ld);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001164 if (ld->matcherr == 'e') {
1165 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001166 ////full_write(STDERR_FILENO, printptr, printlen);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001167 fwrite(printptr, 1, printlen, stderr);
1168 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001169 if (ld->match != '+')
1170 continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001171 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001172 }
1173
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 */
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001178 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001179 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001180 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001181 lineptr[0] = ch = '\n';
1182 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001183 stdin_cnt = 1;
1184 } else {
1185 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001186 np = memRchr(lineptr, '\n', stdin_cnt);
1187 if (np)
1188 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001189 ch = lineptr[linelen-1];
1190 }
1191 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1192 for (i = 0; i < dirn; ++i) {
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001193 if (dir[i].fddir == -1)
1194 continue;
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001195 if (dir[i].matcherr == 'e') {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001196 ////full_write(STDERR_FILENO, lineptr, linelen);
Denis Vlasenko64392902007-02-03 00:53:43 +00001197 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001198 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001199 if (dir[i].match != '+')
1200 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001201 buffer_pwrite(i, lineptr, linelen);
1202 }
1203 }
1204
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001205 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001206 if (stdin_cnt > 0) {
1207 lineptr += linelen;
1208 /* If we see another '\n', we don't need to read
1209 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001210 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001211 if (np)
1212 goto print_to_nl;
1213 /* Move unprocessed data to the front of line */
1214 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1215 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01001216 fflush_all();////
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001217 }
1218
1219 for (i = 0; i < dirn; ++i) {
1220 if (dir[i].ppid)
1221 while (!processorstop(&dir[i]))
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001222 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001223 logdir_close(&dir[i]);
1224 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001225 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001226}