blob: 8dbf671064d585084515fe9bf8d597df912d8277 [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
Denys Vlasenko4eed2c62017-07-18 22:01:24 +0200128//config: bool "svlogd (15 kb)"
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200129//config: default y
130//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200131//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.
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200134
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
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200140//usage: "[-tttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..."
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100141//usage:#define svlogd_full_usage "\n\n"
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200142//usage: "Read log data from stdin and write to rotated log files in DIRs"
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100143//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"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200158#include "common_bufsiz.h"
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000159#include "runit_lib.h"
160
Denis Vlasenko45946f82007-08-20 17:27:40 +0000161#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
162
163#define FMT_PTIME 30
164
Denis Vlasenko339936b2007-10-05 22:11:06 +0000165struct logdir {
Denis Vlasenko64392902007-02-03 00:53:43 +0000166 ////char *btmp;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000167 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
168 char *inst;
169 char *processor;
170 char *name;
171 unsigned size;
172 unsigned sizemax;
173 unsigned nmax;
174 unsigned nmin;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000175 unsigned rotate_period;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000176 int ppid;
177 int fddir;
178 int fdcur;
Denis Vlasenko64392902007-02-03 00:53:43 +0000179 FILE* filecur; ////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000180 int fdlock;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000181 unsigned next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000182 char fnsave[FMT_PTIME];
183 char match;
184 char matcherr;
Denis Vlasenko339936b2007-10-05 22:11:06 +0000185};
186
187
188struct globals {
189 struct logdir *dir;
190 unsigned verbose;
191 int linemax;
192 ////int buflen;
193 int linelen;
194
195 int fdwdir;
196 char **fndir;
197 int wstat;
198 unsigned nearest_rotate;
199
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200200 void* (*memRchr)(const void *, int, size_t);
Denys Vlasenko681efe22011-03-08 21:00:36 +0100201 char *shell;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200202
Denis Vlasenko339936b2007-10-05 22:11:06 +0000203 smallint exitasap;
204 smallint rotateasap;
205 smallint reopenasap;
206 smallint linecomplete;
207 smallint tmaxflag;
208
209 char repl;
210 const char *replace;
211 int fl_flag_0;
212 unsigned dirn;
213
214 sigset_t blocked_sigset;
215};
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100216#define G (*ptr_to_globals)
Denis Vlasenko339936b2007-10-05 22:11:06 +0000217#define dir (G.dir )
218#define verbose (G.verbose )
219#define linemax (G.linemax )
220#define buflen (G.buflen )
221#define linelen (G.linelen )
222#define fndir (G.fndir )
223#define fdwdir (G.fdwdir )
224#define wstat (G.wstat )
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200225#define memRchr (G.memRchr )
Denis Vlasenko339936b2007-10-05 22:11:06 +0000226#define nearest_rotate (G.nearest_rotate)
227#define exitasap (G.exitasap )
228#define rotateasap (G.rotateasap )
229#define reopenasap (G.reopenasap )
230#define linecomplete (G.linecomplete )
231#define tmaxflag (G.tmaxflag )
232#define repl (G.repl )
233#define replace (G.replace )
234#define blocked_sigset (G.blocked_sigset)
235#define fl_flag_0 (G.fl_flag_0 )
236#define dirn (G.dirn )
Denys Vlasenko9de2e5a2016-04-21 18:38:51 +0200237#define line bb_common_bufsiz1
Denis Vlasenko339936b2007-10-05 22:11:06 +0000238#define INIT_G() do { \
Denys Vlasenko9de2e5a2016-04-21 18:38:51 +0200239 setup_common_bufsiz(); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000240 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denis Vlasenko339936b2007-10-05 22:11:06 +0000241 linemax = 1000; \
242 /*buflen = 1024;*/ \
243 linecomplete = 1; \
244 replace = ""; \
245} while (0)
246
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000247
248#define FATAL "fatal: "
249#define WARNING "warning: "
250#define PAUSE "pausing: "
251#define INFO "info: "
252
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000253static void fatalx(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000254{
255 bb_error_msg_and_die(FATAL"%s", m0);
256}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000257static void warn(const char *m0)
258{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000259 bb_perror_msg(WARNING"%s", m0);
260}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000261static void warn2(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000262{
263 bb_perror_msg(WARNING"%s: %s", m0, m1);
264}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000265static void warnx(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000266{
267 bb_error_msg(WARNING"%s: %s", m0, m1);
268}
269static void pause_nomem(void)
270{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000271 bb_error_msg(PAUSE"out of memory");
272 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000273}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000274static void pause1cannot(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000275{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000276 bb_perror_msg(PAUSE"can't %s", m0);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000277 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000278}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000279static void pause2cannot(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000280{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000281 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000282 sleep(3);
283}
284
285static char* wstrdup(const char *str)
286{
287 char *s;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000288 while (!(s = strdup(str)))
289 pause_nomem();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000290 return s;
291}
292
Denys Vlasenko05e86052010-10-13 12:53:27 +0200293static unsigned pmatch(const char *p, const char *s, unsigned len)
294{
295 for (;;) {
296 char c = *p++;
297 if (!c) return !len;
298 switch (c) {
299 case '*':
300 c = *p;
301 if (!c) return 1;
302 for (;;) {
303 if (!len) return 0;
304 if (*s == c) break;
305 ++s;
306 --len;
307 }
308 continue;
309 case '+':
310 c = *p++;
311 if (c != *s) return 0;
312 for (;;) {
313 if (!len) return 1;
314 if (*s != c) break;
315 ++s;
316 --len;
317 }
318 continue;
319 /*
320 case '?':
321 if (*p == '?') {
322 if (*s != '?') return 0;
323 ++p;
324 }
325 ++s; --len;
326 continue;
327 */
328 default:
329 if (!len) return 0;
330 if (*s != c) return 0;
331 ++s;
332 --len;
333 continue;
334 }
335 }
336 return 0;
337}
338
Denis Vlasenko45946f82007-08-20 17:27:40 +0000339/*** ex fmt_ptime.[ch] ***/
340
341/* NUL terminated */
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200342static void fmt_time_human_30nul(char *s, char dt_delim)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000343{
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100344 struct tm *ptm;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000345 struct timeval tv;
346
347 gettimeofday(&tv, NULL);
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100348 ptm = gmtime(&tv.tv_sec);
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200349 sprintf(s, "%04u-%02u-%02u%c%02u:%02u:%02u.%06u000",
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100350 (unsigned)(1900 + ptm->tm_year),
351 (unsigned)(ptm->tm_mon + 1),
352 (unsigned)(ptm->tm_mday),
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200353 dt_delim,
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100354 (unsigned)(ptm->tm_hour),
355 (unsigned)(ptm->tm_min),
356 (unsigned)(ptm->tm_sec),
Denis Vlasenko45946f82007-08-20 17:27:40 +0000357 (unsigned)(tv.tv_usec)
358 );
359 /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
360 /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
361 /* 20 (up to '.' inclusive) + 9 (not including '\0') */
362}
363
364/* NOT terminated! */
365static void fmt_time_bernstein_25(char *s)
366{
367 uint32_t pack[3];
368 struct timeval tv;
369 unsigned sec_hi;
370
371 gettimeofday(&tv, NULL);
372 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
373 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
374 tv.tv_usec *= 1000;
375 /* Network order is big-endian: most significant byte first.
376 * This is exactly what we want here */
377 pack[0] = htonl(sec_hi);
378 pack[1] = htonl(tv.tv_sec);
379 pack[2] = htonl(tv.tv_usec);
380 *s++ = '@';
381 bin2hex(s, (char*)pack, 12);
382}
383
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000384static void processorstart(struct logdir *ld)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000385{
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000386 char sv_ch;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000387 int pid;
388
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000389 if (!ld->processor) return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000390 if (ld->ppid) {
391 warnx("processor already running", ld->name);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000392 return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000393 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000394
395 /* vfork'ed child trashes this byte, save... */
396 sv_ch = ld->fnsave[26];
397
Denys Vlasenko681efe22011-03-08 21:00:36 +0100398 if (!G.shell)
399 G.shell = xstrdup(get_shell_name());
400
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000401 while ((pid = vfork()) == -1)
402 pause2cannot("vfork for processor", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000403 if (!pid) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000404 int fd;
405
406 /* child */
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000407 /* Non-ignored signals revert to SIG_DFL on exec anyway */
408 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000409 + (1 << SIGTERM)
410 + (1 << SIGALRM)
411 + (1 << SIGHUP)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000412 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000413 sig_unblock(SIGTERM);
414 sig_unblock(SIGALRM);
415 sig_unblock(SIGHUP);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000416
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000417 if (verbose)
418 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
419 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000420 xmove_fd(fd, 0);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000421 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000422 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000423 xmove_fd(fd, 1);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200424 fd = open("state", O_RDONLY|O_NDELAY);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000425 if (fd == -1) {
426 if (errno != ENOENT)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000427 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000428 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000429 fd = xopen("state", O_RDONLY|O_NDELAY);
430 }
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000431 xmove_fd(fd, 4);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000432 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000433 xmove_fd(fd, 5);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000434
Denys Vlasenko681efe22011-03-08 21:00:36 +0100435 execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000436 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000437 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000438 ld->fnsave[26] = sv_ch; /* ...restore */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000439 ld->ppid = pid;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000440}
441
442static unsigned processorstop(struct logdir *ld)
443{
444 char f[28];
445
446 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000447 sig_unblock(SIGHUP);
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000448 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000449 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000450 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000451 ld->ppid = 0;
452 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200453 if (ld->fddir == -1)
454 return 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000455 while (fchdir(ld->fddir) == -1)
456 pause2cannot("change directory, want processor", ld->name);
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200457 if (WEXITSTATUS(wstat) != 0) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000458 warnx("processor failed, restart", ld->name);
459 ld->fnsave[26] = 't';
460 unlink(ld->fnsave);
461 ld->fnsave[26] = 'u';
462 processorstart(ld);
463 while (fchdir(fdwdir) == -1)
464 pause1cannot("change to initial working directory");
465 return ld->processor ? 0 : 1;
466 }
467 ld->fnsave[26] = 't';
468 memcpy(f, ld->fnsave, 26);
469 f[26] = 's';
470 f[27] = '\0';
471 while (rename(ld->fnsave, f) == -1)
472 pause2cannot("rename processed", ld->name);
473 while (chmod(f, 0744) == -1)
474 pause2cannot("set mode of processed", ld->name);
475 ld->fnsave[26] = 'u';
476 if (unlink(ld->fnsave) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000477 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000478 while (rename("newstate", "state") == -1)
479 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000480 if (verbose)
481 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000482 while (fchdir(fdwdir) == -1)
483 pause1cannot("change to initial working directory");
484 return 1;
485}
486
487static void rmoldest(struct logdir *ld)
488{
489 DIR *d;
490 struct dirent *f;
491 char oldest[FMT_PTIME];
492 int n = 0;
493
494 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
495 while (!(d = opendir(".")))
496 pause2cannot("open directory, want rotate", ld->name);
497 errno = 0;
498 while ((f = readdir(d))) {
499 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
500 if (f->d_name[26] == 't') {
501 if (unlink(f->d_name) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000502 warn2("can't unlink processor leftover", f->d_name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000503 } else {
504 ++n;
505 if (strcmp(f->d_name, oldest) < 0)
506 memcpy(oldest, f->d_name, 27);
507 }
508 errno = 0;
509 }
510 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000511 if (errno)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000512 warn2("can't read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000513 closedir(d);
514
515 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000516 if (verbose)
517 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000518 if ((*oldest == '@') && (unlink(oldest) == -1))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000519 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000520 }
521}
522
523static unsigned rotate(struct logdir *ld)
524{
525 struct stat st;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000526 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000527
528 if (ld->fddir == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000529 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000530 return 0;
531 }
532 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000533 while (!processorstop(ld))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000534 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000535
536 while (fchdir(ld->fddir) == -1)
537 pause2cannot("change directory, want rotate", ld->name);
538
539 /* create new filename */
540 ld->fnsave[25] = '.';
541 ld->fnsave[26] = 's';
542 if (ld->processor)
543 ld->fnsave[26] = 'u';
544 ld->fnsave[27] = '\0';
545 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000546 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000547 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000548 stat(ld->fnsave, &st);
549 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000550
Denis Vlasenko45946f82007-08-20 17:27:40 +0000551 now = monotonic_sec();
552 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
553 ld->next_rotate = now + ld->rotate_period;
554 if (LESS(ld->next_rotate, nearest_rotate))
555 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000556 }
557
558 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000559 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000560 pause2cannot("fsync current logfile", ld->name);
561 while (fchmod(ld->fdcur, 0744) == -1)
562 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000563 ////close(ld->fdcur);
564 fclose(ld->filecur);
565
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000566 if (verbose) {
567 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
568 ld->fnsave, ld->size);
569 }
570 while (rename("current", ld->fnsave) == -1)
571 pause2cannot("rename current", ld->name);
572 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
573 pause2cannot("create new current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200574 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
575 pause2cannot("create new current", ld->name); /* very unlikely */
Denis Vlasenko64392902007-02-03 00:53:43 +0000576 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000577 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000578 ld->size = 0;
579 while (fchmod(ld->fdcur, 0644) == -1)
580 pause2cannot("set mode of current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200581
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000582 rmoldest(ld);
583 processorstart(ld);
584 }
585
586 while (fchdir(fdwdir) == -1)
587 pause1cannot("change to initial working directory");
588 return 1;
589}
590
591static int buffer_pwrite(int n, char *s, unsigned len)
592{
593 int i;
594 struct logdir *ld = &dir[n];
595
596 if (ld->sizemax) {
597 if (ld->size >= ld->sizemax)
598 rotate(ld);
599 if (len > (ld->sizemax - ld->size))
600 len = ld->sizemax - ld->size;
601 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000602 while (1) {
603 ////i = full_write(ld->fdcur, s, len);
604 ////if (i != -1) break;
605 i = fwrite(s, 1, len, ld->filecur);
606 if (i == len) break;
607
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000608 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
609 DIR *d;
610 struct dirent *f;
611 char oldest[FMT_PTIME];
612 int j = 0;
613
614 while (fchdir(ld->fddir) == -1)
615 pause2cannot("change directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100616 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000617 oldest[0] = 'A';
618 oldest[1] = oldest[27] = '\0';
619 while (!(d = opendir(".")))
620 pause2cannot("open directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100621 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000622 errno = 0;
623 while ((f = readdir(d)))
624 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
625 ++j;
626 if (strcmp(f->d_name, oldest) < 0)
627 memcpy(oldest, f->d_name, 27);
628 }
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000629 if (errno) warn2("can't read directory, want remove old logfile",
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000630 ld->name);
631 closedir(d);
632 errno = ENOSPC;
633 if (j > ld->nmin) {
634 if (*oldest == '@') {
635 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
636 ld->name, oldest);
637 errno = 0;
638 if (unlink(oldest) == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000639 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000640 errno = ENOSPC;
641 }
642 while (fchdir(fdwdir) == -1)
643 pause1cannot("change to initial working directory");
644 }
645 }
646 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000647 if (errno)
648 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000649 }
650
651 ld->size += i;
652 if (ld->sizemax)
653 if (s[i-1] == '\n')
654 if (ld->size >= (ld->sizemax - linemax))
655 rotate(ld);
656 return i;
657}
658
659static void logdir_close(struct logdir *ld)
660{
661 if (ld->fddir == -1)
662 return;
663 if (verbose)
664 bb_error_msg(INFO"close: %s", ld->name);
665 close(ld->fddir);
666 ld->fddir = -1;
667 if (ld->fdcur == -1)
668 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000669 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000670 pause2cannot("fsync current logfile", ld->name);
671 while (fchmod(ld->fdcur, 0744) == -1)
672 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000673 ////close(ld->fdcur);
674 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000675 ld->fdcur = -1;
676 if (ld->fdlock == -1)
677 return; /* impossible */
678 close(ld->fdlock);
679 ld->fdlock = -1;
680 free(ld->processor);
681 ld->processor = NULL;
682}
683
Denys Vlasenkoa7bb3c12009-10-08 12:28:08 +0200684static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000685{
686 char buf[128];
Denis Vlasenko45946f82007-08-20 17:27:40 +0000687 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000688 char *new, *s, *np;
689 int i;
690 struct stat st;
691
Denis Vlasenko45946f82007-08-20 17:27:40 +0000692 now = monotonic_sec();
693
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000694 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
695 if (ld->fddir == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000696 warn2("can't open log directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000697 return 0;
698 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000699 close_on_exec_on(ld->fddir);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000700 if (fchdir(ld->fddir) == -1) {
701 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000702 warn2("can't change directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000703 return 0;
704 }
705 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
706 if ((ld->fdlock == -1)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200707 || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000708 ) {
709 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000710 warn2("can't lock directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000711 while (fchdir(fdwdir) == -1)
712 pause1cannot("change to initial working directory");
713 return 0;
714 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000715 close_on_exec_on(ld->fdlock);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000716
717 ld->size = 0;
718 ld->sizemax = 1000000;
719 ld->nmax = ld->nmin = 10;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000720 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000721 ld->name = (char*)fn;
722 ld->ppid = 0;
723 ld->match = '+';
724 free(ld->inst); ld->inst = NULL;
725 free(ld->processor); ld->processor = NULL;
726
727 /* read config */
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200728 i = open_read_close("config", buf, sizeof(buf) - 1);
Denis Vlasenkof223efb2007-08-03 10:58:12 +0000729 if (i < 0 && errno != ENOENT)
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000730 bb_perror_msg(WARNING"%s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000731 if (i > 0) {
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200732 buf[i] = '\0';
Denis Vlasenko45946f82007-08-20 17:27:40 +0000733 if (verbose)
734 bb_error_msg(INFO"read: %s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000735 s = buf;
736 while (s) {
737 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000738 if (np)
739 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000740 switch (s[0]) {
741 case '+':
742 case '-':
743 case 'e':
744 case 'E':
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200745 /* Filtering requires one-line buffering,
746 * resetting the "find newline" function
747 * accordingly */
748 memRchr = memchr;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000749 /* Add '\n'-terminated line to ld->inst */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000750 while (1) {
Denys Vlasenko90a99042009-09-06 02:36:23 +0200751 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000752 if (l >= 0 && new)
753 break;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000754 pause_nomem();
755 }
756 free(ld->inst);
757 ld->inst = new;
758 break;
759 case 's': {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000760 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
761 break;
762 }
763 case 'n':
Denys Vlasenko77832482010-08-12 14:14:45 +0200764 ld->nmax = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000765 break;
766 case 'N':
Denys Vlasenko77832482010-08-12 14:14:45 +0200767 ld->nmin = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000768 break;
769 case 't': {
770 static const struct suffix_mult mh_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000771 { "m", 60 },
772 { "h", 60*60 },
773 /*{ "d", 24*60*60 },*/
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200774 { "", 0 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000775 };
Denis Vlasenko45946f82007-08-20 17:27:40 +0000776 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
777 if (ld->rotate_period) {
778 ld->next_rotate = now + ld->rotate_period;
779 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
780 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000781 tmaxflag = 1;
782 }
783 break;
784 }
785 case '!':
786 if (s[1]) {
787 free(ld->processor);
Francis Roundsd2c5de02016-09-14 11:53:51 +0000788 ld->processor = wstrdup(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000789 }
790 break;
791 }
792 s = np;
793 }
794 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
795 s = ld->inst;
796 while (s) {
797 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000798 if (np)
799 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000800 s = np;
801 }
802 }
803
804 /* open current */
805 i = stat("current", &st);
806 if (i != -1) {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000807 if (st.st_size && !(st.st_mode & S_IXUSR)) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000808 ld->fnsave[25] = '.';
809 ld->fnsave[26] = 'u';
810 ld->fnsave[27] = '\0';
811 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000812 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000813 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000814 stat(ld->fnsave, &st);
815 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000816 while (rename("current", ld->fnsave) == -1)
817 pause2cannot("rename current", ld->name);
818 rmoldest(ld);
819 i = -1;
820 } else {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000821 /* st.st_size can be not just bigger, but WIDER!
822 * This code is safe: if st.st_size > 4GB, we select
823 * ld->sizemax (because it's "unsigned") */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000824 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
825 }
826 } else {
827 if (errno != ENOENT) {
828 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000829 warn2("can't stat current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000830 while (fchdir(fdwdir) == -1)
831 pause1cannot("change to initial working directory");
832 return 0;
833 }
834 }
835 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
836 pause2cannot("open current", ld->name);
Denys Vlasenkoa7ccdee2009-11-15 23:28:11 +0100837 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
838 pause2cannot("open current", ld->name); ////
Denis Vlasenko64392902007-02-03 00:53:43 +0000839 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
840
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000841 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000842 while (fchmod(ld->fdcur, 0644) == -1)
843 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000844
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000845 if (verbose) {
846 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
847 else bb_error_msg(INFO"new: %s/current", ld->name);
848 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000849
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000850 while (fchdir(fdwdir) == -1)
851 pause1cannot("change to initial working directory");
852 return 1;
853}
854
855static void logdirs_reopen(void)
856{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000857 int l;
858 int ok = 0;
859
860 tmaxflag = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000861 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000862 logdir_close(&dir[l]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000863 if (logdir_open(&dir[l], fndir[l]))
864 ok = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000865 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000866 if (!ok)
867 fatalx("no functional log directories");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000868}
869
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000870/* Will look good in libbb one day */
871static ssize_t ndelay_read(int fd, void *buf, size_t count)
872{
873 if (!(fl_flag_0 & O_NONBLOCK))
874 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
875 count = safe_read(fd, buf, count);
876 if (!(fl_flag_0 & O_NONBLOCK))
877 fcntl(fd, F_SETFL, fl_flag_0);
878 return count;
879}
880
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000881/* Used for reading stdin */
Denis Vlasenko5d61e712007-09-27 10:09:59 +0000882static int buffer_pread(/*int fd, */char *s, unsigned len)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000883{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000884 unsigned now;
885 struct pollfd input;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000886 int i;
887
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200888 input.fd = STDIN_FILENO;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000889 input.events = POLLIN;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000890
Denis Vlasenkob9528352007-05-06 01:37:21 +0000891 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000892 if (rotateasap) {
893 for (i = 0; i < dirn; ++i)
894 rotate(dir + i);
895 rotateasap = 0;
896 }
897 if (exitasap) {
898 if (linecomplete)
899 return 0;
900 len = 1;
901 }
902 if (reopenasap) {
903 logdirs_reopen();
904 reopenasap = 0;
905 }
906 now = monotonic_sec();
907 nearest_rotate = now + (45 * 60 + 45);
908 for (i = 0; i < dirn; ++i) {
909 if (dir[i].rotate_period) {
910 if (LESS(dir[i].next_rotate, now))
911 rotate(dir + i);
912 if (LESS(dir[i].next_rotate, nearest_rotate))
913 nearest_rotate = dir[i].next_rotate;
914 }
915 }
916
Denis Vlasenko339936b2007-10-05 22:11:06 +0000917 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000918 i = nearest_rotate - now;
919 if (i > 1000000)
920 i = 1000000;
921 if (i <= 0)
922 i = 1;
923 poll(&input, 1, i * 1000);
Denis Vlasenko339936b2007-10-05 22:11:06 +0000924 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000925
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000926 i = ndelay_read(STDIN_FILENO, s, len);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000927 if (i >= 0)
928 break;
929 if (errno == EINTR)
930 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000931 if (errno != EAGAIN) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000932 warn("can't read standard input");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000933 break;
934 }
935 /* else: EAGAIN - normal, repeat silently */
Denis Vlasenkob9528352007-05-06 01:37:21 +0000936 } while (!exitasap);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000937
938 if (i > 0) {
939 int cnt;
940 linecomplete = (s[i-1] == '\n');
Denis Vlasenko45946f82007-08-20 17:27:40 +0000941 if (!repl)
942 return i;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000943
944 cnt = i;
945 while (--cnt >= 0) {
946 char ch = *s;
947 if (ch != '\n') {
948 if (ch < 32 || ch > 126)
949 *s = repl;
950 else {
951 int j;
952 for (j = 0; replace[j]; ++j) {
953 if (ch == replace[j]) {
954 *s = repl;
955 break;
956 }
957 }
958 }
959 }
960 s++;
961 }
962 }
963 return i;
964}
965
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000966static void sig_term_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000967{
968 if (verbose)
969 bb_error_msg(INFO"sig%s received", "term");
970 exitasap = 1;
971}
972
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000973static void sig_child_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000974{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000975 pid_t pid;
976 int l;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000977
978 if (verbose)
979 bb_error_msg(INFO"sig%s received", "child");
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000980 while ((pid = wait_any_nohang(&wstat)) > 0) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000981 for (l = 0; l < dirn; ++l) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000982 if (dir[l].ppid == pid) {
983 dir[l].ppid = 0;
984 processorstop(&dir[l]);
985 break;
986 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000987 }
988 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000989}
990
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000991static void sig_alarm_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000992{
993 if (verbose)
994 bb_error_msg(INFO"sig%s received", "alarm");
995 rotateasap = 1;
996}
997
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000998static void sig_hangup_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000999{
1000 if (verbose)
1001 bb_error_msg(INFO"sig%s received", "hangup");
1002 reopenasap = 1;
1003}
1004
1005static void logmatch(struct logdir *ld)
1006{
1007 char *s;
1008
1009 ld->match = '+';
1010 ld->matcherr = 'E';
1011 s = ld->inst;
1012 while (s && s[0]) {
1013 switch (s[0]) {
1014 case '+':
1015 case '-':
1016 if (pmatch(s+1, line, linelen))
1017 ld->match = s[0];
1018 break;
1019 case 'e':
1020 case 'E':
1021 if (pmatch(s+1, line, linelen))
1022 ld->matcherr = s[0];
1023 break;
1024 }
1025 s += strlen(s) + 1;
1026 }
1027}
1028
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001029int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001030int svlogd_main(int argc, char **argv)
1031{
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001032 char *r, *l, *b;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001033 ssize_t stdin_cnt = 0;
1034 int i;
1035 unsigned opt;
1036 unsigned timestamp = 0;
1037
Denis Vlasenko339936b2007-10-05 22:11:06 +00001038 INIT_G();
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001039
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001040 opt_complementary = "tt:vv";
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001041 opt = getopt32(argv, "r:R:l:b:tv",
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001042 &r, &replace, &l, &b, &timestamp, &verbose);
1043 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}