Add logger tool from Finit project to complement sysklogd

This patch introduces a relicensed logit from the Finit[1] project.  It
has been rebranded as logger to complement the features implemented in
the sysklogd project.

Note, logger conflicts with the tool of the same name from util-linux.

[1]: https://github.com/troglobit/finit

Signed-off-by: Joachim Nilsson <troglobit@gmail.com>
This commit is contained in:
Joachim Nilsson 2019-10-13 14:44:51 +02:00
parent 4fcebdb03c
commit 1a09654e79
5 changed files with 388 additions and 0 deletions

View File

@ -1,2 +1,3 @@
dist_man1_MANS = logger.1
dist_man5_MANS = syslog.conf.5
dist_man8_MANS = syslogd.8 klogd.8 sysklogd.8

87
man/logger.1 Normal file
View File

@ -0,0 +1,87 @@
.\" -*- nroff -*-
.\" Copyright (c) 2018, 2019 Joachim Nilsson <troglobit@gmail.com>
.\" All rights reserved.
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\" 3. Neither the name of the University nor the names of its contributors
.\" may be used to endorse or promote products derived from this software
.\" without specific prior written permission.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.Dd Oct 13, 2019
.Dt logger 1
.Os "sysklogd (2.0)"
.Sh NAME
.Nm logger
.Nd Send messages to system log, or a log file
.Sh SYNOPSIS
.Nm
.Op Fl hsv
.Op Fl f Ar FILE
.Op Fl n Ar SIZE
.Op Fl p Ar PRIO
.Op Fl r Ar NUM
.Op Fl t Ar TAG
.Qq Ar message
.Sh DESCRIPTIOMN
.Nm
can be used to send messages to the system log from a UNIX shell, or
script.
.Sh OPTIONS
This program follows the usual UNIX command line syntax:
.Bl -tag -width Ds
.It Fl f Ar FILE
File to write log messages to, instead of syslog.
.It Fl h
Show program help.
.It Fl n Ar SIZE
Number of bytes before rotating when logging to a file, default: 200 kB.
.It Fl p Ar PRIO
Priority, numeric or facility.level pair.
.It Fl r Ar NUM
Number of rotated files to keep when logging to a file, default: 5.
.It Fl s
Log to stderr as well as the system log.
.It Fl t Ar TAG
Log using the specified tag, default: username.
.It Fl v
Show program version.
.El
.Sh EXAMPLES
.Bd -unfilled -offset left
logger -t dropbear -p auth.notice "Successful login for user 'admin' from 1.2.3.4"
logger -t udhcpc -f /tmp/script.log "New lease 1.2.3.200 obtained for interface eth0"
.Ed
.Sh SEE ALSO
.Xr syslog 3
.Xr syslogd 8
.Sh AUTHORS
.Nm
was originally written by Joachim Nilsson to be a part of the
.Xr finit 1
system monitor (PID 1), where it is called
.Nm logit .
It is included here to complement
.Xr syslogd 8
and be extended upon in the sysklogd project.
.Sh STANDARDS
The
.Nm
command is expected to be IEEE Std 1003.2 ("POSIX.2") compatible.
.Sh AVAILABILITY

1
src/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
logger

View File

@ -16,6 +16,7 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
bin_PROGRAMS = logger
sbin_PROGRAMS = syslogd klogd
AM_CFLAGS = -fomit-frame-pointer -fno-strength-reduce -Wno-unused-result
@ -28,3 +29,6 @@ klogd_SOURCES = klogd.c klogd.h syslog.c pidfile.c pidfile.h \
ksym.c ksyms.h ksym_mod.c module.h
klogd_CPPFLAGS = -DSYSV -DFSSTND -DALLOW_KERNEL_LOGGING \
-D_BSD_SOURCE -D_SVID_SOURCE -D_DEFAULT_SOURCE
logger_SOURCES = logger.c
logger_CPPFLAGS = -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_GNU_SOURCE -D_DEFAULT_SOURCE

295
src/logger.c Normal file
View File

