source: branches/3.2/mindi-busybox/runit/svlogd.c @ 3232

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