An initialization system's main interface with the user is through it's configuration scripts and service files. Let's take a look at what this looks like for systemd
and OpenRC.
With systemd, daemon configuration is handled by .toml
configuration files. With OpenRC, this task is performed by shell scripts.
Daemons and services
systemd stores service files in /etc/systemd/system/
(user-provided) or /usr/lib/systemd/system/
(package-provided). .service
files are used to declare configuration.
OpenRC stores scripts in /etc/init.d/
, the scripts are typically standalone, configuration does not require multiple separate files.
Here is the contents of /etc/systemd/system/sshd.service
on a Fedora 20 system:
[]
=OpenSSH server daemon
=syslog.target network.target auditd.service
[]
=/etc/sysconfig/sshd
=/usr/sbin/sshd-keygen
=/usr/sbin/sshd -D $OPTIONS
=/bin/kill -HUP $MAINPID
=process
=on-failure
=42s
[]
=multi-user.target
For comparison, this is an excerpt of /etc/init.d/sshd
on Funtoo Linux, not the entire file:
An important distinction to note is that while systemd's configuration is declarative, OpenRC's configuration is programmatic. The decision to use a declarative method was explained in this article under the "Keeping the First User PID Small" heading.
Shell is fast and shell is slow. It is fast to hack, but slow in execution. The classic sysvinit boot logic is modelled around shell scripts.
One of systemd's main goals, at least in regards to services, is to start them fast, and for this reason they are willing to sacrifice some flexibility. Due to successful deployments of systemd in Arch Linux and Fedora (among others), this idea has been proven feasible.
For many services, a full blown shell script is not necessary. For example, with OpenRC's sshd
script the start
and stop
jobs really only check the configuration of the service (checkconfig
is defined elsewhere in the file) and then invoke start-stop-daemon
. It's possible to simply call a shell script from a systemd service, but this forces the use of multiple files, and results in a loss of the speed gained from not utilizing the shell.
Dependencies
In systemd dependencies are handled via the declarations Wants
, Requires
, and After
in the [Unit]
section of the .service
. For more info visit Arch Linux's SystemD Dependencies Section. For example:
/etc/systemd/system/A.service
[]
# Optionally depends on `C` and `D`.
=C.service D.service
# Start after `D`, but concurrently with `C`
=D.service
/etc/systemd/system/B.service
[]
# Strictly depend on A and Networking.
=A.service Networking.target
# Start after `A`, but concurrently with `Networking`
=A.service
It's rather fascinating that Requires
and Wants
do not imply After
for their specified services. It does make sense though, many services do not immediately require their attached services.
For OpenRC, dependencies are handled by the depend
function of the script. For more info visit The Funtoo OpenRC Page.
/etc/init.d/nginx
There are five options:
need
introduces a hard dependency (likeRequire
)use
is a soft dependency which will only invoke if the depended upon service is also on the same runlevel.after
functions likeAfter
in system, making sure this service starts after the specified one.provide
can be used to allow the service to 'stand in' for another. For example, MariaDB and MySQL could bothprovide sql
.keyword
allows for overrides. Check the documentation for more on this.
Other files
Systemd uses a number of extra file types. Some examples are:
.wants
, a folder with symlinks to services that are a needed by a target. For example,/usr/lib/systemd/system/multi-user.target
has a cooresponding folder/usr/lib/systemd/system/multi-user.target.wants
with symlinks to service files likesystemd-logind.service
, andsystemd-user-sessions.service
on CentOS 7..socket
, denotes a socket activated configuration. It's possible to set up certain services in systemd to not start until their socket is activated on the system..timer
, denotes a timer based activation. This fulfills some of the same roles as programs likevixiecron
andfcron
.
OpenRC allows for files in /etc/conf.d
to be sourced for scripts of the same name in /etc/init.d
. The advantage of this is that most scripts in /etc/init.d
won't need to be modified (and thus can be updated safely). This is a collary to the Store config in the environment section of the 12factor methodology.
Encourage the separation of configuration and runtime.
Here we can see the origin of the ${SSHD_OPTS}
from the earlier example of /etc/init.d/sshd
:
/etc/conf.d/sshd:
# /etc/conf.d/sshd: config file for /etc/init.d/sshd
# Where is your sshd_config file stored?
SSHD_CONFDIR="/etc/ssh"
# Any random options you want to pass to sshd.
# See the sshd(8) manpage for more info.
SSHD_OPTS=""
# Pid file to use (needs to be absolute path).
#SSHD_PIDFILE="/var/run/sshd.pid"
# Path to the sshd binary (needs to be absolute path).
#SSHD_BINARY="/usr/sbin/sshd"
Configuring Init Itself
OpenRC is configured the same way as most traditional Linux/BSD services are configured. The default configuration on Funtoo is well commented and usually self-explanatory, even including gotchas in their notes. There are a few knobs to tweak based on user preferences.
/etc/rc.conf:
# Set to "YES" if you want the rc system to try and start services
# in parallel for a slight speed improvement. When running in parallel we
# prefix the service output with its name as the output will get
# jumbled up.
# WARNING: whilst we have improved parallel, it can still potentially lock
# the boot process. Don't file bugs about this unless you can supply
# patches that fix it without breaking other things!
rc_parallel="YES"
# rc_logger launches a logging daemon to log the entire rc process to
# /var/log/rc.log
# NOTE: Linux systems require the devfs service to be started before
# logging can take place and as such cannot log the sysinit runlevel.
rc_logger="YES"
systemd's configuration is similarly accessible in /etc/systemd/*.conf
, from there it's possible to set things like the default CPU limits for processes and other things.
/etc/systemd/system.conf:
[]
#CPUAffinity=1 2
#DefaultStandardOutput=journal
#DefaultStandardError=inherit
#JoinControllers=cpu,cpuacct net_cls,net_prio
#RuntimeWatchdogSec=0
#ShutdownWatchdogSec=10min
#CapabilityBoundingSet=
#TimerSlackNSec=
#DefaultTimeoutStartSec=90s
#DefaultTimeoutStopSec=90s
#DefaultRestartSec=100ms
#DefaultStartLimitInterval=10s
#DefaultStartLimitBurst=5
#DefaultEnvironment=
#DefaultLimitCPU=
#DefaultLimitFSIZE=
#DefaultLimitDATA=
#DefaultLimitSTACK=
#DefaultLimitCORE=
journald.conf
offers configuration options on the journal, or logger.
Thoughts
The declarative model of systemd emulates a "batteries included" model. It relies on integrated, homogeneous functionality in order to gain performance boosts and an encompassing feature set. Over the past few years the project has had a growing scope in the userland, as evidenced by the existance of things like the logind.conf
and journald.conf
, both of which used to be jobs outside of the initialization system. The regular syntax of all of these configurations does offer a boon, but at the cost of modularity and with greater complexity.
OpenRC's sysvinit based startup shows it's age, but the system's lack of opinionation offers huge benefits. With OpenRC, there is no persistent daemon, though integration with supervision tools like s6
and monit
exist. OpenRC also plays nice with *cron
. Keeping itself in a limited scope, and maintaining the "Do one thing and do it well" philosophy that gave rise to the UNIX ecosystem. It has worn it's age well, however it's reliance on old assumptions about the lifecycle of a system boot limits it's capabilities.