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