diff --git a/.gitignore b/.gitignore index b532609..4a53b47 100644 --- a/.gitignore +++ b/.gitignore @@ -20,9 +20,6 @@ reboot shutdown killall5 runsvc -syslog -usyslogd -klogd gcrond services/sigkill diff --git a/Makefile.am b/Makefile.am index 806b777..19b79e8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/README.md b/README.md index 06ba004..305686c 100644 --- a/README.md +++ b/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. diff --git a/cmd/Makemodule.am b/cmd/Makemodule.am index 76a0e49..30e9619 100644 --- a/cmd/Makemodule.am +++ b/cmd/Makemodule.am @@ -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) diff --git a/cmd/syslog.1 b/cmd/syslog.1 deleted file mode 100644 index 382b568..0000000 --- a/cmd/syslog.1 +++ /dev/null @@ -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 . -.br -This is free software: you are free to change and redistribute it. -There is NO WARRANTY, to the extent permitted by law. diff --git a/cmd/syslog.c b/cmd/syslog.c deleted file mode 100644 index 48c9343..0000000 --- a/cmd/syslog.c +++ /dev/null @@ -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 . - */ -#include -#include -#include -#include -#include -#include -#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 Logging facilty name or numeric identifier.\n" -" -l, --level Log level name or numeric identifier.\n" -" -i, --ident 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; -} - diff --git a/configure.ac b/configure.ac index 376c76c..068940b 100644 --- a/configure.ac +++ b/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 diff --git a/docs/cmdline.md b/docs/cmdline.md index e1500aa..e47cd8e 100644 --- a/docs/cmdline.md +++ b/docs/cmdline.md @@ -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. diff --git a/docs/defconfig.md b/docs/defconfig.md index a2c790f..346e900 100644 --- a/docs/defconfig.md +++ b/docs/defconfig.md @@ -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. diff --git a/docs/usyslogd.md b/docs/usyslogd.md deleted file mode 100644 index 525c475..0000000 --- a/docs/usyslogd.md +++ /dev/null @@ -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. diff --git a/services/Makemodule.am b/services/Makemodule.am index 9aa620c..7aae6c2 100644 --- a/services/Makemodule.am +++ b/services/Makemodule.am @@ -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 diff --git a/syslogd/Makemodule.am b/syslogd/Makemodule.am deleted file mode 100644 index f6c19fd..0000000 --- a/syslogd/Makemodule.am +++ /dev/null @@ -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 diff --git a/syslogd/klogd.c b/syslogd/klogd.c deleted file mode 100644 index d2301ee..0000000 --- a/syslogd/klogd.c +++ /dev/null @@ -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 . - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#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 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; -} diff --git a/syslogd/logfile.c b/syslogd/logfile.c deleted file mode 100644 index 62e58f4..0000000 --- a/syslogd/logfile.c +++ /dev/null @@ -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 . - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#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; diff --git a/syslogd/proto.c b/syslogd/proto.c deleted file mode 100644 index 4ab2f47..0000000 --- a/syslogd/proto.c +++ /dev/null @@ -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 . - */ -#include -#include -#include -#include - -#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; -} diff --git a/syslogd/syslogd.c b/syslogd/syslogd.c deleted file mode 100644 index 3b88299..0000000 --- a/syslogd/syslogd.c +++ /dev/null @@ -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 . - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 Automatically rotate log files bigger than this.\n" -" -u, --user Run the syslog daemon as this user. If not set,\n" -" try to use the user '" DEFAULT_USER "'.\n" -" -g, --group 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; -} diff --git a/syslogd/syslogd.h b/syslogd/syslogd.h deleted file mode 100644 index 47fc31c..0000000 --- a/syslogd/syslogd.h +++ /dev/null @@ -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 . - */ -#ifndef SYSLOGD_H -#define SYSLOGD_H - - -#include -#include - -#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 */