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