runit/*: get rid of tai[a] time abstraction, it's too bloaty.
[oweals/busybox.git] / runit / svlogd.c
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 Denis Vlasenko <vda.linux@googlemail.com> */
29 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31 #include <sys/poll.h>
32 #include <sys/file.h>
33 #include "libbb.h"
34 #include "runit_lib.h"
35
36 #define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
37
38 #define FMT_PTIME 30
39
40 static unsigned verbose;
41 static int linemax = 1000;
42 ////static int buflen = 1024;
43 static int linelen;
44
45 static char **fndir;
46 static int fdwdir;
47 static int wstat;
48 static unsigned nearest_rotate;
49
50 static char *line;
51 static smallint exitasap;
52 static smallint rotateasap;
53 static smallint reopenasap;
54 static smallint linecomplete = 1;
55
56 static smallint tmaxflag;
57
58 static char repl;
59 static const char *replace = "";
60
61 static sigset_t *blocked_sigset;
62 static int fl_flag_0;
63
64 static struct logdir {
65         ////char *btmp;
66         /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
67         char *inst;
68         char *processor;
69         char *name;
70         unsigned size;
71         unsigned sizemax;
72         unsigned nmax;
73         unsigned nmin;
74         unsigned rotate_period;
75         int ppid;
76         int fddir;
77         int fdcur;
78         FILE* filecur; ////
79         int fdlock;
80         unsigned next_rotate;
81         char fnsave[FMT_PTIME];
82         char match;
83         char matcherr;
84 } *dir;
85 static unsigned dirn;
86
87 #define FATAL "fatal: "
88 #define WARNING "warning: "
89 #define PAUSE "pausing: "
90 #define INFO "info: "
91
92 #define usage() bb_show_usage()
93 static void fatalx(const char *m0)
94 {
95         bb_error_msg_and_die(FATAL"%s", m0);
96 }
97 static void warn(const char *m0)
98 {
99         bb_perror_msg(WARNING"%s", m0);
100 }
101 static void warn2(const char *m0, const char *m1)
102 {
103         bb_perror_msg(WARNING"%s: %s", m0, m1);
104 }
105 static void warnx(const char *m0, const char *m1)
106 {
107         bb_error_msg(WARNING"%s: %s", m0, m1);
108 }
109 static void pause_nomem(void)
110 {
111         bb_error_msg(PAUSE"out of memory");
112         sleep(3);
113 }
114 static void pause1cannot(const char *m0)
115 {
116         bb_perror_msg(PAUSE"cannot %s", m0);
117         sleep(3);
118 }
119 static void pause2cannot(const char *m0, const char *m1)
120 {
121         bb_perror_msg(PAUSE"cannot %s %s", m0, m1);
122         sleep(3);
123 }
124
125 static char* wstrdup(const char *str)
126 {
127         char *s;
128         while (!(s = strdup(str)))
129                 pause_nomem();
130         return s;
131 }
132
133 /*** ex fmt_ptime.[ch] ***/
134
135 /* NUL terminated */
136 static void fmt_time_human_30nul(char *s)
137 {
138         struct tm *t;
139         struct timeval tv;
140
141         gettimeofday(&tv, NULL);
142         t = gmtime(&(tv.tv_sec));
143         sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
144                 (unsigned)(1900 + t->tm_year),
145                 (unsigned)(t->tm_mon + 1),
146                 (unsigned)(t->tm_mday),
147                 (unsigned)(t->tm_hour),
148                 (unsigned)(t->tm_min),
149                 (unsigned)(t->tm_sec),
150                 (unsigned)(tv.tv_usec)
151         );
152         /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
153         /* 5   + 3   + 3   + 3   + 3   + 3   + 9 = */
154         /* 20 (up to '.' inclusive) + 9 (not including '\0') */
155 }
156
157 /* NOT terminated! */
158 static void fmt_time_bernstein_25(char *s)
159 {
160         uint32_t pack[3];
161         struct timeval tv;
162         unsigned sec_hi;
163
164         gettimeofday(&tv, NULL);
165         sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
166         tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
167         tv.tv_usec *= 1000;
168         /* Network order is big-endian: most significant byte first.
169          * This is exactly what we want here */
170         pack[0] = htonl(sec_hi);
171         pack[1] = htonl(tv.tv_sec);
172         pack[2] = htonl(tv.tv_usec);
173         *s++ = '@';
174         bin2hex(s, (char*)pack, 12);
175 }
176
177 static unsigned processorstart(struct logdir *ld)
178 {
179         int pid;
180
181         if (!ld->processor) return 0;
182         if (ld->ppid) {
183                 warnx("processor already running", ld->name);
184                 return 0;
185         }
186         while ((pid = fork()) == -1)
187                 pause2cannot("fork for processor", ld->name);
188         if (!pid) {
189                 char *prog[4];
190                 int fd;
191
192                 /* child */
193                 signal(SIGTERM, SIG_DFL);
194                 signal(SIGALRM, SIG_DFL);
195                 signal(SIGHUP, SIG_DFL);
196                 sig_unblock(SIGTERM);
197                 sig_unblock(SIGALRM);
198                 sig_unblock(SIGHUP);
199
200                 if (verbose)
201                         bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
202                 fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
203                 xmove_fd(fd, 0);
204                 ld->fnsave[26] = 't';
205                 fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
206                 xmove_fd(fd, 1);
207                 fd = open_read("state");
208                 if (fd == -1) {
209                         if (errno != ENOENT)
210                                 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "open state for", ld->name);
211                         close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
212                         fd = xopen("state", O_RDONLY|O_NDELAY);
213                 }
214                 xmove_fd(fd, 4);
215                 fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
216                 xmove_fd(fd, 5);
217
218 // getenv("SHELL")?
219                 prog[0] = (char*)"sh";
220                 prog[1] = (char*)"-c";
221                 prog[2] = ld->processor;
222                 prog[3] = NULL;
223                 execve("/bin/sh", prog, environ);
224                 bb_perror_msg_and_die(FATAL"cannot %s processor %s", "run", ld->name);
225         }
226         ld->ppid = pid;
227         return 1;
228 }
229
230 static unsigned processorstop(struct logdir *ld)
231 {
232         char f[28];
233
234         if (ld->ppid) {
235                 sig_unblock(SIGHUP);
236                 while (wait_pid(&wstat, ld->ppid) == -1)
237                         pause2cannot("wait for processor", ld->name);
238                 sig_block(SIGHUP);
239                 ld->ppid = 0;
240         }
241         if (ld->fddir == -1) return 1;
242         while (fchdir(ld->fddir) == -1)
243                 pause2cannot("change directory, want processor", ld->name);
244         if (wait_exitcode(wstat) != 0) {
245                 warnx("processor failed, restart", ld->name);
246                 ld->fnsave[26] = 't';
247                 unlink(ld->fnsave);
248                 ld->fnsave[26] = 'u';
249                 processorstart(ld);
250                 while (fchdir(fdwdir) == -1)
251                         pause1cannot("change to initial working directory");
252                 return ld->processor ? 0 : 1;
253         }
254         ld->fnsave[26] = 't';
255         memcpy(f, ld->fnsave, 26);
256         f[26] = 's';
257         f[27] = '\0';
258         while (rename(ld->fnsave, f) == -1)
259                 pause2cannot("rename processed", ld->name);
260         while (chmod(f, 0744) == -1)
261                 pause2cannot("set mode of processed", ld->name);
262         ld->fnsave[26] = 'u';
263         if (unlink(ld->fnsave) == -1)
264                 bb_error_msg(WARNING"cannot unlink: %s/%s", ld->name, ld->fnsave);
265         while (rename("newstate", "state") == -1)
266                 pause2cannot("rename state", ld->name);
267         if (verbose)
268                 bb_error_msg(INFO"processed: %s/%s", ld->name, f);
269         while (fchdir(fdwdir) == -1)
270                 pause1cannot("change to initial working directory");
271         return 1;
272 }
273
274 static void rmoldest(struct logdir *ld)
275 {
276         DIR *d;
277         struct dirent *f;
278         char oldest[FMT_PTIME];
279         int n = 0;
280
281         oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
282         while (!(d = opendir(".")))
283                 pause2cannot("open directory, want rotate", ld->name);
284         errno = 0;
285         while ((f = readdir(d))) {
286                 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
287                         if (f->d_name[26] == 't') {
288                                 if (unlink(f->d_name) == -1)
289                                         warn2("cannot unlink processor leftover", f->d_name);
290                         } else {
291                                 ++n;
292                                 if (strcmp(f->d_name, oldest) < 0)
293                                         memcpy(oldest, f->d_name, 27);
294                         }
295                         errno = 0;
296                 }
297         }
298         if (errno)
299                 warn2("cannot read directory", ld->name);
300         closedir(d);
301
302         if (ld->nmax && (n > ld->nmax)) {
303                 if (verbose)
304                         bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
305                 if ((*oldest == '@') && (unlink(oldest) == -1))
306                         warn2("cannot unlink oldest logfile", ld->name);
307         }
308 }
309
310 static unsigned rotate(struct logdir *ld)
311 {
312         struct stat st;
313         unsigned now;
314
315         if (ld->fddir == -1) {
316                 ld->rotate_period = 0;
317                 return 0;
318         }
319         if (ld->ppid)
320                 while (!processorstop(ld))
321                         continue;
322
323         while (fchdir(ld->fddir) == -1)
324                 pause2cannot("change directory, want rotate", ld->name);
325
326         /* create new filename */
327         ld->fnsave[25] = '.';
328         ld->fnsave[26] = 's';
329         if (ld->processor)
330                 ld->fnsave[26] = 'u';
331         ld->fnsave[27] = '\0';
332         do {
333                 fmt_time_bernstein_25(ld->fnsave);
334                 errno = 0;
335                 stat(ld->fnsave, &st);
336         } while (errno != ENOENT);
337
338         now = monotonic_sec();
339         if (ld->rotate_period && LESS(ld->next_rotate, now)) {
340                 ld->next_rotate = now + ld->rotate_period;
341                 if (LESS(ld->next_rotate, nearest_rotate))
342                         nearest_rotate = ld->next_rotate;
343         }
344
345         if (ld->size > 0) {
346                 while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
347                         pause2cannot("fsync current logfile", ld->name);
348                 while (fchmod(ld->fdcur, 0744) == -1)
349                         pause2cannot("set mode of current", ld->name);
350                 ////close(ld->fdcur);
351                 fclose(ld->filecur);
352
353                 if (verbose) {
354                         bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
355                                         ld->fnsave, ld->size);
356                 }
357                 while (rename("current", ld->fnsave) == -1)
358                         pause2cannot("rename current", ld->name);
359                 while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
360                         pause2cannot("create new current", ld->name);
361                 /* we presume this cannot fail */
362                 ld->filecur = fdopen(ld->fdcur, "a"); ////
363                 setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
364                 coe(ld->fdcur);
365                 ld->size = 0;
366                 while (fchmod(ld->fdcur, 0644) == -1)
367                         pause2cannot("set mode of current", ld->name);
368                 rmoldest(ld);
369                 processorstart(ld);
370         }
371
372         while (fchdir(fdwdir) == -1)
373                 pause1cannot("change to initial working directory");
374         return 1;
375 }
376
377 static int buffer_pwrite(int n, char *s, unsigned len)
378 {
379         int i;
380         struct logdir *ld = &dir[n];
381
382         if (ld->sizemax) {
383                 if (ld->size >= ld->sizemax)
384                         rotate(ld);
385                 if (len > (ld->sizemax - ld->size))
386                         len = ld->sizemax - ld->size;
387         }
388         while (1) {
389                 ////i = full_write(ld->fdcur, s, len);
390                 ////if (i != -1) break;
391                 i = fwrite(s, 1, len, ld->filecur);
392                 if (i == len) break;
393
394                 if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
395                         DIR *d;
396                         struct dirent *f;
397                         char oldest[FMT_PTIME];
398                         int j = 0;
399
400                         while (fchdir(ld->fddir) == -1)
401                                 pause2cannot("change directory, want remove old logfile",
402                                                          ld->name);
403                         oldest[0] = 'A';
404                         oldest[1] = oldest[27] = '\0';
405                         while (!(d = opendir(".")))
406                                 pause2cannot("open directory, want remove old logfile",
407                                                          ld->name);
408                         errno = 0;
409                         while ((f = readdir(d)))
410                                 if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
411                                         ++j;
412                                         if (strcmp(f->d_name, oldest) < 0)
413                                                 memcpy(oldest, f->d_name, 27);
414                                 }
415                         if (errno) warn2("cannot read directory, want remove old logfile",
416                                         ld->name);
417                         closedir(d);
418                         errno = ENOSPC;
419                         if (j > ld->nmin) {
420                                 if (*oldest == '@') {
421                                         bb_error_msg(WARNING"out of disk space, delete: %s/%s",
422                                                         ld->name, oldest);
423                                         errno = 0;
424                                         if (unlink(oldest) == -1) {
425                                                 warn2("cannot unlink oldest logfile", ld->name);
426                                                 errno = ENOSPC;
427                                         }
428                                         while (fchdir(fdwdir) == -1)
429                                                 pause1cannot("change to initial working directory");
430                                 }
431                         }
432                 }
433                 if (errno)
434                         pause2cannot("write to current", ld->name);
435         }
436
437         ld->size += i;
438         if (ld->sizemax)
439                 if (s[i-1] == '\n')
440                         if (ld->size >= (ld->sizemax - linemax))
441                                 rotate(ld);
442         return i;
443 }
444
445 static void logdir_close(struct logdir *ld)
446 {
447         if (ld->fddir == -1)
448                 return;
449         if (verbose)
450                 bb_error_msg(INFO"close: %s", ld->name);
451         close(ld->fddir);
452         ld->fddir = -1;
453         if (ld->fdcur == -1)
454                 return; /* impossible */
455         while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
456                 pause2cannot("fsync current logfile", ld->name);
457         while (fchmod(ld->fdcur, 0744) == -1)
458                 pause2cannot("set mode of current", ld->name);
459         ////close(ld->fdcur);
460         fclose(ld->filecur);
461         ld->fdcur = -1;
462         if (ld->fdlock == -1)
463                 return; /* impossible */
464         close(ld->fdlock);
465         ld->fdlock = -1;
466         free(ld->processor);
467         ld->processor = NULL;
468 }
469
470 static unsigned logdir_open(struct logdir *ld, const char *fn)
471 {
472         char buf[128];
473         unsigned now;
474         char *new, *s, *np;
475         int i;
476         struct stat st;
477
478         now = monotonic_sec();
479
480         ld->fddir = open(fn, O_RDONLY|O_NDELAY);
481         if (ld->fddir == -1) {
482                 warn2("cannot open log directory", (char*)fn);
483                 return 0;
484         }
485         coe(ld->fddir);
486         if (fchdir(ld->fddir) == -1) {
487                 logdir_close(ld);
488                 warn2("cannot change directory", (char*)fn);
489                 return 0;
490         }
491         ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
492         if ((ld->fdlock == -1)
493          || (lock_exnb(ld->fdlock) == -1)
494         ) {
495                 logdir_close(ld);
496                 warn2("cannot lock directory", (char*)fn);
497                 while (fchdir(fdwdir) == -1)
498                         pause1cannot("change to initial working directory");
499                 return 0;
500         }
501         coe(ld->fdlock);
502
503         ld->size = 0;
504         ld->sizemax = 1000000;
505         ld->nmax = ld->nmin = 10;
506         ld->rotate_period = 0;
507         ld->name = (char*)fn;
508         ld->ppid = 0;
509         ld->match = '+';
510         free(ld->inst); ld->inst = NULL;
511         free(ld->processor); ld->processor = NULL;
512
513         /* read config */
514         i = open_read_close("config", buf, sizeof(buf));
515         if (i < 0 && errno != ENOENT)
516                 bb_perror_msg(WARNING": %s/config", ld->name);
517         if (i > 0) {
518                 if (verbose)
519                         bb_error_msg(INFO"read: %s/config", ld->name);
520                 s = buf;
521                 while (s) {
522                         np = strchr(s, '\n');
523                         if (np) *np++ = '\0';
524                         switch (s[0]) {
525                         case '+':
526                         case '-':
527                         case 'e':
528                         case 'E':
529                                 while (1) {
530                                         int l = asprintf(&new, "%s%s\n", ld->inst?:"", s);
531                                         if (l >= 0 && new) break;
532                                         pause_nomem();
533                                 }
534                                 free(ld->inst);
535                                 ld->inst = new;
536                                 break;
537                         case 's': {
538                                 static const struct suffix_mult km_suffixes[] = {
539                                         { "k", 1024 },
540                                         { "m", 1024*1024 },
541                                         { }
542                                 };
543                                 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
544                                 break;
545                         }
546                         case 'n':
547                                 ld->nmax = xatoi_u(&s[1]);
548                                 break;
549                         case 'N':
550                                 ld->nmin = xatoi_u(&s[1]);
551                                 break;
552                         case 't': {
553                                 static const struct suffix_mult mh_suffixes[] = {
554                                         { "m", 60 },
555                                         { "h", 60*60 },
556                                         /*{ "d", 24*60*60 },*/
557                                         { }
558                                 };
559                                 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
560                                 if (ld->rotate_period) {
561                                         ld->next_rotate = now + ld->rotate_period;
562                                         if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
563                                                 nearest_rotate = ld->next_rotate;
564                                         tmaxflag = 1;
565                                 }
566                                 break;
567                         }
568                         case '!':
569                                 if (s[1]) {
570                                         free(ld->processor);
571                                         ld->processor = wstrdup(s);
572                                 }
573                                 break;
574                         }
575                         s = np;
576                 }
577                 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
578                 s = ld->inst;
579                 while (s) {
580                         np = strchr(s, '\n');
581                         if (np) *np++ = '\0';
582                         s = np;
583                 }
584         }
585
586         /* open current */
587         i = stat("current", &st);
588         if (i != -1) {
589                 if (st.st_size && ! (st.st_mode & S_IXUSR)) {
590                         ld->fnsave[25] = '.';
591                         ld->fnsave[26] = 'u';
592                         ld->fnsave[27] = '\0';
593                         do {
594                                 fmt_time_bernstein_25(ld->fnsave);
595                                 errno = 0;
596                                 stat(ld->fnsave, &st);
597                         } while (errno != ENOENT);
598                         while (rename("current", ld->fnsave) == -1)
599                                 pause2cannot("rename current", ld->name);
600                         rmoldest(ld);
601                         i = -1;
602                 } else {
603                         /* Be paranoid: st.st_size can be not just bigger, but WIDER! */
604                         /* (bug in original svlogd. remove this comment when fixed there) */
605                         ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
606                 }
607         } else {
608                 if (errno != ENOENT) {
609                         logdir_close(ld);
610                         warn2("cannot stat current", ld->name);
611                         while (fchdir(fdwdir) == -1)
612                                 pause1cannot("change to initial working directory");
613                         return 0;
614                 }
615         }
616         while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
617                 pause2cannot("open current", ld->name);
618         /* we presume this cannot fail */
619         ld->filecur = fdopen(ld->fdcur, "a"); ////
620         setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
621
622         coe(ld->fdcur);
623         while (fchmod(ld->fdcur, 0644) == -1)
624                 pause2cannot("set mode of current", ld->name);
625
626         if (verbose) {
627                 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
628                 else bb_error_msg(INFO"new: %s/current", ld->name);
629         }
630
631         while (fchdir(fdwdir) == -1)
632                 pause1cannot("change to initial working directory");
633         return 1;
634 }
635
636 static void logdirs_reopen(void)
637 {
638         int l;
639         int ok = 0;
640
641         tmaxflag = 0;
642         for (l = 0; l < dirn; ++l) {
643                 logdir_close(&dir[l]);
644                 if (logdir_open(&dir[l], fndir[l]))
645                         ok = 1;
646         }
647         if (!ok)
648                 fatalx("no functional log directories");
649 }
650
651 /* Will look good in libbb one day */
652 static ssize_t ndelay_read(int fd, void *buf, size_t count)
653 {
654         if (!(fl_flag_0 & O_NONBLOCK))
655                 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
656         count = safe_read(fd, buf, count);
657         if (!(fl_flag_0 & O_NONBLOCK))
658                 fcntl(fd, F_SETFL, fl_flag_0);
659         return count;
660 }
661
662 /* Used for reading stdin */
663 static int buffer_pread(int fd, char *s, unsigned len)
664 {
665         unsigned now;
666         struct pollfd input;
667         int i;
668
669         input.fd = 0;
670         input.events = POLLIN|POLLHUP|POLLERR;
671
672         do {
673                 if (rotateasap) {
674                         for (i = 0; i < dirn; ++i)
675                                 rotate(dir + i);
676                         rotateasap = 0;
677                 }
678                 if (exitasap) {
679                         if (linecomplete)
680                                 return 0;
681                         len = 1;
682                 }
683                 if (reopenasap) {
684                         logdirs_reopen();
685                         reopenasap = 0;
686                 }
687                 now = monotonic_sec();
688                 nearest_rotate = now + (45 * 60 + 45);
689                 for (i = 0; i < dirn; ++i) {
690                         if (dir[i].rotate_period) {
691                                 if (LESS(dir[i].next_rotate, now))
692                                         rotate(dir + i);
693                                 if (LESS(dir[i].next_rotate, nearest_rotate))
694                                         nearest_rotate = dir[i].next_rotate;
695                         }
696                 }
697
698                 sigprocmask(SIG_UNBLOCK, blocked_sigset, NULL);
699                 i = nearest_rotate - now;
700                 if (i > 1000000)
701                         i = 1000000;
702                 if (i <= 0)
703                         i = 1;
704                 poll(&input, 1, i * 1000);
705                 sigprocmask(SIG_BLOCK, blocked_sigset, NULL);
706
707                 i = ndelay_read(fd, s, len);
708                 if (i >= 0)
709                         break;
710                 if (errno == EINTR)
711                         continue;
712                 if (errno != EAGAIN) {
713                         warn("cannot read standard input");
714                         break;
715                 }
716                 /* else: EAGAIN - normal, repeat silently */
717         } while (!exitasap);
718
719         if (i > 0) {
720                 int cnt;
721                 linecomplete = (s[i-1] == '\n');
722                 if (!repl)
723                         return i;
724
725                 cnt = i;
726                 while (--cnt >= 0) {
727                         char ch = *s;
728                         if (ch != '\n') {
729                                 if (ch < 32 || ch > 126)
730                                         *s = repl;
731                                 else {
732                                         int j;
733                                         for (j = 0; replace[j]; ++j) {
734                                                 if (ch == replace[j]) {
735                                                         *s = repl;
736                                                         break;
737                                                 }
738                                         }
739                                 }
740                         }
741                         s++;
742                 }
743         }
744         return i;
745 }
746
747
748 static void sig_term_handler(int sig_no)
749 {
750         if (verbose)
751                 bb_error_msg(INFO"sig%s received", "term");
752         exitasap = 1;
753 }
754
755 static void sig_child_handler(int sig_no)
756 {
757         int pid, l;
758
759         if (verbose)
760                 bb_error_msg(INFO"sig%s received", "child");
761         while ((pid = wait_nohang(&wstat)) > 0) {
762                 for (l = 0; l < dirn; ++l) {
763                         if (dir[l].ppid == pid) {
764                                 dir[l].ppid = 0;
765                                 processorstop(&dir[l]);
766                                 break;
767                         }
768                 }
769         }
770 }
771
772 static void sig_alarm_handler(int sig_no)
773 {
774         if (verbose)
775                 bb_error_msg(INFO"sig%s received", "alarm");
776         rotateasap = 1;
777 }
778
779 static void sig_hangup_handler(int sig_no)
780 {
781         if (verbose)
782                 bb_error_msg(INFO"sig%s received", "hangup");
783         reopenasap = 1;
784 }
785
786 static void logmatch(struct logdir *ld)
787 {
788         char *s;
789
790         ld->match = '+';
791         ld->matcherr = 'E';
792         s = ld->inst;
793         while (s && s[0]) {
794                 switch (s[0]) {
795                 case '+':
796                 case '-':
797                         if (pmatch(s+1, line, linelen))
798                                 ld->match = s[0];
799                         break;
800                 case 'e':
801                 case 'E':
802                         if (pmatch(s+1, line, linelen))
803                                 ld->matcherr = s[0];
804                         break;
805                 }
806                 s += strlen(s) + 1;
807         }
808 }
809
810 int svlogd_main(int argc, char **argv);
811 int svlogd_main(int argc, char **argv)
812 {
813         sigset_t ss;
814         char *r,*l,*b;
815         ssize_t stdin_cnt = 0;
816         int i;
817         unsigned opt;
818         unsigned timestamp = 0;
819         void* (*memRchr)(const void *, int, size_t) = memchr;
820
821 #define line bb_common_bufsiz1
822
823         opt_complementary = "tt:vv";
824         opt = getopt32(argv, "r:R:l:b:tv",
825                         &r, &replace, &l, &b, &timestamp, &verbose);
826         if (opt & 1) { // -r
827                 repl = r[0];
828                 if (!repl || r[1]) usage();
829         }
830         if (opt & 2) if (!repl) repl = '_'; // -R
831         if (opt & 4) { // -l
832                 linemax = xatou_range(l, 0, BUFSIZ-26);
833                 if (linemax == 0) linemax = BUFSIZ-26;
834                 if (linemax < 256) linemax = 256;
835         }
836         ////if (opt & 8) { // -b
837         ////    buflen = xatoi_u(b);
838         ////    if (buflen == 0) buflen = 1024;
839         ////}
840         //if (opt & 0x10) timestamp++; // -t
841         //if (opt & 0x20) verbose++; // -v
842         //if (timestamp > 2) timestamp = 2;
843         argv += optind;
844         argc -= optind;
845
846         dirn = argc;
847         if (dirn <= 0) usage();
848         ////if (buflen <= linemax) usage();
849         fdwdir = xopen(".", O_RDONLY|O_NDELAY);
850         coe(fdwdir);
851         dir = xzalloc(dirn * sizeof(struct logdir));
852         for (i = 0; i < dirn; ++i) {
853                 dir[i].fddir = -1;
854                 dir[i].fdcur = -1;
855                 ////dir[i].btmp = xmalloc(buflen);
856                 /*dir[i].ppid = 0;*/
857         }
858         /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
859         fndir = argv;
860         /* We cannot set NONBLOCK on fd #0 permanently - this setting
861          * _isn't_ per-process! It is shared among all other processes
862          * with the same stdin */
863         fl_flag_0 = fcntl(0, F_GETFL);
864
865         blocked_sigset = &ss;
866         sigemptyset(&ss);
867         sigaddset(&ss, SIGTERM);
868         sigaddset(&ss, SIGCHLD);
869         sigaddset(&ss, SIGALRM);
870         sigaddset(&ss, SIGHUP);
871         sigprocmask(SIG_BLOCK, &ss, NULL);
872         sig_catch(SIGTERM, sig_term_handler);
873         sig_catch(SIGCHLD, sig_child_handler);
874         sig_catch(SIGALRM, sig_alarm_handler);
875         sig_catch(SIGHUP, sig_hangup_handler);
876
877         logdirs_reopen();
878
879         /* Without timestamps, we don't have to print each line
880          * separately, so we can look for _last_ newline, not first,
881          * thus batching writes */
882         if (!timestamp)
883                 memRchr = memrchr;
884
885         setvbuf(stderr, NULL, _IOFBF, linelen);
886
887         /* Each iteration processes one or more lines */
888         while (1) {
889                 char stamp[FMT_PTIME];
890                 char *lineptr;
891                 char *printptr;
892                 char *np;
893                 int printlen;
894                 char ch;
895
896                 lineptr = line;
897                 if (timestamp)
898                         lineptr += 26;
899
900                 /* lineptr[0..linemax-1] - buffer for stdin */
901                 /* (possibly has some unprocessed data from prev loop) */
902
903                 /* Refill the buffer if needed */
904                 np = memRchr(lineptr, '\n', stdin_cnt);
905                 if (!np && !exitasap) {
906                         i = linemax - stdin_cnt; /* avail. bytes at tail */
907                         if (i >= 128) {
908                                 i = buffer_pread(0, lineptr + stdin_cnt, i);
909                                 if (i <= 0) /* EOF or error on stdin */
910                                         exitasap = 1;
911                                 else {
912                                         np = memRchr(lineptr + stdin_cnt, '\n', i);
913                                         stdin_cnt += i;
914                                 }
915                         }
916                 }
917                 if (stdin_cnt <= 0 && exitasap)
918                         break;
919
920                 /* Search for '\n' (in fact, np already holds the result) */
921                 linelen = stdin_cnt;
922                 if (np) {
923  print_to_nl:           /* NB: starting from here lineptr may point
924                          * farther out into line[] */
925                         linelen = np - lineptr + 1;
926                 }
927                 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
928                 ch = lineptr[linelen-1];
929
930                 /* Biggest performance hit was coming from the fact
931                  * that we did not buffer writes. We were reading many lines
932                  * in one read() above, but wrote one line per write().
933                  * We are using stdio to fix that */
934
935                 /* write out lineptr[0..linelen-1] to each log destination
936                  * (or lineptr[-26..linelen-1] if timestamping) */
937                 printlen = linelen;
938                 printptr = lineptr;
939                 if (timestamp) {
940                         if (timestamp == 1)
941                                 fmt_time_bernstein_25(stamp);
942                         else /* 2: */
943                                 fmt_time_human_30nul(stamp);
944                         printlen += 26;
945                         printptr -= 26;
946                         memcpy(printptr, stamp, 25);
947                         printptr[25] = ' ';
948                 }
949                 for (i = 0; i < dirn; ++i) {
950                         struct logdir *ld = &dir[i];
951                         if (ld->fddir == -1) continue;
952                         if (ld->inst)
953                                 logmatch(ld);
954                         if (ld->matcherr == 'e')
955                                 ////full_write(2, printptr, printlen);
956                                 fwrite(lineptr, 1, linelen, stderr);
957                         if (ld->match != '+') continue;
958                         buffer_pwrite(i, printptr, printlen);
959                 }
960
961                 /* If we didn't see '\n' (long input line), */
962                 /* read/write repeatedly until we see it */
963                 while (ch != '\n') {
964                         /* lineptr is emptied now, safe to use as buffer */
965                         stdin_cnt = exitasap ? -1 : buffer_pread(0, lineptr, linemax);
966                         if (stdin_cnt <= 0) { /* EOF or error on stdin */
967                                 exitasap = 1;
968                                 lineptr[0] = ch = '\n';
969                                 linelen = 1;
970                                 stdin_cnt = 1;
971                         } else {
972                                 linelen = stdin_cnt;
973                                 np = memRchr(lineptr, '\n', stdin_cnt);
974                                 if (np)
975                                         linelen = np - lineptr + 1;
976                                 ch = lineptr[linelen-1];
977                         }
978                         /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
979                         for (i = 0; i < dirn; ++i) {
980                                 if (dir[i].fddir == -1) continue;
981                                 if (dir[i].matcherr == 'e')
982                                         ////full_write(2, lineptr, linelen);
983                                         fwrite(lineptr, 1, linelen, stderr);
984                                 if (dir[i].match != '+') continue;
985                                 buffer_pwrite(i, lineptr, linelen);
986                         }
987                 }
988
989                 stdin_cnt -= linelen;
990                 if (stdin_cnt > 0) {
991                         lineptr += linelen;
992                         /* If we see another '\n', we don't need to read
993                          * next piece of input: can print what we have */
994                         np = memRchr(lineptr, '\n', stdin_cnt);
995                         if (np)
996                                 goto print_to_nl;
997                         /* Move unprocessed data to the front of line */
998                         memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
999                 }
1000                 fflush(NULL);////
1001         }
1002
1003         for (i = 0; i < dirn; ++i) {
1004                 if (dir[i].ppid)
1005                         while (!processorstop(&dir[i]))
1006                                 /* repeat */;
1007                 logdir_close(&dir[i]);
1008         }
1009         return 0;
1010 }