[RFC PATCH 1/2] Support logging to UDP destinations.
Hao Xiang
hao.xiang at linux.dev
Thu Jul 18 17:49:13 UTC 2024
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.xiang at 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
More information about the busybox
mailing list