blob: dbe8df65c1258c7a4c3ad508483842f443d2e59f [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
Denys Vlasenkoaebb7422009-08-02 00:55:49 +020030/*
31Config files
32
33On startup, and after receiving a HUP signal, svlogd checks for each
34log directory log if the configuration file log/config exists,
35and if so, reads the file line by line and adjusts configuration
36for log as follows:
37
38If the line is empty, or starts with a #, it is ignored. A line
39of the form
40
41ssize
42 sets the maximum file size of current when svlogd should rotate
43 the current log file to size bytes. Default is 1000000.
44 If size is zero, svlogd doesnt rotate log files
45 You should set size to at least (2 * len).
46nnum
47 sets the number of old log files svlogd should maintain to num.
48 If svlogd sees more that num old log files in log after log file
49 rotation, it deletes the oldest one. Default is 10.
50 If num is zero, svlogd doesnt remove old log files.
51Nmin
52 sets the minimum number of old log files svlogd should maintain
53 to min. min must be less than num. If min is set, and svlogd
54 cannot write to current because the filesystem is full,
55 and it sees more than min old log files, it deletes the oldest one.
56ttimeout
57 sets the maximum age of the current log file when svlogd should
58 rotate the current log file to timeout seconds. If current
59 is timeout seconds old, and is not empty, svlogd forces log file rotation.
60!processor
61 tells svlogd to feed each recent log file through processor
62 (see above) on log file rotation. By default log files are not processed.
63ua.b.c.d[:port]
64 tells svlogd to transmit the first len characters of selected
65 log messages to the IP address a.b.c.d, port number port.
66 If port isnt set, the default port for syslog is used (514).
67 len can be set through the -l option, see below. If svlogd
68 has trouble sending udp packets, it writes error messages
69 to the log directory. Attention: logging through udp is unreliable,
70 and should be used in private networks only.
71Ua.b.c.d[:port]
72 is the same as the u line above, but the log messages are no longer
73 written to the log directory, but transmitted through udp only.
74 Error messages from svlogd concerning sending udp packages still go
75 to the log directory.
76pprefix
77 tells svlogd to prefix each line to be written to the log directory,
78 to standard error, or through UDP, with prefix.
79
80If a line starts with a -, +, e, or E, svlogd matches the first len characters
81of each log message against pattern and acts accordingly:
82
83-pattern
84 the log message is deselected.
85+pattern
86 the log message is selected.
87epattern
88 the log message is selected to be printed to standard error.
89Epattern
90 the log message is deselected to be printed to standard error.
91
92Initially each line is selected to be written to log/current. Deselected
93log messages are discarded from log. Initially each line is deselected
94to be written to standard err. Log messages selected for standard error
95are written to standard error.
96
97Pattern Matching
98
99svlogd matches a log message against the string pattern as follows:
100
101pattern is applied to the log message one character by one, starting
102with the first. A character not a star (*) and not a plus (+) matches itself.
103A plus matches the next character in pattern in the log message one
104or more times. A star before the end of pattern matches any string
105in the log message that does not include the next character in pattern.
106A star at the end of pattern matches any string.
107
108Timestamps optionally added by svlogd are not considered part
109of the log message.
110
111An svlogd pattern is not a regular expression. For example consider
112a log message like this
113
1142005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
115
116The following pattern doesnt match
117
118-*pid*
119
120because the first star matches up to the first p in tcpsvd,
121and then the match fails because i is not s. To match this
122log message, you can use a pattern like this instead
123
124-*: *: pid *
125*/
126
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200127//config:config SVLOGD
128//config: bool "svlogd"
129//config: default y
130//config: help
131//config: svlogd continuously reads log data from its standard input, optionally
132//config: filters log messages, and writes the data to one or more automatically
133//config: rotated logs.
134
135//applet:IF_SVLOGD(APPLET(svlogd, BB_DIR_USR_SBIN, BB_SUID_DROP))
136
137//kbuild:lib-$(CONFIG_SVLOGD) += svlogd.o
138
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100139//usage:#define svlogd_trivial_usage
140//usage: "[-ttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..."
141//usage:#define svlogd_full_usage "\n\n"
142//usage: "Continuously read log data from stdin and write to rotated log files in DIRs"
143//usage: "\n"
144//usage: "\n""DIR/config file modifies behavior:"
145//usage: "\n""sSIZE - when to rotate logs"
146//usage: "\n""nNUM - number of files to retain"
147/*usage: "\n""NNUM - min number files to retain" - confusing */
148/*usage: "\n""tSEC - rotate file if it get SEC seconds old" - confusing */
149//usage: "\n""!PROG - process rotated log with PROG"
150/*usage: "\n""uIPADDR - send log over UDP" - unsupported */
151/*usage: "\n""UIPADDR - send log over UDP and DONT log" - unsupported */
152/*usage: "\n""pPFX - prefix each line with PFX" - unsupported */
153//usage: "\n""+,-PATTERN - (de)select line for logging"
154//usage: "\n""E,ePATTERN - (de)select line for stderr"
155
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000156#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000157#include "libbb.h"
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000158#include "runit_lib.h"
159
Denis Vlasenko45946f82007-08-20 17:27:40 +0000160#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
161
162#define FMT_PTIME 30
163
Denis Vlasenko339936b2007-10-05 22:11:06 +0000164struct logdir {
Denis Vlasenko64392902007-02-03 00:53:43 +0000165 ////char *btmp;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000166 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
167 char *inst;
168 char *processor;
169 char *name;
170 unsigned size;
171 unsigned sizemax;
172 unsigned nmax;
173 unsigned nmin;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000174 unsigned rotate_period;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000175 int ppid;
176 int fddir;
177 int fdcur;
Denis Vlasenko64392902007-02-03 00:53:43 +0000178 FILE* filecur; ////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000179 int fdlock;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000180 unsigned next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000181 char fnsave[FMT_PTIME];
182 char match;
183 char matcherr;
Denis Vlasenko339936b2007-10-05 22:11:06 +0000184};
185
186
187struct globals {
188 struct logdir *dir;
189 unsigned verbose;
190 int linemax;
191 ////int buflen;
192 int linelen;
193
194 int fdwdir;
195 char **fndir;
196 int wstat;
197 unsigned nearest_rotate;
198
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200199 void* (*memRchr)(const void *, int, size_t);
Denys Vlasenko681efe22011-03-08 21:00:36 +0100200 char *shell;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200201
Denis Vlasenko339936b2007-10-05 22:11:06 +0000202 smallint exitasap;
203 smallint rotateasap;
204 smallint reopenasap;
205 smallint linecomplete;
206 smallint tmaxflag;
207
208 char repl;
209 const char *replace;
210 int fl_flag_0;
211 unsigned dirn;
212
213 sigset_t blocked_sigset;
214};
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100215#define G (*ptr_to_globals)
Denis Vlasenko339936b2007-10-05 22:11:06 +0000216#define dir (G.dir )
217#define verbose (G.verbose )
218#define linemax (G.linemax )
219#define buflen (G.buflen )
220#define linelen (G.linelen )
221#define fndir (G.fndir )
222#define fdwdir (G.fdwdir )
223#define wstat (G.wstat )
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200224#define memRchr (G.memRchr )
Denis Vlasenko339936b2007-10-05 22:11:06 +0000225#define nearest_rotate (G.nearest_rotate)
226#define exitasap (G.exitasap )
227#define rotateasap (G.rotateasap )
228#define reopenasap (G.reopenasap )
229#define linecomplete (G.linecomplete )
230#define tmaxflag (G.tmaxflag )
231#define repl (G.repl )
232#define replace (G.replace )
233#define blocked_sigset (G.blocked_sigset)
234#define fl_flag_0 (G.fl_flag_0 )
235#define dirn (G.dirn )
236#define INIT_G() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000237 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denis Vlasenko339936b2007-10-05 22:11:06 +0000238 linemax = 1000; \
239 /*buflen = 1024;*/ \
240 linecomplete = 1; \
241 replace = ""; \
242} while (0)
243
244#define line bb_common_bufsiz1
245
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000246
247#define FATAL "fatal: "
248#define WARNING "warning: "
249#define PAUSE "pausing: "
250#define INFO "info: "
251
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000252static void fatalx(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000253{
254 bb_error_msg_and_die(FATAL"%s", m0);
255}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000256static void warn(const char *m0)
257{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000258 bb_perror_msg(WARNING"%s", m0);
259}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000260static void warn2(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000261{
262 bb_perror_msg(WARNING"%s: %s", m0, m1);
263}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000264static void warnx(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000265{
266 bb_error_msg(WARNING"%s: %s", m0, m1);
267}
268static void pause_nomem(void)
269{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000270 bb_error_msg(PAUSE"out of memory");
271 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000272}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000273static void pause1cannot(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000274{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000275 bb_perror_msg(PAUSE"can't %s", m0);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000276 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000277}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000278static void pause2cannot(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000279{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000280 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000281 sleep(3);
282}
283
284static char* wstrdup(const char *str)
285{
286 char *s;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000287 while (!(s = strdup(str)))
288 pause_nomem();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000289 return s;
290}
291
Denys Vlasenko05e86052010-10-13 12:53:27 +0200292static unsigned pmatch(const char *p, const char *s, unsigned len)
293{
294 for (;;) {
295 char c = *p++;
296 if (!c) return !len;
297 switch (c) {
298 case '*':
299 c = *p;
300 if (!c) return 1;
301 for (;;) {
302 if (!len) return 0;
303 if (*s == c) break;
304 ++s;
305 --len;
306 }
307 continue;
308 case '+':
309 c = *p++;
310 if (c != *s) return 0;
311 for (;;) {
312 if (!len) return 1;
313 if (*s != c) break;
314 ++s;
315 --len;
316 }
317 continue;
318 /*
319 case '?':
320 if (*p == '?') {
321 if (*s != '?') return 0;
322 ++p;
323 }
324 ++s; --len;
325 continue;
326 */
327 default:
328 if (!len) return 0;
329 if (*s != c) return 0;
330 ++s;
331 --len;
332 continue;
333 }
334 }
335 return 0;
336}
337
Denis Vlasenko45946f82007-08-20 17:27:40 +0000338/*** ex fmt_ptime.[ch] ***/
339
340/* NUL terminated */
341static void fmt_time_human_30nul(char *s)
342{
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100343 struct tm *ptm;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000344 struct timeval tv;
345
346 gettimeofday(&tv, NULL);
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100347 ptm = gmtime(&tv.tv_sec);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000348 sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100349 (unsigned)(1900 + ptm->tm_year),
350 (unsigned)(ptm->tm_mon + 1),
351 (unsigned)(ptm->tm_mday),
352 (unsigned)(ptm->tm_hour),
353 (unsigned)(ptm->tm_min),
354 (unsigned)(ptm->tm_sec),
Denis Vlasenko45946f82007-08-20 17:27:40 +0000355 (unsigned)(tv.tv_usec)
356 );
357 /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
358 /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
359 /* 20 (up to '.' inclusive) + 9 (not including '\0') */
360}
361
362/* NOT terminated! */
363static void fmt_time_bernstein_25(char *s)
364{
365 uint32_t pack[3];
366 struct timeval tv;
367 unsigned sec_hi;
368
369 gettimeofday(&tv, NULL);
370 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
371 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
372 tv.tv_usec *= 1000;
373 /* Network order is big-endian: most significant byte first.
374 * This is exactly what we want here */
375 pack[0] = htonl(sec_hi);
376 pack[1] = htonl(tv.tv_sec);
377 pack[2] = htonl(tv.tv_usec);
378 *s++ = '@';
379 bin2hex(s, (char*)pack, 12);
380}
381
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000382static void processorstart(struct logdir *ld)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000383{
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000384 char sv_ch;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000385 int pid;
386
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000387 if (!ld->processor) return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000388 if (ld->ppid) {
389 warnx("processor already running", ld->name);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000390 return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000391 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000392
393 /* vfork'ed child trashes this byte, save... */
394 sv_ch = ld->fnsave[26];
395
Denys Vlasenko681efe22011-03-08 21:00:36 +0100396 if (!G.shell)
397 G.shell = xstrdup(get_shell_name());
398
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000399 while ((pid = vfork()) == -1)
400 pause2cannot("vfork for processor", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000401 if (!pid) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000402 int fd;
403
404 /* child */
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000405 /* Non-ignored signals revert to SIG_DFL on exec anyway */
406 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000407 + (1 << SIGTERM)
408 + (1 << SIGALRM)
409 + (1 << SIGHUP)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000410 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000411 sig_unblock(SIGTERM);
412 sig_unblock(SIGALRM);
413 sig_unblock(SIGHUP);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000414
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000415 if (verbose)
416 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
417 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000418 xmove_fd(fd, 0);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000419 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000420 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000421 xmove_fd(fd, 1);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200422 fd = open("state", O_RDONLY|O_NDELAY);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000423 if (fd == -1) {
424 if (errno != ENOENT)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000425 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000426 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000427 fd = xopen("state", O_RDONLY|O_NDELAY);
428 }
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000429 xmove_fd(fd, 4);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000430 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000431 xmove_fd(fd, 5);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000432
Denys Vlasenko681efe22011-03-08 21:00:36 +0100433 execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000434 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000435 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000436 ld->fnsave[26] = sv_ch; /* ...restore */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000437 ld->ppid = pid;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000438}
439
440static unsigned processorstop(struct logdir *ld)
441{
442 char f[28];
443
444 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000445 sig_unblock(SIGHUP);
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000446 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000447 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000448 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000449 ld->ppid = 0;
450 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200451 if (ld->fddir == -1)
452 return 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000453 while (fchdir(ld->fddir) == -1)
454 pause2cannot("change directory, want processor", ld->name);
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200455 if (WEXITSTATUS(wstat) != 0) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000456 warnx("processor failed, restart", ld->name);
457 ld->fnsave[26] = 't';
458 unlink(ld->fnsave);
459 ld->fnsave[26] = 'u';
460 processorstart(ld);
461 while (fchdir(fdwdir) == -1)
462 pause1cannot("change to initial working directory");
463 return ld->processor ? 0 : 1;
464 }
465 ld->fnsave[26] = 't';
466 memcpy(f, ld->fnsave, 26);
467 f[26] = 's';
468 f[27] = '\0';
469 while (rename(ld->fnsave, f) == -1)
470 pause2cannot("rename processed", ld->name);
471 while (chmod(f, 0744) == -1)
472 pause2cannot("set mode of processed", ld->name);
473 ld->fnsave[26] = 'u';
474 if (unlink(ld->fnsave) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000475 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000476 while (rename("newstate", "state") == -1)
477 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000478 if (verbose)
479 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000480 while (fchdir(fdwdir) == -1)
481 pause1cannot("change to initial working directory");
482 return 1;
483}
484
485static void rmoldest(struct logdir *ld)
486{
487 DIR *d;
488 struct dirent *f;
489 char oldest[FMT_PTIME];
490 int n = 0;
491
492 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
493 while (!(d = opendir(".")))
494 pause2cannot("open directory, want rotate", ld->name);
495 errno = 0;
496 while ((f = readdir(d))) {
497 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
498 if (f->d_name[26] == 't') {
499 if (unlink(f->d_name) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000500 warn2("can't unlink processor leftover", f->d_name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000501 } else {
502 ++n;
503 if (strcmp(f->d_name, oldest) < 0)
504 memcpy(oldest, f->d_name, 27);
505 }
506 errno = 0;
507 }
508 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000509 if (errno)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000510 warn2("can't read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000511 closedir(d);
512
513 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000514 if (verbose)
515 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000516 if ((*oldest == '@') && (unlink(oldest) == -1))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000517 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000518 }
519}
520
521static unsigned rotate(struct logdir *ld)
522{
523 struct stat st;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000524 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000525
526 if (ld->fddir == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000527 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000528 return 0;
529 }
530 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000531 while (!processorstop(ld))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000532 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000533
534 while (fchdir(ld->fddir) == -1)
535 pause2cannot("change directory, want rotate", ld->name);
536
537 /* create new filename */
538 ld->fnsave[25] = '.';
539 ld->fnsave[26] = 's';
540 if (ld->processor)
541 ld->fnsave[26] = 'u';
542 ld->fnsave[27] = '\0';
543 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000544 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000545 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000546 stat(ld->fnsave, &st);
547 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000548
Denis Vlasenko45946f82007-08-20 17:27:40 +0000549 now = monotonic_sec();
550 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
551 ld->next_rotate = now + ld->rotate_period;
552 if (LESS(ld->next_rotate, nearest_rotate))
553 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000554 }
555
556 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000557 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000558 pause2cannot("fsync current logfile", ld->name);
559 while (fchmod(ld->fdcur, 0744) == -1)
560 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000561 ////close(ld->fdcur);
562 fclose(ld->filecur);
563
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000564 if (verbose) {
565 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
566 ld->fnsave, ld->size);
567 }
568 while (rename("current", ld->fnsave) == -1)
569 pause2cannot("rename current", ld->name);
570 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
571 pause2cannot("create new current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200572 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
573 pause2cannot("create new current", ld->name); /* very unlikely */
Denis Vlasenko64392902007-02-03 00:53:43 +0000574 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000575 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000576 ld->size = 0;
577 while (fchmod(ld->fdcur, 0644) == -1)
578 pause2cannot("set mode of current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200579
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000580 rmoldest(ld);
581 processorstart(ld);
582 }
583
584 while (fchdir(fdwdir) == -1)
585 pause1cannot("change to initial working directory");
586 return 1;
587}
588
589static int buffer_pwrite(int n, char *s, unsigned len)
590{
591 int i;
592 struct logdir *ld = &dir[n];
593
594 if (ld->sizemax) {
595 if (ld->size >= ld->sizemax)
596 rotate(ld);
597 if (len > (ld->sizemax - ld->size))
598 len = ld->sizemax - ld->size;
599 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000600 while (1) {
601 ////i = full_write(ld->fdcur, s, len);
602 ////if (i != -1) break;
603 i = fwrite(s, 1, len, ld->filecur);
604 if (i == len) break;
605
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000606 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
607 DIR *d;
608 struct dirent *f;
609 char oldest[FMT_PTIME];
610 int j = 0;
611
612 while (fchdir(ld->fddir) == -1)
613 pause2cannot("change directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100614 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000615 oldest[0] = 'A';
616 oldest[1] = oldest[27] = '\0';
617 while (!(d = opendir(".")))
618 pause2cannot("open directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100619 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000620 errno = 0;
621 while ((f = readdir(d)))
622 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
623 ++j;
624 if (strcmp(f->d_name, oldest) < 0)
625 memcpy(oldest, f->d_name, 27);
626 }
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000627 if (errno) warn2("can't read directory, want remove old logfile",
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000628 ld->name);
629 closedir(d);
630 errno = ENOSPC;
631 if (j > ld->nmin) {
632 if (*oldest == '@') {
633 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
634 ld->name, oldest);
635 errno = 0;
636 if (unlink(oldest) == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000637 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000638 errno = ENOSPC;
639 }
640 while (fchdir(fdwdir) == -1)
641 pause1cannot("change to initial working directory");
642 }
643 }
644 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000645 if (errno)
646 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000647 }
648
649 ld->size += i;
650 if (ld->sizemax)
651 if (s[i-1] == '\n')
652 if (ld->size >= (ld->sizemax - linemax))
653 rotate(ld);
654 return i;
655}
656
657static void logdir_close(struct logdir *ld)
658{
659 if (ld->fddir == -1)
660 return;
661 if (verbose)
662 bb_error_msg(INFO"close: %s", ld->name);
663 close(ld->fddir);
664 ld->fddir = -1;
665 if (ld->fdcur == -1)
666 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000667 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000668 pause2cannot("fsync current logfile", ld->name);
669 while (fchmod(ld->fdcur, 0744) == -1)
670 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000671 ////close(ld->fdcur);
672 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000673 ld->fdcur = -1;
674 if (ld->fdlock == -1)
675 return; /* impossible */
676 close(ld->fdlock);
677 ld->fdlock = -1;
678 free(ld->processor);
679 ld->processor = NULL;
680}
681
Denys Vlasenkoa7bb3c12009-10-08 12:28:08 +0200682static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000683{
684 char buf[128];
Denis Vlasenko45946f82007-08-20 17:27:40 +0000685 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000686 char *new, *s, *np;
687 int i;
688 struct stat st;
689
Denis Vlasenko45946f82007-08-20 17:27:40 +0000690 now = monotonic_sec();
691
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000692 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
693 if (ld->fddir == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000694 warn2("can't open log directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000695 return 0;
696 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000697 close_on_exec_on(ld->fddir);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000698 if (fchdir(ld->fddir) == -1) {
699 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000700 warn2("can't change directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000701 return 0;
702 }
703 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
704 if ((ld->fdlock == -1)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200705 || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000706 ) {
707 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000708 warn2("can't lock directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000709 while (fchdir(fdwdir) == -1)
710 pause1cannot("change to initial working directory");
711 return 0;
712 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000713 close_on_exec_on(ld->fdlock);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000714
715 ld->size = 0;
716 ld->sizemax = 1000000;
717 ld->nmax = ld->nmin = 10;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000718 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000719 ld->name = (char*)fn;
720 ld->ppid = 0;
721 ld->match = '+';
722 free(ld->inst); ld->inst = NULL;
723 free(ld->processor); ld->processor = NULL;
724
725 /* read config */
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200726 i = open_read_close("config", buf, sizeof(buf) - 1);
Denis Vlasenkof223efb2007-08-03 10:58:12 +0000727 if (i < 0 && errno != ENOENT)
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000728 bb_perror_msg(WARNING"%s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000729 if (i > 0) {
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200730 buf[i] = '\0';
Denis Vlasenko45946f82007-08-20 17:27:40 +0000731 if (verbose)
732 bb_error_msg(INFO"read: %s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000733 s = buf;
734 while (s) {
735 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000736 if (np)
737 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000738 switch (s[0]) {
739 case '+':
740 case '-':
741 case 'e':
742 case 'E':
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200743 /* Filtering requires one-line buffering,
744 * resetting the "find newline" function
745 * accordingly */
746 memRchr = memchr;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000747 /* Add '\n'-terminated line to ld->inst */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000748 while (1) {
Denys Vlasenko90a99042009-09-06 02:36:23 +0200749 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000750 if (l >= 0 && new)
751 break;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000752 pause_nomem();
753 }
754 free(ld->inst);
755 ld->inst = new;
756 break;
757 case 's': {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000758 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
759 break;
760 }
761 case 'n':
Denys Vlasenko77832482010-08-12 14:14:45 +0200762 ld->nmax = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000763 break;
764 case 'N':
Denys Vlasenko77832482010-08-12 14:14:45 +0200765 ld->nmin = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000766 break;
767 case 't': {
768 static const struct suffix_mult mh_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000769 { "m", 60 },
770 { "h", 60*60 },
771 /*{ "d", 24*60*60 },*/
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200772 { "", 0 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000773 };
Denis Vlasenko45946f82007-08-20 17:27:40 +0000774 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
775 if (ld->rotate_period) {
776 ld->next_rotate = now + ld->rotate_period;
777 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
778 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000779 tmaxflag = 1;
780 }
781 break;
782 }
783 case '!':
784 if (s[1]) {
785 free(ld->processor);
786 ld->processor = wstrdup(s);
787 }
788 break;
789 }
790 s = np;
791 }
792 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
793 s = ld->inst;
794 while (s) {
795 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000796 if (np)
797 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000798 s = np;
799 }
800 }
801
802 /* open current */
803 i = stat("current", &st);
804 if (i != -1) {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000805 if (st.st_size && !(st.st_mode & S_IXUSR)) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000806 ld->fnsave[25] = '.';
807 ld->fnsave[26] = 'u';
808 ld->fnsave[27] = '\0';
809 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000810 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000811 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000812 stat(ld->fnsave, &st);
813 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000814 while (rename("current", ld->fnsave) == -1)
815 pause2cannot("rename current", ld->name);
816 rmoldest(ld);
817 i = -1;
818 } else {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000819 /* st.st_size can be not just bigger, but WIDER!
820 * This code is safe: if st.st_size > 4GB, we select
821 * ld->sizemax (because it's "unsigned") */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000822 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
823 }
824 } else {
825 if (errno != ENOENT) {
826 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000827 warn2("can't stat current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000828 while (fchdir(fdwdir) == -1)
829 pause1cannot("change to initial working directory");
830 return 0;
831 }
832 }
833 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
834 pause2cannot("open current", ld->name);
Denys Vlasenkoa7ccdee2009-11-15 23:28:11 +0100835 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
836 pause2cannot("open current", ld->name); ////
Denis Vlasenko64392902007-02-03 00:53:43 +0000837 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
838
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000839 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000840 while (fchmod(ld->fdcur, 0644) == -1)
841 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000842
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000843 if (verbose) {
844 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
845 else bb_error_msg(INFO"new: %s/current", ld->name);
846 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000847
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000848 while (fchdir(fdwdir) == -1)
849 pause1cannot("change to initial working directory");
850 return 1;
851}
852
853static void logdirs_reopen(void)
854{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000855 int l;
856 int ok = 0;
857
858 tmaxflag = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000859 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000860 logdir_close(&dir[l]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000861 if (logdir_open(&dir[l], fndir[l]))
862 ok = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000863 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000864 if (!ok)
865 fatalx("no functional log directories");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000866}
867
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000868/* Will look good in libbb one day */
869static ssize_t ndelay_read(int fd, void *buf, size_t count)
870{
871 if (!(fl_flag_0 & O_NONBLOCK))
872 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
873 count = safe_read(fd, buf, count);
874 if (!(fl_flag_0 & O_NONBLOCK))
875 fcntl(fd, F_SETFL, fl_flag_0);
876 return count;
877}
878
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000879/* Used for reading stdin */
Denis Vlasenko5d61e712007-09-27 10:09:59 +0000880static int buffer_pread(/*int fd, */char *s, unsigned len)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000881{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000882 unsigned now;
883 struct pollfd input;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000884 int i;
885
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200886 input.fd = STDIN_FILENO;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000887 input.events = POLLIN;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000888
Denis Vlasenkob9528352007-05-06 01:37:21 +0000889 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000890 if (rotateasap) {
891 for (i = 0; i < dirn; ++i)
892 rotate(dir + i);
893 rotateasap = 0;
894 }
895 if (exitasap) {
896 if (linecomplete)
897 return 0;
898 len = 1;
899 }
900 if (reopenasap) {
901 logdirs_reopen();
902 reopenasap = 0;
903 }
904 now = monotonic_sec();
905 nearest_rotate = now + (45 * 60 + 45);
906 for (i = 0; i < dirn; ++i) {
907 if (dir[i].rotate_period) {
908 if (LESS(dir[i].next_rotate, now))
909 rotate(dir + i);
910 if (LESS(dir[i].next_rotate, nearest_rotate))
911 nearest_rotate = dir[i].next_rotate;
912 }
913 }
914
Denis Vlasenko339936b2007-10-05 22:11:06 +0000915 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000916 i = nearest_rotate - now;
917 if (i > 1000000)
918 i = 1000000;
919 if (i <= 0)
920 i = 1;
921 poll(&input, 1, i * 1000);
Denis Vlasenko339936b2007-10-05 22:11:06 +0000922 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000923
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000924 i = ndelay_read(STDIN_FILENO, s, len);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000925 if (i >= 0)
926 break;
927 if (errno == EINTR)
928 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000929 if (errno != EAGAIN) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000930 warn("can't read standard input");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000931 break;
932 }
933 /* else: EAGAIN - normal, repeat silently */
Denis Vlasenkob9528352007-05-06 01:37:21 +0000934 } while (!exitasap);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000935
936 if (i > 0) {
937 int cnt;
938 linecomplete = (s[i-1] == '\n');
Denis Vlasenko45946f82007-08-20 17:27:40 +0000939 if (!repl)
940 return i;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000941
942 cnt = i;
943 while (--cnt >= 0) {
944 char ch = *s;
945 if (ch != '\n') {
946 if (ch < 32 || ch > 126)
947 *s = repl;
948 else {
949 int j;
950 for (j = 0; replace[j]; ++j) {
951 if (ch == replace[j]) {
952 *s = repl;
953 break;
954 }
955 }
956 }
957 }
958 s++;
959 }
960 }
961 return i;
962}
963
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000964static void sig_term_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000965{
966 if (verbose)
967 bb_error_msg(INFO"sig%s received", "term");
968 exitasap = 1;
969}
970
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000971static void sig_child_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000972{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000973 pid_t pid;
974 int l;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000975
976 if (verbose)
977 bb_error_msg(INFO"sig%s received", "child");
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000978 while ((pid = wait_any_nohang(&wstat)) > 0) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000979 for (l = 0; l < dirn; ++l) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000980 if (dir[l].ppid == pid) {
981 dir[l].ppid = 0;
982 processorstop(&dir[l]);
983 break;
984 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000985 }
986 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000987}
988
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000989static void sig_alarm_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000990{
991 if (verbose)
992 bb_error_msg(INFO"sig%s received", "alarm");
993 rotateasap = 1;
994}
995
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000996static void sig_hangup_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000997{
998 if (verbose)
999 bb_error_msg(INFO"sig%s received", "hangup");
1000 reopenasap = 1;
1001}
1002
1003static void logmatch(struct logdir *ld)
1004{
1005 char *s;
1006
1007 ld->match = '+';
1008 ld->matcherr = 'E';
1009 s = ld->inst;
1010 while (s && s[0]) {
1011 switch (s[0]) {
1012 case '+':
1013 case '-':
1014 if (pmatch(s+1, line, linelen))
1015 ld->match = s[0];
1016 break;
1017 case 'e':
1018 case 'E':
1019 if (pmatch(s+1, line, linelen))
1020 ld->matcherr = s[0];
1021 break;
1022 }
1023 s += strlen(s) + 1;
1024 }
1025}
1026
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001027int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001028int svlogd_main(int argc, char **argv)
1029{
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001030 char *r, *l, *b;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001031 ssize_t stdin_cnt = 0;
1032 int i;
1033 unsigned opt;
1034 unsigned timestamp = 0;
1035
Denis Vlasenko339936b2007-10-05 22:11:06 +00001036 INIT_G();
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001037
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001038 opt_complementary = "tt:vv";
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001039 opt = getopt32(argv, "r:R:l:b:tv",
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001040 &r, &replace, &l, &b, &timestamp, &verbose);
1041 if (opt & 1) { // -r
1042 repl = r[0];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001043 if (!repl || r[1])
1044 bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001045 }
1046 if (opt & 2) if (!repl) repl = '_'; // -R
1047 if (opt & 4) { // -l
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001048 linemax = xatou_range(l, 0, BUFSIZ-26);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001049 if (linemax == 0)
1050 linemax = BUFSIZ-26;
1051 if (linemax < 256)
1052 linemax = 256;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001053 }
Denis Vlasenko64392902007-02-03 00:53:43 +00001054 ////if (opt & 8) { // -b
Denys Vlasenko77832482010-08-12 14:14:45 +02001055 //// buflen = xatoi_positive(b);
Denis Vlasenko64392902007-02-03 00:53:43 +00001056 //// if (buflen == 0) buflen = 1024;
1057 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001058 //if (opt & 0x10) timestamp++; // -t
1059 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001060 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001061 argv += optind;
1062 argc -= optind;
1063
1064 dirn = argc;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001065 if (dirn <= 0)
1066 bb_show_usage();
1067 ////if (buflen <= linemax) bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001068 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +00001069 close_on_exec_on(fdwdir);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001070 dir = xzalloc(dirn * sizeof(dir[0]));
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001071 for (i = 0; i < dirn; ++i) {
1072 dir[i].fddir = -1;
1073 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001074 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenkob9528352007-05-06 01:37:21 +00001075 /*dir[i].ppid = 0;*/
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001076 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001077 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001078 fndir = argv;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001079 /* We cannot set NONBLOCK on fd #0 permanently - this setting
1080 * _isn't_ per-process! It is shared among all other processes
1081 * with the same stdin */
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001082 fl_flag_0 = fcntl(0, F_GETFL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001083
Denis Vlasenko339936b2007-10-05 22:11:06 +00001084 sigemptyset(&blocked_sigset);
1085 sigaddset(&blocked_sigset, SIGTERM);
1086 sigaddset(&blocked_sigset, SIGCHLD);
1087 sigaddset(&blocked_sigset, SIGALRM);
1088 sigaddset(&blocked_sigset, SIGHUP);
1089 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +00001090 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
1091 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
1092 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
1093 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001094
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001095 /* Without timestamps, we don't have to print each line
1096 * separately, so we can look for _last_ newline, not first,
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001097 * thus batching writes. If filtering is enabled in config,
1098 * logdirs_reopen resets it to memchr.
1099 */
1100 memRchr = (timestamp ? memchr : memrchr);
1101
1102 logdirs_reopen();
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001103
Denis Vlasenko64392902007-02-03 00:53:43 +00001104 setvbuf(stderr, NULL, _IOFBF, linelen);
1105
Denis Vlasenko4e1715f2007-01-28 14:51:32 +00001106 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001107 while (1) {
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001108 char stamp[FMT_PTIME];
1109 char *lineptr;
1110 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001111 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001112 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001113 char ch;
1114
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001115 lineptr = line;
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001116 if (timestamp)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001117 lineptr += 26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001118
1119 /* lineptr[0..linemax-1] - buffer for stdin */
1120 /* (possibly has some unprocessed data from prev loop) */
1121
1122 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001123 np = memRchr(lineptr, '\n', stdin_cnt);
1124 if (!np && !exitasap) {
1125 i = linemax - stdin_cnt; /* avail. bytes at tail */
1126 if (i >= 128) {
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001127 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001128 if (i <= 0) /* EOF or error on stdin */
1129 exitasap = 1;
1130 else {
1131 np = memRchr(lineptr + stdin_cnt, '\n', i);
1132 stdin_cnt += i;
1133 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001134 }
1135 }
1136 if (stdin_cnt <= 0 && exitasap)
1137 break;
1138
1139 /* Search for '\n' (in fact, np already holds the result) */
1140 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001141 if (np) {
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02001142 print_to_nl:
1143 /* NB: starting from here lineptr may point
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001144 * farther out into line[] */
1145 linelen = np - lineptr + 1;
1146 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001147 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1148 ch = lineptr[linelen-1];
1149
Denis Vlasenko64392902007-02-03 00:53:43 +00001150 /* Biggest performance hit was coming from the fact
1151 * that we did not buffer writes. We were reading many lines
1152 * in one read() above, but wrote one line per write().
1153 * We are using stdio to fix that */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001154
1155 /* write out lineptr[0..linelen-1] to each log destination
1156 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001157 printlen = linelen;
1158 printptr = lineptr;
1159 if (timestamp) {
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001160 if (timestamp == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +00001161 fmt_time_bernstein_25(stamp);
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001162 else /* 2: */
Denis Vlasenko45946f82007-08-20 17:27:40 +00001163 fmt_time_human_30nul(stamp);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001164 printlen += 26;
1165 printptr -= 26;
1166 memcpy(printptr, stamp, 25);
1167 printptr[25] = ' ';
1168 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001169 for (i = 0; i < dirn; ++i) {
1170 struct logdir *ld = &dir[i];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001171 if (ld->fddir == -1)
1172 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001173 if (ld->inst)
1174 logmatch(ld);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001175 if (ld->matcherr == 'e') {
1176 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001177 ////full_write(STDERR_FILENO, printptr, printlen);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001178 fwrite(printptr, 1, printlen, stderr);
1179 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001180 if (ld->match != '+')
1181 continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001182 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001183 }
1184
1185 /* If we didn't see '\n' (long input line), */
1186 /* read/write repeatedly until we see it */
1187 while (ch != '\n') {
1188 /* lineptr is emptied now, safe to use as buffer */
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001189 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001190 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001191 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001192 lineptr[0] = ch = '\n';
1193 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001194 stdin_cnt = 1;
1195 } else {
1196 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001197 np = memRchr(lineptr, '\n', stdin_cnt);
1198 if (np)
1199 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001200 ch = lineptr[linelen-1];
1201 }
1202 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1203 for (i = 0; i < dirn; ++i) {
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001204 if (dir[i].fddir == -1)
1205 continue;
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001206 if (dir[i].matcherr == 'e') {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001207 ////full_write(STDERR_FILENO, lineptr, linelen);
Denis Vlasenko64392902007-02-03 00:53:43 +00001208 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001209 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001210 if (dir[i].match != '+')
1211 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001212 buffer_pwrite(i, lineptr, linelen);
1213 }
1214 }
1215
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001216 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001217 if (stdin_cnt > 0) {
1218 lineptr += linelen;
1219 /* If we see another '\n', we don't need to read
1220 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001221 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001222 if (np)
1223 goto print_to_nl;
1224 /* Move unprocessed data to the front of line */
1225 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1226 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01001227 fflush_all();////
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001228 }
1229
1230 for (i = 0; i < dirn; ++i) {
1231 if (dir[i].ppid)
1232 while (!processorstop(&dir[i]))
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001233 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001234 logdir_close(&dir[i]);
1235 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001236 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001237}