@ -0,0 +1,295 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 2018, 2019 Joachim Nilsson <troglobit@gmail.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <config.h>
#include <errno.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define SYSLOG_NAMES
#include <syslog.h>
#include <unistd.h>
#include <sys/stat.h>
static const char version_info[] = PACKAGE_NAME " v" PACKAGE_VERSION;
static int create(char *path, mode_t mode, uid_t uid, gid_t gid)
{
return mknod(path, S_IFREG | mode, 0) || chown(path, uid, gid);
}
/*
* This function triggers a log rotates of @file when size >= @sz bytes
* At most @num old versions are kept and by default it starts gzipping
* .2 and older log files. If gzip is not available in $PATH then @num
* files are kept uncompressed.
*/
static int logrotate(char *file, int num, off_t sz)
{
int cnt;
struct stat st;
if (stat(file, &st))
return 1;
if (sz > 0 && S_ISREG(st.st_mode) && st.st_size > sz) {
if (num > 0) {
size_t len = strlen(file) + 10 + 1;
char ofile[len];
char nfile[len];
/* First age zipped log files */
for (cnt = num; cnt > 2; cnt--) {
snprintf(ofile, len, "%s.%d.gz", file, cnt - 1);
snprintf(nfile, len, "%s.%d.gz", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
}
for (cnt = num; cnt > 0; cnt--) {
snprintf(ofile, len, "%s.%d", file, cnt - 1);
snprintf(nfile, len, "%s.%d", file, cnt);
/* May fail because ofile doesn't exist yet, ignore. */
(void)rename(ofile, nfile);
if (cnt == 2 && !access(nfile, F_OK)) {
size_t len = 5 + strlen(nfile) + 1;
char cmd[len];
snprintf(cmd, len, "gzip %s", nfile);
system(cmd);
remove(nfile);
}
}
if (rename(file, nfile))
(void)truncate(file, 0);
else
create(file, st.st_mode, st.st_uid, st.st_gid);
} else {
if (truncate(file, 0))
syslog(LOG_ERR | LOG_PERROR, "Failed truncating %s during logrotate: %s", file, strerror(errno));
}
}
return 0;
}
static int checksz(FILE *fp, off_t sz)
{
struct stat st;
if (sz <= 0)
return 0;
if (!fstat(fileno(fp), &st) && st.st_size > sz) {
fclose(fp);
return 1;
}
return 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);
}
static int logit(int level, char *buf, size_t len)
{
if (buf[0]) {
syslog(level, "%s", buf);
return 0;
}
while ((fgets(buf, len, stdin)))
syslog(level, "%s", buf);
return 0;
}
static int parse_prio(char *arg, int *f, int *l)
{
char *ptr;
ptr = strchr(arg, '.');
if (ptr) {
*ptr++ = 0;
for (int i = 0; facilitynames[i].c_name; i++) {
if (!strcmp(facilitynames[i].c_name, arg)) {
*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;
}
}
return 0;
}
static int usage(int code)
{
fprintf(stderr, "Usage: logger [OPTIONS] [MESSAGE]\n"
"\n"
"Write MESSAGE (or stdin) to syslog, or file (with logrotate)\n"
"\n"
" -h This help text\n"
" -p PRIO Priority (numeric or facility.level 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"
" -f FILE File to write log messages to, instead of syslog\n"
" -n SIZE Number of bytes before rotating, default: 200 kB\n"
" -r NUM Number of rotated files to keep, default: 5\n"
" -v Show program version\n"
"\n"
"This version of logger is distributed as part of sysklogd.\n"
"Bug report address: %s\n", PACKAGE_BUGREPORT);
return code;
}
int main(int argc, char *argv[])
{
int c, rc, num = 5;
int facility = LOG_USER;
int level = LOG_INFO;
int log_opts = LOG_NOWAIT;
off_t size = 200 * 1024;
char *ident = NULL, *logfile = NULL;
char buf[512] = "";
while ((c = getopt(argc, argv, "f:hn:p:r:st:v")) != EOF) {
switch (c) {
case 'f':
logfile = optarg;
break;
case 'h':
return usage(0);
case 'n':
size = atoi(optarg);
break;
case 'p':
if (parse_prio(optarg, &facility, &level))
return usage(1);
break;
case 'r':
num = atoi(optarg);
break;
case 's':
log_opts |= LOG_PERROR;
break;
case 't':
ident = optarg;
break;
case 'v': /* version */
fprintf(stderr, "%s\n", version_info);
return 0;
default:
return usage(1);
}
}
if (!ident)
ident = getenv("LOGNAME") ?: getenv("USER");
if (optind < argc) {
size_t pos = 0, len = sizeof(buf);
while (optind < argc) {
size_t bytes;
bytes = snprintf(&buf[pos], len, "%s ", argv[optind++]);
pos += bytes;
len -= bytes;
}
}
openlog(ident, log_opts, facility);
if (logfile)
rc = flogit(logfile, num, size, buf, sizeof(buf));
else
rc = logit(level, buf, sizeof(buf));
closelog();
return rc;
}