Remove usyslogd/klogd and syslog utility program

This is split out to a seperate package.

Signed-off-by: David Oberhollenzer <david.oberhollenzer@tele2.at>
This commit is contained in:
David Oberhollenzer 2018-10-28 13:41:35 +01:00
parent d4ce928fc0
commit acd09007a1
17 changed files with 10 additions and 1498 deletions

3
.gitignore vendored
View File

@ -20,9 +20,6 @@ reboot
shutdown
killall5
runsvc
syslog
usyslogd
klogd
gcrond
services/sigkill

View File

@ -23,7 +23,6 @@ include initd/Makemodule.am
include crond/Makemodule.am
include scripts/Makemodule.am
include services/Makemodule.am
include syslogd/Makemodule.am
install-exec-hook:
(cd $(DESTDIR)$(sbindir); $(LN_S) shutdown reboot)
@ -56,9 +55,3 @@ if GCROND
$(MKDIR_P) $(DESTDIR)$(GCRONDIR)
$(LN_S) $(TEMPLATEDIR)/gcrond $(DESTDIR)$(SVCDIR)/gcrond
endif
if USYSLOGD
$(LN_S) $(TEMPLATEDIR)/usyslogd $(DESTDIR)$(SVCDIR)/usyslogd
endif
if KLOGD
$(LN_S) $(TEMPLATEDIR)/klogd $(DESTDIR)$(SVCDIR)/klogd
endif

View File

@ -2,15 +2,8 @@
This directory contains the source code for a tiny service supervision
framework devised for the Pygos system, consisting of an init daemon,
a small syslog daemon, a _definitely_ non standards compliant cron
implementation and various command line utilities.
The individual parts of the framework are designed to be independent of
each other (for instance, the tiny syslogd is intended to work with *any*
init system and other components of the framework don't depend on the presence
of this *specific* syslog implementation) and everything that is not strictly
part of the init system (such as the syslog daemon) can be disabled through
the configure script.
a _definitely_ non standards compliant cron implementation and various
command line utilities.
The programs of this package are developed first and foremost for GNU/Linux
@ -40,9 +33,8 @@ Right now, the system is in a "basically works" proof of concept stage and
needs some more work to become usable.
There are plans for *maybe* *eventually* adding more fancy features like
support for Linux name spaces, seccomp filters and cgroups or network
back ends for the syslog daemon, but right now, features are added only
when the need arises.
support for Linux name spaces, seccomp filters and cgroups, but right now,
features are added only when the need arises.
See [docs/init.md](docs/init.md) for more information on the design,
@ -60,9 +52,6 @@ configuration works.
See [docs/defconfig.md](docs/defconfig.md) for an explanation on the default
services and configuration provided with this package.
See [docs/usyslogd.md](docs/usyslogd.md) for details on the tiny syslog
implementation.
See [docs/gcron.md](docs/gcron.md) for details on the cron implementation.

View File

@ -31,17 +31,6 @@ service_SOURCES += cmd/service/schedule.c
service_SOURCES += cmd/service/unschedule.c
endif
if USYSLOGD
syslog_SOURCES = cmd/syslog.c
syslog_CPPFLAGS = $(AM_CPPFLAGS)
syslog_CFLAGS = $(AM_CFLAGS)
syslog_LDFLAGS = $(AM_LDFLAGS)
syslog_LDADD = libinit.a
bin_PROGRAMS += syslog
dist_man1_MANS += cmd/syslog.1
endif
dist_man8_MANS += cmd/shutdown.8 cmd/service/service.8
EXTRA_DIST += $(SRVHEADERS)

View File

