Tomato 1.28
[tomato.git] / release / src / router / busybox / runit / svlogd.c
blob9609fa37c72d6a30ab753cb84ce96d721cf1024a
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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.
28 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
31 #include <sys/poll.h>
32 #include <sys/file.h>
33 #include "libbb.h"
34 #include "runit_lib.h"
36 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
38 #define FMT_PTIME 30
40 struct logdir {
41 ////char *btmp;
42 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
43 char *inst;
44 char *processor;
45 char *name;
46 unsigned size;
47 unsigned sizemax;
48 unsigned nmax;
49 unsigned nmin;
50 unsigned rotate_period;
51 int ppid;
52 int fddir;
53 int fdcur;
54 FILE* filecur; ////
55 int fdlock;
56 unsigned next_rotate;
57 char fnsave[FMT_PTIME];
58 char match;
59 char matcherr;
63 struct globals {
64 struct logdir *dir;
65 unsigned verbose;
66 int linemax;
67 ////int buflen;
68 int linelen;
70 int fdwdir;
71 char **fndir;
72 int wstat;
73 unsigned nearest_rotate;
75 smallint exitasap;
76 smallint rotateasap;
77 smallint reopenasap;
78 smallint linecomplete;
79 smallint tmaxflag;
81 char repl;
82 const char *replace;
83 int fl_flag_0;
84 unsigned dirn;
86 sigset_t blocked_sigset;
88 #define G (*(struct globals*)ptr_to_globals)
89 #define dir (G.dir )
90 #define verbose (G.verbose )
91 #define linemax (G.linemax )
92 #define buflen (G.buflen )
93 #define linelen (G.linelen )
94 #define fndir (G.fndir )
95 #define fdwdir (G.fdwdir )
96 #define wstat (G.wstat )
97 #define nearest_rotate (G.nearest_rotate)
98 #define exitasap (G.exitasap )
99 #define rotateasap (G.rotateasap )
100 #define reopenasap (G.reopenasap )
101 #define linecomplete (G.linecomplete )
102 #define tmaxflag (G.tmaxflag )
103 #define repl (G.repl )
104 #define replace (G.replace )
105 #define blocked_sigset (G.blocked_sigset)
106 #define fl_flag_0 (G.fl_flag_0 )
107 #define dirn (G.dirn )
108 #define INIT_G() do { \
109 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
110 linemax = 1000; \
111 /*buflen = 1024;*/ \
112 linecomplete = 1; \
113 replace = ""; \
114 } while (0)
116 #define line bb_common_bufsiz1
119 #define FATAL "fatal: "
120 #define WARNING "warning: "
121 #define PAUSE "pausing: "
122 #define INFO "info: "
124 #define usage() bb_show_usage()
125 static void fatalx(const char *m0)
127 bb_error_msg_and_die(FATAL"%s", m0);
129 static void warn(const char *m0)
131 bb_perror_msg(WARNING"%s", m0);
133 static void warn2(const char *m0, const char *m1)
135 bb_perror_msg(WARNING"%s: %s", m0, m1);
137 static void warnx(const char *m0, const char *m1)
139 bb_error_msg(WARNING"%s: %s", m0, m1);
141 static void pause_nomem(void)
143 bb_error_msg(PAUSE"out of memory");
144 sleep(3);
146 static void pause1cannot(const char *m0)
148 bb_perror_msg(PAUSE"can't %s", m0);
149 sleep(3);
151 static void pause2cannot(const char *m0, const char *m1)
153 bb_perror_msg(PAUSE"can't %s %s", m0, m1);
154 sleep(3);
157 static char* wstrdup(const char *str)
159 char *s;
160 while (!(s = strdup(str)))
161 pause_nomem();
162 return s;
165 /*** ex fmt_ptime.[ch] ***/
167 /* NUL terminated */
168 static void fmt_time_human_30nul(char *s)
170 struct tm *t;
171 struct timeval tv;
173 gettimeofday(&tv, NULL);
174 t = gmtime(&(tv.tv_sec));
175 sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
176 (unsigned)(1900 + t->tm_year),
177 (unsigned)(t->tm_mon + 1),
178 (unsigned)(t->tm_mday),
179 (unsigned)(t->tm_hour),
180 (unsigned)(t->tm_min),
181 (unsigned)(t->tm_sec),
182 (unsigned)(tv.tv_usec)
184 /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
185 /* 5 + 3 + 3 + 3 + 3 + 3 + 9 = */
186 /* 20 (up to '.' inclusive) + 9 (not including '\0') */
189 /* NOT terminated! */
190 static void fmt_time_bernstein_25(char *s)
192 uint32_t pack[3];
193 struct timeval tv;
194 unsigned sec_hi;
196 gettimeofday(&tv, NULL);
197 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
198 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
199 tv.tv_usec *= 1000;
200 /* Network order is big-endian: most significant byte first.
201 * This is exactly what we want here */
202 pack[0] = htonl(sec_hi);
203 pack[1] = htonl(tv.tv_sec);
204 pack[2] = htonl(tv.tv_usec);
205 *s++ = '@';
206 bin2hex(s, (char*)pack, 12);
209 static void processorstart(struct logdir *ld)
211 char sv_ch;
212 int pid;
214 if (!ld->processor) return;
215 if (ld->ppid) {
216 warnx("processor already running", ld->name);
217 return;
220 /* vfork'ed child trashes this byte, save... */
221 sv_ch = ld->fnsave[26];
223 while ((pid = vfork()) == -1)
224 pause2cannot("vfork for processor", ld->name);
225 if (!pid) {
226 char *prog[4];
227 int fd;
229 /* child */
230 /* Non-ignored signals revert to SIG_DFL on exec anyway */
231 /*bb_signals(0
232 + (1 << SIGTERM)
233 + (1 << SIGALRM)
234 + (1 << SIGHUP)
235 , SIG_DFL);*/
236 sig_unblock(SIGTERM);
237 sig_unblock(SIGALRM);
238 sig_unblock(SIGHUP);
240 if (verbose)
241 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
242 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
243 xmove_fd(fd, 0);
244 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
245 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
246 xmove_fd(fd, 1);
247 fd = open_read("state");
248 if (fd == -1) {
249 if (errno != ENOENT)
250 bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
251 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
252 fd = xopen("state", O_RDONLY|O_NDELAY);
254 xmove_fd(fd, 4);
255 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
256 xmove_fd(fd, 5);
258 // getenv("SHELL")?
259 prog[0] = (char*)"sh";
260 prog[1] = (char*)"-c";
261 prog[2] = ld->processor;
262 prog[3] = NULL;
263 execv("/bin/sh", prog);
264 bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
266 ld->fnsave[26] = sv_ch; /* ...restore */
267 ld->ppid = pid;
270 static unsigned processorstop(struct logdir *ld)
272 char f[28];
274 if (ld->ppid) {
275 sig_unblock(SIGHUP);
276 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
277 pause2cannot("wait for processor", ld->name);
278 sig_block(SIGHUP);
279 ld->ppid = 0;
281 if (ld->fddir == -1) return 1;
282 while (fchdir(ld->fddir) == -1)
283 pause2cannot("change directory, want processor", ld->name);
284 if (wait_exitcode(wstat) != 0) {
285 warnx("processor failed, restart", ld->name);
286 ld->fnsave[26] = 't';
287 unlink(ld->fnsave);
288 ld->fnsave[26] = 'u';
289 processorstart(ld);
290 while (fchdir(fdwdir) == -1)
291 pause1cannot("change to initial working directory");
292 return ld->processor ? 0 : 1;
294 ld->fnsave[26] = 't';
295 memcpy(f, ld->fnsave, 26);
296 f[26] = 's';
297 f[27] = '\0';
298 while (rename(ld->fnsave, f) == -1)
299 pause2cannot("rename processed", ld->name);
300 while (chmod(f, 0744) == -1)
301 pause2cannot("set mode of processed", ld->name);
302 ld->fnsave[26] = 'u';
303 if (unlink(ld->fnsave) == -1)
304 bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
305 while (rename("newstate", "state") == -1)
306 pause2cannot("rename state", ld->name);
307 if (verbose)
308 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
309 while (fchdir(fdwdir) == -1)
310 pause1cannot("change to initial working directory");
311 return 1;
314 static void rmoldest(struct logdir *ld)
316 DIR *d;
317 struct dirent *f;
318 char oldest[FMT_PTIME];
319 int n = 0;
321 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
322 while (!(d = opendir(".")))
323 pause2cannot("open directory, want rotate", ld->name);
324 errno = 0;
325 while ((f = readdir(d))) {
326 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
327 if (f->d_name[26] == 't') {
328 if (unlink(f->d_name) == -1)
329 warn2("can't unlink processor leftover", f->d_name);
330 } else {
331 ++n;
332 if (strcmp(f->d_name, oldest) < 0)
333 memcpy(oldest, f->d_name, 27);
335 errno = 0;
338 if (errno)
339 warn2("can't read directory", ld->name);
340 closedir(d);
342 if (ld->nmax && (n > ld->nmax)) {
343 if (verbose)
344 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
345 if ((*oldest == '@') && (unlink(oldest) == -1))
346 warn2("can't unlink oldest logfile", ld->name);
350 static unsigned rotate(struct logdir *ld)
352 struct stat st;
353 unsigned now;
355 if (ld->fddir == -1) {
356 ld->rotate_period = 0;
357 return 0;
359 if (ld->ppid)
360 while (!processorstop(ld))
361 continue;
363 while (fchdir(ld->fddir) == -1)
364 pause2cannot("change directory, want rotate", ld->name);
366 /* create new filename */
367 ld->fnsave[25] = '.';
368 ld->fnsave[26] = 's';
369 if (ld->processor)
370 ld->fnsave[26] = 'u';
371 ld->fnsave[27] = '\0';
372 do {
373 fmt_time_bernstein_25(ld->fnsave);
374 errno = 0;
375 stat(ld->fnsave, &st);
376 } while (errno != ENOENT);
378 now = monotonic_sec();
379 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
380 ld->next_rotate = now + ld->rotate_period;
381 if (LESS(ld->next_rotate, nearest_rotate))
382 nearest_rotate = ld->next_rotate;
385 if (ld->size > 0) {
386 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
387 pause2cannot("fsync current logfile", ld->name);
388 while (fchmod(ld->fdcur, 0744) == -1)
389 pause2cannot("set mode of current", ld->name);
390 ////close(ld->fdcur);
391 fclose(ld->filecur);
393 if (verbose) {
394 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
395 ld->fnsave, ld->size);
397 while (rename("current", ld->fnsave) == -1)
398 pause2cannot("rename current", ld->name);
399 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
400 pause2cannot("create new current", ld->name);
401 /* we presume this cannot fail */
402 ld->filecur = fdopen(ld->fdcur, "a"); ////
403 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
404 close_on_exec_on(ld->fdcur);
405 ld->size = 0;
406 while (fchmod(ld->fdcur, 0644) == -1)
407 pause2cannot("set mode of current", ld->name);
408 rmoldest(ld);
409 processorstart(ld);
412 while (fchdir(fdwdir) == -1)
413 pause1cannot("change to initial working directory");
414 return 1;
417 static int buffer_pwrite(int n, char *s, unsigned len)
419 int i;
420 struct logdir *ld = &dir[n];
422 if (ld->sizemax) {
423 if (ld->size >= ld->sizemax)
424 rotate(ld);
425 if (len > (ld->sizemax - ld->size))
426 len = ld->sizemax - ld->size;
428 while (1) {
429 ////i = full_write(ld->fdcur, s, len);
430 ////if (i != -1) break;
431 i = fwrite(s, 1, len, ld->filecur);
432 if (i == len) break;
434 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
435 DIR *d;
436 struct dirent *f;
437 char oldest[FMT_PTIME];
438 int j = 0;
440 while (fchdir(ld->fddir) == -1)
441 pause2cannot("change directory, want remove old logfile",
442 ld->name);
443 oldest[0] = 'A';
444 oldest[1] = oldest[27] = '\0';
445 while (!(d = opendir(".")))
446 pause2cannot("open directory, want remove old logfile",
447 ld->name);
448 errno = 0;
449 while ((f = readdir(d)))
450 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
451 ++j;
452 if (strcmp(f->d_name, oldest) < 0)
453 memcpy(oldest, f->d_name, 27);
455 if (errno) warn2("can't read directory, want remove old logfile",
456 ld->name);
457 closedir(d);
458 errno = ENOSPC;
459 if (j > ld->nmin) {
460 if (*oldest == '@') {
461 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
462 ld->name, oldest);
463 errno = 0;
464 if (unlink(oldest) == -1) {
465 warn2("can't unlink oldest logfile", ld->name);
466 errno = ENOSPC;
468 while (fchdir(fdwdir) == -1)
469 pause1cannot("change to initial working directory");
473 if (errno)
474 pause2cannot("write to current", ld->name);
477 ld->size += i;
478 if (ld->sizemax)
479 if (s[i-1] == '\n')
480 if (ld->size >= (ld->sizemax - linemax))
481 rotate(ld);
482 return i;
485 static void logdir_close(struct logdir *ld)
487 if (ld->fddir == -1)
488 return;
489 if (verbose)
490 bb_error_msg(INFO"close: %s", ld->name);
491 close(ld->fddir);
492 ld->fddir = -1;
493 if (ld->fdcur == -1)
494 return; /* impossible */
495 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
496 pause2cannot("fsync current logfile", ld->name);
497 while (fchmod(ld->fdcur, 0744) == -1)
498 pause2cannot("set mode of current", ld->name);
499 ////close(ld->fdcur);
500 fclose(ld->filecur);
501 ld->fdcur = -1;
502 if (ld->fdlock == -1)
503 return; /* impossible */
504 close(ld->fdlock);
505 ld->fdlock = -1;
506 free(ld->processor);
507 ld->processor = NULL;
510 static unsigned logdir_open(struct logdir *ld, const char *fn)
512 char buf[128];
513 unsigned now;
514 char *new, *s, *np;
515 int i;
516 struct stat st;
518 now = monotonic_sec();
520 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
521 if (ld->fddir == -1) {
522 warn2("can't open log directory", (char*)fn);
523 return 0;
525 close_on_exec_on(ld->fddir);
526 if (fchdir(ld->fddir) == -1) {
527 logdir_close(ld);
528 warn2("can't change directory", (char*)fn);
529 return 0;
531 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
532 if ((ld->fdlock == -1)
533 || (lock_exnb(ld->fdlock) == -1)
535 logdir_close(ld);
536 warn2("can't lock directory", (char*)fn);
537 while (fchdir(fdwdir) == -1)
538 pause1cannot("change to initial working directory");
539 return 0;
541 close_on_exec_on(ld->fdlock);
543 ld->size = 0;
544 ld->sizemax = 1000000;
545 ld->nmax = ld->nmin = 10;
546 ld->rotate_period = 0;
547 ld->name = (char*)fn;
548 ld->ppid = 0;
549 ld->match = '+';
550 free(ld->inst); ld->inst = NULL;
551 free(ld->processor); ld->processor = NULL;
553 /* read config */
554 i = open_read_close("config", buf, sizeof(buf));
555 if (i < 0 && errno != ENOENT)
556 bb_perror_msg(WARNING"%s/config", ld->name);
557 if (i > 0) {
558 if (verbose)
559 bb_error_msg(INFO"read: %s/config", ld->name);
560 s = buf;
561 while (s) {
562 np = strchr(s, '\n');
563 if (np)
564 *np++ = '\0';
565 switch (s[0]) {
566 case '+':
567 case '-':
568 case 'e':
569 case 'E':
570 /* Add '\n'-terminated line to ld->inst */
571 while (1) {
572 int l = asprintf(&new, "%s%s\n", ld->inst ? : "", s);
573 if (l >= 0 && new)
574 break;
575 pause_nomem();
577 free(ld->inst);
578 ld->inst = new;
579 break;
580 case 's': {
581 static const struct suffix_mult km_suffixes[] = {
582 { "k", 1024 },
583 { "m", 1024*1024 },
586 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
587 break;
589 case 'n':
590 ld->nmax = xatoi_u(&s[1]);
591 break;
592 case 'N':
593 ld->nmin = xatoi_u(&s[1]);
594 break;
595 case 't': {
596 static const struct suffix_mult mh_suffixes[] = {
597 { "m", 60 },
598 { "h", 60*60 },
599 /*{ "d", 24*60*60 },*/
602 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
603 if (ld->rotate_period) {
604 ld->next_rotate = now + ld->rotate_period;
605 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
606 nearest_rotate = ld->next_rotate;
607 tmaxflag = 1;
609 break;
611 case '!':
612 if (s[1]) {
613 free(ld->processor);
614 ld->processor = wstrdup(s);
616 break;
618 s = np;
620 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
621 s = ld->inst;
622 while (s) {
623 np = strchr(s, '\n');
624 if (np)
625 *np++ = '\0';
626 s = np;
630 /* open current */
631 i = stat("current", &st);
632 if (i != -1) {
633 if (st.st_size && !(st.st_mode & S_IXUSR)) {
634 ld->fnsave[25] = '.';
635 ld->fnsave[26] = 'u';
636 ld->fnsave[27] = '\0';
637 do {
638 fmt_time_bernstein_25(ld->fnsave);
639 errno = 0;
640 stat(ld->fnsave, &st);
641 } while (errno != ENOENT);
642 while (rename("current", ld->fnsave) == -1)
643 pause2cannot("rename current", ld->name);
644 rmoldest(ld);
645 i = -1;
646 } else {
647 /* st.st_size can be not just bigger, but WIDER!
648 * This code is safe: if st.st_size > 4GB, we select
649 * ld->sizemax (because it's "unsigned") */
650 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
652 } else {
653 if (errno != ENOENT) {
654 logdir_close(ld);
655 warn2("can't stat current", ld->name);
656 while (fchdir(fdwdir) == -1)
657 pause1cannot("change to initial working directory");
658 return 0;
661 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
662 pause2cannot("open current", ld->name);
663 /* we presume this cannot fail */
664 ld->filecur = fdopen(ld->fdcur, "a"); ////
665 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
667 close_on_exec_on(ld->fdcur);
668 while (fchmod(ld->fdcur, 0644) == -1)
669 pause2cannot("set mode of current", ld->name);
671 if (verbose) {
672 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
673 else bb_error_msg(INFO"new: %s/current", ld->name);
676 while (fchdir(fdwdir) == -1)
677 pause1cannot("change to initial working directory");
678 return 1;
681 static void logdirs_reopen(void)
683 int l;
684 int ok = 0;
686 tmaxflag = 0;
687 for (l = 0; l < dirn; ++l) {
688 logdir_close(&dir[l]);
689 if (logdir_open(&dir[l], fndir[l]))
690 ok = 1;
692 if (!ok)
693 fatalx("no functional log directories");
696 /* Will look good in libbb one day */
697 static ssize_t ndelay_read(int fd, void *buf, size_t count)
699 if (!(fl_flag_0 & O_NONBLOCK))
700 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
701 count = safe_read(fd, buf, count);
702 if (!(fl_flag_0 & O_NONBLOCK))
703 fcntl(fd, F_SETFL, fl_flag_0);
704 return count;
707 /* Used for reading stdin */
708 static int buffer_pread(/*int fd, */char *s, unsigned len)
710 unsigned now;
711 struct pollfd input;
712 int i;
714 input.fd = 0;
715 input.events = POLLIN;
717 do {
718 if (rotateasap) {
719 for (i = 0; i < dirn; ++i)
720 rotate(dir + i);
721 rotateasap = 0;
723 if (exitasap) {
724 if (linecomplete)
725 return 0;
726 len = 1;
728 if (reopenasap) {
729 logdirs_reopen();
730 reopenasap = 0;
732 now = monotonic_sec();
733 nearest_rotate = now + (45 * 60 + 45);
734 for (i = 0; i < dirn; ++i) {
735 if (dir[i].rotate_period) {
736 if (LESS(dir[i].next_rotate, now))
737 rotate(dir + i);
738 if (LESS(dir[i].next_rotate, nearest_rotate))
739 nearest_rotate = dir[i].next_rotate;
743 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
744 i = nearest_rotate - now;
745 if (i > 1000000)
746 i = 1000000;
747 if (i <= 0)
748 i = 1;
749 poll(&input, 1, i * 1000);
750 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
752 i = ndelay_read(STDIN_FILENO, s, len);
753 if (i >= 0)
754 break;
755 if (errno == EINTR)
756 continue;
757 if (errno != EAGAIN) {
758 warn("can't read standard input");
759 break;
761 /* else: EAGAIN - normal, repeat silently */
762 } while (!exitasap);
764 if (i > 0) {
765 int cnt;
766 linecomplete = (s[i-1] == '\n');
767 if (!repl)
768 return i;
770 cnt = i;
771 while (--cnt >= 0) {
772 char ch = *s;
773 if (ch != '\n') {
774 if (ch < 32 || ch > 126)
775 *s = repl;
776 else {
777 int j;
778 for (j = 0; replace[j]; ++j) {
779 if (ch == replace[j]) {
780 *s = repl;
781 break;
786 s++;
789 return i;
792 static void sig_term_handler(int sig_no UNUSED_PARAM)
794 if (verbose)
795 bb_error_msg(INFO"sig%s received", "term");
796 exitasap = 1;
799 static void sig_child_handler(int sig_no UNUSED_PARAM)
801 pid_t pid;
802 int l;
804 if (verbose)
805 bb_error_msg(INFO"sig%s received", "child");
806 while ((pid = wait_any_nohang(&wstat)) > 0) {
807 for (l = 0; l < dirn; ++l) {
808 if (dir[l].ppid == pid) {
809 dir[l].ppid = 0;
810 processorstop(&dir[l]);
811 break;
817 static void sig_alarm_handler(int sig_no UNUSED_PARAM)
819 if (verbose)
820 bb_error_msg(INFO"sig%s received", "alarm");
821 rotateasap = 1;
824 static void sig_hangup_handler(int sig_no UNUSED_PARAM)
826 if (verbose)
827 bb_error_msg(INFO"sig%s received", "hangup");
828 reopenasap = 1;
831 static void logmatch(struct logdir *ld)
833 char *s;
835 ld->match = '+';
836 ld->matcherr = 'E';
837 s = ld->inst;
838 while (s && s[0]) {
839 switch (s[0]) {
840 case '+':
841 case '-':
842 if (pmatch(s+1, line, linelen))
843 ld->match = s[0];
844 break;
845 case 'e':
846 case 'E':
847 if (pmatch(s+1, line, linelen))
848 ld->matcherr = s[0];
849 break;
851 s += strlen(s) + 1;
855 int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
856 int svlogd_main(int argc, char **argv)
858 char *r,*l,*b;
859 ssize_t stdin_cnt = 0;
860 int i;
861 unsigned opt;
862 unsigned timestamp = 0;
863 void* (*memRchr)(const void *, int, size_t) = memchr;
865 INIT_G();
867 opt_complementary = "tt:vv";
868 opt = getopt32(argv, "r:R:l:b:tv",
869 &r, &replace, &l, &b, &timestamp, &verbose);
870 if (opt & 1) { // -r
871 repl = r[0];
872 if (!repl || r[1]) usage();
874 if (opt & 2) if (!repl) repl = '_'; // -R
875 if (opt & 4) { // -l
876 linemax = xatou_range(l, 0, BUFSIZ-26);
877 if (linemax == 0) linemax = BUFSIZ-26;
878 if (linemax < 256) linemax = 256;
880 ////if (opt & 8) { // -b
881 //// buflen = xatoi_u(b);
882 //// if (buflen == 0) buflen = 1024;
883 ////}
884 //if (opt & 0x10) timestamp++; // -t
885 //if (opt & 0x20) verbose++; // -v
886 //if (timestamp > 2) timestamp = 2;
887 argv += optind;
888 argc -= optind;
890 dirn = argc;
891 if (dirn <= 0) usage();
892 ////if (buflen <= linemax) usage();
893 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
894 close_on_exec_on(fdwdir);
895 dir = xzalloc(dirn * sizeof(struct logdir));
896 for (i = 0; i < dirn; ++i) {
897 dir[i].fddir = -1;
898 dir[i].fdcur = -1;
899 ////dir[i].btmp = xmalloc(buflen);
900 /*dir[i].ppid = 0;*/
902 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
903 fndir = argv;
904 /* We cannot set NONBLOCK on fd #0 permanently - this setting
905 * _isn't_ per-process! It is shared among all other processes
906 * with the same stdin */
907 fl_flag_0 = fcntl(0, F_GETFL);
909 sigemptyset(&blocked_sigset);
910 sigaddset(&blocked_sigset, SIGTERM);
911 sigaddset(&blocked_sigset, SIGCHLD);
912 sigaddset(&blocked_sigset, SIGALRM);
913 sigaddset(&blocked_sigset, SIGHUP);
914 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
915 bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
916 bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
917 bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
918 bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
920 logdirs_reopen();
922 /* Without timestamps, we don't have to print each line
923 * separately, so we can look for _last_ newline, not first,
924 * thus batching writes */
925 if (!timestamp)
926 memRchr = memrchr;
928 setvbuf(stderr, NULL, _IOFBF, linelen);
930 /* Each iteration processes one or more lines */
931 while (1) {
932 char stamp[FMT_PTIME];
933 char *lineptr;
934 char *printptr;
935 char *np;
936 int printlen;
937 char ch;
939 lineptr = line;
940 if (timestamp)
941 lineptr += 26;
943 /* lineptr[0..linemax-1] - buffer for stdin */
944 /* (possibly has some unprocessed data from prev loop) */
946 /* Refill the buffer if needed */
947 np = memRchr(lineptr, '\n', stdin_cnt);
948 if (!np && !exitasap) {
949 i = linemax - stdin_cnt; /* avail. bytes at tail */
950 if (i >= 128) {
951 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
952 if (i <= 0) /* EOF or error on stdin */
953 exitasap = 1;
954 else {
955 np = memRchr(lineptr + stdin_cnt, '\n', i);
956 stdin_cnt += i;
960 if (stdin_cnt <= 0 && exitasap)
961 break;
963 /* Search for '\n' (in fact, np already holds the result) */
964 linelen = stdin_cnt;
965 if (np) {
966 print_to_nl: /* NB: starting from here lineptr may point
967 * farther out into line[] */
968 linelen = np - lineptr + 1;
970 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
971 ch = lineptr[linelen-1];
973 /* Biggest performance hit was coming from the fact
974 * that we did not buffer writes. We were reading many lines
975 * in one read() above, but wrote one line per write().
976 * We are using stdio to fix that */
978 /* write out lineptr[0..linelen-1] to each log destination
979 * (or lineptr[-26..linelen-1] if timestamping) */
980 printlen = linelen;
981 printptr = lineptr;
982 if (timestamp) {
983 if (timestamp == 1)
984 fmt_time_bernstein_25(stamp);
985 else /* 2: */
986 fmt_time_human_30nul(stamp);
987 printlen += 26;
988 printptr -= 26;
989 memcpy(printptr, stamp, 25);
990 printptr[25] = ' ';
992 for (i = 0; i < dirn; ++i) {
993 struct logdir *ld = &dir[i];
994 if (ld->fddir == -1) continue;
995 if (ld->inst)
996 logmatch(ld);
997 if (ld->matcherr == 'e') {
998 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
999 ////full_write(STDERR_FILENO, printptr, printlen);
1000 fwrite(printptr, 1, printlen, stderr);
1002 if (ld->match != '+') continue;
1003 buffer_pwrite(i, printptr, printlen);
1006 /* If we didn't see '\n' (long input line), */
1007 /* read/write repeatedly until we see it */
1008 while (ch != '\n') {
1009 /* lineptr is emptied now, safe to use as buffer */
1010 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
1011 if (stdin_cnt <= 0) { /* EOF or error on stdin */
1012 exitasap = 1;
1013 lineptr[0] = ch = '\n';
1014 linelen = 1;
1015 stdin_cnt = 1;
1016 } else {
1017 linelen = stdin_cnt;
1018 np = memRchr(lineptr, '\n', stdin_cnt);
1019 if (np)
1020 linelen = np - lineptr + 1;
1021 ch = lineptr[linelen-1];
1023 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1024 for (i = 0; i < dirn; ++i) {
1025 if (dir[i].fddir == -1) continue;
1026 if (dir[i].matcherr == 'e') {
1027 ////full_write(STDERR_FILENO, lineptr, linelen);
1028 fwrite(lineptr, 1, linelen, stderr);
1030 if (dir[i].match != '+') continue;
1031 buffer_pwrite(i, lineptr, linelen);
1035 stdin_cnt -= linelen;
1036 if (stdin_cnt > 0) {
1037 lineptr += linelen;
1038 /* If we see another '\n', we don't need to read
1039 * next piece of input: can print what we have */
1040 np = memRchr(lineptr, '\n', stdin_cnt);
1041 if (np)
1042 goto print_to_nl;
1043 /* Move unprocessed data to the front of line */
1044 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1046 fflush(NULL);////
1049 for (i = 0; i < dirn; ++i) {
1050 if (dir[i].ppid)
1051 while (!processorstop(&dir[i]))
1052 /* repeat */;
1053 logdir_close(&dir[i]);
1055 return 0;