blob: 3ed13b67bfb00ee7d6af7d54678f91c8d409cebc [file] [log] [blame]
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
Denis Vlasenkod18f52b2008-03-02 12:53:15 +000028/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denis Vlasenko83ea6432006-11-16 02:27:24 +000029
Denys Vlasenkoaebb7422009-08-02 00:55:49 +020030/*
31Config files
32
33On startup, and after receiving a HUP signal, svlogd checks for each
34log directory log if the configuration file log/config exists,
35and if so, reads the file line by line and adjusts configuration
36for log as follows:
37
38If the line is empty, or starts with a #, it is ignored. A line
39of the form
40
41ssize
42 sets the maximum file size of current when svlogd should rotate
43 the current log file to size bytes. Default is 1000000.
44 If size is zero, svlogd doesnt rotate log files
45 You should set size to at least (2 * len).
46nnum
47 sets the number of old log files svlogd should maintain to num.
48 If svlogd sees more that num old log files in log after log file
49 rotation, it deletes the oldest one. Default is 10.
50 If num is zero, svlogd doesnt remove old log files.
51Nmin
52 sets the minimum number of old log files svlogd should maintain
53 to min. min must be less than num. If min is set, and svlogd
54 cannot write to current because the filesystem is full,
55 and it sees more than min old log files, it deletes the oldest one.
56ttimeout
57 sets the maximum age of the current log file when svlogd should
58 rotate the current log file to timeout seconds. If current
59 is timeout seconds old, and is not empty, svlogd forces log file rotation.
60!processor
61 tells svlogd to feed each recent log file through processor
62 (see above) on log file rotation. By default log files are not processed.
63ua.b.c.d[:port]
64 tells svlogd to transmit the first len characters of selected
65 log messages to the IP address a.b.c.d, port number port.
66 If port isnt set, the default port for syslog is used (514).
67 len can be set through the -l option, see below. If svlogd
68 has trouble sending udp packets, it writes error messages
69 to the log directory. Attention: logging through udp is unreliable,
70 and should be used in private networks only.
71Ua.b.c.d[:port]
72 is the same as the u line above, but the log messages are no longer
73 written to the log directory, but transmitted through udp only.
74 Error messages from svlogd concerning sending udp packages still go
75 to the log directory.
76pprefix
77 tells svlogd to prefix each line to be written to the log directory,
78 to standard error, or through UDP, with prefix.
79
80If a line starts with a -, +, e, or E, svlogd matches the first len characters
81of each log message against pattern and acts accordingly:
82
83-pattern
84 the log message is deselected.
85+pattern
86 the log message is selected.
87epattern
88 the log message is selected to be printed to standard error.
89Epattern
90 the log message is deselected to be printed to standard error.
91
92Initially each line is selected to be written to log/current. Deselected
93log messages are discarded from log. Initially each line is deselected
94to be written to standard err. Log messages selected for standard error
95are written to standard error.
96
97Pattern Matching
98
99svlogd matches a log message against the string pattern as follows:
100
101pattern is applied to the log message one character by one, starting
102with the first. A character not a star (*) and not a plus (+) matches itself.
103A plus matches the next character in pattern in the log message one
104or more times. A star before the end of pattern matches any string
105in the log message that does not include the next character in pattern.
106A star at the end of pattern matches any string.
107
108Timestamps optionally added by svlogd are not considered part
109of the log message.
110
111An svlogd pattern is not a regular expression. For example consider
112a log message like this
113
1142005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
115
116The following pattern doesnt match
117
118-*pid*
119
120because the first star matches up to the first p in tcpsvd,
121and then the match fails because i is not s. To match this
122log message, you can use a pattern like this instead
123
124-*: *: pid *
125*/
126
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200127//config:config SVLOGD
128//config: bool "svlogd"
129//config: default y
130//config: help
131//config: svlogd continuously reads log data from its standard input, optionally
132//config: filters log messages, and writes the data to one or more automatically
133//config: rotated logs.
134
135//applet:IF_SVLOGD(APPLET(svlogd, BB_DIR_USR_SBIN, BB_SUID_DROP))
136
137//kbuild:lib-$(CONFIG_SVLOGD) += svlogd.o
138
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100139//usage:#define svlogd_trivial_usage
140//usage: "[-ttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..."
141//usage:#define svlogd_full_usage "\n\n"
142//usage: "Continuously read log data from stdin and write to rotated log files in DIRs"
143//usage: "\n"
144//usage: "\n""DIR/config file modifies behavior:"
145//usage: "\n""sSIZE - when to rotate logs"
146//usage: "\n""nNUM - number of files to retain"
147/*usage: "\n""NNUM - min number files to retain" - confusing */
148/*usage: "\n""tSEC - rotate file if it get SEC seconds old" - confusing */
149//usage: "\n""!PROG - process rotated log with PROG"
150/*usage: "\n""uIPADDR - send log over UDP" - unsupported */
151/*usage: "\n""UIPADDR - send log over UDP and DONT log" - unsupported */
152/*usage: "\n""pPFX - prefix each line with PFX" - unsupported */
153//usage: "\n""+,-PATTERN - (de)select line for logging"
154//usage: "\n""E,ePATTERN - (de)select line for stderr"
155
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000156#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000157#include "libbb.h"
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 */
342static void fmt_time_human_30nul(char *s)
343{
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);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000349 sprintf(s, "%04u-%02u-%02u_%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),
353 (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
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001039 opt_complementary = "tt:vv";
Denis Vlasenkofe7cd642007-08-18 15:32:12 +00001040 opt = getopt32(argv, "r:R:l:b:tv",
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001041 &r, &replace, &l, &b, &timestamp, &verbose);
1042 if (opt & 1) { // -r
1043 repl = r[0];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001044 if (!repl || r[1])
1045 bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001046 }
1047 if (opt & 2) if (!repl) repl = '_'; // -R
1048 if (opt & 4) { // -l
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +02001049 linemax = xatou_range(l, 0, COMMON_BUFSIZE-26);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001050 if (linemax == 0)
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +02001051 linemax = COMMON_BUFSIZE-26;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001052 if (linemax < 256)
1053 linemax = 256;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001054 }
Denis Vlasenko64392902007-02-03 00:53:43 +00001055 ////if (opt & 8) { // -b
Denys Vlasenko77832482010-08-12 14:14:45 +02001056 //// buflen = xatoi_positive(b);
Denis Vlasenko64392902007-02-03 00:53:43 +00001057 //// if (buflen == 0) buflen = 1024;
1058 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001059 //if (opt & 0x10) timestamp++; // -t
1060 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001061 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001062 argv += optind;
1063 argc -= optind;
1064
1065 dirn = argc;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001066 if (dirn <= 0)
1067 bb_show_usage();
1068 ////if (buflen <= linemax) bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001069 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +00001070 close_on_exec_on(fdwdir);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001071 dir = xzalloc(dirn * sizeof(dir[0]));
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001072 for (i = 0; i < dirn; ++i) {
1073 dir[i].fddir = -1;
1074 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001075 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenkob9528352007-05-06 01:37:21 +00001076 /*dir[i].ppid = 0;*/
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001077 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001078 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001079 fndir = argv;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001080 /* We cannot set NONBLOCK on fd #0 permanently - this setting
1081 * _isn't_ per-process! It is shared among all other processes
1082 * with the same stdin */
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001083 fl_flag_0 = fcntl(0, F_GETFL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001084
Denis Vlasenko339936b2007-10-05 22:11:06 +00001085 sigemptyset(&blocked_sigset);
1086 sigaddset(&blocked_sigset, SIGTERM);
1087 sigaddset(&blocked_sigset, SIGCHLD);
1088 sigaddset(&blocked_sigset, SIGALRM);
1089 sigaddset(&blocked_sigset, SIGHUP);
1090 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +00001091 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
1092 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
1093 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
1094 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001095
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001096 /* Without timestamps, we don't have to print each line
1097 * separately, so we can look for _last_ newline, not first,
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001098 * thus batching writes. If filtering is enabled in config,
1099 * logdirs_reopen resets it to memchr.
1100 */
1101 memRchr = (timestamp ? memchr : memrchr);
1102
1103 logdirs_reopen();
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001104
Denis Vlasenko64392902007-02-03 00:53:43 +00001105 setvbuf(stderr, NULL, _IOFBF, linelen);
1106
Denis Vlasenko4e1715f2007-01-28 14:51:32 +00001107 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001108 while (1) {
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001109 char stamp[FMT_PTIME];
1110 char *lineptr;
1111 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001112 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001113 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001114 char ch;
1115
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001116 lineptr = line;
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001117 if (timestamp)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001118 lineptr += 26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001119
1120 /* lineptr[0..linemax-1] - buffer for stdin */
1121 /* (possibly has some unprocessed data from prev loop) */
1122
1123 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001124 np = memRchr(lineptr, '\n', stdin_cnt);
1125 if (!np && !exitasap) {
1126 i = linemax - stdin_cnt; /* avail. bytes at tail */
1127 if (i >= 128) {
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001128 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001129 if (i <= 0) /* EOF or error on stdin */
1130 exitasap = 1;
1131 else {
1132 np = memRchr(lineptr + stdin_cnt, '\n', i);
1133 stdin_cnt += i;
1134 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001135 }
1136 }
1137 if (stdin_cnt <= 0 && exitasap)
1138 break;
1139
1140 /* Search for '\n' (in fact, np already holds the result) */
1141 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001142 if (np) {
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02001143 print_to_nl:
1144 /* NB: starting from here lineptr may point
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001145 * farther out into line[] */
1146 linelen = np - lineptr + 1;
1147 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001148 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1149 ch = lineptr[linelen-1];
1150
Denis Vlasenko64392902007-02-03 00:53:43 +00001151 /* Biggest performance hit was coming from the fact
1152 * that we did not buffer writes. We were reading many lines
1153 * in one read() above, but wrote one line per write().
1154 * We are using stdio to fix that */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001155
1156 /* write out lineptr[0..linelen-1] to each log destination
1157 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001158 printlen = linelen;
1159 printptr = lineptr;
1160 if (timestamp) {
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001161 if (timestamp == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +00001162 fmt_time_bernstein_25(stamp);
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001163 else /* 2: */
Denis Vlasenko45946f82007-08-20 17:27:40 +00001164 fmt_time_human_30nul(stamp);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001165 printlen += 26;
1166 printptr -= 26;
1167 memcpy(printptr, stamp, 25);
1168 printptr[25] = ' ';
1169 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001170 for (i = 0; i < dirn; ++i) {
1171 struct logdir *ld = &dir[i];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001172 if (ld->fddir == -1)
1173 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001174 if (ld->inst)
1175 logmatch(ld);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001176 if (ld->matcherr == 'e') {
1177 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001178 ////full_write(STDERR_FILENO, printptr, printlen);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001179 fwrite(printptr, 1, printlen, stderr);
1180 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001181 if (ld->match != '+')
1182 continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001183 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001184 }
1185
1186 /* If we didn't see '\n' (long input line), */
1187 /* read/write repeatedly until we see it */
1188 while (ch != '\n') {
1189 /* lineptr is emptied now, safe to use as buffer */
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001190 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001191 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001192 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001193 lineptr[0] = ch = '\n';
1194 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001195 stdin_cnt = 1;
1196 } else {
1197 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001198 np = memRchr(lineptr, '\n', stdin_cnt);
1199 if (np)
1200 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001201 ch = lineptr[linelen-1];
1202 }
1203 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1204 for (i = 0; i < dirn; ++i) {
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001205 if (dir[i].fddir == -1)
1206 continue;
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001207 if (dir[i].matcherr == 'e') {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001208 ////full_write(STDERR_FILENO, lineptr, linelen);
Denis Vlasenko64392902007-02-03 00:53:43 +00001209 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001210 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001211 if (dir[i].match != '+')
1212 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001213 buffer_pwrite(i, lineptr, linelen);
1214 }
1215 }
1216
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001217 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001218 if (stdin_cnt > 0) {
1219 lineptr += linelen;
1220 /* If we see another '\n', we don't need to read
1221 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001222 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001223 if (np)
1224 goto print_to_nl;
1225 /* Move unprocessed data to the front of line */
1226 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1227 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01001228 fflush_all();////
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001229 }
1230
1231 for (i = 0; i < dirn; ++i) {
1232 if (dir[i].ppid)
1233 while (!processorstop(&dir[i]))
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001234 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001235 logdir_close(&dir[i]);
1236 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001237 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001238}