blob: b0690d794d201c1417c3f4b7415a5546cbb10137 [file] [log] [blame]
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
Denys Vlasenko95f79532017-08-02 14:26:33 +020016THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED
Denis Vlasenko83ea6432006-11-16 02:27:24 +000017WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
Denis Vlasenkod18f52b2008-03-02 12:53:15 +000028/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denis Vlasenko83ea6432006-11-16 02:27:24 +000029
Denys Vlasenkoaebb7422009-08-02 00:55:49 +020030/*
31Config files
32
33On startup, and after receiving a HUP signal, svlogd checks for each
34log directory log if the configuration file log/config exists,
35and if so, reads the file line by line and adjusts configuration
36for log as follows:
37
38If the line is empty, or starts with a #, it is ignored. A line
39of the form
40
41ssize
42 sets the maximum file size of current when svlogd should rotate
43 the current log file to size bytes. Default is 1000000.
44 If size is zero, svlogd doesnt rotate log files
45 You should set size to at least (2 * len).
46nnum
47 sets the number of old log files svlogd should maintain to num.
48 If svlogd sees more that num old log files in log after log file
49 rotation, it deletes the oldest one. Default is 10.
50 If num is zero, svlogd doesnt remove old log files.
51Nmin
52 sets the minimum number of old log files svlogd should maintain
53 to min. min must be less than num. If min is set, and svlogd
54 cannot write to current because the filesystem is full,
55 and it sees more than min old log files, it deletes the oldest one.
56ttimeout
57 sets the maximum age of the current log file when svlogd should
58 rotate the current log file to timeout seconds. If current
59 is timeout seconds old, and is not empty, svlogd forces log file rotation.
60!processor
61 tells svlogd to feed each recent log file through processor
62 (see above) on log file rotation. By default log files are not processed.
63ua.b.c.d[:port]
64 tells svlogd to transmit the first len characters of selected
65 log messages to the IP address a.b.c.d, port number port.
66 If port isnt set, the default port for syslog is used (514).
67 len can be set through the -l option, see below. If svlogd
68 has trouble sending udp packets, it writes error messages
69 to the log directory. Attention: logging through udp is unreliable,
70 and should be used in private networks only.
71Ua.b.c.d[:port]
72 is the same as the u line above, but the log messages are no longer
73 written to the log directory, but transmitted through udp only.
74 Error messages from svlogd concerning sending udp packages still go
75 to the log directory.
76pprefix
77 tells svlogd to prefix each line to be written to the log directory,
78 to standard error, or through UDP, with prefix.
79
80If a line starts with a -, +, e, or E, svlogd matches the first len characters
81of each log message against pattern and acts accordingly:
82
83-pattern
84 the log message is deselected.
85+pattern
86 the log message is selected.
87epattern
88 the log message is selected to be printed to standard error.
89Epattern
90 the log message is deselected to be printed to standard error.
91
92Initially each line is selected to be written to log/current. Deselected
93log messages are discarded from log. Initially each line is deselected
94to be written to standard err. Log messages selected for standard error
95are written to standard error.
96
97Pattern Matching
98
99svlogd matches a log message against the string pattern as follows:
100
101pattern is applied to the log message one character by one, starting
102with the first. A character not a star (*) and not a plus (+) matches itself.
103A plus matches the next character in pattern in the log message one
104or more times. A star before the end of pattern matches any string
105in the log message that does not include the next character in pattern.
106A star at the end of pattern matches any string.
107
108Timestamps optionally added by svlogd are not considered part
109of the log message.
110
111An svlogd pattern is not a regular expression. For example consider
112a log message like this
113
1142005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
115
116The following pattern doesnt match
117
118-*pid*
119
120because the first star matches up to the first p in tcpsvd,
121and then the match fails because i is not s. To match this
122log message, you can use a pattern like this instead
123
124-*: *: pid *
125*/
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200126//config:config SVLOGD
Denys Vlasenko4eed2c62017-07-18 22:01:24 +0200127//config: bool "svlogd (15 kb)"
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200128//config: default y
129//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200130//config: svlogd continuously reads log data from its standard input, optionally
131//config: filters log messages, and writes the data to one or more automatically
132//config: rotated logs.
Denys Vlasenko0863e1a2015-10-19 00:41:28 +0200133
134//applet:IF_SVLOGD(APPLET(svlogd, BB_DIR_USR_SBIN, BB_SUID_DROP))
135
136//kbuild:lib-$(CONFIG_SVLOGD) += svlogd.o
137
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100138//usage:#define svlogd_trivial_usage
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200139//usage: "[-tttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..."
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100140//usage:#define svlogd_full_usage "\n\n"
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200141//usage: "Read log data from stdin and write to rotated log files in DIRs"
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100142//usage: "\n"
Denys Vlasenko3f52d132018-02-09 20:21:01 +0100143//usage: "\n""-r C Replace non-printable characters with C"
144//usage: "\n""-R CHARS Also replace CHARS with C (default _)"
145//usage: "\n""-t Timestamp with @tai64n"
146//usage: "\n""-tt Timestamp with yyyy-mm-dd_hh:mm:ss.sssss"
147//usage: "\n""-ttt Timestamp with yyyy-mm-ddThh:mm:ss.sssss"
148//usage: "\n""-v Verbose"
149//usage: "\n"
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100150//usage: "\n""DIR/config file modifies behavior:"
Denys Vlasenko3f52d132018-02-09 20:21:01 +0100151//usage: "\n""sSIZE - when to rotate logs (default 1000000, 0 disables)"
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100152//usage: "\n""nNUM - number of files to retain"
Denys Vlasenko3f52d132018-02-09 20:21:01 +0100153///////: "\n""NNUM - min number files to retain" - confusing
154///////: "\n""tSEC - rotate file if it get SEC seconds old" - confusing
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100155//usage: "\n""!PROG - process rotated log with PROG"
Denys Vlasenko3f52d132018-02-09 20:21:01 +0100156///////: "\n""uIPADDR - send log over UDP" - unsupported
157///////: "\n""UIPADDR - send log over UDP and DONT log" - unsupported
158///////: "\n""pPFX - prefix each line with PFX" - unsupported
Denys Vlasenko3a0f6902011-01-22 00:38:24 +0100159//usage: "\n""+,-PATTERN - (de)select line for logging"
160//usage: "\n""E,ePATTERN - (de)select line for stderr"
161
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000162#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000163#include "libbb.h"
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +0200164#include "common_bufsiz.h"
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000165#include "runit_lib.h"
166
Denis Vlasenko45946f82007-08-20 17:27:40 +0000167#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
168
169#define FMT_PTIME 30
170
Denis Vlasenko339936b2007-10-05 22:11:06 +0000171struct logdir {
Denis Vlasenko64392902007-02-03 00:53:43 +0000172 ////char *btmp;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000173 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
174 char *inst;
175 char *processor;
176 char *name;
177 unsigned size;
178 unsigned sizemax;
179 unsigned nmax;
180 unsigned nmin;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000181 unsigned rotate_period;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000182 int ppid;
183 int fddir;
184 int fdcur;
Denis Vlasenko64392902007-02-03 00:53:43 +0000185 FILE* filecur; ////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000186 int fdlock;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000187 unsigned next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000188 char fnsave[FMT_PTIME];
189 char match;
190 char matcherr;
Denis Vlasenko339936b2007-10-05 22:11:06 +0000191};
192
193
194struct globals {
195 struct logdir *dir;
196 unsigned verbose;
197 int linemax;
198 ////int buflen;
199 int linelen;
200
201 int fdwdir;
202 char **fndir;
203 int wstat;
204 unsigned nearest_rotate;
205
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200206 void* (*memRchr)(const void *, int, size_t);
Denys Vlasenko681efe22011-03-08 21:00:36 +0100207 char *shell;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200208
Denis Vlasenko339936b2007-10-05 22:11:06 +0000209 smallint exitasap;
210 smallint rotateasap;
211 smallint reopenasap;
212 smallint linecomplete;
213 smallint tmaxflag;
214
215 char repl;
216 const char *replace;
217 int fl_flag_0;
218 unsigned dirn;
219
220 sigset_t blocked_sigset;
221};
Denys Vlasenko98a4c7c2010-02-04 15:00:15 +0100222#define G (*ptr_to_globals)
Denis Vlasenko339936b2007-10-05 22:11:06 +0000223#define dir (G.dir )
224#define verbose (G.verbose )
225#define linemax (G.linemax )
226#define buflen (G.buflen )
227#define linelen (G.linelen )
228#define fndir (G.fndir )
229#define fdwdir (G.fdwdir )
230#define wstat (G.wstat )
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200231#define memRchr (G.memRchr )
Denis Vlasenko339936b2007-10-05 22:11:06 +0000232#define nearest_rotate (G.nearest_rotate)
233#define exitasap (G.exitasap )
234#define rotateasap (G.rotateasap )
235#define reopenasap (G.reopenasap )
236#define linecomplete (G.linecomplete )
237#define tmaxflag (G.tmaxflag )
238#define repl (G.repl )
239#define replace (G.replace )
240#define blocked_sigset (G.blocked_sigset)
241#define fl_flag_0 (G.fl_flag_0 )
242#define dirn (G.dirn )
Denys Vlasenko9de2e5a2016-04-21 18:38:51 +0200243#define line bb_common_bufsiz1
Denis Vlasenko339936b2007-10-05 22:11:06 +0000244#define INIT_G() do { \
Denys Vlasenko9de2e5a2016-04-21 18:38:51 +0200245 setup_common_bufsiz(); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000246 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denis Vlasenko339936b2007-10-05 22:11:06 +0000247 linemax = 1000; \
248 /*buflen = 1024;*/ \
249 linecomplete = 1; \
250 replace = ""; \
251} while (0)
252
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000253
254#define FATAL "fatal: "
255#define WARNING "warning: "
256#define PAUSE "pausing: "
257#define INFO "info: "
258
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000259static void fatalx(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000260{
261 bb_error_msg_and_die(FATAL"%s", m0);
262}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000263static void warn(const char *m0)
264{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000265 bb_perror_msg(WARNING"%s", m0);
266}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000267static void warn2(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000268{
269 bb_perror_msg(WARNING"%s: %s", m0, m1);
270}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000271static void warnx(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000272{
273 bb_error_msg(WARNING"%s: %s", m0, m1);
274}
275static void pause_nomem(void)
276{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000277 bb_error_msg(PAUSE"out of memory");
278 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000279}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000280static void pause1cannot(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000281{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000282 bb_perror_msg(PAUSE"can't %s", m0);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000283 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000284}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000285static void pause2cannot(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000286{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000287 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000288 sleep(3);
289}
290
291static char* wstrdup(const char *str)
292{
293 char *s;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000294 while (!(s = strdup(str)))
295 pause_nomem();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000296 return s;
297}
298
Denys Vlasenko05e86052010-10-13 12:53:27 +0200299static unsigned pmatch(const char *p, const char *s, unsigned len)
300{
301 for (;;) {
302 char c = *p++;
303 if (!c) return !len;
304 switch (c) {
305 case '*':
306 c = *p;
307 if (!c) return 1;
308 for (;;) {
309 if (!len) return 0;
310 if (*s == c) break;
311 ++s;
312 --len;
313 }
314 continue;
315 case '+':
316 c = *p++;
317 if (c != *s) return 0;
318 for (;;) {
319 if (!len) return 1;
320 if (*s != c) break;
321 ++s;
322 --len;
323 }
324 continue;
325 /*
326 case '?':
327 if (*p == '?') {
328 if (*s != '?') return 0;
329 ++p;
330 }
331 ++s; --len;
332 continue;
333 */
334 default:
335 if (!len) return 0;
336 if (*s != c) return 0;
337 ++s;
338 --len;
339 continue;
340 }
341 }
342 return 0;
343}
344
Denis Vlasenko45946f82007-08-20 17:27:40 +0000345/*** ex fmt_ptime.[ch] ***/
346
347/* NUL terminated */
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200348static void fmt_time_human_30nul(char *s, char dt_delim)
Denis Vlasenko45946f82007-08-20 17:27:40 +0000349{
Denys Vlasenkoe1b1b792018-03-06 18:11:47 +0100350 struct tm tm;
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100351 struct tm *ptm;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000352 struct timeval tv;
353
354 gettimeofday(&tv, NULL);
Denys Vlasenkoe1b1b792018-03-06 18:11:47 +0100355 ptm = gmtime_r(&tv.tv_sec, &tm);
356 /* ^^^ using gmtime_r() instead of gmtime() to not use static data */
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200357 sprintf(s, "%04u-%02u-%02u%c%02u:%02u:%02u.%06u000",
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100358 (unsigned)(1900 + ptm->tm_year),
359 (unsigned)(ptm->tm_mon + 1),
360 (unsigned)(ptm->tm_mday),
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200361 dt_delim,
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100362 (unsigned)(ptm->tm_hour),
363 (unsigned)(ptm->tm_min),
364 (unsigned)(ptm->tm_sec),
Denis Vlasenko45946f82007-08-20 17:27:40 +0000365 (unsigned)(tv.tv_usec)
366 );
367 /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
368 /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
369 /* 20 (up to '.' inclusive) + 9 (not including '\0') */
370}
371
372/* NOT terminated! */
373static void fmt_time_bernstein_25(char *s)
374{
375 uint32_t pack[3];
376 struct timeval tv;
377 unsigned sec_hi;
378
379 gettimeofday(&tv, NULL);
380 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
381 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
382 tv.tv_usec *= 1000;
383 /* Network order is big-endian: most significant byte first.
384 * This is exactly what we want here */
385 pack[0] = htonl(sec_hi);
386 pack[1] = htonl(tv.tv_sec);
387 pack[2] = htonl(tv.tv_usec);
388 *s++ = '@';
389 bin2hex(s, (char*)pack, 12);
390}
391
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000392static void processorstart(struct logdir *ld)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000393{
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000394 char sv_ch;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000395 int pid;
396
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000397 if (!ld->processor) return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000398 if (ld->ppid) {
399 warnx("processor already running", ld->name);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000400 return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000401 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000402
403 /* vfork'ed child trashes this byte, save... */
404 sv_ch = ld->fnsave[26];
405
Denys Vlasenko681efe22011-03-08 21:00:36 +0100406 if (!G.shell)
407 G.shell = xstrdup(get_shell_name());
408
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000409 while ((pid = vfork()) == -1)
410 pause2cannot("vfork for processor", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000411 if (!pid) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000412 int fd;
413
414 /* child */
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000415 /* Non-ignored signals revert to SIG_DFL on exec anyway */
416 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000417 + (1 << SIGTERM)
418 + (1 << SIGALRM)
419 + (1 << SIGHUP)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000420 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000421 sig_unblock(SIGTERM);
422 sig_unblock(SIGALRM);
423 sig_unblock(SIGHUP);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000424
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000425 if (verbose)
426 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
427 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000428 xmove_fd(fd, 0);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000429 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000430 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000431 xmove_fd(fd, 1);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200432 fd = open("state", O_RDONLY|O_NDELAY);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000433 if (fd == -1) {
434 if (errno != ENOENT)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000435 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000436 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000437 fd = xopen("state", O_RDONLY|O_NDELAY);
438 }
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000439 xmove_fd(fd, 4);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000440 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000441 xmove_fd(fd, 5);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000442
Denys Vlasenko681efe22011-03-08 21:00:36 +0100443 execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000444 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000445 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000446 ld->fnsave[26] = sv_ch; /* ...restore */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000447 ld->ppid = pid;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000448}
449
450static unsigned processorstop(struct logdir *ld)
451{
452 char f[28];
453
454 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000455 sig_unblock(SIGHUP);
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000456 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000457 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000458 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000459 ld->ppid = 0;
460 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200461 if (ld->fddir == -1)
462 return 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000463 while (fchdir(ld->fddir) == -1)
464 pause2cannot("change directory, want processor", ld->name);
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200465 if (WEXITSTATUS(wstat) != 0) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000466 warnx("processor failed, restart", ld->name);
467 ld->fnsave[26] = 't';
468 unlink(ld->fnsave);
469 ld->fnsave[26] = 'u';
470 processorstart(ld);
471 while (fchdir(fdwdir) == -1)
472 pause1cannot("change to initial working directory");
473 return ld->processor ? 0 : 1;
474 }
475 ld->fnsave[26] = 't';
476 memcpy(f, ld->fnsave, 26);
477 f[26] = 's';
478 f[27] = '\0';
479 while (rename(ld->fnsave, f) == -1)
480 pause2cannot("rename processed", ld->name);
481 while (chmod(f, 0744) == -1)
482 pause2cannot("set mode of processed", ld->name);
483 ld->fnsave[26] = 'u';
484 if (unlink(ld->fnsave) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000485 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000486 while (rename("newstate", "state") == -1)
487 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000488 if (verbose)
489 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000490 while (fchdir(fdwdir) == -1)
491 pause1cannot("change to initial working directory");
492 return 1;
493}
494
495static void rmoldest(struct logdir *ld)
496{
497 DIR *d;
498 struct dirent *f;
499 char oldest[FMT_PTIME];
500 int n = 0;
501
502 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
503 while (!(d = opendir(".")))
504 pause2cannot("open directory, want rotate", ld->name);
505 errno = 0;
506 while ((f = readdir(d))) {
507 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
508 if (f->d_name[26] == 't') {
509 if (unlink(f->d_name) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000510 warn2("can't unlink processor leftover", f->d_name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000511 } else {
512 ++n;
513 if (strcmp(f->d_name, oldest) < 0)
514 memcpy(oldest, f->d_name, 27);
515 }
516 errno = 0;
517 }
518 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000519 if (errno)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000520 warn2("can't read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000521 closedir(d);
522
523 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000524 if (verbose)
525 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000526 if ((*oldest == '@') && (unlink(oldest) == -1))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000527 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000528 }
529}
530
531static unsigned rotate(struct logdir *ld)
532{
533 struct stat st;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000534 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000535
536 if (ld->fddir == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000537 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000538 return 0;
539 }
540 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000541 while (!processorstop(ld))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000542 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000543
544 while (fchdir(ld->fddir) == -1)
545 pause2cannot("change directory, want rotate", ld->name);
546
547 /* create new filename */
548 ld->fnsave[25] = '.';
549 ld->fnsave[26] = 's';
550 if (ld->processor)
551 ld->fnsave[26] = 'u';
552 ld->fnsave[27] = '\0';
553 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000554 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000555 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000556 stat(ld->fnsave, &st);
557 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000558
Denis Vlasenko45946f82007-08-20 17:27:40 +0000559 now = monotonic_sec();
560 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
561 ld->next_rotate = now + ld->rotate_period;
562 if (LESS(ld->next_rotate, nearest_rotate))
563 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000564 }
565
566 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000567 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000568 pause2cannot("fsync current logfile", ld->name);
569 while (fchmod(ld->fdcur, 0744) == -1)
570 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000571 ////close(ld->fdcur);
572 fclose(ld->filecur);
573
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000574 if (verbose) {
575 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
576 ld->fnsave, ld->size);
577 }
578 while (rename("current", ld->fnsave) == -1)
579 pause2cannot("rename current", ld->name);
580 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
581 pause2cannot("create new current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200582 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
583 pause2cannot("create new current", ld->name); /* very unlikely */
Denis Vlasenko64392902007-02-03 00:53:43 +0000584 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000585 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000586 ld->size = 0;
587 while (fchmod(ld->fdcur, 0644) == -1)
588 pause2cannot("set mode of current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200589
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000590 rmoldest(ld);
591 processorstart(ld);
592 }
593
594 while (fchdir(fdwdir) == -1)
595 pause1cannot("change to initial working directory");
596 return 1;
597}
598
599static int buffer_pwrite(int n, char *s, unsigned len)
600{
601 int i;
602 struct logdir *ld = &dir[n];
603
604 if (ld->sizemax) {
605 if (ld->size >= ld->sizemax)
606 rotate(ld);
607 if (len > (ld->sizemax - ld->size))
608 len = ld->sizemax - ld->size;
609 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000610 while (1) {
611 ////i = full_write(ld->fdcur, s, len);
612 ////if (i != -1) break;
613 i = fwrite(s, 1, len, ld->filecur);
614 if (i == len) break;
615
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000616 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
617 DIR *d;
618 struct dirent *f;
619 char oldest[FMT_PTIME];
620 int j = 0;
621
622 while (fchdir(ld->fddir) == -1)
623 pause2cannot("change directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100624 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000625 oldest[0] = 'A';
626 oldest[1] = oldest[27] = '\0';
627 while (!(d = opendir(".")))
628 pause2cannot("open directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100629 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000630 errno = 0;
631 while ((f = readdir(d)))
632 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
633 ++j;
634 if (strcmp(f->d_name, oldest) < 0)
635 memcpy(oldest, f->d_name, 27);
636 }
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000637 if (errno) warn2("can't read directory, want remove old logfile",
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000638 ld->name);
639 closedir(d);
640 errno = ENOSPC;
641 if (j > ld->nmin) {
642 if (*oldest == '@') {
643 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
644 ld->name, oldest);
645 errno = 0;
646 if (unlink(oldest) == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000647 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000648 errno = ENOSPC;
649 }
650 while (fchdir(fdwdir) == -1)
651 pause1cannot("change to initial working directory");
652 }
653 }
654 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000655 if (errno)
656 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000657 }
658
659 ld->size += i;
660 if (ld->sizemax)
661 if (s[i-1] == '\n')
662 if (ld->size >= (ld->sizemax - linemax))
663 rotate(ld);
664 return i;
665}
666
667static void logdir_close(struct logdir *ld)
668{
669 if (ld->fddir == -1)
670 return;
671 if (verbose)
672 bb_error_msg(INFO"close: %s", ld->name);
673 close(ld->fddir);
674 ld->fddir = -1;
675 if (ld->fdcur == -1)
676 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000677 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000678 pause2cannot("fsync current logfile", ld->name);
679 while (fchmod(ld->fdcur, 0744) == -1)
680 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000681 ////close(ld->fdcur);
682 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000683 ld->fdcur = -1;
684 if (ld->fdlock == -1)
685 return; /* impossible */
686 close(ld->fdlock);
687 ld->fdlock = -1;
688 free(ld->processor);
689 ld->processor = NULL;
690}
691
Denys Vlasenkoa7bb3c12009-10-08 12:28:08 +0200692static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000693{
694 char buf[128];
Denis Vlasenko45946f82007-08-20 17:27:40 +0000695 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000696 char *new, *s, *np;
697 int i;
698 struct stat st;
699
Denis Vlasenko45946f82007-08-20 17:27:40 +0000700 now = monotonic_sec();
701
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000702 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
703 if (ld->fddir == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000704 warn2("can't open log directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000705 return 0;
706 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000707 close_on_exec_on(ld->fddir);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000708 if (fchdir(ld->fddir) == -1) {
709 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000710 warn2("can't change directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000711 return 0;
712 }
713 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
714 if ((ld->fdlock == -1)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200715 || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000716 ) {
717 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000718 warn2("can't lock directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000719 while (fchdir(fdwdir) == -1)
720 pause1cannot("change to initial working directory");
721 return 0;
722 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000723 close_on_exec_on(ld->fdlock);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000724
725 ld->size = 0;
726 ld->sizemax = 1000000;
727 ld->nmax = ld->nmin = 10;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000728 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000729 ld->name = (char*)fn;
730 ld->ppid = 0;
731 ld->match = '+';
732 free(ld->inst); ld->inst = NULL;
733 free(ld->processor); ld->processor = NULL;
734
735 /* read config */
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200736 i = open_read_close("config", buf, sizeof(buf) - 1);
Denis Vlasenkof223efb2007-08-03 10:58:12 +0000737 if (i < 0 && errno != ENOENT)
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000738 bb_perror_msg(WARNING"%s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000739 if (i > 0) {
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200740 buf[i] = '\0';
Denis Vlasenko45946f82007-08-20 17:27:40 +0000741 if (verbose)
742 bb_error_msg(INFO"read: %s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000743 s = buf;
744 while (s) {
745 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000746 if (np)
747 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000748 switch (s[0]) {
749 case '+':
750 case '-':
751 case 'e':
752 case 'E':
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200753 /* Filtering requires one-line buffering,
754 * resetting the "find newline" function
755 * accordingly */
756 memRchr = memchr;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000757 /* Add '\n'-terminated line to ld->inst */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000758 while (1) {
Denys Vlasenko90a99042009-09-06 02:36:23 +0200759 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000760 if (l >= 0 && new)
761 break;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000762 pause_nomem();
763 }
764 free(ld->inst);
765 ld->inst = new;
766 break;
767 case 's': {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000768 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
769 break;
770 }
771 case 'n':
Denys Vlasenko77832482010-08-12 14:14:45 +0200772 ld->nmax = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000773 break;
774 case 'N':
Denys Vlasenko77832482010-08-12 14:14:45 +0200775 ld->nmin = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000776 break;
777 case 't': {
778 static const struct suffix_mult mh_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000779 { "m", 60 },
780 { "h", 60*60 },
781 /*{ "d", 24*60*60 },*/
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200782 { "", 0 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000783 };
Denis Vlasenko45946f82007-08-20 17:27:40 +0000784 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
785 if (ld->rotate_period) {
786 ld->next_rotate = now + ld->rotate_period;
787 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
788 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000789 tmaxflag = 1;
790 }
791 break;
792 }
793 case '!':
794 if (s[1]) {
795 free(ld->processor);
Francis Roundsd2c5de02016-09-14 11:53:51 +0000796 ld->processor = wstrdup(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000797 }
798 break;
799 }
800 s = np;
801 }
802 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
803 s = ld->inst;
804 while (s) {
805 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000806 if (np)
807 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000808 s = np;
809 }
810 }
811
812 /* open current */
813 i = stat("current", &st);
814 if (i != -1) {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000815 if (st.st_size && !(st.st_mode & S_IXUSR)) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000816 ld->fnsave[25] = '.';
817 ld->fnsave[26] = 'u';
818 ld->fnsave[27] = '\0';
819 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000820 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000821 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000822 stat(ld->fnsave, &st);
823 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000824 while (rename("current", ld->fnsave) == -1)
825 pause2cannot("rename current", ld->name);
826 rmoldest(ld);
827 i = -1;
828 } else {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000829 /* st.st_size can be not just bigger, but WIDER!
830 * This code is safe: if st.st_size > 4GB, we select
831 * ld->sizemax (because it's "unsigned") */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000832 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
833 }
834 } else {
835 if (errno != ENOENT) {
836 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000837 warn2("can't stat current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000838 while (fchdir(fdwdir) == -1)
839 pause1cannot("change to initial working directory");
840 return 0;
841 }
842 }
843 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
844 pause2cannot("open current", ld->name);
Denys Vlasenkoa7ccdee2009-11-15 23:28:11 +0100845 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
846 pause2cannot("open current", ld->name); ////
Denis Vlasenko64392902007-02-03 00:53:43 +0000847 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
848
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000849 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000850 while (fchmod(ld->fdcur, 0644) == -1)
851 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000852
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000853 if (verbose) {
854 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
855 else bb_error_msg(INFO"new: %s/current", ld->name);
856 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000857
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000858 while (fchdir(fdwdir) == -1)
859 pause1cannot("change to initial working directory");
860 return 1;
861}
862
863static void logdirs_reopen(void)
864{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000865 int l;
866 int ok = 0;
867
868 tmaxflag = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000869 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000870 logdir_close(&dir[l]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000871 if (logdir_open(&dir[l], fndir[l]))
872 ok = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000873 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000874 if (!ok)
875 fatalx("no functional log directories");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000876}
877
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000878/* Will look good in libbb one day */
879static ssize_t ndelay_read(int fd, void *buf, size_t count)
880{
881 if (!(fl_flag_0 & O_NONBLOCK))
882 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
883 count = safe_read(fd, buf, count);
884 if (!(fl_flag_0 & O_NONBLOCK))
885 fcntl(fd, F_SETFL, fl_flag_0);
886 return count;
887}
888
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000889/* Used for reading stdin */
Denis Vlasenko5d61e712007-09-27 10:09:59 +0000890static int buffer_pread(/*int fd, */char *s, unsigned len)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000891{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000892 unsigned now;
893 struct pollfd input;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000894 int i;
895
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200896 input.fd = STDIN_FILENO;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000897 input.events = POLLIN;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000898
Denis Vlasenkob9528352007-05-06 01:37:21 +0000899 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000900 if (rotateasap) {
901 for (i = 0; i < dirn; ++i)
902 rotate(dir + i);
903 rotateasap = 0;
904 }
905 if (exitasap) {
906 if (linecomplete)
907 return 0;
908 len = 1;
909 }
910 if (reopenasap) {
911 logdirs_reopen();
912 reopenasap = 0;
913 }
914 now = monotonic_sec();
915 nearest_rotate = now + (45 * 60 + 45);
916 for (i = 0; i < dirn; ++i) {
917 if (dir[i].rotate_period) {
918 if (LESS(dir[i].next_rotate, now))
919 rotate(dir + i);
920 if (LESS(dir[i].next_rotate, nearest_rotate))
921 nearest_rotate = dir[i].next_rotate;
922 }
923 }
924
Denis Vlasenko339936b2007-10-05 22:11:06 +0000925 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000926 i = nearest_rotate - now;
927 if (i > 1000000)
928 i = 1000000;
929 if (i <= 0)
930 i = 1;
931 poll(&input, 1, i * 1000);
Denis Vlasenko339936b2007-10-05 22:11:06 +0000932 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000933
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000934 i = ndelay_read(STDIN_FILENO, s, len);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000935 if (i >= 0)
936 break;
937 if (errno == EINTR)
938 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000939 if (errno != EAGAIN) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000940 warn("can't read standard input");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000941 break;
942 }
943 /* else: EAGAIN - normal, repeat silently */
Denis Vlasenkob9528352007-05-06 01:37:21 +0000944 } while (!exitasap);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000945
946 if (i > 0) {
947 int cnt;
948 linecomplete = (s[i-1] == '\n');
Denis Vlasenko45946f82007-08-20 17:27:40 +0000949 if (!repl)
950 return i;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000951
952 cnt = i;
953 while (--cnt >= 0) {
954 char ch = *s;
955 if (ch != '\n') {
956 if (ch < 32 || ch > 126)
957 *s = repl;
958 else {
959 int j;
960 for (j = 0; replace[j]; ++j) {
961 if (ch == replace[j]) {
962 *s = repl;
963 break;
964 }
965 }
966 }
967 }
968 s++;
969 }
970 }
971 return i;
972}
973
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000974static void sig_term_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000975{
976 if (verbose)
977 bb_error_msg(INFO"sig%s received", "term");
978 exitasap = 1;
979}
980
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000981static void sig_child_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000982{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000983 pid_t pid;
984 int l;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000985
986 if (verbose)
987 bb_error_msg(INFO"sig%s received", "child");
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000988 while ((pid = wait_any_nohang(&wstat)) > 0) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000989 for (l = 0; l < dirn; ++l) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000990 if (dir[l].ppid == pid) {
991 dir[l].ppid = 0;
992 processorstop(&dir[l]);
993 break;
994 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000995 }
996 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000997}
998
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000999static void sig_alarm_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001000{
1001 if (verbose)
1002 bb_error_msg(INFO"sig%s received", "alarm");
1003 rotateasap = 1;
1004}
1005
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001006static void sig_hangup_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001007{
1008 if (verbose)
1009 bb_error_msg(INFO"sig%s received", "hangup");
1010 reopenasap = 1;
1011}
1012
Denys Vlasenko0df289f2018-11-01 14:53:04 +01001013static void logmatch(struct logdir *ld, char* lineptr, int lineptr_len)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001014{
1015 char *s;
1016
1017 ld->match = '+';
1018 ld->matcherr = 'E';
1019 s = ld->inst;
1020 while (s && s[0]) {
1021 switch (s[0]) {
1022 case '+':
1023 case '-':
Denys Vlasenko0df289f2018-11-01 14:53:04 +01001024 if (pmatch(s+1, lineptr, lineptr_len))
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001025 ld->match = s[0];
1026 break;
1027 case 'e':
1028 case 'E':
Denys Vlasenko0df289f2018-11-01 14:53:04 +01001029 if (pmatch(s+1, lineptr, lineptr_len))
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001030 ld->matcherr = s[0];
1031 break;
1032 }
1033 s += strlen(s) + 1;
1034 }
1035}
1036
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001037int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001038int svlogd_main(int argc, char **argv)
1039{
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001040 char *r, *l, *b;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001041 ssize_t stdin_cnt = 0;
1042 int i;
1043 unsigned opt;
1044 unsigned timestamp = 0;
1045
Denis Vlasenko339936b2007-10-05 22:11:06 +00001046 INIT_G();
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001047
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001048 opt = getopt32(argv, "^"
1049 "r:R:l:b:tv" "\0" "tt:vv",
1050 &r, &replace, &l, &b, &timestamp, &verbose
1051 );
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001052 if (opt & 1) { // -r
1053 repl = r[0];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001054 if (!repl || r[1])
1055 bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001056 }
1057 if (opt & 2) if (!repl) repl = '_'; // -R
1058 if (opt & 4) { // -l
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +02001059 linemax = xatou_range(l, 0, COMMON_BUFSIZE-26);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001060 if (linemax == 0)
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +02001061 linemax = COMMON_BUFSIZE-26;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001062 if (linemax < 256)
1063 linemax = 256;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001064 }
Denis Vlasenko64392902007-02-03 00:53:43 +00001065 ////if (opt & 8) { // -b
Denys Vlasenko77832482010-08-12 14:14:45 +02001066 //// buflen = xatoi_positive(b);
Denis Vlasenko64392902007-02-03 00:53:43 +00001067 //// if (buflen == 0) buflen = 1024;
1068 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001069 //if (opt & 0x10) timestamp++; // -t
1070 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001071 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001072 argv += optind;
1073 argc -= optind;
1074
1075 dirn = argc;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001076 if (dirn <= 0)
1077 bb_show_usage();
1078 ////if (buflen <= linemax) bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001079 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +00001080 close_on_exec_on(fdwdir);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001081 dir = xzalloc(dirn * sizeof(dir[0]));
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001082 for (i = 0; i < dirn; ++i) {
1083 dir[i].fddir = -1;
1084 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001085 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenkob9528352007-05-06 01:37:21 +00001086 /*dir[i].ppid = 0;*/
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001087 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001088 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001089 fndir = argv;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001090 /* We cannot set NONBLOCK on fd #0 permanently - this setting
1091 * _isn't_ per-process! It is shared among all other processes
1092 * with the same stdin */
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001093 fl_flag_0 = fcntl(0, F_GETFL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001094
Denis Vlasenko339936b2007-10-05 22:11:06 +00001095 sigemptyset(&blocked_sigset);
1096 sigaddset(&blocked_sigset, SIGTERM);
1097 sigaddset(&blocked_sigset, SIGCHLD);
1098 sigaddset(&blocked_sigset, SIGALRM);
1099 sigaddset(&blocked_sigset, SIGHUP);
1100 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +00001101 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
1102 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
1103 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
1104 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001105
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001106 /* Without timestamps, we don't have to print each line
1107 * separately, so we can look for _last_ newline, not first,
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001108 * thus batching writes. If filtering is enabled in config,
1109 * logdirs_reopen resets it to memchr.
1110 */
1111 memRchr = (timestamp ? memchr : memrchr);
1112
1113 logdirs_reopen();
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001114
Denis Vlasenko64392902007-02-03 00:53:43 +00001115 setvbuf(stderr, NULL, _IOFBF, linelen);
1116
Denis Vlasenko4e1715f2007-01-28 14:51:32 +00001117 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001118 while (1) {
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001119 char stamp[FMT_PTIME];
1120 char *lineptr;
1121 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001122 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001123 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001124 char ch;
1125
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001126 lineptr = line;
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001127 if (timestamp)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001128 lineptr += 26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001129
1130 /* lineptr[0..linemax-1] - buffer for stdin */
1131 /* (possibly has some unprocessed data from prev loop) */
1132
1133 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001134 np = memRchr(lineptr, '\n', stdin_cnt);
1135 if (!np && !exitasap) {
1136 i = linemax - stdin_cnt; /* avail. bytes at tail */
1137 if (i >= 128) {
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001138 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001139 if (i <= 0) /* EOF or error on stdin */
1140 exitasap = 1;
1141 else {
1142 np = memRchr(lineptr + stdin_cnt, '\n', i);
1143 stdin_cnt += i;
1144 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001145 }
1146 }
1147 if (stdin_cnt <= 0 && exitasap)
1148 break;
1149
1150 /* Search for '\n' (in fact, np already holds the result) */
1151 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001152 if (np) {
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02001153 print_to_nl:
1154 /* NB: starting from here lineptr may point
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001155 * farther out into line[] */
1156 linelen = np - lineptr + 1;
1157 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001158 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1159 ch = lineptr[linelen-1];
1160
Denis Vlasenko64392902007-02-03 00:53:43 +00001161 /* Biggest performance hit was coming from the fact
1162 * that we did not buffer writes. We were reading many lines
1163 * in one read() above, but wrote one line per write().
1164 * We are using stdio to fix that */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001165
1166 /* write out lineptr[0..linelen-1] to each log destination
1167 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001168 printlen = linelen;
1169 printptr = lineptr;
1170 if (timestamp) {
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001171 if (timestamp == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +00001172 fmt_time_bernstein_25(stamp);
Denys Vlasenko79c0d732017-05-15 19:12:09 +02001173 else /* 2+: */
1174 fmt_time_human_30nul(stamp, timestamp == 2 ? '_' : 'T');
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001175 printlen += 26;
1176 printptr -= 26;
1177 memcpy(printptr, stamp, 25);
1178 printptr[25] = ' ';
1179 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001180 for (i = 0; i < dirn; ++i) {
1181 struct logdir *ld = &dir[i];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001182 if (ld->fddir == -1)
1183 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001184 if (ld->inst)
Denys Vlasenko0df289f2018-11-01 14:53:04 +01001185 logmatch(ld, lineptr, linelen);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001186 if (ld->matcherr == 'e') {
1187 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001188 ////full_write(STDERR_FILENO, printptr, printlen);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001189 fwrite(printptr, 1, printlen, stderr);
1190 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001191 if (ld->match != '+')
1192 continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001193 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001194 }
1195
1196 /* If we didn't see '\n' (long input line), */
1197 /* read/write repeatedly until we see it */
1198 while (ch != '\n') {
1199 /* lineptr is emptied now, safe to use as buffer */
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001200 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001201 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001202 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001203 lineptr[0] = ch = '\n';
1204 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001205 stdin_cnt = 1;
1206 } else {
1207 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001208 np = memRchr(lineptr, '\n', stdin_cnt);
1209 if (np)
1210 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001211 ch = lineptr[linelen-1];
1212 }
1213 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1214 for (i = 0; i < dirn; ++i) {
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001215 if (dir[i].fddir == -1)
1216 continue;
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001217 if (dir[i].matcherr == 'e') {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001218 ////full_write(STDERR_FILENO, lineptr, linelen);
Denis Vlasenko64392902007-02-03 00:53:43 +00001219 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001220 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001221 if (dir[i].match != '+')
1222 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001223 buffer_pwrite(i, lineptr, linelen);
1224 }
1225 }
1226
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001227 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001228 if (stdin_cnt > 0) {
1229 lineptr += linelen;
1230 /* If we see another '\n', we don't need to read
1231 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001232 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001233 if (np)
1234 goto print_to_nl;
1235 /* Move unprocessed data to the front of line */
1236 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1237 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01001238 fflush_all();////
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001239 }
1240
1241 for (i = 0; i < dirn; ++i) {
1242 if (dir[i].ppid)
1243 while (!processorstop(&dir[i]))
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001244 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001245 logdir_close(&dir[i]);
1246 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001247 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001248}