diff --git a/modules/qualifying/learning_systemd.md b/modules/qualifying/learning_systemd.md index 37071c5..2b28989 100644 --- a/modules/qualifying/learning_systemd.md +++ b/modules/qualifying/learning_systemd.md @@ -565,3 +565,489 @@ Some things that are easy to do with cron are difficult to do with timer units a * Emails: there is no built-in equivalent to cron's MAILTO for sending emails on job failure. See the next section for an example of setting up a similar functionality using OnFailure=. Also note that user timer units will only run during an active user login session by default. However, lingering can enable services to run at boot even when the user has no active login session. + +## A sidetrack into runlevels + +The world of Linux has a concept called *runlevels* which determines a target state the machine is in, or to which you want the manche to go to. +It's a complicated way of saying fully operational with graphical interface, a root only rescue mode, a reboot, halted etc. +The official specification of the runlevels defines them as such. + +* Runlevel 0 or Halt is used to shift the computer from one state to another. It shut down the system. +* Runlevel 1, s, S or Single-User Mode is used for administrative and recovery functions. It has only enough daemons to allow one user (the root user) to log in and perform system maintenance tasks. All local file systems are mounted. Some essential services are started, but networking remains disabled. +* Runlevel 2 or Multi-user Mode is used for most daemons running and allows multiple users the ability to log in and use system services but without networking. On Debian and its derivatives, a full multi-user mode with X running and a graphical login. Most other distributions leave this runlevel undefined. +* Runlevel 3 or Extended Multi-user Mode is used for a full multi-user mode with a console (without GUI) login screen with network services available +* Runlevel 4 is not normally used and undefined so it can be used for a personal customization +* Runlevel 5 or Graphical Mode is same as Runlevel 3 with graphical login _(such as GDN)_. +* Runlevel 6 or Reboot is a transitional runlevel to reboot the system. + +You can inspect the runlevel your system is currenty at by ececuting the following command. + +``` +➜ ~ git:(master) ✗ sudo runlevel +N 5 +➜ ~ git:(master) ✗ +``` + +You can change your runlevel with the `sudo telinit`, followed by the level number, command. +You'll probably won't see that much difference between levels but try to change it to level `6` and see what happens. +If you change the runlevel to `1` your machine will probably freeze. +This has to do with the fact we haven't set a `root` password on most of our machines so the single user mode can't be accessed. +Try setting a root password and reset the level to one and see what happens. + +## Systemd targets + +Systemd take the concept of runlevels a bit further and they are renamed to **targets**. +The mapping of runlevels to targets is as follows. + +* poweroff.target (runlevel 0): shutdown and power off the system +* rescue.target (runlevel 1): launch the rescue shell session +* multi-user.target (runlevel 2,3,4): set the system in non graphical (console) multi-user system +* graphical.target (runlevel 5): use a graphical multi-user system with network services +* reboot.target (runlevel 6): shutdown and reboot the system + +But, there are a *lot* more targets available on a machine running systemd. +Luckily `systemctl` offers a nice way to inspect them. + +``` +➜ ~ git:(master) ✗ sudo systemctl list-units --type target + UNIT LOAD ACTIVE SUB DESCRIPTION + basic.target loaded active active Basic System + cryptsetup.target loaded active active Local Encrypted Volumes + getty.target loaded active active Login Prompts + local-fs-pre.target loaded active active Local File Systems (Pre) + local-fs.target loaded active active Local File Systems + multi-user.target loaded active active Multi-User System + network.target loaded active active Network + nfs-client.target loaded active active NFS client services + paths.target loaded active active Paths + remote-fs-pre.target loaded active active Remote File Systems (Pre) + remote-fs.target loaded active active Remote File Systems + rpcbind.target loaded active active RPC Port Mapper + slices.target loaded active active Slices + sockets.target loaded active active Sockets + swap.target loaded active active Swap + sysinit.target loaded active active System Initialization + time-set.target loaded active active System Time Set + time-sync.target loaded active active System Time Synchronized + timers.target loaded active active Timers + +LOAD = Reflects whether the unit definition was properly loaded. +ACTIVE = The high-level unit activation state, i.e. generalization of SUB. +SUB = The low-level unit activation state, values depend on unit type. +19 loaded units listed. Pass --all to see loaded but inactive units, too. +To show all installed unit files use 'systemctl list-unit-files'. +``` + +Notice how some of the mappings, such as rescue.target, are missing? +We can show the inactive ones as well if we add the `--all` argument. + +``` +➜ ~ git:(master) ✗ sudo systemctl list-units --type target --all --no-pager + UNIT LOAD ACTIVE SUB DESCRIPTION + basic.target loaded active active Basic System + blockdev@dev-disk-by\x2duuid-4a77d180\x2dfc64\x2d4057… loaded inactive dead Block Device Preparation for /dev/disk/by-uuid/4a77d18… + blockdev@dev-dm\x2d1.target loaded inactive dead Block Device Preparation for /dev/dm-1 + blockdev@dev-mapper-deathstar\x2d\x2dvg\x2droot.target loaded inactive dead Block Device Preparation for /dev/mapper/deathstar--vg… + blockdev@dev-mapper-deathstar\x2d\x2dvg\x2dswap_1.tar… loaded inactive dead Block Device Preparation for /dev/mapper/deathstar--vg… + blockdev@dev-sda1.target loaded inactive dead Block Device Preparation for /dev/sda1 + bluetooth.target loaded inactive dead Bluetooth + cryptsetup.target loaded active active Local Encrypted Volumes + emergency.target loaded inactive dead Emergency Mode + first-boot-complete.target loaded inactive dead First Boot Complete + getty-pre.target loaded inactive dead Login Prompts (Pre) + getty.target loaded active active Login Prompts + graphical.target loaded inactive dead Graphical Interface + local-fs-pre.target loaded active active Local File Systems (Pre) + local-fs.target loaded active active Local File Systems + multi-user.target loaded active active Multi-User System + network-online.target loaded inactive dead Network is Online + network-pre.target loaded inactive dead Network (Pre) + network.target loaded active active Network + nfs-client.target loaded active active NFS client services + nss-user-lookup.target loaded inactive dead User and Group Name Lookups + paths.target loaded active active Paths + remote-fs-pre.target loaded active active Remote File Systems (Pre) + remote-fs.target loaded active active Remote File Systems + rescue.target loaded inactive dead Rescue Mode + rpcbind.target loaded active active RPC Port Mapper + shutdown.target loaded inactive dead Shutdown + slices.target loaded active active Slices + sockets.target loaded active active Sockets + sound.target loaded inactive dead Sound Card + swap.target loaded active active Swap + sysinit.target loaded active active System Initialization + time-set.target loaded active active System Time Set + time-sync.target loaded active active System Time Synchronized + timers.target loaded active active Timers + umount.target loaded inactive dead Unmount All Filesystems + +LOAD = Reflects whether the unit definition was properly loaded. +ACTIVE = The high-level unit activation state, i.e. generalization of SUB. +SUB = The low-level unit activation state, values depend on unit type. +36 loaded units listed. +To show all installed unit files use 'systemctl list-unit-files'. +➜ ~ git:(master) ✗ +``` + +That's better but still, some other ones such as poweroff.target seem to be missing. +Those are both not active and not loaded, but still available. +We can list all unit files known to our system with a different command. + +``` +➜ ~ git:(master) ✗ sudo systemctl list-unit-files --type target --all --no-pager +UNIT FILE STATE VENDOR PRESET +basic.target static - +blockdev@.target static - +bluetooth.target static - +boot-complete.target static - +cryptsetup-pre.target static - +cryptsetup.target static - +ctrl-alt-del.target alias - +default.target alias - +emergency.target static - +exit.target disabled disabled +final.target static - +first-boot-complete.target static - +getty-pre.target static - +getty.target static - +graphical.target static - +halt.target disabled disabled +hibernate.target static - +hybrid-sleep.target static - +initrd-fs.target static - +initrd-root-device.target static - +initrd-root-fs.target static - +initrd-switch-root.target static - +initrd.target static - +kexec.target disabled disabled +local-fs-pre.target static - +local-fs.target static - +multi-user.target static - +network-online.target static - +network-pre.target static - +network.target static - +nfs-client.target enabled enabled +nss-lookup.target static - +nss-user-lookup.target static - +paths.target static - +poweroff.target disabled disabled +printer.target static - +reboot.target disabled enabled +remote-cryptsetup.target disabled enabled +remote-fs-pre.target static - +remote-fs.target enabled enabled +rescue-ssh.target static - +rescue.target static - +rpcbind.target static - +runlevel0.target alias - +runlevel1.target alias - +runlevel2.target alias - +runlevel3.target alias - +runlevel4.target alias - +runlevel5.target alias - +runlevel6.target alias - +shutdown.target static - +sigpwr.target static - +sleep.target static - +slices.target static - +smartcard.target static - +sockets.target static - +sound.target static - +suspend-then-hibernate.target static - +suspend.target static - +swap.target static - +sysinit.target static - +system-update-pre.target static - +system-update.target static - +time-set.target static - +time-sync.target static - +timers.target static - +umount.target static - +usb-gadget.target static - + +68 unit files listed. +➜ ~ git:(master) ✗ +``` + +That seems to be complete. +Now, how do we switch form one target to an other in a modern systemd-like fashion? +For this we use the `isolate` argument to `systemctl`. +A quick test of a this can be done as such, `sudo systemctl isolate reboot.target`. +On a Linux system where root has a password set you could try the `rescue.target` as well. +You can get and set the default runlevel of you system with the following commands. + +``` +➜ ~ git:(master) ✗ sudo systemctl get-default +graphical.target +➜ ~ git:(master) ✗ sudo systemctl set-default multi-user.target +Created symlink /etc/systemd/system/default.target → /lib/systemd/system/multi-user.target. +➜ ~ git:(master) ✗ sudo systemctl get-default +multi-user.target +➜ ~ git:(master) ✗ +``` + +## A deeper look into targets + +What is included in all of these targets? +We can inspect their dependencies by invoking the `list-dependencies` argument to `systemctl`. +Let's start with the most basic one, the `rescue.target`. + +``` +➜ ~ git:(master) ✗ sudo systemctl list-dependencies rescue.target --no-pager +rescue.target +● ├─rescue.service +● ├─systemd-update-utmp-runlevel.service +● └─sysinit.target +● ├─apparmor.service +● ├─blk-availability.service +● ├─dev-hugepages.mount +● ├─dev-mqueue.mount +● ├─keyboard-setup.service +● ├─kmod-static-nodes.service +● ├─lvm2-lvmpolld.socket +● ├─lvm2-monitor.service +● ├─proc-sys-fs-binfmt_misc.automount +● ├─sys-fs-fuse-connections.mount +● ├─sys-kernel-config.mount +● ├─sys-kernel-debug.mount +● ├─sys-kernel-tracing.mount +● ├─systemd-ask-password-console.path +● ├─systemd-binfmt.service +● ├─systemd-boot-system-token.service +● ├─systemd-hwdb-update.service +● ├─systemd-journal-flush.service +● ├─systemd-journald.service +● ├─systemd-machine-id-commit.service +● ├─systemd-modules-load.service +● ├─systemd-pstore.service +● ├─systemd-random-seed.service +● ├─systemd-sysctl.service +● ├─systemd-sysusers.service +● ├─systemd-timesyncd.service +● ├─systemd-tmpfiles-setup-dev.service +● ├─systemd-tmpfiles-setup.service +● ├─systemd-udev-trigger.service +● ├─systemd-udevd.service +● ├─systemd-update-utmp.service +● ├─cryptsetup.target +● ├─local-fs.target +● │ ├─-.mount +● │ ├─boot.mount +● │ ├─systemd-fsck-root.service +● │ └─systemd-remount-fs.service +● └─swap.target +● └─dev-mapper-deathstar\x2d\x2dvg\x2dswap_1.swap +➜ ~ git:(master) ✗ +``` + +As you can see, it's quite basic. +All of these services and additional targets will try to be loaded and started when we enter rescue mode. +Now, let's compare it to the most elaborate runlevel, `5`. + +``` +➜ ~ git:(master) ✗ sudo systemctl list-dependencies graphical.target --no-pager +graphical.target +● ├─display-manager.service +● ├─systemd-update-utmp-runlevel.service +● ├─udisks2.service +● └─multi-user.target +● ├─avahi-daemon.service +● ├─binfmt-support.service +● ├─blueman-mechanism.service +● ├─chrony.service +● ├─console-setup.service +● ├─cron.service +● ├─cups-browsed.service +● ├─cups.path +● ├─dbus.service +● ├─e2scrub_reap.service +● ├─ModemManager.service +● ├─networking.service +● ├─rpcbind.service +● ├─rsyslog.service +● ├─ssh.service +● ├─systemd-ask-password-wall.path +● ├─systemd-logind.service +● ├─systemd-update-utmp-runlevel.service +● ├─systemd-user-sessions.service +● ├─wpa_supplicant.service +● ├─basic.target +● │ ├─-.mount +● │ ├─tmp.mount +● │ ├─paths.target +● │ ├─slices.target +● │ │ ├─-.slice +● │ │ └─system.slice +● │ ├─sockets.target +● │ │ ├─avahi-daemon.socket +● │ │ ├─cups.socket +● │ │ ├─dbus.socket +● │ │ ├─dm-event.socket +● │ │ ├─pcscd.socket +● │ │ ├─rpcbind.socket +● │ │ ├─systemd-initctl.socket +● │ │ ├─systemd-journald-audit.socket +● │ │ ├─systemd-journald-dev-log.socket +● │ │ ├─systemd-journald.socket +● │ │ ├─systemd-udevd-control.socket +● │ │ └─systemd-udevd-kernel.socket +● │ ├─sysinit.target +● │ │ ├─apparmor.service +● │ │ ├─blk-availability.service +● │ │ ├─dev-hugepages.mount +● │ │ ├─dev-mqueue.mount +● │ │ ├─keyboard-setup.service +● │ │ ├─kmod-static-nodes.service +● │ │ ├─lvm2-lvmpolld.socket +● │ │ ├─lvm2-monitor.service +● │ │ ├─proc-sys-fs-binfmt_misc.automount +● │ │ ├─sys-fs-fuse-connections.mount +● │ │ ├─sys-kernel-config.mount +● │ │ ├─sys-kernel-debug.mount +● │ │ ├─sys-kernel-tracing.mount +● │ │ ├─systemd-ask-password-console.path +● │ │ ├─systemd-binfmt.service +● │ │ ├─systemd-boot-system-token.service +● │ │ ├─systemd-hwdb-update.service +● │ │ ├─systemd-journal-flush.service +● │ │ ├─systemd-journald.service +● │ │ ├─systemd-machine-id-commit.service +● │ │ ├─systemd-modules-load.service +● │ │ ├─systemd-pstore.service +● │ │ ├─systemd-random-seed.service +● │ │ ├─systemd-sysctl.service +● │ │ ├─systemd-sysusers.service +● │ │ ├─systemd-timesyncd.service +● │ │ ├─systemd-tmpfiles-setup-dev.service +● │ │ ├─systemd-tmpfiles-setup.service +● │ │ ├─systemd-udev-trigger.service +● │ │ ├─systemd-udevd.service +● │ │ ├─systemd-update-utmp.service +● │ │ ├─cryptsetup.target +● │ │ ├─local-fs.target +● │ │ │ ├─-.mount +● │ │ │ ├─boot.mount +● │ │ │ ├─systemd-fsck-root.service +● │ │ │ └─systemd-remount-fs.service +● │ │ └─swap.target +● │ │ └─dev-mapper-deathstar\x2d\x2dvg\x2dswap_1.swap +● │ └─timers.target +● │ ├─apt-daily-upgrade.timer +● │ ├─apt-daily.timer +● │ ├─e2scrub_all.timer +● │ ├─logrotate.timer +● │ ├─man-db.timer +● │ └─systemd-tmpfiles-clean.timer +● ├─getty.target +● │ ├─getty-static.service +● │ └─getty@tty1.service +● ├─nfs-client.target +● │ ├─auth-rpcgss-module.service +● │ ├─nfs-blkmap.service +● │ └─remote-fs-pre.target +● └─remote-fs.target +● └─nfs-client.target +● ├─auth-rpcgss-module.service +● ├─nfs-blkmap.service +● └─remote-fs-pre.target +➜ ~ git:(master) ✗ +``` + +You immediately see, and probably recognise a lot of very useful services that get launched when we enter the graphical target. +Mind you that the output above is from a pretty lean system running a minimal i3 graphical environment. + +We can also use the `list-dependencies` to inspect services such as `sshd.service`. +The list below is everything sshd depends on to succesfully run as a systemd service. + +``` +sshd.service +● ├─-.mount +● ├─system.slice +● └─sysinit.target +● ├─apparmor.service +● ├─blk-availability.service +● ├─dev-hugepages.mount +● ├─dev-mqueue.mount +● ├─keyboard-setup.service +● ├─kmod-static-nodes.service +● ├─lvm2-lvmpolld.socket +● ├─lvm2-monitor.service +● ├─proc-sys-fs-binfmt_misc.automount +● ├─sys-fs-fuse-connections.mount +● ├─sys-kernel-config.mount +● ├─sys-kernel-debug.mount +● ├─sys-kernel-tracing.mount +● ├─systemd-ask-password-console.path +● ├─systemd-binfmt.service +● ├─systemd-boot-system-token.service +● ├─systemd-hwdb-update.service +● ├─systemd-journal-flush.service +● ├─systemd-journald.service +● ├─systemd-machine-id-commit.service +● ├─systemd-modules-load.service +● ├─systemd-pstore.service +● ├─systemd-random-seed.service +● ├─systemd-sysctl.service +● ├─systemd-sysusers.service +● ├─systemd-timesyncd.service +● ├─systemd-tmpfiles-setup-dev.service +● ├─systemd-tmpfiles-setup.service +● ├─systemd-udev-trigger.service +● ├─systemd-udevd.service +● ├─systemd-update-utmp.service +● ├─cryptsetup.target +● ├─local-fs.target +● │ ├─-.mount +● │ ├─boot.mount +● │ ├─systemd-fsck-root.service +● │ └─systemd-remount-fs.service +● └─swap.target +● └─dev-mapper-deathstar\x2d\x2dvg\x2dswap_1.swap +➜ ~ git:(master) ✗ +``` + +A very clever *reverse dependency* list can be show by adding the `--reverse` argument. +The output below show the dependencies of the networking.service first. +You can see it *needs* the ifupdown-pre.service, system.slice and the network.target. +The second command shows the reverse, which services or targets *depend* on the networking.service to be up and running. + +``` +➜ ~ git:(master) ✗ sudo systemctl list-dependencies networking.service --no-pager +networking.service +● ├─ifupdown-pre.service +● ├─system.slice +● └─network.target +➜ ~ git:(master) ✗ sudo systemctl list-dependencies networking.service --no-pager --reverse +networking.service +● ├─multi-user.target +● │ └─graphical.target +● └─network-online.target +➜ ~ git:(master) ✗ +``` + +The combination of both can give you a solid understanding of how all services and targets are interconnected. +Just as with services we can inspect *what* a target is doing by looking at it's unit file. + +``` +➜ ~ git:(master) ✗ sudo systemctl cat network-online.target +# /lib/systemd/system/network-online.target +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# This file is part of systemd. +# +# systemd is free software; you can redistribute it and/or modify it +# under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; either version 2.1 of the License, or +# (at your option) any later version. + +[Unit] +Description=Network is Online +Documentation=man:systemd.special(7) +Documentation=https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget +After=network.target +➜ ~ git:(master) ✗ +``` + +This might not tell you all that much on first sight, but I urge you to take the time out to really read the `man systemd.special`. +It will explain you all the intricacies of the different standard targets and how you can use them to your benefit. +