@ -1,47 +0,0 @@
.TH syslog 1 "August 2018" "Pygos Init"
.SH NAME
syslog \- send a log message to the syslog daemon
.SH SYNOPSIS
.B syslog
[options] message..
.SH DESCRIPTION
The syslog command concatenates all non option arguments to a single message
that it sends to the syslog daemon.
.SH OPTIONS
.TP
.BR \-h , " \-\-help"
Display a brief help text, a list of available facilities, log levels, all the
default setting and exit.
.TP
.BR \-V , " \-\-version"
Print version information and exit.
.TP
.BR \-f , " \-\-facility " \fIfacility\fP
Logging facility name or numeric identifier. Use
.B \-\-help
to get a list of all available facility names.
.TP
.BR \-l , " \-\-level " \fIlevel\fP
A log level number from 0 to 8 with 8 being most severe, or a name from
.I debug
to
.I emergency.
Use
.B \-\-help
to get a list of all available log level names.
.TP
.BR \-i , " \-\-ident " \fIname\fP
Program name to specify to syslog. If not set, "(shell)" is used as sensible
default to indicate that the message came from the command line.
.TP
.BR \-c , " \-\-console"
Try to write directly to the console if opening the syslog socket fails.
.SH AVAILABILITY
This program is part of the Pygos init system.
.SH COPYRIGHT
Copyright \(co 2018 David Oberhollenzer
.br
License: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>.
.br
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

View File

@ -1,223 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <getopt.h>
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include "util.h"
static int facility = LOG_USER;
static int level = LOG_INFO;
static int flags = LOG_NDELAY | LOG_NOWAIT;
static const char *ident = "(shell)";
static const enum_map_t facility_map[] = {
{ "auth", LOG_AUTH },
{ "cron", LOG_CRON },
{ "daemon", LOG_DAEMON },
{ "ftp", LOG_FTP },
{ "local0", LOG_LOCAL0 },
{ "local1", LOG_LOCAL1 },
{ "local2", LOG_LOCAL2 },
{ "local3", LOG_LOCAL3 },
{ "local4", LOG_LOCAL4 },
{ "local5", LOG_LOCAL5 },
{ "local6", LOG_LOCAL6 },
{ "local7", LOG_LOCAL7 },
{ "lpr", LOG_LPR },
{ "news", LOG_NEWS },
{ "user", LOG_USER },
{ "uucp", LOG_UUCP },
{ NULL, 0 },
};
static const enum_map_t level_map[] = {
{ "emergency", LOG_EMERG },
{ "alert", LOG_ALERT },
{ "critical", LOG_CRIT },
{ "error", LOG_ERR },
{ "warning", LOG_WARNING },
{ "notice", LOG_NOTICE },
{ "info", LOG_INFO },
{ "debug", LOG_DEBUG },
{ NULL, 0 },
};
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "console", required_argument, NULL, 'c' },
{ "facility", required_argument, NULL, 'f' },
{ "level", required_argument, NULL, 'l' },
{ "ident", required_argument, NULL, 'i' },
{ NULL, 0, NULL, 0 },
};
static const char *shortopt = "hVcf:l:i:";
static const char *helptext =
"Usage: syslog [OPTION]... [STRING]...\n\n"
"Concatenate the given STRINGs and send a log message to the syslog daemon.\n"
"\n"
"The following OPTIONSs can be used:\n"
" -f, --facility <facility> Logging facilty name or numeric identifier.\n"
" -l, --level <level> Log level name or numeric identifier.\n"
" -i, --ident <name> Program name for log syslog message.\n"
" Default is %s.\n\n"
" -c, --console Write to the console if opening the syslog\n"
" socket fails.\n\n"
" -h, --help Print this help text and exit\n"
" -V, --version Print version information and exit\n\n";
static void print_map(const enum_map_t *map, int defaultval,
const char *option)
{
size_t i;
printf("The following values can be used for %s:\n", option);
for (i = 0; map[i].name != NULL; ++i) {
if (map[i].value == defaultval) {
printf(" %s (=%d), set as default\n",
map[i].name, map[i].value);
} else {
printf(" %s (=%d)\n", map[i].name, map[i].value);
}
}
fputc('\n', stdout);
}
static NORETURN void usage(int status)
{
if (status != EXIT_SUCCESS) {
fputs("Try `syslog --help' for more information\n", stderr);
} else {
printf(helptext, ident);
print_map(level_map, level, "--level");
print_map(facility_map, facility, "--facility");
}
exit(status);
}
static int readint(const char *str)
{
int x = 0;
if (!isdigit(*str))
return -1;
while (isdigit(*str))
x = x * 10 + (*(str++)) - '0';
return (*str == '\0') ? x : -1;
}
static void process_options(int argc, char **argv)
{
const enum_map_t *e;
int c;
for (;;) {
c = getopt_long(argc, argv, shortopt, options, NULL);
if (c == -1)
break;
switch (c) {
case 'f':
facility = readint(optarg);
if (facility >= 0)
break;
e = enum_by_name(facility_map, optarg);
if (e == NULL) {
fprintf(stderr, "Unknown facility name '%s'\n",
optarg);
usage(EXIT_FAILURE);
}
facility = e->value;
break;
case 'l':
level = readint(optarg);
if (level >= 0)
break;
e = enum_by_name(level_map, optarg);
if (e == NULL) {
fprintf(stderr, "Unknown log level '%s'\n",
optarg);
usage(EXIT_FAILURE);
}
level = e->value;
break;
case 'i':
ident = optarg;
break;
case 'c':
flags |= LOG_CONS;
break;
case 'h':
usage(EXIT_SUCCESS);
case 'V':
print_version("syslog");
default:
usage(EXIT_FAILURE);
}
}
}
int main(int argc, char **argv)
{
size_t len = 0;
char *str;
int i;
process_options(argc, argv);
if (optind >= argc) {
fputs("Error: no log string provided.\n", stderr);
usage(EXIT_FAILURE);
}
for (i = optind; i < argc; ++i)
len += strlen(argv[i]);
len += argc - optind - 1;
str = calloc(1, len + 1);
if (str == NULL) {
fputs("syslog: out of memory\n", stderr);
return EXIT_FAILURE;
}
for (i = optind; i < argc; ++i) {
if (i > optind)
strcat(str, " ");
strcat(str, argv[i]);
}
openlog(ident, flags, facility);
syslog(level, "%s", str);
closelog();
free(str);
return EXIT_SUCCESS;
}

