The original implementation of svlogd from smarden.org supports
logging to a UDP destination but it is not yet in the busybox
version. This change implements this feature. The current implementation
has a few limitations.

* Only supports UDP destination as an additional log target. Logs are
still written to files.
* While sending logs to UDP destination, only the first 1000 characters
per line are send in a UDP message. If a log line is very long, UDP
message will be truncated.

Signed-off-by: Hao Xiang <hao.xi...@linux.dev>
---
 runit/svlogd.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 197 insertions(+), 6 deletions(-)

diff --git a/runit/svlogd.c b/runit/svlogd.c
index f7576f0fa..c808dfc42 100644
--- a/runit/svlogd.c
+++ b/runit/svlogd.c
@@ -153,7 +153,7 @@ log message, you can use a pattern like this instead
 ///////:   "\n""NNUM - min number files to retain" - confusing
 ///////:   "\n""tSEC - rotate file if it get SEC seconds old" - confusing
 //usage:   "\n""!PROG - process rotated log with PROG"
-///////:   "\n""uIPADDR - send log over UDP" - unsupported
+//usage:   "\n""uIPADDR - send log over UDP"
 ///////:   "\n""UIPADDR - send log over UDP and DONT log" - unsupported
 ///////:   "\n""pPFX - prefix each line with PFX" - unsupported
 //usage:   "\n""+,-PATTERN - (de)select line for logging"
@@ -188,6 +188,9 @@ struct logdir {
        char fnsave[FMT_PTIME];
        char match;
        char matcherr;
+       struct sockaddr_in udpaddr;
+       int udpfd;
+       struct msghdr *msg;
 };
 
 
