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