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