Merge branch 'master' into dev

This commit is contained in:
Joachim Wiberg 2022-07-31 12:29:29 +02:00
commit f0b7b6fdf8
5 changed files with 263 additions and 36 deletions

View File

@ -33,11 +33,15 @@
.Nd Send messages to system log, or a log file .Nd Send messages to system log, or a log file
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
.Op Fl chiknsv .Op Fl 46bchiknsv
.Op Fl d Ar SD .Op Fl d Ar SD
.Op Fl f Ar FILE .Op Fl f Ar FILE
.Op Fl h Ar HOST
.Op Fl H Ar HOSTNAME
.Op Fl I Ar PID
.Op Fl m Ar MSGID .Op Fl m Ar MSGID
.Op Fl p Ar PRIO .Op Fl p Ar PRIO
.Op Fl P Ar PORT
.Op Fl r Ar SIZE:NUM .Op Fl r Ar SIZE:NUM
.Op Fl t Ar TAG .Op Fl t Ar TAG
.Op Fl u Ar SOCK .Op Fl u Ar SOCK
@ -61,6 +65,16 @@ reads input from
.Sh OPTIONS .Sh OPTIONS
This program follows the usual UNIX command line syntax: This program follows the usual UNIX command line syntax:
.Bl -tag -width Ds .Bl -tag -width Ds
.It Fl 4
Force
.Nm
to use IPv4 addresses only.
.It Fl 6
Force
.Nm
to use IPv6 addresses only.
.It Fl b
Use RFC3164 (BSD) style format, default: RFC5424.
.It Fl c .It Fl c
Log to console Log to console
.Ql ( LOG_CONS ) .Ql ( LOG_CONS )
@ -83,6 +97,25 @@ accepts
.Fl f- .Fl f-
as an alias for as an alias for
.Ar stdout . .Ar stdout .
.It Fl H Ar hostname
Set the hostname in the header of the message to specified value.
If not specified, host part of
.Xr gethostname 3
will be used.
.It Fl h Ar host
Send the message to the remote system
.Ar host
instead of logging it locally.
.It Fl I Ar PID
Like
.Fl i ,
but uses
.Ar PID .
Useful when logging from shell scripts that send multiple messages.
E.g., the following arguments might be a useful template:
.Bd -literal -offset indent
logger -t $(basename $0) -I $$
.Ed
.It Fl i .It Fl i
Log the process id of the logger process with each line Log the process id of the logger process with each line
.Ql ( LOG_PID ) . .Ql ( LOG_PID ) .
@ -112,6 +145,17 @@ or sending remote in correctly formatted RFC5424 style.
.It Fl n .It Fl n
Open log file immediately Open log file immediately
.Ql ( LOG_NDELAY ) . .Ql ( LOG_NDELAY ) .
.It Fl P Ar port
Send the message to the specified
.Ar port
number on a remote system,
which can be specified as a service name
or as a decimal number.
The default is
.Dq Li syslog .
If an unknown service name is used,
.Nm
prints a warning and falls back to port 514.
.It Fl p Ar PRIO .It Fl p Ar PRIO
Priority, numeric or Priority, numeric or
.Ar facility.severity .Ar facility.severity

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"
@ -61,8 +64,8 @@ static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
*/ */
static int logrotate(char *file, int num, off_t sz) static int logrotate(char *file, int num, off_t sz)
{ {
int cnt;
struct stat st; struct stat st;
int cnt;
if (stat(file, &st)) if (stat(file, &st))
return 1; return 1;
@ -122,6 +125,39 @@ 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);
} }
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) {
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;
@ -193,16 +229,23 @@ static int usage(int code)
"\n" "\n"
"Write MESSAGE (or line-by-line stdin) to syslog, or file (with logrotate).\n" "Write MESSAGE (or line-by-line stdin) to syslog, or file (with logrotate).\n"
"\n" "\n"
" -4 Prefer IPv4 address when sending remote, see -h\n"
" -6 Prefer IPv6 address when sending remote, see -h\n"
" -b Use RFC3164 (BSD) style format, default: RFC5424\n"
" -c Log to console (LOG_CONS) on failure\n" " -c Log to console (LOG_CONS) on failure\n"
" -d SD Log SD as RFC5424 style 'structured data' in message\n" " -d SD Log SD as RFC5424 style 'structured data' in message\n"
" -f FILE Log file to write messages to, instead of syslog daemon\n" " -f FILE Log file to write messages to, instead of syslog daemon\n"
" -h HOST Send (UDP) message to this remote syslog server (IP or DNS name)\n"
" -H NAME Use NAME instead of system hostname in message header\n"
" -i Log process ID of the logger process with each line (LOG_PID)\n" " -i Log process ID of the logger process with each line (LOG_PID)\n"
" -I PID Log process ID using PID, recommed using PID $$ for shell scripts\n"
#ifdef __linux__ #ifdef __linux__
" -k Log to kernel /dev/kmsg if /dev/log doesn't exist yet\n" " -k Log to kernel /dev/kmsg if /dev/log doesn't exist yet\n"
#endif #endif
" -m MSGID Log message using this RFC5424 style MSGID\n" " -m MSGID Log message using this RFC5424 style MSGID\n"
" -n Open log file immediately (LOG_NDELAY)\n" " -n Open log file immediately (LOG_NDELAY)\n"
" -p PRIO Log message priority (numeric or facility.severity pair)\n" " -p PRIO Log message priority (numeric or facility.severity pair)\n"
" -P PORT Use PORT (or named UDP service) for remote server, default: syslog\n"
" -r S[:R] Enable log file rotation, default: 200 kB \e[4ms\e[0mize, 5 \e[4mr\e[0motations\n" " -r S[:R] Enable log file rotation, default: 200 kB \e[4ms\e[0mize, 5 \e[4mr\e[0motations\n"
" -s Log to stderr as well as the system log\n" " -s Log to stderr as well as the system log\n"
" -t TAG Log using the specified tag (defaults to user name)\n" " -t TAG Log using the specified tag (defaults to user name)\n"
@ -220,21 +263,36 @@ static int usage(int code)
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
FILE *fp = NULL; char *ident = NULL, *logfile = NULL;
int c, num = 5; char *host = NULL, *sockpath = NULL;
char *msgid = NULL, *sd = NULL;
char *svcname = "syslog";
off_t size = 200 * 1024;
int facility = LOG_USER; int facility = LOG_USER;
int severity = LOG_INFO; int severity = LOG_INFO;
int log_opts = 0; int family = AF_UNSPEC;
int rotate = 0; struct sockaddr sa;
int allow_kmsg = 0; int allow_kmsg = 0;
off_t size = 200 * 1024;
char *ident = NULL, *logfile = NULL;
char *msgid = NULL, *sd = NULL;
char *sockpath = NULL;
char buf[512] = ""; char buf[512] = "";
int log_opts = 0;
FILE *fp = NULL;
int c, num = 5;
int rotate = 0;
while ((c = getopt(argc, argv, "?cd:f:ikm:np:r:st:u:v")) != EOF) { while ((c = getopt(argc, argv, "46?bcd:f:h:H:iI:km:np:P:r:st:u:v")) != EOF) {
switch (c) { switch (c) {
case '4':
family = AF_INET;
break;
case '6':
family = AF_INET6;
break;
case 'b':
log_opts |= LOG_RFC3154;
break;
case 'c': case 'c':
log_opts |= LOG_CONS; log_opts |= LOG_CONS;
break; break;
@ -247,10 +305,23 @@ int main(int argc, char *argv[])
logfile = optarg; logfile = optarg;
break; break;
case 'h':
host = optarg;
break;
case 'H':
strlcpy(log.log_hostname, optarg, sizeof(log.log_hostname));
break;
case 'i': case 'i':
log_opts |= LOG_PID; log_opts |= LOG_PID;
break; break;
case 'I':
log_opts |= LOG_PID;
log.log_pid = atoi(optarg);
break;
case 'k': case 'k':
#ifdef __linux__ #ifdef __linux__
allow_kmsg = 1; allow_kmsg = 1;
@ -272,6 +343,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 +426,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;
@ -225,6 +241,17 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
if ((pri & LOG_FACMASK) == 0) if ((pri & LOG_FACMASK) == 0)
pri |= data->log_fac; pri |= data->log_fac;
/* Get system time, wallclock, fall back to UNIX time */
if (gettimeofday(&tv, NULL) == -1) {
tv.tv_sec = time(NULL);
tv.tv_usec = 0;
}
/* strftime() implies tzset(), localtime_r() doesn't. */
tzset();
now = (time_t) tv.tv_sec;
localtime_r(&now, &tmnow);
/* Build the message. */ /* Build the message. */
p = tbuf; p = tbuf;
tbuf_left = TBUF_LEN; tbuf_left = TBUF_LEN;
@ -237,23 +264,59 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
tbuf_left -= prlen; \ tbuf_left -= prlen; \
} while (/*CONSTCOND*/0) } while (/*CONSTCOND*/0)
/* Default log format is RFC5424, continues below BSD format */
if (data->log_stat & LOG_RFC3154) {
if (!(data->log_stat & LOG_NLOG)) {
prlen = snprintf(p, tbuf_left, "<%d>", pri);
DEC();
} else
prlen = 0;
prlen = strftime(dbuf, sizeof(dbuf), "%b %d %T ", &tmnow);
if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG)) {
iov[iovcnt].iov_base = dbuf;
iov[iovcnt].iov_len = strlen(dbuf);
iovcnt++;
}
if (data->log_host) {
memcpy(p, dbuf, prlen);
DEC();
}
if (data->log_hostname[0] == '\0' && gethostname(data->log_hostname,
sizeof(data->log_hostname)) == -1) {
/* can this really happen? */
data->log_hostname[0] = '-';
data->log_hostname[1] = '\0';
}
prlen = snprintf(p, tbuf_left, "%s ", data->log_hostname);
DEC();
if (data->log_tag == NULL)
data->log_tag = getprogname();
prlen = snprintf(p, tbuf_left, "%s", data->log_tag);
DEC();
if (data->log_stat & LOG_PID) {
if (data->log_pid == -1)
data->log_pid = getpid();
prlen = snprintf(p, tbuf_left, "[%d]", data->log_pid);
DEC();
}
strlcat(p, ":", tbuf_left);
prlen = 1;
DEC();
goto output;
}
if (!(data->log_stat & LOG_NLOG)) { if (!(data->log_stat & LOG_NLOG)) {
prlen = snprintf(p, tbuf_left, "<%d>1 ", pri); prlen = snprintf(p, tbuf_left, "<%d>1 ", pri);
DEC(); DEC();
} else } else
prlen = 0; prlen = 0;
if (gettimeofday(&tv, NULL) == -1) {
tv.tv_sec = time(NULL);
tv.tv_usec = 0;
}
{ {
/* strftime() implies tzset(), localtime_r() doesn't. */
tzset();
now = (time_t) tv.tv_sec;
localtime_r(&now, &tmnow);
prlen = strftime(p, tbuf_left, "%FT%T", &tmnow); prlen = strftime(p, tbuf_left, "%FT%T", &tmnow);
DEC(); DEC();
prlen = snprintf(p, tbuf_left, ".%06ld", (long)tv.tv_usec); prlen = snprintf(p, tbuf_left, ".%06ld", (long)tv.tv_usec);
@ -307,7 +370,9 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
DEC(); DEC();
if (data->log_stat & LOG_PID) { if (data->log_stat & LOG_PID) {
prlen = snprintf(p, tbuf_left, "%d ", getpid()); if (data->log_pid == -1)
data->log_pid = getpid();
prlen = snprintf(p, tbuf_left, "%d ", data->log_pid);
if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG)) { if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG)) {
iov[iovcnt].iov_base = __UNCONST("["); iov[iovcnt].iov_base = __UNCONST("[");
iov[iovcnt].iov_len = 1; iov[iovcnt].iov_len = 1;
@ -343,6 +408,7 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
} else } else
strlcat(fmt_cat, "-", FMT_LEN); strlcat(fmt_cat, "-", FMT_LEN);
output:
if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG)) if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG))
msgsdlen = strlen(fmt_cat) + 1; msgsdlen = strlen(fmt_cat) + 1;
else else
@ -425,8 +491,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 +509,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 +562,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 +570,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
@ -534,10 +629,12 @@ openlog_unlocked_r(const char *ident, int logstat, int logfac,
if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0) if (logfac != 0 && (logfac &~ LOG_FACMASK) == 0)
data->log_fac = logfac; data->log_fac = logfac;
if (data->log_stat & LOG_NDELAY) /* open immediately */ if (data->log_stat & LOG_NDELAY) { /* open immediately */
connectlog_r(data); connectlog_r(data);
if (data->log_connected)
data->log_opened = 1; data->log_opened = 1;
} else
data->log_opened = 1;
} }
void void

