Bump to version 1.22.1
[platform/upstream/busybox.git] / runit / svlogd.c
index 79286a3..c080b9a 100644 (file)
@@ -28,7 +28,120 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 /* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */
 /* TODO: depends on runit_lib.c - review and reduce/eliminate */
 
-#include <sys/poll.h>
+/*
+Config files
+
+On startup, and after receiving a HUP signal, svlogd checks for each
+log directory log if the configuration file log/config exists,
+and if so, reads the file line by line and adjusts configuration
+for log as follows:
+
+If the line is empty, or starts with a #, it is ignored. A line
+of the form
+
+ssize
+    sets the maximum file size of current when svlogd should rotate
+    the current log file to size bytes. Default is 1000000.
+    If size is zero, svlogd doesnt rotate log files
+    You should set size to at least (2 * len).
+nnum
+    sets the number of old log files svlogd should maintain to num.
+    If svlogd sees more that num old log files in log after log file
+    rotation, it deletes the oldest one. Default is 10.
+    If num is zero, svlogd doesnt remove old log files.
+Nmin
+    sets the minimum number of old log files svlogd should maintain
+    to min. min must be less than num. If min is set, and svlogd
+    cannot write to current because the filesystem is full,
+    and it sees more than min old log files, it deletes the oldest one.
+ttimeout
+    sets the maximum age of the current log file when svlogd should
+    rotate the current log file to timeout seconds. If current
+    is timeout seconds old, and is not empty, svlogd forces log file rotation.
+!processor
+    tells svlogd to feed each recent log file through processor
+    (see above) on log file rotation. By default log files are not processed.
+ua.b.c.d[:port]
+    tells svlogd to transmit the first len characters of selected
+    log messages to the IP address a.b.c.d, port number port.
+    If port isnt set, the default port for syslog is used (514).
+    len can be set through the -l option, see below. If svlogd
+    has trouble sending udp packets, it writes error messages
+    to the log directory. Attention: logging through udp is unreliable,
+    and should be used in private networks only.
+Ua.b.c.d[:port]
+    is the same as the u line above, but the log messages are no longer
+    written to the log directory, but transmitted through udp only.
+    Error messages from svlogd concerning sending udp packages still go
+    to the log directory.
+pprefix
+    tells svlogd to prefix each line to be written to the log directory,
+    to standard error, or through UDP, with prefix.
+
+If a line starts with a -, +, e, or E, svlogd matches the first len characters
+of each log message against pattern and acts accordingly:
+
+-pattern
+    the log message is deselected.
++pattern
+    the log message is selected.
+epattern
+    the log message is selected to be printed to standard error.
+Epattern
+    the log message is deselected to be printed to standard error.
+
+Initially each line is selected to be written to log/current. Deselected
+log messages are discarded from log. Initially each line is deselected
+to be written to standard err. Log messages selected for standard error
+are written to standard error.
+
+Pattern Matching
+
+svlogd matches a log message against the string pattern as follows:
+
+pattern is applied to the log message one character by one, starting
+with the first. A character not a star (*) and not a plus (+) matches itself.
+A plus matches the next character in pattern in the log message one
+or more times. A star before the end of pattern matches any string
+in the log message that does not include the next character in pattern.
+A star at the end of pattern matches any string.
+
+Timestamps optionally added by svlogd are not considered part
+of the log message.
+
+An svlogd pattern is not a regular expression. For example consider
+a log message like this
+
+2005-12-18_09:13:50.97618 tcpsvd: info: pid 1977 from 10.4.1.14
+
+The following pattern doesnt match
+
+-*pid*
+
+because the first star matches up to the first p in tcpsvd,
+and then the match fails because i is not s. To match this
+log message, you can use a pattern like this instead
+
+-*: *: pid *
+*/
+
+//usage:#define svlogd_trivial_usage
+//usage:       "[-ttv] [-r C] [-R CHARS] [-l MATCHLEN] [-b BUFLEN] DIR..."
+//usage:#define svlogd_full_usage "\n\n"
+//usage:       "Continuously read log data from stdin and write to rotated log files in DIRs"
+//usage:   "\n"
+//usage:   "\n""DIR/config file modifies behavior:"
+//usage:   "\n""sSIZE - when to rotate logs"
+//usage:   "\n""nNUM - number of files to retain"
+/*usage:   "\n""NNUM - min number files to retain" - confusing */
+/*usage:   "\n""tSEC - rotate file if it get SEC seconds old" - confusing */
+//usage:   "\n""!PROG - process rotated log with PROG"
+/*usage:   "\n""uIPADDR - send log over UDP" - unsupported */
+/*usage:   "\n""UIPADDR - send log over UDP and DONT log" - unsupported */
+/*usage:   "\n""pPFX - prefix each line with PFX" - unsupported */
+//usage:   "\n""+,-PATTERN - (de)select line for logging"
+//usage:   "\n""E,ePATTERN - (de)select line for stderr"
+
 #include <sys/file.h>
 #include "libbb.h"
 #include "runit_lib.h"
