b5eed15ab80a110965c957307434a7f3fe0a69f3
[platform/upstream/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)
524                                 *np++ = '\0';
525                         switch (s[0]) {
526                         case '+':
527                         case '-':
528                         case 'e':
529                         case 'E':
530                                 /* Add '\n'-terminated line to ld->inst */
531                                 while (1) {
532                                         int l = asprintf(&new, "%s%s\n", ld->inst ? : "", s);
533                                         if (l >= 0 && new)
534                                                 break;
535                                         pause_nomem();
536                                 }
537                                 free(ld->inst);
538                                 ld->inst = new;
539                                 break;
540                         case 's': {
541                                 static const struct suffix_mult km_suffixes[] = {
542                                         { "k", 1024 },
543                                         { "m", 1024*1024 },
544                                         { }
545                                 };
546                                 ld->sizemax = xatou_sfx(&s[1], km_suffixes);
547                                 break;
548                         }
549                         case 'n':
550                                 ld->nmax = xatoi_u(&s[1]);
551                                 break;
552                         case 'N':
553                                 ld->nmin = xatoi_u(&s[1]);
554                                 break;
555                         case 't': {
556                                 static const struct suffix_mult mh_suffixes[] = {
557                                         { "m", 60 },
558                                         { "h", 60*60 },
559                                         /*{ "d", 24*60*60 },*/
560                                         { }
561                                 };
562                                 ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
563                                 if (ld->rotate_period) {
564                                         ld->next_rotate = now + ld->rotate_period;
565                                         if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
566                                                 nearest_rotate = ld->next_rotate;
567                                         tmaxflag = 1;
568                                 }
569                                 break;
570                         }
571                         case '!':
572                                 if (s[1]) {
573                                         free(ld->processor);
574                                         ld->processor = wstrdup(s);
575                                 }
576                                 break;
577                         }
578                         s = np;
579                 }
580                 /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
581                 s = ld->inst;
582                 while (s) {
583                         np = strchr(s, '\n');
584                         if (np)
585                                 *np++ = '\0';
586                         s = np;
587                 }
588         }
589
590         /* open current */
591         i = stat("current", &st);
592         if (i != -1) {
593                 if (st.st_size && !(st.st_mode & S_IXUSR)) {
594                         ld->fnsave[25] = '.';
595                         ld->fnsave[26] = 'u';
596                         ld->fnsave[27] = '\0';
597                         do {
598                                 fmt_time_bernstein_25(ld->fnsave);
599                                 errno = 0;
600                                 stat(ld->fnsave, &st);
601                         } while (errno != ENOENT);
602                         while (rename("current", ld->fnsave) == -1)
603                                 pause2cannot("rename current", ld->name);
604                         rmoldest(ld);
605                         i = -1;
606                 } else {
607                         /* st.st_size can be not just bigger, but WIDER!
608                          * This code is safe: if st.st_size > 4GB, we select
609                          * ld->sizemax (because it's "unsigned") */
610                         ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
611                 }
612         } else {
613                 if (errno != ENOENT) {
614                         logdir_close(ld);
615                         warn2("cannot stat current", ld->name);
616                         while (fchdir(fdwdir) == -1)
617                                 pause1cannot("change to initial working directory");
618                         return 0;
619                 }
620         }
621         while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
622                 pause2cannot("open current", ld->name);
623         /* we presume this cannot fail */
624         ld->filecur = fdopen(ld->fdcur, "a"); ////
625         setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
626
627         coe(ld->fdcur);
628         while (fchmod(ld->fdcur, 0644) == -1)
629                 pause2cannot("set mode of current", ld->name);
630
631         if (verbose) {
632                 if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
633                 else bb_error_msg(INFO"new: %s/current", ld->name);
634         }
635
636         while (fchdir(fdwdir) == -1)
637                 pause1cannot("change to initial working directory");
638         return 1;
639 }
640
641 static void logdirs_reopen(void)
642 {
643         int l;
644         int ok = 0;
645
646         tmaxflag = 0;
647         for (l = 0; l < dirn; ++l) {
648                 logdir_close(&dir[l]);
649                 if (logdir_open(&dir[l], fndir[l]))
650                         ok = 1;
651         }
652         if (!ok)
653                 fatalx("no functional log directories");
654 }
655
656 /* Will look good in libbb one day */
657 static ssize_t ndelay_read(int fd, void *buf, size_t count)
658 {
659         if (!(fl_flag_0 & O_NONBLOCK))
660                 fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
661         count = safe_read(fd, buf, count);
662         if (!(fl_flag_0 & O_NONBLOCK))
663                 fcntl(fd, F_SETFL, fl_flag_0);
664         return count;
665 }
666
667 /* Used for reading stdin */
668 static int buffer_pread(/*int fd, */char *s, unsigned len)
669 {
670         unsigned now;
671         struct pollfd input;
672         int i;
673
674         input.fd = 0;
675         input.events = POLLIN;
676
677         do {
678                 if (rotateasap) {
679                         for (i = 0; i < dirn; ++i)
680                                 rotate(dir + i);
681                         rotateasap = 0;
682                 }
683                 if (exitasap) {
684                         if (linecomplete)
685                                 return 0;
686                         len = 1;
687                 }
688                 if (reopenasap) {
689                         logdirs_reopen();
690                         reopenasap = 0;
691                 }
692                 now = monotonic_sec();
693                 nearest_rotate = now + (45 * 60 + 45);
694                 for (i = 0; i < dirn; ++i) {
695                         if (dir[i].rotate_period) {
696                                 if (LESS(dir[i].next_rotate, now))
697                                         rotate(dir + i);
698                                 if (LESS(dir[i].next_rotate, nearest_rotate))
699                                         nearest_rotate = dir[i].next_rotate;
700                         }
701                 }
702
703                 sigprocmask(SIG_UNBLOCK, blocked_sigset, NULL);
704                 i = nearest_rotate - now;
705                 if (i > 1000000)
706                         i = 1000000;
707                 if (i <= 0)
708                         i = 1;
709                 poll(&input, 1, i * 1000);
710                 sigprocmask(SIG_BLOCK, blocked_sigset, NULL);
711
712                 i = ndelay_read(0, s, len);
713                 if (i >= 0)
714                         break;
715                 if (errno == EINTR)
716                         continue;
717                 if (errno != EAGAIN) {
718                         warn("cannot read standard input");
719                         break;
720                 }
721                 /* else: EAGAIN - normal, repeat silently */
722         } while (!exitasap);
723
724         if (i > 0) {
725                 int cnt;
726                 linecomplete = (s[i-1] == '\n');
727                 if (!repl)
728                         return i;
729
730                 cnt = i;
731                 while (--cnt >= 0) {
732                         char ch = *s;
733                         if (ch != '\n') {
734                                 if (ch < 32 || ch > 126)
735                                         *s = repl;
736                                 else {
737                                         int j;
738                                         for (j = 0; replace[j]; ++j) {
739                                                 if (ch == replace[j]) {
740                                                         *s = repl;
741                                                         break;
742                                                 }
743                                         }
744                                 }
745                         }
746                         s++;
747                 }
748         }
749         return i;
750 }
751
752 static void sig_term_handler(int sig_no)
753 {
754         if (verbose)
755                 bb_error_msg(INFO"sig%s received", "term");
756         exitasap = 1;
757 }
758
759 static void sig_child_handler(int sig_no)
760 {
761         int pid, l;
762
763         if (verbose)
764                 bb_error_msg(INFO"sig%s received", "child");
765         while ((pid = wait_nohang(&wstat)) > 0) {
766                 for (l = 0; l < dirn; ++l) {
767                         if (dir[l].ppid == pid) {
768                                 dir[l].ppid = 0;
769                                 processorstop(&dir[l]);
770                                 break;
771                         }
772                 }
773         }
774 }
775
776 static void sig_alarm_handler(int sig_no)
777 {
778         if (verbose)
779                 bb_error_msg(INFO"sig%s received", "alarm");
780         rotateasap = 1;
781 }
782
783 static void sig_hangup_handler(int sig_no)
784 {
785         if (verbose)
786                 bb_error_msg(INFO"sig%s received", "hangup");
787         reopenasap = 1;
788 }
789
790 static void logmatch(struct logdir *ld)
791 {
792         char *s;
793
794         ld->match = '+';
795         ld->matcherr = 'E';
796         s = ld->inst;
797         while (s && s[0]) {
798                 switch (s[0]) {
799                 case '+':
800                 case '-':
801                         if (pmatch(s+1, line, linelen))
802                                 ld->match = s[0];
803                         break;
804                 case 'e':
805                 case 'E':
806                         if (pmatch(s+1, line, linelen))
807                                 ld->matcherr = s[0];
808                         break;
809                 }
810                 s += strlen(s) + 1;
811         }
812 }
813
814 int svlogd_main(int argc, char **argv);
815 int svlogd_main(int argc, char **argv)
816 {
817         sigset_t ss;
818         char *r,*l,*b;
819         ssize_t stdin_cnt = 0;
820         int i;
821         unsigned opt;
822         unsigned timestamp = 0;
823         void* (*memRchr)(const void *, int, size_t) = memchr;
824
825 #define line bb_common_bufsiz1
826
827         opt_complementary = "tt:vv";
828         opt = getopt32(argv, "r:R:l:b:tv",
829                         &r, &replace, &l, &b, &timestamp, &verbose);
830         if (opt & 1) { // -r
831                 repl = r[0];
832                 if (!repl || r[1]) usage();
833         }
834         if (opt & 2) if (!repl) repl = '_'; // -R
835         if (opt & 4) { // -l
836                 linemax = xatou_range(l, 0, BUFSIZ-26);
837                 if (linemax == 0) linemax = BUFSIZ-26;
838                 if (linemax < 256) linemax = 256;
839         }
840         ////if (opt & 8) { // -b
841         ////    buflen = xatoi_u(b);
842         ////    if (buflen == 0) buflen = 1024;
843         ////}
844         //if (opt & 0x10) timestamp++; // -t
845         //if (opt & 0x20) verbose++; // -v
846         //if (timestamp > 2) timestamp = 2;
847         argv += optind;
848         argc -= optind;
849
850         dirn = argc;
851         if (dirn <= 0) usage();
852         ////if (buflen <= linemax) usage();
853         fdwdir = xopen(".", O_RDONLY|O_NDELAY);
854         coe(fdwdir);
855         dir = xzalloc(dirn * sizeof(struct logdir));
856         for (i = 0; i < dirn; ++i) {
857                 dir[i].fddir = -1;
858                 dir[i].fdcur = -1;
859                 ////dir[i].btmp = xmalloc(buflen);
860                 /*dir[i].ppid = 0;*/
861         }
862         /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
863         fndir = argv;
864         /* We cannot set NONBLOCK on fd #0 permanently - this setting
865          * _isn't_ per-process! It is shared among all other processes
866          * with the same stdin */
867         fl_flag_0 = fcntl(0, F_GETFL);
868
869         blocked_sigset = &ss;
870         sigemptyset(&ss);
871         sigaddset(&ss, SIGTERM);
872         sigaddset(&ss, SIGCHLD);
873         sigaddset(&ss, SIGALRM);
874         sigaddset(&ss, SIGHUP);
875         sigprocmask(SIG_BLOCK, &ss, NULL);
876         sig_catch(SIGTERM, sig_term_handler);
877         sig_catch(SIGCHLD, sig_child_handler);
878         sig_catch(SIGALRM, sig_alarm_handler);
879         sig_catch(SIGHUP, sig_hangup_handler);
880
881         logdirs_reopen();
882
883         /* Without timestamps, we don't have to print each line
884          * separately, so we can look for _last_ newline, not first,
885          * thus batching writes */
886         if (!timestamp)
887                 memRchr = memrchr;
888
889         setvbuf(stderr, NULL, _IOFBF, linelen);
890
891         /* Each iteration processes one or more lines */
892         while (1) {
893                 char stamp[FMT_PTIME];
894                 char *lineptr;
895                 char *printptr;
896                 char *np;
897                 int printlen;
898                 char ch;
899
900                 lineptr = line;
901                 if (timestamp)
902                         lineptr += 26;
903
904                 /* lineptr[0..linemax-1] - buffer for stdin */
905                 /* (possibly has some unprocessed data from prev loop) */
906
907                 /* Refill the buffer if needed */
908                 np = memRchr(lineptr, '\n', stdin_cnt);
909                 if (!np && !exitasap) {
910                         i = linemax - stdin_cnt; /* avail. bytes at tail */
911                         if (i >= 128) {
912                                 i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
913                                 if (i <= 0) /* EOF or error on stdin */
914                                         exitasap = 1;
915                                 else {
916                                         np = memRchr(lineptr + stdin_cnt, '\n', i);
917                                         stdin_cnt += i;
918                                 }
919                         }
920                 }
921                 if (stdin_cnt <= 0 && exitasap)
922                         break;
923
924                 /* Search for '\n' (in fact, np already holds the result) */
925                 linelen = stdin_cnt;
926                 if (np) {
927  print_to_nl:           /* NB: starting from here lineptr may point
928                          * farther out into line[] */
929                         linelen = np - lineptr + 1;
930                 }
931                 /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
932                 ch = lineptr[linelen-1];
933
934                 /* Biggest performance hit was coming from the fact
935                  * that we did not buffer writes. We were reading many lines
936                  * in one read() above, but wrote one line per write().
937                  * We are using stdio to fix that */
938
939                 /* write out lineptr[0..linelen-1] to each log destination
940                  * (or lineptr[-26..linelen-1] if timestamping) */
941                 printlen = linelen;
942                 printptr = lineptr;
943                 if (timestamp) {
944                         if (timestamp == 1)
945                                 fmt_time_bernstein_25(stamp);
946                         else /* 2: */
947                                 fmt_time_human_30nul(stamp);
948                         printlen += 26;
949                         printptr -= 26;
950                         memcpy(printptr, stamp, 25);
951                         printptr[25] = ' ';
952                 }
953                 for (i = 0; i < dirn; ++i) {
954                         struct logdir *ld = &dir[i];
955                         if (ld->fddir == -1) continue;
956                         if (ld->inst)
957                                 logmatch(ld);
958                         if (ld->matcherr == 'e')
959                                 ////full_write(2, printptr, printlen);
960                                 fwrite(lineptr, 1, linelen, stderr);
961                         if (ld->match != '+') continue;
962                         buffer_pwrite(i, printptr, printlen);
963                 }
964
965                 /* If we didn't see '\n' (long input line), */
966                 /* read/write repeatedly until we see it */
967                 while (ch != '\n') {
968                         /* lineptr is emptied now, safe to use as buffer */
969                         stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
970                         if (stdin_cnt <= 0) { /* EOF or error on stdin */
971                                 exitasap = 1;
972                                 lineptr[0] = ch = '\n';
973                                 linelen = 1;
974                                 stdin_cnt = 1;
975                         } else {
976                                 linelen = stdin_cnt;
977                                 np = memRchr(lineptr, '\n', stdin_cnt);
978                                 if (np)
979                                         linelen = np - lineptr + 1;
980                                 ch = lineptr[linelen-1];
981                         }
982                         /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
983                         for (i = 0; i < dirn; ++i) {
984                                 if (dir[i].fddir == -1) continue;
985                                 if (dir[i].matcherr == 'e')
986                                         ////full_write(2, lineptr, linelen);
987                                         fwrite(lineptr, 1, linelen, stderr);
988                                 if (dir[i].match != '+') continue;
989                                 buffer_pwrite(i, lineptr, linelen);
990                         }
991                 }
992
993                 stdin_cnt -= linelen;
994                 if (stdin_cnt > 0) {
995                         lineptr += linelen;
996                         /* If we see another '\n', we don't need to read
997                          * next piece of input: can print what we have */
998                         np = memRchr(lineptr, '\n', stdin_cnt);
999                         if (np)
1000                                 goto print_to_nl;
1001                         /* Move unprocessed data to the front of line */
1002                         memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1003                 }
1004                 fflush(NULL);////
1005         }
1006
1007         for (i = 0; i < dirn; ++i) {
1008                 if (dir[i].ppid)
1009                         while (!processorstop(&dir[i]))
1010                                 /* repeat */;
1011                 logdir_close(&dir[i]);
1012         }
1013         return 0;
1014 }