blob: 412290ca9e1362319d447d2cc42317651cf7ab4f [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 Vlasenkodc698bb2010-01-09 19:10:49 +0100350 struct tm *ptm;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000351 struct timeval tv;
352
353 gettimeofday(&tv, NULL);
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100354 ptm = gmtime(&tv.tv_sec);
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200355 sprintf(s, "%04u-%02u-%02u%c%02u:%02u:%02u.%06u000",
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100356 (unsigned)(1900 + ptm->tm_year),
357 (unsigned)(ptm->tm_mon + 1),
358 (unsigned)(ptm->tm_mday),
Denys Vlasenko79c0d732017-05-15 19:12:09 +0200359 dt_delim,
Denys Vlasenkodc698bb2010-01-09 19:10:49 +0100360 (unsigned)(ptm->tm_hour),
361 (unsigned)(ptm->tm_min),
362 (unsigned)(ptm->tm_sec),
Denis Vlasenko45946f82007-08-20 17:27:40 +0000363 (unsigned)(tv.tv_usec)
364 );
365 /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
366 /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
367 /* 20 (up to '.' inclusive) + 9 (not including '\0') */
368}
369
370/* NOT terminated! */
371static void fmt_time_bernstein_25(char *s)
372{
373 uint32_t pack[3];
374 struct timeval tv;
375 unsigned sec_hi;
376
377 gettimeofday(&tv, NULL);
378 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
379 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
380 tv.tv_usec *= 1000;
381 /* Network order is big-endian: most significant byte first.
382 * This is exactly what we want here */
383 pack[0] = htonl(sec_hi);
384 pack[1] = htonl(tv.tv_sec);
385 pack[2] = htonl(tv.tv_usec);
386 *s++ = '@';
387 bin2hex(s, (char*)pack, 12);
388}
389
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000390static void processorstart(struct logdir *ld)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000391{
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000392 char sv_ch;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000393 int pid;
394
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000395 if (!ld->processor) return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000396 if (ld->ppid) {
397 warnx("processor already running", ld->name);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000398 return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000399 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000400
401 /* vfork'ed child trashes this byte, save... */
402 sv_ch = ld->fnsave[26];
403
Denys Vlasenko681efe22011-03-08 21:00:36 +0100404 if (!G.shell)
405 G.shell = xstrdup(get_shell_name());
406
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000407 while ((pid = vfork()) == -1)
408 pause2cannot("vfork for processor", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000409 if (!pid) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000410 int fd;
411
412 /* child */
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000413 /* Non-ignored signals revert to SIG_DFL on exec anyway */
414 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000415 + (1 << SIGTERM)
416 + (1 << SIGALRM)
417 + (1 << SIGHUP)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000418 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000419 sig_unblock(SIGTERM);
420 sig_unblock(SIGALRM);
421 sig_unblock(SIGHUP);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000422
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000423 if (verbose)
424 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
425 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000426 xmove_fd(fd, 0);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000427 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000428 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000429 xmove_fd(fd, 1);
Denys Vlasenko05e86052010-10-13 12:53:27 +0200430 fd = open("state", O_RDONLY|O_NDELAY);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000431 if (fd == -1) {
432 if (errno != ENOENT)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000433 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000434 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000435 fd = xopen("state", O_RDONLY|O_NDELAY);
436 }
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000437 xmove_fd(fd, 4);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000438 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000439 xmove_fd(fd, 5);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000440
Denys Vlasenko681efe22011-03-08 21:00:36 +0100441 execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000442 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000443 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000444 ld->fnsave[26] = sv_ch; /* ...restore */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000445 ld->ppid = pid;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000446}
447
448static unsigned processorstop(struct logdir *ld)
449{
450 char f[28];
451
452 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000453 sig_unblock(SIGHUP);
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000454 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000455 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000456 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000457 ld->ppid = 0;
458 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200459 if (ld->fddir == -1)
460 return 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000461 while (fchdir(ld->fddir) == -1)
462 pause2cannot("change directory, want processor", ld->name);
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200463 if (WEXITSTATUS(wstat) != 0) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000464 warnx("processor failed, restart", ld->name);
465 ld->fnsave[26] = 't';
466 unlink(ld->fnsave);
467 ld->fnsave[26] = 'u';
468 processorstart(ld);
469 while (fchdir(fdwdir) == -1)
470 pause1cannot("change to initial working directory");
471 return ld->processor ? 0 : 1;
472 }
473 ld->fnsave[26] = 't';
474 memcpy(f, ld->fnsave, 26);
475 f[26] = 's';
476 f[27] = '\0';
477 while (rename(ld->fnsave, f) == -1)
478 pause2cannot("rename processed", ld->name);
479 while (chmod(f, 0744) == -1)
480 pause2cannot("set mode of processed", ld->name);
481 ld->fnsave[26] = 'u';
482 if (unlink(ld->fnsave) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000483 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000484 while (rename("newstate", "state") == -1)
485 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000486 if (verbose)
487 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000488 while (fchdir(fdwdir) == -1)
489 pause1cannot("change to initial working directory");
490 return 1;
491}
492
493static void rmoldest(struct logdir *ld)
494{
495 DIR *d;
496 struct dirent *f;
497 char oldest[FMT_PTIME];
498 int n = 0;
499
500 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
501 while (!(d = opendir(".")))
502 pause2cannot("open directory, want rotate", ld->name);
503 errno = 0;
504 while ((f = readdir(d))) {
505 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
506 if (f->d_name[26] == 't') {
507 if (unlink(f->d_name) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000508 warn2("can't unlink processor leftover", f->d_name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000509 } else {
510 ++n;
511 if (strcmp(f->d_name, oldest) < 0)
512 memcpy(oldest, f->d_name, 27);
513 }
514 errno = 0;
515 }
516 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000517 if (errno)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000518 warn2("can't read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000519 closedir(d);
520
521 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000522 if (verbose)
523 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000524 if ((*oldest == '@') && (unlink(oldest) == -1))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000525 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000526 }
527}
528
529static unsigned rotate(struct logdir *ld)
530{
531 struct stat st;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000532 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000533
534 if (ld->fddir == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000535 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000536 return 0;
537 }
538 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000539 while (!processorstop(ld))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000540 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000541
542 while (fchdir(ld->fddir) == -1)
543 pause2cannot("change directory, want rotate", ld->name);
544
545 /* create new filename */
546 ld->fnsave[25] = '.';
547 ld->fnsave[26] = 's';
548 if (ld->processor)
549 ld->fnsave[26] = 'u';
550 ld->fnsave[27] = '\0';
551 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000552 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000553 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000554 stat(ld->fnsave, &st);
555 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000556
Denis Vlasenko45946f82007-08-20 17:27:40 +0000557 now = monotonic_sec();
558 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
559 ld->next_rotate = now + ld->rotate_period;
560 if (LESS(ld->next_rotate, nearest_rotate))
561 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000562 }
563
564 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000565 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000566 pause2cannot("fsync current logfile", ld->name);
567 while (fchmod(ld->fdcur, 0744) == -1)
568 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000569 ////close(ld->fdcur);
570 fclose(ld->filecur);
571
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000572 if (verbose) {
573 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
574 ld->fnsave, ld->size);
575 }
576 while (rename("current", ld->fnsave) == -1)
577 pause2cannot("rename current", ld->name);
578 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
579 pause2cannot("create new current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200580 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
581 pause2cannot("create new current", ld->name); /* very unlikely */
Denis Vlasenko64392902007-02-03 00:53:43 +0000582 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000583 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000584 ld->size = 0;
585 while (fchmod(ld->fdcur, 0644) == -1)
586 pause2cannot("set mode of current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200587
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000588 rmoldest(ld);
589 processorstart(ld);
590 }
591
592 while (fchdir(fdwdir) == -1)
593 pause1cannot("change to initial working directory");
594 return 1;
595}
596
597static int buffer_pwrite(int n, char *s, unsigned len)
598{
599 int i;
600 struct logdir *ld = &dir[n];
601
602 if (ld->sizemax) {
603 if (ld->size >= ld->sizemax)
604 rotate(ld);
605 if (len > (ld->sizemax - ld->size))
606 len = ld->sizemax - ld->size;
607 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000608 while (1) {
609 ////i = full_write(ld->fdcur, s, len);
610 ////if (i != -1) break;
611 i = fwrite(s, 1, len, ld->filecur);
612 if (i == len) break;
613
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000614 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
615 DIR *d;
616 struct dirent *f;
617 char oldest[FMT_PTIME];
618 int j = 0;
619
620 while (fchdir(ld->fddir) == -1)
621 pause2cannot("change directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100622 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000623 oldest[0] = 'A';
624 oldest[1] = oldest[27] = '\0';
625 while (!(d = opendir(".")))
626 pause2cannot("open directory, want remove old logfile",
Denys Vlasenko60cb48c2013-01-14 15:57:44 +0100627 ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000628 errno = 0;
629 while ((f = readdir(d)))
630 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
631 ++j;
632 if (strcmp(f->d_name, oldest) < 0)
633 memcpy(oldest, f->d_name, 27);
634 }
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000635 if (errno) warn2("can't read directory, want remove old logfile",
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000636 ld->name);
637 closedir(d);
638 errno = ENOSPC;
639 if (j > ld->nmin) {
640 if (*oldest == '@') {
641 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
642 ld->name, oldest);
643 errno = 0;
644 if (unlink(oldest) == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000645 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000646 errno = ENOSPC;
647 }
648 while (fchdir(fdwdir) == -1)
649 pause1cannot("change to initial working directory");
650 }
651 }
652 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000653 if (errno)
654 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000655 }
656
657 ld->size += i;
658 if (ld->sizemax)
659 if (s[i-1] == '\n')
660 if (ld->size >= (ld->sizemax - linemax))
661 rotate(ld);
662 return i;
663}
664
665static void logdir_close(struct logdir *ld)
666{
667 if (ld->fddir == -1)
668 return;
669 if (verbose)
670 bb_error_msg(INFO"close: %s", ld->name);
671 close(ld->fddir);
672 ld->fddir = -1;
673 if (ld->fdcur == -1)
674 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000675 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000676 pause2cannot("fsync current logfile", ld->name);
677 while (fchmod(ld->fdcur, 0744) == -1)
678 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000679 ////close(ld->fdcur);
680 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000681 ld->fdcur = -1;
682 if (ld->fdlock == -1)
683 return; /* impossible */
684 close(ld->fdlock);
685 ld->fdlock = -1;
686 free(ld->processor);
687 ld->processor = NULL;
688}
689
Denys Vlasenkoa7bb3c12009-10-08 12:28:08 +0200690static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000691{
692 char buf[128];
Denis Vlasenko45946f82007-08-20 17:27:40 +0000693 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000694 char *new, *s, *np;
695 int i;
696 struct stat st;
697
Denis Vlasenko45946f82007-08-20 17:27:40 +0000698 now = monotonic_sec();
699
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000700 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
701 if (ld->fddir == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000702 warn2("can't open log directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000703 return 0;
704 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000705 close_on_exec_on(ld->fddir);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000706 if (fchdir(ld->fddir) == -1) {
707 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000708 warn2("can't change directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000709 return 0;
710 }
711 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
712 if ((ld->fdlock == -1)
Denys Vlasenko05e86052010-10-13 12:53:27 +0200713 || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000714 ) {
715 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000716 warn2("can't lock directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000717 while (fchdir(fdwdir) == -1)
718 pause1cannot("change to initial working directory");
719 return 0;
720 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000721 close_on_exec_on(ld->fdlock);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000722
723 ld->size = 0;
724 ld->sizemax = 1000000;
725 ld->nmax = ld->nmin = 10;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000726 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000727 ld->name = (char*)fn;
728 ld->ppid = 0;
729 ld->match = '+';
730 free(ld->inst); ld->inst = NULL;
731 free(ld->processor); ld->processor = NULL;
732
733 /* read config */
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200734 i = open_read_close("config", buf, sizeof(buf) - 1);
Denis Vlasenkof223efb2007-08-03 10:58:12 +0000735 if (i < 0 && errno != ENOENT)
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000736 bb_perror_msg(WARNING"%s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000737 if (i > 0) {
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200738 buf[i] = '\0';
Denis Vlasenko45946f82007-08-20 17:27:40 +0000739 if (verbose)
740 bb_error_msg(INFO"read: %s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000741 s = buf;
742 while (s) {
743 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000744 if (np)
745 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000746 switch (s[0]) {
747 case '+':
748 case '-':
749 case 'e':
750 case 'E':
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200751 /* Filtering requires one-line buffering,
752 * resetting the "find newline" function
753 * accordingly */
754 memRchr = memchr;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000755 /* Add '\n'-terminated line to ld->inst */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000756 while (1) {
Denys Vlasenko90a99042009-09-06 02:36:23 +0200757 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000758 if (l >= 0 && new)
759 break;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000760 pause_nomem();
761 }
762 free(ld->inst);
763 ld->inst = new;
764 break;
765 case 's': {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000766 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
767 break;
768 }
769 case 'n':
Denys Vlasenko77832482010-08-12 14:14:45 +0200770 ld->nmax = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000771 break;
772 case 'N':
Denys Vlasenko77832482010-08-12 14:14:45 +0200773 ld->nmin = xatoi_positive(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000774 break;
775 case 't': {
776 static const struct suffix_mult mh_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000777 { "m", 60 },
778 { "h", 60*60 },
779 /*{ "d", 24*60*60 },*/
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200780 { "", 0 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000781 };
Denis Vlasenko45946f82007-08-20 17:27:40 +0000782 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
783 if (ld->rotate_period) {
784 ld->next_rotate = now + ld->rotate_period;
785 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
786 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000787 tmaxflag = 1;
788 }
789 break;
790 }
791 case '!':
792 if (s[1]) {
793 free(ld->processor);
Francis Roundsd2c5de02016-09-14 11:53:51 +0000794 ld->processor = wstrdup(&s[1]);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000795 }
796 break;
797 }
798 s = np;
799 }
800 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
801 s = ld->inst;
802 while (s) {
803 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000804 if (np)
805 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000806 s = np;
807 }
808 }
809
810 /* open current */
811 i = stat("current", &st);
812 if (i != -1) {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000813 if (st.st_size && !(st.st_mode & S_IXUSR)) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000814 ld->fnsave[25] = '.';
815 ld->fnsave[26] = 'u';
816 ld->fnsave[27] = '\0';
817 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000818 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000819 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000820 stat(ld->fnsave, &st);
821 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000822 while (rename("current", ld->fnsave) == -1)
823 pause2cannot("rename current", ld->name);
824 rmoldest(ld);
825 i = -1;
826 } else {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000827 /* st.st_size can be not just bigger, but WIDER!
828 * This code is safe: if st.st_size > 4GB, we select
829 * ld->sizemax (because it's "unsigned") */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000830 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
831 }
832 } else {
833 if (errno != ENOENT) {
834 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000835 warn2("can't stat current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000836 while (fchdir(fdwdir) == -1)
837 pause1cannot("change to initial working directory");
838 return 0;
839 }
840 }
841 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
842 pause2cannot("open current", ld->name);
Denys Vlasenkoa7ccdee2009-11-15 23:28:11 +0100843 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
844 pause2cannot("open current", ld->name); ////
Denis Vlasenko64392902007-02-03 00:53:43 +0000845 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
846
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000847 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000848 while (fchmod(ld->fdcur, 0644) == -1)
849 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000850
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000851 if (verbose) {
852 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
853 else bb_error_msg(INFO"new: %s/current", ld->name);
854 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000855
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000856 while (fchdir(fdwdir) == -1)
857 pause1cannot("change to initial working directory");
858 return 1;
859}
860
861static void logdirs_reopen(void)
862{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000863 int l;
864 int ok = 0;
865
866 tmaxflag = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000867 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000868 logdir_close(&dir[l]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000869 if (logdir_open(&dir[l], fndir[l]))
870 ok = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000871 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000872 if (!ok)
873 fatalx("no functional log directories");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000874}
875
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000876/* Will look good in libbb one day */
877static ssize_t ndelay_read(int fd, void *buf, size_t count)
878{
879 if (!(fl_flag_0 & O_NONBLOCK))
880 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
881 count = safe_read(fd, buf, count);
882 if (!(fl_flag_0 & O_NONBLOCK))
883 fcntl(fd, F_SETFL, fl_flag_0);
884 return count;
885}
886
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000887/* Used for reading stdin */
Denis Vlasenko5d61e712007-09-27 10:09:59 +0000888static int buffer_pread(/*int fd, */char *s, unsigned len)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000889{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000890 unsigned now;
891 struct pollfd input;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000892 int i;
893
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200894 input.fd = STDIN_FILENO;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000895 input.events = POLLIN;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000896
Denis Vlasenkob9528352007-05-06 01:37:21 +0000897 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000898 if (rotateasap) {
899 for (i = 0; i < dirn; ++i)
900 rotate(dir + i);
901 rotateasap = 0;
902 }
903 if (exitasap) {
904 if (linecomplete)
905 return 0;
906 len = 1;
907 }
908 if (reopenasap) {
909 logdirs_reopen();
910 reopenasap = 0;
911 }
912 now = monotonic_sec();
913 nearest_rotate = now + (45 * 60 + 45);
914 for (i = 0; i < dirn; ++i) {
915 if (dir[i].rotate_period) {
916 if (LESS(dir[i].next_rotate, now))
917 rotate(dir + i);
918 if (LESS(dir[i].next_rotate, nearest_rotate))
919 nearest_rotate = dir[i].next_rotate;
920 }
921 }
922
Denis Vlasenko339936b2007-10-05 22:11:06 +0000923 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000924 i = nearest_rotate - now;
925 if (i > 1000000)
926 i = 1000000;
927 if (i <= 0)
928 i = 1;
929 poll(&input, 1, i * 1000);
Denis Vlasenko339936b2007-10-05 22:11:06 +0000930 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000931
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000932 i = ndelay_read(STDIN_FILENO, s, len);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000933 if (i >= 0)
934 break;
935 if (errno == EINTR)
936 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000937 if (errno != EAGAIN) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000938 warn("can't read standard input");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000939 break;
940 }
941 /* else: EAGAIN - normal, repeat silently */
Denis Vlasenkob9528352007-05-06 01:37:21 +0000942 } while (!exitasap);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000943
944 if (i > 0) {
945 int cnt;
946 linecomplete = (s[i-1] == '\n');
Denis Vlasenko45946f82007-08-20 17:27:40 +0000947 if (!repl)
948 return i;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000949
950 cnt = i;
951 while (--cnt >= 0) {
952 char ch = *s;
953 if (ch != '\n') {
954 if (ch < 32 || ch > 126)
955 *s = repl;
956 else {
957 int j;
958 for (j = 0; replace[j]; ++j) {
959 if (ch == replace[j]) {
960 *s = repl;
961 break;
962 }
963 }
964 }
965 }
966 s++;
967 }
968 }
969 return i;
970}
971
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000972static void sig_term_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000973{
974 if (verbose)
975 bb_error_msg(INFO"sig%s received", "term");
976 exitasap = 1;
977}
978
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000979static void sig_child_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000980{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000981 pid_t pid;
982 int l;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000983
984 if (verbose)
985 bb_error_msg(INFO"sig%s received", "child");
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000986 while ((pid = wait_any_nohang(&wstat)) > 0) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000987 for (l = 0; l < dirn; ++l) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000988 if (dir[l].ppid == pid) {
989 dir[l].ppid = 0;
990 processorstop(&dir[l]);
991 break;
992 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000993 }
994 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000995}
996
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000997static void sig_alarm_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000998{
999 if (verbose)
1000 bb_error_msg(INFO"sig%s received", "alarm");
1001 rotateasap = 1;
1002}
1003
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001004static void sig_hangup_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001005{
1006 if (verbose)
1007 bb_error_msg(INFO"sig%s received", "hangup");
1008 reopenasap = 1;
1009}
1010
1011static void logmatch(struct logdir *ld)
1012{
1013 char *s;
1014
1015 ld->match = '+';
1016 ld->matcherr = 'E';
1017 s = ld->inst;
1018 while (s && s[0]) {
1019 switch (s[0]) {
1020 case '+':
1021 case '-':
1022 if (pmatch(s+1, line, linelen))
1023 ld->match = s[0];
1024 break;
1025 case 'e':
1026 case 'E':
1027 if (pmatch(s+1, line, linelen))
1028 ld->matcherr = s[0];
1029 break;
1030 }
1031 s += strlen(s) + 1;
1032 }
1033}
1034
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +00001035int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001036int svlogd_main(int argc, char **argv)
1037{
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001038 char *r, *l, *b;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001039 ssize_t stdin_cnt = 0;
1040 int i;
1041 unsigned opt;
1042 unsigned timestamp = 0;
1043
Denis Vlasenko339936b2007-10-05 22:11:06 +00001044 INIT_G();
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001045
Denys Vlasenko22542ec2017-08-08 21:55:02 +02001046 opt = getopt32(argv, "^"
1047 "r:R:l:b:tv" "\0" "tt:vv",
1048 &r, &replace, &l, &b, &timestamp, &verbose
1049 );
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001050 if (opt & 1) { // -r
1051 repl = r[0];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001052 if (!repl || r[1])
1053 bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001054 }
1055 if (opt & 2) if (!repl) repl = '_'; // -R
1056 if (opt & 4) { // -l
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +02001057 linemax = xatou_range(l, 0, COMMON_BUFSIZE-26);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001058 if (linemax == 0)
Denys Vlasenkoe6a2f4c2016-04-21 16:26:30 +02001059 linemax = COMMON_BUFSIZE-26;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001060 if (linemax < 256)
1061 linemax = 256;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001062 }
Denis Vlasenko64392902007-02-03 00:53:43 +00001063 ////if (opt & 8) { // -b
Denys Vlasenko77832482010-08-12 14:14:45 +02001064 //// buflen = xatoi_positive(b);
Denis Vlasenko64392902007-02-03 00:53:43 +00001065 //// if (buflen == 0) buflen = 1024;
1066 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001067 //if (opt & 0x10) timestamp++; // -t
1068 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001069 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001070 argv += optind;
1071 argc -= optind;
1072
1073 dirn = argc;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001074 if (dirn <= 0)
1075 bb_show_usage();
1076 ////if (buflen <= linemax) bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001077 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +00001078 close_on_exec_on(fdwdir);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001079 dir = xzalloc(dirn * sizeof(dir[0]));
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001080 for (i = 0; i < dirn; ++i) {
1081 dir[i].fddir = -1;
1082 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001083 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenkob9528352007-05-06 01:37:21 +00001084 /*dir[i].ppid = 0;*/
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001085 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001086 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001087 fndir = argv;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001088 /* We cannot set NONBLOCK on fd #0 permanently - this setting
1089 * _isn't_ per-process! It is shared among all other processes
1090 * with the same stdin */
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001091 fl_flag_0 = fcntl(0, F_GETFL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001092
Denis Vlasenko339936b2007-10-05 22:11:06 +00001093 sigemptyset(&blocked_sigset);
1094 sigaddset(&blocked_sigset, SIGTERM);
1095 sigaddset(&blocked_sigset, SIGCHLD);
1096 sigaddset(&blocked_sigset, SIGALRM);
1097 sigaddset(&blocked_sigset, SIGHUP);
1098 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +00001099 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
1100 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
1101 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
1102 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001103
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001104 /* Without timestamps, we don't have to print each line
1105 * separately, so we can look for _last_ newline, not first,
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001106 * thus batching writes. If filtering is enabled in config,
1107 * logdirs_reopen resets it to memchr.
1108 */
1109 memRchr = (timestamp ? memchr : memrchr);
1110
1111 logdirs_reopen();
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001112
Denis Vlasenko64392902007-02-03 00:53:43 +00001113 setvbuf(stderr, NULL, _IOFBF, linelen);
1114
Denis Vlasenko4e1715f2007-01-28 14:51:32 +00001115 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001116 while (1) {
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001117 char stamp[FMT_PTIME];
1118 char *lineptr;
1119 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001120 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001121 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001122 char ch;
1123
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001124 lineptr = line;
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001125 if (timestamp)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001126 lineptr += 26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001127
1128 /* lineptr[0..linemax-1] - buffer for stdin */
1129 /* (possibly has some unprocessed data from prev loop) */
1130
1131 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001132 np = memRchr(lineptr, '\n', stdin_cnt);
1133 if (!np && !exitasap) {
1134 i = linemax - stdin_cnt; /* avail. bytes at tail */
1135 if (i >= 128) {
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001136 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001137 if (i <= 0) /* EOF or error on stdin */
1138 exitasap = 1;
1139 else {
1140 np = memRchr(lineptr + stdin_cnt, '\n', i);
1141 stdin_cnt += i;
1142 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001143 }
1144 }
1145 if (stdin_cnt <= 0 && exitasap)
1146 break;
1147
1148 /* Search for '\n' (in fact, np already holds the result) */
1149 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001150 if (np) {
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02001151 print_to_nl:
1152 /* NB: starting from here lineptr may point
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001153 * farther out into line[] */
1154 linelen = np - lineptr + 1;
1155 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001156 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1157 ch = lineptr[linelen-1];
1158
Denis Vlasenko64392902007-02-03 00:53:43 +00001159 /* Biggest performance hit was coming from the fact
1160 * that we did not buffer writes. We were reading many lines
1161 * in one read() above, but wrote one line per write().
1162 * We are using stdio to fix that */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001163
1164 /* write out lineptr[0..linelen-1] to each log destination
1165 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001166 printlen = linelen;
1167 printptr = lineptr;
1168 if (timestamp) {
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001169 if (timestamp == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +00001170 fmt_time_bernstein_25(stamp);
Denys Vlasenko79c0d732017-05-15 19:12:09 +02001171 else /* 2+: */
1172 fmt_time_human_30nul(stamp, timestamp == 2 ? '_' : 'T');
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001173 printlen += 26;
1174 printptr -= 26;
1175 memcpy(printptr, stamp, 25);
1176 printptr[25] = ' ';
1177 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001178 for (i = 0; i < dirn; ++i) {
1179 struct logdir *ld = &dir[i];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001180 if (ld->fddir == -1)
1181 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001182 if (ld->inst)
1183 logmatch(ld);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001184 if (ld->matcherr == 'e') {
1185 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001186 ////full_write(STDERR_FILENO, printptr, printlen);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001187 fwrite(printptr, 1, printlen, stderr);
1188 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001189 if (ld->match != '+')
1190 continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001191 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001192 }
1193
1194 /* If we didn't see '\n' (long input line), */
1195 /* read/write repeatedly until we see it */
1196 while (ch != '\n') {
1197 /* lineptr is emptied now, safe to use as buffer */
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001198 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001199 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001200 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001201 lineptr[0] = ch = '\n';
1202 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001203 stdin_cnt = 1;
1204 } else {
1205 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001206 np = memRchr(lineptr, '\n', stdin_cnt);
1207 if (np)
1208 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001209 ch = lineptr[linelen-1];
1210 }
1211 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1212 for (i = 0; i < dirn; ++i) {
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001213 if (dir[i].fddir == -1)
1214 continue;
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001215 if (dir[i].matcherr == 'e') {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001216 ////full_write(STDERR_FILENO, lineptr, linelen);
Denis Vlasenko64392902007-02-03 00:53:43 +00001217 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001218 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001219 if (dir[i].match != '+')
1220 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001221 buffer_pwrite(i, lineptr, linelen);
1222 }
1223 }
1224
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001225 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001226 if (stdin_cnt > 0) {
1227 lineptr += linelen;
1228 /* If we see another '\n', we don't need to read
1229 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001230 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001231 if (np)
1232 goto print_to_nl;
1233 /* Move unprocessed data to the front of line */
1234 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1235 }
Denys Vlasenko8131eea2009-11-02 14:19:51 +01001236 fflush_all();////
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001237 }
1238
1239 for (i = 0; i < dirn; ++i) {
1240 if (dir[i].ppid)
1241 while (!processorstop(&dir[i]))
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001242 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001243 logdir_close(&dir[i]);
1244 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001245 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001246}