With systemd one can query if the boot has completed by the command

systemctl is-system-running

which returns error code if boot hasn't completed. If one want to wait for the boot to complete one can of course rerun the command until it returns 0.

I think there "must be a better way" to do this. Is there a method that doesn't rely on polling?

The reason I want to do this is that I currently have a script that downloads firmware to a device that is then rebooted and want to monitor it to see if it boots cleanly. I currently do this by issuing systemctl is-system-running via SSH until it returns 0 or it doesn't within the time the boot ought to have completed. Of course I want this to finish as soon as possible.

  • Of course. Write a service that requires the system to finish starting. – Ignacio Vazquez-Abrams Aug 3 '18 at 13:02
  • @IgnacioVazquez-Abrams I was thinking of a doing this in a script. – skyking Aug 3 '18 at 13:03
  • @IgnacioVazquez-Abrams It would be nice to mention exactly what you think is unclear in the question so I can improve it. (I think it's quite clear myself) – skyking Aug 6 '18 at 11:29
  • That wasn't my close reason, but okay. – Ignacio Vazquez-Abrams Aug 6 '18 at 14:23
  • @skyking Just update it to say you want a command that will wait until boot completes... BTW, PR #9796 was just accepted in systemd, so starting with systemd v240 it will be possible to do so with systemctl is-system-running --wait. :-D – filbranden Aug 7 '18 at 7:36

It is possible to wait for this condition without polling this command on a loop, but unfortunately it's not that straightforward...

systemd will post a StartupFinished signal on D-Bus once the boot is complete, so it is possible to watch for that and get a notification for it.

Here is a simple dbus-wait tool that can watch for signals on D-Bus.

I managed to wait until startup was completed by using this command:

dbus-wait org.freedesktop.systemd1.Manager StartupFinished

The problem with this approach is that there's a race condition there. If first I check whether the system is running (with systemctl is-system-running) and see that it's still "starting" and then decide to wait for the signal, it's possible that by the time dbus-wait will connect to the bus and start waiting for the signal, that systemd just finished startup and already posted this signal in the bus, which will make dbus-wait wait for an event that won't happen and eventually time out...

On the other hand, it should be possible to implement this in a way that is free from race conditions, by first registering the watch for the event, then checking the property and if it's still not set to "running" then run the event loop to wait for the signal.

I implemented the approach above and proposed it to upstream systemd. My suggestion is to allow running systemctl is-system-running --wait for this purpose (but it is possible this might end up making it to systemd with a different syntax, possibly a separate command.) The proposal for the new systemd feature can be found at PR #9796 on systemd project. (I'll update this answer once it's accepted and merged.)

UPDATE: PR #9796 was just merged into systemd, so starting with systemd v240, it will be possible to wait until boot is completed with the following command:

systemctl is-system-running --wait
| improve this answer | |
  • The thing with systemctl is-system-running --wait is that dbus might not be running yet. I need to execute systemctl list-units in a LXC container (using lxc-attach) after starting it. Both list-units and is-system-running say "Failed to connect to bus: No such file or directory" at first. In this case I see no better option than: while ! systemctl is-system-running --wait; do sleep 1; done. – x-yuri Nov 28 '19 at 0:25
  • @x-yuri: You comment is spot on, this race condition completely undermines the usefulness of is-system-running. Unfortunately, your loop-based solution is incomplete as well: if the system never enters the running state but immediately transitions to say degraded, it will never terminate. – phg Jul 24 at 15:02

Waiting Unit file

Here's a prototype that does what your looking for. To start here's a systemd unit file, waity.service:

$ cat /etc/systemd/system/waity.service
Description=Waity Service


Waiting Script

It runs a script which does the interrogating of systemd to see if it's finished booting:

$ cat /opt/bin/waity.sh

while $(sleep 10); do
  echo "waiting for systemd to finish booting..."
  if systemctl is-system-running | grep -qE "running|degraded"; then

echo "systemd finished booting..."
echo "...do something else..."

Example run

Now enable this service:

$ systemctl enable --now waity.service

Now when we start up our system this service will sit in a 10 second loop until it sees that the is-system-running action of systemctl returns either a running or degraded state. Either of these is an indication that the system has completed booting.

The status of this service:

$ systemctl status waity.service
‚óŹ waity.service - Waity Service
   Loaded: loaded (/etc/systemd/system/waity.service; static; vendor preset: disabled)
   Active: inactive (dead)

Aug 03 22:06:01 centos7 waity.sh[4519]: Got unexpected auxiliary data with level=1 and type=2
Aug 03 22:06:01 centos7 waity.sh[4519]: Got unexpected auxiliary data with level=1 and type=2
Aug 03 22:06:01 centos7 waity.sh[4519]: systemd finished booting...
Aug 03 22:06:01 centos7 waity.sh[4519]: ...do something else...
Aug 03 22:06:01 centos7 systemd[1]: Child 4519 belongs to waity.service
Aug 03 22:06:01 centos7 systemd[1]: waity.service: main process exited, code=exited, status=0/SUCCESS
Aug 03 22:06:01 centos7 systemd[1]: waity.service changed running -> dead
Aug 03 22:06:01 centos7 systemd[1]: waity.service: cgroup is empty
Aug 03 22:06:01 centos7 systemd[1]: Collecting waity.service
Aug 03 22:07:17 centos7 systemd[1]: Collecting waity.service

We can see in the above messages that we've detected a completed boot:

Aug 03 22:06:01 centos7 waity.sh[4519]: systemd finished booting...
Aug 03 22:06:01 centos7 waity.sh[4519]: ...do something else...

At which point we could do the ...do something else.... Once this is all complete this service will go to a done/dead state.

| improve this answer | |
  • This is a very complete answer and it seems to do exactly what the OP asked for, since it removes the need to externally poll the system. I wonder if there's a solution without busy waiting as well, as one would do asynchronously with callbacks or promises in JavaScript. – Telmo Trooper Aug 4 '18 at 2:29
  • 1
    @TelmoTrooper - I saw none, but that doesn't mean there aren't. Given this is running just during a boot, it runs every 10 seconds, and then once the boot is complete it stops, this will run at most maybe a half dozen times, seems reasonable to me, IMO. – slm Aug 4 '18 at 2:31
  • 1
    Sim doesn't this have the same drawback as my original solution? Namely that it will be between 0 and 10s late due to the polling loop? That's what I wanted to avoid. – skyking Aug 6 '18 at 11:33
  • @skyking - there's a race condition in the other approach using D-Bus, you can't escape it, hence why I did a simple polling solution here, to be able to gain access to the is-system-running action of systemctl. The amount of time can be controlled with this approach by making a tighter loop. I see no other method beyond this here. – slm Aug 6 '18 at 11:44

Not the answer you're looking for? Browse other questions tagged or ask your own question.