From dfb654688ac6cb753b916e315cfed22f0792f878 Mon Sep 17 00:00:00 2001 From: Joachim Wiberg Date: Fri, 29 Jul 2022 14:53:23 +0200 Subject: [PATCH] logger: initial support for logging to a remote host This is the first RFC5424 (only) support for for logging to a remote host. The syntax continues to follow the FreeBSD logger. Signed-off-by: Joachim Wiberg --- src/logger.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/syslog.c | 67 ++++++++++++++++++++++++++------ src/syslog.h | 2 + 3 files changed, 160 insertions(+), 15 deletions(-) diff --git a/src/logger.c b/src/logger.c index 5e0b449..6ddb612 100644 --- a/src/logger.c +++ b/src/logger.c @@ -35,11 +35,14 @@ #include #include #include +#include #include #include #include #include #include +#include +#include #define SYSLOG_NAMES #include "compat.h" @@ -122,6 +125,77 @@ static void log_kmsg(FILE *fp, char *ident, int pri, int opts, char *buf) fprintf(fp, "<%d>%s[%d]:%s\n", pri, ident, getpid(), buf); } +/* + * Used on systems that don't have sa->sa_len + */ +#ifndef HAVE_SA_LEN +static socklen_t sa_len(struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET6) + return sizeof(struct sockaddr_in6); + if (sa->sa_family == AF_INET) + return sizeof(struct sockaddr_in); + return 0; +} +#endif +#include +static void print_addr(struct sockaddr *sa) +{ + struct sockaddr_in6 *sin6; + struct sockaddr_in *sin; + socklen_t len; + void *address; + char buf[128]; + + if (sa->sa_family == AF_INET6) { + sin6 = (struct sockaddr_in6 *)sa; + address = &sin6->sin6_addr; + len = sizeof(*sin6); + } else { + sin = (struct sockaddr_in *)sa; + address = &sin->sin_addr; + len = sizeof(*sin); + } + + printf("address %s len %u vs calculated %u\n", + inet_ntop(sa->sa_family, address, buf, sizeof(buf)), + len, sa_len(sa)); +} + +static int nslookup(const char *host, const char *svcname, int family, struct sockaddr *sa) +{ + struct addrinfo hints, *ai, *result; + int error; + + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = !host ? AI_PASSIVE : 0; + hints.ai_family = family; + hints.ai_socktype = SOCK_DGRAM; + + error = getaddrinfo(host, svcname, &hints, &result); + if (error == EAI_SERVICE) { + warnx("%s/udp: unknown service, trying syslog port 514", svcname); + svcname = "514"; + error = getaddrinfo(host, svcname, &hints, &result); + } + if (error) { + warnx("%s (%s:%s)", gai_strerror(error), host, svcname); + return 1; + } + + for (ai = result; ai; ai = ai->ai_next) { + print_addr(ai->ai_addr); + if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) + continue; + + memcpy(sa, ai->ai_addr, ai->ai_addrlen); + break; + } + freeaddrinfo(result); + + return 0; +} + static int checksz(FILE *fp, off_t sz) { struct stat st; @@ -220,21 +294,32 @@ static int usage(int code) int main(int argc, char *argv[]) { - FILE *fp = NULL; - int c, num = 5; int facility = LOG_USER; int severity = LOG_INFO; + int family = AF_UNSPEC; + FILE *fp = NULL; + int c, num = 5; int log_opts = 0; int rotate = 0; int allow_kmsg = 0; off_t size = 200 * 1024; char *ident = NULL, *logfile = NULL; char *msgid = NULL, *sd = NULL; - char *sockpath = NULL; + char *host = NULL, *sockpath = NULL; + char *svcname = "syslog"; + struct sockaddr sa; char buf[512] = ""; - while ((c = getopt(argc, argv, "?cd:f:ikm:np:r:st:u:v")) != EOF) { + while ((c = getopt(argc, argv, "46?cd:f:h:ikm:np:P:r:st:u:v")) != EOF) { switch (c) { + case '4': + family = AF_INET; + break; + + case '6': + family = AF_INET6; + break; + case 'c': log_opts |= LOG_CONS; break; @@ -247,6 +332,10 @@ int main(int argc, char *argv[]) logfile = optarg; break; + case 'h': + host = optarg; + break; + case 'i': log_opts |= LOG_PID; break; @@ -272,6 +361,10 @@ int main(int argc, char *argv[]) return usage(1); break; + case 'P': + svcname = optarg; + break; + case 'r': parse_rotation(optarg, &size, &num); if (size > 0 && num > 0) @@ -351,6 +444,11 @@ int main(int argc, char *argv[]) return fclose(fp); } + } else if (host) { + log.log_host = &sa; + if (nslookup(host, svcname, family, &sa)) + return 1; + log_opts |= LOG_NDELAY; } openlog_r(ident, log_opts, facility, &log); diff --git a/src/syslog.c b/src/syslog.c index 95f71e0..35bacb7 100644 --- a/src/syslog.c +++ b/src/syslog.c @@ -96,6 +96,20 @@ is_socket(int fd) return 1; } +/* + * Used on systems that don't have sa->sa_len + */ +#ifndef HAVE_SA_LEN +static socklen_t sa_len(struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET6) + return sizeof(struct sockaddr_in6); + if (sa->sa_family == AF_INET) + return sizeof(struct sockaddr_in); + return 0; +} +#endif + /* * syslog, vsyslog -- * print message on log file; output is intended for syslogd(8). @@ -190,6 +204,8 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid, { static const char BRCOSP[] = "]: "; static const char CRLF[] = "\r\n"; + struct sockaddr *sa = NULL; + socklen_t len = 0; size_t cnt, prlen, tries; char ch, *p, *t; struct timeval tv; @@ -425,8 +441,17 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid, goto done; } + if (data->log_host) { + sa = data->log_host; +#ifdef HAVE_SA_LEN + len = sa->sa_len; +#else + len = sa_len(sa); +#endif + } + /* - * If the send() failed, there are two likely scenarios: + * If the send() fails, there are two likely scenarios: * 1) syslogd was restarted * 2) /dev/log is out of socket buffer space * We attempt to reconnect to /dev/log to take care of @@ -434,7 +459,7 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid, * to give syslogd a chance to empty its socket buffer. */ for (tries = 0; tries < MAXTRIES; tries++) { - if (send(data->log_file, tbuf, cnt, 0) != -1) + if (sendto(data->log_file, tbuf, cnt, 0, sa, len) != -1) break; if (errno != ENOBUFS) { disconnectlog_r(data); @@ -487,7 +512,7 @@ disconnectlog_r(struct syslog_data *data) static void connectlog_r(struct syslog_data *data) { - /* AF_UNIX address of local logger */ + struct sockaddr *sa = data->log_host; static struct sockaddr_un sun = { .sun_family = AF_LOCAL, #ifdef HAVE_SA_LEN @@ -495,28 +520,48 @@ connectlog_r(struct syslog_data *data) #endif .sun_path = _PATH_LOG, }; + socklen_t len; + int family; char *path; - path = getenv("SYSLOG_UNIX_PATH"); - if (!data->log_sockpath && path) - data->log_sockpath = path; - if (data->log_sockpath && !access(data->log_sockpath, W_OK)) - strlcpy(sun.sun_path, data->log_sockpath, sizeof(sun.sun_path)); + if (sa) { + family = sa->sa_family; +#ifdef HAVE_SA_LEN + len = sa->sa_len; +#else + len = sa_len(sa); +#endif + } else { + sa = (struct sockaddr *)&sun; + family = AF_UNIX; +#ifdef HAVE_SA_LEN + len = sa->sa_len; +#else + len = sizeof(sun); +#endif + + path = getenv("SYSLOG_UNIX_PATH"); + if (!data->log_sockpath && path) + data->log_sockpath = path; + + if (data->log_sockpath && !access(data->log_sockpath, W_OK)) + strlcpy(sun.sun_path, data->log_sockpath, sizeof(sun.sun_path)); + } if (data->log_file == -1 || fcntl(data->log_file, F_GETFL, 0) == -1) { - data->log_file = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0); + data->log_file = socket(family, SOCK_DGRAM | SOCK_CLOEXEC, 0); if (data->log_file == -1) return; data->log_connected = 0; } + if (!data->log_connected) { if (!is_socket(data->log_file)) { data->log_connected = 1; return; } - if (connect(data->log_file, (const struct sockaddr *)&sun, - sizeof(sun)) == -1) { + if (connect(data->log_file, sa, len) == -1) { (void)close(data->log_file); data->log_file = -1; } else diff --git a/src/syslog.h b/src/syslog.h index ff19b12..56db6ef 100644 --- a/src/syslog.h +++ b/src/syslog.h @@ -206,6 +206,7 @@ struct syslog_data { char log_hostname[256]; /* MAXHOSTNAMELEN */ int log_fac; int log_mask; + void *log_host; /* struct sockaddr* */ }; #define SYSLOG_DATA_INIT { \ @@ -219,6 +220,7 @@ struct syslog_data { .log_hostname = { '\0' }, \ .log_fac = LOG_USER, \ .log_mask = 0xff, \ + .log_host = NULL, \ } #ifdef __cplusplus