blob: 02c30569648f33b1c45c84070946bdf5496805c9 [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 Vlasenkob097a842018-12-28 03:20:17 +0100127//config: bool "svlogd (16 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{
James Byrne69374872019-07-02 11:35:03 +0200277 bb_simple_error_msg(PAUSE"out of memory");
Denis Vlasenko8c783952007-01-27 22:21:52 +0000278 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
Denys Vlasenko3c13da32020-12-30 23:48:01 +0100354 xgettimeofday(&tv);
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
Denys Vlasenko3c13da32020-12-30 23:48:01 +0100379 xgettimeofday(&tv);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000380 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 */
Denys Vlasenkoac444862021-06-05 14:51:53 +0200415 /* Non-ignored signals revert to SIG_DFL on exec anyway.
416 * But we can get signals BEFORE execl(), this is unlikely
417 * but wouldn't be good...
418 */
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000419 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000420 + (1 << SIGTERM)
Denys Vlasenkoac444862021-06-05 14:51:53 +0200421 //+ (1 << SIGCHLD)
Denis Vlasenko25591c32008-02-16 22:58:56 +0000422 + (1 << SIGALRM)
423 + (1 << SIGHUP)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000424 , SIG_DFL);*/
Denys Vlasenkoac444862021-06-05 14:51:53 +0200425 /* runit 2.1.2 does not unblock SIGCHLD, a bug? we do: */
426 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000427
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000428 if (verbose)
429 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
Denys Vlasenkoac444862021-06-05 14:51:53 +0200430
431 fd = open_or_warn(ld->fnsave, O_RDONLY|O_NDELAY);
432 /* Used to have xopen() above, but it causes infinite restarts of processor
433 * if file is gone - which can happen even because of _us_!
434 * Users report that if on reboot, time is reset to before existing
435 * logfiles creation time, rmoldest() deletes the newest logfile (!)
436 * and we end up here trying to open this now-deleted file.
437 */
438 if (fd < 0)
439 _exit(0); /* fake "success": do not run processor again */
440
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000441 xmove_fd(fd, 0);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000442 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000443 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000444 xmove_fd(fd, 1);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200445 fd = open("state", O_RDONLY|O_NDELAY);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000446 if (fd == -1) {
447 if (errno != ENOENT)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000448 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000449 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000450 fd = xopen("state", O_RDONLY|O_NDELAY);
451 }
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000452 xmove_fd(fd, 4);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000453 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000454 xmove_fd(fd, 5);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000455
Denys Vlasenko681efe22011-03-08 21:00:36 +0100456 execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000457 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000458 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000459 ld->fnsave[26] = sv_ch; /* ...restore */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000460 ld->ppid = pid;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000461}
462
463static unsigned processorstop(struct logdir *ld)
464{
465 char f[28];
466
467 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000468 sig_unblock(SIGHUP);
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000469 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000470 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000471 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000472 ld->ppid = 0;
473 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200474 if (ld->fddir == -1)
475 return 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000476 while (fchdir(ld->fddir) == -1)
477 pause2cannot("change directory, want processor", ld->name);
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200478 if (WEXITSTATUS(wstat) != 0) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000479 warnx("processor failed, restart", ld->name);
480 ld->fnsave[26] = 't';
481 unlink(ld->fnsave);
482 ld->fnsave[26] = 'u';
483 processorstart(ld);
484 while (fchdir(fdwdir) == -1)
485 pause1cannot("change to initial working directory");
486 return ld->processor ? 0 : 1;
487 }
488 ld->fnsave[26] = 't';
489 memcpy(f, ld->fnsave, 26);
490 f[26] = 's';
491 f[27] = '\0';
492 while (rename(ld->fnsave, f) == -1)
493 pause2cannot("rename processed", ld->name);
494 while (chmod(f, 0744) == -1)
495 pause2cannot("set mode of processed", ld->name);
496 ld->fnsave[26] = 'u';
497 if (unlink(ld->fnsave) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000498 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000499 while (rename("newstate", "state") == -1)
500 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000501 if (verbose)
502 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000503 while (fchdir(fdwdir) == -1)
504 pause1cannot("change to initial working directory");
505 return 1;
506}
507
508static void rmoldest(struct logdir *ld)
509{
510 DIR *d;
511 struct dirent *f;
512 char oldest[FMT_PTIME];
513 int n = 0;
514
515 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
516 while (!(d = opendir(".")))
517 pause2cannot("open directory, want rotate", ld->name);
518 errno = 0;
519 while ((f = readdir(d))) {
520 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
521 if (f->d_name[26] == 't') {
522 if (unlink(f->d_name) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000523 warn2("can't unlink processor leftover", f->d_name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000524 } else {
525 ++n;
526 if (strcmp(f->d_name, oldest) < 0)
527 memcpy(oldest, f->d_name, 27);
528 }
529 errno = 0;
530 }
531 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000532 if (errno)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000533 warn2("can't read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000534 closedir(d);
535
536 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000537 if (verbose)
538 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000539 if ((*oldest == '@') && (unlink(oldest) == -1))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000540 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000541 }
542}
543
544static unsigned rotate(struct logdir *ld)
545{
546 struct stat st;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000547 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000548
549 if (ld->fddir == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000550 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000551 return 0;
552 }
553 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000554 while (!processorstop(ld))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000555 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000556
557 while (fchdir(ld->fddir) == -1)
558 pause2cannot("change directory, want rotate", ld->name);
559
560 /* create new filename */
561 ld->fnsave[25] = '.';
562 ld->fnsave[26] = 's';
563 if (ld->processor)
564 ld->fnsave[26] = 'u';
565 ld->fnsave[27] = '\0';
566 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000567 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000568 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000569 stat(ld->fnsave, &st);
570 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000571
Denis Vlasenko45946f82007-08-20 17:27:40 +0000572 now = monotonic_sec();
573 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
574 ld->next_rotate = now + ld->rotate_period;
575 if (LESS(ld->next_rotate, nearest_rotate))
576 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000577 }
578
579 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000580 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000581 pause2cannot("fsync current logfile", ld->name);
582 while (fchmod(ld->fdcur, 0744) == -1)
583 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000584 ////close(ld->fdcur);
585 fclose(ld->filecur);
586
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000587 if (verbose) {
588 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
589 ld->fnsave, ld->size);
590 }
591 while (rename("current", ld->fnsave) == -1)
592 pause2cannot("rename current", ld->name);
593 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
594 pause2cannot("create new current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200595 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
596 pause2cannot("create new current", ld->name); /* very unlikely */
Denis Vlasenko64392902007-02-03 00:53:43 +0000597 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000598 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000599 ld->size = 0;
600 while (fchmod(ld->fdcur, 0644) == -1)
601 pause2cannot("set mode of current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200602
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000603 rmoldest(ld);
604 processorstart(ld);
605 }
606
607 while (fchdir(fdwdir) == -1)
608 pause1cannot("change to initial working directory");
609 return 1;
610}
611
612static int buffer_pwrite(int n, char *s, unsigned len)
613{
614 int i;
615 struct logdir *ld = &dir[n];
616
617 if (ld->sizemax) {
618 if (ld->size >= ld->sizemax)
619 rotate(ld);
620 if (len > (ld->sizemax - ld->size))
621 len = ld->sizemax - ld->size;
622 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000623 while (1) {
624 ////i = full_write(ld->fdcur, s, len);
625 ////if (i != -1) break;
626 i = fwrite(s, 1, len, ld->filecur);
627 if (i == len) break;
628
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000629 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
630 DIR *d;
631 struct dirent *f;
632 char oldest[FMT_PTIME];
633 int j = 0;
634
635 while (fchdir(ld->fddir) == -1)
636 pause2cannot("change directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100637 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000638 oldest[0] = 'A';
639 oldest[1] = oldest[27] = '\0';
640 while (!(d = opendir(".")))
641 pause2cannot("open directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100642 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000643 errno = 0;
644 while ((f = readdir(d)))
645 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
646 ++j;
647 if (strcmp(f->d_name, oldest) < 0)
648 memcpy(oldest, f->d_name, 27);
649 }
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000650 if (errno) warn2("can't read directory, want remove old logfile",
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000651 ld->name);
652 closedir(d);
653 errno = ENOSPC;
654 if (j > ld->nmin) {
655 if (*oldest == '@') {
656 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
657 ld->name, oldest);
658 errno = 0;
659 if (unlink(oldest) == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000660 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000661 errno = ENOSPC;
662 }
663 while (fchdir(fdwdir) == -1)
664 pause1cannot("change to initial working directory");
665 }
666 }
667 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000668 if (errno)
669 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000670 }
671
672 ld->size += i;
673 if (ld->sizemax)
674 if (s[i-1] == '\n')
675 if (ld->size >= (ld->sizemax - linemax))
676 rotate(ld);
677 return i;
678}
679
680static void logdir_close(struct logdir *ld)
681{
682 if (ld->fddir == -1)
683 return;
684 if (verbose)
685 bb_error_msg(INFO"close: %s", ld->name);
686 close(ld->fddir);
687 ld->fddir = -1;
688 if (ld->fdcur == -1)
689 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000690 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000691 pause2cannot("fsync current logfile", ld->name);
692 while (fchmod(ld->fdcur, 0744) == -1)
693 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000694 ////close(ld->fdcur);
695 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000696 ld->fdcur = -1;
697 if (ld->fdlock == -1)
698 return; /* impossible */
699 close(ld->fdlock);
700 ld->fdlock = -1;
701 free(ld->processor);
702 ld->processor = NULL;
703}
704
Denys Vlasenkoa7bb3c12009-10-08 12:28:08 +0200705static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000706{
707 char buf[128];
Denis Vlasenko45946f82007-08-20 17:27:40 +0000708 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000709 char *new, *s, *np;
710 int i;
711 struct stat st;
712
Denis Vlasenko45946f82007-08-20 17:27:40 +0000713 now = monotonic_sec();
714
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000715 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
716 if (ld->fddir == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000717 warn2("can't open log directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000718 return 0;
719 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000720 close_on_exec_on(ld->fddir);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000721 if (fchdir(ld->fddir) == -1) {
722 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000723 warn2("can't change directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000724 return 0;
725 }
726 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
727 if ((ld->fdlock == -1)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200728 || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000729 ) {
730 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000731 warn2("can't lock directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000732 while (fchdir(fdwdir) == -1)
733 pause1cannot("change to initial working directory");
734 return 0;
735 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000736 close_on_exec_on(ld->fdlock);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000737
738 ld->size = 0;
739 ld->sizemax = 1000000;
740 ld->nmax = ld->nmin = 10;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000741 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000742 ld->name = (char*)fn;
743 ld->ppid = 0;
744 ld->match = '+';
745 free(ld->inst); ld->inst = NULL;
746 free(ld->processor); ld->processor = NULL;
747
748 /* read config */
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200749 i = open_read_close("config", buf, sizeof(buf) - 1);
Denis Vlasenkof223efb2007-08-03 10:58:12 +0000750 if (i < 0 && errno != ENOENT)
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000751 bb_perror_msg(WARNING"%s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000752 if (i > 0) {
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200753 buf[i] = '\0';
Denis Vlasenko45946f82007-08-20 17:27:40 +0000754 if (verbose)
755 bb_error_msg(INFO"read: %s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000756 s = buf;
757 while (s) {
758 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000759 if (np)
760 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000761 switch (s[0]) {
762 case '+':
763 case '-':
764 case 'e':
765 case 'E':
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200766 /* Filtering requires one-line buffering,
767 * resetting the "find newline" function
768 * accordingly */
769 memRchr = memchr;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000770 /* Add '\n'-terminated line to ld->inst */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000771 while (1) {
Denys Vlasenko90a99042009-09-06 02:36:23 +0200772 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000773 if (l >= 0 && new)
774 break;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000775 pause_nomem();
776 }
777 free(ld->inst);
778 ld->inst = new;
779 break;
780 case 's': {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000781 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
782 break;
783 }
784 case 'n':
Denys Vlasenko77832482010-08-12 14:14:45 +0200785 ld->nmax = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000786 break;
787 case 'N':
Denys Vlasenko77832482010-08-12 14:14:45 +0200788 ld->nmin = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000789 break;
790 case 't': {
Denys Vlasenko965b7952020-11-30 13:03:03 +0100791 static const struct suffix_mult mh_suffixes[] ALIGN_SUFFIX = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000792 { "m", 60 },
793 { "h", 60*60 },
794 /*{ "d", 24*60*60 },*/
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200795 { "", 0 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000796 };
Denis Vlasenko45946f82007-08-20 17:27:40 +0000797 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
798 if (ld->rotate_period) {
799 ld->next_rotate = now + ld->rotate_period;
800 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
801 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000802 tmaxflag = 1;
803 }
804 break;
805 }
806 case '!':
807 if (s[1]) {
808 free(ld->processor);
Francis Roundsd2c5de02016-09-14 11:53:51 +0000809 ld->processor = wstrdup(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000810 }
811 break;
812 }
813 s = np;
814 }
815 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
816 s = ld->inst;
817 while (s) {
818 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000819 if (np)
820 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000821 s = np;
822 }
823 }
824
825 /* open current */
826 i = stat("current", &st);
827 if (i != -1) {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000828 if (st.st_size && !(st.st_mode & S_IXUSR)) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000829 ld->fnsave[25] = '.';
830 ld->fnsave[26] = 'u';
831 ld->fnsave[27] = '\0';
832 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000833 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000834 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000835 stat(ld->fnsave, &st);
836 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000837 while (rename("current", ld->fnsave) == -1)
838 pause2cannot("rename current", ld->name);
839 rmoldest(ld);
840 i = -1;
841 } else {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000842 /* st.st_size can be not just bigger, but WIDER!
843 * This code is safe: if st.st_size > 4GB, we select
844 * ld->sizemax (because it's "unsigned") */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000845 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
846 }
847 } else {
848 if (errno != ENOENT) {
849 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000850 warn2("can't stat current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000851 while (fchdir(fdwdir) == -1)
852 pause1cannot("change to initial working directory");
853 return 0;
854 }
855 }
856 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
857 pause2cannot("open current", ld->name);
Denys Vlasenkoa7ccdee2009-11-15 23:28:11 +0100858 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
859 pause2cannot("open current", ld->name); ////
Denis Vlasenko64392902007-02-03 00:53:43 +0000860 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
861
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000862 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000863 while (fchmod(ld->fdcur, 0644) == -1)
864 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000865
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000866 if (verbose) {
867 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
868 else bb_error_msg(INFO"new: %s/current", ld->name);
869 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000870
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000871 while (fchdir(fdwdir) == -1)
872 pause1cannot("change to initial working directory");
873 return 1;
874}
875
876static void logdirs_reopen(void)
877{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000878 int l;
879 int ok = 0;
880
881 tmaxflag = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000882 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000883 logdir_close(&dir[l]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000884 if (logdir_open(&dir[l], fndir[l]))
885 ok = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000886 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000887 if (!ok)
888 fatalx("no functional log directories");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000889}
890
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000891/* Will look good in libbb one day */
892static ssize_t ndelay_read(int fd, void *buf, size_t count)
893{
894 if (!(fl_flag_0 & O_NONBLOCK))
895 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
896 count = safe_read(fd, buf, count);
897 if (!(fl_flag_0 & O_NONBLOCK))
898 fcntl(fd, F_SETFL, fl_flag_0);
899 return count;
900}
901
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000902/* Used for reading stdin */
Denis Vlasenko5d61e712007-09-27 10:09:59 +0000903static int buffer_pread(/*int fd, */char *s, unsigned len)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000904{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000905 unsigned now;
906 struct pollfd input;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000907 int i;
908
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200909 input.fd = STDIN_FILENO;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000910 input.events = POLLIN;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000911
Denis Vlasenkob9528352007-05-06 01:37:21 +0000912 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000913 if (rotateasap) {
914 for (i = 0; i < dirn; ++i)
915 rotate(dir + i);
916 rotateasap = 0;
917 }
918 if (exitasap) {
919 if (linecomplete)
920 return 0;
921 len = 1;
922 }
923 if (reopenasap) {
924 logdirs_reopen();
925 reopenasap = 0;
926 }
927 now = monotonic_sec();
928 nearest_rotate = now + (45 * 60 + 45);
929 for (i = 0; i < dirn; ++i) {
930 if (dir[i].rotate_period) {
931 if (LESS(dir[i].next_rotate, now))
932 rotate(dir + i);
933 if (LESS(dir[i].next_rotate, nearest_rotate))
934 nearest_rotate = dir[i].next_rotate;
935 }
936 }
937
Denis Vlasenko339936b2007-10-05 22:11:06 +0000938 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000939 i = nearest_rotate - now;
940 if (i > 1000000)
941 i = 1000000;
942 if (i <= 0)
943 i = 1;
944 poll(&input, 1, i * 1000);
Denis Vlasenko339936b2007-10-05 22:11:06 +0000945 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000946
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000947 i = ndelay_read(STDIN_FILENO, s, len);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000948 if (i >= 0)
949 break;
950 if (errno == EINTR)
951 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000952 if (errno != EAGAIN) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000953 warn("can't read standard input");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000954 break;
955 }
956 /* else: EAGAIN - normal, repeat silently */
Denis Vlasenkob9528352007-05-06 01:37:21 +0000957 } while (!exitasap);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000958
959 if (i > 0) {
960 int cnt;
961 linecomplete = (s[i-1] == '\n');
Denis Vlasenko45946f82007-08-20 17:27:40 +0000962 if (!repl)
963 return i;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000964
965 cnt = i;
966 while (--cnt >= 0) {
967 char ch = *s;
968 if (ch != '\n') {
969 if (ch < 32 || ch > 126)
970 *s = repl;
971 else {
972 int j;
973 for (j = 0; replace[j]; ++j) {
974 if (ch == replace[j]) {
975 *s = repl;
976 break;
977 }
978 }
979 }
980 }
981 s++;
982 }
983 }
984 return i;
985}
986
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000987static void sig_term_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000988{
989 if (verbose)
990 bb_error_msg(INFO"sig%s received", "term");
991 exitasap = 1;
992}
993
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000994static void sig_child_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000995{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000996 pid_t pid;
997 int l;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000998
999 if (verbose)
1000 bb_error_msg(INFO"sig%s received", "child");
Denis Vlasenkofb0eba72008-01-02 19:55:04 +00001001 while ((pid = wait_any_nohang(&wstat)) > 0) {
Denis Vlasenko45946f82007-08-20 17:27:40 +00001002 for (l = 0; l < dirn; ++l) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001003 if (dir[l].ppid == pid) {
1004 dir[l].ppid = 0;
1005 processorstop(&dir[l]);
1006 break;
1007 }
Denis Vlasenko45946f82007-08-20 17:27:40 +00001008 }
1009 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001010}
1011
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001012static void sig_alarm_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001013{
1014 if (verbose)
1015 bb_error_msg(INFO"sig%s received", "alarm");
1016 rotateasap = 1;
1017}
1018
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001019static void sig_hangup_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001020{
1021 if (verbose)
1022 bb_error_msg(INFO"sig%s received", "hangup");
1023 reopenasap = 1;
1024}
1025
Denys Vlasenko0df289f2018-11-01 14:53:04 +01001026static void logmatch(struct logdir *ld, char* lineptr, int lineptr_len)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001027{
1028 char *s;
1029
1030 ld->match = '+';
1031 ld->matcherr = 'E';
1032 s = ld->inst;
1033 while (s && s[0]) {
1034 switch (s[0]) {
1035 case '+':
1036 case '-':
Denys Vlasenko0df289f2018-11-01 14:53:04 +01001037 if (pmatch(s+1, lineptr, lineptr_len))
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001038 ld->match = s[0];
1039 break;
1040 case 'e':
1041 case 'E':
Denys Vlasenko0df289f2018-11-01 14:53:04 +01001042 if (pmatch(s+1, lineptr, lineptr_len))
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001043 ld->matcherr = s[0];
1044 break;
1045 }
1046 s += strlen(s) + 1;
1047 }
1048}
1049
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001050int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001051int svlogd_main(int argc, char **argv)
1052{
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001053 char *r, *l, *b;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001054 ssize_t stdin_cnt = 0;
1055 int i;
1056 unsigned opt;
1057 unsigned timestamp = 0;
1058
Denis Vlasenko339936b2007-10-05 22:11:06 +00001059 INIT_G();
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001060
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001061 opt = getopt32(argv, "^"
1062 "r:R:l:b:tv" "\0" "tt:vv",
1063 &r, &replace, &l, &b, &timestamp, &verbose
1064 );
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001065 if (opt & 1) { // -r
1066 repl = r[0];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001067 if (!repl || r[1])
1068 bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001069 }
1070 if (opt & 2) if (!repl) repl = '_'; // -R
1071 if (opt & 4) { // -l
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +02001072 linemax = xatou_range(l, 0, COMMON_BUFSIZE-26);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001073 if (linemax == 0)
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +02001074 linemax = COMMON_BUFSIZE-26;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001075 if (linemax < 256)
1076 linemax = 256;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001077 }
Denis Vlasenko64392902007-02-03 00:53:43 +00001078 ////if (opt & 8) { // -b
Denys Vlasenko77832482010-08-12 14:14:45 +02001079 //// buflen = xatoi_positive(b);
Denis Vlasenko64392902007-02-03 00:53:43 +00001080 //// if (buflen == 0) buflen = 1024;
1081 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001082 //if (opt & 0x10) timestamp++; // -t
1083 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001084 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001085 argv += optind;
1086 argc -= optind;
1087
1088 dirn = argc;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001089 if (dirn <= 0)
1090 bb_show_usage();
1091 ////if (buflen <= linemax) bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001092 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +00001093 close_on_exec_on(fdwdir);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001094 dir = xzalloc(dirn * sizeof(dir[0]));
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001095 for (i = 0; i < dirn; ++i) {
1096 dir[i].fddir = -1;
1097 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001098 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenkob9528352007-05-06 01:37:21 +00001099 /*dir[i].ppid = 0;*/
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001100 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001101 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001102 fndir = argv;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001103 /* We cannot set NONBLOCK on fd #0 permanently - this setting
1104 * _isn't_ per-process! It is shared among all other processes
1105 * with the same stdin */
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001106 fl_flag_0 = fcntl(0, F_GETFL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001107
Denis Vlasenko339936b2007-10-05 22:11:06 +00001108 sigemptyset(&blocked_sigset);
1109 sigaddset(&blocked_sigset, SIGTERM);
1110 sigaddset(&blocked_sigset, SIGCHLD);
1111 sigaddset(&blocked_sigset, SIGALRM);
1112 sigaddset(&blocked_sigset, SIGHUP);
1113 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denys Vlasenko5dadd492021-06-05 16:20:05 +02001114 bb_signals_norestart(1 << SIGTERM, sig_term_handler);
1115 bb_signals_norestart(1 << SIGCHLD, sig_child_handler);
1116 bb_signals_norestart(1 << SIGALRM, sig_alarm_handler);
1117 bb_signals_norestart(1 << SIGHUP, sig_hangup_handler);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001118
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001119 /* Without timestamps, we don't have to print each line
1120 * separately, so we can look for _last_ newline, not first,
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001121 * thus batching writes. If filtering is enabled in config,
1122 * logdirs_reopen resets it to memchr.
1123 */
1124 memRchr = (timestamp ? memchr : memrchr);
1125
1126 logdirs_reopen();
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001127
Denis Vlasenko64392902007-02-03 00:53:43 +00001128 setvbuf(stderr, NULL, _IOFBF, linelen);
1129
Denis Vlasenko4e1715f2007-01-28 14:51:32 +00001130 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001131 while (1) {
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001132 char stamp[FMT_PTIME];
1133 char *lineptr;
1134 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001135 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001136 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001137 char ch;
1138
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001139 lineptr = line;
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001140 if (timestamp)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001141 lineptr += 26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001142
1143 /* lineptr[0..linemax-1] - buffer for stdin */
1144 /* (possibly has some unprocessed data from prev loop) */
1145
1146 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001147 np = memRchr(lineptr, '\n', stdin_cnt);
1148 if (!np && !exitasap) {
1149 i = linemax - stdin_cnt; /* avail. bytes at tail */
1150 if (i >= 128) {
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001151 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001152 if (i <= 0) /* EOF or error on stdin */
1153 exitasap = 1;
1154 else {
1155 np = memRchr(lineptr + stdin_cnt, '\n', i);
1156 stdin_cnt += i;
1157 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001158 }
1159 }
1160 if (stdin_cnt <= 0 && exitasap)
1161 break;
1162
1163 /* Search for '\n' (in fact, np already holds the result) */
1164 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001165 if (np) {
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02001166 print_to_nl:
1167 /* NB: starting from here lineptr may point
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001168 * farther out into line[] */
1169 linelen = np - lineptr + 1;
1170 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001171 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1172 ch = lineptr[linelen-1];
1173
Denis Vlasenko64392902007-02-03 00:53:43 +00001174 /* Biggest performance hit was coming from the fact
1175 * that we did not buffer writes. We were reading many lines
1176 * in one read() above, but wrote one line per write().
1177 * We are using stdio to fix that */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001178
1179 /* write out lineptr[0..linelen-1] to each log destination
1180 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001181 printlen = linelen;
1182 printptr = lineptr;
1183 if (timestamp) {
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001184 if (timestamp == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +00001185 fmt_time_bernstein_25(stamp);
Denys Vlasenko79c0d732017-05-15 19:12:09 +02001186 else /* 2+: */
1187 fmt_time_human_30nul(stamp, timestamp == 2 ? '_' : 'T');
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001188 printlen += 26;
1189 printptr -= 26;
1190 memcpy(printptr, stamp, 25);
1191 printptr[25] = ' ';
1192 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001193 for (i = 0; i < dirn; ++i) {
1194 struct logdir *ld = &dir[i];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001195 if (ld->fddir == -1)
1196 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001197 if (ld->inst)
Denys Vlasenko0df289f2018-11-01 14:53:04 +01001198 logmatch(ld, lineptr, linelen);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001199 if (ld->matcherr == 'e') {
1200 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001201 ////full_write(STDERR_FILENO, printptr, printlen);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001202 fwrite(printptr, 1, printlen, stderr);
1203 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001204 if (ld->match != '+')
1205 continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001206 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001207 }
1208
1209 /* If we didn't see '\n' (long input line), */
1210 /* read/write repeatedly until we see it */
1211 while (ch != '\n') {
1212 /* lineptr is emptied now, safe to use as buffer */
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001213 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001214 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001215 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001216 lineptr[0] = ch = '\n';
1217 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001218 stdin_cnt = 1;
1219 } else {
1220 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001221 np = memRchr(lineptr, '\n', stdin_cnt);
1222 if (np)
1223 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001224 ch = lineptr[linelen-1];
1225 }
1226 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1227 for (i = 0; i < dirn; ++i) {
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001228 if (dir[i].fddir == -1)
1229 continue;
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001230 if (dir[i].matcherr == 'e') {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001231 ////full_write(STDERR_FILENO, lineptr, linelen);
Denis Vlasenko64392902007-02-03 00:53:43 +00001232 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001233 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001234 if (dir[i].match != '+')
1235 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001236 buffer_pwrite(i, lineptr, linelen);
1237 }
1238 }
1239
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001240 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001241 if (stdin_cnt > 0) {
1242 lineptr += linelen;
1243 /* If we see another '\n', we don't need to read
1244 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001245 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001246 if (np)
1247 goto print_to_nl;
1248 /* Move unprocessed data to the front of line */
1249 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1250 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01001251 fflush_all();////
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001252 }
1253
1254 for (i = 0; i < dirn; ++i) {
1255 if (dir[i].ppid)
1256 while (!processorstop(&dir[i]))
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001257 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001258 logdir_close(&dir[i]);
1259 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001260 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001261}