Support for logging to a file/stdout using syslog API

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson 2019-10-29 09:08:37 +01:00
parent ee2d0ce106
commit 176ba2d095
3 changed files with 145 additions and 82 deletions

View File

@ -40,18 +40,29 @@
.Op Ar MESSAGE
.Sh DESCRIPTIOMN
.Nm
can be used to send messages to the system log from a UNIX shell, or
script.
can be used to log messages to the system log daemon from a UNIX shell,
or script. Optionally a stand-alone log file can be used, which is the
automatically log rotated. Without a
.Ar MESSAGE
argument
.Nm
reads input from
.Ar stdin .
.Sh OPTIONS
This program follows the usual UNIX command line syntax:
.Bl -tag -width Ds
.It Fl f Ar FILE
Log file to write messages to, instead of syslog daemon.
.Nm
accepts
.Fl f-
as an alias for
.Ar stdout .
.It Fl h
Show program help.
.It Fl p Ar PRIO
Priority, numeric or
.Ar facility.level
.Ar facility.severity
pair.
.It Fl r Ar SIZE:NUM
Controls log file rotation.
@ -69,7 +80,15 @@ Show program version.
.It Ar MESSAGE
Log message to write. Remember to use single/double qoutes if calling
.Nm
from a shell prompt due to expansion the shell does.
from a shell prompt due to expansion the shell does. If no message is
given
.Nm
will read from
.Ar stdin
until EOF. In this mode every new row (newline separated) is converted
into an independent
.Xr syslog 3
call.
.El
.Sh EXAMPLES
.Bd -unfilled -offset left

View File

@ -29,7 +29,9 @@
* SUCH DAMAGE.
*/
#include <config.h>
#include "config.h"
#include <err.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
@ -42,7 +44,7 @@
#include "compat.h"
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
static struct syslog_data log = SYSLOG_DATA_INIT;
static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
{
@ -113,6 +115,7 @@ static int checksz(FILE *fp, off_t sz)
{
struct stat st;
fsync(fileno(fp));
if (sz <= 0)
return 0;
@ -124,49 +127,20 @@ static int checksz(FILE *fp, off_t sz)
return 0;
}
static int logit(int level, char *buf, size_t len)
char *chomp(char *str)
{
if (buf[0]) {
syslog(level, "%s", buf);
return 0;
}
char *p;
while ((fgets(buf, len, stdin)))
syslog(level, "%s", buf);
if (!str || strlen(str) < 1) {
errno = EINVAL;
return NULL;
}
return 0;
}
p = str + strlen(str) - 1;
while (p >= str && *p == '\n')
*p-- = 0;
static int flogit(char *logfile, int num, off_t sz, char *buf, size_t len)
{
FILE *fp;
reopen:
fp = fopen(logfile, "a");
if (!fp) {
syslog(LOG_ERR | LOG_PERROR, "Failed opening %s: %s", logfile, strerror(errno));
return 1;
}
if (buf[0]) {
fprintf(fp, "%s\n", buf);
fsync(fileno(fp));
if (checksz(fp, sz))
return logrotate(logfile, num, sz);
} else {
while ((fgets(buf, len, stdin))) {
fputs(buf, fp);
fsync(fileno(fp));
if (checksz(fp, sz)) {
logrotate(logfile, num, sz);
buf[0] = 0;
goto reopen;
}
}
}
return fclose(fp);
return str;
}
static int parse_prio(char *arg, int *f, int *l)
@ -178,20 +152,22 @@ static int parse_prio(char *arg, int *f, int *l)
*ptr++ = 0;
for (int i = 0; facilitynames[i].c_name; i++) {
if (!strcmp(facilitynames[i].c_name, arg)) {
*f = facilitynames[i].c_val;
break;
}
if (strcmp(facilitynames[i].c_name, arg))
continue;
*f = facilitynames[i].c_val;
break;
}
arg = ptr;
}
for (int i = 0; prioritynames[i].c_name; i++) {
if (!strcmp(prioritynames[i].c_name, arg)) {
*l = prioritynames[i].c_val;
break;
}
if (strcmp(prioritynames[i].c_name, arg))
continue;
*l = prioritynames[i].c_val;
break;
}
return 0;
@ -203,7 +179,7 @@ static int usage(int code)
"\n"
"Write MESSAGE (or stdin) to syslog, or file (with logrotate)\n"
"\n"
" -p PRIO Log message priority (numeric or facility.level pair)\n"
" -p PRIO Log message priority (numeric or facility.severity pair)\n"
" -t TAG Log using the specified tag (defaults to user name)\n"
" -s Log to stderr as well as the system log\n"
"\n"
@ -221,10 +197,12 @@ static int usage(int code)
int main(int argc, char *argv[])
{
int c, rc, num = 5;
FILE *fp;
int c, num = 5;
int facility = LOG_USER;
int level = LOG_INFO;
int log_opts = LOG_NOWAIT;
int severity = LOG_INFO;
int log_opts = LOG_NDELAY | LOG_PID;
int rotate = 0;
off_t size = 200 * 1024;
char *ident = NULL, *logfile = NULL;
char buf[512] = "";
@ -236,7 +214,7 @@ int main(int argc, char *argv[])
break;
case 'p':
if (parse_prio(optarg, &facility, &level))
if (parse_prio(optarg, &facility, &severity))
return usage(1);
break;
@ -276,14 +254,34 @@ int main(int argc, char *argv[])
}
}
openlog(ident, log_opts, facility);
if (logfile) {
if (strcmp(logfile, "-")) {
log_opts |= LOG_NLOG;
fp = fopen(logfile, "a");
if (!fp)
err(1, "Failed opening %s for writing: %m", logfile);
rotate = 1;
} else {
log_opts |= LOG_STDOUT;
fp = stdout;
}
if (logfile)
rc = flogit(logfile, num, size, buf, sizeof(buf));
else
rc = logit(level, buf, sizeof(buf));
log.log_file = fileno(fp);
}
closelog();
openlog_r(ident, log_opts, facility, &log);
return rc;
if (!buf[0]) {
while ((fgets(buf, sizeof(buf), stdin)))
syslog_r(severity, &log, "%s", chomp(buf));
} else
syslog_r(severity, &log, "%s", buf);
closelog_r(&log);
if (rotate && checksz(fp, size))
logrotate(logfile, num, size);
return 0;
}

