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