View File

@ -37,24 +37,6 @@ UL_WARN_ADD([-pedantic])
AC_SUBST([WARN_CFLAGS])
AC_ARG_WITH([usyslogd],
[AS_HELP_STRING([--without-usyslogd], [Build without syslog daemon])],
[case "${withval}" in
yes) AM_CONDITIONAL([USYSLOGD], [true]) ;;
no) AM_CONDITIONAL([USYSLOGD], [false]) ;;
*) AC_MSG_ERROR([bad value ${withval} for --without-usyslogd]) ;;
esac],
[AM_CONDITIONAL([USYSLOGD], [true])])
AC_ARG_WITH([klogd],
[AS_HELP_STRING([--without-klogd], [Build without kernel log daemon])],
[case "${withval}" in
yes) AM_CONDITIONAL([KLOGD], [true]) ;;
no) AM_CONDITIONAL([KLOGD], [false]) ;;
*) AC_MSG_ERROR([bad value ${withval} for --without-klogd]) ;;
esac],
[AM_CONDITIONAL([KLOGD], [true])])
AC_ARG_WITH([gcrond],
[AS_HELP_STRING([--without-gcrond], [Build without gcron daemon])],
[case "${withval}" in

View File

@ -33,15 +33,3 @@ The option `-f` or `--force` can be used to by pass the init system entirely
and force a hard reset or power off by directly signalling the kernel.
Running any one of those programs requires superuser privileges.
## syslog
If the `usyslogd` service is built as part of this package, a program called
`syslog` is built that can be used from the command line to send syslog
messages.
This can for instance be used to produce log messages from shell scripts.
The log level, facility and identity string can be specified.
See `syslog --help` for more information.

View File

@ -21,7 +21,7 @@ The pseudo targets are (in the order that they are executed):
The system has reached a sane state, i.e. the hostname is set, the system
clock has a sane value, modules and kernel parameters are loaded, some
very basic, fundamental services are running (e.g. syslog).
very basic, fundamental services are running.
Everything that is part of that setup process goes between `vfs` and
`sysinit`, everything that requires a sane setup goes *after* `sysinit`.
@ -55,10 +55,6 @@ the `vfs` target and *before* the `sysinit` target:
* hostname - reload hostname `/etc/hostname`
* loopback - bring the loopback device up
* usyslogd - if the `usyslogd` service is compiled with this package, this
service is enabled by default and starts `usyslogd`.
* klogd - if the `klogd` daemon is compiled with this package, this service
is enabled by default and starts after `usyslogd`.
* modules - iterate over the file `/etc/modules` and try to load each module
using modprobe.
* sysctl - restore kernel parameters using `sysctl --system`. See `sysctl(8)`
@ -117,4 +113,7 @@ For the shutdown and reboot targets, the following services are executed:
* sshd - Starts an OpenSSH server after the network pseudo service and after
the sshd_keygen service.
* nginx - Starts the Nginx server after the network pseudo serivce.
* usyslogd - Starts and supervises the `usyslogd` syslog implementation as
part of the `sysinit` target.
* klogd - Stats and supervises the `klogd` daemon as part of the `sysinit`
target. The `klogd` daemon is part of the `usyslog` package.

View File

@ -1,103 +0,0 @@
# Syslogd Implementation
A tiny syslogd implementation `usyslogd` is provided as part of this package.
It opens a socket in `/dev/log`, processes syslog messages and forwards the
parsed message to a modular backend interface.
Currently, there is only one implementation of the backend interface that dumps
the log messages into files in the processes working directory (by default
`/var/log`).
A simple log rotation scheme has been implemented.
## Kernel Message Logging
An additional small `klogd` daemon is provided that redirects kernel messages
to the syslog daemon.
It can be enabled or disabled independently of the `usyslogd` daemon and is
designed to work with any other syslogd implementation.
## Security Considerations
By default, the daemon switches its working directory to `/var/log`. The
directory is created if it doesn't exist and the daemon always tries to
change its mode to one that doesn't allow other users (except group members)
to access the directory.
If told to so on the command line, the daemon chroots to the log directory.
By default, the daemon then tries to drop privileges by switching to user and
group named `syslogd` if they exist (any other user or group can be specified
on the command line; doing so causes syslogd to fail if they don't exist).
On a system that hosts accounts for multiple users that may be more or less
trusted, one may consider only giving system services access to the syslog
socket and not allowing regular users. Otherwise, a user may flood the syslog
daemon with messages, possibly leading to resource starvation, or (in the case
of size limited log rotation outlined below) to the loss of otherwise critical
log messages. Since this is not the primary target of the Pygos system, such
a mechanism is not yet implemented.
In case of a system where only daemons are running, the above mentioned
security measure is useless. If a remote attacker manages to get regular user
privileges, you already have a different, much greater problem. Also, a remote
attacker would have to compromise a local daemon that already has special
access to the syslog socket, which is again your least concern in this
scenario.
## Logrotation
The backend can be configured to do log rotation in a continuous fashion (i.e.
in a way that log messages aren't lost), or in a way where it drops old
messages. Furthermore, the backend can be configured to automatically do a log
rotation if a certain size threshold is hit.
If the `usyslogd` receives a `SIGHUP`, it tells the backend to do log rotation.
In the case of the size threshold, the backend is expected to do the rotation
on its own if the predetermined limit is hit.
## File Based Backend
The file based backend writes log messages to files in the current working
directory (by default `/var/log`), named either after the ident string (if
specified) or the facility name.
Log messages are prefixed with an ISO 8601 time stamp, optionally the facility
name (unless part of the file name), the log level and the senders PID. Each
of those fields is enclosed in brackets.
Log rotation in a continuous fashion means renaming the existing log file to
one suffixed with the current time stamp. Overwriting old messages renaming
the log file by appending a constant `.1` suffix.
## Default Configuration
The default service configuration limits the log file size to 8 KiB and
configures the daemon to overwrite old messages when rotating log files,
effectively limiting the amount of log data to 16 KiB per source or facility.
The intended use case in the Pygos system is logging to a ramdisk without
exhausting available memory.
## Possible Future Directions
In the near term future, the daemon probably requires more fine grained control
over logging such as setting a minimum log level or a way to configure limits
per facility or service.
In the medium term future, extended resource control using c-groups might be
a possibility.
Future directions may include adding other backends, such as forwarding the
log messages to a central server, for instance using syslog over UDP/TCP or
using the front end of some time series database.

View File

@ -9,20 +9,12 @@ init_DATA += services/dnsmasq services/ifdown services/modules
init_DATA += services/network services/hostapd services/swclock
init_DATA += services/swclocksave services/nft services/sigkill
init_DATA += services/sshd services/sshd_keygen services/consolefont
init_DATA += services/nginx
init_DATA += services/nginx services/usyslogd services/klogd
if GCROND
init_DATA += services/swclocksave.gcron
endif
if USYSLOGD
init_DATA += services/usyslogd
endif
if USYSLOGD
init_DATA += services/klogd
endif
if GCROND
init_DATA += services/gcrond
endif

View File

@ -1,20 +0,0 @@
if USYSLOGD
usyslogd_SOURCES = syslogd/syslogd.c syslogd/syslogd.h
usyslogd_SOURCES += syslogd/proto.c syslogd/logfile.c
usyslogd_CPPFLAGS = $(AM_CPPFLAGS)
usyslogd_CFLAGS = $(AM_CFLAGS)
usyslogd_LDFLAGS = $(AM_LDFLAGS)
usyslogd_LDADD = libinit.a
sbin_PROGRAMS += usyslogd
endif
if KLOGD
klogd_SOURCES = syslogd/klogd.c
klogd_CPPFLAGS = $(AM_CPPFLAGS)
klogd_CFLAGS = $(AM_CFLAGS)
klogd_LDFLAGS = $(AM_LDFLAGS)
klogd_LDADD = libinit.a
sbin_PROGRAMS += klogd
endif

View File

@ -1,184 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <sys/klog.h>
#include <syslog.h>
#include <signal.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <stdio.h>
#include <errno.h>
#include "config.h"
#include "util.h"
enum {
KLOG_CLOSE = 0,
KLOG_OPEN = 1,
KLOG_READ = 2,
KLOG_CONSOLE_OFF = 6,
KLOG_CONSOLE_ON = 7,
KLOG_CONSOLE_LEVEL = 8,
};
static char log_buffer[4096];
static sig_atomic_t running = 1;
static int level = 0;
static const struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "level", required_argument, NULL, 'l' },
{ NULL, 0, NULL, 0 },
};
static const char *shortopt = "hVl:";
static const char *helptext =
"Usage: klogd [OPTION]... \n\n"
"Collect printk() messages from the kernel and forward them to syslogd.\n"
"\n"
"The following OPTIONSs can be used:\n"
" -l, --level <level> Minimum log level that should be printed to console.\n"
" If not set, logging to console is turned off.\n"
" -h, --help Print this help text and exit\n"
" -V, --version Print version information and exit\n\n";
static void process_options(int argc, char **argv)
{
int c;
for (;;) {
c = getopt_long(argc, argv, shortopt, options, NULL);
if (c == -1)
break;
switch (c) {
case 'l':
level = strtoul(optarg, NULL, 10);
break;
case 'h':
fputs(helptext, stdout);
exit(EXIT_SUCCESS);
case 'V':
print_version("klogd");
default:
fputs("Try `klogd --help' for more information\n",
stderr);
exit(EXIT_FAILURE);
}
}
}
static void sighandler(int signo)
{
if (signo == SIGTERM || signo == SIGINT)
running = 0;
}
static void sigsetup(void)
{
struct sigaction act;
sigset_t mask;
memset(&act, 0, sizeof(act));
act.sa_handler = sighandler;
sigaction(SIGTERM, &act, NULL);
sigaction(SIGINT, &act, NULL);
sigfillset(&mask);
sigdelset(&mask, SIGTERM);
sigdelset(&mask, SIGINT);
sigprocmask(SIG_SETMASK, &mask, NULL);
}
static void log_open(void)
{
klogctl(KLOG_OPEN, NULL, 0);
if (level) {
klogctl(KLOG_CONSOLE_LEVEL, NULL, level);
} else {
klogctl(KLOG_CONSOLE_OFF, NULL, 0);
}
openlog("kernel", 0, LOG_KERN);
}
static void log_close(void)
{
klogctl(KLOG_CONSOLE_ON, NULL, 0);
klogctl(KLOG_CLOSE, NULL, 0);
syslog(LOG_NOTICE, "-- klogd terminating --");
}
int main(int argc, char **argv)
{
int diff, count = 0, priority, ret = EXIT_SUCCESS;
char *ptr, *end;
process_options(argc, argv);
sigsetup();
log_open();
/* TODO: seccomp lockdown? */
while (running) {
diff = klogctl(KLOG_READ, log_buffer + count,
sizeof(log_buffer) - 1 - count);
if (diff < 0) {
if (errno == EINTR)
continue;
syslog(LOG_CRIT, "klogctl read: %s", strerror(errno));
ret = EXIT_FAILURE;
break;
}
count += diff;
log_buffer[count] = '\0';
ptr = log_buffer;
for (;;) {
end = strchr(ptr, '\n');
if (end == NULL) {
count = strlen(ptr);
memmove(log_buffer, ptr, count);
break;
}
*(end++) = '\0';
priority = LOG_INFO;
if (*ptr == '<') {
++ptr;
if (*ptr)
priority = strtoul(ptr, &ptr, 10);
if (*ptr == '>')
++ptr;
}
if (*ptr)
syslog(priority, "%s", ptr);
ptr = end;
}
}
log_close();
return ret;
}

View File

@ -1,284 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include "syslogd.h"
#include "util.h"
static const enum_map_t levels[] = {
{ "emergency", 0 },
{ "alert", 1 },
{ "critical", 2 },
{ "error", 3 },
{ "warning", 4 },
{ "notice", 5 },
{ "info", 6 },
{ "debug", 7 },
{ NULL, 0 },
};
static const enum_map_t facilities[] = {
{ "kernel", 0 },
{ "user", 1 },
{ "mail", 2 },
{ "daemon", 3 },
{ "auth", 4 },
{ "syslog", 5 },
{ "lpr", 6 },
{ "news", 7 },
{ "uucp", 8 },
{ "clock", 9 },
{ "authpriv", 10 },
{ "ftp", 11 },
{ "ntp", 12 },
{ "audit", 13 },
{ "alert", 14 },
{ "cron", 15 },
{ "local0", 16 },
{ "local1", 17 },
{ "local2", 18 },
{ "local3", 19 },
{ "local4", 20 },
{ "local5", 21 },
{ "local6", 22 },
{ "local7", 23 },
{ NULL, 0 },
};
typedef struct logfile_t {
struct logfile_t *next;
size_t size;
int fd;
char filename[];
} logfile_t;
typedef struct {
log_backend_t base;
logfile_t *list;
size_t maxsize;
int flags;
} log_backend_file_t;
static int logfile_open(logfile_t *file)
{
struct stat sb;
file->fd = open(file->filename, O_WRONLY | O_CREAT, 0640);
if (file->fd < 0) {
perror(file->filename);
return -1;
}
if (lseek(file->fd, 0, SEEK_END))
goto fail;
if (fstat(file->fd, &sb))
goto fail;
file->size = sb.st_size;
return 0;
fail:
perror(file->filename);
close(file->fd);
file->fd = -1;
return -1;
}
static logfile_t *logfile_create(const char *filename)
{
logfile_t *file = calloc(1, sizeof(*file) + strlen(filename) + 1);
if (file == NULL) {
perror("calloc");
return NULL;
}
strcpy(file->filename, filename);
if (logfile_open(file)) {
free(file);
return NULL;
}
return file;
}
static int logfile_write(logfile_t *file, const syslog_msg_t *msg)
{
const char *lvl_str, *fac_name;
char timebuf[32];
struct tm tm;
int ret;
if (file->fd < 0 && logfile_open(file) != 0)
return -1;
lvl_str = enum_to_name(levels, msg->level);
if (lvl_str == NULL)
return -1;
gmtime_r(&msg->timestamp, &tm);
strftime(timebuf, sizeof(timebuf), "%FT%T", &tm);
if (msg->ident != NULL) {
fac_name = enum_to_name(facilities, msg->facility);
if (fac_name == NULL)
return -1;
ret = dprintf(file->fd, "[%s][%s][%s][%u] %s\n", timebuf,
fac_name, lvl_str, msg->pid, msg->message);
} else {
ret = dprintf(file->fd, "[%s][%s][%u] %s\n", timebuf, lvl_str,
msg->pid, msg->message);
}
fsync(file->fd);
if (ret > 0)
file->size += ret;
return 0;
}
static int logfile_rotate(logfile_t *f, int flags)
{
char timebuf[32];
char *filename;
struct tm tm;
time_t now;
if (flags & LOG_ROTATE_OVERWRITE) {
strcpy(timebuf, "1");
} else {
now = time(NULL);
gmtime_r(&now, &tm);
strftime(timebuf, sizeof(timebuf), "%FT%T", &tm);
}
filename = alloca(strlen(f->filename) + strlen(timebuf) + 2);
sprintf(filename, "%s.%s", f->filename, timebuf);
if (rename(f->filename, filename)) {
perror(filename);
return -1;
}
close(f->fd);
logfile_open(f);
return 0;
}
/*****************************************************************************/
static int file_backend_init(log_backend_t *backend, int flags,
size_t sizelimit)
{
log_backend_file_t *log = (log_backend_file_t *)backend;
log->flags = flags;
log->maxsize = sizelimit;
return 0;
}
static void file_backend_cleanup(log_backend_t *backend)
{
log_backend_file_t *log = (log_backend_file_t *)backend;
logfile_t *f;
while (log->list != NULL) {
f = log->list;
log->list = f->next;
close(f->fd);
free(f);
}
}
static int file_backend_write(log_backend_t *backend, const syslog_msg_t *msg)
{
log_backend_file_t *log = (log_backend_file_t *)backend;
const char *ident;
char *filename;
logfile_t *f;
size_t len;
if (msg->ident != NULL) {
ident = msg->ident;
} else {
ident = enum_to_name(facilities, msg->facility);
if (ident == NULL)
return -1;
}
len = strlen(ident) + strlen(".log") + 1;
filename = alloca(len);
strcpy(filename, ident);
strcat(filename, ".log");
for (f = log->list; f != NULL; f = f->next) {
if (strcmp(filename, f->filename) == 0)
break;
}
if (f == NULL) {
f = logfile_create(filename);
if (f == NULL)
return -1;
f->next = log->list;
log->list = f;
}
if (logfile_write(f, msg))
return -1;
if ((log->flags & LOG_ROTATE_SIZE_LIMIT) && f->size >= log->maxsize)
logfile_rotate(f, log->flags);
return 0;
}
static void file_backend_rotate(log_backend_t *backend)
{
log_backend_file_t *log = (log_backend_file_t *)backend;
logfile_t *f;
for (f = log->list; f != NULL; f = f->next)
logfile_rotate(f, log->flags);
}
log_backend_file_t filebackend = {
.base = {
.init = file_backend_init,
.cleanup = file_backend_cleanup,
.write = file_backend_write,
.rotate = file_backend_rotate,
},
.list = NULL,
};
log_backend_t *logmgr = (log_backend_t *)&filebackend;

View File

@ -1,194 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <stddef.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include "syslogd.h"
static const char *months[] = {
"Jan", "Feb", "Mar", "Apr",
"May", "Jun", "Jul", "Aug",
"Sep", "Oct", "Nov", "Dec"
};
static const int days[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static int isleap(int year)
{
return ((year % 4 == 0) && (year % 100 != 0)) || ((year % 400) == 0);
}
static int mdays(int year, int month)
{
return (isleap(year) && month == 2) ? 29 : days[month - 1];
}
static char *read_num(char *str, int *out, int maxval)
{
if (str == NULL || !isdigit(*str))
return NULL;
for (*out = 0; isdigit(*str); ++str) {
(*out) = (*out) * 10 + (*str) - '0';
if ((*out) > maxval)
return NULL;
}
return str;
}
static char *skip_space(char *str)
{
if (str == NULL || !isspace(*str))
return NULL;
while (isspace(*str))
++str;
return str;
}
static char *read_date_bsd(char *str, struct tm *tm)
{
int year, month, day, hour, minute, second;
time_t t;
/* decode date */
for (month = 0; month < 12; ++month) {
if (strncmp(str, months[month], 3) == 0) {
str = skip_space(str + 3);
break;
}
}
str = read_num(str, &day, 31);
str = skip_space(str);
t = time(NULL);
if (localtime_r(&t, tm) == NULL)
return NULL;
year = tm->tm_year;
/* sanity check */
if (str == NULL || month >= 12 || day < 1)
return NULL;
if (month == 11 && tm->tm_mon == 0)
--year;
if (day > mdays(year + 1900, month + 1))
return NULL;
/* decode time */
str = read_num(str, &hour, 23);
if (str == NULL || *(str++) != ':')
return NULL;
str = read_num(str, &minute, 59);
if (str == NULL || *(str++) != ':')
return NULL;
str = read_num(str, &second, 59);
str = skip_space(str);
/* store result */
memset(tm, 0, sizeof(*tm));
tm->tm_sec = second;
tm->tm_min = minute;
tm->tm_hour = hour;
tm->tm_mday = day;
tm->tm_mon = month;
tm->tm_year = year;
return str;
}
static char *decode_priority(char *str, int *priority)
{
while (isspace(*str))
++str;
if (*(str++) != '<')
return NULL;
str = read_num(str, priority, 23 * 8 + 7);
if (str == NULL || *(str++) != '>')
return NULL;
while (isspace(*str))
++str;
return str;
}
int syslog_msg_parse(syslog_msg_t *msg, char *str)
{
char *ident, *ptr;
struct tm tstamp;
pid_t pid = 0;
int priority;
size_t len;
memset(msg, 0, sizeof(*msg));
str = decode_priority(str, &priority);
if (str == NULL)
return -1;
msg->facility = priority >> 3;
msg->level = priority & 0x07;
str = read_date_bsd(str, &tstamp);
if (str == NULL)
return -1;
ident = str;
while (*str != '\0' && *str != ':')
++str;
if (*str == ':') {
*(str++) = '\0';
while (isspace(*str))
++str;
ptr = ident;
while (*ptr != '[' && *ptr != '\0')
++ptr;
if (*ptr == '[') {
*(ptr++) = '\0';
while (isdigit(*ptr))
pid = pid * 10 + *(ptr++) - '0';
}
} else {
ident = NULL;
}
if (ident != NULL && ident[0] == '\0')
ident = NULL;
msg->timestamp = mktime(&tstamp);
msg->pid = pid;
msg->ident = ident;
msg->message = str;
len = strlen(str);
while (len > 0 && isspace(str[len - 1]))
--len;
str[len] = '\0';
if (ident != NULL) {
for (ptr = ident; *ptr != '\0'; ++ptr) {
if (!isalnum(*ptr))
*ptr = '_';
}
}
return 0;
}

View File

@ -1,273 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <getopt.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <pwd.h>
#include <grp.h>
#include "syslogd.h"
#include "util.h"
static const struct option long_opts[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "rotate-replace", no_argument, NULL, 'r' },
{ "chroot", no_argument, NULL, 'c' },
{ "max-size", required_argument, NULL, 'm' },
{ "user", required_argument, NULL, 'u' },
{ "group", required_argument, NULL, 'g' },
{ NULL, 0, NULL, 0 },
};
static const char *short_opts = "hVcrm:u:g:";
const char *usage_string =
"Usage: usyslogd [OPTIONS..]\n\n"
"The following options are supported:\n"
" -h, --help Print this help text and exit\n"
" -V, --version Print version information and exit\n"
" -r, --rotate-replace Replace old log files when doing log rotation.\n"
" -m, --max-size <size> Automatically rotate log files bigger than this.\n"
" -u, --user <name> Run the syslog daemon as this user. If not set,\n"
" try to use the user '" DEFAULT_USER "'.\n"
" -g, --group <name> Run the syslog daemon as this group. If not set,\n"
" try to use the group '" DEFAULT_GROUP "'.\n"
" -c, --chroot If set, do a chroot into the log file path.\n";
static volatile sig_atomic_t syslog_run = 1;
static volatile sig_atomic_t syslog_rotate = 0;
static int log_flags = 0;
static size_t max_size = 0;
static uid_t uid = 0;
static gid_t gid = 0;
static bool dochroot = false;
static void sighandler(int signo)
{
switch (signo) {
case SIGINT:
case SIGTERM:
syslog_run = 0;
break;
case SIGHUP:
syslog_rotate = 1;
break;
default:
break;
}
}
static void signal_setup(void)
{
struct sigaction act;
memset(&act, 0, sizeof(act));
act.sa_handler = sighandler;
sigaction(SIGINT, &act, NULL);
sigaction(SIGTERM, &act, NULL);
sigaction(SIGHUP, &act, NULL);
}
static int handle_data(int fd)
{
char buffer[2048];
syslog_msg_t msg;
ssize_t ret;
memset(buffer, 0, sizeof(buffer));
ret = read(fd, buffer, sizeof(buffer));
if (ret <= 0)
return -1;
if (syslog_msg_parse(&msg, buffer))
return -1;
return logmgr->write(logmgr, &msg);
}
static void process_options(int argc, char **argv)
{
struct passwd *pw = getpwnam(DEFAULT_USER);
struct group *grp = getgrnam(DEFAULT_GROUP);
char *end;
int i;
if (pw != NULL)
uid = pw->pw_uid;
if (grp != NULL)
gid = grp->gr_gid;
for (;;) {
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
if (i == -1)
break;
switch (i) {
case 'r':
log_flags |= LOG_ROTATE_OVERWRITE;
break;
case 'm':
log_flags |= LOG_ROTATE_SIZE_LIMIT;
max_size = strtol(optarg, &end, 10);
if (max_size == 0 || *end != '\0') {
fputs("Numeric argument > 0 expected for -m\n",
stderr);
goto fail;
}
break;
case 'u':
pw = getpwnam(optarg);
if (pw == NULL) {
fprintf(stderr, "Cannot get UID for user %s\n",
optarg);
goto fail;
}
uid = pw->pw_uid;
break;
case 'g':
grp = getgrnam(optarg);
if (grp == NULL) {
fprintf(stderr,
"Cannot get GID for group %s\n",
optarg);
goto fail;
}
gid = grp->gr_gid;
break;
case 'c':
dochroot = true;
break;
case 'h':
fputs(usage_string, stdout);
exit(EXIT_SUCCESS);
case 'V':
print_version("usyslogd");
default:
goto fail;
}
}
return;
fail:
fputs("Try `usyslogd --help' for more information\n", stderr);
exit(EXIT_FAILURE);
}
static int chroot_setup(void)
{
if (mkdir(SYSLOG_PATH, 0750)) {
if (errno != EEXIST) {
perror("mkdir " SYSLOG_PATH);
return -1;
}
}
if (uid > 0 && gid > 0 && chown(SYSLOG_PATH, uid, gid) != 0) {
perror("chown " SYSLOG_PATH);
return -1;
}
if (chmod(SYSLOG_PATH, 0750)) {
perror("chmod " SYSLOG_PATH);
return -1;
}
if (chdir(SYSLOG_PATH)) {
perror("cd " SYSLOG_PATH);
return -1;
}
if (dochroot && chroot(SYSLOG_PATH) != 0) {
perror("chroot " SYSLOG_PATH);
return -1;
}
return 0;
}
static int user_setup(void)
{
if (gid > 0 && setresgid(gid, gid, gid) != 0) {
perror("setgid");
return -1;
}
if (uid > 0 && setresuid(uid, uid, uid) != 0) {
perror("setuid");
return -1;
}
return 0;
}
int main(int argc, char **argv)
{
int sfd, status = EXIT_FAILURE;
process_options(argc, argv);
signal_setup();
sfd = mksock(SYSLOG_SOCKET, SOCK_FLAG_EVERYONE | SOCK_FLAG_DGRAM);
if (sfd < 0)
return EXIT_FAILURE;
if (uid > 0 && gid > 0 && chown(SYSLOG_SOCKET, uid, gid) != 0) {
perror("chown " SYSLOG_SOCKET);
return -1;
}
if (chroot_setup())
return EXIT_FAILURE;
if (user_setup())
return EXIT_FAILURE;
if (logmgr->init(logmgr, log_flags, max_size))
goto out;
while (syslog_run) {
if (syslog_rotate) {
logmgr->rotate(logmgr);
syslog_rotate = 0;
}
handle_data(sfd);
}
status = EXIT_SUCCESS;
out:
logmgr->cleanup(logmgr);
if (sfd > 0)
close(sfd);
unlink(SYSLOG_SOCKET);
return status;
}

View File

@ -1,89 +0,0 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
/*
* Copyright (C) 2018 - David Oberhollenzer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef SYSLOGD_H
#define SYSLOGD_H
#include <sys/types.h>
#include <time.h>
#include "config.h"
#define SYSLOG_SOCKET PREFIXPATH "/dev/log"
#define SYSLOG_PATH PREFIXPATH "/var/log"
#define DEFAULT_USER "syslogd"
#define DEFAULT_GROUP "syslogd"
/*
encapsulates the split up data from a message received
through the local syslog socket.
*/
typedef struct {
int facility;
int level;
time_t timestamp;
pid_t pid;
const char *ident;
const char *message;
} syslog_msg_t;
enum {
/*
Rotate log data in a way that we still generate a continuous stream
of log data. E.g. in the case of log files, move the current log file
to one suffixed with a timestamp. We don't lose any log data.
*/
LOG_ROTATE_CONTINUOUS = 0x00,
/*
Rotate log data by overwriting old data with more recent data.
E.g. in the case of log files, move the current log file to one
with a constant prefix, overwriting any existing data.
*/
LOG_ROTATE_OVERWRITE = 0x01,
/*
Automatically do a log rotatation if a log stream reaches a preset
size limit.
*/
LOG_ROTATE_SIZE_LIMIT = 0x10,
};
typedef struct log_backend_t {
int (*init)(struct log_backend_t *log, int flags, size_t sizelimit);
void (*cleanup)(struct log_backend_t *log);
int (*write)(struct log_backend_t *log, const syslog_msg_t *msg);
void (*rotate)(struct log_backend_t *log);
} log_backend_t;
extern log_backend_t *logmgr;
/*
Parse a message string received from the syslog socket and produce
a split up representation for the message.
*/
int syslog_msg_parse(syslog_msg_t *msg, char *str);
#endif /* LOGFILE_H */