[OE-core] [OE-Core][PATCH v2 2/3] systemd-systemctl: Restore support for enable command

Alex Kiernan alex.kiernan at gmail.com
Wed May 8 15:57:27 UTC 2019


Refactor so that SystemdUnit is its own class, then add support for the
enable command. This restores the ability of systemd.bbclass to create
instances using syntax such as:

  SYSTEMD_SERVICE_${PN} = "serial-getty at ttyAMA0.service"

Signed-off-by: Alex Kiernan <alex.kiernan at gmail.com>
---

Changes in v2:
- restore support for enable command

 .../systemd/systemd-systemctl/systemctl       | 179 ++++++++++--------
 1 file changed, 102 insertions(+), 77 deletions(-)

diff --git a/meta/recipes-core/systemd/systemd-systemctl/systemctl b/meta/recipes-core/systemd/systemd-systemctl/systemctl
index 7fdaf8ce03d9..8d7b3ba32d62 100755
--- a/meta/recipes-core/systemd/systemd-systemctl/systemctl
+++ b/meta/recipes-core/systemd/systemd-systemctl/systemctl
@@ -20,6 +20,8 @@ SYSCONFDIR = Path("etc")
 BASE_LIBDIR = Path("lib")
 LIBDIR = Path("usr", "lib")
 
+locations = list()
+
 
 class SystemdFile():
     """Class representing a single systemd configuration file"""
@@ -111,12 +113,6 @@ class Presets():
 
     def _collect_presets(self, scope, root):
         """Collect list of preset files"""
-        locations = [SYSCONFDIR / "systemd"]
-        # Handle the usrmerge case by ignoring /lib when it's a symlink
-        if not BASE_LIBDIR.is_symlink():
-            locations.append(BASE_LIBDIR / "systemd")
-        locations.append(LIBDIR / "systemd")
-
         presets = dict()
         for location in locations:
             paths = (root / location / scope).glob("*.preset")
@@ -146,27 +142,6 @@ class Presets():
         return None
 
 
-def collect_services(root):
-    """Collect list of service files"""
-    locations = [SYSCONFDIR / "systemd"]
-    # Handle the usrmerge case by ignoring /lib when it's a symlink
-    if not BASE_LIBDIR.is_symlink():
-        locations.append(BASE_LIBDIR / "systemd")
-    locations.append(LIBDIR / "systemd")
-
-    services = dict()
-    for location in locations:
-        paths = (root / location / "system").glob("*")
-        for path in paths:
-            if path.is_dir():
-                continue
-            # implement earlier names override later ones
-            if path.name not in services:
-                services[path.name] = path
-
-    return services
-
-
 def add_link(path, target):
     try:
         path.parent.mkdir(parents=True)
@@ -177,69 +152,113 @@ def add_link(path, target):
         path.symlink_to(target)
 
 
-def process_deps(root, config, service, location, prop, dirstem):
-    systemdir = SYSCONFDIR / "systemd" / "system"
+class SystemdUnitNotFoundError(Exception):
+    pass
 
-    target = ROOT / location.relative_to(root)
-    try:
-        for dependent in config.get('Install', prop):
-            wants = root / systemdir / "{}.{}".format(dependent, dirstem) / service
-            add_link(wants, target)
 
-    except KeyError:
-        pass
+class SystemdUnit():
+    def __init__(self, root, unit):
+        self.root = root
+        self.unit = unit
+        self.config = None
+
+    def _path_for_unit(self, unit):
+        for location in locations:
+            path = self.root / location / "system" / unit
+            if path.exists():
+                return path
 
+        raise SystemdUnitNotFoundError(self.root, unit)
 
-def enable(root, service, location, services):
-    if location.is_symlink():
-        # ignore aliases
-        return
+    def _process_deps(self, config, service, location, prop, dirstem):
+        systemdir = self.root / SYSCONFDIR / "systemd" / "system"
+
+        target = ROOT / location.relative_to(self.root)
+        try:
+            for dependent in config.get('Install', prop):
+                wants = systemdir / "{}.{}".format(dependent, dirstem) / service
+                add_link(wants, target)
+
+        except KeyError:
+            pass
+
+    def enable(self):
+        # if we're enabling an instance, first extract the actual instance
+        # then figure out what the template unit is
+        template = re.match(r"[^@]+@(?P<instance>[^\.]*)\.", self.unit)
+        if template:
+            instance = template.group('instance')
+            unit = re.sub(r"@[^\.]*\.", "@.", self.unit, 1)
+        else:
+            instance = None
+            unit = self.unit
+
+        path = self._path_for_unit(unit)
+
+        if path.is_symlink():
+            # ignore aliases
+            return
 
