[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