source: branches/2.2.9/mindi-busybox/runit/svlogd.c @ 2725

Last change on this file since 2725 was 2725, checked in by bruno, 9 years ago
  • Update mindi-busybox to 1.18.3 to avoid problems with the tar command which is now failing on recent versions with busybox 1.7.3
  • Property svn:eol-style set to native
File size: 30.4 KB
Line 
1/*
2Copyright (c) 2001-2006, Gerrit Pape
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, 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
16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*/
27
28/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
29/* TODO: depends on runit_lib.c - review and reduce/eliminate */
30
31/*
32Config files
33
34On startup, and after receiving a HUP signal, svlogd checks for each
35log directory log if the configuration file log/config exists,
36and if so, reads the file line by line and adjusts configuration
37for log as follows:
38
39If the line is empty, or starts with a #, it is ignored. A line
40of the form
41
42ssize
43    sets the maximum file size of current when svlogd should rotate
44    the current log file to size bytes. Default is 1000000.
45    If size is zero, svlogd doesnt rotate log files
46    You should set size to at least (2 * len).
47nnum
48    sets the number of old log files svlogd should maintain to num.
49    If svlogd sees more that num old log files in log after log file
50    rotation, it deletes the oldest one. Default is 10.
51    If num is zero, svlogd doesnt remove old log files.
52Nmin
53    sets the minimum number of old log files svlogd should maintain
54    to min. min must be less than num. If min is set, and svlogd
55    cannot write to current because the filesystem is full,
56    and it sees more than min old log files, it deletes the oldest one.
57ttimeout
58    sets the maximum age of the current log file when svlogd should
59    rotate the current log file to timeout seconds. If current
60    is timeout seconds old, and is not empty, svlogd forces log file rotation.
61!processor
62    tells svlogd to feed each recent log file through processor
63    (see above) on log file rotation. By default log files are not processed.
64ua.b.c.d[:port]
65    tells svlogd to transmit the first len characters of selected
66    log messages to the IP address a.b.c.d, port number port.
67    If port isnt set, the default port for syslog is used (514).
68    len can be set through the -l option, see below. If svlogd
69    has trouble sending udp packets, it writes error messages
70    to the log directory. Attention: logging through udp is unreliable,
71    and should be used in private networks only.
72Ua.b.c.d[:port]
73    is the same as the u line above, but the log messages are no longer
74    written to the log directory, but transmitted through udp only.
75    Error messages from svlogd concerning sending udp packages still go
76    to the log directory.
77pprefix
78    tells svlogd to prefix each line to be written to the log directory,
79    to standard error, or through UDP, with prefix.
80
81If a line starts with a -, +, e, or E, svlogd matches the first len characters
82of each log message against pattern and acts accordingly:
83
84-pattern
85    the log message is deselected.
86+pattern
87    the log message is selected.
88epattern
89    the log message is selected to be printed to standard error.
90Epattern
91    the log message is deselected to be printed to standard error.
92
93Initially each line is selected to be written to log/current. Deselected
94log messages are discarded from log. Initially each line is deselected
95to be written to standard err. Log messages selected for standard error
96are written to standard error.
97
98Pattern Matching
99
100svlogd matches a log message against the string pattern as follows:
101
102pattern is applied to the log message one character by one, starting
103with the first. A character not a star (*) and not a plus (+) matches itself.
104A plus matches the next character in pattern in the log message one
105or more times. A star before the end of pattern matches any string
106in the log message that does not include the next character in pattern.
107A star at the end of pattern matches any string.
108
109Timestamps optionally added by svlogd are not considered part
110of the log message.
111
112An svlogd pattern is not a regular expression. For example consider
113a log message like this
114
1152005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
116
117The following pattern doesnt match
118
119-*pid*
120
121because the first star matches up to the first p in tcpsvd,
122and then the match fails because i is not s. To match this
123log message, you can use a pattern like this instead
124
125-*: *: pid *
126*/
127
128#include <sys/poll.h>
129#include <sys/file.h>
130#include "libbb.h"
131#include "runit_lib.h"
132
133#define LESS(a,b) ((int)((unsigned)(b) - (unsigned)(a)) > 0)
134
135#define FMT_PTIME 30
136
137struct logdir {
138    ////char *btmp;
139    /* pattern list to match, in "aa\0bb\0\cc\0\0" form */
140    char *inst;
141    char *processor;
142    char *name;
143    unsigned size;
144    unsigned sizemax;
145    unsigned nmax;
146    unsigned nmin;
147    unsigned rotate_period;
148    int ppid;
149    int fddir;
150    int fdcur;
151    FILE* filecur; ////
152    int fdlock;
153    unsigned next_rotate;
154    char fnsave[FMT_PTIME];
155    char match;
156    char matcherr;
157};
158
159
160struct globals {
161    struct logdir *dir;
162    unsigned verbose;
163    int linemax;
164    ////int buflen;
165    int linelen;
166
167    int fdwdir;
168    char **fndir;
169    int wstat;
170    unsigned nearest_rotate;
171
172    void* (*memRchr)(const void *, int, size_t);
173
174    smallint exitasap;
175    smallint rotateasap;
176    smallint reopenasap;
177    smallint linecomplete;
178    smallint tmaxflag;
179
180    char repl;
181    const char *replace;
182    int fl_flag_0;
183    unsigned dirn;
184
185    sigset_t blocked_sigset;
186};
187#define G (*ptr_to_globals)
188#define dir            (G.dir           )
189#define verbose        (G.verbose       )
190#define linemax        (G.linemax       )
191#define buflen         (G.buflen        )
192#define linelen        (G.linelen       )
193#define fndir          (G.fndir         )
194#define fdwdir         (G.fdwdir        )
195#define wstat          (G.wstat         )
196#define memRchr        (G.memRchr       )
197#define nearest_rotate (G.nearest_rotate)
198#define exitasap       (G.exitasap      )
199#define rotateasap     (G.rotateasap    )
200#define reopenasap     (G.reopenasap    )
201#define linecomplete   (G.linecomplete  )
202#define tmaxflag       (G.tmaxflag      )
203#define repl           (G.repl          )
204#define replace        (G.replace       )
205#define blocked_sigset (G.blocked_sigset)
206#define fl_flag_0      (G.fl_flag_0     )
207#define dirn           (G.dirn          )
208#define INIT_G() do { \
209    SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
210    linemax = 1000; \
211    /*buflen = 1024;*/ \
212    linecomplete = 1; \
213    replace = ""; \
214} while (0)
215
216#define line bb_common_bufsiz1
217
218
219#define FATAL "fatal: "
220#define WARNING "warning: "
221#define PAUSE "pausing: "
222#define INFO "info: "
223
224static void fatalx(const char *m0)
225{
226    bb_error_msg_and_die(FATAL"%s", m0);
227}
228static void warn(const char *m0)
229{
230    bb_perror_msg(WARNING"%s", m0);
231}
232static void warn2(const char *m0, const char *m1)
233{
234    bb_perror_msg(WARNING"%s: %s", m0, m1);
235}
236static void warnx(const char *m0, const char *m1)
237{
238    bb_error_msg(WARNING"%s: %s", m0, m1);
239}
240static void pause_nomem(void)
241{
242    bb_error_msg(PAUSE"out of memory");
243    sleep(3);
244}
245static void pause1cannot(const char *m0)
246{
247    bb_perror_msg(PAUSE"can't %s", m0);
248    sleep(3);
249}
250static void pause2cannot(const char *m0, const char *m1)
251{
252    bb_perror_msg(PAUSE"can't %s %s", m0, m1);
253    sleep(3);
254}
255
256static char* wstrdup(const char *str)
257{
258    char *s;
259    while (!(s = strdup(str)))
260        pause_nomem();
261    return s;
262}
263
264static unsigned pmatch(const char *p, const char *s, unsigned len)
265{
266    for (;;) {
267        char c = *p++;
268        if (!c) return !len;
269        switch (c) {
270        case '*':
271            c = *p;
272            if (!c) return 1;
273            for (;;) {
274                if (!len) return 0;
275                if (*s == c) break;
276                ++s;
277                --len;
278            }
279            continue;
280        case '+':
281            c = *p++;
282            if (c != *s) return 0;
283            for (;;) {
284                if (!len) return 1;
285                if (*s != c) break;
286                ++s;
287                --len;
288            }
289            continue;
290            /*
291        case '?':
292            if (*p == '?') {
293                if (*s != '?') return 0;
294                ++p;
295            }
296            ++s; --len;
297            continue;
298            */
299        default:
300            if (!len) return 0;
301            if (*s != c) return 0;
302            ++s;
303            --len;
304            continue;
305        }
306    }
307    return 0;
308}
309
310/*** ex fmt_ptime.[ch] ***/
311
312/* NUL terminated */
313static void fmt_time_human_30nul(char *s)
314{
315    struct tm *ptm;
316    struct timeval tv;
317
318    gettimeofday(&tv, NULL);
319    ptm = gmtime(&tv.tv_sec);
320    sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
321        (unsigned)(1900 + ptm->tm_year),
322        (unsigned)(ptm->tm_mon + 1),
323        (unsigned)(ptm->tm_mday),
324        (unsigned)(ptm->tm_hour),
325        (unsigned)(ptm->tm_min),
326        (unsigned)(ptm->tm_sec),
327        (unsigned)(tv.tv_usec)
328    );
329    /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
330    /* 5   + 3   + 3   + 3   + 3   + 3   + 9 = */
331    /* 20 (up to '.' inclusive) + 9 (not including '\0') */
332}
333
334/* NOT terminated! */
335static void fmt_time_bernstein_25(char *s)
336{
337    uint32_t pack[3];
338    struct timeval tv;
339    unsigned sec_hi;
340
341    gettimeofday(&tv, NULL);
342    sec_hi = (0x400000000000000aULL + tv.tv_sec) >> 32;
343    tv.tv_sec = (time_t)(0x400000000000000aULL) + tv.tv_sec;
344    tv.tv_usec *= 1000;
345    /* Network order is big-endian: most significant byte first.
346     * This is exactly what we want here */
347    pack[0] = htonl(sec_hi);
348    pack[1] = htonl(tv.tv_sec);
349    pack[2] = htonl(tv.tv_usec);
350    *s++ = '@';
351    bin2hex(s, (char*)pack, 12);
352}
353
354static void processorstart(struct logdir *ld)
355{
356    char sv_ch;
357    int pid;
358
359    if (!ld->processor) return;
360    if (ld->ppid) {
361        warnx("processor already running", ld->name);
362        return;
363    }
364
365    /* vfork'ed child trashes this byte, save... */
366    sv_ch = ld->fnsave[26];
367
368    while ((pid = vfork()) == -1)
369        pause2cannot("vfork for processor", ld->name);
370    if (!pid) {
371        int fd;
372
373        /* child */
374        /* Non-ignored signals revert to SIG_DFL on exec anyway */
375        /*bb_signals(0
376            + (1 << SIGTERM)
377            + (1 << SIGALRM)
378            + (1 << SIGHUP)
379            , SIG_DFL);*/
380        sig_unblock(SIGTERM);
381        sig_unblock(SIGALRM);
382        sig_unblock(SIGHUP);
383
384        if (verbose)
385            bb_error_msg(INFO"processing: %s/%s", ld->name, ld->fnsave);
386        fd = xopen(ld->fnsave, O_RDONLY|O_NDELAY);
387        xmove_fd(fd, 0);
388        ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
389        fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
390        xmove_fd(fd, 1);
391        fd = open("state", O_RDONLY|O_NDELAY);
392        if (fd == -1) {
393            if (errno != ENOENT)
394                bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
395            close(xopen("state", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT));
396            fd = xopen("state", O_RDONLY|O_NDELAY);
397        }
398        xmove_fd(fd, 4);
399        fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
400        xmove_fd(fd, 5);
401
402// getenv("SHELL")?
403        execl(DEFAULT_SHELL, DEFAULT_SHELL_SHORT_NAME, "-c", ld->processor, (char*) NULL);
404        bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
405    }
406    ld->fnsave[26] = sv_ch; /* ...restore */
407    ld->ppid = pid;
408}
409
410static unsigned processorstop(struct logdir *ld)
411{
412    char f[28];
413
414    if (ld->ppid) {
415        sig_unblock(SIGHUP);
416        while (safe_waitpid(ld->ppid, &wstat, 0) == -1)
417            pause2cannot("wait for processor", ld->name);
418        sig_block(SIGHUP);
419        ld->ppid = 0;
420    }
421    if (ld->fddir == -1)
422        return 1;
423    while (fchdir(ld->fddir) == -1)
424        pause2cannot("change directory, want processor", ld->name);
425    if (WEXITSTATUS(wstat) != 0) {
426        warnx("processor failed, restart", ld->name);
427        ld->fnsave[26] = 't';
428        unlink(ld->fnsave);
429        ld->fnsave[26] = 'u';
430        processorstart(ld);
431        while (fchdir(fdwdir) == -1)
432            pause1cannot("change to initial working directory");
433        return ld->processor ? 0 : 1;
434    }
435    ld->fnsave[26] = 't';
436    memcpy(f, ld->fnsave, 26);
437    f[26] = 's';
438    f[27] = '\0';
439    while (rename(ld->fnsave, f) == -1)
440        pause2cannot("rename processed", ld->name);
441    while (chmod(f, 0744) == -1)
442        pause2cannot("set mode of processed", ld->name);
443    ld->fnsave[26] = 'u';
444    if (unlink(ld->fnsave) == -1)
445        bb_error_msg(WARNING"can't unlink: %s/%s", ld->name, ld->fnsave);
446    while (rename("newstate", "state") == -1)
447        pause2cannot("rename state", ld->name);
448    if (verbose)
449        bb_error_msg(INFO"processed: %s/%s", ld->name, f);
450    while (fchdir(fdwdir) == -1)
451        pause1cannot("change to initial working directory");
452    return 1;
453}
454
455static void rmoldest(struct logdir *ld)
456{
457    DIR *d;
458    struct dirent *f;
459    char oldest[FMT_PTIME];
460    int n = 0;
461
462    oldest[0] = 'A'; oldest[1] = oldest[27] = 0;
463    while (!(d = opendir(".")))
464        pause2cannot("open directory, want rotate", ld->name);
465    errno = 0;
466    while ((f = readdir(d))) {
467        if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
468            if (f->d_name[26] == 't') {
469                if (unlink(f->d_name) == -1)
470                    warn2("can't unlink processor leftover", f->d_name);
471            } else {
472                ++n;
473                if (strcmp(f->d_name, oldest) < 0)
474                    memcpy(oldest, f->d_name, 27);
475            }
476            errno = 0;
477        }
478    }
479    if (errno)
480        warn2("can't read directory", ld->name);
481    closedir(d);
482
483    if (ld->nmax && (n > ld->nmax)) {
484        if (verbose)
485            bb_error_msg(INFO"delete: %s/%s", ld->name, oldest);
486        if ((*oldest == '@') && (unlink(oldest) == -1))
487            warn2("can't unlink oldest logfile", ld->name);
488    }
489}
490
491static unsigned rotate(struct logdir *ld)
492{
493    struct stat st;
494    unsigned now;
495
496    if (ld->fddir == -1) {
497        ld->rotate_period = 0;
498        return 0;
499    }
500    if (ld->ppid)
501        while (!processorstop(ld))
502            continue;
503
504    while (fchdir(ld->fddir) == -1)
505        pause2cannot("change directory, want rotate", ld->name);
506
507    /* create new filename */
508    ld->fnsave[25] = '.';
509    ld->fnsave[26] = 's';
510    if (ld->processor)
511        ld->fnsave[26] = 'u';
512    ld->fnsave[27] = '\0';
513    do {
514        fmt_time_bernstein_25(ld->fnsave);
515        errno = 0;
516        stat(ld->fnsave, &st);
517    } while (errno != ENOENT);
518
519    now = monotonic_sec();
520    if (ld->rotate_period && LESS(ld->next_rotate, now)) {
521        ld->next_rotate = now + ld->rotate_period;
522        if (LESS(ld->next_rotate, nearest_rotate))
523            nearest_rotate = ld->next_rotate;
524    }
525
526    if (ld->size > 0) {
527        while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
528            pause2cannot("fsync current logfile", ld->name);
529        while (fchmod(ld->fdcur, 0744) == -1)
530            pause2cannot("set mode of current", ld->name);
531        ////close(ld->fdcur);
532        fclose(ld->filecur);
533
534        if (verbose) {
535            bb_error_msg(INFO"rename: %s/current %s %u", ld->name,
536                    ld->fnsave, ld->size);
537        }
538        while (rename("current", ld->fnsave) == -1)
539            pause2cannot("rename current", ld->name);
540        while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
541            pause2cannot("create new current", ld->name);
542        while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL) ////
543            pause2cannot("create new current", ld->name); /* very unlikely */
544        setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
545        close_on_exec_on(ld->fdcur);
546        ld->size = 0;
547        while (fchmod(ld->fdcur, 0644) == -1)
548            pause2cannot("set mode of current", ld->name);
549
550        rmoldest(ld);
551        processorstart(ld);
552    }
553
554    while (fchdir(fdwdir) == -1)
555        pause1cannot("change to initial working directory");
556    return 1;
557}
558
559static int buffer_pwrite(int n, char *s, unsigned len)
560{
561    int i;
562    struct logdir *ld = &dir[n];
563
564    if (ld->sizemax) {
565        if (ld->size >= ld->sizemax)
566            rotate(ld);
567        if (len > (ld->sizemax - ld->size))
568            len = ld->sizemax - ld->size;
569    }
570    while (1) {
571        ////i = full_write(ld->fdcur, s, len);
572        ////if (i != -1) break;
573        i = fwrite(s, 1, len, ld->filecur);
574        if (i == len) break;
575
576        if ((errno == ENOSPC) && (ld->nmin < ld->nmax)) {
577            DIR *d;
578            struct dirent *f;
579            char oldest[FMT_PTIME];
580            int j = 0;
581
582            while (fchdir(ld->fddir) == -1)
583                pause2cannot("change directory, want remove old logfile",
584                             ld->name);
585            oldest[0] = 'A';
586            oldest[1] = oldest[27] = '\0';
587            while (!(d = opendir(".")))
588                pause2cannot("open directory, want remove old logfile",
589                             ld->name);
590            errno = 0;
591            while ((f = readdir(d)))
592                if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
593                    ++j;
594                    if (strcmp(f->d_name, oldest) < 0)
595                        memcpy(oldest, f->d_name, 27);
596                }
597            if (errno) warn2("can't read directory, want remove old logfile",
598                    ld->name);
599            closedir(d);
600            errno = ENOSPC;
601            if (j > ld->nmin) {
602                if (*oldest == '@') {
603                    bb_error_msg(WARNING"out of disk space, delete: %s/%s",
604                            ld->name, oldest);
605                    errno = 0;
606                    if (unlink(oldest) == -1) {
607                        warn2("can't unlink oldest logfile", ld->name);
608                        errno = ENOSPC;
609                    }
610                    while (fchdir(fdwdir) == -1)
611                        pause1cannot("change to initial working directory");
612                }
613            }
614        }
615        if (errno)
616            pause2cannot("write to current", ld->name);
617    }
618
619    ld->size += i;
620    if (ld->sizemax)
621        if (s[i-1] == '\n')
622            if (ld->size >= (ld->sizemax - linemax))
623                rotate(ld);
624    return i;
625}
626
627static void logdir_close(struct logdir *ld)
628{
629    if (ld->fddir == -1)
630        return;
631    if (verbose)
632        bb_error_msg(INFO"close: %s", ld->name);
633    close(ld->fddir);
634    ld->fddir = -1;
635    if (ld->fdcur == -1)
636        return; /* impossible */
637    while (fflush(ld->filecur) || fsync(ld->fdcur) == -1)
638        pause2cannot("fsync current logfile", ld->name);
639    while (fchmod(ld->fdcur, 0744) == -1)
640        pause2cannot("set mode of current", ld->name);
641    ////close(ld->fdcur);
642    fclose(ld->filecur);
643    ld->fdcur = -1;
644    if (ld->fdlock == -1)
645        return; /* impossible */
646    close(ld->fdlock);
647    ld->fdlock = -1;
648    free(ld->processor);
649    ld->processor = NULL;
650}
651
652static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
653{
654    char buf[128];
655    unsigned now;
656    char *new, *s, *np;
657    int i;
658    struct stat st;
659
660    now = monotonic_sec();
661
662    ld->fddir = open(fn, O_RDONLY|O_NDELAY);
663    if (ld->fddir == -1) {
664        warn2("can't open log directory", (char*)fn);
665        return 0;
666    }
667    close_on_exec_on(ld->fddir);
668    if (fchdir(ld->fddir) == -1) {
669        logdir_close(ld);
670        warn2("can't change directory", (char*)fn);
671        return 0;
672    }
673    ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
674    if ((ld->fdlock == -1)
675     || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
676    ) {
677        logdir_close(ld);
678        warn2("can't lock directory", (char*)fn);
679        while (fchdir(fdwdir) == -1)
680            pause1cannot("change to initial working directory");
681        return 0;
682    }
683    close_on_exec_on(ld->fdlock);
684
685    ld->size = 0;
686    ld->sizemax = 1000000;
687    ld->nmax = ld->nmin = 10;
688    ld->rotate_period = 0;
689    ld->name = (char*)fn;
690    ld->ppid = 0;
691    ld->match = '+';
692    free(ld->inst); ld->inst = NULL;
693    free(ld->processor); ld->processor = NULL;
694
695    /* read config */
696    i = open_read_close("config", buf, sizeof(buf) - 1);
697    if (i < 0 && errno != ENOENT)
698        bb_perror_msg(WARNING"%s/config", ld->name);
699    if (i > 0) {
700        buf[i] = '\0';
701        if (verbose)
702            bb_error_msg(INFO"read: %s/config", ld->name);
703        s = buf;
704        while (s) {
705            np = strchr(s, '\n');
706            if (np)
707                *np++ = '\0';
708            switch (s[0]) {
709            case '+':
710            case '-':
711            case 'e':
712            case 'E':
713                /* Filtering requires one-line buffering,
714                 * resetting the "find newline" function
715                 * accordingly */
716                memRchr = memchr;
717                /* Add '\n'-terminated line to ld->inst */
718                while (1) {
719                    int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
720                    if (l >= 0 && new)
721                        break;
722                    pause_nomem();
723                }
724                free(ld->inst);
725                ld->inst = new;
726                break;
727            case 's': {
728                static const struct suffix_mult km_suffixes[] = {
729                    { "k", 1024 },
730                    { "m", 1024*1024 },
731                    { "", 0 }
732                };
733                ld->sizemax = xatou_sfx(&s[1], km_suffixes);
734                break;
735            }
736            case 'n':
737                ld->nmax = xatoi_positive(&s[1]);
738                break;
739            case 'N':
740                ld->nmin = xatoi_positive(&s[1]);
741                break;
742            case 't': {
743                static const struct suffix_mult mh_suffixes[] = {
744                    { "m", 60 },
745                    { "h", 60*60 },
746                    /*{ "d", 24*60*60 },*/
747                    { "", 0 }
748                };
749                ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
750                if (ld->rotate_period) {
751                    ld->next_rotate = now + ld->rotate_period;
752                    if (!tmaxflag || LESS(ld->next_rotate, nearest_rotate))
753                        nearest_rotate = ld->next_rotate;
754                    tmaxflag = 1;
755                }
756                break;
757            }
758            case '!':
759                if (s[1]) {
760                    free(ld->processor);
761                    ld->processor = wstrdup(s);
762                }
763                break;
764            }
765            s = np;
766        }
767        /* Convert "aa\nbb\ncc\n\0" to "aa\0bb\0cc\0\0" */
768        s = ld->inst;
769        while (s) {
770            np = strchr(s, '\n');
771            if (np)
772                *np++ = '\0';
773            s = np;
774        }
775    }
776
777    /* open current */
778    i = stat("current", &st);
779    if (i != -1) {
780        if (st.st_size && !(st.st_mode & S_IXUSR)) {
781            ld->fnsave[25] = '.';
782            ld->fnsave[26] = 'u';
783            ld->fnsave[27] = '\0';
784            do {
785                fmt_time_bernstein_25(ld->fnsave);
786                errno = 0;
787                stat(ld->fnsave, &st);
788            } while (errno != ENOENT);
789            while (rename("current", ld->fnsave) == -1)
790                pause2cannot("rename current", ld->name);
791            rmoldest(ld);
792            i = -1;
793        } else {
794            /* st.st_size can be not just bigger, but WIDER!
795             * This code is safe: if st.st_size > 4GB, we select
796             * ld->sizemax (because it's "unsigned") */
797            ld->size = (st.st_size > ld->sizemax) ? ld->sizemax : st.st_size;
798        }
799    } else {
800        if (errno != ENOENT) {
801            logdir_close(ld);
802            warn2("can't stat current", ld->name);
803            while (fchdir(fdwdir) == -1)
804                pause1cannot("change to initial working directory");
805            return 0;
806        }
807    }
808    while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
809        pause2cannot("open current", ld->name);
810    while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
811        pause2cannot("open current", ld->name); ////
812    setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
813
814    close_on_exec_on(ld->fdcur);
815    while (fchmod(ld->fdcur, 0644) == -1)
816        pause2cannot("set mode of current", ld->name);
817
818    if (verbose) {
819        if (i == 0) bb_error_msg(INFO"append: %s/current", ld->name);
820        else bb_error_msg(INFO"new: %s/current", ld->name);
821    }
822
823    while (fchdir(fdwdir) == -1)
824        pause1cannot("change to initial working directory");
825    return 1;
826}
827
828static void logdirs_reopen(void)
829{
830    int l;
831    int ok = 0;
832
833    tmaxflag = 0;
834    for (l = 0; l < dirn; ++l) {
835        logdir_close(&dir[l]);
836        if (logdir_open(&dir[l], fndir[l]))
837            ok = 1;
838    }
839    if (!ok)
840        fatalx("no functional log directories");
841}
842
843/* Will look good in libbb one day */
844static ssize_t ndelay_read(int fd, void *buf, size_t count)
845{
846    if (!(fl_flag_0 & O_NONBLOCK))
847        fcntl(fd, F_SETFL, fl_flag_0 | O_NONBLOCK);
848    count = safe_read(fd, buf, count);
849    if (!(fl_flag_0 & O_NONBLOCK))
850        fcntl(fd, F_SETFL, fl_flag_0);
851    return count;
852}
853
854/* Used for reading stdin */
855static int buffer_pread(/*int fd, */char *s, unsigned len)
856{
857    unsigned now;
858    struct pollfd input;
859    int i;
860
861    input.fd = STDIN_FILENO;
862    input.events = POLLIN;
863
864    do {
865        if (rotateasap) {
866            for (i = 0; i < dirn; ++i)
867                rotate(dir + i);
868            rotateasap = 0;
869        }
870        if (exitasap) {
871            if (linecomplete)
872                return 0;
873            len = 1;
874        }
875        if (reopenasap) {
876            logdirs_reopen();
877            reopenasap = 0;
878        }
879        now = monotonic_sec();
880        nearest_rotate = now + (45 * 60 + 45);
881        for (i = 0; i < dirn; ++i) {
882            if (dir[i].rotate_period) {
883                if (LESS(dir[i].next_rotate, now))
884                    rotate(dir + i);
885                if (LESS(dir[i].next_rotate, nearest_rotate))
886                    nearest_rotate = dir[i].next_rotate;
887            }
888        }
889
890        sigprocmask(SIG_UNBLOCK, &blocked_sigset, NULL);
891        i = nearest_rotate - now;
892        if (i > 1000000)
893            i = 1000000;
894        if (i <= 0)
895            i = 1;
896        poll(&input, 1, i * 1000);
897        sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
898
899        i = ndelay_read(STDIN_FILENO, s, len);
900        if (i >= 0)
901            break;
902        if (errno == EINTR)
903            continue;
904        if (errno != EAGAIN) {
905            warn("can't read standard input");
906            break;
907        }
908        /* else: EAGAIN - normal, repeat silently */
909    } while (!exitasap);
910
911    if (i > 0) {
912        int cnt;
913        linecomplete = (s[i-1] == '\n');
914        if (!repl)
915            return i;
916
917        cnt = i;
918        while (--cnt >= 0) {
919            char ch = *s;
920            if (ch != '\n') {
921                if (ch < 32 || ch > 126)
922                    *s = repl;
923                else {
924                    int j;
925                    for (j = 0; replace[j]; ++j) {
926                        if (ch == replace[j]) {
927                            *s = repl;
928                            break;
929                        }
930                    }
931                }
932            }
933            s++;
934        }
935    }
936    return i;
937}
938
939static void sig_term_handler(int sig_no UNUSED_PARAM)
940{
941    if (verbose)
942        bb_error_msg(INFO"sig%s received", "term");
943    exitasap = 1;
944}
945
946static void sig_child_handler(int sig_no UNUSED_PARAM)
947{
948    pid_t pid;
949    int l;
950
951    if (verbose)
952        bb_error_msg(INFO"sig%s received", "child");
953    while ((pid = wait_any_nohang(&wstat)) > 0) {
954        for (l = 0; l < dirn; ++l) {
955            if (dir[l].ppid == pid) {
956                dir[l].ppid = 0;
957                processorstop(&dir[l]);
958                break;
959            }
960        }
961    }
962}
963
964static void sig_alarm_handler(int sig_no UNUSED_PARAM)
965{
966    if (verbose)
967        bb_error_msg(INFO"sig%s received", "alarm");
968    rotateasap = 1;
969}
970
971static void sig_hangup_handler(int sig_no UNUSED_PARAM)
972{
973    if (verbose)
974        bb_error_msg(INFO"sig%s received", "hangup");
975    reopenasap = 1;
976}
977
978static void logmatch(struct logdir *ld)
979{
980    char *s;
981
982    ld->match = '+';
983    ld->matcherr = 'E';
984    s = ld->inst;
985    while (s && s[0]) {
986        switch (s[0]) {
987        case '+':
988        case '-':
989            if (pmatch(s+1, line, linelen))
990                ld->match = s[0];
991            break;
992        case 'e':
993        case 'E':
994            if (pmatch(s+1, line, linelen))
995                ld->matcherr = s[0];
996            break;
997        }
998        s += strlen(s) + 1;
999    }
1000}
1001
1002int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1003int svlogd_main(int argc, char **argv)
1004{
1005    char *r, *l, *b;
1006    ssize_t stdin_cnt = 0;
1007    int i;
1008    unsigned opt;
1009    unsigned timestamp = 0;
1010
1011    INIT_G();
1012
1013    opt_complementary = "tt:vv";
1014    opt = getopt32(argv, "r:R:l:b:tv",
1015            &r, &replace, &l, &b, &timestamp, &verbose);
1016    if (opt & 1) { // -r
1017        repl = r[0];
1018        if (!repl || r[1])
1019            bb_show_usage();
1020    }
1021    if (opt & 2) if (!repl) repl = '_'; // -R
1022    if (opt & 4) { // -l
1023        linemax = xatou_range(l, 0, BUFSIZ-26);
1024        if (linemax == 0)
1025            linemax = BUFSIZ-26;
1026        if (linemax < 256)
1027            linemax = 256;
1028    }
1029    ////if (opt & 8) { // -b
1030    ////    buflen = xatoi_positive(b);
1031    ////    if (buflen == 0) buflen = 1024;
1032    ////}
1033    //if (opt & 0x10) timestamp++; // -t
1034    //if (opt & 0x20) verbose++; // -v
1035    //if (timestamp > 2) timestamp = 2;
1036    argv += optind;
1037    argc -= optind;
1038
1039    dirn = argc;
1040    if (dirn <= 0)
1041        bb_show_usage();
1042    ////if (buflen <= linemax) bb_show_usage();
1043    fdwdir = xopen(".", O_RDONLY|O_NDELAY);
1044    close_on_exec_on(fdwdir);
1045    dir = xzalloc(dirn * sizeof(dir[0]));
1046    for (i = 0; i < dirn; ++i) {
1047        dir[i].fddir = -1;
1048        dir[i].fdcur = -1;
1049        ////dir[i].btmp = xmalloc(buflen);
1050        /*dir[i].ppid = 0;*/
1051    }
1052    /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
1053    fndir = argv;
1054    /* We cannot set NONBLOCK on fd #0 permanently - this setting
1055     * _isn't_ per-process! It is shared among all other processes
1056     * with the same stdin */
1057    fl_flag_0 = fcntl(0, F_GETFL);
1058
1059    sigemptyset(&blocked_sigset);
1060    sigaddset(&blocked_sigset, SIGTERM);
1061    sigaddset(&blocked_sigset, SIGCHLD);
1062    sigaddset(&blocked_sigset, SIGALRM);
1063    sigaddset(&blocked_sigset, SIGHUP);
1064    sigprocmask(SIG_BLOCK, &blocked_sigset, NULL);
1065    bb_signals_recursive_norestart(1 << SIGTERM, sig_term_handler);
1066    bb_signals_recursive_norestart(1 << SIGCHLD, sig_child_handler);
1067    bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
1068    bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
1069
1070    /* Without timestamps, we don't have to print each line
1071     * separately, so we can look for _last_ newline, not first,
1072     * thus batching writes. If filtering is enabled in config,
1073     * logdirs_reopen resets it to memchr.
1074     */
1075    memRchr = (timestamp ? memchr : memrchr);
1076
1077    logdirs_reopen();
1078
1079    setvbuf(stderr, NULL, _IOFBF, linelen);
1080
1081    /* Each iteration processes one or more lines */
1082    while (1) {
1083        char stamp[FMT_PTIME];
1084        char *lineptr;
1085        char *printptr;
1086        char *np;
1087        int printlen;
1088        char ch;
1089
1090        lineptr = line;
1091        if (timestamp)
1092            lineptr += 26;
1093
1094        /* lineptr[0..linemax-1] - buffer for stdin */
1095        /* (possibly has some unprocessed data from prev loop) */
1096
1097        /* Refill the buffer if needed */
1098        np = memRchr(lineptr, '\n', stdin_cnt);
1099        if (!np && !exitasap) {
1100            i = linemax - stdin_cnt; /* avail. bytes at tail */
1101            if (i >= 128) {
1102                i = buffer_pread(/*0, */lineptr + stdin_cnt, i);
1103                if (i <= 0) /* EOF or error on stdin */
1104                    exitasap = 1;
1105                else {
1106                    np = memRchr(lineptr + stdin_cnt, '\n', i);
1107                    stdin_cnt += i;
1108                }
1109            }
1110        }
1111        if (stdin_cnt <= 0 && exitasap)
1112            break;
1113
1114        /* Search for '\n' (in fact, np already holds the result) */
1115        linelen = stdin_cnt;
1116        if (np) {
1117 print_to_nl:
1118            /* NB: starting from here lineptr may point
1119             * farther out into line[] */
1120            linelen = np - lineptr + 1;
1121        }
1122        /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1123        ch = lineptr[linelen-1];
1124
1125        /* Biggest performance hit was coming from the fact
1126         * that we did not buffer writes. We were reading many lines
1127         * in one read() above, but wrote one line per write().
1128         * We are using stdio to fix that */
1129
1130        /* write out lineptr[0..linelen-1] to each log destination
1131         * (or lineptr[-26..linelen-1] if timestamping) */
1132        printlen = linelen;
1133        printptr = lineptr;
1134        if (timestamp) {
1135            if (timestamp == 1)
1136                fmt_time_bernstein_25(stamp);
1137            else /* 2: */
1138                fmt_time_human_30nul(stamp);
1139            printlen += 26;
1140            printptr -= 26;
1141            memcpy(printptr, stamp, 25);
1142            printptr[25] = ' ';
1143        }
1144        for (i = 0; i < dirn; ++i) {
1145            struct logdir *ld = &dir[i];
1146            if (ld->fddir == -1)
1147                continue;
1148            if (ld->inst)
1149                logmatch(ld);
1150            if (ld->matcherr == 'e') {
1151                /* runit-1.8.0 compat: if timestamping, do it on stderr too */
1152                ////full_write(STDERR_FILENO, printptr, printlen);
1153                fwrite(printptr, 1, printlen, stderr);
1154            }
1155            if (ld->match != '+')
1156                continue;
1157            buffer_pwrite(i, printptr, printlen);
1158        }
1159
1160        /* If we didn't see '\n' (long input line), */
1161        /* read/write repeatedly until we see it */
1162        while (ch != '\n') {
1163            /* lineptr is emptied now, safe to use as buffer */
1164            stdin_cnt = exitasap ? -1 : buffer_pread(/*0, */lineptr, linemax);
1165            if (stdin_cnt <= 0) { /* EOF or error on stdin */
1166                exitasap = 1;
1167                lineptr[0] = ch = '\n';
1168                linelen = 1;
1169                stdin_cnt = 1;
1170            } else {
1171                linelen = stdin_cnt;
1172                np = memRchr(lineptr, '\n', stdin_cnt);
1173                if (np)
1174                    linelen = np - lineptr + 1;
1175                ch = lineptr[linelen-1];
1176            }
1177            /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
1178            for (i = 0; i < dirn; ++i) {
1179                if (dir[i].fddir == -1)
1180                    continue;
1181                if (dir[i].matcherr == 'e') {
1182                    ////full_write(STDERR_FILENO, lineptr, linelen);
1183                    fwrite(lineptr, 1, linelen, stderr);
1184                }
1185                if (dir[i].match != '+')
1186                    continue;
1187                buffer_pwrite(i, lineptr, linelen);
1188            }
1189        }
1190
1191        stdin_cnt -= linelen;
1192        if (stdin_cnt > 0) {
1193            lineptr += linelen;
1194            /* If we see another '\n', we don't need to read
1195             * next piece of input: can print what we have */
1196            np = memRchr(lineptr, '\n', stdin_cnt);
1197            if (np)
1198                goto print_to_nl;
1199            /* Move unprocessed data to the front of line */
1200            memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
1201        }
1202        fflush_all();////
1203    }
1204
1205    for (i = 0; i < dirn; ++i) {
1206        if (dir[i].ppid)
1207            while (!processorstop(&dir[i]))
1208                continue;
1209        logdir_close(&dir[i]);
1210    }
1211    return 0;
1212}
Note: See TracBrowser for help on using the repository browser.