#1091519 deb-systemd-helper: Fails when non-existing unit is listed in Also= line

#1091519#5
Date:
2024-12-27 22:10:35 UTC
From:
To:
When installing libvirt-daemon on a testing/unstable system, the
following error message is reported:

  /usr/bin/deb-systemd-helper: error: unable to read virtlockd.socket

Full debug output, with useless information redacted:

  $ sudo _DEB_SYSTEMD_HELPER_DEBUG=1 apt-get install --no-install-recommends libvirt-daemon
  The following NEW packages will be installed:
    libvirt-daemon
  Selecting previously unselected package libvirt-daemon.
  Unpacking libvirt-daemon (10.10.0-3) ...
  Setting up libvirt-daemon (10.10.0-3) ...
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = unmask, scriptname = libvirtd.service, service_path = /lib/systemd/system/libvirtd.service
  (deb-systemd-helper DEBUG) rmdir_if_empty /var/lib/systemd/deb-systemd-helper-masked
  (deb-systemd-helper DEBUG) rmdir(/var/lib/systemd/deb-systemd-helper-masked) failed (No such file or directory)
  (deb-systemd-helper DEBUG) rmdir_if_empty /var/lib/systemd/deb-systemd-user-helper-masked
  (deb-systemd-helper DEBUG) rmdir(/var/lib/systemd/deb-systemd-user-helper-masked) failed (No such file or directory)
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = was-enabled, scriptname = libvirtd.service, service_path = /lib/systemd/system/libvirtd.service
  (deb-systemd-helper DEBUG) Reading state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd.service.dsh-also
  (deb-systemd-helper DEBUG) Contents: $VAR1 = [];

  (deb-systemd-helper DEBUG) All links present, considering libvirtd.service was-enabled.
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = enable, scriptname = libvirtd.service, service_path = /lib/systemd/system/libvirtd.service
  (deb-systemd-helper DEBUG) Using systemctl preset to enable libvirtd.service
  Failed to enable auxiliary unit virtlockd.socket, ignoring.
  Created symlink '/etc/systemd/system/sockets.target.wants/libvirtd.socket' → '/usr/lib/systemd/system/libvirtd.socket'.
  Created symlink '/etc/systemd/system/sockets.target.wants/libvirtd-ro.socket' → '/usr/lib/systemd/system/libvirtd-ro.socket'.
  Created symlink '/etc/systemd/system/sockets.target.wants/libvirtd-admin.socket' → '/usr/lib/systemd/system/libvirtd-admin.socket'.
  /usr/bin/deb-systemd-helper: error: unable to read virtlockd.socket
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = unmask, scriptname = libvirtd-admin.socket, service_path = /lib/systemd/system/libvirtd-admin.socket
  (deb-systemd-helper DEBUG) rmdir_if_empty /var/lib/systemd/deb-systemd-helper-masked
  (deb-systemd-helper DEBUG) rmdir(/var/lib/systemd/deb-systemd-helper-masked) failed (No such file or directory)
  (deb-systemd-helper DEBUG) rmdir_if_empty /var/lib/systemd/deb-systemd-user-helper-masked
  (deb-systemd-helper DEBUG) rmdir(/var/lib/systemd/deb-systemd-user-helper-masked) failed (No such file or directory)
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = was-enabled, scriptname = libvirtd-admin.socket, service_path = /lib/systemd/system/libvirtd-admin.socket
  (deb-systemd-helper DEBUG) Reading state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-admin.socket.dsh-also
  (deb-systemd-helper DEBUG) Contents: $VAR1 = [];

  (deb-systemd-helper DEBUG) All links present, considering libvirtd-admin.socket was-enabled.
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = enable, scriptname = libvirtd-admin.socket, service_path = /lib/systemd/system/libvirtd-admin.socket
  (deb-systemd-helper DEBUG) Using systemctl preset to enable libvirtd-admin.socket
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.statefOsJc.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-admin.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.statenPIoH.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-admin.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.stateLCkXc.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-admin.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.statep2PDL.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-admin.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.stateM29Ok.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-admin.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.stateF7a2H.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-admin.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.stateDAd0c.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-admin.socket.dsh-also
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = unmask, scriptname = libvirtd-ro.socket, service_path = /lib/systemd/system/libvirtd-ro.socket
  (deb-systemd-helper DEBUG) rmdir_if_empty /var/lib/systemd/deb-systemd-helper-masked
  (deb-systemd-helper DEBUG) rmdir(/var/lib/systemd/deb-systemd-helper-masked) failed (No such file or directory)
  (deb-systemd-helper DEBUG) rmdir_if_empty /var/lib/systemd/deb-systemd-user-helper-masked
  (deb-systemd-helper DEBUG) rmdir(/var/lib/systemd/deb-systemd-user-helper-masked) failed (No such file or directory)
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = was-enabled, scriptname = libvirtd-ro.socket, service_path = /lib/systemd/system/libvirtd-ro.socket
  (deb-systemd-helper DEBUG) Reading state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-ro.socket.dsh-also
  (deb-systemd-helper DEBUG) Contents: $VAR1 = [];

  (deb-systemd-helper DEBUG) All links present, considering libvirtd-ro.socket was-enabled.
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = enable, scriptname = libvirtd-ro.socket, service_path = /lib/systemd/system/libvirtd-ro.socket
  (deb-systemd-helper DEBUG) Using systemctl preset to enable libvirtd-ro.socket
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.statetIBlZ.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-ro.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.stateBihfm.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-ro.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.stateH5caR.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-ro.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.stateQDBhi.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-ro.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.state1lH4W.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-ro.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.stateXiC5q.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-ro.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.statepS8xr.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd-ro.socket.dsh-also
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = unmask, scriptname = libvirtd.socket, service_path = /lib/systemd/system/libvirtd.socket
  (deb-systemd-helper DEBUG) rmdir_if_empty /var/lib/systemd/deb-systemd-helper-masked
  (deb-systemd-helper DEBUG) rmdir(/var/lib/systemd/deb-systemd-helper-masked) failed (No such file or directory)
  (deb-systemd-helper DEBUG) rmdir_if_empty /var/lib/systemd/deb-systemd-user-helper-masked
  (deb-systemd-helper DEBUG) rmdir(/var/lib/systemd/deb-systemd-user-helper-masked) failed (No such file or directory)
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = was-enabled, scriptname = libvirtd.socket, service_path = /lib/systemd/system/libvirtd.socket
  (deb-systemd-helper DEBUG) Reading state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd.socket.dsh-also
  (deb-systemd-helper DEBUG) Contents: $VAR1 = [];

  (deb-systemd-helper DEBUG) All links present, considering libvirtd.socket was-enabled.
  (deb-systemd-helper DEBUG) is purge = no
  (deb-systemd-helper DEBUG) action = enable, scriptname = libvirtd.socket, service_path = /lib/systemd/system/libvirtd.socket
  (deb-systemd-helper DEBUG) Using systemctl preset to enable libvirtd.socket
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.state0LqFQ.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.statePtygq.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.stateyivti.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.statelrMK_.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.statesTPSG.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.state2YGDX.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd.socket.dsh-also
  (deb-systemd-helper DEBUG) Renaming temp file /var/lib/systemd/deb-systemd-helper-enabled/.stateQmQOZ.tmp to state file /var/lib/systemd/deb-systemd-helper-enabled/libvirtd.socket.dsh-also