@@ -72,6 +185,9 @@ struct globals {
        int wstat;
        unsigned nearest_rotate;
 
+       void* (*memRchr)(const void *, int, size_t);
+       char *shell;
+
        smallint exitasap;
        smallint rotateasap;
        smallint reopenasap;
@@ -85,7 +201,7 @@ struct globals {
 
        sigset_t blocked_sigset;
 };
-#define G (*(struct globals*)ptr_to_globals)
+#define G (*ptr_to_globals)
 #define dir            (G.dir           )
 #define verbose        (G.verbose       )
 #define linemax        (G.linemax       )
@@ -94,6 +210,7 @@ struct globals {
 #define fndir          (G.fndir         )
 #define fdwdir         (G.fdwdir        )
 #define wstat          (G.wstat         )
+#define memRchr        (G.memRchr       )
 #define nearest_rotate (G.nearest_rotate)
 #define exitasap       (G.exitasap      )
 #define rotateasap     (G.rotateasap    )
@@ -121,7 +238,6 @@ struct globals {
 #define PAUSE "pausing: "
 #define INFO "info: "
 
-#define usage() bb_show_usage()
 static void fatalx(const char *m0)
 {
        bb_error_msg_and_die(FATAL"%s", m0);
@@ -162,23 +278,69 @@ static char* wstrdup(const char *str)
        return s;
 }
 
+static unsigned pmatch(const char *p, const char *s, unsigned len)
+{
+       for (;;) {
+               char c = *p++;
+               if (!c) return !len;
+               switch (c) {
+               case '*':
+                       c = *p;
+                       if (!c) return 1;
+                       for (;;) {
+                               if (!len) return 0;
+                               if (*s == c) break;
+                               ++s;
+                               --len;
+                       }
+                       continue;
+               case '+':
+                       c = *p++;
+                       if (c != *s) return 0;
+                       for (;;) {
+                               if (!len) return 1;
+                               if (*s != c) break;
+                               ++s;
+                               --len;
+                       }
+                       continue;
+                       /*
+               case '?':
+                       if (*p == '?') {
+                               if (*s != '?') return 0;
+                               ++p;
+                       }
+                       ++s; --len;
+                       continue;
+                       */
+               default:
+                       if (!len) return 0;
+                       if (*s != c) return 0;
+                       ++s;
+                       --len;
+                       continue;
+               }
+       }
+       return 0;
+}
+
 /*** ex fmt_ptime.[ch] ***/
 
 /* NUL terminated */
 static void fmt_time_human_30nul(char *s)
 {
-       struct tm *t;
+       struct tm *ptm;
        struct timeval tv;
 
        gettimeofday(&tv, NULL);
-       t = gmtime(&(tv.tv_sec));
+       ptm = gmtime(&tv.tv_sec);
        sprintf(s, "%04u-%02u-%02u_%02u:%02u:%02u.%06u000",
-               (unsigned)(1900 + t->tm_year),
-               (unsigned)(t->tm_mon + 1),
-               (unsigned)(t->tm_mday),
-               (unsigned)(t->tm_hour),
-               (unsigned)(t->tm_min),
-               (unsigned)(t->tm_sec),
+               (unsigned)(1900 + ptm->tm_year),
+               (unsigned)(ptm->tm_mon + 1),
+               (unsigned)(ptm->tm_mday),
+               (unsigned)(ptm->tm_hour),
+               (unsigned)(ptm->tm_min),
+               (unsigned)(ptm->tm_sec),
                (unsigned)(tv.tv_usec)
        );
        /* 4+1 + 2+1 + 2+1 + 2+1 + 2+1 + 2+1 + 9 = */
@@ -220,6 +382,9 @@ static void processorstart(struct logdir *ld)
        /* vfork'ed child trashes this byte, save... */
        sv_ch = ld->fnsave[26];
 
+       if (!G.shell)
+               G.shell = xstrdup(get_shell_name());
+
        while ((pid = vfork()) == -1)
                pause2cannot("vfork for processor", ld->name);
        if (!pid) {
@@ -243,7 +408,7 @@ static void processorstart(struct logdir *ld)
                ld->fnsave[26] = 't'; /* <- that's why we need sv_ch! */
                fd = xopen(ld->fnsave, O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
                xmove_fd(fd, 1);
-               fd = open_read("state");
+               fd = open("state", O_RDONLY|O_NDELAY);
                if (fd == -1) {
                        if (errno != ENOENT)
                                bb_perror_msg_and_die(FATAL"can't %s processor %s", "open state for", ld->name);
@@ -254,8 +419,7 @@ static void processorstart(struct logdir *ld)
                fd = xopen("newstate", O_WRONLY|O_NDELAY|O_TRUNC|O_CREAT);
                xmove_fd(fd, 5);
 
-// getenv("SHELL")?
-               execl("/bin/sh", "/bin/sh" + 5, "-c", ld->processor, (char*) NULL);
+               execl(G.shell, G.shell, "-c", ld->processor, (char*) NULL);
                bb_perror_msg_and_die(FATAL"can't %s processor %s", "run", ld->name);
        }
        ld->fnsave[26] = sv_ch; /* ...restore */
@@ -273,7 +437,8 @@ static unsigned processorstop(struct logdir *ld)
                sig_block(SIGHUP);
                ld->ppid = 0;
        }
-       if (ld->fddir == -1) return 1;
+       if (ld->fddir == -1)
+               return 1;
        while (fchdir(ld->fddir) == -1)
                pause2cannot("change directory, want processor", ld->name);
        if (WEXITSTATUS(wstat) != 0) {
@@ -435,12 +600,12 @@ static int buffer_pwrite(int n, char *s, unsigned len)
 
                        while (fchdir(ld->fddir) == -1)
                                pause2cannot("change directory, want remove old logfile",
-                                                        ld->name);
+                                                       ld->name);
                        oldest[0] = 'A';
                        oldest[1] = oldest[27] = '\0';
                        while (!(d = opendir(".")))
                                pause2cannot("open directory, want remove old logfile",
-                                                        ld->name);
+                                                       ld->name);
                        errno = 0;
                        while ((f = readdir(d)))
                                if ((f->d_name[0] == '@') && (strlen(f->d_name) == 27)) {
@@ -503,7 +668,7 @@ static void logdir_close(struct logdir *ld)
        ld->processor = NULL;
 }
 
-static unsigned logdir_open(struct logdir *ld, const char *fn)
+static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
 {
        char buf[128];
        unsigned now;
@@ -526,7 +691,7 @@ static unsigned logdir_open(struct logdir *ld, const char *fn)
        }
        ld->fdlock = open("lock", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600);
        if ((ld->fdlock == -1)
-        || (lock_exnb(ld->fdlock) == -1)
+        || (flock(ld->fdlock, LOCK_EX | LOCK_NB) == -1)
        ) {
                logdir_close(ld);
                warn2("can't lock directory", (char*)fn);
@@ -564,9 +729,13 @@ static unsigned logdir_open(struct logdir *ld, const char *fn)
                        case '-':
                        case 'e':
                        case 'E':
+                               /* Filtering requires one-line buffering,
+                                * resetting the "find newline" function
+                                * accordingly */
+                               memRchr = memchr;
                                /* Add '\n'-terminated line to ld->inst */
                                while (1) {
-                                       int l = asprintf(&new, "%s%s\n", ld->inst ? : "", s);
+                                       int l = asprintf(&new, "%s%s\n", ld->inst ? ld->inst : "", s);
                                        if (l >= 0 && new)
                                                break;
                                        pause_nomem();
@@ -575,26 +744,21 @@ static unsigned logdir_open(struct logdir *ld, const char *fn)
                                ld->inst = new;
                                break;
                        case 's': {
-                               static const struct suffix_mult km_suffixes[] = {
-                                       { "k", 1024 },
-                                       { "m", 1024*1024 },
-                                       { }
-                               };
                                ld->sizemax = xatou_sfx(&s[1], km_suffixes);
                                break;
                        }
                        case 'n':
-                               ld->nmax = xatoi_u(&s[1]);
+                               ld->nmax = xatoi_positive(&s[1]);
                                break;
                        case 'N':
-                               ld->nmin = xatoi_u(&s[1]);
+                               ld->nmin = xatoi_positive(&s[1]);
                                break;
                        case 't': {
                                static const struct suffix_mult mh_suffixes[] = {
                                        { "m", 60 },
                                        { "h", 60*60 },
                                        /*{ "d", 24*60*60 },*/
-                                       { }
+                                       { "", 0 }
                                };
                                ld->rotate_period = xatou_sfx(&s[1], mh_suffixes);
                                if (ld->rotate_period) {
@@ -657,8 +821,8 @@ static unsigned logdir_open(struct logdir *ld, const char *fn)
        }
        while ((ld->fdcur = open("current", O_WRONLY|O_NDELAY|O_APPEND|O_CREAT, 0600)) == -1)
                pause2cannot("open current", ld->name);
-       /* we presume this cannot fail */
-       ld->filecur = fdopen(ld->fdcur, "a"); ////
+       while ((ld->filecur = fdopen(ld->fdcur, "a")) == NULL)
+               pause2cannot("open current", ld->name); ////
        setvbuf(ld->filecur, NULL, _IOFBF, linelen); ////
 
        close_on_exec_on(ld->fdcur);
@@ -708,7 +872,7 @@ static int buffer_pread(/*int fd, */char *s, unsigned len)
        struct pollfd input;
        int i;
 
-       input.fd = 0;
+       input.fd = STDIN_FILENO;
        input.events = POLLIN;
 
        do {
@@ -852,12 +1016,11 @@ static void logmatch(struct logdir *ld)
 int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int svlogd_main(int argc, char **argv)
 {
-       char *r,*l,*b;
+       char *r, *l, *b;
        ssize_t stdin_cnt = 0;
        int i;
        unsigned opt;
        unsigned timestamp = 0;
-       void* (*memRchr)(const void *, int, size_t) = memchr;
 
        INIT_G();
 
@@ -866,16 +1029,19 @@ int svlogd_main(int argc, char **argv)
                        &r, &replace, &l, &b, &timestamp, &verbose);
        if (opt & 1) { // -r
                repl = r[0];
-               if (!repl || r[1]) usage();
+               if (!repl || r[1])
+                       bb_show_usage();
        }
        if (opt & 2) if (!repl) repl = '_'; // -R
        if (opt & 4) { // -l
                linemax = xatou_range(l, 0, BUFSIZ-26);
-               if (linemax == 0) linemax = BUFSIZ-26;
-               if (linemax < 256) linemax = 256;
+               if (linemax == 0)
+                       linemax = BUFSIZ-26;
+               if (linemax < 256)
+                       linemax = 256;
        }
        ////if (opt & 8) { // -b
-       ////    buflen = xatoi_u(b);
+       ////    buflen = xatoi_positive(b);
        ////    if (buflen == 0) buflen = 1024;
        ////}
        //if (opt & 0x10) timestamp++; // -t
@@ -885,11 +1051,12 @@ int svlogd_main(int argc, char **argv)
        argc -= optind;
 
        dirn = argc;
-       if (dirn <= 0) usage();
-       ////if (buflen <= linemax) usage();
+       if (dirn <= 0)
+               bb_show_usage();
+       ////if (buflen <= linemax) bb_show_usage();
        fdwdir = xopen(".", O_RDONLY|O_NDELAY);
        close_on_exec_on(fdwdir);
-       dir = xzalloc(dirn * sizeof(struct logdir));
+       dir = xzalloc(dirn * sizeof(dir[0]));
        for (i = 0; i < dirn; ++i) {
                dir[i].fddir = -1;
                dir[i].fdcur = -1;
@@ -914,13 +1081,14 @@ int svlogd_main(int argc, char **argv)
        bb_signals_recursive_norestart(1 << SIGALRM, sig_alarm_handler);
        bb_signals_recursive_norestart(1 << SIGHUP, sig_hangup_handler);
 
-       logdirs_reopen();
-
        /* Without timestamps, we don't have to print each line
         * separately, so we can look for _last_ newline, not first,
-        * thus batching writes */
-       if (!timestamp)
-               memRchr = memrchr;
+        * thus batching writes. If filtering is enabled in config,
+        * logdirs_reopen resets it to memchr.
+        */
+       memRchr = (timestamp ? memchr : memrchr);
+
+       logdirs_reopen();
 
        setvbuf(stderr, NULL, _IOFBF, linelen);
 
@@ -960,7 +1128,8 @@ int svlogd_main(int argc, char **argv)
                /* Search for '\n' (in fact, np already holds the result) */
                linelen = stdin_cnt;
                if (np) {
- print_to_nl:          /* NB: starting from here lineptr may point
+ print_to_nl:
+                       /* NB: starting from here lineptr may point
                         * farther out into line[] */
                        linelen = np - lineptr + 1;
                }
@@ -988,7 +1157,8 @@ int svlogd_main(int argc, char **argv)
                }
                for (i = 0; i < dirn; ++i) {
                        struct logdir *ld = &dir[i];
-                       if (ld->fddir == -1) continue;
+                       if (ld->fddir == -1)
+                               continue;
                        if (ld->inst)
                                logmatch(ld);
                        if (ld->matcherr == 'e') {
@@ -996,7 +1166,8 @@ int svlogd_main(int argc, char **argv)
                                ////full_write(STDERR_FILENO, printptr, printlen);
                                fwrite(printptr, 1, printlen, stderr);
                        }
-                       if (ld->match != '+') continue;
+                       if (ld->match != '+')
+                               continue;
                        buffer_pwrite(i, printptr, printlen);
                }
 
@@ -1019,12 +1190,14 @@ int svlogd_main(int argc, char **argv)
                        }
                        /* linelen == no of chars incl. '\n' (or == stdin_cnt) */
                        for (i = 0; i < dirn; ++i) {
-                               if (dir[i].fddir == -1) continue;
+                               if (dir[i].fddir == -1)
+                                       continue;
                                if (dir[i].matcherr == 'e') {
                                        ////full_write(STDERR_FILENO, lineptr, linelen);
                                        fwrite(lineptr, 1, linelen, stderr);
                                }
-                               if (dir[i].match != '+') continue;
+                               if (dir[i].match != '+')
+                                       continue;
                                buffer_pwrite(i, lineptr, linelen);
                        }
                }
@@ -1040,13 +1213,13 @@ int svlogd_main(int argc, char **argv)
                        /* Move unprocessed data to the front of line */
                        memmove((timestamp ? line+26 : line), lineptr, stdin_cnt);
                }
-               fflush(NULL);////
+               fflush_all();////
        }
 
        for (i = 0; i < dirn; ++i) {
                if (dir[i].ppid)
                        while (!processorstop(&dir[i]))
-                               /* repeat */;
+                               continue;
                logdir_close(&dir[i]);
        }
        return 0;