-    config = SystemdFile(root, location)
-    template = re.match(r"[^@]+@(?P<instance>[^\.]*)\.", service)
-    if template:
-        instance = template.group('instance')
-        if not instance:
+        config = SystemdFile(self.root, path)
+        if instance == "":
             try:
-                instance = config.get('Install', 'DefaultInstance')[0]
-                service = service.replace("@.", "@{}.".format(instance))
+                default_instance = config.get('Install', 'DefaultInstance')[0]
             except KeyError:
-                pass
-        if instance is None:
-            return
-    else:
-        instance = None
+                # no default instance, so nothing to enable
+                return
 
-    process_deps(root, config, service, location, 'WantedBy', 'wants')
-    process_deps(root, config, service, location, 'RequiredBy', 'requires')
+            service = self.unit.replace("@.",
+                                        "@{}.".format(default_instance))
+        else:
+            service = self.unit
 
-    try:
-        for also in config.get('Install', 'Also'):
-            enable(root, also, services[also], services)
+        self._process_deps(config, service, path, 'WantedBy', 'wants')
+        self._process_deps(config, service, path, 'RequiredBy', 'requires')
 
-    except KeyError:
-        pass
+        try:
+            for also in config.get('Install', 'Also'):
+                SystemdUnit(self.root, also).enable()
 
-    systemdir = root / SYSCONFDIR / "systemd" / "system"
-    target = ROOT / location.relative_to(root)
-    try:
-        for dest in config.get('Install', 'Alias'):
-            alias = systemdir / dest
-            add_link(alias, target)
+        except KeyError:
+            pass
 
-    except KeyError:
-        pass
+        systemdir = self.root / SYSCONFDIR / "systemd" / "system"
+        target = ROOT / path.relative_to(self.root)
+        try:
+            for dest in config.get('Install', 'Alias'):
+                alias = systemdir / dest
+                add_link(alias, target)
+
+        except KeyError:
+            pass
+
+    def mask(self):
+        systemdir = self.root / SYSCONFDIR / "systemd" / "system"
+        add_link(systemdir / self.unit, "/dev/null")
+
+
+def collect_services(root):
+    """Collect list of service files"""
+    services = set()
+    for location in locations:
+        paths = (root / location / "system").glob("*")
+        for path in paths:
+            if path.is_dir():
+                continue
+            services.add(path.name)
+
+    return services
 
 
 def preset_all(root):
     presets = Presets('system-preset', root)
     services = collect_services(root)
 
-    for service, location in services.items():
+    for service in services:
         state = presets.state(service)
 
         if state == "enable" or state is None:
-            enable(root, service, location, services)
+            SystemdUnit(root, service).enable()
 
     # If we populate the systemd links we also create /etc/machine-id, which
     # allows systemd to boot with the filesystem read-only before generating
@@ -251,18 +270,13 @@ def preset_all(root):
     (root / SYSCONFDIR / "machine-id").touch()
 
 
-def mask(root, *services):
-    systemdir = root / SYSCONFDIR / "systemd" / "system"
-    for service in services:
-        add_link(systemdir / service, "/dev/null")
-
-
 def main():
     if sys.version_info < (3, 4, 0):
         sys.exit("Python 3.4 or greater is required")
 
     parser = argparse.ArgumentParser()
-    parser.add_argument('command', nargs=1, choices=['mask', 'preset-all'])
+    parser.add_argument('command', nargs=1, choices=['enable', 'mask',
+                                                     'preset-all'])
     parser.add_argument('service', nargs=argparse.REMAINDER)
     parser.add_argument('--root')
     parser.add_argument('--preset-mode',
@@ -272,9 +286,20 @@ def main():
     args = parser.parse_args()
 
     root = Path(args.root) if args.root else ROOT
+
+    locations.append(SYSCONFDIR / "systemd")
+    # Handle the usrmerge case by ignoring /lib when it's a symlink
+    if not (root / BASE_LIBDIR).is_symlink():
+        locations.append(BASE_LIBDIR / "systemd")
+    locations.append(LIBDIR / "systemd")
+
     command = args.command[0]
     if command == "mask":
-        mask(root, *args.service)
+        for service in args.service:
+            SystemdUnit(root, service).mask()
+    elif command == "enable":
+        for service in args.service:
+            SystemdUnit(root, service).enable()
     elif command == "preset-all":
         if len(args.service) != 0:
             sys.exit("Too many arguments.")
-- 
2.17.1



More information about the Openembedded-core mailing list