diff mbox series

[meta-oe] systemd-systemctl: enable templated services

Message ID 20260908112519.1089163-1-f.pflug@pengutronix.de
State New
Headers show
Series [meta-oe] systemd-systemctl: enable templated services | expand

Commit Message

Fabian Pflug Sept. 8, 2026, 11:24 a.m. UTC
The wrapper script previously did not allow to instantiate templated
services without DefaultInstance defined. This change allows
foo@.service be installed and therefor be in enabled state.

In real systemd land, templated units without a specified instance are
valid, but we restrict it here a bit in order to not have a foo@.service
with:

  [Install]
  WantedBy=bar.service    # bar.service starts -> start foo@???.service

In this case, system can never resolve the specific instance of
foo@.service when bar.service is started. It is fixed by adding the
template_only variable, which restricts the dependents of generic
template units to template-only ones. This means the following is still
legal for the above foo@.service:

  [Install]
  WantedBy=bar@.service   # bar@1.service starts -> start foo@1.service
                          # bar@2.service starts -> start foo@2.service
                          # etc...

  WantedBy=bas@1.service  # bas@1.service starts -> start foo@1.service
                          # bas@2.service starts -> no dependency here
                          # etc...

Signed-off-by: Fabian Pflug <f.pflug@pengutronix.de>
---
 .../systemd/systemd-systemctl/systemctl       | 30 ++++++++++++-------
 1 file changed, 20 insertions(+), 10 deletions(-)
diff mbox series

Patch

diff --git a/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
index 07d24fd175..a9785e8b4b 100755
--- a/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
+++ b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
@@ -179,12 +179,18 @@  class SystemdUnit():
 
         raise SystemdUnitNotFoundError(self.root, unit)
 
-    def _process_deps(self, config, service, location, prop, dirstem):
+    def _process_deps(self, config, service, location, prop, dirstem,
+                      template_only):
         systemdir = self.root / SYSCONFDIR / "systemd" / "system"
 
         target = ROOT / location.relative_to(self.root)
         try:
             for dependent in config.get('Install', prop):
+                # template-only dependencies means either foo@.unit or foo@bar.unit,
+                # but not foo.unit, as then systemd cannot resolve the instance
+                if template_only and not re.match(r"[^@]+@[^\.]*\.", dependent):
+                        continue
+
                 wants = systemdir / "{}.{}".format(dependent, dirstem) / service
                 add_link(wants, target)
 
@@ -209,20 +215,24 @@  class SystemdUnit():
             return
 
         config = SystemdFile(self.root, path)
+        service = self.unit
+        template_only = False
+
+        # handle enabling a template unit with unspecified instance
         if instance == "":
             try:
                 default_instance = config.get('Install', 'DefaultInstance')[0]
             except KeyError:
-                # no default instance, so nothing to enable
-                return
+                # no default instance, allow template only dependencies
+                template_only = True
+            else:
+                # otherwise rewrite the service name with the default instance
+                service = self.unit.replace("@.", "@{}.".format(default_instance))
 
-            service = self.unit.replace("@.",
-                                        "@{}.".format(default_instance))
-        else:
-            service = self.unit
-
-        self._process_deps(config, service, path, 'WantedBy', 'wants')
-        self._process_deps(config, service, path, 'RequiredBy', 'requires')
+        self._process_deps(config, service, path, 'WantedBy', 'wants',
+                           template_only)
+        self._process_deps(config, service, path, 'RequiredBy', 'requires',
+                           template_only)
 
         try:
             for also in config.get('Install', 'Also'):