Note that I had installed all dependencies (libvirt-daemon-common,
libvirt-daemon-log) ahead of time to ensure that only the relevant
package would be included in this transaction.

This issue has been reported several months ago as

https://bugs.debian.org/1082939

and unfortunately I hadn't been able to turn that into a proper
report against init-system-helpers until now. Apologies for the
delay.

The root cause is that libvirtd.service contains

  [Install]
  Also=virtlockd.socket

but virtlockd (as well as its sockets) is shipped in the
libvirt-daemon-lock package, and the libvirt-daemon package (which
contains libvirtd and its sockets) *intentionally* does not Depend on
it.

The explanation is that virtlockd is a soft dependency for libvirtd:
specifically, libvirtd.service contains

  [Unit]
  Wants=virtlockd.socket
  After=virtlockd.socket

but it can work perfectly well without it, and in fact in most cases
the virtlockd integration will be disabled, as per default
configuration. This is why Wants= is used instead of Requires=.

systemd itself handles this nicely by simply ignoring the soft
dependency when the corresponing unit doesn't exist.
deb-systemd-helper, on the other hand, reports an error and refuses
to create the libvirtd.service.dsh-also state file, which in turns
results in partial cleanup on purge as well as potentially other
side-effects that I haven't noticed.

I believe that deb-systemd-helper should follow systemd's example and
treat this situation more gracefully, but to be completely honest I
don't fully understand what its state files are used for in the first
place, so it's quite possible that this take is unhelpfully naive :)

