[systemd-devel] Starting a service at shutdown time, with requirements

Colin Walters walters at verbum.org
Tue Mar 20 19:09:01 UTC 2018


I'm working on: https://github.com/ostreedev/ostree/issues/545
TL;DR:
libostree is an image-like update system, and I want to take some
actions at system shutdown time, specifically performing a "snapshot+merge"
of /etc after most other services are shut down.  The way ostree
handles /etc (IMO a lot more correctly) is a major distinguishing point from a lot
of other image-like systems out there.

What I have so far is this (distilled into a simple easily tested unit not tied to ostree):

[Unit]
Description=cgwalters-shutdown
DefaultDependencies=no
After=shutdown.target
Before=final.target
# FIXME
# Before=sysroot.mount
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/sh -c 'sleep 5 && ls -ald /sysroot/ostree/repo'
[Install]
WantedBy=final.target

I modeled this after `halt-local.service`.  But as you
might infer from the FIXME, the problem I'm hitting is that the /sysroot
mount is unmounted before this service is started.  I think the problem
I'm hitting is this bit from `man systemd.unit`:

> Given two units with any ordering dependency between them, if one unit is shut down and the other is started up, the shutdown is ordered before the start-up.

Which does sort of make sense to me in general; but is problematic for
this "start service on shutdown" case.   I was looking a bit at the way plymouth
hooks into shutdown, but it seemed complex, and as plymouth doesn't (AFAIK)
have any real dependencies on the filesystem state I'm not sure it matches what I need.

I can think of ways to hack around this, by e.g. having the service start up on
shutdown, take a snapshot of a private mount namespace, have it be excluded
from the Conflicts=shutdown.target, then have a unit that triggers before final.target
(like the above) that sends an IPC to actually do what I want.  But that adds a notable
amount of complexity, and I'd like to have this process feel as obvious/transparent as possible.

Another way I've thought about handling this is to basically invert things so that
we have a "stub" unit that starts on bootup, and its ExecStop does the real work:

[Unit]
Description=cgwalters-startstop
ConditionPathExists=/run/ostree-booted
DefaultDependencies=no
RequiresMountsFor=/sysroot
After=basic.target
Before=multi-user.target
Conflicts=final.target
Before=final.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/true
ExecStop=/usr/bin/sh -c 'sleep 5 && ls -ald /ostree/repo'

[Install]
WantedBy=multi-user.target

Which seems to work but feels...a bit ugly?


More information about the systemd-devel mailing list