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