I've attached a tentative patch that turns the hard failure into a
soft one. Consider it a mere conversation starter.

Cheers.

#1091519#10
Date:
2025-01-19 14:42:58 UTC
From:
To:
Any thoughts on the issue?

Thanks.

#1091519#17
Date:
2025-04-05 15:11:50 UTC
From:
To:
Hi,

it would be nice to get any feedback.

As things are right now, it looks like I'm going to be forced to work
around this in the libvirt package, which obviously I'm not a fan of.

Thanks.

#1091519#22
Date:
2025-09-12 14:21:58 UTC
From:
To:
I think false conclusions are drawn here:

Also= lines for non-existing units are not a problem, but skipped
silently. The problem here is, there multiple units of libvirt packages
have an Also= loop:

libvirtd-admin.socket:
```
Also=libvirtd.socket
Also=libvirtd-ro.socket
```

libvirtd-ro.socket:
```
Also=libvirtd.socket
Also=libvirtd-admin.socket
```

libvirtd.socket:
```
Also=libvirtd-ro.socket
Also=libvirtd-admin.socket
```

libvirt-daemon.postrm or on distro upgrades the previous
libvirt-daemon-system.postrm call "deb-systemd-helper purge" for each of
those, and perl even warns about the resulting recursion:
```
recursion on subroutine "main::remove_links" at
/usr/bin/deb-systemd-helper line 488, <$fh> line 14.
```

The function contains this code block:
```
sub update_state {
...
     # Read $service_path, recurse for all Also= units.
     # This might not work when $service_path was already deleted,
     # i.e. after apt-get remove. In this case we just return
     # silently in order to not confuse the user about whether
     # disabling actually worked or not — the case is handled by
     # dh_installsystemd generating an appropriate disable
     # command by parsing the service file at debhelper-time.
     open(my $fh, '<', "$dpkg_root$service_path") or return;
     while (my $line = <$fh>) {
         chomp($line);
         my $service_link;

         if ($line =~ /^\s*Also=(.+)$/i) {
             remove_links(find_unit($1));
         }
     }
     close($fh);
```
So using deb-systemd-helper on libvirtd.socket repeats the function on
libvirtd-ro.socket which repeats it on libvirtd.socket again and so on
and so forth.

As can be seen and as the comment explains, missing units are gracefully
handled, and they would even break the endless loop in this case.

Generally, for all such kind of dependencies and ordering, there must
never be loops in systemd units, but they must have a clear
single-direction parent/child or before/after structure. Choose one of
those units as the primary one which is enabled on install and disabled
on uninstall, and add Also= lines only to this one, but remove them from
the other two. That should fix the hanging issue.

Best regards,
Micha

#1091519#27
Date:
2025-09-12 14:54:01 UTC
From:
To:
Ah sorry, looks like the missing "Also=" units really were an issue before.

However, now it is the present "Also=" units if an old package `postrm
purge` calls "deb-systemd-helper purge" for any of them. One of our
users reported this when upgrading from Debian Bookworm to Trixie, where
a new version of "libvirt-daemon" was installed, and an old obsolete
version of "libvirt-daemon-system" was finally purged.

While such, related to a distribution upgrade, may be a rare case,
removing these After= loops remains reasonable. deb-systemd-helper will
choke on such, and there is always a chance for such a call while units
are still present for various reasons.

Shall I start a new bug report for this? I can also open a merge request
at salsa, but would need to know which of the 3 units shall keep the
"Also=" lines. The postinst script enables and starts all 3 of them, so
from that, no most probable parent/child structure is visible.

Best regards,
Micha

#1091519#32
Date:
2026-01-04 20:37:23 UTC
From:
To:
This bug hits users like me, when following the Bookworm -> Trixie update
instructions on

[...]
Purging configuration files for libvirt-daemon-system (9.0.0-4+deb12u2) ...
Deep recursion on subroutine "main::remove_links" at /usr/bin/deb-systemd-
helper line 488, <$fh> line 14.