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