svlogd: fix compat problem: svlogd -tt should timestanp stderr too
[busybox.git] / runit / svlogd.c
blob2dc8cb987ea6759ad2c302f9f6be2b27efa15e6c
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 Denis 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 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"cannot %s", m0);
149 sleep(3);
151 static void pause2cannot(const char *m0, const char *m1)
153 bb_perror_msg(PAUSE"cannot %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 unsigned processorstart(struct logdir *ld)
211 int pid;
213 if (!ld->processor) return 0;
214 if (ld->ppid) {
215 warnx("processor already running", ld->name);
216 return 0;
218 while ((pid = fork()) == -1)
219 pause2cannot("fork for processor", ld->name);
220 if (!pid) {
221 char *prog[4];
222 int fd;
224 /* child */
225 signal(SIGTERM, SIG_DFL);
226 signal(SIGALRM, SIG_DFL);
227 signal(SIGHUP, SIG_DFL);
228 sig_unblock(SIGTERM);
229 sig_unblock(SIGALRM);
230 sig_unblock(SIGHUP);
232 if (verbose)
233 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
234 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
235 xmove_fd(fd, 0);
236 ld->fnsave[26] = 't';
237 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
238 xmove_fd(fd, 1);
239 fd = open_read("state");
240 if (fd == -1) {
241 if (errno != ENOENT)
242 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
243 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
244 fd = xopen("state", O_RDONLY|O_NDELAY);
246 xmove_fd(fd, 4);
247 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
248 xmove_fd(fd, 5);
250 // getenv("SHELL")?
251 prog[0] = (char*)"sh";
252 prog[1] = (char*)"-c";
253 prog[2] = ld->processor;
254 prog[3] = NULL;
255 execve("/bin/sh", prog, environ);
256 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
258 ld->ppid = pid;
259 return 1;
262 static unsigned processorstop(struct logdir *ld)
264 char f[28];
266 if (ld->ppid) {
267 sig_unblock(SIGHUP);
268 while (wait_pid(&wstat, ld->ppid) == -1)
269 pause2cannot("wait for processor", ld->name);
270 sig_block(SIGHUP);
271 ld->ppid = 0;
273 if (ld->fddir == -1) return 1;
274 while (fchdir(ld->fddir) == -1)
275 pause2cannot("change directory, want processor", ld->name);
276 if (wait_exitcode(wstat) != 0) {
277 warnx("processor failed, restart", ld->name);
278 ld->fnsave[26] = 't';
279 unlink(ld->fnsave);
280 ld->fnsave[26] = 'u';
281 processorstart(ld);
282 while (fchdir(fdwdir) == -1)
283 pause1cannot("change to initial working directory");
284 return ld->processor ? 0 : 1;
286 ld->fnsave[26] = 't';
287 memcpy(f, ld->fnsave, 26);
288 f[26] = 's';
289 f[27] = '\0';
290 while (rename(ld->fnsave, f) == -1)
291 pause2cannot("rename processed", ld->name);
292 while (chmod(f, 0744) == -1)
293 pause2cannot("set mode of processed", ld->name);
294 ld->fnsave[26] = 'u';
295 if (unlink(ld->fnsave) == -1)
296 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
297 while (rename("newstate", "state") == -1)
298 pause2cannot("rename state", ld->name);
299 if (verbose)
300 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
301 while (fchdir(fdwdir) == -1)
302 pause1cannot("change to initial working directory");
303 return 1;
306 static void rmoldest(struct logdir *ld)
308 DIR *d;
309 struct dirent *f;
310 char oldest[FMT_PTIME];
311 int n = 0;
313 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
314 while (!(d = opendir(".")))
315 pause2cannot("open directory, want rotate", ld->name);
316 errno = 0;
317 while ((f = readdir(d))) {
318 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
319 if (f->d_name[26] == 't') {
320 if (unlink(f->d_name) == -1)
321 warn2("cannot unlink processor leftover", f->d_name);
322 } else {
323 ++n;
324 if (strcmp(f->d_name, oldest) < 0)
325 memcpy(oldest, f->d_name, 27);
327 errno = 0;
330 if (errno)
331 warn2("cannot read directory", ld->name);
332 closedir(d);
334 if (ld->nmax && (n > ld->nmax)) {
335 if (verbose)
336 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
337 if ((*oldest == '@') && (unlink(oldest) == -1))
338 warn2("cannot unlink oldest logfile", ld->name);
342 static unsigned rotate(struct logdir *ld)
344 struct stat st;
345 unsigned now;
347 if (ld->fddir == -1) {
348 ld->rotate_period = 0;
349 return 0;
351 if (ld->ppid)
352 while (!processorstop(ld))
353 continue;
355 while (fchdir(ld->fddir) == -1)
356 pause2cannot("change directory, want rotate", ld->name);
358 /* create new filename */
359 ld->fnsave[25] = '.';
360 ld->fnsave[26] = 's';
361 if (ld->processor)
362 ld->fnsave[26] = 'u';
363 ld->fnsave[27] = '\0';
364 do {
365 fmt_time_bernstein_25(ld->fnsave);
366 errno = 0;
367 stat(ld->fnsave, &st);
368 } while (errno != ENOENT);
370 now = monotonic_sec();
371 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
372 ld->next_rotate = now + ld->rotate_period;
373 if (LESS(ld->next_rotate, nearest_rotate))
374 nearest_rotate = ld->next_rotate;
377 if (ld->size > 0) {
378 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
379 pause2cannot("fsync current logfile", ld->name);
380 while (fchmod(ld->fdcur, 0744) == -1)
381 pause2cannot("set mode of current", ld->name);
382 ////close(ld->fdcur);
383 fclose(ld->filecur);
385 if (verbose) {
386 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
387 ld->fnsave, ld->size);
389 while (rename("current", ld->fnsave) == -1)
390 pause2cannot("rename current", ld->name);
391 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
392 pause2cannot("create new current", ld->name);
393 /* we presume this cannot fail */
394 ld->filecur = fdopen(ld->fdcur, "a"); ////
395 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
396 close_on_exec_on(ld->fdcur);
397 ld->size = 0;
398 while (fchmod(ld->fdcur, 0644) == -1)
399 pause2cannot("set mode of current", ld->name);
400 rmoldest(ld);
401 processorstart(ld);
404 while (fchdir(fdwdir) == -1)
405 pause1cannot("change to initial working directory");
406 return 1;
409 static int buffer_pwrite(int n, char *s, unsigned len)
411 int i;
412 struct logdir *ld = &dir[n];
414 if (ld->sizemax) {
415 if (ld->size >= ld->sizemax)
416 rotate(ld);
417 if (len > (ld->sizemax - ld->size))
418 len = ld->sizemax - ld->size;
420 while (1) {
421 ////i = full_write(ld->fdcur, s, len);
422 ////if (i != -1) break;
423 i = fwrite(s, 1, len, ld->filecur);
424 if (i == len) break;
426 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
427 DIR *d;
428 struct dirent *f;
429 char oldest[FMT_PTIME];
430 int j = 0;
432 while (fchdir(ld->fddir) == -1)
433 pause2cannot("change directory, want remove old logfile",
434 ld->name);
435 oldest[0] = 'A';
436 oldest[1] = oldest[27] = '\0';
437 while (!(d = opendir(".")))
438 pause2cannot("open directory, want remove old logfile",
439 ld->name);
440 errno = 0;
441 while ((f = readdir(d)))
442 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
443 ++j;
444 if (strcmp(f->d_name, oldest) < 0)
445 memcpy(oldest, f->d_name, 27);
447 if (errno) warn2("cannot read directory, want remove old logfile",
448 ld->name);
449 closedir(d);
450 errno = ENOSPC;
451 if (j > ld->nmin) {
452 if (*oldest == '@') {
453 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
454 ld->name, oldest);
455 errno = 0;
456 if (unlink(oldest) == -1) {
457 warn2("cannot unlink oldest logfile", ld->name);
458 errno = ENOSPC;
460 while (fchdir(fdwdir) == -1)
461 pause1cannot("change to initial working directory");
465 if (errno)
466 pause2cannot("write to current", ld->name);
469 ld->size += i;
470 if (ld->sizemax)
471 if (s[i-1] == '\n')
472 if (ld->size >= (ld->sizemax - linemax))
473 rotate(ld);
474 return i;
477 static void logdir_close(struct logdir *ld)
479 if (ld->fddir == -1)
480 return;
481 if (verbose)
482 bb_error_msg(INFO"close: %s", ld->name);
483 close(ld->fddir);
484 ld->fddir = -1;
485 if (ld->fdcur == -1)
486 return; /* impossible */
487 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
488 pause2cannot("fsync current logfile", ld->name);
489 while (fchmod(ld->fdcur, 0744) == -1)
490 pause2cannot("set mode of current", ld->name);
491 ////close(ld->fdcur);
492 fclose(ld->filecur);
493 ld->fdcur = -1;
494 if (ld->fdlock == -1)
495 return; /* impossible */
496 close(ld->fdlock);
497 ld->fdlock = -1;
498 free(ld->processor);
499 ld->processor = NULL;
502 static unsigned logdir_open(struct logdir *ld, const char *fn)
504 char buf[128];
505 unsigned now;
506 char *new, *s, *np;
507 int i;
508 struct stat st;
510 now = monotonic_sec();
512 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
513 if (ld->fddir == -1) {
514 warn2("cannot open log directory", (char*)fn);
515 return 0;
517 close_on_exec_on(ld->fddir);
518 if (fchdir(ld->fddir) == -1) {
519 logdir_close(ld);
520 warn2("cannot change directory", (char*)fn);
521 return 0;
523 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
524 if ((ld->fdlock == -1)
525 || (lock_exnb(ld->fdlock) == -1)
527 logdir_close(ld);
528 warn2("cannot lock directory", (char*)fn);
529 while (fchdir(fdwdir) == -1)
530 pause1cannot("change to initial working directory");
531 return 0;
533 close_on_exec_on(ld->fdlock);
535 ld->size = 0;
536 ld->sizemax = 1000000;
537 ld->nmax = ld->nmin = 10;
538 ld->rotate_period = 0;
539 ld->name = (char*)fn;
540 ld->ppid = 0;
541 ld->match = '+';
542 free(ld->inst); ld->inst = NULL;
543 free(ld->processor); ld->processor = NULL;
545 /* read config */
546 i = open_read_close("config", buf, sizeof(buf));
547 if (i < 0 && errno != ENOENT)
548 bb_perror_msg(WARNING"%s/config", ld->name);
549 if (i > 0) {
550 if (verbose)
551 bb_error_msg(INFO"read: %s/config", ld->name);
552 s = buf;
553 while (s) {
554 np = strchr(s, '\n');
555 if (np)
556 *np++ = '\0';
557 switch (s[0]) {
558 case '+':
559 case '-':
560 case 'e':
561 case 'E':
562 /* Add '\n'-terminated line to ld->inst */
563 while (1) {
564 int l = asprintf(&new, "%s%s\n", ld->inst ? : "", s);
565 if (l >= 0 && new)
566 break;
567 pause_nomem();
569 free(ld->inst);
570 ld->inst = new;
571 break;
572 case 's': {
573 static const struct suffix_mult km_suffixes[] = {
574 { "k", 1024 },
575 { "m", 1024*1024 },
578 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
579 break;
581 case 'n':
582 ld->nmax = xatoi_u(&s[1]);
583 break;
584 case 'N':
585 ld->nmin = xatoi_u(&s[1]);
586 break;
587 case 't': {
588 static const struct suffix_mult mh_suffixes[] = {
589 { "m", 60 },
590 { "h", 60*60 },
591 /*{ "d", 24*60*60 },*/
594 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
595 if (ld->rotate_period) {
596 ld->next_rotate = now + ld->rotate_period;
597 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
598 nearest_rotate = ld->next_rotate;
599 tmaxflag = 1;
601 break;
603 case '!':
604 if (s[1]) {
605 free(ld->processor);
606 ld->processor = wstrdup(s);
608 break;
610 s = np;
612 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
613 s = ld->inst;
614 while (s) {
615 np = strchr(s, '\n');
616 if (np)
617 *np++ = '\0';
618 s = np;
622 /* open current */
623 i = stat("current", &st);
624 if (i != -1) {
625 if (st.st_size && !(st.st_mode & S_IXUSR)) {
626 ld->fnsave[25] = '.';
627 ld->fnsave[26] = 'u';
628 ld->fnsave[27] = '\0';
629 do {
630 fmt_time_bernstein_25(ld->fnsave);
631 errno = 0;
632 stat(ld->fnsave, &st);
633 } while (errno != ENOENT);
634 while (rename("current", ld->fnsave) == -1)
635 pause2cannot("rename current", ld->name);
636 rmoldest(ld);
637 i = -1;
638 } else {
639 /* st.st_size can be not just bigger, but WIDER!
640 * This code is safe: if st.st_size > 4GB, we select
641 * ld->sizemax (because it's "unsigned") */
642 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
644 } else {
645 if (errno != ENOENT) {
646 logdir_close(ld);
647 warn2("cannot stat current", ld->name);
648 while (fchdir(fdwdir) == -1)
649 pause1cannot("change to initial working directory");
650 return 0;
653 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
654 pause2cannot("open current", ld->name);
655 /* we presume this cannot fail */
656 ld->filecur = fdopen(ld->fdcur, "a"); ////
657 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
659 close_on_exec_on(ld->fdcur);
660 while (fchmod(ld->fdcur, 0644) == -1)
661 pause2cannot("set mode of current", ld->name);
663 if (verbose) {
664 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
665 else bb_error_msg(INFO"new: %s/current", ld->name);
668 while (fchdir(fdwdir) == -1)
669 pause1cannot("change to initial working directory");
670 return 1;
673 static void logdirs_reopen(void)
675 int l;
676 int ok = 0;
678 tmaxflag = 0;
679 for (l = 0; l < dirn; ++l) {
680 logdir_close(&dir[l]);
681 if (logdir_open(&dir[l], fndir[l]))
682 ok = 1;
684 if (!ok)
685 fatalx("no functional log directories");
688 /* Will look good in libbb one day */
689 static ssize_t ndelay_read(int fd, void *buf, size_t count)
691 if (!(fl_flag_0 & O_NONBLOCK))
692 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
693 count = safe_read(fd, buf, count);
694 if (!(fl_flag_0 & O_NONBLOCK))
695 fcntl(fd, F_SETFL, fl_flag_0);
696 return count;
699 /* Used for reading stdin */
700 static int buffer_pread(/*int fd, */char *s, unsigned len)
702 unsigned now;
703 struct pollfd input;
704 int i;
706 input.fd = 0;
707 input.events = POLLIN;
709 do {
710 if (rotateasap) {
711 for (i = 0; i < dirn; ++i)
712 rotate(dir + i);
713 rotateasap = 0;
715 if (exitasap) {
716 if (linecomplete)
717 return 0;
718 len = 1;
720 if (reopenasap) {
721 logdirs_reopen();
722 reopenasap = 0;
724 now = monotonic_sec();
725 nearest_rotate = now + (45 * 60 + 45);
726 for (i = 0; i < dirn; ++i) {
727 if (dir[i].rotate_period) {
728 if (LESS(dir[i].next_rotate, now))
729 rotate(dir + i);
730 if (LESS(dir[i].next_rotate, nearest_rotate))
731 nearest_rotate = dir[i].next_rotate;
735 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
736 i = nearest_rotate - now;
737 if (i > 1000000)
738 i = 1000000;
739 if (i <= 0)
740 i = 1;
741 poll(&input, 1, i * 1000);
742 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
744 i = ndelay_read(0, s, len);
745 if (i >= 0)
746 break;
747 if (errno == EINTR)
748 continue;
749 if (errno != EAGAIN) {
750 warn("cannot read standard input");
751 break;
753 /* else: EAGAIN - normal, repeat silently */
754 } while (!exitasap);
756 if (i > 0) {
757 int cnt;
758 linecomplete = (s[i-1] == '\n');
759 if (!repl)
760 return i;
762 cnt = i;
763 while (--cnt >= 0) {
764 char ch = *s;
765 if (ch != '\n') {
766 if (ch < 32 || ch > 126)
767 *s = repl;
768 else {
769 int j;
770 for (j = 0; replace[j]; ++j) {
771 if (ch == replace[j]) {
772 *s = repl;
773 break;
778 s++;
781 return i;
784 static void sig_term_handler(int sig_no)
786 if (verbose)
787 bb_error_msg(INFO"sig%s received", "term");
788 exitasap = 1;
791 static void sig_child_handler(int sig_no)
793 int pid, l;
795 if (verbose)
796 bb_error_msg(INFO"sig%s received", "child");
797 while ((pid = wait_nohang(&wstat)) > 0) {
798 for (l = 0; l < dirn; ++l) {
799 if (dir[l].ppid == pid) {
800 dir[l].ppid = 0;
801 processorstop(&dir[l]);
802 break;
808 static void sig_alarm_handler(int sig_no)
810 if (verbose)
811 bb_error_msg(INFO"sig%s received", "alarm");
812 rotateasap = 1;
815 static void sig_hangup_handler(int sig_no)
817 if (verbose)
818 bb_error_msg(INFO"sig%s received", "hangup");
819 reopenasap = 1;
822 static void logmatch(struct logdir *ld)
824 char *s;
826 ld->match = '+';
827 ld->matcherr = 'E';
828 s = ld->inst;
829 while (s && s[0]) {
830 switch (s[0]) {
831 case '+':
832 case '-':
833 if (pmatch(s+1, line, linelen))
834 ld->match = s[0];
835 break;
836 case 'e':
837 case 'E':
838 if (pmatch(s+1, line, linelen))
839 ld->matcherr = s[0];
840 break;
842 s += strlen(s) + 1;
846 int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
847 int svlogd_main(int argc, char **argv)
849 char *r,*l,*b;
850 ssize_t stdin_cnt = 0;
851 int i;
852 unsigned opt;
853 unsigned timestamp = 0;
854 void* (*memRchr)(const void *, int, size_t) = memchr;
856 INIT_G();
858 opt_complementary = "tt:vv";
859 opt = getopt32(argv, "r:R:l:b:tv",
860 &r, &replace, &l, &b, &timestamp, &verbose);
861 if (opt & 1) { // -r
862 repl = r[0];
863 if (!repl || r[1]) usage();
865 if (opt & 2) if (!repl) repl = '_'; // -R
866 if (opt & 4) { // -l
867 linemax = xatou_range(l, 0, BUFSIZ-26);
868 if (linemax == 0) linemax = BUFSIZ-26;
869 if (linemax < 256) linemax = 256;
871 ////if (opt & 8) { // -b
872 //// buflen = xatoi_u(b);
873 //// if (buflen == 0) buflen = 1024;
874 ////}
875 //if (opt & 0x10) timestamp++; // -t
876 //if (opt & 0x20) verbose++; // -v
877 //if (timestamp > 2) timestamp = 2;
878 argv += optind;
879 argc -= optind;
881 dirn = argc;
882 if (dirn <= 0) usage();
883 ////if (buflen <= linemax) usage();
884 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
885 close_on_exec_on(fdwdir);
886 dir = xzalloc(dirn * sizeof(struct logdir));
887 for (i = 0; i < dirn; ++i) {
888 dir[i].fddir = -1;
889 dir[i].fdcur = -1;
890 ////dir[i].btmp = xmalloc(buflen);
891 /*dir[i].ppid = 0;*/
893 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
894 fndir = argv;
895 /* We cannot set NONBLOCK on fd #0 permanently - this setting
896 * _isn't_ per-process! It is shared among all other processes
897 * with the same stdin */
898 fl_flag_0 = fcntl(0, F_GETFL);
900 sigemptyset(&blocked_sigset);
901 sigaddset(&blocked_sigset, SIGTERM);
902 sigaddset(&blocked_sigset, SIGCHLD);
903 sigaddset(&blocked_sigset, SIGALRM);
904 sigaddset(&blocked_sigset, SIGHUP);
905 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
906 sig_catch(SIGTERM, sig_term_handler);
907 sig_catch(SIGCHLD, sig_child_handler);
908 sig_catch(SIGALRM, sig_alarm_handler);
909 sig_catch(SIGHUP, sig_hangup_handler);
911 logdirs_reopen();
913 /* Without timestamps, we don't have to print each line
914 * separately, so we can look for _last_ newline, not first,
915 * thus batching writes */
916 if (!timestamp)
917 memRchr = memrchr;
919 setvbuf(stderr, NULL, _IOFBF, linelen);
921 /* Each iteration processes one or more lines */
922 while (1) {
923 char stamp[FMT_PTIME];
924 char *lineptr;
925 char *printptr;
926 char *np;
927 int printlen;
928 char ch;
930 lineptr = line;
931 if (timestamp)
932 lineptr += 26;
934 /* lineptr[0..linemax-1] - buffer for stdin */
935 /* (possibly has some unprocessed data from prev loop) */
937 /* Refill the buffer if needed */
938 np = memRchr(lineptr, '\n', stdin_cnt);
939 if (!np && !exitasap) {
940 i = linemax - stdin_cnt; /* avail. bytes at tail */
941 if (i >= 128) {
942 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
943 if (i <= 0) /* EOF or error on stdin */
944 exitasap = 1;
945 else {
946 np = memRchr(lineptr + stdin_cnt, '\n', i);
947 stdin_cnt += i;
951 if (stdin_cnt <= 0 && exitasap)
952 break;
954 /* Search for '\n' (in fact, np already holds the result) */
955 linelen = stdin_cnt;
956 if (np) {
957 print_to_nl: /* NB: starting from here lineptr may point
958 * farther out into line[] */
959 linelen = np - lineptr + 1;
961 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
962 ch = lineptr[linelen-1];
964 /* Biggest performance hit was coming from the fact
965 * that we did not buffer writes. We were reading many lines
966 * in one read() above, but wrote one line per write().
967 * We are using stdio to fix that */
969 /* write out lineptr[0..linelen-1] to each log destination
970 * (or lineptr[-26..linelen-1] if timestamping) */
971 printlen = linelen;
972 printptr = lineptr;
973 if (timestamp) {
974 if (timestamp == 1)
975 fmt_time_bernstein_25(stamp);
976 else /* 2: */
977 fmt_time_human_30nul(stamp);
978 printlen += 26;
979 printptr -= 26;
980 memcpy(printptr, stamp, 25);
981 printptr[25] = ' ';
983 for (i = 0; i < dirn; ++i) {
984 struct logdir *ld = &dir[i];
985 if (ld->fddir == -1) continue;
986 if (ld->inst)
987 logmatch(ld);
988 if (ld->matcherr == 'e') {
989 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
990 ////full_write(2, printptr, printlen);
991 fwrite(printptr, 1, printlen, stderr);
993 if (ld->match != '+') continue;
994 buffer_pwrite(i, printptr, printlen);
997 /* If we didn't see '\n' (long input line), */
998 /* read/write repeatedly until we see it */
999 while (ch != '\n') {
1000 /* lineptr is emptied now, safe to use as buffer */
1001 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
1002 if (stdin_cnt <= 0) { /* EOF or error on stdin */
1003 exitasap = 1;
1004 lineptr[0] = ch = '\n';
1005 linelen = 1;
1006 stdin_cnt = 1;
1007 } else {
1008 linelen = stdin_cnt;
1009 np = memRchr(lineptr, '\n', stdin_cnt);
1010 if (np)
1011 linelen = np - lineptr + 1;
1012 ch = lineptr[linelen-1];
1014 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1015 for (i = 0; i < dirn; ++i) {
1016 if (dir[i].fddir == -1) continue;
1017 if (dir[i].matcherr == 'e') {
1018 ////full_write(2, lineptr, linelen);
1019 fwrite(lineptr, 1, linelen, stderr);
1021 if (dir[i].match != '+') continue;
1022 buffer_pwrite(i, lineptr, linelen);
1026 stdin_cnt -= linelen;
1027 if (stdin_cnt > 0) {
1028 lineptr += linelen;
1029 /* If we see another '\n', we don't need to read
1030 * next piece of input: can print what we have */
1031 np = memRchr(lineptr, '\n', stdin_cnt);
1032 if (np)
1033 goto print_to_nl;
1034 /* Move unprocessed data to the front of line */
1035 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1037 fflush(NULL);////
1040 for (i = 0; i < dirn; ++i) {
1041 if (dir[i].ppid)
1042 while (!processorstop(&dir[i]))
1043 /* repeat */;
1044 logdir_close(&dir[i]);
1046 return 0;