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 <troglobit@gmail.com>
This commit is contained in:
Joachim Wiberg 2022-07-29 14:53:23 +02:00
parent 1044669b8d
commit dfb654688a
3 changed files with 160 additions and 15 deletions

View File

@ -35,11 +35,14 @@
#include <err.h> #include <err.h>
#include <errno.h> #include <errno.h>
#include <getopt.h> #include <getopt.h>
#include <netdb.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#define SYSLOG_NAMES #define SYSLOG_NAMES
#include "compat.h" #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); 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 <arpa/inet.h>
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) static int checksz(FILE *fp, off_t sz)
{ {
struct stat st; struct stat st;
@ -220,21 +294,32 @@ static int usage(int code)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
FILE *fp = NULL;
int c, num = 5;
int facility = LOG_USER; int facility = LOG_USER;
int severity = LOG_INFO; int severity = LOG_INFO;
int family = AF_UNSPEC;
FILE *fp = NULL;
int c, num = 5;
int log_opts = 0; int log_opts = 0;
int rotate = 0; int rotate = 0;
int allow_kmsg = 0; int allow_kmsg = 0;
off_t size = 200 * 1024; off_t size = 200 * 1024;
char *ident = NULL, *logfile = NULL; char *ident = NULL, *logfile = NULL;
char *msgid = NULL, *sd = NULL; char *msgid = NULL, *sd = NULL;
char *sockpath = NULL; char *host = NULL, *sockpath = NULL;
char *svcname = "syslog";
struct sockaddr sa;
char buf[512] = ""; 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) { switch (c) {
case '4':
family = AF_INET;
break;
case '6':
family = AF_INET6;
break;
case 'c': case 'c':
log_opts |= LOG_CONS; log_opts |= LOG_CONS;
break; break;
@ -247,6 +332,10 @@ int main(int argc, char *argv[])
logfile = optarg; logfile = optarg;
break; break;
case 'h':
host = optarg;
break;
case 'i': case 'i':
log_opts |= LOG_PID; log_opts |= LOG_PID;
break; break;
@ -272,6 +361,10 @@ int main(int argc, char *argv[])
return usage(1); return usage(1);
break; break;
case 'P':
svcname = optarg;
break;
case 'r': case 'r':
parse_rotation(optarg, &size, &num); parse_rotation(optarg, &size, &num);
if (size > 0 && num > 0) if (size > 0 && num > 0)
@ -351,6 +444,11 @@ int main(int argc, char *argv[])
return fclose(fp); 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); openlog_r(ident, log_opts, facility, &log);

View File

@ -96,6 +96,20 @@ is_socket(int fd)
return 1; 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 -- * syslog, vsyslog --
* print message on log file; output is intended for syslogd(8). * 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 BRCOSP[] = "]: ";
static const char CRLF[] = "\r\n"; static const char CRLF[] = "\r\n";
struct sockaddr *sa = NULL;
socklen_t len = 0;
size_t cnt, prlen, tries; size_t cnt, prlen, tries;
char ch, *p, *t; char ch, *p, *t;
struct timeval tv; struct timeval tv;
@ -425,8 +441,17 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
goto done; 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 * 1) syslogd was restarted
* 2) /dev/log is out of socket buffer space * 2) /dev/log is out of socket buffer space
* We attempt to reconnect to /dev/log to take care of * 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. * to give syslogd a chance to empty its socket buffer.
*/ */
for (tries = 0; tries < MAXTRIES; tries++) { 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; break;
if (errno != ENOBUFS) { if (errno != ENOBUFS) {
disconnectlog_r(data); disconnectlog_r(data);
@ -487,7 +512,7 @@ disconnectlog_r(struct syslog_data *data)
static void static void
connectlog_r(struct syslog_data *data) connectlog_r(struct syslog_data *data)
{ {
/* AF_UNIX address of local logger */ struct sockaddr *sa = data->log_host;
static struct sockaddr_un sun = { static struct sockaddr_un sun = {
.sun_family = AF_LOCAL, .sun_family = AF_LOCAL,
#ifdef HAVE_SA_LEN #ifdef HAVE_SA_LEN
@ -495,28 +520,48 @@ connectlog_r(struct syslog_data *data)
#endif #endif
.sun_path = _PATH_LOG, .sun_path = _PATH_LOG,
}; };
socklen_t len;
int family;
char *path; char *path;
path = getenv("SYSLOG_UNIX_PATH"); if (sa) {
if (!data->log_sockpath && path) family = sa->sa_family;
data->log_sockpath = path; #ifdef HAVE_SA_LEN
if (data->log_sockpath && !access(data->log_sockpath, W_OK)) len = sa->sa_len;
strlcpy(sun.sun_path, data->log_sockpath, sizeof(sun.sun_path)); #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) { 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) if (data->log_file == -1)
return; return;
data->log_connected = 0; data->log_connected = 0;
} }
if (!data->log_connected) { if (!data->log_connected) {
if (!is_socket(data->log_file)) { if (!is_socket(data->log_file)) {
data->log_connected = 1; data->log_connected = 1;
return; return;
} }
if (connect(data->log_file, (const struct sockaddr *)&sun, if (connect(data->log_file, sa, len) == -1) {
sizeof(sun)) == -1) {
(void)close(data->log_file); (void)close(data->log_file);
data->log_file = -1; data->log_file = -1;
} else } else

View File

@ -206,6 +206,7 @@ struct syslog_data {
char log_hostname[256]; /* MAXHOSTNAMELEN */ char log_hostname[256]; /* MAXHOSTNAMELEN */
int log_fac; int log_fac;
int log_mask; int log_mask;
void *log_host; /* struct sockaddr* */
}; };
#define SYSLOG_DATA_INIT { \ #define SYSLOG_DATA_INIT { \
@ -219,6 +220,7 @@ struct syslog_data {
.log_hostname = { '\0' }, \ .log_hostname = { '\0' }, \
.log_fac = LOG_USER, \ .log_fac = LOG_USER, \
.log_mask = 0xff, \ .log_mask = 0xff, \
.log_host = NULL, \
} }
#ifdef __cplusplus #ifdef __cplusplus