View File

@ -190,6 +190,7 @@ CODE facilitynames[] = {
#define LOG_PTRIM 0x040 /* trim tag and pid from messages to stderr */ #define LOG_PTRIM 0x040 /* trim tag and pid from messages to stderr */
#define LOG_NLOG 0x080 /* don't write to the system log */ #define LOG_NLOG 0x080 /* don't write to the system log */
#define LOG_STDOUT 0x100 /* like nlog, for debugging syslogp() API */ #define LOG_STDOUT 0x100 /* like nlog, for debugging syslogp() API */
#define LOG_RFC3154 0x200 /* Log to remote/ipc socket in old BSD format */
#ifndef __KERNEL__ #ifndef __KERNEL__
@ -206,6 +207,8 @@ 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* */
int log_pid;
}; };
#define SYSLOG_DATA_INIT { \ #define SYSLOG_DATA_INIT { \
@ -219,6 +222,8 @@ 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, \
.log_pid = -1, \
} }
#ifdef __cplusplus #ifdef __cplusplus

View File

@ -30,6 +30,7 @@ MSG=$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG$MSG
logger ${MSG} logger ${MSG}
logger 1${MSG} logger 1${MSG}
logger 2${MSG} logger 2${MSG}
sleep 1
if [ -f "${LOG}.0" ] && if [ -f "${LOG}.0" ] &&
grep 'script 1' "${NOT1STAMP}" && grep 'script 1' "${NOT1STAMP}" &&