View File

@ -40,6 +40,7 @@ __RCSID("$NetBSD: syslog.c,v 1.55 2015/10/26 11:44:30 roy Exp $");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/uio.h>
#include <sys/un.h>
@ -71,7 +72,8 @@ static mutex_t syslog_mutex = MUTEX_INITIALIZER;
* wrapper to catch GLIBC syslog(), which provides this for security measures
* Note: we only enter here if user includes GLIBC syslog.h
*/
void __syslog_chk(int pri, int flag __attribute__((unused)), const char *fmt, ...)
void
__syslog_chk(int pri, int flag __attribute__((unused)), const char *fmt, ...)
{
va_list ap;
@ -80,6 +82,20 @@ void __syslog_chk(int pri, int flag __attribute__((unused)), const char *fmt, ..
va_end(ap);
}
/*
* Used to determine file/socket type of log_file
*/
static int
is_socket(int fd)
{
struct stat st;
if (fstat(fd, &st) || !S_ISSOCK(st.st_mode))
return 0;
return 1;
}
/*
* syslog, vsyslog --
* print message on log file; output is intended for syslogd(8).
@ -186,8 +202,10 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
char tbuf[TBUF_LEN], fmt_cpy[FMT_LEN], fmt_cat[FMT_LEN] = "";
size_t tbuf_left, fmt_left, msgsdlen;
char *fmt = fmt_cat;
struct iovec iov[7]; /* prog + [ + pid + ]: + fmt + crlf */
int opened, iovcnt;
char dbuf[30];
struct iovec iov[8]; /* date/time + prog + [ + pid + ]: + fmt + crlf */
int iovcnt = 0;
int opened;
#define INTERNALLOG LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID
/* Check for invalid bits. */
@ -219,8 +237,11 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
tbuf_left -= prlen; \
} while (/*CONSTCOND*/0)
prlen = snprintf(p, tbuf_left, "<%d>1 ", pri);
DEC();
if (!(data->log_stat & LOG_NLOG)) {
prlen = snprintf(p, tbuf_left, "<%d>1 ", pri);
DEC();
} else
prlen = 0;
if (gettimeofday(&tv, NULL) != -1) {
/* strftime() implies tzset(), localtime_r() doesn't. */
@ -241,6 +262,13 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
p[prlen-2] = ':';
prlen += 1;
}
if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG)) {
strftime(dbuf, sizeof(dbuf), "%b %d %Y %T ", &tmnow);
iov[iovcnt].iov_base = dbuf;
iov[iovcnt].iov_len = strlen(dbuf);
iovcnt++;
}
}
if (data == &sdata)
@ -266,8 +294,7 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
if (data == &sdata)
mutex_unlock(&syslog_mutex);
if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
iovcnt = 0;
if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG)) {
iov[iovcnt].iov_base = p;
iov[iovcnt].iov_len = prlen - 1;
iovcnt++;
@ -276,7 +303,7 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
if (data->log_stat & LOG_PID) {
prlen = snprintf(p, tbuf_left, "%d ", getpid());
if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG)) {
iov[iovcnt].iov_base = __UNCONST("[");
iov[iovcnt].iov_len = 1;
iovcnt++;
@ -289,7 +316,7 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
}
} else {
prlen = snprintf(p, tbuf_left, "- ");
if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG)) {
iov[iovcnt].iov_base = __UNCONST(BRCOSP + 1);
iov[iovcnt].iov_len = 2;
iovcnt++;
@ -311,7 +338,7 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
} else
strlcat(fmt_cat, "-", FMT_LEN);
if (data->log_stat & (LOG_PERROR|LOG_CONS))
if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG))
msgsdlen = strlen(fmt_cat) + 1;
else
msgsdlen = 0; /* XXX: GCC */
@ -354,8 +381,7 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
*t = '\0';
prlen = vsnprintf(p, tbuf_left, fmt_cpy, ap);
if (data->log_stat & (LOG_PERROR|LOG_CONS)) {
if (data->log_stat & (LOG_PERROR|LOG_CONS|LOG_NLOG)) {
iov[iovcnt].iov_base = p + msgsdlen;
iov[iovcnt].iov_len = prlen - msgsdlen;
iovcnt++;
@ -379,6 +405,21 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
openlog_unlocked_r(data->log_tag, data->log_stat, 0, data);
connectlog_r(data);
/* Don't write to system log, instead use fd in log_file */
if (data->log_stat & LOG_NLOG) {
iov[iovcnt].iov_base = __UNCONST(CRLF + 1);
iov[iovcnt].iov_len = 1;
(void)writev(data->log_file, iov, iovcnt + 1);
goto done;
}
/* Log to stdout, usually for debugging syslogp() API */
if (data->log_stat & LOG_STDOUT) {
strlcat(tbuf, "\n", sizeof(tbuf));
write(data->log_file, tbuf, strlen(tbuf));
goto done;
}
/*
* If the send() failed, there are two likely scenarios:
* 1) syslogd was restarted
@ -411,6 +452,7 @@ vsyslogp_r(int pri, struct syslog_data *data, const char *msgid,
(void)close(fd);
}
done:
if (data == &sdata)
mutex_unlock(&syslog_mutex);
@ -450,15 +492,19 @@ connectlog_r(struct syslog_data *data)
};
if (data->log_file == -1 || fcntl(data->log_file, F_GETFL, 0) == -1) {
if ((data->log_file = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC,
0)) == -1)
data->log_file = socket(AF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0);
if (data->log_file == -1)
return;
data->log_connected = 0;
}
if (!data->log_connected) {
if (connect(data->log_file,
(const struct sockaddr *)(const void *)&sun,
(socklen_t)sizeof(sun)) == -1) {
if (!is_socket(data->log_file)) {
data->log_connected = 1;
return;
}
if (connect(data->log_file, (const struct sockaddr *)&sun,
sizeof(sun)) == -1) {
(void)close(data->log_file);
data->log_file = -1;
} else