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