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