blob: dfd7e38a0338f21172eb1dfd46de1a0e9c981f65 [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
Denys Vlasenko95f79532017-08-02 14:26:33 +020016THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
Denis Vlasenko83ea6432006-11-16 02:27:24 +000017WARRANTIES, 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*/
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200126//config:config SVLOGD
Denys Vlasenko4eed2c62017-07-18 22:01:24 +0200127//config: bool "svlogd (15 kb)"
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200128//config: default y
129//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200130//config: svlogd continuously reads log data from its standard input, optionally
131//config: filters log messages, and writes the data to one or more automatically
132//config: rotated logs.
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200133
134//applet:IF_SVLOGD(APPLET(svlogd, BB_DIR_USR_SBIN, BB_SUID_DROP))
135
136//kbuild:lib-$(CONFIG_SVLOGD) += svlogd.o
137
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100138//usage:#define svlogd_trivial_usage
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200139//usage: "[-tttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..."
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100140//usage:#define svlogd_full_usage "\n\n"
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200141//usage: "Read log data from stdin and write to rotated log files in DIRs"
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100142//usage: "\n"
143//usage: "\n""DIR/config file modifies behavior:"
144//usage: "\n""sSIZE - when to rotate logs"
145//usage: "\n""nNUM - number of files to retain"
146/*usage: "\n""NNUM - min number files to retain" - confusing */
147/*usage: "\n""tSEC - rotate file if it get SEC seconds old" - confusing */
148//usage: "\n""!PROG - process rotated log with PROG"
149/*usage: "\n""uIPADDR - send log over UDP" - unsupported */
150/*usage: "\n""UIPADDR - send log over UDP and DONT log" - unsupported */
151/*usage: "\n""pPFX - prefix each line with PFX" - unsupported */
152//usage: "\n""+,-PATTERN - (de)select line for logging"
153//usage: "\n""E,ePATTERN - (de)select line for stderr"
154
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000155#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000156#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200157#include "common_bufsiz.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 )
Denys Vlasenko9de2e5a2016-04-21 18:38:51 +0200236#define line bb_common_bufsiz1
Denis Vlasenko339936b2007-10-05 22:11:06 +0000237#define INIT_G() do { \
Denys Vlasenko9de2e5a2016-04-21 18:38:51 +0200238 setup_common_bufsiz(); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000239 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denis Vlasenko339936b2007-10-05 22:11:06 +0000240 linemax = 1000; \
241 /*buflen = 1024;*/ \
242 linecomplete = 1; \
243 replace = ""; \
244} while (0)
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 */
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200341static void fmt_time_human_30nul(char *s, char dt_delim)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000342{
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);
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200348 sprintf(s, "%04u-%02u-%02u%c%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),
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200352 dt_delim,
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100353 (unsigned)(ptm->tm_hour),
354 (unsigned)(ptm->tm_min),
355 (unsigned)(ptm->tm_sec),
Denis Vlasenko45946f82007-08-20 17:27:40 +0000356 (unsigned)(tv.tv_usec)
357 );
358 /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
359 /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
360 /* 20 (up to '.' inclusive) + 9 (not including '\0') */
361}
362
363/* NOT terminated! */
364static void fmt_time_bernstein_25(char *s)
365{
366 uint32_t pack[3];
367 struct timeval tv;
368 unsigned sec_hi;
369
370 gettimeofday(&tv, NULL);
371 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
372 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
373 tv.tv_usec *= 1000;
374 /* Network order is big-endian: most significant byte first.
375 * This is exactly what we want here */
376 pack[0] = htonl(sec_hi);
377 pack[1] = htonl(tv.tv_sec);
378 pack[2] = htonl(tv.tv_usec);
379 *s++ = '@';
380 bin2hex(s, (char*)pack, 12);
381}
382
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000383static void processorstart(struct logdir *ld)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000384{
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000385 char sv_ch;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000386 int pid;
387
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000388 if (!ld->processor) return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000389 if (ld->ppid) {
390 warnx("processor already running", ld->name);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000391 return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000392 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000393
394 /* vfork'ed child trashes this byte, save... */
395 sv_ch = ld->fnsave[26];
396
Denys Vlasenko681efe22011-03-08 21:00:36 +0100397 if (!G.shell)
398 G.shell = xstrdup(get_shell_name());
399
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000400 while ((pid = vfork()) == -1)
401 pause2cannot("vfork for processor", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000402 if (!pid) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000403 int fd;
404
405 /* child */
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000406 /* Non-ignored signals revert to SIG_DFL on exec anyway */
407 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000408 + (1 << SIGTERM)
409 + (1 << SIGALRM)
410 + (1 << SIGHUP)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000411 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000412 sig_unblock(SIGTERM);
413 sig_unblock(SIGALRM);
414 sig_unblock(SIGHUP);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000415
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000416 if (verbose)
417 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
418 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000419 xmove_fd(fd, 0);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000420 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000421 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000422 xmove_fd(fd, 1);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200423 fd = open("state", O_RDONLY|O_NDELAY);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000424 if (fd == -1) {
425 if (errno != ENOENT)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000426 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000427 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000428 fd = xopen("state", O_RDONLY|O_NDELAY);
429 }
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000430 xmove_fd(fd, 4);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000431 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000432 xmove_fd(fd, 5);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000433
Denys Vlasenko681efe22011-03-08 21:00:36 +0100434 execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000435 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000436 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000437 ld->fnsave[26] = sv_ch; /* ...restore */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000438 ld->ppid = pid;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000439}
440
441static unsigned processorstop(struct logdir *ld)
442{
443 char f[28];
444
445 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000446 sig_unblock(SIGHUP);
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000447 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000448 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000449 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000450 ld->ppid = 0;
451 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200452 if (ld->fddir == -1)
453 return 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000454 while (fchdir(ld->fddir) == -1)
455 pause2cannot("change directory, want processor", ld->name);
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200456 if (WEXITSTATUS(wstat) != 0) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000457 warnx("processor failed, restart", ld->name);
458 ld->fnsave[26] = 't';
459 unlink(ld->fnsave);
460 ld->fnsave[26] = 'u';
461 processorstart(ld);
462 while (fchdir(fdwdir) == -1)
463 pause1cannot("change to initial working directory");
464 return ld->processor ? 0 : 1;
465 }
466 ld->fnsave[26] = 't';
467 memcpy(f, ld->fnsave, 26);
468 f[26] = 's';
469 f[27] = '\0';
470 while (rename(ld->fnsave, f) == -1)
471 pause2cannot("rename processed", ld->name);
472 while (chmod(f, 0744) == -1)
473 pause2cannot("set mode of processed", ld->name);
474 ld->fnsave[26] = 'u';
475 if (unlink(ld->fnsave) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000476 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000477 while (rename("newstate", "state") == -1)
478 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000479 if (verbose)
480 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000481 while (fchdir(fdwdir) == -1)
482 pause1cannot("change to initial working directory");
483 return 1;
484}
485
486static void rmoldest(struct logdir *ld)
487{
488 DIR *d;
489 struct dirent *f;
490 char oldest[FMT_PTIME];
491 int n = 0;
492
493 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
494 while (!(d = opendir(".")))
495 pause2cannot("open directory, want rotate", ld->name);
496 errno = 0;
497 while ((f = readdir(d))) {
498 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
499 if (f->d_name[26] == 't') {
500 if (unlink(f->d_name) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000501 warn2("can't unlink processor leftover", f->d_name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000502 } else {
503 ++n;
504 if (strcmp(f->d_name, oldest) < 0)
505 memcpy(oldest, f->d_name, 27);
506 }
507 errno = 0;
508 }
509 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000510 if (errno)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000511 warn2("can't read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000512 closedir(d);
513
514 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000515 if (verbose)
516 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000517 if ((*oldest == '@') && (unlink(oldest) == -1))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000518 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000519 }
520}
521
522static unsigned rotate(struct logdir *ld)
523{
524 struct stat st;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000525 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000526
527 if (ld->fddir == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000528 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000529 return 0;
530 }
531 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000532 while (!processorstop(ld))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000533 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000534
535 while (fchdir(ld->fddir) == -1)
536 pause2cannot("change directory, want rotate", ld->name);
537
538 /* create new filename */
539 ld->fnsave[25] = '.';
540 ld->fnsave[26] = 's';
541 if (ld->processor)
542 ld->fnsave[26] = 'u';
543 ld->fnsave[27] = '\0';
544 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000545 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000546 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000547 stat(ld->fnsave, &st);
548 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000549
Denis Vlasenko45946f82007-08-20 17:27:40 +0000550 now = monotonic_sec();
551 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
552 ld->next_rotate = now + ld->rotate_period;
553 if (LESS(ld->next_rotate, nearest_rotate))
554 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000555 }
556
557 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000558 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000559 pause2cannot("fsync current logfile", ld->name);
560 while (fchmod(ld->fdcur, 0744) == -1)
561 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000562 ////close(ld->fdcur);
563 fclose(ld->filecur);
564
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000565 if (verbose) {
566 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
567 ld->fnsave, ld->size);
568 }
569 while (rename("current", ld->fnsave) == -1)
570 pause2cannot("rename current", ld->name);
571 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
572 pause2cannot("create new current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200573 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
574 pause2cannot("create new current", ld->name); /* very unlikely */
Denis Vlasenko64392902007-02-03 00:53:43 +0000575 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000576 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000577 ld->size = 0;
578 while (fchmod(ld->fdcur, 0644) == -1)
579 pause2cannot("set mode of current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200580
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000581 rmoldest(ld);
582 processorstart(ld);
583 }
584
585 while (fchdir(fdwdir) == -1)
586 pause1cannot("change to initial working directory");
587 return 1;
588}
589
590static int buffer_pwrite(int n, char *s, unsigned len)
591{
592 int i;
593 struct logdir *ld = &dir[n];
594
595 if (ld->sizemax) {
596 if (ld->size >= ld->sizemax)
597 rotate(ld);
598 if (len > (ld->sizemax - ld->size))
599 len = ld->sizemax - ld->size;
600 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000601 while (1) {
602 ////i = full_write(ld->fdcur, s, len);
603 ////if (i != -1) break;
604 i = fwrite(s, 1, len, ld->filecur);
605 if (i == len) break;
606
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000607 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
608 DIR *d;
609 struct dirent *f;
610 char oldest[FMT_PTIME];
611 int j = 0;
612
613 while (fchdir(ld->fddir) == -1)
614 pause2cannot("change directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100615 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000616 oldest[0] = 'A';
617 oldest[1] = oldest[27] = '\0';
618 while (!(d = opendir(".")))
619 pause2cannot("open directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100620 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000621 errno = 0;
622 while ((f = readdir(d)))
623 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
624 ++j;
625 if (strcmp(f->d_name, oldest) < 0)
626 memcpy(oldest, f->d_name, 27);
627 }
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000628 if (errno) warn2("can't read directory, want remove old logfile",
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000629 ld->name);
630 closedir(d);
631 errno = ENOSPC;
632 if (j > ld->nmin) {
633 if (*oldest == '@') {
634 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
635 ld->name, oldest);
636 errno = 0;
637 if (unlink(oldest) == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000638 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000639 errno = ENOSPC;
640 }
641 while (fchdir(fdwdir) == -1)
642 pause1cannot("change to initial working directory");
643 }
644 }
645 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000646 if (errno)
647 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000648 }
649
650 ld->size += i;
651 if (ld->sizemax)
652 if (s[i-1] == '\n')
653 if (ld->size >= (ld->sizemax - linemax))
654 rotate(ld);
655 return i;
656}
657
658static void logdir_close(struct logdir *ld)
659{
660 if (ld->fddir == -1)
661 return;
662 if (verbose)
663 bb_error_msg(INFO"close: %s", ld->name);
664 close(ld->fddir);
665 ld->fddir = -1;
666 if (ld->fdcur == -1)
667 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000668 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000669 pause2cannot("fsync current logfile", ld->name);
670 while (fchmod(ld->fdcur, 0744) == -1)
671 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000672 ////close(ld->fdcur);
673 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000674 ld->fdcur = -1;
675 if (ld->fdlock == -1)
676 return; /* impossible */
677 close(ld->fdlock);
678 ld->fdlock = -1;
679 free(ld->processor);
680 ld->processor = NULL;
681}
682
Denys Vlasenkoa7bb3c12009-10-08 12:28:08 +0200683static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000684{
685 char buf[128];
Denis Vlasenko45946f82007-08-20 17:27:40 +0000686 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000687 char *new, *s, *np;
688 int i;
689 struct stat st;
690
Denis Vlasenko45946f82007-08-20 17:27:40 +0000691 now = monotonic_sec();
692
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000693 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
694 if (ld->fddir == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000695 warn2("can't open log directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000696 return 0;
697 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000698 close_on_exec_on(ld->fddir);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000699 if (fchdir(ld->fddir) == -1) {
700 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000701 warn2("can't change directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000702 return 0;
703 }
704 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
705 if ((ld->fdlock == -1)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200706 || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000707 ) {
708 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000709 warn2("can't lock directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000710 while (fchdir(fdwdir) == -1)
711 pause1cannot("change to initial working directory");
712 return 0;
713 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000714 close_on_exec_on(ld->fdlock);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000715
716 ld->size = 0;
717 ld->sizemax = 1000000;
718 ld->nmax = ld->nmin = 10;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000719 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000720 ld->name = (char*)fn;
721 ld->ppid = 0;
722 ld->match = '+';
723 free(ld->inst); ld->inst = NULL;
724 free(ld->processor); ld->processor = NULL;
725
726 /* read config */
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200727 i = open_read_close("config", buf, sizeof(buf) - 1);
Denis Vlasenkof223efb2007-08-03 10:58:12 +0000728 if (i < 0 && errno != ENOENT)
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000729 bb_perror_msg(WARNING"%s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000730 if (i > 0) {
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200731 buf[i] = '\0';
Denis Vlasenko45946f82007-08-20 17:27:40 +0000732 if (verbose)
733 bb_error_msg(INFO"read: %s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000734 s = buf;
735 while (s) {
736 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000737 if (np)
738 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000739 switch (s[0]) {
740 case '+':
741 case '-':
742 case 'e':
743 case 'E':
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200744 /* Filtering requires one-line buffering,
745 * resetting the "find newline" function
746 * accordingly */
747 memRchr = memchr;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000748 /* Add '\n'-terminated line to ld->inst */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000749 while (1) {
Denys Vlasenko90a99042009-09-06 02:36:23 +0200750 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000751 if (l >= 0 && new)
752 break;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000753 pause_nomem();
754 }
755 free(ld->inst);
756 ld->inst = new;
757 break;
758 case 's': {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000759 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
760 break;
761 }
762 case 'n':
Denys Vlasenko77832482010-08-12 14:14:45 +0200763 ld->nmax = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000764 break;
765 case 'N':
Denys Vlasenko77832482010-08-12 14:14:45 +0200766 ld->nmin = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000767 break;
768 case 't': {
769 static const struct suffix_mult mh_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000770 { "m", 60 },
771 { "h", 60*60 },
772 /*{ "d", 24*60*60 },*/
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200773 { "", 0 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000774 };
Denis Vlasenko45946f82007-08-20 17:27:40 +0000775 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
776 if (ld->rotate_period) {
777 ld->next_rotate = now + ld->rotate_period;
778 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
779 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000780 tmaxflag = 1;
781 }
782 break;
783 }
784 case '!':
785 if (s[1]) {
786 free(ld->processor);
Francis Roundsd2c5de02016-09-14 11:53:51 +0000787 ld->processor = wstrdup(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000788 }
789 break;
790 }
791 s = np;
792 }
793 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
794 s = ld->inst;
795 while (s) {
796 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000797 if (np)
798 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000799 s = np;
800 }
801 }
802
803 /* open current */
804 i = stat("current", &st);
805 if (i != -1) {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000806 if (st.st_size && !(st.st_mode & S_IXUSR)) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000807 ld->fnsave[25] = '.';
808 ld->fnsave[26] = 'u';
809 ld->fnsave[27] = '\0';
810 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000811 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000812 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000813 stat(ld->fnsave, &st);
814 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000815 while (rename("current", ld->fnsave) == -1)
816 pause2cannot("rename current", ld->name);
817 rmoldest(ld);
818 i = -1;
819 } else {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000820 /* st.st_size can be not just bigger, but WIDER!
821 * This code is safe: if st.st_size > 4GB, we select
822 * ld->sizemax (because it's "unsigned") */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000823 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
824 }
825 } else {
826 if (errno != ENOENT) {
827 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000828 warn2("can't stat current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000829 while (fchdir(fdwdir) == -1)
830 pause1cannot("change to initial working directory");
831 return 0;
832 }
833 }
834 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
835 pause2cannot("open current", ld->name);
Denys Vlasenkoa7ccdee2009-11-15 23:28:11 +0100836 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
837 pause2cannot("open current", ld->name); ////
Denis Vlasenko64392902007-02-03 00:53:43 +0000838 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
839
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000840 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000841 while (fchmod(ld->fdcur, 0644) == -1)
842 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000843
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000844 if (verbose) {
845 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
846 else bb_error_msg(INFO"new: %s/current", ld->name);
847 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000848
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000849 while (fchdir(fdwdir) == -1)
850 pause1cannot("change to initial working directory");
851 return 1;
852}
853
854static void logdirs_reopen(void)
855{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000856 int l;
857 int ok = 0;
858
859 tmaxflag = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000860 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000861 logdir_close(&dir[l]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000862 if (logdir_open(&dir[l], fndir[l]))
863 ok = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000864 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000865 if (!ok)
866 fatalx("no functional log directories");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000867}
868
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000869/* Will look good in libbb one day */
870static ssize_t ndelay_read(int fd, void *buf, size_t count)
871{
872 if (!(fl_flag_0 & O_NONBLOCK))
873 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
874 count = safe_read(fd, buf, count);
875 if (!(fl_flag_0 & O_NONBLOCK))
876 fcntl(fd, F_SETFL, fl_flag_0);
877 return count;
878}
879
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000880/* Used for reading stdin */
Denis Vlasenko5d61e712007-09-27 10:09:59 +0000881static int buffer_pread(/*int fd, */char *s, unsigned len)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000882{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000883 unsigned now;
884 struct pollfd input;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000885 int i;
886
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200887 input.fd = STDIN_FILENO;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000888 input.events = POLLIN;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000889
Denis Vlasenkob9528352007-05-06 01:37:21 +0000890 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000891 if (rotateasap) {
892 for (i = 0; i < dirn; ++i)
893 rotate(dir + i);
894 rotateasap = 0;
895 }
896 if (exitasap) {
897 if (linecomplete)
898 return 0;
899 len = 1;
900 }
901 if (reopenasap) {
902 logdirs_reopen();
903 reopenasap = 0;
904 }
905 now = monotonic_sec();
906 nearest_rotate = now + (45 * 60 + 45);
907 for (i = 0; i < dirn; ++i) {
908 if (dir[i].rotate_period) {
909 if (LESS(dir[i].next_rotate, now))
910 rotate(dir + i);
911 if (LESS(dir[i].next_rotate, nearest_rotate))
912 nearest_rotate = dir[i].next_rotate;
913 }
914 }
915
Denis Vlasenko339936b2007-10-05 22:11:06 +0000916 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000917 i = nearest_rotate - now;
918 if (i > 1000000)
919 i = 1000000;
920 if (i <= 0)
921 i = 1;
922 poll(&input, 1, i * 1000);
Denis Vlasenko339936b2007-10-05 22:11:06 +0000923 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000924
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000925 i = ndelay_read(STDIN_FILENO, s, len);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000926 if (i >= 0)
927 break;
928 if (errno == EINTR)
929 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000930 if (errno != EAGAIN) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000931 warn("can't read standard input");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000932 break;
933 }
934 /* else: EAGAIN - normal, repeat silently */
Denis Vlasenkob9528352007-05-06 01:37:21 +0000935 } while (!exitasap);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000936
937 if (i > 0) {
938 int cnt;
939 linecomplete = (s[i-1] == '\n');
Denis Vlasenko45946f82007-08-20 17:27:40 +0000940 if (!repl)
941 return i;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000942
943 cnt = i;
944 while (--cnt >= 0) {
945 char ch = *s;
946 if (ch != '\n') {
947 if (ch < 32 || ch > 126)
948 *s = repl;
949 else {
950 int j;
951 for (j = 0; replace[j]; ++j) {
952 if (ch == replace[j]) {
953 *s = repl;
954 break;
955 }
956 }
957 }
958 }
959 s++;
960 }
961 }
962 return i;
963}
964
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000965static void sig_term_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000966{
967 if (verbose)
968 bb_error_msg(INFO"sig%s received", "term");
969 exitasap = 1;
970}
971
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000972static void sig_child_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000973{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000974 pid_t pid;
975 int l;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000976
977 if (verbose)
978 bb_error_msg(INFO"sig%s received", "child");
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000979 while ((pid = wait_any_nohang(&wstat)) > 0) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000980 for (l = 0; l < dirn; ++l) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000981 if (dir[l].ppid == pid) {
982 dir[l].ppid = 0;
983 processorstop(&dir[l]);
984 break;
985 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000986 }
987 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000988}
989
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000990static void sig_alarm_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000991{
992 if (verbose)
993 bb_error_msg(INFO"sig%s received", "alarm");
994 rotateasap = 1;
995}
996
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000997static void sig_hangup_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000998{
999 if (verbose)
1000 bb_error_msg(INFO"sig%s received", "hangup");
1001 reopenasap = 1;
1002}
1003
1004static void logmatch(struct logdir *ld)
1005{
1006 char *s;
1007
1008 ld->match = '+';
1009 ld->matcherr = 'E';
1010 s = ld->inst;
1011 while (s && s[0]) {
1012 switch (s[0]) {
1013 case '+':
1014 case '-':
1015 if (pmatch(s+1, line, linelen))
1016 ld->match = s[0];
1017 break;
1018 case 'e':
1019 case 'E':
1020 if (pmatch(s+1, line, linelen))
1021 ld->matcherr = s[0];
1022 break;
1023 }
1024 s += strlen(s) + 1;
1025 }
1026}
1027
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001028int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001029int svlogd_main(int argc, char **argv)
1030{
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001031 char *r, *l, *b;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001032 ssize_t stdin_cnt = 0;
1033 int i;
1034 unsigned opt;
1035 unsigned timestamp = 0;
1036
Denis Vlasenko339936b2007-10-05 22:11:06 +00001037 INIT_G();
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001038
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001039 opt = getopt32(argv, "^"
1040 "r:R:l:b:tv" "\0" "tt:vv",
1041 &r, &replace, &l, &b, &timestamp, &verbose
1042 );
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001043 if (opt & 1) { // -r
1044 repl = r[0];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001045 if (!repl || r[1])
1046 bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001047 }
1048 if (opt & 2) if (!repl) repl = '_'; // -R
1049 if (opt & 4) { // -l
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +02001050 linemax = xatou_range(l, 0, COMMON_BUFSIZE-26);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001051 if (linemax == 0)
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +02001052 linemax = COMMON_BUFSIZE-26;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001053 if (linemax < 256)
1054 linemax = 256;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001055 }
Denis Vlasenko64392902007-02-03 00:53:43 +00001056 ////if (opt & 8) { // -b
Denys Vlasenko77832482010-08-12 14:14:45 +02001057 //// buflen = xatoi_positive(b);
Denis Vlasenko64392902007-02-03 00:53:43 +00001058 //// if (buflen == 0) buflen = 1024;
1059 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001060 //if (opt & 0x10) timestamp++; // -t
1061 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001062 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001063 argv += optind;
1064 argc -= optind;
1065
1066 dirn = argc;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001067 if (dirn <= 0)
1068 bb_show_usage();
1069 ////if (buflen <= linemax) bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001070 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +00001071 close_on_exec_on(fdwdir);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001072 dir = xzalloc(dirn * sizeof(dir[0]));
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001073 for (i = 0; i < dirn; ++i) {
1074 dir[i].fddir = -1;
1075 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001076 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenkob9528352007-05-06 01:37:21 +00001077 /*dir[i].ppid = 0;*/
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001078 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001079 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001080 fndir = argv;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001081 /* We cannot set NONBLOCK on fd #0 permanently - this setting
1082 * _isn't_ per-process! It is shared among all other processes
1083 * with the same stdin */
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001084 fl_flag_0 = fcntl(0, F_GETFL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001085
Denis Vlasenko339936b2007-10-05 22:11:06 +00001086 sigemptyset(&blocked_sigset);
1087 sigaddset(&blocked_sigset, SIGTERM);
1088 sigaddset(&blocked_sigset, SIGCHLD);
1089 sigaddset(&blocked_sigset, SIGALRM);
1090 sigaddset(&blocked_sigset, SIGHUP);
1091 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +00001092 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
1093 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
1094 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
1095 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001096
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001097 /* Without timestamps, we don't have to print each line
1098 * separately, so we can look for _last_ newline, not first,
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001099 * thus batching writes. If filtering is enabled in config,
1100 * logdirs_reopen resets it to memchr.
1101 */
1102 memRchr = (timestamp ? memchr : memrchr);
1103
1104 logdirs_reopen();
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001105
Denis Vlasenko64392902007-02-03 00:53:43 +00001106 setvbuf(stderr, NULL, _IOFBF, linelen);
1107
Denis Vlasenko4e1715f2007-01-28 14:51:32 +00001108 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001109 while (1) {
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001110 char stamp[FMT_PTIME];
1111 char *lineptr;
1112 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001113 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001114 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001115 char ch;
1116
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001117 lineptr = line;
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001118 if (timestamp)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001119 lineptr += 26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001120
1121 /* lineptr[0..linemax-1] - buffer for stdin */
1122 /* (possibly has some unprocessed data from prev loop) */
1123
1124 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001125 np = memRchr(lineptr, '\n', stdin_cnt);
1126 if (!np && !exitasap) {
1127 i = linemax - stdin_cnt; /* avail. bytes at tail */
1128 if (i >= 128) {
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001129 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001130 if (i <= 0) /* EOF or error on stdin */
1131 exitasap = 1;
1132 else {
1133 np = memRchr(lineptr + stdin_cnt, '\n', i);
1134 stdin_cnt += i;
1135 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001136 }
1137 }
1138 if (stdin_cnt <= 0 && exitasap)
1139 break;
1140
1141 /* Search for '\n' (in fact, np already holds the result) */
1142 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001143 if (np) {
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02001144 print_to_nl:
1145 /* NB: starting from here lineptr may point
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001146 * farther out into line[] */
1147 linelen = np - lineptr + 1;
1148 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001149 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1150 ch = lineptr[linelen-1];
1151
Denis Vlasenko64392902007-02-03 00:53:43 +00001152 /* Biggest performance hit was coming from the fact
1153 * that we did not buffer writes. We were reading many lines
1154 * in one read() above, but wrote one line per write().
1155 * We are using stdio to fix that */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001156
1157 /* write out lineptr[0..linelen-1] to each log destination
1158 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001159 printlen = linelen;
1160 printptr = lineptr;
1161 if (timestamp) {
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001162 if (timestamp == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +00001163 fmt_time_bernstein_25(stamp);
Denys Vlasenko79c0d732017-05-15 19:12:09 +02001164 else /* 2+: */
1165 fmt_time_human_30nul(stamp, timestamp == 2 ? '_' : 'T');
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001166 printlen += 26;
1167 printptr -= 26;
1168 memcpy(printptr, stamp, 25);
1169 printptr[25] = ' ';
1170 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001171 for (i = 0; i < dirn; ++i) {
1172 struct logdir *ld = &dir[i];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001173 if (ld->fddir == -1)
1174 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001175 if (ld->inst)
1176 logmatch(ld);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001177 if (ld->matcherr == 'e') {
1178 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001179 ////full_write(STDERR_FILENO, printptr, printlen);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001180 fwrite(printptr, 1, printlen, stderr);
1181 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001182 if (ld->match != '+')
1183 continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001184 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001185 }
1186
1187 /* If we didn't see '\n' (long input line), */
1188 /* read/write repeatedly until we see it */
1189 while (ch != '\n') {
1190 /* lineptr is emptied now, safe to use as buffer */
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001191 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001192 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001193 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001194 lineptr[0] = ch = '\n';
1195 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001196 stdin_cnt = 1;
1197 } else {
1198 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001199 np = memRchr(lineptr, '\n', stdin_cnt);
1200 if (np)
1201 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001202 ch = lineptr[linelen-1];
1203 }
1204 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1205 for (i = 0; i < dirn; ++i) {
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001206 if (dir[i].fddir == -1)
1207 continue;
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001208 if (dir[i].matcherr == 'e') {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001209 ////full_write(STDERR_FILENO, lineptr, linelen);
Denis Vlasenko64392902007-02-03 00:53:43 +00001210 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001211 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001212 if (dir[i].match != '+')
1213 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001214 buffer_pwrite(i, lineptr, linelen);
1215 }
1216 }
1217
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001218 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001219 if (stdin_cnt > 0) {
1220 lineptr += linelen;
1221 /* If we see another '\n', we don't need to read
1222 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001223 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001224 if (np)
1225 goto print_to_nl;
1226 /* Move unprocessed data to the front of line */
1227 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1228 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01001229 fflush_all();////
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001230 }
1231
1232 for (i = 0; i < dirn; ++i) {
1233 if (dir[i].ppid)
1234 while (!processorstop(&dir[i]))
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001235 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001236 logdir_close(&dir[i]);
1237 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001238 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001239}