Fun with systemd targets

The existing LCFG boot component allows one to configure components such that they are not started, at system boot time, until after those components which may ask for a system reboot have completed. Simply put, the boot component starts all components with a start ID < 100. It then reboots the system if any of those components have requested a reboot. If no reboot has been requested, it then starts those components with a start ID >= 100. This is so that user fronting services such as web, getty and ssh aren’t started just for them to be taken down should a reboot be requested.

We wanted to replicate this behaviour with systemd. It appeared obvious(?) that targets would assist us in doing this.

Our first attempt was to create a new target lcfg-multi-user-stable.target to be the end ‘default’ target, and have this require the normal multi-user.target. The following diagram shows the basic structure :-

    • lcfg-multi-user.target (as end ‘default’ target)
      • multi-user.target
        • lcfg-auth.service
        • lcfg-client.service
        • ….
        • lcfg-updaterpms.service
        • getty.service
        • other stock units
      • lcfg-rebootcheck.service (after multi-user.target)
      • lcfg-clearbootctx.service (after multi-user.target)
      • lcfg-cron.service (after multi-user.target)
      • lcfg-apacheconf.service (after multi-user.target)
      • lcfg-openssh.service (after multi-user.target)

The lcfg-rebootcheck tool reboots the system if any component started whilst reaching multi-user.target has requested a reboot.

The above configuration appeared to work fine, but for the fact that getty was being started before the reboot check was being performed. From the above table, we can see that we want to start getty.service after multi-user.target (or lcfg-rebootcheck.service). Unfortunately it proved impossible to achieve this. Getty is, by default, pulled in as a “wants” of multi-user.target. This dependency is hard-coded in /usr/lib/systemd. Amazingly, whilst it is possible to override a unit file in /usr/lib/systemd/system by creating a file with the same name in /etc/systemd/system, or to disable a unit file by creating a symlink of the same name to /dev/null, it is not possible to override dependency files in .wants or .requires directories.

For our second attempt, we decided to keep the default multi-user.target as the end ‘default’ target, adding new LCFG targets as “wants” of multi-user.target.

  • multi-user (as end ‘default’ target)
    • lcfg-multi-user-stable
      • lcfg-multi-user
        • lcfg-auth
        • lcfg-client
        • lcfg-updaterpms
      • lcfg-rebootcheck (after lcfg-multi-user)
    • lcfg-clearbootctx (after lcfg-multi-user-stable)
    • lcfg-cron (after lcfg-multi-user-stable)
    • lcfg-apacheconf (after lcfg-multi-user-stable)
    • lcfg-openssh (after lcfg-multi-user-stable)
    • getty (after lcfg-multi-user-stable)

The above configuration achieves what we wanted.

Whilst we have worked round the inability to override dependencies in this case, we’re convinced we’ll trip up over this problem again in the future.

This entry was posted in systemd. Bookmark the permalink.

Leave a Reply