mirror of
https://github.com/pygos/init.git
synced 2024-11-24 20:10:42 +01:00
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:
parent
d4ce928fc0
commit
acd09007a1
17 changed files with 10 additions and 1498 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -20,9 +20,6 @@ reboot
|
|||
shutdown
|
||||
killall5
|
||||
runsvc
|
||||
syslog
|
||||
usyslogd
|
||||
klogd
|
||||
gcrond
|
||||
|
||||
services/sigkill
|
||||
|
|
|
@ -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
|
||||
|
|
19
README.md
19
README.md
|
@ -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.
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
47
cmd/syslog.1
47
cmd/syslog.1
|
@ -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.
|
223
cmd/syslog.c
223
cmd/syslog.c
|
@ -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;
|
||||
}
|
||||
|
18
configure.ac
18
configure.ac
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
103
docs/usyslogd.md
103
docs/usyslogd.md
|
@ -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.
|
|
@ -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
|
||||
|
|
|
@ -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
|
184
syslogd/klogd.c
184
syslogd/klogd.c
|
@ -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;
|
||||
}
|
|
@ -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;
|
194
syslogd/proto.c
194
syslogd/proto.c
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
Loading…
Reference in a new issue