Magellan Linux

Contents of /trunk/mkinitrd-magellan/busybox/runit/svlogd.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 984 - (show annotations) (download)
Sun May 30 11:32:42 2010 UTC (13 years, 11 months ago) by niro
File MIME type: text/plain
File size: 30433 byte(s)
-updated to busybox-1.16.1 and enabled blkid/uuid support in default config
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, 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
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31 /*
32 Config files
33
34 On startup, and after receiving a HUP signal, svlogd checks for each
35 log directory log if the configuration file log/config exists,
36 and if so, reads the file line by line and adjusts configuration
37 for log as follows:
38
39 If the line is empty, or starts with a #, it is ignored. A line
40 of the form
41
42 ssize
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).
47 nnum
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.
52 Nmin
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.
57 ttimeout
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.
64 ua.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.
72 Ua.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.
77 pprefix
78 tells svlogd to prefix each line to be written to the log directory,
79 to standard error, or through UDP, with prefix.
80
81 If a line starts with a -, +, e, or E, svlogd matches the first len characters
82 of 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.
88 epattern
89 the log message is selected to be printed to standard error.
90 Epattern
91 the log message is deselected to be printed to standard error.
92
93 Initially each line is selected to be written to log/current. Deselected
94 log messages are discarded from log. Initially each line is deselected
95 to be written to standard err. Log messages selected for standard error
96 are written to standard error.
97
98 Pattern Matching
99
100 svlogd matches a log message against the string pattern as follows:
101
102 pattern is applied to the log message one character by one, starting
103 with the first. A character not a star (*) and not a plus (+) matches itself.
104 A plus matches the next character in pattern in the log message one
105 or more times. A star before the end of pattern matches any string
106 in the log message that does not include the next character in pattern.
107 A star at the end of pattern matches any string.
108
109 Timestamps optionally added by svlogd are not considered part
110 of the log message.
111
112 An svlogd pattern is not a regular expression. For example consider
113 a log message like this
114
115 2005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
116
117 The following pattern doesnt match
118
119 -*pid*
120
121 because the first star matches up to the first p in tcpsvd,
122 and then the match fails because i is not s. To match this
123 log message, you can use a pattern like this instead
124
125 -*: *: pid *
126 */
127
128 #include <sys/poll.h>
129 #include <sys/file.h>
130 #include "libbb.h"
131 #include "runit_lib.h"
132
133 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
134
135 #define FMT_PTIME 30
136
137 struct logdir {
138 ////char *btmp;
139 /* 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;
147 unsigned rotate_period;
148 int ppid;
149 int fddir;
150 int fdcur;
151 FILE* filecur; ////
152 int fdlock;
153 unsigned next_rotate;
154 char fnsave[FMT_PTIME];
155 char match;
156 char matcherr;
157 };
158
159
160 struct 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
172 void* (*memRchr)(const void *, int, size_t);
173
174 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 )
196 #define memRchr (G.memRchr )
197 #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 { \
209 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
210 linemax = 1000; \
211 /*buflen = 1024;*/ \
212 linecomplete = 1; \
213 replace = ""; \
214 } while (0)
215
216 #define line bb_common_bufsiz1
217
218
219 #define FATAL "fatal: "
220 #define WARNING "warning: "
221 #define PAUSE "pausing: "
222 #define INFO "info: "
223
224 static void fatalx(const char *m0)
225 {
226 bb_error_msg_and_die(FATAL"%s", m0);
227 }
228 static void warn(const char *m0)
229 {
230 bb_perror_msg(WARNING"%s", m0);
231 }
232 static void warn2(const char *m0, const char *m1)
233 {
234 bb_perror_msg(WARNING"%s: %s", m0, m1);
235 }
236 static void warnx(const char *m0, const char *m1)
237 {
238 bb_error_msg(WARNING"%s: %s", m0, m1);
239 }
240 static void pause_nomem(void)
241 {
242 bb_error_msg(PAUSE"out of memory");
243 sleep(3);
244 }
245 static void pause1cannot(const char *m0)
246 {
247 bb_perror_msg(PAUSE"can't %s", m0);
248 sleep(3);
249 }
250 static void pause2cannot(const char *m0, const char *m1)
251 {
252 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
253 sleep(3);
254 }
255
256 static char* wstrdup(const char *str)
257 {
258 char *s;
259 while (!(s = strdup(str)))
260 pause_nomem();
261 return s;
262 }
263
264 /*** ex fmt_ptime.[ch] ***/
265
266 /* NUL terminated */
267 static void fmt_time_human_30nul(char *s)
268 {
269 struct tm *ptm;
270 struct timeval tv;
271
272 gettimeofday(&tv, NULL);
273 ptm = gmtime(&tv.tv_sec);
274 sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
275 (unsigned)(1900 + ptm->tm_year),
276 (unsigned)(ptm->tm_mon + 1),
277 (unsigned)(ptm->tm_mday),
278 (unsigned)(ptm->tm_hour),
279 (unsigned)(ptm->tm_min),
280 (unsigned)(ptm->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! */
289 static 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
308 static void processorstart(struct logdir *ld)
309 {
310 char sv_ch;
311 int pid;
312
313 if (!ld->processor) return;
314 if (ld->ppid) {
315 warnx("processor already running", ld->name);
316 return;
317 }
318
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);
324 if (!pid) {
325 int fd;
326
327 /* child */
328 /* Non-ignored signals revert to SIG_DFL on exec anyway */
329 /*bb_signals(0
330 + (1 << SIGTERM)
331 + (1 << SIGALRM)
332 + (1 << SIGHUP)
333 , SIG_DFL);*/
334 sig_unblock(SIGTERM);
335 sig_unblock(SIGALRM);
336 sig_unblock(SIGHUP);
337
338 if (verbose)
339 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
340 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
341 xmove_fd(fd, 0);
342 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
343 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
344 xmove_fd(fd, 1);
345 fd = open_read("state");
346 if (fd == -1) {
347 if (errno != ENOENT)
348 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
349 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
350 fd = xopen("state", O_RDONLY|O_NDELAY);
351 }
352 xmove_fd(fd, 4);
353 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
354 xmove_fd(fd, 5);
355
356 // getenv("SHELL")?
357 execl("/bin/sh", "/bin/sh" + 5, "-c", ld->processor, (char*) NULL);
358 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
359 }
360 ld->fnsave[26] = sv_ch; /* ...restore */
361 ld->ppid = pid;
362 }
363
364 static unsigned processorstop(struct logdir *ld)
365 {
366 char f[28];
367
368 if (ld->ppid) {
369 sig_unblock(SIGHUP);
370 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
371 pause2cannot("wait for processor", ld->name);
372 sig_block(SIGHUP);
373 ld->ppid = 0;
374 }
375 if (ld->fddir == -1)
376 return 1;
377 while (fchdir(ld->fddir) == -1)
378 pause2cannot("change directory, want processor", ld->name);
379 if (WEXITSTATUS(wstat) != 0) {
380 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)
399 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
400 while (rename("newstate", "state") == -1)
401 pause2cannot("rename state", ld->name);
402 if (verbose)
403 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
404 while (fchdir(fdwdir) == -1)
405 pause1cannot("change to initial working directory");
406 return 1;
407 }
408
409 static 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)
424 warn2("can't unlink processor leftover", f->d_name);
425 } else {
426 ++n;
427 if (strcmp(f->d_name, oldest) < 0)
428 memcpy(oldest, f->d_name, 27);
429 }
430 errno = 0;
431 }
432 }
433 if (errno)
434 warn2("can't read directory", ld->name);
435 closedir(d);
436
437 if (ld->nmax && (n > ld->nmax)) {
438 if (verbose)
439 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
440 if ((*oldest == '@') && (unlink(oldest) == -1))
441 warn2("can't unlink oldest logfile", ld->name);
442 }
443 }
444
445 static unsigned rotate(struct logdir *ld)
446 {
447 struct stat st;
448 unsigned now;
449
450 if (ld->fddir == -1) {
451 ld->rotate_period = 0;
452 return 0;
453 }
454 if (ld->ppid)
455 while (!processorstop(ld))
456 continue;
457
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 {
468 fmt_time_bernstein_25(ld->fnsave);
469 errno = 0;
470 stat(ld->fnsave, &st);
471 } while (errno != ENOENT);
472
473 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;
478 }
479
480 if (ld->size > 0) {
481 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
482 pause2cannot("fsync current logfile", ld->name);
483 while (fchmod(ld->fdcur, 0744) == -1)
484 pause2cannot("set mode of current", ld->name);
485 ////close(ld->fdcur);
486 fclose(ld->filecur);
487
488 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);
496 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
497 pause2cannot("create new current", ld->name); /* very unlikely */
498 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
499 close_on_exec_on(ld->fdcur);
500 ld->size = 0;
501 while (fchmod(ld->fdcur, 0644) == -1)
502 pause2cannot("set mode of current", ld->name);
503
504 rmoldest(ld);
505 processorstart(ld);
506 }
507
508 while (fchdir(fdwdir) == -1)
509 pause1cannot("change to initial working directory");
510 return 1;
511 }
512
513 static 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 }
524 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
530 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 }
551 if (errno) warn2("can't read directory, want remove old logfile",
552 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) {
561 warn2("can't unlink oldest logfile", ld->name);
562 errno = ENOSPC;
563 }
564 while (fchdir(fdwdir) == -1)
565 pause1cannot("change to initial working directory");
566 }
567 }
568 }
569 if (errno)
570 pause2cannot("write to current", ld->name);
571 }
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
581 static 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 */
591 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
592 pause2cannot("fsync current logfile", ld->name);
593 while (fchmod(ld->fdcur, 0744) == -1)
594 pause2cannot("set mode of current", ld->name);
595 ////close(ld->fdcur);
596 fclose(ld->filecur);
597 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
606 static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
607 {
608 char buf[128];
609 unsigned now;
610 char *new, *s, *np;
611 int i;
612 struct stat st;
613
614 now = monotonic_sec();
615
616 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
617 if (ld->fddir == -1) {
618 warn2("can't open log directory", (char*)fn);
619 return 0;
620 }
621 close_on_exec_on(ld->fddir);
622 if (fchdir(ld->fddir) == -1) {
623 logdir_close(ld);
624 warn2("can't change directory", (char*)fn);
625 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);
632 warn2("can't lock directory", (char*)fn);
633 while (fchdir(fdwdir) == -1)
634 pause1cannot("change to initial working directory");
635 return 0;
636 }
637 close_on_exec_on(ld->fdlock);
638
639 ld->size = 0;
640 ld->sizemax = 1000000;
641 ld->nmax = ld->nmin = 10;
642 ld->rotate_period = 0;
643 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 */
650 i = open_read_close("config", buf, sizeof(buf) - 1);
651 if (i < 0 && errno != ENOENT)
652 bb_perror_msg(WARNING"%s/config", ld->name);
653 if (i > 0) {
654 buf[i] = '\0';
655 if (verbose)
656 bb_error_msg(INFO"read: %s/config", ld->name);
657 s = buf;
658 while (s) {
659 np = strchr(s, '\n');
660 if (np)
661 *np++ = '\0';
662 switch (s[0]) {
663 case '+':
664 case '-':
665 case 'e':
666 case 'E':
667 /* Filtering requires one-line buffering,
668 * resetting the "find newline" function
669 * accordingly */
670 memRchr = memchr;
671 /* Add '\n'-terminated line to ld->inst */
672 while (1) {
673 int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
674 if (l >= 0 && new)
675 break;
676 pause_nomem();
677 }
678 free(ld->inst);
679 ld->inst = new;
680 break;
681 case 's': {
682 static const struct suffix_mult km_suffixes[] = {
683 { "k", 1024 },
684 { "m", 1024*1024 },
685 { "", 0 }
686 };
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[] = {
698 { "m", 60 },
699 { "h", 60*60 },
700 /*{ "d", 24*60*60 },*/
701 { "", 0 }
702 };
703 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;
708 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');
725 if (np)
726 *np++ = '\0';
727 s = np;
728 }
729 }
730
731 /* open current */
732 i = stat("current", &st);
733 if (i != -1) {
734 if (st.st_size && !(st.st_mode & S_IXUSR)) {
735 ld->fnsave[25] = '.';
736 ld->fnsave[26] = 'u';
737 ld->fnsave[27] = '\0';
738 do {
739 fmt_time_bernstein_25(ld->fnsave);
740 errno = 0;
741 stat(ld->fnsave, &st);
742 } while (errno != ENOENT);
743 while (rename("current", ld->fnsave) == -1)
744 pause2cannot("rename current", ld->name);
745 rmoldest(ld);
746 i = -1;
747 } else {
748 /* 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") */
751 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
752 }
753 } else {
754 if (errno != ENOENT) {
755 logdir_close(ld);
756 warn2("can't stat current", ld->name);
757 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);
764 while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
765 pause2cannot("open current", ld->name); ////
766 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
767
768 close_on_exec_on(ld->fdcur);
769 while (fchmod(ld->fdcur, 0644) == -1)
770 pause2cannot("set mode of current", ld->name);
771
772 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 }
776
777 while (fchdir(fdwdir) == -1)
778 pause1cannot("change to initial working directory");
779 return 1;
780 }
781
782 static void logdirs_reopen(void)
783 {
784 int l;
785 int ok = 0;
786
787 tmaxflag = 0;
788 for (l = 0; l < dirn; ++l) {
789 logdir_close(&dir[l]);
790 if (logdir_open(&dir[l], fndir[l]))
791 ok = 1;
792 }
793 if (!ok)
794 fatalx("no functional log directories");
795 }
796
797 /* Will look good in libbb one day */
798 static 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
808 /* Used for reading stdin */
809 static int buffer_pread(/*int fd, */char *s, unsigned len)
810 {
811 unsigned now;
812 struct pollfd input;
813 int i;
814
815 input.fd = STDIN_FILENO;
816 input.events = POLLIN;
817
818 do {
819 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
844 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
845 i = nearest_rotate - now;
846 if (i > 1000000)
847 i = 1000000;
848 if (i <= 0)
849 i = 1;
850 poll(&input, 1, i * 1000);
851 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
852
853 i = ndelay_read(STDIN_FILENO, s, len);
854 if (i >= 0)
855 break;
856 if (errno == EINTR)
857 continue;
858 if (errno != EAGAIN) {
859 warn("can't read standard input");
860 break;
861 }
862 /* else: EAGAIN - normal, repeat silently */
863 } while (!exitasap);
864
865 if (i > 0) {
866 int cnt;
867 linecomplete = (s[i-1] == '\n');
868 if (!repl)
869 return i;
870
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
893 static void sig_term_handler(int sig_no UNUSED_PARAM)
894 {
895 if (verbose)
896 bb_error_msg(INFO"sig%s received", "term");
897 exitasap = 1;
898 }
899
900 static void sig_child_handler(int sig_no UNUSED_PARAM)
901 {
902 pid_t pid;
903 int l;
904
905 if (verbose)
906 bb_error_msg(INFO"sig%s received", "child");
907 while ((pid = wait_any_nohang(&wstat)) > 0) {
908 for (l = 0; l < dirn; ++l) {
909 if (dir[l].ppid == pid) {
910 dir[l].ppid = 0;
911 processorstop(&dir[l]);
912 break;
913 }
914 }
915 }
916 }
917
918 static void sig_alarm_handler(int sig_no UNUSED_PARAM)
919 {
920 if (verbose)
921 bb_error_msg(INFO"sig%s received", "alarm");
922 rotateasap = 1;
923 }
924
925 static void sig_hangup_handler(int sig_no UNUSED_PARAM)
926 {
927 if (verbose)
928 bb_error_msg(INFO"sig%s received", "hangup");
929 reopenasap = 1;
930 }
931
932 static 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
956 int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
957 int svlogd_main(int argc, char **argv)
958 {
959 char *r, *l, *b;
960 ssize_t stdin_cnt = 0;
961 int i;
962 unsigned opt;
963 unsigned timestamp = 0;
964
965 INIT_G();
966
967 opt_complementary = "tt:vv";
968 opt = getopt32(argv, "r:R:l:b:tv",
969 &r, &replace, &l, &b, &timestamp, &verbose);
970 if (opt & 1) { // -r
971 repl = r[0];
972 if (!repl || r[1])
973 bb_show_usage();
974 }
975 if (opt & 2) if (!repl) repl = '_'; // -R
976 if (opt & 4) { // -l
977 linemax = xatou_range(l, 0, BUFSIZ-26);
978 if (linemax == 0)
979 linemax = BUFSIZ-26;
980 if (linemax < 256)
981 linemax = 256;
982 }
983 ////if (opt & 8) { // -b
984 //// buflen = xatoi_u(b);
985 //// if (buflen == 0) buflen = 1024;
986 ////}
987 //if (opt & 0x10) timestamp++; // -t
988 //if (opt & 0x20) verbose++; // -v
989 //if (timestamp > 2) timestamp = 2;
990 argv += optind;
991 argc -= optind;
992
993 dirn = argc;
994 if (dirn <= 0)
995 bb_show_usage();
996 ////if (buflen <= linemax) bb_show_usage();
997 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
998 close_on_exec_on(fdwdir);
999 dir = xzalloc(dirn * sizeof(dir[0]));
1000 for (i = 0; i < dirn; ++i) {
1001 dir[i].fddir = -1;
1002 dir[i].fdcur = -1;
1003 ////dir[i].btmp = xmalloc(buflen);
1004 /*dir[i].ppid = 0;*/
1005 }
1006 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
1007 fndir = argv;
1008 /* 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 */
1011 fl_flag_0 = fcntl(0, F_GETFL);
1012
1013 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);
1019 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);
1023
1024 /* Without timestamps, we don't have to print each line
1025 * separately, so we can look for _last_ newline, not first,
1026 * 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();
1032
1033 setvbuf(stderr, NULL, _IOFBF, linelen);
1034
1035 /* Each iteration processes one or more lines */
1036 while (1) {
1037 char stamp[FMT_PTIME];
1038 char *lineptr;
1039 char *printptr;
1040 char *np;
1041 int printlen;
1042 char ch;
1043
1044 lineptr = line;
1045 if (timestamp)
1046 lineptr += 26;
1047
1048 /* lineptr[0..linemax-1] - buffer for stdin */
1049 /* (possibly has some unprocessed data from prev loop) */
1050
1051 /* Refill the buffer if needed */
1052 np = memRchr(lineptr, '\n', stdin_cnt);
1053 if (!np && !exitasap) {
1054 i = linemax - stdin_cnt; /* avail. bytes at tail */
1055 if (i >= 128) {
1056 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
1057 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 }
1063 }
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;
1070 if (np) {
1071 print_to_nl: /* NB: starting from here lineptr may point
1072 * farther out into line[] */
1073 linelen = np - lineptr + 1;
1074 }
1075 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1076 ch = lineptr[linelen-1];
1077
1078 /* 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 */
1082
1083 /* write out lineptr[0..linelen-1] to each log destination
1084 * (or lineptr[-26..linelen-1] if timestamping) */
1085 printlen = linelen;
1086 printptr = lineptr;
1087 if (timestamp) {
1088 if (timestamp == 1)
1089 fmt_time_bernstein_25(stamp);
1090 else /* 2: */
1091 fmt_time_human_30nul(stamp);
1092 printlen += 26;
1093 printptr -= 26;
1094 memcpy(printptr, stamp, 25);
1095 printptr[25] = ' ';
1096 }
1097 for (i = 0; i < dirn; ++i) {
1098 struct logdir *ld = &dir[i];
1099 if (ld->fddir == -1)
1100 continue;
1101 if (ld->inst)
1102 logmatch(ld);
1103 if (ld->matcherr == 'e') {
1104 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
1105 ////full_write(STDERR_FILENO, printptr, printlen);
1106 fwrite(printptr, 1, printlen, stderr);
1107 }
1108 if (ld->match != '+')
1109 continue;
1110 buffer_pwrite(i, printptr, printlen);
1111 }
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 */
1117 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
1118 if (stdin_cnt <= 0) { /* EOF or error on stdin */
1119 exitasap = 1;
1120 lineptr[0] = ch = '\n';
1121 linelen = 1;
1122 stdin_cnt = 1;
1123 } else {
1124 linelen = stdin_cnt;
1125 np = memRchr(lineptr, '\n', stdin_cnt);
1126 if (np)
1127 linelen = np - lineptr + 1;
1128 ch = lineptr[linelen-1];
1129 }
1130 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1131 for (i = 0; i < dirn; ++i) {
1132 if (dir[i].fddir == -1)
1133 continue;
1134 if (dir[i].matcherr == 'e') {
1135 ////full_write(STDERR_FILENO, lineptr, linelen);
1136 fwrite(lineptr, 1, linelen, stderr);
1137 }
1138 if (dir[i].match != '+')
1139 continue;
1140 buffer_pwrite(i, lineptr, linelen);
1141 }
1142 }
1143
1144 stdin_cnt -= linelen;
1145 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 */
1149 np = memRchr(lineptr, '\n', stdin_cnt);
1150 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 }
1155 fflush_all();////
1156 }
1157
1158 for (i = 0; i < dirn; ++i) {
1159 if (dir[i].ppid)
1160 while (!processorstop(&dir[i]))
1161 continue;
1162 logdir_close(&dir[i]);
1163 }
1164 return 0;
1165 }