Bug#762700: systemd: journald fails to forward most boot messages to syslog
Christian Seiler
christian at iwakd.de
Wed Feb 25 17:59:59 GMT 2015
Control: severity -1 serious
(justification for severity: breaks unrelated software, in this case
all syslog daemons on a default installation; could possibly be
considered grave or even criticial - data loss affecting nearly all
users)
This bug is actually much worse than it seems. In fact, on a default
installation, most boot messages won't get logged via syslog. On a
relatively minimal VM, I consistently get the journal message:
Forwarding to syslog missed 15 messages
(The exact number varies a bit.) On a bare metal machine with a bit
more installed (but still no GUI, for example), I get ~60 messages
missed.
More messages might get missed during the time the system is running,
but that depends on how many messages are generated, so if it's only a
few at a time, this won't be a problem.
The reason is the following: for datagram UNIX sockets, there are two
limits that actually make a difference: SO_SNDBUF and the system-wide
sysctl net.unix.max_dgram_qlen. For SOCK_DGRAM sockets, SO_SNDBUF
relates to the max size of a single dgram, whereas the sysctl relates
to how many datagrams may be queued at all. SO_RCVBUF doesn't have any
effect on UNIX sockets.
Some observations:
- setting ReceiveBuffer=8M is a no-op here, could easily be dropped
- SendBuffer=8M will increase the max size of a single log message
that may be sent via this socket (8M is probably at bit much)
- net.unix.max_dgram_qlen defaults to 10 (!)
- any time a socket is created within the kernel, the current value
of net.unix.max_dgram_qlen is copied into the socket's data
structure within the kernel, so net.unix.max_dgram_qlen has to be
set to the chosen value BEFORE the socket is created
- net.unix.max_dgram_qlen applies to all UNIX dgram sockets in the
system
- funnily enough, the kernel can't properly count an in reality 11
(instead of 10) datagrams can be queued (> instead of >= in the
code)
- syslog daemons read kernel messages directly from /proc/kmsg (and
journal doesn't forward them), so they are not affected by this
issue
This has the following consequences:
- at boot, exactly 11 messages will be queued during the time the
system boots but the syslog daemon is not started yet
- unless you have a *really* minimal system, the number of messages
generated while booting up is typically larger than 11, at least
20-25 on even a relatively minimal VM, on a typical bare-metal
system with more stuff installed it's even higher
- all messages after those 11 all go to the journal, but are dropped
from the syslog forwarding, so syslog won't get any more messages
until it's finished starting and has consumed the first 11 messages
in the queue
- if you have a high message load, since journald might try to
forward the messages to syslog in a relatively tight loop, the
limit of 11 is easily surpassed even during runtime
Steps to reproduce:
1. boot a typical Debian Jessie installation (systemd, any syslog
daemon, journal forwarding to syslog enabled)
2. run the following command:
journalctl --quiet MESSAGE_ID=0027229ca0644181a76c4e92458afa2e
This will print a message such as:
systemd-journal[132]: Forwarding to syslog missed 35 messages.
3. run journalctl | grep -v kernel: | less to see the boot messages
4. check /var/log/syslog for other messages, and quite a few are
missing (note that by default, some do go to /var/log/auth.log
etc., but there will be some that don't appear at all in any log
that syslog writes)
Solution:
- first of all, increasing SendBuffer in the socket unit won't
really help, unless you have *reall* long log lines (default max
line len in the kernel is $((2048-32)), which corresponds to a
SendBuffer of 1024 (for some reason the value is doubled, see
man:unix(7)). So perhaps the default SendBuffer is sufficient or
maybe increase to 16K or so, but 8M is most likely more than
excessive.
- increase net.unix.max_dgram_qlen to some higher value.
This is the tricky part: max_dgram_qlen has to be set before the
syslog socket is created, so using the sysctl.d/ support is not
an option. The only easy way I found was to create service for
this that runs at the very beginning:
setup-dgram-qlen.service:
[Unit]
DefaultDependencies=no
Before=syslog.socket
[Service]
Type=oneshot
ExecStart=/sbin/sysctl -w net.unix.max_dgram_qlen=512
(should be activated statically, by a Wants= dependency from
syslog.socket)
(possibly add StandardOutput=null)
(or hack it into systemd bootup sequence after mounting /proc,
but using this unit was much simpler and can later also be
customized / masked / ... by the admin)
Note that this is now set for all sockets, and I don't know the
precise ramifications for that. (OTOH, if somebody really wanted
to DOS using this, they could easily just create 1e6 sockets in
the fs in a couple of processes, listen on them and then queue
stuff there, so I don't see how setting this limit higher will
hurt much, but I don't really have an overview here.)
Also: I don't know what a reasonable number here is. With 512 it
works, but is that better than 128? Or better than 1024? I don't
really know...
Thoughts?
Christian
More information about the Pkg-systemd-maintainers
mailing list