- Add -S option to specify the source address/port for UDP communication.

- Document -S option.
- Document that -h option supports AF_LOCAL.
- Split preparation of UDP sockets in logmessage() into socksetup().
This commit is contained in:
hrs 2016-12-22 23:39:11 +00:00
parent 320d6d4d09
commit 96d0f372b9
2 changed files with 202 additions and 61 deletions

View File

@ -28,7 +28,7 @@
.\" @(#)logger.1 8.1 (Berkeley) 6/6/93
.\" $FreeBSD$
.\"
.Dd March 21, 2015
.Dd December 23, 2016
.Dt LOGGER 1
.Os
.Sh NAME
@ -41,6 +41,7 @@
.Op Fl h Ar host
.Op Fl P Ar port
.Op Fl p Ar pri
.Op Fl S Ar addr Ns \&: Ns Ar port
.Op Fl t Ar tag
.Op Ar message ...
.Sh DESCRIPTION
@ -80,6 +81,28 @@ This option is ignored when a message is also specified.
Send the message to the remote system
.Ar host
instead of logging it locally.
Note that
.Nm
currently supports
.Li AF_INET
.Pq IPv4 ,
.Li AF_INET6
.Pq IPv6 ,
and
.Li AF_LOCAL
.Pq Unix-domain socket
address families.
The following address formats are valid in
.Ar host :
.Pp
.Bl -tag -width "AF_LOCAL" -compact
.It Li AF_INET
192.168.2.1
.It Li AF_INET6
2001:db8::1
.It Li AF_LOCAL
.Pa /var/run/log
.El
.It Fl P Ar port
Send the message to the specified
.Ar port
@ -101,6 +124,20 @@ level in the
.Ar local3
facility.
The default is ``user.notice.''
.It Fl S Ar addr Ns \&: Ns Ar port
Specify source address and/or source port when using
.Fl h
option.
The same address will be used for all of the remote addresses
when
.Fl A
flag is enabled.
Note that a numeric IPv6 address in
.Ar addr
must be enclosed with
.Qq \&[
and
.Qq \&] .
.It Fl t Ar tag
Mark every line in the log with the specified
.Ar tag

View File