@@ -197,6 +200,7 @@ struct globals {
        int linemax;
        ////int buflen;
        int linelen;
+       int udplinelen;
 
        int fdwdir;
        char **fndir;
@@ -216,6 +220,8 @@ struct globals {
        const char *replace;
        int fl_flag_0;
        unsigned dirn;
+       char *udpline;
+       char *udplineptr;
 
        sigset_t blocked_sigset;
 };
@@ -225,6 +231,7 @@ struct globals {
 #define linemax        (G.linemax       )
 #define buflen         (G.buflen        )
 #define linelen        (G.linelen       )
+#define udplinelen     (G.udplinelen    )
 #define fndir          (G.fndir         )
 #define fdwdir         (G.fdwdir        )
 #define wstat          (G.wstat         )
@@ -240,6 +247,8 @@ struct globals {
 #define blocked_sigset (G.blocked_sigset)
 #define fl_flag_0      (G.fl_flag_0     )
 #define dirn           (G.dirn          )
+#define udpline        (G.udpline       )
+#define udplineptr     (G.udplineptr    )
 #define line bb_common_bufsiz1
 #define INIT_G() do { \
        setup_common_bufsiz(); \
@@ -256,6 +265,9 @@ struct globals {
 #define PAUSE "pausing: "
 #define INFO "info: "
 
+#define MAX_PREFIX_LEN 60
+#define MAX_IOV_LEN 2
+
 static void fatalx(const char *m0)
 {
        bb_error_msg_and_die(FATAL"%s", m0);
@@ -677,6 +689,64 @@ static int buffer_pwrite(int n, char *s, unsigned len)
        return i;
 }
 
+/* Add a buffer line to the UDP message's IOV. */
+static void udp_add_buf(struct msghdr *msg, char *buf, int len)
+{
+       if (msg->msg_iovlen == MAX_IOV_LEN)
+               return;
+
+       msg->msg_iov[msg->msg_iovlen].iov_base = buf;
+       msg->msg_iov[msg->msg_iovlen].iov_len = len;
+       msg->msg_iovlen++;
+}
+
+/* Copy the message payload into a dedicated buffer */
+/* for UDP message. The memory copy always appends  */
+/* to the end of the buffer until the it's full     */
+static void udp_append_line(char *buf, int len)
+{
+       if (!udplinelen)
+               return;
+
+       if (udplinelen < len) {
+               len = udplinelen;
+       }
+       memcpy(udplineptr, buf, len);
+       udplineptr += len;
+       udplinelen -= len;
+}
+
+static size_t get_msg_len(struct msghdr *msg)
+{
+       size_t len = 0;
+       for (int i = 0; i < msg->msg_iovlen; i++) {
+               len += msg->msg_iov[i].iov_len;
+       }
+       return len;
+}
+
+/* Send the UDP message after IOV is set */
+static void udp_write(int n)
+{
+       struct logdir *ld = &dir[n];
+       char *message;
+       size_t totallen = 0;
+
+       const char *udp_failure = "warning: failure sending through udp: ";
+       const char *alloc_failure = "warning: failure allocating string";
+
+       totallen = get_msg_len(ld->msg);
+
+       if (sendmsg(ld->udpfd, ld->msg, 0) != totallen) {
+               if (asprintf(&message, "%s", udp_failure) != -1) {
+                       buffer_pwrite(n, message, strlen(message));
+                       free(message);
+               } else {
+                       warn(alloc_failure);
+               }
+       }
+}
+
 static void logdir_close(struct logdir *ld)
 {
        if (ld->fddir == -1)
@@ -700,16 +770,40 @@ static void logdir_close(struct logdir *ld)
        ld->fdlock = -1;
        free(ld->processor);
        ld->processor = NULL;
+       if (ld->udpfd != -1)
+               close(ld->udpfd);
+       ld->udpfd = -1;
+       if (ld->msg != NULL)
+               free(ld->msg);
+       ld->msg = NULL;
+}
+
+static struct msghdr *create_msg(struct sockaddr_in *udpaddr)
+{
+       struct msghdr *msg;
+       msg = xzalloc(sizeof(struct msghdr) + sizeof(struct iovec) * 
MAX_IOV_LEN);
+       msg->msg_iov = (struct iovec *)(msg + sizeof(struct msghdr));
+       msg->msg_name = udpaddr;
+       msg->msg_namelen = sizeof(*udpaddr);
+
+       return msg;
 }
 
 static NOINLINE unsigned logdir_open(struct logdir *ld, const char *fn)
 {
-       char buf[128];
+       char buf[256];
        unsigned now;
        char *new, *s, *np;
        int i;
        struct stat st;
 
+       char *start;
+       char cp[32];
+       int max_len = sizeof(cp) - 1;
+       int port;
+       char *sp;
+       int len;
+
        now = monotonic_sec();
 
        ld->fddir = open(fn, O_RDONLY|O_NDELAY);
@@ -742,6 +836,10 @@ static NOINLINE unsigned logdir_open(struct logdir *ld, 
const char *fn)
        ld->name = (char*)fn;
        ld->ppid = 0;
        ld->match = '+';
+       ld->udpfd = -1;
+       ld->udpaddr.sin_family = AF_INET;
+       ld->udpaddr.sin_port = 0;
+       ld->msg = NULL;
        free(ld->inst); ld->inst = NULL;
        free(ld->processor); ld->processor = NULL;
 
@@ -809,6 +907,48 @@ static NOINLINE unsigned logdir_open(struct logdir *ld, 
const char *fn)
                                        ld->processor = wstrdup(&s[1]);
                                }
                                break;
+                       case 'u':
+                               start = &s[1];
+                               if (*start == '\0') {
+                                       warn("No ip address string specified");
+                                       break;
+                               }
+                               sp = strchr(start, ':');
+                               if (sp) {
+                                       port = atoi(sp + 1);
+                                       if (port == 0) {
+                                               warn2("Can't convert string to 
port", sp + 1);
+                                               break;
+                                       }
+                                       len = sp - start;
+                               } else {
+                                       /* Default port 514 */
+                                       port = 514;
+                                       len = strlen(start);
+                               }
+                               if (len > max_len)
+                                       len = max_len;
+                               strncpy(cp, start, len);
+                               cp[len] = '\0';
+                               if (!inet_aton(cp, &ld->udpaddr.sin_addr)) {
+                                       warn2("Can't convert string to ip 
address", cp);
+                                       break;
+                               }
+                               ld->udpaddr.sin_port = port;
+                               ld->udpfd = socket(AF_INET, SOCK_DGRAM, 0);
+                               if (ld->udpfd == -1) {
+                                       warn("Can't open UDP socket");
+                                       break;
+                               }
+                               ld->msg = create_msg(&ld->udpaddr);
+                               if (!udpline) {
+                                       udpline = xzalloc(COMMON_BUFSIZE);
+                               }
+                               /* UDP requires one-line buffering,
+                                * resetting the "find newline" function
+                                * accordingly */
+                               memRchr = memchr;
+                               break;
                        }
                        s = np;
                }
@@ -879,6 +1019,13 @@ static void logdirs_reopen(void)
        int ok = 0;
 
        tmaxflag = 0;
+
+       if (udpline) {
+               free(udpline);
+               udpline = NULL;
+               udplinelen = 0;
+       }
+
        for (l = 0; l < dirn; ++l) {
                logdir_close(&dir[l]);
                if (logdir_open(&dir[l], fndir[l]))
@@ -1047,6 +1194,23 @@ static void logmatch(struct logdir *ld, char* lineptr, 
int lineptr_len)
        }
 }
 
+static char *get_lineptr(unsigned timestamp)
+{
+       char *lineptr = line;
+       if (timestamp)
+               lineptr += 26;
+       return lineptr;
+}
+
+static bool dir_log_match(void)
+{
+       for (int i = 0; i < dirn; ++i) {
+               if (dir[i].match == '+')
+                       return TRUE;
+       }
+       return FALSE;
+}
+
 int svlogd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int svlogd_main(int argc, char **argv)
 {
@@ -1055,6 +1219,7 @@ int svlogd_main(int argc, char **argv)
        int i;
        unsigned opt;
        unsigned timestamp = 0;
+       bool match;
 
        INIT_G();
 
@@ -1097,6 +1262,7 @@ int svlogd_main(int argc, char **argv)
                dir[i].fdcur = -1;
                ////dir[i].btmp = xmalloc(buflen);
                /*dir[i].ppid = 0;*/
+               dir[i].udpfd = -1;
        }
        /* line = xmalloc(linemax + (timestamp ? 26 : 0)); */
        fndir = argv;
@@ -1136,9 +1302,7 @@ int svlogd_main(int argc, char **argv)
                int printlen;
                char ch;
 
-               lineptr = line;
-               if (timestamp)
-                       lineptr += 26;
+               lineptr = get_lineptr(timestamp);
 
                /* lineptr[0..linemax-1] - buffer for stdin */
                /* (possibly has some unprocessed data from prev loop) */
@@ -1192,6 +1356,8 @@ int svlogd_main(int argc, char **argv)
                }
                for (i = 0; i < dirn; ++i) {
                        struct logdir *ld = &dir[i];
+                       if (ld->msg)
+                               ld->msg->msg_iovlen = 0;
                        if (ld->fddir == -1)
                                continue;
                        if (ld->inst)
@@ -1206,6 +1372,18 @@ int svlogd_main(int argc, char **argv)
                        buffer_pwrite(i, printptr, printlen);
                }
 
+               /* Reset the UDP message buffer */
+               if (udpline) {
+                       udplineptr = udpline;
+                       udplinelen = COMMON_BUFSIZE;
+               }
+
+               match = dir_log_match();
+
+               /* Copy print buffer to UDP message */
+               if (udpline && match)
+                       udp_append_line(printptr, printlen);
+
                /* If we didn't see '\n' (long input line), */
                /* read/write repeatedly until we see it */
                while (ch != '\n') {
@@ -1235,6 +1413,19 @@ int svlogd_main(int argc, char **argv)
                                        continue;
                                buffer_pwrite(i, lineptr, linelen);
                        }
+                       /* Copy print buffer to UDP message */
+                       if (udpline && match)
+                               udp_append_line(lineptr, linelen);
+               }
+
+               for (i = 0; i < dirn; i++) {
+                       if (dir[i].match != '+')
+                               continue;
+                       if (dir[i].udpfd != -1) {
+                               /* Add log payload to UDP message */
+                               udp_add_buf(dir[i].msg, udpline, udplineptr - 
udpline);
+                               udp_write(i);
+                       }
                }
 
                stdin_cnt -= linelen;
@@ -1246,7 +1437,7 @@ int svlogd_main(int argc, char **argv)
                        if (np)
                                goto print_to_nl;
                        /* Move unprocessed data to the front of line */
-                       memmove((timestamp ? line+26 : line), lineptr, 
stdin_cnt);
+                       memmove(get_lineptr(timestamp), lineptr, stdin_cnt);
                }
                fflush_all();////
        }
-- 
2.45.2

_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to