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 <errno.h>
#include <getopt.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#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 <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)
{
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);

View File

@ -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

View File

@ -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