2 Copyright (c) 2001-2006, Gerrit Pape
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 */
34 #include "runit_lib.h"
36 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
42 /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
50 unsigned rotate_period;
57 char fnsave[FMT_PTIME];
73 unsigned nearest_rotate;
78 smallint linecomplete;
86 sigset_t blocked_sigset;
88 #define G (*(struct globals*)ptr_to_globals)
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))); \
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");
146 static void pause1cannot(const char *m0)
148 bb_perror_msg(PAUSE"cannot %s", m0);
151 static void pause2cannot(const char *m0, const char *m1)
153 bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
157 static char* wstrdup(const char *str)
160 while (!(s = strdup(str)))
165 /*** ex fmt_ptime.[ch] ***/
168 static void fmt_time_human_30nul(char *s)
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)
196 gettimeofday(&tv, NULL);
197 sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
198 tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
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);
206 bin2hex(s, (char*)pack, 12);
209 static void processorstart(struct logdir *ld)
214 if (!ld->processor) return;
216 warnx("processor already running", ld->name);
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);
235 sig_unblock(SIGTERM);
236 sig_unblock(SIGALRM);
240 bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
241 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
243 ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
244 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
246 fd = open_read("state");
249 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
250 close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
251 fd = xopen("state", O_RDONLY|O_NDELAY);
254 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
258 prog[0] = (char*)"sh";
259 prog[1] = (char*)"-c";
260 prog[2] = ld->processor;
262 execv("/bin/sh", prog);
263 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
265 ld->fnsave[26] = sv_ch; /* ...restore */
269 static unsigned processorstop(struct logdir *ld)
275 while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
276 pause2cannot("wait for processor", ld->name);
280 if (ld->fddir == -1) return 1;
281 while (fchdir(ld->fddir) == -1)
282 pause2cannot("change directory, want processor", ld->name);
283 if (wait_exitcode(wstat) != 0) {
284 warnx("processor failed, restart", ld->name);
285 ld->fnsave[26] = 't';
287 ld->fnsave[26] = 'u';
289 while (fchdir(fdwdir) == -1)
290 pause1cannot("change to initial working directory");
291 return ld->processor ? 0 : 1;
293 ld->fnsave[26] = 't';
294 memcpy(f, ld->fnsave, 26);
297 while (rename(ld->fnsave, f) == -1)
298 pause2cannot("rename processed", ld->name);
299 while (chmod(f, 0744) == -1)
300 pause2cannot("set mode of processed", ld->name);
301 ld->fnsave[26] = 'u';
302 if (unlink(ld->fnsave) == -1)
303 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
304 while (rename("newstate", "state") == -1)
305 pause2cannot("rename state", ld->name);
307 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
308 while (fchdir(fdwdir) == -1)
309 pause1cannot("change to initial working directory");
313 static void rmoldest(struct logdir *ld)
317 char oldest[FMT_PTIME];
320 oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
321 while (!(d = opendir(".")))
322 pause2cannot("open directory, want rotate", ld->name);
324 while ((f = readdir(d))) {
325 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
326 if (f->d_name[26] == 't') {
327 if (unlink(f->d_name) == -1)
328 warn2("cannot unlink processor leftover", f->d_name);
331 if (strcmp(f->d_name, oldest) < 0)
332 memcpy(oldest, f->d_name, 27);
338 warn2("cannot read directory", ld->name);
341 if (ld->nmax && (n > ld->nmax)) {
343 bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
344 if ((*oldest == '@') && (unlink(oldest) == -1))
345 warn2("cannot unlink oldest logfile", ld->name);
349 static unsigned rotate(struct logdir *ld)
354 if (ld->fddir == -1) {
355 ld->rotate_period = 0;
359 while (!processorstop(ld))
362 while (fchdir(ld->fddir) == -1)
363 pause2cannot("change directory, want rotate", ld->name);
365 /* create new filename */
366 ld->fnsave[25] = '.';
367 ld->fnsave[26] = 's';
369 ld->fnsave[26] = 'u';
370 ld->fnsave[27] = '\0';
372 fmt_time_bernstein_25(ld->fnsave);
374 stat(ld->fnsave, &st);
375 } while (errno != ENOENT);
377 now = monotonic_sec();
378 if (ld->rotate_period && LESS(ld->next_rotate, now)) {
379 ld->next_rotate = now + ld->rotate_period;
380 if (LESS(ld->next_rotate, nearest_rotate))
381 nearest_rotate = ld->next_rotate;
385 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
386 pause2cannot("fsync current logfile", ld->name);
387 while (fchmod(ld->fdcur, 0744) == -1)
388 pause2cannot("set mode of current", ld->name);
389 ////close(ld->fdcur);
393 bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
394 ld->fnsave, ld->size);
396 while (rename("current", ld->fnsave) == -1)
397 pause2cannot("rename current", ld->name);
398 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
399 pause2cannot("create new current", ld->name);
400 /* we presume this cannot fail */
401 ld->filecur = fdopen(ld->fdcur, "a"); ////
402 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
403 close_on_exec_on(ld->fdcur);
405 while (fchmod(ld->fdcur, 0644) == -1)
406 pause2cannot("set mode of current", ld->name);
411 while (fchdir(fdwdir) == -1)
412 pause1cannot("change to initial working directory");
416 static int buffer_pwrite(int n, char *s, unsigned len)
419 struct logdir *ld = &dir[n];
422 if (ld->size >= ld->sizemax)
424 if (len > (ld->sizemax - ld->size))
425 len = ld->sizemax - ld->size;
428 ////i = full_write(ld->fdcur, s, len);
429 ////if (i != -1) break;
430 i = fwrite(s, 1, len, ld->filecur);
433 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
436 char oldest[FMT_PTIME];
439 while (fchdir(ld->fddir) == -1)
440 pause2cannot("change directory, want remove old logfile",
443 oldest[1] = oldest[27] = '\0';
444 while (!(d = opendir(".")))
445 pause2cannot("open directory, want remove old logfile",
448 while ((f = readdir(d)))
449 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
451 if (strcmp(f->d_name, oldest) < 0)
452 memcpy(oldest, f->d_name, 27);
454 if (errno) warn2("cannot read directory, want remove old logfile",
459 if (*oldest == '@') {
460 bb_error_msg(WARNING"out of disk space, delete: %s/%s",
463 if (unlink(oldest) == -1) {
464 warn2("cannot unlink oldest logfile", ld->name);
467 while (fchdir(fdwdir) == -1)
468 pause1cannot("change to initial working directory");
473 pause2cannot("write to current", ld->name);
479 if (ld->size >= (ld->sizemax - linemax))
484 static void logdir_close(struct logdir *ld)
489 bb_error_msg(INFO"close: %s", ld->name);
493 return; /* impossible */
494 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
495 pause2cannot("fsync current logfile", ld->name);
496 while (fchmod(ld->fdcur, 0744) == -1)
497 pause2cannot("set mode of current", ld->name);
498 ////close(ld->fdcur);
501 if (ld->fdlock == -1)
502 return; /* impossible */
506 ld->processor = NULL;
509 static unsigned logdir_open(struct logdir *ld, const char *fn)
517 now = monotonic_sec();
519 ld->fddir = open(fn, O_RDONLY|O_NDELAY);
520 if (ld->fddir == -1) {
521 warn2("cannot open log directory", (char*)fn);
524 close_on_exec_on(ld->fddir);
525 if (fchdir(ld->fddir) == -1) {
527 warn2("cannot change directory", (char*)fn);
530 ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
531 if ((ld->fdlock == -1)
532 || (lock_exnb(ld->fdlock) == -1)
535 warn2("cannot lock directory", (char*)fn);
536 while (fchdir(fdwdir) == -1)
537 pause1cannot("change to initial working directory");
540 close_on_exec_on(ld->fdlock);
543 ld->sizemax = 1000000;
544 ld->nmax = ld->nmin = 10;
545 ld->rotate_period = 0;
546 ld->name = (char*)fn;
549 free(ld->inst); ld->inst = NULL;
550 free(ld->processor); ld->processor = NULL;
553 i = open_read_close("config", buf, sizeof(buf));
554 if (i < 0 && errno != ENOENT)
555 bb_perror_msg(WARNING"%s/config", ld->name);
558 bb_error_msg(INFO"read: %s/config", ld->name);
561 np = strchr(s, '\n');
569 /* Add '\n'-terminated line to ld->inst */
571 int l = asprintf(&new, "%s%s\n", ld->inst ? : "", s);
580 static const struct suffix_mult km_suffixes[] = {
585 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
589 ld->nmax = xatoi_u(&s[1]);
592 ld->nmin = xatoi_u(&s[1]);
595 static const struct suffix_mult mh_suffixes[] = {
598 /*{ "d", 24*60*60 },*/
601 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
602 if (ld->rotate_period) {
603 ld->next_rotate = now + ld->rotate_period;
604 if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
605 nearest_rotate = ld->next_rotate;
613 ld->processor = wstrdup(s);
619 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
622 np = strchr(s, '\n');
630 i = stat("current", &st);
632 if (st.st_size && !(st.st_mode & S_IXUSR)) {
633 ld->fnsave[25] = '.';
634 ld->fnsave[26] = 'u';
635 ld->fnsave[27] = '\0';
637 fmt_time_bernstein_25(ld->fnsave);
639 stat(ld->fnsave, &st);
640 } while (errno != ENOENT);
641 while (rename("current", ld->fnsave) == -1)
642 pause2cannot("rename current", ld->name);
646 /* st.st_size can be not just bigger, but WIDER!
647 * This code is safe: if st.st_size > 4GB, we select
648 * ld->sizemax (because it's "unsigned") */
649 ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
652 if (errno != ENOENT) {
654 warn2("cannot stat current", ld->name);
655 while (fchdir(fdwdir) == -1)
656 pause1cannot("change to initial working directory");
660 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
661 pause2cannot("open current", ld->name);
662 /* we presume this cannot fail */
663 ld->filecur = fdopen(ld->fdcur, "a"); ////
664 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
666 close_on_exec_on(ld->fdcur);
667 while (fchmod(ld->fdcur, 0644) == -1)
668 pause2cannot("set mode of current", ld->name);
671 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
672 else bb_error_msg(INFO"new: %s/current", ld->name);
675 while (fchdir(fdwdir) == -1)
676 pause1cannot("change to initial working directory");
680 static void logdirs_reopen(void)
686 for (l = 0; l < dirn; ++l) {
687 logdir_close(&dir[l]);
688 if (logdir_open(&dir[l], fndir[l]))
692 fatalx("no functional log directories");
695 /* Will look good in libbb one day */
696 static ssize_t ndelay_read(int fd, void *buf, size_t count)
698 if (!(fl_flag_0 & O_NONBLOCK))
699 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
700 count = safe_read(fd, buf, count);
701 if (!(fl_flag_0 & O_NONBLOCK))
702 fcntl(fd, F_SETFL, fl_flag_0);
706 /* Used for reading stdin */
707 static int buffer_pread(/*int fd, */char *s, unsigned len)
714 input.events = POLLIN;
718 for (i = 0; i < dirn; ++i)
731 now = monotonic_sec();
732 nearest_rotate = now + (45 * 60 + 45);
733 for (i = 0; i < dirn; ++i) {
734 if (dir[i].rotate_period) {
735 if (LESS(dir[i].next_rotate, now))
737 if (LESS(dir[i].next_rotate, nearest_rotate))
738 nearest_rotate = dir[i].next_rotate;
742 sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
743 i = nearest_rotate - now;
748 poll(&input, 1, i * 1000);
749 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
751 i = ndelay_read(STDIN_FILENO, s, len);
756 if (errno != EAGAIN) {
757 warn("cannot read standard input");
760 /* else: EAGAIN - normal, repeat silently */
765 linecomplete = (s[i-1] == '\n');
773 if (ch < 32 || ch > 126)
777 for (j = 0; replace[j]; ++j) {
778 if (ch == replace[j]) {
791 static void sig_term_handler(int sig_no ATTRIBUTE_UNUSED)
794 bb_error_msg(INFO"sig%s received", "term");
798 static void sig_child_handler(int sig_no ATTRIBUTE_UNUSED)
803 bb_error_msg(INFO"sig%s received", "child");
804 while ((pid = wait_any_nohang(&wstat)) > 0) {
805 for (l = 0; l < dirn; ++l) {
806 if (dir[l].ppid == pid) {
808 processorstop(&dir[l]);
815 static void sig_alarm_handler(int sig_no ATTRIBUTE_UNUSED)
818 bb_error_msg(INFO"sig%s received", "alarm");
822 static void sig_hangup_handler(int sig_no ATTRIBUTE_UNUSED)
825 bb_error_msg(INFO"sig%s received", "hangup");
829 static void logmatch(struct logdir *ld)
840 if (pmatch(s+1, line, linelen))
845 if (pmatch(s+1, line, linelen))
853 int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
854 int svlogd_main(int argc, char **argv)
857 ssize_t stdin_cnt = 0;
860 unsigned timestamp = 0;
861 void* (*memRchr)(const void *, int, size_t) = memchr;
865 opt_complementary = "tt:vv";
866 opt = getopt32(argv, "r:R:l:b:tv",
867 &r, &replace, &l, &b, ×tamp, &verbose);
870 if (!repl || r[1]) usage();
872 if (opt & 2) if (!repl) repl = '_'; // -R
874 linemax = xatou_range(l, 0, BUFSIZ-26);
875 if (linemax == 0) linemax = BUFSIZ-26;
876 if (linemax < 256) linemax = 256;
878 ////if (opt & 8) { // -b
879 //// buflen = xatoi_u(b);
880 //// if (buflen == 0) buflen = 1024;
882 //if (opt & 0x10) timestamp++; // -t
883 //if (opt & 0x20) verbose++; // -v
884 //if (timestamp > 2) timestamp = 2;
889 if (dirn <= 0) usage();
890 ////if (buflen <= linemax) usage();
891 fdwdir = xopen(".", O_RDONLY|O_NDELAY);
892 close_on_exec_on(fdwdir);
893 dir = xzalloc(dirn * sizeof(struct logdir));
894 for (i = 0; i < dirn; ++i) {
897 ////dir[i].btmp = xmalloc(buflen);
900 /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
902 /* We cannot set NONBLOCK on fd #0 permanently - this setting
903 * _isn't_ per-process! It is shared among all other processes
904 * with the same stdin */
905 fl_flag_0 = fcntl(0, F_GETFL);
907 sigemptyset(&blocked_sigset);
908 sigaddset(&blocked_sigset, SIGTERM);
909 sigaddset(&blocked_sigset, SIGCHLD);
910 sigaddset(&blocked_sigset, SIGALRM);
911 sigaddset(&blocked_sigset, SIGHUP);
912 sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
913 bb_signals_recursive(1 << SIGTERM, sig_term_handler);
914 bb_signals_recursive(1 << SIGCHLD, sig_child_handler);
915 bb_signals_recursive(1 << SIGALRM, sig_alarm_handler);
916 bb_signals_recursive(1 << SIGHUP, sig_hangup_handler);
920 /* Without timestamps, we don't have to print each line
921 * separately, so we can look for _last_ newline, not first,
922 * thus batching writes */
926 setvbuf(stderr, NULL, _IOFBF, linelen);
928 /* Each iteration processes one or more lines */
930 char stamp[FMT_PTIME];
941 /* lineptr[0..linemax-1] - buffer for stdin */
942 /* (possibly has some unprocessed data from prev loop) */
944 /* Refill the buffer if needed */
945 np = memRchr(lineptr, '\n', stdin_cnt);
946 if (!np && !exitasap) {
947 i = linemax - stdin_cnt; /* avail. bytes at tail */
949 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
950 if (i <= 0) /* EOF or error on stdin */
953 np = memRchr(lineptr + stdin_cnt, '\n', i);
958 if (stdin_cnt <= 0 && exitasap)
961 /* Search for '\n' (in fact, np already holds the result) */
964 print_to_nl: /* NB: starting from here lineptr may point
965 * farther out into line[] */
966 linelen = np - lineptr + 1;
968 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
969 ch = lineptr[linelen-1];
971 /* Biggest performance hit was coming from the fact
972 * that we did not buffer writes. We were reading many lines
973 * in one read() above, but wrote one line per write().
974 * We are using stdio to fix that */
976 /* write out lineptr[0..linelen-1] to each log destination
977 * (or lineptr[-26..linelen-1] if timestamping) */
982 fmt_time_bernstein_25(stamp);
984 fmt_time_human_30nul(stamp);
987 memcpy(printptr, stamp, 25);
990 for (i = 0; i < dirn; ++i) {
991 struct logdir *ld = &dir[i];
992 if (ld->fddir == -1) continue;
995 if (ld->matcherr == 'e') {
996 /* runit-1.8.0 compat: if timestamping, do it on stderr too */
997 ////full_write(STDERR_FILENO, printptr, printlen);
998 fwrite(printptr, 1, printlen, stderr);
1000 if (ld->match != '+') continue;
1001 buffer_pwrite(i, printptr, printlen);
1004 /* If we didn't see '\n' (long input line), */
1005 /* read/write repeatedly until we see it */
1006 while (ch != '\n') {
1007 /* lineptr is emptied now, safe to use as buffer */
1008 stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
1009 if (stdin_cnt <= 0) { /* EOF or error on stdin */
1011 lineptr[0] = ch = '\n';
1015 linelen = stdin_cnt;
1016 np = memRchr(lineptr, '\n', stdin_cnt);
1018 linelen = np - lineptr + 1;
1019 ch = lineptr[linelen-1];
1021 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1022 for (i = 0; i < dirn; ++i) {
1023 if (dir[i].fddir == -1) continue;
1024 if (dir[i].matcherr == 'e') {
1025 ////full_write(STDERR_FILENO, lineptr, linelen);
1026 fwrite(lineptr, 1, linelen, stderr);
1028 if (dir[i].match != '+') continue;
1029 buffer_pwrite(i, lineptr, linelen);
1033 stdin_cnt -= linelen;
1034 if (stdin_cnt > 0) {
1036 /* If we see another '\n', we don't need to read
1037 * next piece of input: can print what we have */
1038 np = memRchr(lineptr, '\n', stdin_cnt);
1041 /* Move unprocessed data to the front of line */
1042 memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1047 for (i = 0; i < dirn; ++i) {
1049 while (!processorstop(&dir[i]))
1051 logdir_close(&dir[i]);