Debian: native systemd
I'm a happy user of Debian. I like it quite.
But there are some things I dislike. One is Debian's
policy to start automatically any installed daemon. I even wrote
a disable-services shell script to solve this problem. This script
has to be run after each package upgrade. If you need to want know
more, there's a German language description of it: "Debian: Startup-Scripte disablen".
As there was much talk about systemd. Let's see how this works.
Debian's systemd.deb
The first stop is, of course, the package provided by Debian itself.
I installed it, made sure that I had init=/bin/systemd on the
kernel command line ... and it worked like a charm.
It was not noticeable faster. That's because Debian's systemd honors
/etc/rcS.d/ and /etc/rc2.d/. Every file in those directories are
made implicitly into systemd services and started. But all of those
files use lots of shell script magic, which make them slow.
The only good thing now is that I don't need the disable-services
script anymore. Because I can disable any of them with a simply
symlink for good:
cd /etc/systemd/system
ln -s /dev/null openbsd-inetd.service
Nice so far. But I thought "When I'm already using systemd, why don't I use it totally".
Compiling my own
Systemd from GIT
So first I uninstalled (even purged) Debian's systemd and
systemd-sysv packages.
Then I thought "If I'm going bleeding edge, than make it all the way" and decided to get systemd from GIT. Visiting http://cgit.freedesktop.org/systemd/ told me all I needed was
git clone git://anongit.freedesktop.org/systemd
For your reference: I wrote this article using systemd version
v32-1-g71092d7 (output of git describe).
Configuration
Now you need to run ./autogen.sh. This script generates configure
and calls it. It's even possible to already specify configure options,
so I started it this way:
./autogen.sh \
--prefix=/usr \
--with-udevrulesdir=/lib/udev/rules.d \
--with-distro=debian \
--with-sysvinit-path= \
--with-sysvrcd-path= \
--disable-selinux \
--disable-libcryptsetup \
--disable-audit \
--disable-tcpwrap \
--disable-plymouth
- the first few options just set standard directory paths
--with-sysvinit-path=and--with-sysvrcd-path=define two empty paths. If they are both empty, then systemd won't include any legacy sysv code. It will completely ignore your/etc/rcS/and/etc/rcS/directories. Just like I want it.--disable-selinuxdisables SELINUX. I haven't included it in my kernel anyway and the systemd mailing list contains hints that enabling this makes systemd use up much more memory.--disable-cryptsetupdisables supporting code for encrypted hard disks. I don't use this at home.--disable-audit: somebody thought that systemd should emit a red nasty error message when it doesn't find audit support compiled into the kernel. This get rid of it ... and as well of all the audit code :-)--disable-tcpwrapmy linux system is behind a DSL router and no outsider can reach my system, so I have no reason for TCP wrap at all. And if, then I'd rather use port-knocking ...--disable-plymouth: Plymouth is a graphical boot setup, originally for Red Hat / Fedora, but also available for Debian. At home I'm old-fashioned and boot my system up in text mode. I want to see what's going on. So get rid of this.
I would have also added --disable-acl, because I don't use them
either. But the source of systemd is buggy, it won't compile with
ACL disabled.
But before we compile, I applied a bunch of little patches. Some are my own, some are from the debian systemd package:
Debianization
The original debian package contained some patches to systemd, which you can see here. Because I use systemd from GIT, I decided to use only 0001-Tweak-unit-files-for-Debian.patch.
Well, almost. That patch depends getty on openvpn and apache2
for no apparent reason.
Code tweaking
I also applied a patch to unclutter the output of systemd. This patch is completely optional. When I turn on debug messages, systemd emits two lines for each unit it starts:
Starting File System Check on Root Device...
...
Started File System Check on Root Device.
For every unit! And systemd has a lot of units. Way too much clutter for my taste. So I first disabled the "Started" messages.
And then thought that "File System Check on Root Device" sounds nice for an end-user. But while I'm fine tuning this new init system for my usage, I'd rather want to know the name of the service. So I made sure that I see instead:
Starting fsck-root.service
That way, should there be a problem, I know that I have to look into
the file /lib/systemd/system/fsck-root.service.
Index: systemd/src/job.c
===================================================================
--- systemd.orig/src/job.c 2011-07-09 18:56:33.754601166 +0200
+++ systemd/src/job.c 2011-07-09 18:56:59.037797531 +0200
@@ -484,7 +484,7 @@
switch (result) {
case JOB_DONE:
- unit_status_printf(u, "Started %s.\n", unit_description(u));
+ log_debug("Started %s.\n", unit_description(u));
break;
case JOB_FAILED:
Index: systemd/src/unit.c
===================================================================
--- systemd.orig/src/unit.c 2011-07-09 18:56:33.761267796 +0200
+++ systemd/src/unit.c 2011-07-09 18:56:59.037797531 +0200
@@ -903,7 +903,7 @@
unit_add_to_dbus_queue(u);
- unit_status_printf(u, "Starting %s...\n", unit_description(u));
+ unit_status_printf(u, "Starting %s\n", u->meta.id);
return UNIT_VTABLE(u)->start(u);
}
Installation
Compilation & installation is easy:
make
make install
Fine tuning
Systemd configuration
I use this /etc/systemd/system.conf file:
[Manager]
LogLevel=debug
LogTarget=syslog-or-kmsg
#LogColor=yes
#LogLocation=no
#DumpCore=yes
#CrashShell=no
ShowStatus=yes
#SysVConsole=yes
#CrashChVT=1
#CPUAffinity=1 2
MountAuto=yes
SwapAuto=yes
#DefaultControllers=cpu
#DefaultStandardOutput=inherit
#DefaultStandardError=inherit
First boot
Now make sure that the command line to the kernel contains
"init=/bin/systemd". I also want to have some status output, so
I removed the "quiet" kernel command line parameter. "quiet" is
not only honored by the linux kernel, but also by systemd.
I rebooted and ... my system booted successfully on the first try.
Success!
Fixing garbled debug output
The first problem: the debug output of systemd is garbled. The getty service from console one clears the screen and writes stuff onto the console while systemd also writes there. Both writes fight ... and no one wins.
So I temporarily disable the getty service for console 1:
cd /etc/systemd/system
ln -s /dev/null getty@tty1.service
With a newer version of systemd (from v32 on) you can also do:
systemctl mask getty@tty1.service
A reboot now shows that the debug output to be much cleaner.
Success!
Console keymap
Console setup on Debian is black magic. Yes, really. I have the slight feeling that even Debian package maintainer don't have a clue what the canonical procedure is to setup the keymap and font. Or why do so many (partially competing) packages exist for this? To name a few: console-common, console-data, console-setup, console-setup-linux, console-setup-mini, console-tools, keyboard-configuration.
The systemd developers also threw their hands into the air.
vconsole-setup.c
didn't contain any #ifdef TARGET_DEBIAN when I wrote this article.
No wonder that systemd-vconsole-setup.service spits out error
messages and can't setup my keymap.
So I first disable this beast:
cd /etc/systemd/system
ln -s /dev/null systemd-vconsole-setup.service
rm -f /lib/systemd/systemd/sysinit.target.wants/*vconsole*
Unfortunately, the rm command is necessary, otherwise you'll get annoying
error message at start up.
Then I install my own service that simply loads the boot time keymap. Store
this into /etc/systemd/systemd/boottime-kmap.service:
[Unit]
Description=Boottime keyboard map
DefaultDependencies=no
Before=sysinit.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/loadkeys -q /etc/console/boottime.kmap.gz
StandardOutput=syslog
[Install]
WantedBy=sysinit.target
and then enable it:
systemctl enable boottime-kmap.service
The systemctl enable command just reads the [Install] section of a unit
file and create the necessary symlink.
You can now start the service right away ...
systemctl start boottime-kmap.service
... or reboot. And now you'll have at least a working keymap.
Success!
Network interfaces
To bring up the network interfaces the debian way, I install this file
as /etc/systemd/system/ifupdown-clean.service:
[Unit]
Description=Clean old interface status during boot
DefaultDependencies=no
After=remount-rootfs.service
[Service]
Type=oneshot
ExecStart=-/bin/rm -f /etc/network/run/ifstate
RemainAfterExit=true
and this file as /etc/systemd/system/ifup@.service:
[Unit]
Description=Start ifup for %I
After=local-fs.target
After=ifupdown-clean.service
Wants=ifupdown-clean.service
[Service]
ExecStart=/sbin/ifup --allow=hotplug %I
ExecStop=/sbin/ifdown %I
RemainAfterExit=true
Success!
Starting cron
Currently the debian packages for udevd, rsyslogd and
dbus-daemon install systemd .service files. No other daemon on my
system has a service file. So I have to write service files for some
of them by hand.
But writing unit files is easy. Here is my unit file to start cron:
[Unit]
Description=Periodic Command Scheduler
After=syslog.target
[Service]
Type=forking
PIDFile=/var/run/crond.pid
ExecStart=/usr/sbin/cron
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
StandardOutput=syslog
[Install]
WantedBy=multi-user.target
Store it as /etc/systemd/system/cron.service and activate it via
systemctl enable cron.service
systemctl start cron.service
Success!
Starting SSH
Here I now use the power of systemd. I don't start OpenSSH at boot time. Instead, I let systemd open the TCP port 22 and watch for it. Only when a connection comes in will sshd be started. Basically there's an inetd inside systemd.
First the unit file for the TCP socket. Store it as
/etc/systemd/system/sshd.socket:
[Unit]
Conflicts=sshd.service
[Socket]
ListenStream=22
Accept=yes
[Install]
WantedBy=sockets.target
And here is the unit file for the ssh service. Store it as /etc/systemd/system/sshd@.service:
[Unit]
Description=SSH Per-Connection Server
#Requires=sshdgenkeys.service
After=syslog.target
#After=sshdgenkeys.service
[Service]
ExecStartPre=/bin/mkdir -m700 -p /var/run/sshd
ExecStart=-/usr/sbin/sshd -i
ExecReload=/bin/kill -HUP $MAINPID
StandardInput=socket
Now we activate it:
systemctl enable sshd.socket
systemctl start sshd.socket
And if you look with netstat -ntlp, you'll see the socket is open:
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp6 0 0 :::22 :::* LISTEN 1/systemd
But if you look at the output of ps, you won't yet find sshd running. Now ssh into your own box:
$ ssh localhost
root@localhost's password:
Success!
Binary formats
The last unit file for today will be the support for the linux kernel
binfmt feature. It allows one to start wine or java files directly,
because the kernel knows there signature and the needed interpreter / binary
helper.
Store this as /etc/systemd/system/binfmt-support.service:
[Unit]
Description=Support for extra binary formats
After=local-fs.target
[Service]
ExecStart=/usr/sbin/update-binfmts --enable
ExecStop=/usr/sbin/update-binfmts --disable
RemainAfterExit=true
[Install]
WantedBy=multi-user.target
and activate it:
systemctl enable binfmt-support
systemctl start binfmt-support
Now we check if it was successful:
$ ls /proc/sys/fs/binfmt_misc
-rw-r--r-- 1 root root 0 Jul 10 22:40 jar
-rw-r--r-- 1 root root 0 Jul 10 22:40 llvm.binfmt
-rw-r--r-- 1 root root 0 Jul 10 22:40 python2.5
-rw-r--r-- 1 root root 0 Jul 10 22:40 python2.6
--w------- 1 root root 0 Jul 10 22:40 register
-rw-r--r-- 1 root root 0 Jul 10 22:40 status
-rw-r--r-- 1 root root 0 Jul 10 22:40 wine
Success!
Boot analysis
Here's an SVG graph of my current boot up. I didn't do any specific optimization.