[PATCH] Add a minimal sylogd to systemd
William Douglas
william.douglas at intel.com
Wed Jun 15 14:28:23 PDT 2011
For minimal distributions it is useful for systemd to have a
syslogd as this avoids the need for extra packages
(cron, rsyslog, syslog-ng, logrotate).
This also implements nice things like using SCM_CREDENTIALS and
SO_TIMESTAMP to get an accurate process id and time when possible.
It also allows configurable log rotation schemes.
---
Makefile.am | 35 +-
man/systemd-syslog.conf.xml | 143 +++
man/systemd-syslogd.xml | 85 ++
src/special.h | 4 +-
src/syslog.conf | 58 ++
src/syslogd.c | 1661 +++++++++++++++++++++++++++++++++
src/syslogd.h | 50 +
units/syslog-bridge.socket | 2 +
units/syslogd.socket | 23 +
units/syslogd.target | 19 +
units/systemd-kmsg-syslogd.service.in | 2 +
units/systemd-logger.service.in | 6 +-
units/systemd-syslogd.service.in | 19 +
13 files changed, 2094 insertions(+), 13 deletions(-)
create mode 100644 man/systemd-syslog.conf.xml
create mode 100644 man/systemd-syslogd.xml
create mode 100644 src/syslog.conf
create mode 100644 src/syslogd.c
create mode 100644 src/syslogd.h
create mode 100644 units/syslogd.socket
create mode 100644 units/syslogd.target
create mode 100644 units/systemd-syslogd.service.in
diff --git a/Makefile.am b/Makefile.am
index 51a5ab1..1778282 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -45,6 +45,7 @@ systemunitdir=$(rootdir)/lib/systemd/system
AM_CPPFLAGS = \
-include $(top_builddir)/config.h \
-DSYSTEM_CONFIG_FILE=\"$(pkgsysconfdir)/system.conf\" \
+ -DSYSLOG_CONFIG_FILE=\"$(pkgsysconfdir)/syslog.conf\" \
-DSYSTEM_CONFIG_UNIT_PATH=\"$(pkgsysconfdir)/system\" \
-DSYSTEM_DATA_UNIT_PATH=\"$(systemunitdir)\" \
-DSYSTEM_SYSVINIT_PATH=\"$(SYSTEM_SYSVINIT_PATH)\" \
@@ -147,6 +148,7 @@ rootlibexec_PROGRAMS = \
systemd-modules-load \
systemd-remount-api-vfs \
systemd-kmsg-syslogd \
+ systemd-syslogd \
systemd-vconsole-setup \
systemd-reply-password \
systemd-readahead-collect \
@@ -193,7 +195,8 @@ pamlib_LTLIBRARIES = \
endif
dist_pkgsysconf_DATA = \
- src/system.conf
+ src/system.conf \
+ src/syslog.conf
dist_dbuspolicy_DATA = \
src/org.freedesktop.systemd1.conf \
@@ -265,6 +268,7 @@ dist_systemunit_DATA = \
units/systemd-logger.socket \
units/systemd-shutdownd.socket \
units/syslog-bridge.socket \
+ units/syslogd.socket \
units/dev-hugepages.automount \
units/dev-hugepages.mount \
units/dev-mqueue.automount \
@@ -285,7 +289,8 @@ dist_systemunit_DATA = \
units/quotaon.service \
units/systemd-ask-password-wall.path \
units/systemd-ask-password-console.path \
- units/syslog-bridge.target
+ units/syslog-bridge.target \
+ units/syslogd.target
if HAVE_SYSV_COMPAT
dist_systemunit_DATA += \
@@ -307,6 +312,7 @@ nodist_systemunit_DATA = \
units/systemd-shutdownd.service \
units/systemd-hostnamed.service \
units/systemd-kmsg-syslogd.service \
+ units/systemd-syslogd.service \
units/systemd-modules-load.service \
units/systemd-vconsole-setup.service \
units/systemd-remount-api-vfs.service \
@@ -354,6 +360,7 @@ EXTRA_DIST = \
units/systemd-shutdownd.service.in \
units/systemd-hostnamed.service.in \
units/systemd-kmsg-syslogd.service.in \
+ units/systemd-syslogd.service.in \
units/systemd-modules-load.service.in \
units/systemd-vconsole-setup.service.in \
units/systemd-remount-api-vfs.service.in \
@@ -608,7 +615,9 @@ MANPAGES = \
man/machine-info.5 \
man/modules-load.d.5 \
man/sysctl.d.5 \
- man/systemd-ask-password.1
+ man/systemd-ask-password.1 \
+ man/systemd-syslogd.8 \
+ man/systemd-syslog.conf.5
if ENABLE_BINFMT
MANPAGES += \
@@ -1004,6 +1013,17 @@ systemd_kmsg_syslogd_LDADD = \
libsystemd-basic.la \
libsystemd-daemon.la
+systemd_syslogd_SOURCES = \
+ src/syslogd.c \
+ src/fdset.c
+
+systemd_syslogd_CFLAGS = \
+ $(AM_CFLAGS)
+
+systemd_syslogd_LDADD = \
+ libsystemd-basic.la \
+ libsystemd-daemon.la
+
systemctl_SOURCES = \
src/systemctl.c \
src/utmp-wtmp.c \
@@ -1349,11 +1369,10 @@ endif
rm -f user && \
$(LN_S) $(pkgsysconfdir)/user user )
( cd $(DESTDIR)$(systemunitdir)/sockets.target.wants && \
- rm -f systemd-initctl.socket systemd-logger.socket systemd-shutdownd.socket syslog-bridge.socket && \
- $(LN_S) ../systemd-logger.socket systemd-logger.socket && \
+ rm -f systemd-initctl.socket systemd-shutdownd.socket syslogd.socket && \
$(LN_S) ../systemd-initctl.socket systemd-initctl.socket && \
$(LN_S) ../systemd-shutdownd.socket systemd-shutdownd.socket && \
- $(LN_S) ../syslog-bridge.socket syslog-bridge.socket )
+ $(LN_S) ../syslogd.socket syslogd.socket )
( cd $(DESTDIR)$(systemunitdir)/runlevel1.target.wants && \
rm -f systemd-update-utmp-runlevel.service && \
$(LN_S) ../systemd-update-utmp-runlevel.service systemd-update-utmp-runlevel.service )
@@ -1433,7 +1452,7 @@ endif
systemd-tmpfiles-setup.service \
systemd-sysctl.service \
systemd-ask-password-console.path \
- systemd-kmsg-syslogd.service \
+ systemd-syslogd.service \
cryptsetup.target && \
$(LN_S) ../dev-hugepages.automount dev-hugepages.automount && \
$(LN_S) ../dev-mqueue.automount dev-mqueue.automount && \
@@ -1445,7 +1464,7 @@ endif
$(LN_S) ../systemd-tmpfiles-setup.service systemd-tmpfiles-setup.service && \
$(LN_S) ../systemd-sysctl.service systemd-sysctl.service && \
$(LN_S) ../systemd-ask-password-console.path systemd-ask-password-console.path && \
- $(LN_S) ../systemd-kmsg-syslogd.service && \
+ $(LN_S) ../systemd-syslogd.service systemd-sylogd.service && \
$(LN_S) ../cryptsetup.target cryptsetup.target )
if ENABLE_BINFMT
( cd $(DESTDIR)$(systemunitdir)/sysinit.target.wants && \
diff --git a/man/systemd-syslog.conf.xml b/man/systemd-syslog.conf.xml
new file mode 100644
index 0000000..738b827
--- /dev/null
+++ b/man/systemd-syslog.conf.xml
@@ -0,0 +1,143 @@
+<?xml version="1.0"?>
+<!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+<!--
+ This file is part of systemd.
+
+ systemd 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 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+
+ Copyright (C) 2011 Intel Corp.
+ Authors:
+ William Douglas <william.douglas at intel.com>
+-->
+<refentry id="systemd-syslog.conf">
+
+ <refentryinfo>
+ <title>systemd-syslog.conf</title>
+ <productname>systemd</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>William</firstname>
+ <surname>Douglas</surname>
+ <email>william.douglas at linux.intel.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-syslog.conf</refentrytitle>
+ <manvolnum>5</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-syslog.conf</refname>
+ <refpurpose>Configuration for creation, updating and
+ rotating of system log files</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <para><filename>/etc/systemd/syslog.conf</filename></para>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-syslogd</command> uses the
+ configuration file
+ <filename>/etc/systemd/syslog.conf</filename> to describe the
+ creation, updating and rotation of system log files
+ ie <filename>/var/log/auth</filename>
+ where data sources are /dev/log and /proc/kmsg.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>Configuration Format</title>
+
+ <para>The configuration format is divided into sections
+ each having a section header and one configuration
+ item per line. Each section must contain a filename line
+ and can contain facility, priority, logcount, rotate,
+ compress and creationperms lines:</para>
+
+ <programlisting>[user]
+Filename=/var/log/messages
+CreationPerms=644
+Facility=user
+Priority=alert
+LogCount=2
+Rotate=3w
+Compress=yes</programlisting>
+
+ <refsect2>
+ <title>Type</title>
+ <variablelist>
+ <varlistentry>
+ <term><varname>Filename</varname></term>
+ <listitem><para>Filename to store messages of matching priority and facility</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Facility</varname></term>
+ <listitem><para>Facility level to log, can be a list of different facilities
+ that are space delimited. Allows for both inclusing an exlusive
+ (exclusive prefixed with !).</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Priority</varname></term>
+ <listitem><para>Priority level to log, will log all messages of equal
+ or higher priority</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>LogCount</varname></term>
+ <listitem><para>Number of log file backups to keep in rotation</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Rotate</varname></term>
+ <listitem><para>Max time or size a log file can be before it is rotated.
+ This can be specified in time (ie 5w, 13d, 88h) or size (ie 55K, 102M, 5G).
+ Supported times are hours (h), days (d), or weeks (w). Supported sizes
+ are kilobytes (K), megabytes (M), or Gigabytes (G).
+ Both are case sensitve.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>Compress</varname></term>
+ <listitem><para>Dictates if files should be compressed or not
+ when rotated.</para></listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><varname>CreationPerms</varname></term>
+ <listitem><para>What permissions the file should be created
+ with.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </refsect2>
+
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-syslogd</refentrytitle><manvolnum>8</manvolnum></citerefentry>
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/man/systemd-syslogd.xml b/man/systemd-syslogd.xml
new file mode 100644
index 0000000..2064900
--- /dev/null
+++ b/man/systemd-syslogd.xml
@@ -0,0 +1,85 @@
+<?xml version='1.0'?> <!--*-nxml-*-->
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
+
+<!--
+ This file is part of systemd.
+
+ systemd 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 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+
+ Copyright (C) 2011 Intel Corp.
+ Authors:
+ William Douglas <william.douglas at intel.com>
+-->
+
+<refentry id="systemd-syslogd">
+
+ <refentryinfo>
+ <title>systemd-syslogd</title>
+ <productname>systemd</productname>
+
+ <authorgroup>
+ <author>
+ <contrib>Developer</contrib>
+ <firstname>William</firstname>
+ <surname>Douglas</surname>
+ <email>william.douglas at linux.intel.com</email>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+
+ <refmeta>
+ <refentrytitle>systemd-syslogd</refentrytitle>
+ <manvolnum>8</manvolnum>
+ </refmeta>
+
+ <refnamediv>
+ <refname>systemd-syslogd</refname>
+ <refpurpose>A syslogd and klogd for systemd.</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <cmdsynopsis>
+ <command>systemd-syslogd</command>
+ </cmdsynopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+ <title>Description</title>
+
+ <para><command>systemd-syslogd</command> creates,
+ updates and rotates log data from /dev/log and
+ /proc/kmsg based on the configuration from
+ <filename>systemd-syslog.conf</filename>. See
+ <citerefentry><refentrytitle>systemd-syslog.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>
+ for more details on these files.</para>
+
+ </refsect1>
+
+ <refsect1>
+ <title>Exit status</title>
+
+ <para>On success 0 is returned, a non-zero failure
+ code otherwise.</para>
+ </refsect1>
+
+ <refsect1>
+ <title>See Also</title>
+ <para>
+ <citerefentry><refentrytitle>systemd</refentrytitle><manvolnum>1</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>systemd-syslog.conf</refentrytitle><manvolnum>5</manvolnum></citerefentry>,
+ </para>
+ </refsect1>
+
+</refentry>
diff --git a/src/special.h b/src/special.h
index 3d15fd6..2cedce9 100644
--- a/src/special.h
+++ b/src/special.h
@@ -53,7 +53,7 @@
#define SPECIAL_NETWORK_TARGET "network.target" /* LSB's $network */
#define SPECIAL_NSS_LOOKUP_TARGET "nss-lookup.target" /* LSB's $named */
#define SPECIAL_RPCBIND_TARGET "rpcbind.target" /* LSB's $portmap */
-#define SPECIAL_SYSLOG_TARGET "syslog-bridge.target" /* LSB's $syslog; Should pull in syslog-bridge.socket or syslog.service */
+#define SPECIAL_SYSLOG_TARGET "syslogd.target" /* LSB's $syslog; Should pull in syslog.socket or syslog.service */
#define SPECIAL_TIME_SYNC_TARGET "time-sync.target" /* LSB's $time */
#define SPECIAL_DISPLAY_MANAGER_SERVICE "display-manager.service" /* Debian's $x-display-manager */
#define SPECIAL_MAIL_TRANSFER_AGENT_TARGET "mail-transfer-agent.target" /* Debian's $mail-{transport|transfer-agent */
@@ -69,7 +69,7 @@
#define SPECIAL_DBUS_SERVICE "dbus.service"
#define SPECIAL_DBUS_SOCKET "dbus.socket"
#define SPECIAL_LOGGER_SOCKET "systemd-logger.socket"
-#define SPECIAL_SYSLOG_SOCKET "syslog-bridge.socket"
+#define SPECIAL_SYSLOG_SOCKET "syslogd.socket"
/* Magic init signals */
#define SPECIAL_KBREQUEST_TARGET "kbrequest.target"
diff --git a/src/syslog.conf b/src/syslog.conf
new file mode 100644
index 0000000..73c9542
--- /dev/null
+++ b/src/syslog.conf
@@ -0,0 +1,58 @@
+# This file is part of systemd.
+#
+# systemd 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 2 of the License, or
+# (at your option) any later version.
+#
+# See systemd-syslog.conf(5) for details
+
+[kern]
+Filename=/var/log/kern
+Facility=kern
+
+[user]
+Filename=/var/log/user
+Facility=user
+
+[mail]
+Filename=/var/log/mail
+Facility=mail
+
+[daemon]
+Filename=/var/log/daemon
+Facility=daemon
+
+[auth]
+Filename=/var/log/auth
+Facility=auth
+CreationMask=600
+
+[syslog]
+Filename=/var/log/syslog
+Facility=syslog
+
+[lpr]
+Filename=/var/log/lpr
+Facility=lpr
+
+[news]
+Filename=/var/log/news
+Facility=news
+
+[uucp]
+Filename=/var/log/uucp
+Facility=uucp
+
+[cron]
+Filename=/var/log/cron
+Facility=cron
+
+[authpriv]
+Filename=/var/log/authpriv
+Facility=authpriv
+CreationMask=600
+
+[ftp]
+Filename=/var/log/ftp
+Facility=ftp
diff --git a/src/syslogd.c b/src/syslogd.c
new file mode 100644
index 0000000..7ef26aa
--- /dev/null
+++ b/src/syslogd.c
@@ -0,0 +1,1661 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ systemd 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 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+
+ Copyright (C) 2011 Intel Corp.
+ Authors:
+ William Douglas <william.douglas at intel.com>
+***/
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <time.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/poll.h>
+#include <sys/epoll.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <sys/signalfd.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include <pthread.h>
+
+#include "conf-parser.h"
+#include "util.h"
+#include "log.h"
+#include "macro.h"
+#include "list.h"
+#include "sd-daemon.h"
+#include "fdset.h"
+#include "hashmap.h"
+#include "syslogd.h"
+
+typedef struct Handle Handle;
+
+typedef struct SyslogSection SyslogSection;
+
+struct SyslogSection {
+ /* mutex should be held whenever changes are being made to the log file
+ or when fsize is being updated */
+ pthread_mutex_t file_mtx;
+ long long fsize;
+ char *section;
+ char *filename;
+ unsigned int facility;
+ unsigned int creation_perms;
+ int priority;
+ char rotate_type;
+ int compress;
+ int log_count;
+ union {
+ time_t time;
+ long long size;
+ } rotate;
+};
+
+typedef struct Server {
+ FDSet *syslog_fds;
+ int epoll_fd;
+ int signal_fd;
+
+ LIST_HEAD(Handle, handles);
+ unsigned int n_handles;
+} Server;
+
+struct Handle {
+ Server *server;
+ int fd;
+
+ LIST_FIELDS(Handle, handle);
+};
+
+static SyslogSection **syslog_conf[LOG_NFACILITIES];
+
+static SyslogSection *syslog_rotate = NULL;
+
+static Hashmap *section_conf = NULL;
+
+static pthread_mutex_t rotate_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+enum MESSAGE_FIELDS {
+ PRI_FAC,
+ TIMESTAMP,
+ PROCESS,
+ PID,
+ MESSAGE,
+ VSPACE
+};
+
+static void debug_print(char *s) {
+ int fd;
+
+ fd = open("/tmp/test4", O_WRONLY | O_CREAT | O_APPEND, 0);
+ write(fd, s, strlen(s));
+ close(fd);
+}
+
+static int prio_sort(const void *sys1, const void *sys2) {
+ /* dereference in cast as we are getting
+ pointers to pointers to SyslogSection */
+ SyslogSection *s1 = *(SyslogSection **) sys1;
+ SyslogSection *s2 = *(SyslogSection **) sys2;
+
+ /* note we want descending order so return negative
+ if s1 is greater and positive if s2 is greater */
+ if (s1->priority > s2->priority)
+ return -1;
+ else if (s1->priority < s2->priority)
+ return 1;
+
+ return 0;
+}
+
+static void section_init(SyslogSection *s)
+{
+ s->section = NULL;
+ s->filename = NULL;
+ s->rotate_type = -1;
+ s->facility = 0;
+ s->priority = -1;
+ s->log_count = -1;
+ s->compress = -1;
+ s->creation_perms = UINT_MAX;
+}
+
+static void handle_free(Handle *h) {
+ assert(h);
+
+ if (h->server) {
+ assert(h->server->n_handles > 0);
+ h->server->n_handles--;
+ LIST_REMOVE(Handle, handle, h->server->handles, h);
+ }
+
+ if (h->fd >= 0) {
+ if (h->server)
+ epoll_ctl(h->server->epoll_fd, EPOLL_CTL_DEL, h->fd, NULL);
+
+ close_nointr_nofail(h->fd);
+ }
+
+ free(h);
+}
+
+static void free_syslog_conf(void) {
+ SyslogSection *sys;
+ int i;
+ Iterator it;
+
+ HASHMAP_FOREACH(sys, section_conf, it) {
+ free(sys->section);
+ free(sys->filename);
+ pthread_mutex_destroy(&sys->file_mtx);
+ free(sys);
+ }
+ hashmap_free(section_conf);
+
+ for (i = 0; i < LOG_NFACILITIES; i++) {
+ free(syslog_conf[i]);
+ }
+}
+
+static void server_done(Server *s) {
+ sigset_t mask;
+
+ assert(s);
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ sigprocmask(SIG_SETMASK, &mask, NULL);
+ pthread_mutex_lock(&rotate_mtx);
+ while (s->handles)
+ handle_free(s->handles);
+
+ if (s->epoll_fd >= 0)
+ close_nointr_nofail(s->epoll_fd);
+ if (s->signal_fd >= 0)
+ close_nointr_nofail(s->signal_fd);
+ if (s->syslog_fds)
+ fdset_free(s->syslog_fds);
+ free_syslog_conf();
+}
+
+static int server_init(Server *s, unsigned n_handles) {
+ int r;
+ unsigned i;
+ struct epoll_event ev;
+ sigset_t mask;
+ Handle *h;
+
+ assert(s);
+ assert(n_handles > 0);
+
+ zero(*s);
+ s->n_handles = 0;
+
+ if ((s->epoll_fd = epoll_create1(EPOLL_CLOEXEC)) < 0) {
+ r = -errno;
+ log_error("Failed to create epoll object: %s", strerror(errno));
+ goto fail;
+ }
+
+ if (!(s->syslog_fds = fdset_new())) {
+ r = -ENOMEM;
+ log_error("Failed to allocate file descriptor set: %s", strerror(errno));
+ goto fail;
+ }
+
+ for (i = 0; i < n_handles; i++) {
+ int fd, one = 1;
+
+ fd = SD_LISTEN_FDS_START+i;
+
+ if (!(h = new0(Handle, 1))) {
+ r = -ENOMEM;
+ log_error("Failed to create handle object: %s", strerror(errno));
+ goto fail;
+ }
+
+ zero(ev);
+ ev.events = EPOLLIN;
+
+ if ((r = sd_is_socket(fd, AF_UNSPEC, SOCK_DGRAM, -1)) < 0) {
+ log_error("Failed to determine file descriptor type: %s", strerror(-r));
+ goto fail;
+ }
+ if (r) {
+ if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0)
+ log_error("SO_PASSCRED failed: %m");
+
+ if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0)
+ log_error("SO_TIMESTAMP failed: %m");
+
+ ev.data.fd = fd;
+ if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+ r = -errno;
+ log_error("Failed to add server fd to epoll object: %s", strerror(errno));
+ goto fail;
+ }
+
+ if ((r = fdset_put(s->syslog_fds, fd)) < 0) {
+ log_error("Failed to store file descriptor in set: %s", strerror(-r));
+ goto fail;
+ }
+
+ goto attach;
+ }
+
+ if ((r = sd_is_special(fd, NULL)) < 0) {
+ log_error("Failed to determine file descriptor type: %s", strerror(-r));
+ goto fail;
+ }
+ if (r) {
+ h->fd = -1;
+ ev.data.fd = fd;
+
+ if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
+ r = -errno;
+ log_error("Failed to add server fd to epoll object: %s", strerror(errno));
+ goto fail;
+ }
+
+ goto attach;
+ }
+ /* fd didn't match a supported type */
+ goto fail;
+
+ attach:
+ h->fd = fd;
+ LIST_PREPEND(Handle, handle, s->handles, h);
+ s->handles->server = s;
+ s->n_handles++;
+ }
+
+ assert_se(sigemptyset(&mask) == 0);
+ sigset_add_many(&mask, SIGINT, SIGTERM, -1);
+ assert_se(sigprocmask(SIG_SETMASK, &mask, NULL) == 0);
+
+ if ((s->signal_fd = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC)) < 0) {
+ log_error("signalfd(): %m");
+ return -errno;
+ }
+
+ zero(ev);
+ ev.events = EPOLLIN;
+ ev.data.fd = s->signal_fd;
+
+ if (epoll_ctl(s->epoll_fd, EPOLL_CTL_ADD, s->signal_fd, &ev) < 0) {
+ log_error("epoll_ctl(): %m");
+ return -errno;
+ }
+
+ return 0;
+
+fail:
+ server_done(s);
+ return r;
+}
+
+static char *get_new_fname(char *old_name, int num) {
+ char *c1, *c2;
+ int t;
+ char *new_fname = NULL, *old_copy = NULL, *ext = NULL, *e = NULL;
+
+ if (!(old_copy = strdup(old_name))) {
+ log_error("No memory");
+ return NULL;
+ }
+
+ c2 = c1 = old_copy + strlen(old_copy) - 1;
+
+ t = strtol(c1, &e, 10);
+
+ while(c1 > old_copy && strncmp(c1, ".", 1))
+ c1--;
+
+ if (c1 == old_copy) {
+ log_error("Log file %s name format error", old_copy);
+ goto get_new_name_cleanup;
+ }
+
+ if (e != c2) {
+ /* file ends with number */
+ *c1 = 0;
+ if (asprintf(&new_fname, "%s.%d", old_copy, num) < 0) {
+ new_fname = NULL;
+ log_error("No memory");
+ }
+ } else {
+ /* file ends with extension */
+ c2 = c1 - 1;
+ c1++;
+ if (!(ext = strdup(c1))) {
+ log_error("No memory");
+ goto get_new_name_cleanup;
+ }
+
+ while(c2 > old_copy && strncmp(c2, ".", 1))
+ c2--;
+ if (c2 == old_copy) {
+ log_error("Log file %s name format error", old_copy);
+ goto get_new_name_cleanup;
+ }
+
+ *c2 = 0;
+
+ if (asprintf(&new_fname, "%s.%d.%s", old_copy, num, ext) < 0) {
+ new_fname = NULL;
+ log_error("No memory");
+ }
+ }
+
+get_new_name_cleanup:
+ free(old_copy);
+ free(ext);
+
+ return new_fname;
+}
+
+static int get_old_fname(SyslogSection *sys, int num, char **fname) {
+ struct stat st;
+ char *ofname = NULL;
+ char ext[] = DEFAULT_SYSLOGD_EXTENSION;
+
+ if (asprintf(&ofname, "%s.%d", sys->filename, num) < 0) {
+ log_error("No memory");
+ return -ENOMEM;
+ }
+
+ if (ofname && !stat(ofname, &st)) {
+ *fname = ofname;
+ return 0;
+ }
+
+ free(ofname);
+ ofname = NULL;
+
+ if (asprintf(&ofname, "%s.%d.%s", sys->filename, num, ext) < 0) {
+ log_error("No memory");
+ return -ENOMEM;
+ }
+
+ if (ofname && !stat(ofname, &st)) {
+ *fname = ofname;
+ return 0;
+ }
+
+ free(ofname);
+ return -ENOENT;
+}
+
+static int move_file(SyslogSection *sys, int num) {
+ char *fullname = NULL, *newname = NULL;
+ int r = 0;
+
+ if ((r = get_old_fname(sys, num, &fullname)) < 0) {
+ if (r == -ENOENT) {
+ r = 0;
+ goto move_file_cleanup;
+ }
+ goto move_file_cleanup;
+ }
+
+ if (num == sys->log_count) {
+ if (pthread_mutex_trylock(&sys->file_mtx)) {
+ unlink(fullname);
+ pthread_mutex_unlock(&sys->file_mtx);
+ }
+ } else {
+ if (!(newname = get_new_fname(fullname, num + 1))) {
+ r = -1;
+ goto move_file_cleanup;
+ }
+
+ if (rename(fullname, newname)) {
+ log_error("Couldn't move file %s to %s", fullname, newname);
+ r = -1;
+ goto move_file_cleanup;
+ }
+ }
+
+move_file_cleanup:
+ free(newname);
+ free(fullname);
+ newname = NULL;
+ fullname = NULL;
+ return r;
+}
+
+static void do_compress(char *filename)
+{
+ int rc, st;
+ char cmd[] = DEFAULT_SYSLOGD_COMPRESS_COMMAND;
+ pid_t pid = fork();
+
+ if (pid == 0)
+ execl(cmd, cmd, filename, (char *) NULL);
+ else if (pid > 0)
+ rc = waitpid(pid, &st, 0);
+}
+
+static time_t first_rotate_time(void) {
+ Iterator it;
+ SyslogSection *sys = NULL;
+ time_t shortest, left;
+ struct stat st;
+ struct timeval tv;
+
+ shortest = ~(shortest & 0);
+ shortest ^= 1 << (sizeof(time_t) * 8 - 1);
+
+ if (gettimeofday(&tv, NULL) < 0)
+ return DEFAULT_SYSLOGD_RTIMER;
+
+ HASHMAP_FOREACH(sys, section_conf, it) {
+ if (sys->rotate_type == 't') {
+ if (stat(sys->filename, &st) < 0)
+ continue;
+
+ left = st.st_ctime - (tv.tv_sec - sys->rotate.time);
+ if (shortest > left) {
+ shortest = left;
+ syslog_rotate = sys;
+ }
+ }
+ }
+
+ if (shortest <= 0)
+ shortest = 1;
+
+ return shortest;
+}
+
+static void rotate_time_handler(int sig, siginfo_t *si, void *uc);
+
+static int setup_log_rotate(void) {
+ timer_t timerid;
+ struct sigevent sev;
+ struct itimerspec its;
+ struct sigaction sa;
+
+ sa.sa_sigaction = rotate_time_handler;
+ sigemptyset(&sa.sa_mask);
+
+ its.it_value.tv_sec = first_rotate_time();
+ if (sigaction(SIGUSR1, &sa, NULL) == -1) {
+ log_error("Couldn't set rotate signal action");
+ return -1;
+ }
+ sev.sigev_signo = SIGUSR1;
+
+ sev.sigev_notify = SIGEV_SIGNAL;
+ sev.sigev_value.sival_ptr = &timerid;
+ if (timer_create(CLOCK_MONOTONIC, &sev, &timerid) == -1) {
+ log_error("Couldn't create timer");
+ return -1;
+ }
+
+ if (timer_settime(timerid, 0, &its, NULL) == -1) {
+ log_error("Couldn't set timer");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void do_rotate(SyslogSection *sys) {
+ char *fullname = NULL, *newname = NULL;
+ int num;
+
+ for (num = sys->log_count; num > 0; num--)
+ if (move_file(sys, num))
+ return;
+
+ if (!sys->log_count) {
+ if (pthread_mutex_trylock(&sys->file_mtx)) {
+ unlink(sys->filename);
+ sys->fsize = 0;
+ pthread_mutex_unlock(&sys->file_mtx);
+ }
+ return;
+ }
+
+ if (asprintf(&newname, "%s.1", sys->filename) < 0) {
+ log_error("No memory");
+ return;
+ }
+
+ if (!pthread_mutex_trylock(&sys->file_mtx))
+ return;
+
+ if (rename(sys->filename, newname)) {
+ log_error("Couldn't move file %s to %s", fullname, newname);
+ pthread_mutex_unlock(&sys->file_mtx);
+ return;
+ }
+
+ sys->fsize = 0;
+
+ pthread_mutex_unlock(&sys->file_mtx);
+
+ if (sys->compress)
+ do_compress(newname);
+
+ free(newname);
+}
+
+static void *rotate_size(void *sys) {
+ pthread_mutex_lock(&rotate_mtx);
+
+ do_rotate((SyslogSection *) sys);
+
+ pthread_mutex_unlock(&rotate_mtx);
+ return NULL;
+}
+
+static void rotate_size_start(SyslogSection *sys) {
+ pthread_t thrd;
+
+ if (pthread_create(&thrd, NULL, rotate_size, sys)) {
+ log_error("Couldn't start up time log rotation thread");
+ }
+}
+
+static void *rotate_time(void *vp) {
+ pthread_mutex_lock(&rotate_mtx);
+
+ if (syslog_rotate)
+ do_rotate(syslog_rotate);
+ syslog_rotate = NULL;
+ setup_log_rotate();
+
+ pthread_mutex_unlock(&rotate_mtx);
+ return NULL;
+}
+
+static void rotate_time_handler(int sig, siginfo_t *si, void *uc) {
+ pthread_t thrd;
+
+ if (pthread_create(&thrd, NULL, rotate_time, NULL)) {
+ log_error("Couldn't start up time log rotation thread");
+ }
+}
+
+static void initialize_fsize(void) {
+ Iterator it;
+ SyslogSection *sys = NULL;
+ struct stat st;
+
+ HASHMAP_FOREACH(sys, section_conf, it) {
+ if (sys->rotate_type == 's') {
+ if (stat(sys->filename, &st) < 0)
+ sys->fsize = 0;
+
+ sys->fsize = st.st_size;
+ }
+ }
+}
+
+static void skip_date(const char **buf) {
+ enum {
+ LETTER,
+ SPACE,
+ NUMBER,
+ SPACE_OR_NUMBER,
+ COLON
+ } sequence[] = {
+ LETTER, LETTER, LETTER,
+ SPACE,
+ SPACE_OR_NUMBER, NUMBER,
+ SPACE,
+ SPACE_OR_NUMBER, NUMBER,
+ COLON,
+ SPACE_OR_NUMBER, NUMBER,
+ COLON,
+ SPACE_OR_NUMBER, NUMBER,
+ SPACE
+ };
+
+ const char *p;
+ unsigned i;
+
+ assert(buf);
+ assert(*buf);
+
+ p = *buf;
+
+ for (i = 0; i < ELEMENTSOF(sequence); i++, p++) {
+
+ if (!*p)
+ return;
+
+ switch (sequence[i]) {
+
+ case SPACE:
+ if (*p != ' ')
+ return;
+ break;
+
+ case SPACE_OR_NUMBER:
+ if (*p == ' ')
+ break;
+
+ /* fall through */
+
+ case NUMBER:
+ if (*p < '0' || *p > '9')
+ return;
+
+ break;
+
+ case LETTER:
+ if (!(*p >= 'A' && *p <= 'Z') &&
+ !(*p >= 'a' && *p <= 'z'))
+ return;
+
+ break;
+
+ case COLON:
+ if (*p != ':')
+ return;
+ break;
+
+ }
+ }
+
+ *buf = p;
+}
+
+static size_t read_ktime(const char **buf, struct iovec *iovec) {
+ const char *p;
+ size_t l;
+
+ assert(buf);
+ assert(*buf);
+ assert(iovec);
+
+ p = *buf + 1;
+ p += strspn(p, WHITESPACE);
+
+ l = strcspn(p, WHITESPACE);
+
+ if (l <= 0 ||
+ p[l-1] != ']')
+ return 0;
+
+ l += (p - *buf) + 1;
+ iovec->iov_base = (char *) *buf;
+ iovec->iov_len = l;
+
+ *buf = p + l - (p - *buf);
+
+ return 1;
+}
+
+static int read_process(const char **buf, struct iovec *iovec) {
+ const char *p;
+ size_t l;
+
+ assert(buf);
+ assert(*buf);
+ assert(iovec);
+
+ p = *buf;
+
+ p += strspn(p, WHITESPACE);
+ l = strcspn(p, WHITESPACE);
+
+ if (l <= 0 ||
+ p[l-1] != ':')
+ return 0;
+
+ l--;
+
+ if (p[l-1] == ']') {
+ size_t k = l-1;
+
+ for (;;) {
+
+ if (p[k] == '[') {
+ l = k;
+ break;
+ }
+
+ if (k == 0)
+ break;
+
+ k--;
+ }
+ }
+
+ iovec->iov_base = (char*) p;
+ iovec->iov_len = l;
+ *buf = p + l;
+ return 1;
+}
+
+static void skip_pid(const char **buf) {
+ const char *p;
+
+ assert(buf);
+ assert(*buf);
+
+ p = *buf;
+
+ if (*p != '[')
+ return;
+
+ p++;
+ p += strspn(p, "0123456789");
+
+ if (*p != ']')
+ return;
+
+ p++;
+
+ *buf = p;
+}
+
+static int write_message(const char *buf, struct iovec *iovec, int prifac) {
+ ssize_t k;
+ int r = 0, j, fd, pri, fac;
+ SyslogSection **sys_list = NULL;
+
+ fac = prifac & LOG_FACMASK;
+ pri = prifac & LOG_PRIMASK;
+
+ if (!(sys_list = syslog_conf[fac>>3]))
+ return 0;
+
+ if (*buf) {
+ IOVEC_SET_STRING(iovec[MESSAGE], buf);
+ IOVEC_SET_STRING(iovec[VSPACE], "\n");
+
+ for (j = 0; sys_list[j] && sys_list[j]->priority >= pri; j++) {
+ pthread_mutex_lock(&(sys_list[j]->file_mtx));
+
+ if ((fd = open(sys_list[j]->filename, O_WRONLY | O_CREAT | O_APPEND,
+ sys_list[j]->creation_perms)) != -1) {
+ if ((k = writev(fd, iovec, IOVEC_SYSLOG_SIZE)) <= 0) {
+ log_error("Failed to write log message to %s: %s",
+ sys_list[j]->filename, k < 0 ? strerror(errno) : "short write");
+ r = k < 0 ? -errno : -EIO;
+ } else {
+ if (sys_list[j]->rotate_type == 's') {
+ if (sys_list[j]->fsize + k < 0)
+ sys_list[j]->fsize = LLONG_MAX;
+ else
+ sys_list[j]->fsize += k;
+ }
+ }
+
+ close(fd);
+ }
+
+ if (sys_list[j]->rotate_type == 's' && sys_list[j]->rotate.size <= sys_list[j]->fsize) {
+ rotate_size_start(sys_list[j]);
+ }
+
+ pthread_mutex_unlock(&(sys_list[j]->file_mtx));
+ }
+ }
+ return r;
+}
+
+static int add_pri_fac_names(int prifac, struct iovec *iovec, char *pri_fac_names) {
+ int fac, pri;
+ SyslogSection **sys_list;
+ const char *priority = NULL;
+ const char *facility = NULL;
+
+ fac = prifac & LOG_FACMASK;
+ pri = prifac & LOG_PRIMASK;
+
+ if (!(sys_list = syslog_conf[fac>>3]))
+ return 0;
+
+ if (pri > sys_list[0]->priority)
+ return 0;
+
+ priority = log_level_to_string(pri);
+ facility = log_facility_unshifted_to_string(LOG_FAC(fac));
+ snprintf(pri_fac_names, PRI_FAC_NAME_SIZE, "%-10s:%-10s ", priority, facility);
+ IOVEC_SET_STRING(iovec[PRI_FAC], pri_fac_names);
+
+ return 1;
+}
+
+static int write_message_kmsg(const char *buf, int prifac) {
+ struct iovec iovec[IOVEC_SYSLOG_SIZE];
+ char pri_fac_names[PRI_FAC_NAME_SIZE];
+
+ assert(buf);
+
+ if (*buf == 0)
+ return 0;
+
+ if (!add_pri_fac_names(prifac, iovec, pri_fac_names))
+ return 0;
+
+ zero(iovec[TIMESTAMP]);
+ zero(iovec[PROCESS]);
+ zero(iovec[PID]);
+ return write_message(buf, iovec, prifac);
+}
+
+static int write_message_dev_log(const char *buf, struct ucred *ucred, struct timeval *timeval) {
+ char pid[16];
+ struct tm *tm;
+ struct iovec iovec[IOVEC_SYSLOG_SIZE];
+ char *process = NULL;
+ char timestamp[29];
+ int prifac = LOG_USER | LOG_INFO;
+ char pri_fac_names[PRI_FAC_NAME_SIZE];
+
+ assert(buf);
+
+ parse_syslog_priority((char**) &buf, &prifac);
+
+ if (*buf == 0)
+ return 0;
+
+ if (!add_pri_fac_names(prifac, iovec, pri_fac_names))
+ return 0;
+
+ /* Next, add the date if set */
+ skip_date(&buf);
+ if (timeval) {
+ if ((tm = localtime((const time_t *)timeval))) {
+ strftime(timestamp, sizeof(timestamp),
+ "%F %T %z ", tm);
+ char_array_0(timestamp);
+ IOVEC_SET_STRING(iovec[TIMESTAMP], timestamp);
+ }
+ } else if (!(buf[0] == '[' && read_ktime(&buf, &iovec[TIMESTAMP]))) {
+ zero(iovec[TIMESTAMP]);
+ }
+
+ /* Then, add process if set */
+ if (ucred &&
+ ucred->pid > 0 &&
+ get_process_name(ucred->pid, &process) >= 0) {
+ IOVEC_SET_STRING(iovec[PROCESS], process);
+ } else if (!read_process(&buf, &iovec[PROCESS])) {
+ zero(iovec[PROCESS]);
+ }
+
+ /* Skip the stored PID if we have a better one */
+ if (ucred) {
+ snprintf(pid, sizeof(pid), "[%lu]: ", (unsigned long) ucred->pid);
+ char_array_0(pid);
+ IOVEC_SET_STRING(iovec[PID], pid);
+
+ skip_pid(&buf);
+
+ if (*buf == ':')
+ buf++;
+
+ buf += strspn(buf, WHITESPACE);
+ } else {
+ zero(iovec[PID]);
+ }
+
+ return write_message(buf, iovec, prifac);
+}
+
+static int process_special(struct epoll_event *ev) {
+ char buf[LINE_MAX+1];
+ char *c1, *c2, *offset;
+ ssize_t n;
+ int k;
+ int prifac = -1;
+
+ c1 = offset = buf;
+ for (;;) {
+ if ((n = read(ev->data.fd, offset, sizeof(buf) - (offset - buf))) < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ break;
+
+ return -errno;
+ }
+
+ parse_syslog_priority((char **) &c1, &prifac);
+ while (prifac < 0) {
+ if (!(c2 = memchr(c1, '\n', n - (c1 - buf))))
+ break;
+ if (c2 + 1 >= buf +n)
+ break;
+ c1 = c2 + 1;
+ parse_syslog_priority((char **) &c1, &prifac);
+ }
+
+ if (prifac < 0) {
+ c1 = offset = buf;
+ continue;
+ }
+
+ while ((c2 = memchr(c1, '\n', n - (c1 - buf)))) {
+ *c2 = 0;
+
+ if ((k = write_message_kmsg(strstrip(c1), prifac)) < 0)
+ return k;
+
+ if (c2 + 1 >= buf + n)
+ break;
+
+ c1 = c2 + 1;
+ c2 = NULL;
+ parse_syslog_priority((char **) &c1, &prifac);
+ }
+
+ if (c2) {
+ c1 = offset = buf;
+ continue;
+ }
+
+ if (c1 != buf) {
+ /* we likely have a partial line, this is fun */
+ memmove(buf, c1, n - (c1 - buf));
+ offset = &buf[n - (c1 - buf)];
+ c1 = buf;
+ } else {
+ buf[n] = 0;
+ if ((k = write_message_kmsg(strstrip(buf), prifac)) < 0)
+ return k;
+ c1 = buf;
+ }
+ }
+
+ /* flush out anything we didn't print that would likely make sense */
+ if (errno == EAGAIN && buf != offset && prifac >= 0)
+ return write_message_kmsg(strstrip(buf), prifac);
+
+ return 1;
+}
+
+static int process_socket(struct epoll_event *ev) {
+ for (;;) {
+ char buf[LINE_MAX];
+ struct msghdr msghdr;
+ struct iovec iovec[1];
+ struct ucred *ucred = NULL;
+ struct timeval *timeval = NULL;
+ struct cmsghdr *cmsghdr = NULL;
+ char control[CMSG_SPACE(sizeof(struct ucred)) + CMSG_SPACE(sizeof(struct timeval))];
+ ssize_t n;
+ int k;
+ char *e;
+
+ iovec[0].iov_base = buf;
+ iovec[0].iov_len = sizeof(buf);
+
+ zero(msghdr);
+ zero(control);
+ msghdr.msg_iov = iovec;
+ msghdr.msg_iovlen = 1;
+ msghdr.msg_control = control;
+ msghdr.msg_controllen = sizeof(control);
+
+ if ((n = recvmsg(ev->data.fd, &msghdr, MSG_DONTWAIT)) < 0) {
+
+ if (errno == EINTR || errno == EAGAIN)
+ return 1;
+
+ log_error("recvmsg() failed: %m");
+ return -errno;
+ }
+
+ for (cmsghdr = CMSG_FIRSTHDR(&msghdr);
+ cmsghdr != NULL;
+ cmsghdr = CMSG_NXTHDR(&msghdr, cmsghdr)) {
+ if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct ucred)) &&
+ cmsghdr->cmsg_level == SOL_SOCKET &&
+ cmsghdr->cmsg_type == SCM_CREDENTIALS)
+ ucred = (struct ucred*) CMSG_DATA(cmsghdr);
+ else if (cmsghdr->cmsg_len == CMSG_LEN(sizeof(struct timeval)) &&
+ cmsghdr->cmsg_level == SOL_SOCKET &&
+ cmsghdr->cmsg_type == SO_TIMESTAMP)
+ timeval = (struct timeval*) CMSG_DATA(cmsghdr);
+ }
+ if ((e = memchr(buf, '\n', n)))
+ *e = 0;
+ else
+ buf[n] = 0;
+
+ if ((k = write_message_dev_log(strstrip(buf), ucred, timeval)) < 0)
+ return k;
+ }
+ return 1;
+}
+
+static int process_event(Server *s, struct epoll_event *ev) {
+ assert(s);
+
+ if (ev->events != EPOLLIN) {
+ log_info("Got invalid event from epoll.");
+ return -EIO;
+ }
+
+ if (ev->data.fd == s->signal_fd) {
+ struct signalfd_siginfo sfsi;
+ ssize_t n;
+
+ if ((n = read(s->signal_fd, &sfsi, sizeof(sfsi))) != sizeof(sfsi)) {
+
+ if (n >= 0)
+ return -EIO;
+
+ if (errno == EINTR || errno == EAGAIN)
+ return 0;
+
+ return -errno;
+ }
+
+ log_debug("Received SIG%s", strna(signal_to_string(sfsi.ssi_signo)));
+ return 0;
+
+ } else if ((sd_is_special(ev->data.fd, NULL)) > 0) {
+ return process_special(ev);
+ } else if ((sd_is_socket(ev->data.fd, AF_UNSPEC, SOCK_DGRAM, -1)) > 0) {
+ return process_socket(ev);
+ }
+ return 1;
+}
+
+static SyslogSection *section_get_or_init(Hashmap *h, const char *section)
+{
+ char *sp = NULL;
+ SyslogSection *s;
+ if (!(s = hashmap_get(h, section))) {
+ if(!(s = new(SyslogSection, 1))) {
+ return NULL;
+ }
+ section_init(s);
+ if (!(sp = strdup(section)))
+ return NULL;
+ if (hashmap_put(h, sp, s) < 0)
+ return NULL;
+ s->section = sp;
+ }
+
+ return s;
+}
+
+static int config_parse_syslog_filename(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Hashmap *h = data;
+ SyslogSection *s = NULL;
+ char *p = NULL;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (!(s = section_get_or_init(h, section)))
+ return -ENOMEM;
+
+ if (s->filename) {
+ log_warning("Section has multiple Filename lines");
+ return 0;
+ }
+
+ if (!path_is_absolute(rvalue)) {
+ log_error("[%s %u] Not an absolute path: %s", filename, line, rvalue);
+ return -EINVAL;
+ }
+
+ if (!(p = strdup(rvalue)))
+ return -ENOMEM;
+
+ path_kill_slashes(p);
+
+ s->filename = p;
+
+ return 0;
+}
+
+static int config_parse_syslog_priority(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Hashmap *h = data;
+ SyslogSection *s = NULL;
+ int i;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((i = log_level_from_string(rvalue)) < 0) {
+ log_warning("Invalid Priority value");
+ return 0;
+ }
+
+ if (!(s = section_get_or_init(h, section)))
+ return -ENOMEM;
+
+ if (s->priority < 0)
+ s->priority = i;
+ else
+ log_warning("Section has multiple Priority lines");
+
+ return 0;
+}
+
+static int config_parse_syslog_facility(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Hashmap *h = data;
+ char *fac = NULL;
+ char **facv = NULL;
+ SyslogSection *s = NULL;
+ int i, k;
+ bool invert_flag = false;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if (!(s = section_get_or_init(h, section)))
+ return -ENOMEM;
+
+ if (s->facility > 0) {
+ log_warning("Section has multiple Facility lines");
+ return 0;
+ }
+
+ if (config_parse_strv(filename, line, section, lvalue, ltype, rvalue, &facv, userdata))
+ return -ENOMEM;
+
+ k = 0;
+ while (facv[k] != NULL) {
+ if (facv[k][0] == '*') {
+ s->facility = (1 << LOG_NFACILITIES) - 1;
+ break;
+ }
+ if (facv[k][0] == '!' && !invert_flag) {
+ invert_flag = true;
+ s->facility = (1 << LOG_NFACILITIES) - 1;
+ } else if (facv[k][0] != '!' && invert_flag) {
+ k++;
+ continue;
+ }
+
+ fac = invert_flag ? facv[k]+1 : facv[k];
+ i = log_facility_unshifted_from_string(fac);
+ if (i < 0) {
+ log_warning("Invalid Facility value %s in section %s", fac, section);
+ k++;
+ continue;
+ }
+ /* set (or unset) the bit for the facility, ie LOG_KERN is
+ 0 ie first bit so 1 << 0 */
+ if (invert_flag) {
+ s->facility ^= 1 << i;
+ } else {
+ s->facility |= 1 << i;
+ }
+ k++;
+ }
+
+ for (k = 0; facv[k]; k++)
+ free(facv[k]);
+ free(facv);
+
+ return 0;
+}
+
+static int config_parse_syslog_rotate(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Hashmap *h = data;
+ SyslogSection *s;
+ char *cp, c;
+ long long l;
+ int r;
+ time_t t;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ t = ~(t & 0);
+ t ^= 1 << (sizeof(time_t) * 8 - 1);
+
+ if (!(s = section_get_or_init(h, section)))
+ return -ENOMEM;
+
+ if (s->rotate_type >= 0) {
+ log_warning("Section has multiple Rotate lines");
+ return 0;
+ }
+
+ if ((cp = strstr(rvalue, "K")) || (cp = strstr(rvalue, "M")) || (cp = strstr(rvalue, "G")) ||
+ (cp = strstr(rvalue, "h")) || (cp = strstr(rvalue, "d")) || (cp = strstr(rvalue, "w"))) {
+ c = *cp;
+ *cp = 0;
+
+ if ((r = safe_atolli(rvalue, &l)) < 0) {
+ log_warning("[%s %u] Failed to parse numeric value: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (l < 0) {
+ log_warning("Invalid Rotate value");
+ return 0;
+ }
+
+ switch (c) {
+
+ case 'K':
+ s->rotate_type = 's';
+ l *= 1000;
+ break;
+
+ case 'M':
+ s->rotate_type = 's';
+ l *= 1000000;
+ break;
+
+ case 'G':
+ s->rotate_type = 's';
+ l *= 1000000000;
+ break;
+
+ case 'h':
+ s->rotate_type = 't';
+ l *= 60 * 60;
+ break;
+
+ case 'd':
+ s->rotate_type = 't';
+ l *= 24 * 60 * 60;
+ break;
+
+ case 'w':
+ s->rotate_type = 't';
+ l *= 7 * 24 * 3600;
+ break;
+ }
+
+ if (l < 0) {
+ log_warning("Invalid Rotate value");
+ return 0;
+ }
+
+ if (s->rotate_type == 't') {
+ if (l > t) {
+ log_warning("Invalid Rotate time (too large)");
+ return 0;
+ }
+ s->rotate.time = (time_t) l;
+ } else {
+ s->rotate.size = l;
+ }
+
+ return 0;
+ }
+ log_warning("Invalid Rotate format");
+
+ return 0;
+}
+
+static int config_parse_syslog_log_count(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Hashmap *h = data;
+ SyslogSection *s = NULL;
+ int r, i;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((r = safe_atoi(rvalue, &i)) < 0) {
+ log_warning("[%s:%u] Failed to parse numeric value: %s", filename, line, rvalue);
+ return 0;
+ }
+ if (i < 0) {
+ log_warning("Invalid LogCount value");
+ return 0;
+ }
+
+ if (!(s = section_get_or_init(h, section)))
+ return -ENOMEM;
+
+ if (s->log_count < 0)
+ s->log_count = i;
+ else
+ log_warning("Section has multiple LogCount lines");
+
+ return 0;
+}
+
+static int config_parse_syslog_compress(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Hashmap *h = data;
+ SyslogSection *s = NULL;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((r = parse_boolean(rvalue)) < 0) {
+ log_warning("[%s:%u] Failed to parse boolean value: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!(s = section_get_or_init(h, section)))
+ return -ENOMEM;
+
+ if (s->compress < 0)
+ s->compress = r;
+ else
+ log_warning("Section has multiple Compress lines");
+
+ return 0;
+}
+
+static int config_parse_syslog_creation_perms(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const char *lvalue,
+ int ltype,
+ const char *rvalue,
+ void *data,
+ void *userdata) {
+ Hashmap *h = data;
+ SyslogSection *s = NULL;
+ unsigned int m;
+ int r;
+
+ assert(filename);
+ assert(lvalue);
+ assert(rvalue);
+ assert(data);
+
+ if ((r = sscanf(rvalue, "%o", &m)) < 0) {
+ log_warning("[%s:%u] Failed to parse octal value: %s", filename, line, rvalue);
+ return 0;
+ }
+
+ if (!(s = section_get_or_init(h, section)))
+ return -ENOMEM;
+
+ if (s->creation_perms == UINT_MAX)
+ s->creation_perms = m;
+ else
+ log_warning("Section has multiple CreationMask lines");
+
+ return 0;
+}
+
+static int insert_to_syslog_conf(int *facility_count) {
+ int i, syslog_count[LOG_NFACILITIES];
+ Iterator it;
+ SyslogSection *sys = NULL, **sys_list = NULL;
+
+ zero(syslog_count);
+
+ HASHMAP_FOREACH(sys, section_conf, it) {
+ for (i = 0; i < LOG_NFACILITIES; i++) {
+ if (!(sys->facility & (1 << i)))
+ continue;
+
+ /* this shouldn't happen as we created a sys_list
+ for each facility that was flagged but just in case */
+ if (!(sys_list = syslog_conf[i]))
+ return -1;
+
+ if (!sys_list[syslog_count[i]] && facility_count[i] > syslog_count[i]) {
+ sys_list[syslog_count[i]] = sys;
+ syslog_count[i]++;
+ continue;
+ } else {
+ /* this also shouldn't happen as we counted how many
+ configs use each facility but... */
+ return -1;
+ }
+ }
+ }
+
+ /* again shouldn't happen but if it does, missed one somehow... */
+ for (i = 0; i < LOG_NFACILITIES; i++)
+ if (syslog_count[i] != facility_count[i])
+ return -1;
+
+ for (i = 0; i < LOG_NFACILITIES; i++)
+ if (syslog_count[i] > 1)
+ qsort((void *) syslog_conf[i], (size_t) syslog_count[i],
+ sizeof(SyslogSection *), prio_sort);
+
+ return 0;
+}
+
+static int read_config_file(char *config_filename) {
+ SyslogSection *sys = NULL, **sys_list = NULL;
+ FILE *f;
+ int r, i, j;
+ int facility_count[LOG_NFACILITIES];
+ Iterator it;
+
+ const ConfigItem items[] = {
+ { "Filename", config_parse_syslog_filename, 0, section_conf, NULL },
+ { "Priority", config_parse_syslog_priority, 0, section_conf, NULL },
+ { "Facility", config_parse_syslog_facility, 0, section_conf, NULL },
+ { "Rotate", config_parse_syslog_rotate, 0, section_conf, NULL },
+ { "LogCount", config_parse_syslog_log_count, 0, section_conf, NULL },
+ { "Compress", config_parse_syslog_compress, 0, section_conf, NULL },
+ { "CreationPerms", config_parse_syslog_creation_perms, 0, section_conf, NULL }
+ };
+
+ if (!(f = fopen(config_filename, "r"))) {
+ log_error("Failed to open configuration file '%s': %m", config_filename);
+ return -errno;
+ }
+
+ if ((r = config_parse(config_filename, f, NULL, items, true, NULL)) < 0) {
+ log_error("Failed to parse configuration file: %s", strerror(-r));
+ return -errno;
+ }
+
+ fclose(f);
+
+ zero(facility_count);
+
+ /* now we have a hashmap based on sections but we really want things organized by facility */
+ HASHMAP_FOREACH(sys, section_conf, it) {
+ if (sys->facility == 0)
+ sys->facility = 1 << DEFAULT_SYSLOGD_FACILITY;
+ if (sys->priority < 0)
+ sys->priority = DEFAULT_SYSLOGD_PRIORITY;
+ if (sys->log_count < 0)
+ sys->log_count = DEFAULT_SYSLOGD_LOG_COUNT;
+ if (sys->rotate_type < 0) {
+ sys->rotate_type = 't';
+ sys->rotate.time = DEFAULT_SYSLOGD_ROTATE;
+ }
+ if (sys->compress < 0)
+ sys->compress = DEFAULT_SYSLOGD_COMPRESS;
+ if (!sys->filename)
+ continue;
+
+ pthread_mutex_init(&sys->file_mtx, NULL);
+
+ for (i = 0; i < LOG_NFACILITIES; i++)
+ if (sys->facility & (1 << i))
+ facility_count[i]++;
+ }
+
+ for (i = 0; i < LOG_NFACILITIES; i++) {
+ if (facility_count[i] > 0) {
+ sys_list = new(SyslogSection*, facility_count[i]+1);
+ for (j = 0; j < facility_count[i]+1; j++)
+ sys_list[j] = NULL;
+ } else
+ sys_list = NULL;
+
+ syslog_conf[i] = sys_list;
+ }
+
+ if (insert_to_syslog_conf(facility_count))
+ return -1;
+
+ return 0;
+}
+
+int main(int argc, char *argv[]) {
+ Server server;
+ int r = EXIT_FAILURE, n;
+
+ if (getppid() != 1) {
+ log_error("This program should be invoked by init only.");
+ return EXIT_FAILURE;
+ }
+
+ if (argc > 1) {
+ log_error("This program does not take arguments.");
+ return EXIT_FAILURE;
+ }
+
+ log_set_target(LOG_TARGET_SYSLOG);
+ log_parse_environment();
+ log_open();
+
+ if ((n = sd_listen_fds(true)) < 0) {
+ log_error("Failed to read listening file descriptors from environment: %s", strerror(-r));
+ return EXIT_FAILURE;
+ }
+
+ if (n <= 0 || n > SERVER_FD_MAX) {
+ log_error("No or too many file descriptors passed.");
+ return EXIT_FAILURE;
+ }
+
+ section_conf = hashmap_new(string_hash_func, string_compare_func);
+
+ if (!section_conf) {
+ log_error("Out of memory");
+ return EXIT_FAILURE;
+ }
+
+ if (read_config_file((char *) SYSLOG_CONFIG_FILE) < 0) {
+ log_error("Unable to parse config file");
+ return EXIT_FAILURE;
+ }
+
+ initialize_fsize();
+
+ if (setup_log_rotate() < 0)
+ return EXIT_FAILURE;
+
+ if (server_init(&server, (unsigned) n) < 0)
+ return EXIT_FAILURE;
+
+ log_debug("systemd-syslogd running as pid %lu", (unsigned long) getpid());
+
+ sd_notify(false,
+ "READY=1\n"
+ "STATUS=Processing messages...");
+
+ for (;;) {
+ struct epoll_event event;
+ int k;
+
+ if ((k = epoll_wait(server.epoll_fd, &event, 1, -1)) < 0) {
+
+ if (errno == EINTR)
+ continue;
+
+ log_error("epoll_wait() failed: %m");
+ goto fail;
+ }
+
+ if (k <= 0)
+ break;
+
+ if ((k = process_event(&server, &event)) < 0) {
+ goto fail;
+ }
+
+ if (k == 0)
+ break;
+ }
+
+ r = EXIT_SUCCESS;
+
+ log_debug("systemd-syslogd stopped as pid %lu", (unsigned long) getpid());
+
+fail:
+ sd_notify(false,
+ "STATUS=Shutting down...");
+
+ server_done(&server);
+
+ return r;
+}
diff --git a/src/syslogd.h b/src/syslogd.h
new file mode 100644
index 0000000..f24cbc4
--- /dev/null
+++ b/src/syslogd.h
@@ -0,0 +1,50 @@
+/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
+
+/***
+ This file is part of systemd.
+
+ systemd 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 2 of the License, or
+ (at your option) any later version.
+
+ systemd 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 systemd; If not, see <http://www.gnu.org/licenses/>.
+
+ Copyright (C) 2011 Intel Corp.
+ Authors:
+ William Douglas <william.douglas at intel.com>
+***/
+
+#ifndef SYSTEMD_SYSLOGD_H
+#define SYSTEMD_SYSLOGD_H
+
+#define SERVER_FD_MAX 16
+
+#define PRI_FAC_NAME_SIZE 25
+
+#define IOVEC_SYSLOG_SIZE 6
+
+#define DEFAULT_SYSLOGD_RTIMER 1200
+
+#define DEFAULT_SYSLOGD_FACILITY LOG_USER >> 3
+
+#define DEFAULT_SYSLOGD_PRIORITY LOG_ALERT
+
+#define DEFAULT_SYSLOGD_LOG_COUNT 3
+
+/* 4 weeks */
+#define DEFAULT_SYSLOGD_ROTATE 4 * 7 * 24 * 3600
+
+#define DEFAULT_SYSLOGD_COMPRESS 1
+
+#define DEFAULT_SYSLOGD_COMPRESS_COMMAND "/bin/gzip"
+
+#define DEFAULT_SYSLOGD_EXTENSION "gz"
+
+#endif
diff --git a/units/syslog-bridge.socket b/units/syslog-bridge.socket
index ed86c00..770a909 100644
--- a/units/syslog-bridge.socket
+++ b/units/syslog-bridge.socket
@@ -11,6 +11,8 @@
Description=Syslog Bridge Socket
DefaultDependencies=no
Before=sockets.target syslog-bridge.target
+After=syslogd.socket
+Conflicts=syslogd.socket
# Pull in syslog-bridge.target to tell people that /dev/log is now accessible
Wants=syslog-bridge.target
diff --git a/units/syslogd.socket b/units/syslogd.socket
new file mode 100644
index 0000000..915b122
--- /dev/null
+++ b/units/syslogd.socket
@@ -0,0 +1,23 @@
+# This file is part of systemd.
+#
+# systemd 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 2 of the License, or
+# (at your option) any later version.
+
+# See systemd.special(7) for details
+
+[Unit]
+Description=Syslogd Socket
+DefaultDependencies=no
+Before=sockets.target syslogd.target syslog-bridge.socket
+Conflicts=syslog-bridge.socket
+
+# Pull in syslog.target to tell people that /dev/log is now accessible
+Wants=syslogd.target
+
+[Socket]
+ListenDatagram=/dev/log
+SocketMode=0666
+ListenSpecial=/proc/kmsg
+Service=systemd-syslogd.service
diff --git a/units/syslogd.target b/units/syslogd.target
new file mode 100644
index 0000000..55b052d
--- /dev/null
+++ b/units/syslogd.target
@@ -0,0 +1,19 @@
+# This file is part of systemd.
+#
+# systemd 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 2 of the License, or
+# (at your option) any later version.
+
+# See systemd.special(7) for details
+
+# This exists mostly for compatibility with SysV/LSB units, and
+# implementations lacking socket/bus activation.
+
+[Unit]
+Description=Syslogd
+
+# Avoid that we conflict with shutdown.target, so that we can stay
+# until the very end and do not cancel shutdown.target if we should
+# hapen to be activated very late.
+DefaultDependencies=no
diff --git a/units/systemd-kmsg-syslogd.service.in b/units/systemd-kmsg-syslogd.service.in
index 307165c..0f55998 100644
--- a/units/systemd-kmsg-syslogd.service.in
+++ b/units/systemd-kmsg-syslogd.service.in
@@ -10,6 +10,8 @@
[Unit]
Description=Syslog Kernel Log Buffer Bridge
DefaultDependencies=no
+After=systemd-syslogd.service
+Conflicts=systemd-syslogd.service
[Service]
ExecStart=@rootlibexecdir@/systemd-kmsg-syslogd
diff --git a/units/systemd-logger.service.in b/units/systemd-logger.service.in
index 23c8fc5..c9fffbd 100644
--- a/units/systemd-logger.service.in
+++ b/units/systemd-logger.service.in
@@ -8,10 +8,10 @@
# See systemd.special(7) for details
[Unit]
-Description=Stdio Syslog Bridge
+Description=Stdio Syslogd
DefaultDependencies=no
-Requires=syslog-bridge.socket
-After=syslog-bridge.socket
+Requires=syslogd.socket
+After=syslogd.socket
[Service]
ExecStart=@rootlibexecdir@/systemd-logger
diff --git a/units/systemd-syslogd.service.in b/units/systemd-syslogd.service.in
new file mode 100644
index 0000000..4656bd0
--- /dev/null
+++ b/units/systemd-syslogd.service.in
@@ -0,0 +1,19 @@
+# This file is part of systemd.
+#
+# systemd 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 2 of the License, or
+# (at your option) any later version.
+
+# See systemd.special(7) for details
+
+[Unit]
+Description=Syslog Daemon
+DefaultDependencies=no
+Before=systemd-kmsg-syslogd.service
+Conflicts=systemd-kmsg-syslogd.service
+
+[Service]
+ExecStart=@rootlibexecdir@/systemd-syslogd
+NotifyAccess=all
+Sockets=syslogd.socket
--
1.7.2.3
More information about the systemd-devel
mailing list