@ -57,18 +57,22 @@ __FBSDID("$FreeBSD$");
#define SYSLOG_NAMES
#include <syslog.h>
static int decode(char *, const CODE *);
static int pencode(char *);
static void logmessage(int, const char *, const char *, const char *,
const char *);
static void usage(void);
#define sstosa(ss) ((struct sockaddr *)(void *)ss)
struct socks {
int sock;
int addrlen;
struct sockaddr_storage addr;
int sk_sock;
int sk_addrlen;
struct sockaddr_storage sk_addr;
};
static int decode(char *, const CODE *);
static int pencode(char *);
static ssize_t socksetup(const char *, const char *, const char *,
struct socks **);
static void logmessage(int, const char *, struct socks *, ssize_t,
const char *);
static void usage(void);
#ifdef INET6
static int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */
#else
@ -85,17 +89,20 @@ static int send_to_all = 0; /* send message to all IPv4/IPv6 addresses */
int
main(int argc, char *argv[])
{
struct socks *socks;
ssize_t nsock;
int ch, logflags, pri;
char *tag, *host, buf[1024];
const char *svcname;
const char *svcname, *src;
tag = NULL;
host = NULL;
svcname = "syslog";
src = NULL;
pri = LOG_USER | LOG_NOTICE;
logflags = 0;
unsetenv("TZ");
while ((ch = getopt(argc, argv, "46Af:h:iP:p:st:")) != -1)
while ((ch = getopt(argc, argv, "46Af:h:iP:p:S:st:")) != -1)
switch((char)ch) {
case '4':
family = PF_INET;
@ -128,6 +135,9 @@ main(int argc, char *argv[])
case 's': /* log to standard error */
logflags |= LOG_PERROR;
break;
case 'S': /* source address */
src = optarg;
break;
case 't': /* tag */
tag = optarg;
break;
@ -138,6 +148,16 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
if (host) {
nsock = socksetup(src, host, svcname, &socks);
if (nsock <= 0)
errx(1, "socket");
} else {
if (src)
errx(1, "-h option is missing.");
nsock = 0;
}
if (tag == NULL)
tag = getlogin();
/* setup for logging */
@ -153,11 +173,11 @@ main(int argc, char *argv[])
for (p = buf, endp = buf + sizeof(buf) - 2; *argv;) {
len = strlen(*argv);
if (p + len > endp && p > buf) {
logmessage(pri, tag, host, svcname, buf);
logmessage(pri, tag, socks, nsock, buf);
p = buf;
}
if (len > sizeof(buf) - 1)
logmessage(pri, tag, host, svcname, *argv++);
logmessage(pri, tag, socks, nsock, *argv++);
else {
if (p != buf)
*p++ = ' ';
@ -166,78 +186,162 @@ main(int argc, char *argv[])
}
}
if (p != buf)
logmessage(pri, tag, host, svcname, buf);
logmessage(pri, tag, socks, nsock, buf);
} else
while (fgets(buf, sizeof(buf), stdin) != NULL)
logmessage(pri, tag, host, svcname, buf);
logmessage(pri, tag, socks, nsock, buf);
exit(0);
}
static ssize_t
socksetup(const char *src, const char *dst, const char *svcname,
struct socks **socks)
{
struct addrinfo hints, *res, *res0;
struct sockaddr_storage *ss_src[AF_MAX];
struct socks *sk;
ssize_t nsock = 0;
int error, maxs;
memset(&ss_src[0], 0, sizeof(ss_src));
if (src) {
char *p, *p0, *hs, *hbuf, *sbuf;
hbuf = sbuf = NULL;
p0 = p = strdup(src);
if (p0 == NULL)
err(1, "strdup failed");
hs = p0; /* point to search ":" */
#ifdef INET6
/* -S option supports IPv6 addr in "[2001:db8::1]:service". */
if (*p0 == '[') {
p = strchr(p0, ']');
if (p == NULL)
errx(1, "\"]\" not found in src addr");
*p = '\0';
/* hs points just after ']' (':' or '\0'). */
hs = p + 1;
/*
* p points just after '[' while it points hs
* in the case of [].
*/
p = ((p0 + 1) == (hs - 1)) ? hs : p0 + 1;
}
#endif
if (*p != '\0') {
/* (p == hs) means ":514" or "[]:514". */
hbuf = (p == hs && *p == ':') ? NULL : p;
p = strchr(hs, ':');
if (p != NULL) {
*p = '\0';
sbuf = (*(p + 1) != '\0') ? p + 1 : NULL;
}
}
hints = (struct addrinfo){
.ai_family = family,
.ai_socktype = SOCK_DGRAM,
.ai_flags = AI_PASSIVE
};
error = getaddrinfo(hbuf, sbuf, &hints, &res0);
if (error)
errx(1, "%s: %s", gai_strerror(error), src);
for (res = res0; res; res = res->ai_next) {
switch (res->ai_family) {
case AF_INET:
#ifdef INET6
case AF_INET6:
#endif
if (ss_src[res->ai_family] != NULL)
continue;
ss_src[res->ai_family] =
malloc(sizeof(struct sockaddr_storage));
if (ss_src[res->ai_family] == NULL)
err(1, "malloc failed");
memcpy(ss_src[res->ai_family], res->ai_addr,
res->ai_addrlen);
}
}
freeaddrinfo(res0);
free(p0);
}
/* resolve hostname */
hints = (struct addrinfo){
.ai_family = family,
.ai_socktype = SOCK_DGRAM
};
error = getaddrinfo(dst, svcname, &hints, &res0);
if (error == EAI_SERVICE) {
warnx("%s/udp: unknown service", svcname);
error = getaddrinfo(dst, "514", &hints, &res);
}
if (error)
errx(1, "%s: %s", gai_strerror(error), dst);
/* count max number of sockets we may open */
maxs = 0;
for (res = res0; res; res = res->ai_next)
maxs++;
sk = calloc(maxs, sizeof(*sk));
if (sk == NULL)
errx(1, "couldn't allocate memory for sockets");
for (res = res0; res; res = res->ai_next) {
int s;
s = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (s < 0)
continue;
if (src && ss_src[res->ai_family] == NULL)
errx(1, "address family mismatch");
if (ss_src[res->ai_family]) {
error = bind(s, sstosa(ss_src[res->ai_family]),
ss_src[res->ai_family]->ss_len);
if (error < 0)
err(1, "bind");
}
sk[nsock] = (struct socks){
.sk_addrlen = res->ai_addrlen,
.sk_sock = s
};
memcpy(&sk[nsock].sk_addr, res->ai_addr, res->ai_addrlen);
nsock++;
}
freeaddrinfo(res0);
*socks = sk;
return (nsock);
}
/*
* Send the message to syslog, either on the local host, or on a remote host
*/
static void
logmessage(int pri, const char *tag, const char *host, const char *svcname,
const char *buf)
logmessage(int pri, const char *tag, struct socks *sk, ssize_t nsock,
const char *buf)
{
static struct socks *socks;
static int nsock = 0;
struct addrinfo hints, *res, *r;
char *line;
int maxs, len, sock, error, i, lsent;
int len, i, lsent;
if (host == NULL) {
if (nsock == 0) {
syslog(pri, "%s", buf);
return;
}
if (nsock <= 0) { /* set up socket stuff */
/* resolve hostname */
memset(&hints, 0, sizeof(hints));
hints.ai_family = family;
hints.ai_socktype = SOCK_DGRAM;
error = getaddrinfo(host, svcname, &hints, &res);
if (error == EAI_SERVICE) {
warnx("%s/udp: unknown service", svcname);
error = getaddrinfo(host, "514", &hints, &res);
}
if (error)
errx(1, "%s: %s", gai_strerror(error), host);
/* count max number of sockets we may open */
for (maxs = 0, r = res; r; r = r->ai_next, maxs++);
socks = malloc(maxs * sizeof(struct socks));
if (!socks)
errx(1, "couldn't allocate memory for sockets");
for (r = res; r; r = r->ai_next) {
sock = socket(r->ai_family, r->ai_socktype,
r->ai_protocol);
if (sock < 0)
continue;
memcpy(&socks[nsock].addr, r->ai_addr, r->ai_addrlen);
socks[nsock].addrlen = r->ai_addrlen;
socks[nsock++].sock = sock;
}
freeaddrinfo(res);
if (nsock <= 0)
errx(1, "socket");
}
if ((len = asprintf(&line, "<%d>%s: %s", pri, tag, buf)) == -1)
errx(1, "asprintf");
lsent = -1;
for (i = 0; i < nsock; ++i) {
lsent = sendto(socks[i].sock, line, len, 0,
(struct sockaddr *)&socks[i].addr,
socks[i].addrlen);
for (i = 0; i < nsock; i++) {
lsent = sendto(sk[i].sk_sock, line, len, 0,
sstosa(&sk[i].sk_addr), sk[i].sk_addrlen);
if (lsent == len && !send_to_all)
break;
}
if (lsent != len) {
if (lsent == -1)
warn ("sendto");
warn("sendto");
else
warnx ("sendto: short send - %d bytes", lsent);
warnx("sendto: short send - %d bytes", lsent);
}
free(line);
@ -290,7 +394,7 @@ usage(void)
{
(void)fprintf(stderr, "usage: %s\n",
"logger [-46Ais] [-f file] [-h host] [-P port] [-p pri] [-t tag]\n"
" [message ...]"
" [-S addr:port] [message ...]"
);
exit(1);
}