blob: b7fd152629c10748fbb6af8ed5a008f15e7c28cb [file] [log] [blame]
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
15
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
Denis Vlasenkod18f52b2008-03-02 12:53:15 +000028/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
Denis Vlasenko83ea6432006-11-16 02:27:24 +000029/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
Denys Vlasenkoaebb7422009-08-02 00:55:49 +020031/*
32Config files
33
34On startup, and after receiving a HUP signal, svlogd checks for each
35log directory log if the configuration file log/config exists,
36and if so, reads the file line by line and adjusts configuration
37for log as follows:
38
39If the line is empty, or starts with a #, it is ignored. A line
40of the form
41
42ssize
43 sets the maximum file size of current when svlogd should rotate
44 the current log file to size bytes. Default is 1000000.
45 If size is zero, svlogd doesnt rotate log files
46 You should set size to at least (2 * len).
47nnum
48 sets the number of old log files svlogd should maintain to num.
49 If svlogd sees more that num old log files in log after log file
50 rotation, it deletes the oldest one. Default is 10.
51 If num is zero, svlogd doesnt remove old log files.
52Nmin
53 sets the minimum number of old log files svlogd should maintain
54 to min. min must be less than num. If min is set, and svlogd
55 cannot write to current because the filesystem is full,
56 and it sees more than min old log files, it deletes the oldest one.
57ttimeout
58 sets the maximum age of the current log file when svlogd should
59 rotate the current log file to timeout seconds. If current
60 is timeout seconds old, and is not empty, svlogd forces log file rotation.
61!processor
62 tells svlogd to feed each recent log file through processor
63 (see above) on log file rotation. By default log files are not processed.
64ua.b.c.d[:port]
65 tells svlogd to transmit the first len characters of selected
66 log messages to the IP address a.b.c.d, port number port.
67 If port isnt set, the default port for syslog is used (514).
68 len can be set through the -l option, see below. If svlogd
69 has trouble sending udp packets, it writes error messages
70 to the log directory. Attention: logging through udp is unreliable,
71 and should be used in private networks only.
72Ua.b.c.d[:port]
73 is the same as the u line above, but the log messages are no longer
74 written to the log directory, but transmitted through udp only.
75 Error messages from svlogd concerning sending udp packages still go
76 to the log directory.
77pprefix
78 tells svlogd to prefix each line to be written to the log directory,
79 to standard error, or through UDP, with prefix.
80
81If a line starts with a -, +, e, or E, svlogd matches the first len characters
82of each log message against pattern and acts accordingly:
83
84-pattern
85 the log message is deselected.
86+pattern
87 the log message is selected.
88epattern
89 the log message is selected to be printed to standard error.
90Epattern
91 the log message is deselected to be printed to standard error.
92
93Initially each line is selected to be written to log/current. Deselected
94log messages are discarded from log. Initially each line is deselected
95to be written to standard err. Log messages selected for standard error
96are written to standard error.
97
98Pattern Matching
99
100svlogd matches a log message against the string pattern as follows:
101
102pattern is applied to the log message one character by one, starting
103with the first. A character not a star (*) and not a plus (+) matches itself.
104A plus matches the next character in pattern in the log message one
105or more times. A star before the end of pattern matches any string
106in the log message that does not include the next character in pattern.
107A star at the end of pattern matches any string.
108
109Timestamps optionally added by svlogd are not considered part
110of the log message.
111
112An svlogd pattern is not a regular expression. For example consider
113a log message like this
114
1152005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
116
117The following pattern doesnt match
118
119-*pid*
120
121because the first star matches up to the first p in tcpsvd,
122and then the match fails because i is not s. To match this
123log message, you can use a pattern like this instead
124
125-*: *: pid *
126*/
127
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000128#include <sys/poll.h>
129#include <sys/file.h>
Denis Vlasenkob6adbf12007-05-26 19:00:18 +0000130#include "libbb.h"
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000131#include "runit_lib.h"
132
Denis Vlasenko45946f82007-08-20 17:27:40 +0000133#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
134
135#define FMT_PTIME 30
136
Denis Vlasenko339936b2007-10-05 22:11:06 +0000137struct logdir {
Denis Vlasenko64392902007-02-03 00:53:43 +0000138 ////char *btmp;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000139 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
140 char *inst;
141 char *processor;
142 char *name;
143 unsigned size;
144 unsigned sizemax;
145 unsigned nmax;
146 unsigned nmin;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000147 unsigned rotate_period;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000148 int ppid;
149 int fddir;
150 int fdcur;
Denis Vlasenko64392902007-02-03 00:53:43 +0000151 FILE* filecur; ////
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000152 int fdlock;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000153 unsigned next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000154 char fnsave[FMT_PTIME];
155 char match;
156 char matcherr;
Denis Vlasenko339936b2007-10-05 22:11:06 +0000157};
158
159
160struct globals {
161 struct logdir *dir;
162 unsigned verbose;
163 int linemax;
164 ////int buflen;
165 int linelen;
166
167 int fdwdir;
168 char **fndir;
169 int wstat;
170 unsigned nearest_rotate;
171
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200172 void* (*memRchr)(const void *, int, size_t);
173
Denis Vlasenko339936b2007-10-05 22:11:06 +0000174 smallint exitasap;
175 smallint rotateasap;
176 smallint reopenasap;
177 smallint linecomplete;
178 smallint tmaxflag;
179
180 char repl;
181 const char *replace;
182 int fl_flag_0;
183 unsigned dirn;
184
185 sigset_t blocked_sigset;
186};
187#define G (*(struct globals*)ptr_to_globals)
188#define dir (G.dir )
189#define verbose (G.verbose )
190#define linemax (G.linemax )
191#define buflen (G.buflen )
192#define linelen (G.linelen )
193#define fndir (G.fndir )
194#define fdwdir (G.fdwdir )
195#define wstat (G.wstat )
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200196#define memRchr (G.memRchr )
Denis Vlasenko339936b2007-10-05 22:11:06 +0000197#define nearest_rotate (G.nearest_rotate)
198#define exitasap (G.exitasap )
199#define rotateasap (G.rotateasap )
200#define reopenasap (G.reopenasap )
201#define linecomplete (G.linecomplete )
202#define tmaxflag (G.tmaxflag )
203#define repl (G.repl )
204#define replace (G.replace )
205#define blocked_sigset (G.blocked_sigset)
206#define fl_flag_0 (G.fl_flag_0 )
207#define dirn (G.dirn )
208#define INIT_G() do { \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000209 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
Denis Vlasenko339936b2007-10-05 22:11:06 +0000210 linemax = 1000; \
211 /*buflen = 1024;*/ \
212 linecomplete = 1; \
213 replace = ""; \
214} while (0)
215
216#define line bb_common_bufsiz1
217
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000218
219#define FATAL "fatal: "
220#define WARNING "warning: "
221#define PAUSE "pausing: "
222#define INFO "info: "
223
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000224static void fatalx(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000225{
226 bb_error_msg_and_die(FATAL"%s", m0);
227}
Denis Vlasenkoac678ec2007-04-16 22:32:04 +0000228static void warn(const char *m0)
229{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000230 bb_perror_msg(WARNING"%s", m0);
231}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000232static void warn2(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000233{
234 bb_perror_msg(WARNING"%s: %s", m0, m1);
235}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000236static void warnx(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000237{
238 bb_error_msg(WARNING"%s: %s", m0, m1);
239}
240static void pause_nomem(void)
241{
Denis Vlasenko8c783952007-01-27 22:21:52 +0000242 bb_error_msg(PAUSE"out of memory");
243 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000244}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000245static void pause1cannot(const char *m0)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000246{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000247 bb_perror_msg(PAUSE"can't %s", m0);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000248 sleep(3);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000249}
Denis Vlasenkoab2aea42007-01-29 22:51:58 +0000250static void pause2cannot(const char *m0, const char *m1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000251{
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000252 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000253 sleep(3);
254}
255
256static char* wstrdup(const char *str)
257{
258 char *s;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000259 while (!(s = strdup(str)))
260 pause_nomem();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000261 return s;
262}
263
Denis Vlasenko45946f82007-08-20 17:27:40 +0000264/*** ex fmt_ptime.[ch] ***/
265
266/* NUL terminated */
267static void fmt_time_human_30nul(char *s)
268{
269 struct tm *t;
270 struct timeval tv;
271
272 gettimeofday(&tv, NULL);
273 t = gmtime(&(tv.tv_sec));
274 sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
275 (unsigned)(1900 + t->tm_year),
276 (unsigned)(t->tm_mon + 1),
277 (unsigned)(t->tm_mday),
278 (unsigned)(t->tm_hour),
279 (unsigned)(t->tm_min),
280 (unsigned)(t->tm_sec),
281 (unsigned)(tv.tv_usec)
282 );
283 /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
284 /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
285 /* 20 (up to '.' inclusive) + 9 (not including '\0') */
286}
287
288/* NOT terminated! */
289static void fmt_time_bernstein_25(char *s)
290{
291 uint32_t pack[3];
292 struct timeval tv;
293 unsigned sec_hi;
294
295 gettimeofday(&tv, NULL);
296 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
297 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
298 tv.tv_usec *= 1000;
299 /* Network order is big-endian: most significant byte first.
300 * This is exactly what we want here */
301 pack[0] = htonl(sec_hi);
302 pack[1] = htonl(tv.tv_sec);
303 pack[2] = htonl(tv.tv_usec);
304 *s++ = '@';
305 bin2hex(s, (char*)pack, 12);
306}
307
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000308static void processorstart(struct logdir *ld)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000309{
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000310 char sv_ch;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000311 int pid;
312
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000313 if (!ld->processor) return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000314 if (ld->ppid) {
315 warnx("processor already running", ld->name);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000316 return;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000317 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000318
319 /* vfork'ed child trashes this byte, save... */
320 sv_ch = ld->fnsave[26];
321
322 while ((pid = vfork()) == -1)
323 pause2cannot("vfork for processor", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000324 if (!pid) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000325 int fd;
326
327 /* child */
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000328 /* Non-ignored signals revert to SIG_DFL on exec anyway */
329 /*bb_signals(0
Denis Vlasenko25591c32008-02-16 22:58:56 +0000330 + (1 << SIGTERM)
331 + (1 << SIGALRM)
332 + (1 << SIGHUP)
Denis Vlasenko3fa36e22008-11-09 00:15:11 +0000333 , SIG_DFL);*/
Denis Vlasenko8c783952007-01-27 22:21:52 +0000334 sig_unblock(SIGTERM);
335 sig_unblock(SIGALRM);
336 sig_unblock(SIGHUP);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000337
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000338 if (verbose)
339 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
340 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000341 xmove_fd(fd, 0);
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000342 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000343 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000344 xmove_fd(fd, 1);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000345 fd = open_read("state");
346 if (fd == -1) {
347 if (errno != ENOENT)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000348 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000349 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000350 fd = xopen("state", O_RDONLY|O_NDELAY);
351 }
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000352 xmove_fd(fd, 4);
Denis Vlasenkocf749bc2006-11-26 15:45:17 +0000353 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
Denis Vlasenkocad04ef2007-03-25 23:21:05 +0000354 xmove_fd(fd, 5);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000355
Denis Vlasenko8c783952007-01-27 22:21:52 +0000356// getenv("SHELL")?
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200357 execl("/bin/sh", "/bin/sh" + 5, "-c", ld->processor, (char*) NULL);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000358 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000359 }
Denis Vlasenko4ee7cd42008-03-17 09:13:22 +0000360 ld->fnsave[26] = sv_ch; /* ...restore */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000361 ld->ppid = pid;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000362}
363
364static unsigned processorstop(struct logdir *ld)
365{
366 char f[28];
367
368 if (ld->ppid) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000369 sig_unblock(SIGHUP);
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000370 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000371 pause2cannot("wait for processor", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000372 sig_block(SIGHUP);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000373 ld->ppid = 0;
374 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200375 if (ld->fddir == -1)
376 return 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000377 while (fchdir(ld->fddir) == -1)
378 pause2cannot("change directory, want processor", ld->name);
Denys Vlasenko8f24f982009-06-07 16:02:00 +0200379 if (WEXITSTATUS(wstat) != 0) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000380 warnx("processor failed, restart", ld->name);
381 ld->fnsave[26] = 't';
382 unlink(ld->fnsave);
383 ld->fnsave[26] = 'u';
384 processorstart(ld);
385 while (fchdir(fdwdir) == -1)
386 pause1cannot("change to initial working directory");
387 return ld->processor ? 0 : 1;
388 }
389 ld->fnsave[26] = 't';
390 memcpy(f, ld->fnsave, 26);
391 f[26] = 's';
392 f[27] = '\0';
393 while (rename(ld->fnsave, f) == -1)
394 pause2cannot("rename processed", ld->name);
395 while (chmod(f, 0744) == -1)
396 pause2cannot("set mode of processed", ld->name);
397 ld->fnsave[26] = 'u';
398 if (unlink(ld->fnsave) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000399 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000400 while (rename("newstate", "state") == -1)
401 pause2cannot("rename state", ld->name);
Denis Vlasenko8c783952007-01-27 22:21:52 +0000402 if (verbose)
403 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000404 while (fchdir(fdwdir) == -1)
405 pause1cannot("change to initial working directory");
406 return 1;
407}
408
409static void rmoldest(struct logdir *ld)
410{
411 DIR *d;
412 struct dirent *f;
413 char oldest[FMT_PTIME];
414 int n = 0;
415
416 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
417 while (!(d = opendir(".")))
418 pause2cannot("open directory, want rotate", ld->name);
419 errno = 0;
420 while ((f = readdir(d))) {
421 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
422 if (f->d_name[26] == 't') {
423 if (unlink(f->d_name) == -1)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000424 warn2("can't unlink processor leftover", f->d_name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000425 } else {
426 ++n;
427 if (strcmp(f->d_name, oldest) < 0)
428 memcpy(oldest, f->d_name, 27);
429 }
430 errno = 0;
431 }
432 }
Denis Vlasenko8c783952007-01-27 22:21:52 +0000433 if (errno)
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000434 warn2("can't read directory", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000435 closedir(d);
436
437 if (ld->nmax && (n > ld->nmax)) {
Denis Vlasenko8c783952007-01-27 22:21:52 +0000438 if (verbose)
439 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000440 if ((*oldest == '@') && (unlink(oldest) == -1))
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000441 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000442 }
443}
444
445static unsigned rotate(struct logdir *ld)
446{
447 struct stat st;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000448 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000449
450 if (ld->fddir == -1) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000451 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000452 return 0;
453 }
454 if (ld->ppid)
Denis Vlasenkobf0a2012006-12-26 10:42:51 +0000455 while (!processorstop(ld))
Denis Vlasenko45946f82007-08-20 17:27:40 +0000456 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000457
458 while (fchdir(ld->fddir) == -1)
459 pause2cannot("change directory, want rotate", ld->name);
460
461 /* create new filename */
462 ld->fnsave[25] = '.';
463 ld->fnsave[26] = 's';
464 if (ld->processor)
465 ld->fnsave[26] = 'u';
466 ld->fnsave[27] = '\0';
467 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000468 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000469 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000470 stat(ld->fnsave, &st);
471 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000472
Denis Vlasenko45946f82007-08-20 17:27:40 +0000473 now = monotonic_sec();
474 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
475 ld->next_rotate = now + ld->rotate_period;
476 if (LESS(ld->next_rotate, nearest_rotate))
477 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000478 }
479
480 if (ld->size > 0) {
Denis Vlasenko64392902007-02-03 00:53:43 +0000481 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000482 pause2cannot("fsync current logfile", ld->name);
483 while (fchmod(ld->fdcur, 0744) == -1)
484 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000485 ////close(ld->fdcur);
486 fclose(ld->filecur);
487
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000488 if (verbose) {
489 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
490 ld->fnsave, ld->size);
491 }
492 while (rename("current", ld->fnsave) == -1)
493 pause2cannot("rename current", ld->name);
494 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
495 pause2cannot("create new current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200496 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
497 pause2cannot("create new current", ld->name); /* very unlikely */
Denis Vlasenko64392902007-02-03 00:53:43 +0000498 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000499 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000500 ld->size = 0;
501 while (fchmod(ld->fdcur, 0644) == -1)
502 pause2cannot("set mode of current", ld->name);
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200503
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000504 rmoldest(ld);
505 processorstart(ld);
506 }
507
508 while (fchdir(fdwdir) == -1)
509 pause1cannot("change to initial working directory");
510 return 1;
511}
512
513static int buffer_pwrite(int n, char *s, unsigned len)
514{
515 int i;
516 struct logdir *ld = &dir[n];
517
518 if (ld->sizemax) {
519 if (ld->size >= ld->sizemax)
520 rotate(ld);
521 if (len > (ld->sizemax - ld->size))
522 len = ld->sizemax - ld->size;
523 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000524 while (1) {
525 ////i = full_write(ld->fdcur, s, len);
526 ////if (i != -1) break;
527 i = fwrite(s, 1, len, ld->filecur);
528 if (i == len) break;
529
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000530 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
531 DIR *d;
532 struct dirent *f;
533 char oldest[FMT_PTIME];
534 int j = 0;
535
536 while (fchdir(ld->fddir) == -1)
537 pause2cannot("change directory, want remove old logfile",
538 ld->name);
539 oldest[0] = 'A';
540 oldest[1] = oldest[27] = '\0';
541 while (!(d = opendir(".")))
542 pause2cannot("open directory, want remove old logfile",
543 ld->name);
544 errno = 0;
545 while ((f = readdir(d)))
546 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
547 ++j;
548 if (strcmp(f->d_name, oldest) < 0)
549 memcpy(oldest, f->d_name, 27);
550 }
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000551 if (errno) warn2("can't read directory, want remove old logfile",
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000552 ld->name);
553 closedir(d);
554 errno = ENOSPC;
555 if (j > ld->nmin) {
556 if (*oldest == '@') {
557 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
558 ld->name, oldest);
559 errno = 0;
560 if (unlink(oldest) == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000561 warn2("can't unlink oldest logfile", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000562 errno = ENOSPC;
563 }
564 while (fchdir(fdwdir) == -1)
565 pause1cannot("change to initial working directory");
566 }
567 }
568 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000569 if (errno)
570 pause2cannot("write to current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000571 }
572
573 ld->size += i;
574 if (ld->sizemax)
575 if (s[i-1] == '\n')
576 if (ld->size >= (ld->sizemax - linemax))
577 rotate(ld);
578 return i;
579}
580
581static void logdir_close(struct logdir *ld)
582{
583 if (ld->fddir == -1)
584 return;
585 if (verbose)
586 bb_error_msg(INFO"close: %s", ld->name);
587 close(ld->fddir);
588 ld->fddir = -1;
589 if (ld->fdcur == -1)
590 return; /* impossible */
Denis Vlasenko64392902007-02-03 00:53:43 +0000591 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000592 pause2cannot("fsync current logfile", ld->name);
593 while (fchmod(ld->fdcur, 0744) == -1)
594 pause2cannot("set mode of current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000595 ////close(ld->fdcur);
596 fclose(ld->filecur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000597 ld->fdcur = -1;
598 if (ld->fdlock == -1)
599 return; /* impossible */
600 close(ld->fdlock);
601 ld->fdlock = -1;
602 free(ld->processor);
603 ld->processor = NULL;
604}
605
Denys Vlasenkoa7bb3c12009-10-08 12:28:08 +0200606static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000607{
608 char buf[128];
Denis Vlasenko45946f82007-08-20 17:27:40 +0000609 unsigned now;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000610 char *new, *s, *np;
611 int i;
612 struct stat st;
613
Denis Vlasenko45946f82007-08-20 17:27:40 +0000614 now = monotonic_sec();
615
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000616 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
617 if (ld->fddir == -1) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000618 warn2("can't open log directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000619 return 0;
620 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000621 close_on_exec_on(ld->fddir);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000622 if (fchdir(ld->fddir) == -1) {
623 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000624 warn2("can't change directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000625 return 0;
626 }
627 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
628 if ((ld->fdlock == -1)
629 || (lock_exnb(ld->fdlock) == -1)
630 ) {
631 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000632 warn2("can't lock directory", (char*)fn);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000633 while (fchdir(fdwdir) == -1)
634 pause1cannot("change to initial working directory");
635 return 0;
636 }
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000637 close_on_exec_on(ld->fdlock);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000638
639 ld->size = 0;
640 ld->sizemax = 1000000;
641 ld->nmax = ld->nmin = 10;
Denis Vlasenko45946f82007-08-20 17:27:40 +0000642 ld->rotate_period = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000643 ld->name = (char*)fn;
644 ld->ppid = 0;
645 ld->match = '+';
646 free(ld->inst); ld->inst = NULL;
647 free(ld->processor); ld->processor = NULL;
648
649 /* read config */
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200650 i = open_read_close("config", buf, sizeof(buf) - 1);
Denis Vlasenkof223efb2007-08-03 10:58:12 +0000651 if (i < 0 && errno != ENOENT)
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000652 bb_perror_msg(WARNING"%s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000653 if (i > 0) {
Denys Vlasenkoc5d07fb2009-07-03 18:31:23 +0200654 buf[i] = '\0';
Denis Vlasenko45946f82007-08-20 17:27:40 +0000655 if (verbose)
656 bb_error_msg(INFO"read: %s/config", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000657 s = buf;
658 while (s) {
659 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000660 if (np)
661 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000662 switch (s[0]) {
663 case '+':
664 case '-':
665 case 'e':
666 case 'E':
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200667 /* Filtering requires one-line buffering,
668 * resetting the "find newline" function
669 * accordingly */
670 memRchr = memchr;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000671 /* Add '\n'-terminated line to ld->inst */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000672 while (1) {
Denys Vlasenko90a99042009-09-06 02:36:23 +0200673 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000674 if (l >= 0 && new)
675 break;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000676 pause_nomem();
677 }
678 free(ld->inst);
679 ld->inst = new;
680 break;
681 case 's': {
682 static const struct suffix_mult km_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000683 { "k", 1024 },
684 { "m", 1024*1024 },
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200685 { "", 0 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000686 };
687 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
688 break;
689 }
690 case 'n':
691 ld->nmax = xatoi_u(&s[1]);
692 break;
693 case 'N':
694 ld->nmin = xatoi_u(&s[1]);
695 break;
696 case 't': {
697 static const struct suffix_mult mh_suffixes[] = {
Denis Vlasenkof8689632007-07-27 15:06:25 +0000698 { "m", 60 },
699 { "h", 60*60 },
700 /*{ "d", 24*60*60 },*/
Denys Vlasenko043b1e52009-09-06 12:47:55 +0200701 { "", 0 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000702 };
Denis Vlasenko45946f82007-08-20 17:27:40 +0000703 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
704 if (ld->rotate_period) {
705 ld->next_rotate = now + ld->rotate_period;
706 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
707 nearest_rotate = ld->next_rotate;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000708 tmaxflag = 1;
709 }
710 break;
711 }
712 case '!':
713 if (s[1]) {
714 free(ld->processor);
715 ld->processor = wstrdup(s);
716 }
717 break;
718 }
719 s = np;
720 }
721 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
722 s = ld->inst;
723 while (s) {
724 np = strchr(s, '\n');
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000725 if (np)
726 *np++ = '\0';
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000727 s = np;
728 }
729 }
730
731 /* open current */
732 i = stat("current", &st);
733 if (i != -1) {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000734 if (st.st_size && !(st.st_mode & S_IXUSR)) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000735 ld->fnsave[25] = '.';
736 ld->fnsave[26] = 'u';
737 ld->fnsave[27] = '\0';
738 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000739 fmt_time_bernstein_25(ld->fnsave);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000740 errno = 0;
Denis Vlasenko8c783952007-01-27 22:21:52 +0000741 stat(ld->fnsave, &st);
742 } while (errno != ENOENT);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000743 while (rename("current", ld->fnsave) == -1)
744 pause2cannot("rename current", ld->name);
745 rmoldest(ld);
746 i = -1;
747 } else {
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000748 /* st.st_size can be not just bigger, but WIDER!
749 * This code is safe: if st.st_size > 4GB, we select
750 * ld->sizemax (because it's "unsigned") */
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000751 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
752 }
753 } else {
754 if (errno != ENOENT) {
755 logdir_close(ld);
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000756 warn2("can't stat current", ld->name);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000757 while (fchdir(fdwdir) == -1)
758 pause1cannot("change to initial working directory");
759 return 0;
760 }
761 }
762 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
763 pause2cannot("open current", ld->name);
Denis Vlasenko64392902007-02-03 00:53:43 +0000764 /* we presume this cannot fail */
765 ld->filecur = fdopen(ld->fdcur, "a"); ////
766 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
767
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000768 close_on_exec_on(ld->fdcur);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000769 while (fchmod(ld->fdcur, 0644) == -1)
770 pause2cannot("set mode of current", ld->name);
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000771
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000772 if (verbose) {
773 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
774 else bb_error_msg(INFO"new: %s/current", ld->name);
775 }
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000776
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000777 while (fchdir(fdwdir) == -1)
778 pause1cannot("change to initial working directory");
779 return 1;
780}
781
782static void logdirs_reopen(void)
783{
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000784 int l;
785 int ok = 0;
786
787 tmaxflag = 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000788 for (l = 0; l < dirn; ++l) {
Denis Vlasenkof7996f32007-01-11 17:20:00 +0000789 logdir_close(&dir[l]);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000790 if (logdir_open(&dir[l], fndir[l]))
791 ok = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000792 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000793 if (!ok)
794 fatalx("no functional log directories");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000795}
796
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +0000797/* Will look good in libbb one day */
798static ssize_t ndelay_read(int fd, void *buf, size_t count)
799{
800 if (!(fl_flag_0 & O_NONBLOCK))
801 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
802 count = safe_read(fd, buf, count);
803 if (!(fl_flag_0 & O_NONBLOCK))
804 fcntl(fd, F_SETFL, fl_flag_0);
805 return count;
806}
807
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000808/* Used for reading stdin */
Denis Vlasenko5d61e712007-09-27 10:09:59 +0000809static int buffer_pread(/*int fd, */char *s, unsigned len)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000810{
Denis Vlasenko45946f82007-08-20 17:27:40 +0000811 unsigned now;
812 struct pollfd input;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000813 int i;
814
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200815 input.fd = STDIN_FILENO;
Denis Vlasenko72b6a652007-08-21 11:18:25 +0000816 input.events = POLLIN;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000817
Denis Vlasenkob9528352007-05-06 01:37:21 +0000818 do {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000819 if (rotateasap) {
820 for (i = 0; i < dirn; ++i)
821 rotate(dir + i);
822 rotateasap = 0;
823 }
824 if (exitasap) {
825 if (linecomplete)
826 return 0;
827 len = 1;
828 }
829 if (reopenasap) {
830 logdirs_reopen();
831 reopenasap = 0;
832 }
833 now = monotonic_sec();
834 nearest_rotate = now + (45 * 60 + 45);
835 for (i = 0; i < dirn; ++i) {
836 if (dir[i].rotate_period) {
837 if (LESS(dir[i].next_rotate, now))
838 rotate(dir + i);
839 if (LESS(dir[i].next_rotate, nearest_rotate))
840 nearest_rotate = dir[i].next_rotate;
841 }
842 }
843
Denis Vlasenko339936b2007-10-05 22:11:06 +0000844 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000845 i = nearest_rotate - now;
846 if (i > 1000000)
847 i = 1000000;
848 if (i <= 0)
849 i = 1;
850 poll(&input, 1, i * 1000);
Denis Vlasenko339936b2007-10-05 22:11:06 +0000851 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000852
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +0000853 i = ndelay_read(STDIN_FILENO, s, len);
Denis Vlasenko45946f82007-08-20 17:27:40 +0000854 if (i >= 0)
855 break;
856 if (errno == EINTR)
857 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000858 if (errno != EAGAIN) {
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +0000859 warn("can't read standard input");
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000860 break;
861 }
862 /* else: EAGAIN - normal, repeat silently */
Denis Vlasenkob9528352007-05-06 01:37:21 +0000863 } while (!exitasap);
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000864
865 if (i > 0) {
866 int cnt;
867 linecomplete = (s[i-1] == '\n');
Denis Vlasenko45946f82007-08-20 17:27:40 +0000868 if (!repl)
869 return i;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000870
871 cnt = i;
872 while (--cnt >= 0) {
873 char ch = *s;
874 if (ch != '\n') {
875 if (ch < 32 || ch > 126)
876 *s = repl;
877 else {
878 int j;
879 for (j = 0; replace[j]; ++j) {
880 if (ch == replace[j]) {
881 *s = repl;
882 break;
883 }
884 }
885 }
886 }
887 s++;
888 }
889 }
890 return i;
891}
892
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000893static void sig_term_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000894{
895 if (verbose)
896 bb_error_msg(INFO"sig%s received", "term");
897 exitasap = 1;
898}
899
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000900static void sig_child_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000901{
Denis Vlasenko3854c5d2008-11-06 22:39:57 +0000902 pid_t pid;
903 int l;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000904
905 if (verbose)
906 bb_error_msg(INFO"sig%s received", "child");
Denis Vlasenkofb0eba72008-01-02 19:55:04 +0000907 while ((pid = wait_any_nohang(&wstat)) > 0) {
Denis Vlasenko45946f82007-08-20 17:27:40 +0000908 for (l = 0; l < dirn; ++l) {
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000909 if (dir[l].ppid == pid) {
910 dir[l].ppid = 0;
911 processorstop(&dir[l]);
912 break;
913 }
Denis Vlasenko45946f82007-08-20 17:27:40 +0000914 }
915 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000916}
917
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000918static void sig_alarm_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000919{
920 if (verbose)
921 bb_error_msg(INFO"sig%s received", "alarm");
922 rotateasap = 1;
923}
924
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000925static void sig_hangup_handler(int sig_no UNUSED_PARAM)
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000926{
927 if (verbose)
928 bb_error_msg(INFO"sig%s received", "hangup");
929 reopenasap = 1;
930}
931
932static void logmatch(struct logdir *ld)
933{
934 char *s;
935
936 ld->match = '+';
937 ld->matcherr = 'E';
938 s = ld->inst;
939 while (s && s[0]) {
940 switch (s[0]) {
941 case '+':
942 case '-':
943 if (pmatch(s+1, line, linelen))
944 ld->match = s[0];
945 break;
946 case 'e':
947 case 'E':
948 if (pmatch(s+1, line, linelen))
949 ld->matcherr = s[0];
950 break;
951 }
952 s += strlen(s) + 1;
953 }
954}
955
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000956int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000957int svlogd_main(int argc, char **argv)
958{
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200959 char *r, *l, *b;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000960 ssize_t stdin_cnt = 0;
961 int i;
962 unsigned opt;
963 unsigned timestamp = 0;
964
Denis Vlasenko339936b2007-10-05 22:11:06 +0000965 INIT_G();
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000966
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000967 opt_complementary = "tt:vv";
Denis Vlasenkofe7cd642007-08-18 15:32:12 +0000968 opt = getopt32(argv, "r:R:l:b:tv",
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000969 &r, &replace, &l, &b, &timestamp, &verbose);
970 if (opt & 1) { // -r
971 repl = r[0];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200972 if (!repl || r[1])
973 bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000974 }
975 if (opt & 2) if (!repl) repl = '_'; // -R
976 if (opt & 4) { // -l
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000977 linemax = xatou_range(l, 0, BUFSIZ-26);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200978 if (linemax == 0)
979 linemax = BUFSIZ-26;
980 if (linemax < 256)
981 linemax = 256;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000982 }
Denis Vlasenko64392902007-02-03 00:53:43 +0000983 ////if (opt & 8) { // -b
984 //// buflen = xatoi_u(b);
985 //// if (buflen == 0) buflen = 1024;
986 ////}
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000987 //if (opt & 0x10) timestamp++; // -t
988 //if (opt & 0x20) verbose++; // -v
Denis Vlasenkoca549c52007-01-27 22:24:59 +0000989 //if (timestamp > 2) timestamp = 2;
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000990 argv += optind;
991 argc -= optind;
992
993 dirn = argc;
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200994 if (dirn <= 0)
995 bb_show_usage();
996 ////if (buflen <= linemax) bb_show_usage();
Denis Vlasenko83ea6432006-11-16 02:27:24 +0000997 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
Denis Vlasenko96e1b382007-09-30 23:50:48 +0000998 close_on_exec_on(fdwdir);
Denys Vlasenkoaebb7422009-08-02 00:55:49 +0200999 dir = xzalloc(dirn * sizeof(dir[0]));
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001000 for (i = 0; i < dirn; ++i) {
1001 dir[i].fddir = -1;
1002 dir[i].fdcur = -1;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001003 ////dir[i].btmp = xmalloc(buflen);
Denis Vlasenkob9528352007-05-06 01:37:21 +00001004 /*dir[i].ppid = 0;*/
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001005 }
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001006 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001007 fndir = argv;
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001008 /* We cannot set NONBLOCK on fd #0 permanently - this setting
1009 * _isn't_ per-process! It is shared among all other processes
1010 * with the same stdin */
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001011 fl_flag_0 = fcntl(0, F_GETFL);
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001012
Denis Vlasenko339936b2007-10-05 22:11:06 +00001013 sigemptyset(&blocked_sigset);
1014 sigaddset(&blocked_sigset, SIGTERM);
1015 sigaddset(&blocked_sigset, SIGCHLD);
1016 sigaddset(&blocked_sigset, SIGALRM);
1017 sigaddset(&blocked_sigset, SIGHUP);
1018 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
Denis Vlasenkocab28aa2009-01-31 01:02:07 +00001019 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
1020 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
1021 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
1022 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001023
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001024 /* Without timestamps, we don't have to print each line
1025 * separately, so we can look for _last_ newline, not first,
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001026 * thus batching writes. If filtering is enabled in config,
1027 * logdirs_reopen resets it to memchr.
1028 */
1029 memRchr = (timestamp ? memchr : memrchr);
1030
1031 logdirs_reopen();
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001032
Denis Vlasenko64392902007-02-03 00:53:43 +00001033 setvbuf(stderr, NULL, _IOFBF, linelen);
1034
Denis Vlasenko4e1715f2007-01-28 14:51:32 +00001035 /* Each iteration processes one or more lines */
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001036 while (1) {
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001037 char stamp[FMT_PTIME];
1038 char *lineptr;
1039 char *printptr;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001040 char *np;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001041 int printlen;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001042 char ch;
1043
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001044 lineptr = line;
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001045 if (timestamp)
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001046 lineptr += 26;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001047
1048 /* lineptr[0..linemax-1] - buffer for stdin */
1049 /* (possibly has some unprocessed data from prev loop) */
1050
1051 /* Refill the buffer if needed */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001052 np = memRchr(lineptr, '\n', stdin_cnt);
1053 if (!np && !exitasap) {
1054 i = linemax - stdin_cnt; /* avail. bytes at tail */
1055 if (i >= 128) {
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001056 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001057 if (i <= 0) /* EOF or error on stdin */
1058 exitasap = 1;
1059 else {
1060 np = memRchr(lineptr + stdin_cnt, '\n', i);
1061 stdin_cnt += i;
1062 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001063 }
1064 }
1065 if (stdin_cnt <= 0 && exitasap)
1066 break;
1067
1068 /* Search for '\n' (in fact, np already holds the result) */
1069 linelen = stdin_cnt;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001070 if (np) {
1071 print_to_nl: /* NB: starting from here lineptr may point
1072 * farther out into line[] */
1073 linelen = np - lineptr + 1;
1074 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001075 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1076 ch = lineptr[linelen-1];
1077
Denis Vlasenko64392902007-02-03 00:53:43 +00001078 /* Biggest performance hit was coming from the fact
1079 * that we did not buffer writes. We were reading many lines
1080 * in one read() above, but wrote one line per write().
1081 * We are using stdio to fix that */
Denis Vlasenko4f8d27f2007-02-03 00:52:17 +00001082
1083 /* write out lineptr[0..linelen-1] to each log destination
1084 * (or lineptr[-26..linelen-1] if timestamping) */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001085 printlen = linelen;
1086 printptr = lineptr;
1087 if (timestamp) {
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001088 if (timestamp == 1)
Denis Vlasenko45946f82007-08-20 17:27:40 +00001089 fmt_time_bernstein_25(stamp);
Denis Vlasenkof223efb2007-08-03 10:58:12 +00001090 else /* 2: */
Denis Vlasenko45946f82007-08-20 17:27:40 +00001091 fmt_time_human_30nul(stamp);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001092 printlen += 26;
1093 printptr -= 26;
1094 memcpy(printptr, stamp, 25);
1095 printptr[25] = ' ';
1096 }
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001097 for (i = 0; i < dirn; ++i) {
1098 struct logdir *ld = &dir[i];
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001099 if (ld->fddir == -1)
1100 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001101 if (ld->inst)
1102 logmatch(ld);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001103 if (ld->matcherr == 'e') {
1104 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001105 ////full_write(STDERR_FILENO, printptr, printlen);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001106 fwrite(printptr, 1, printlen, stderr);
1107 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001108 if (ld->match != '+')
1109 continue;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001110 buffer_pwrite(i, printptr, printlen);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001111 }
1112
1113 /* If we didn't see '\n' (long input line), */
1114 /* read/write repeatedly until we see it */
1115 while (ch != '\n') {
1116 /* lineptr is emptied now, safe to use as buffer */
Denis Vlasenko5d61e712007-09-27 10:09:59 +00001117 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001118 if (stdin_cnt <= 0) { /* EOF or error on stdin */
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001119 exitasap = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001120 lineptr[0] = ch = '\n';
1121 linelen = 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001122 stdin_cnt = 1;
1123 } else {
1124 linelen = stdin_cnt;
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001125 np = memRchr(lineptr, '\n', stdin_cnt);
1126 if (np)
1127 linelen = np - lineptr + 1;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001128 ch = lineptr[linelen-1];
1129 }
1130 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1131 for (i = 0; i < dirn; ++i) {
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001132 if (dir[i].fddir == -1)
1133 continue;
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001134 if (dir[i].matcherr == 'e') {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001135 ////full_write(STDERR_FILENO, lineptr, linelen);
Denis Vlasenko64392902007-02-03 00:53:43 +00001136 fwrite(lineptr, 1, linelen, stderr);
Denis Vlasenko7ab5e3d2007-10-22 15:53:34 +00001137 }
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001138 if (dir[i].match != '+')
1139 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001140 buffer_pwrite(i, lineptr, linelen);
1141 }
1142 }
1143
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001144 stdin_cnt -= linelen;
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001145 if (stdin_cnt > 0) {
1146 lineptr += linelen;
1147 /* If we see another '\n', we don't need to read
1148 * next piece of input: can print what we have */
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001149 np = memRchr(lineptr, '\n', stdin_cnt);
Denis Vlasenkoca549c52007-01-27 22:24:59 +00001150 if (np)
1151 goto print_to_nl;
1152 /* Move unprocessed data to the front of line */
1153 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1154 }
Denis Vlasenko64392902007-02-03 00:53:43 +00001155 fflush(NULL);////
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001156 }
1157
1158 for (i = 0; i < dirn; ++i) {
1159 if (dir[i].ppid)
1160 while (!processorstop(&dir[i]))
Denys Vlasenkoaebb7422009-08-02 00:55:49 +02001161 continue;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001162 logdir_close(&dir[i]);
1163 }
Denis Vlasenkoeeafc1a2007-01-27 23:15:50 +00001164 return 0;
Denis Vlasenko83ea6432006-11-16 02:27:24 +00001165}