SysV-init has been a staple of Linux for years and years. In recent versions of Fedora (current version is 18) this has changed to using systemd. I’m no stranger to alternative means for running startup commands (I did, after all, have Annvix using Annvix:User_Guide/Services runit to handle boot with a custom script called srv to handle all the service management stuff (similar to service and chkconfig).

Now I need to learn a new system which is quite a bit different from both and reminds me a lot of launchd on OS X.

Command Comparison

Out of the box, we’re used to commands like “service foo restart” or “chkconfig —add foo” and so forth. In Fedora 17, you could still use these commands and they would transparently go to the equivalent systemctl command. But some stuff doesn’t work (like “chkconfig —list” doesn’t really list the systemd service units), and it’s just plain old better to learn the real commands for the day when these wrappers are deprecated.

SysV-init systemd What it does
chkconfig —list systemctl list-units -t service —all List available services; the systemctl command however does not list targets, so unlike chkconfig we do not know what runlevel (or boot target) a specific service will start in
chkconfig X on systemctl enable X.service Enables service X so that it will start at boot; this does not start X now
chkconfig X off systemctl disable X.service Disables service X so that it will not start at boot; this does not stop X now
service X start systemctl start X.service Starts service X immediately; service provides feedback on success, but systemctl does not (although it will tell you if it fails)
service X stop systemctl stop X.service Stops service X immediately
service X restart systemctl restart X.service Restarts service X immediately
service X status systemctl status X.service or
systemctl is-active X.service
Displays the status of service X; the first command displays the path to the loaded service unit file, the process, PID, etc. The second command will simply display whether or not the service is active (good for shell scripts, etc.)
chkconfig —list | grep ‘3:on’ systemctl -p show “Wants” multi-user.target | cat Lists all services enabled for runlevel 3 (or multi-user/network/text);see #Systemctl_Show below.
telinit 3 systemctl isolate multi-user.target Switch to runlevel 3 (or multi-user.target). See #Runlevels for more details.
chkconfig —list systemctl -t service Show all loaded services (running, failed, active, inactive, etc.). Can use target, etc. as well; no arguments shows everything loaded
n/a systemctl -t service | grep running Show all currently running services

Boot Commands

With systemd, if you want to boot into the non-default runlevel (or target) you need to edit the GRUB boot command for the linux kernel and options to specify the target to boot into. The default is typically graphical.target. You can do this by appending to the boot command:

linux /vmlinuz-3.7.2-204.fc18.i686.PAE root= [...] quiet systemd.unit=multi-user.target

Other valid targets include “rescue.target” (like runlevel 1), “emergency.target” (like using init=/bin/sh). Further details can be found via man systemd.special.

Runlevels

SysV-init had the concept of runlevels, defined in /etc/inittab, for example:

# Default runlevel. The runlevels used are:
#   0 - halt (Do NOT set initdefault to this)
#   1 - Single user mode
#   2 - Multiuser, without NFS (The same as 3, if you do not have networking)
#   3 - Full multiuser mode
#   4 - unused
#   5 - X11
#   6 - reboot (Do NOT set initdefault to this)
#
id:3:initdefault:

The above will boot into runlevel 3 by default. Systemd does not have the same concept of runlevels, it uses targets instead. You can get the output of available targets using:

% systemctl list-units -t target --all
UNIT                         LOAD   ACTIVE   SUB    JOB DESCRIPTION
emergency.target             loaded inactive dead       Emergency Mode
graphical.target             loaded active   active     Graphical Interface
multi-user.target            loaded active   active     Multi-User
rescue.target                loaded inactive dead       Rescue Mode
shutdown.target              loaded inactive dead       Shutdown

I’ve snipped out the targets that are not really relevant as “runlevels” (for instance the “unmount.target” isn’t really a runlevel you would boot into, so I didn’t include it in the above output). Again, if you want to see what any of these load, use systemctl show -p “Wants” rescue.target, which will show ou what services and/or targets this target (or runlevel) wants.

Also note that there is some correspondance with the initial runlevel; for instance multi-user.target can also be called as runlevel3.target. You can look at /lib/systemd/system/runlevel* to see which runlevel target corresponds to the systemctl target, for instance:

 ls /lib/systemd/system/runlevel3.target -al
lrwxrwxrwx. 1 root root 17 Jan  8 21:19 /lib/systemd/system/runlevel3.target -> multi-user.target

Changing the Default Runlevel

Changing the default runlevel is done by changing the symlink of the /etc/systemd/system/default.target:

% ls -al /etc/systemd/system/default.target
lrwxrwxrwx. 1 root root 36 May 28  2012 /etc/systemd/system/default.target -> /lib/systemd/system/runlevel5.target

So if you wanted to boot into runlevel3 (multi-user.target) by default, you would:

# cd /etc/systemd/system
# rm default.target
# ln -s /lib/systemd/system/multi-user.target default.target

Systemctl Commands In Depth

Here we’ll look at a few commands in depth to try to make sense of them.

Systemctl Show

One of the more annoying things about systemctl is the show command. This is probably because it behaves quite differently from what I would expect from chkconfig and it’s worth discussing in a little more depth.

The primary issue is that when you run systemctl show -p “Wants” multi-user.target it pipes it to less, so you don’t really get to see all of the commands at once. Not sure why it does this, but it’s annoying. Piping that output to cat instead gives us the whole thing together:

% systemctl show -p "Wants" multi-user.target | cat
Wants=systemd-update-utmp-runlevel.service sm-client.service abrt-vmcore.service remote-fs.target abrt-xorg.service apcupsd.service sssd.service
mcelog.service nfs-idmap.service nfs-lock.service auditd.service crond.service sendmail.service sshd.service rsyslog.service acpid.service
avahi-daemon.service netatalk.service ntpd.service rpcbind.service NetworkManager.service chronyd.service httpd.service arp-ethers.service
atd.service irqbalance.service abrt-oops.service certmonger.service mysqld.service abrt-ccpp.service cups.path abrtd.service mdmonitor.service
plymouth-quit-wait.service systemd-logind.service dbus.service getty.target systemd-ask-password-wall.path plymouth-quit.service
systemd-user-sessions.service rc-local.service nrpe.service tcsd.service

This shows all the services and targets that the multi-user.target wants, which is our non-GUI runlevel (essentially runlevel 3). So if we want to see what starts with our graphical target:

% systemctl show -p "Wants" graphical.target | cat
Wants=systemd-readahead-collect.service systemd-readahead-replay.service systemd-update-utmp-runlevel.service system-setup-keyboard.service
prefdm.service nrpe.service tcsd.service atieventsd.service

Based on the above, it doesn’t look like the graphical target wants a whole heck of a lot. This is quite different from the chkconfig output where you see all the services and whether or not they are enabled in a particular runlevel. Here, at a quick glance, we would be thinking that things like dbus, getty, sshd, etc. aren’t being started. But we need to look at the dependencies between targets to know for sure what is going on. To that end:

% systemctl show -p "RequiredBy" multi-user.target| cat
RequiredBy=graphical.target

and:

% systemctl show -p "Requires" graphical.target | cat
Requires=multi-user.target

So now we see that multi-user.target is required by graphical.target (and likewise that graphical.target requires multi-user.target). This means that the multi-user.target must be loaded before graphical.target can be loaded. In other words, for the graphical.target we can add the “Wants” of multi-user.target to its “Wants” list. Confused yet? It’s not bad, but just annoying. With systemctl you need to to run two commands (“Wants” on both targets) versus a simple chkconfig command (chkconfig —list | grep ‘5:on’).

References

Here are links to other systemd documentation and articles worth reading: