* [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui
@ 2022-10-12 4:03 Wang Mingyu
2022-10-12 17:20 ` Jose Quaresma
0 siblings, 1 reply; 9+ messages in thread
From: Wang Mingyu @ 2022-10-12 4:03 UTC (permalink / raw)
To: openembedded-devel; +Cc: Wang Mingyu
Signed-off-by: Wang Mingyu <wangmy@fujitsu.com>
---
.../systemd/nativesdk-systemd-systemctl.bb | 17 +
.../systemd/systemd-systemctl/systemctl | 340 ++++++++++++++++++
2 files changed, 357 insertions(+)
create mode 100644 meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
create mode 100755 meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
diff --git a/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
new file mode 100644
index 0000000000..7ac21aa260
--- /dev/null
+++ b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
@@ -0,0 +1,17 @@
+SUMMARY = "Wrapper for enabling systemd services"
+
+LICENSE = "MIT"
+LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
+
+PR = "r6"
+
+inherit nativesdk
+
+SRC_URI = "file://systemctl"
+
+S = "${WORKDIR}"
+
+do_install() {
+ install -d ${D}${bindir}
+ install -m 0755 ${WORKDIR}/systemctl ${D}${bindir}
+}
diff --git a/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
new file mode 100755
index 0000000000..6324319a45
--- /dev/null
+++ b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
@@ -0,0 +1,340 @@
+#!/usr/bin/env python3
+"""systemctl: subset of systemctl used for image construction
+
+Mask/preset systemd units
+"""
+
+import argparse
+import fnmatch
+import os
+import re
+import sys
+
+from collections import namedtuple
+from pathlib import Path
+
+version = 1.0
+
+ROOT = Path("/")
+SYSCONFDIR = Path("etc")
+BASE_LIBDIR = Path("lib")
+LIBDIR = Path("usr", "lib")
+
+locations = list()
+
+
+class SystemdFile():
+ """Class representing a single systemd configuration file"""
+ def __init__(self, root, path):
+ self.sections = dict()
+ self._parse(root, path)
+ dirname = os.path.basename(path.name) + ".d"
+ for location in locations:
+ for path2 in sorted((root / location / "system" / dirname).glob("*.conf")):
+ self._parse(root, path2)
+
+ def _parse(self, root, path):
+ """Parse a systemd syntax configuration file
+
+ Args:
+ path: A pathlib.Path object pointing to the file
+
+ """
+ skip_re = re.compile(r"^\s*([#;]|$)")
+ section_re = re.compile(r"^\s*\[(?P<section>.*)\]")
+ kv_re = re.compile(r"^\s*(?P<key>[^\s]+)\s*=\s*(?P<value>.*)")
+ section = None
+
+ if path.is_symlink():
+ try:
+ path.resolve()
+ except FileNotFoundError:
+ # broken symlink, try relative to root
+ path = root / Path(os.readlink(str(path))).relative_to(ROOT)
+
+ with path.open() as f:
+ for line in f:
+ if skip_re.match(line):
+ continue
+
+ line = line.strip()
+ m = section_re.match(line)
+ if m:
+ if m.group('section') not in self.sections:
+ section = dict()
+ self.sections[m.group('section')] = section
+ else:
+ section = self.sections[m.group('section')]
+ continue
+
+ while line.endswith("\\"):
+ line += f.readline().rstrip("\n")
+
+ m = kv_re.match(line)
+ k = m.group('key')
+ v = m.group('value')
+ if k not in section:
+ section[k] = list()
+ section[k].extend(v.split())
+
+ def get(self, section, prop):
+ """Get a property from section
+
+ Args:
+ section: Section to retrieve property from
+ prop: Property to retrieve
+
+ Returns:
+ List representing all properties of type prop in section.
+
+ Raises:
+ KeyError: if ``section`` or ``prop`` not found
+ """
+ return self.sections[section][prop]
+
+
+class Presets():
+ """Class representing all systemd presets"""
+ def __init__(self, scope, root):
+ self.directives = list()
+ self._collect_presets(scope, root)
+
+ def _parse_presets(self, presets):
+ """Parse presets out of a set of preset files"""
+ skip_re = re.compile(r"^\s*([#;]|$)")
+ directive_re = re.compile(r"^\s*(?P<action>enable|disable)\s+(?P<unit_name>(.+))")
+
+ Directive = namedtuple("Directive", "action unit_name")
+ for preset in presets:
+ with preset.open() as f:
+ for line in f:
+ m = directive_re.match(line)
+ if m:
+ directive = Directive(action=m.group('action'),
+ unit_name=m.group('unit_name'))
+ self.directives.append(directive)
+ elif skip_re.match(line):
+ pass
+ else:
+ sys.exit("Unparsed preset line in {}".format(preset))
+
+ def _collect_presets(self, scope, root):
+ """Collect list of preset files"""
+ presets = dict()
+ for location in locations:
+ paths = (root / location / scope).glob("*.preset")
+ for path in paths:
+ # earlier names override later ones
+ if path.name not in presets:
+ presets[path.name] = path
+
+ self._parse_presets([v for k, v in sorted(presets.items())])
+
+ def state(self, unit_name):
+ """Return state of preset for unit_name
+
+ Args:
+ presets: set of presets
+ unit_name: name of the unit
+
+ Returns:
+ None: no matching preset
+ `enable`: unit_name is enabled
+ `disable`: unit_name is disabled
+ """
+ for directive in self.directives:
+ if fnmatch.fnmatch(unit_name, directive.unit_name):
+ return directive.action
+
+ return None
+
+
+def add_link(path, target):
+ try:
+ path.parent.mkdir(parents=True)
+ except FileExistsError:
+ pass
+ if not path.is_symlink():
+ print("ln -s {} {}".format(target, path))
+ path.symlink_to(target)
+
+
+class SystemdUnitNotFoundError(Exception):
+ def __init__(self, path, unit):
+ self.path = path
+ self.unit = unit
+
+
+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() or path.is_symlink():
+ return path
+
+ raise SystemdUnitNotFoundError(self.root, unit)
+
+ 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, caller_unit=None):
+ # 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(self.root, path)
+ if instance == "":
+ try:
+ default_instance = config.get('Install', 'DefaultInstance')[0]
+ except KeyError:
+ # no default instance, so nothing to enable
+ return
+
+ 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')
+
+ try:
+ for also in config.get('Install', 'Also'):
+ try:
+ if caller_unit != also:
+ SystemdUnit(self.root, also).enable(unit)
+ except SystemdUnitNotFoundError as e:
+ sys.exit("Error: Systemctl also enable issue with %s (%s)" % (service, e.unit))
+
+ 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 in services:
+ state = presets.state(service)
+
+ if state == "enable" or state is None:
+ try:
+ SystemdUnit(root, service).enable()
+ except SystemdUnitNotFoundError:
+ sys.exit("Error: Systemctl preset_all issue in %s" % service)
+
+ # If we populate the systemd links we also create /etc/machine-id, which
+ # allows systemd to boot with the filesystem read-only before generating
+ # a real value and then committing it back.
+ #
+ # For the stateless configuration, where /etc is generated at runtime
+ # (for example on a tmpfs), this script shouldn't run at all and we
+ # allow systemd to completely populate /etc.
+ (root / SYSCONFDIR / "machine-id").touch()
+
+
+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='?', choices=['enable', 'mask',
+ 'preset-all'])
+ parser.add_argument('service', nargs=argparse.REMAINDER)
+ parser.add_argument('--root')
+ parser.add_argument('--preset-mode',
+ choices=['full', 'enable-only', 'disable-only'],
+ default='full')
+
+ 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
+ if not command:
+ parser.print_help()
+ return 0
+
+ if command == "mask":
+ for service in args.service:
+ try:
+ SystemdUnit(root, service).mask()
+ except SystemdUnitNotFoundError as e:
+ sys.exit("Error: Systemctl main mask issue in %s (%s)" % (service, e.unit))
+ elif command == "enable":
+ for service in args.service:
+ try:
+ SystemdUnit(root, service).enable()
+ except SystemdUnitNotFoundError as e:
+ sys.exit("Error: Systemctl main enable issue in %s (%s)" % (service, e.unit))
+ elif command == "preset-all":
+ if len(args.service) != 0:
+ sys.exit("Too many arguments.")
+ if args.preset_mode != "enable-only":
+ sys.exit("Only enable-only is supported as preset-mode.")
+ preset_all(root)
+ else:
+ raise RuntimeError()
+
+
+if __name__ == '__main__':
+ main()
--
2.25.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui
2022-10-12 4:03 [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui Wang Mingyu
@ 2022-10-12 17:20 ` Jose Quaresma
2022-10-12 22:27 ` Khem Raj
0 siblings, 1 reply; 9+ messages in thread
From: Jose Quaresma @ 2022-10-12 17:20 UTC (permalink / raw)
To: wangmy; +Cc: openembedded-devel
[-- Attachment #1: Type: text/plain, Size: 14385 bytes --]
Hi wangmy,
wangmy <wangmy@fujitsu.com> escreveu no dia quarta, 12/10/2022 à(s) 05:04:
> Signed-off-by: Wang Mingyu <wangmy@fujitsu.com>
> ---
> .../systemd/nativesdk-systemd-systemctl.bb | 17 +
> .../systemd/systemd-systemctl/systemctl | 340 ++++++++++++++++++
> 2 files changed, 357 insertions(+)
> create mode 100644 meta-oe/recipes-devtools/systemd/
> nativesdk-systemd-systemctl.bb
> create mode 100755
> meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
>
> diff --git a/meta-oe/recipes-devtools/systemd/
> nativesdk-systemd-systemctl.bb b/meta-oe/recipes-devtools/systemd/
> nativesdk-systemd-systemctl.bb
> new file mode 100644
> index 0000000000..7ac21aa260
> --- /dev/null
> +++ b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
> @@ -0,0 +1,17 @@
> +SUMMARY = "Wrapper for enabling systemd services"
> +
> +LICENSE = "MIT"
> +LIC_FILES_CHKSUM =
> "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
> +
> +PR = "r6"
>
Why is this recipe starting with the package release 6 ?
Jose
> +
> +inherit nativesdk
> +
> +SRC_URI = "file://systemctl"
> +
> +S = "${WORKDIR}"
> +
> +do_install() {
> + install -d ${D}${bindir}
> + install -m 0755 ${WORKDIR}/systemctl ${D}${bindir}
> +}
> diff --git a/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> new file mode 100755
> index 0000000000..6324319a45
> --- /dev/null
> +++ b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> @@ -0,0 +1,340 @@
> +#!/usr/bin/env python3
> +"""systemctl: subset of systemctl used for image construction
> +
> +Mask/preset systemd units
> +"""
> +
> +import argparse
> +import fnmatch
> +import os
> +import re
> +import sys
> +
> +from collections import namedtuple
> +from pathlib import Path
> +
> +version = 1.0
> +
> +ROOT = Path("/")
> +SYSCONFDIR = Path("etc")
> +BASE_LIBDIR = Path("lib")
> +LIBDIR = Path("usr", "lib")
> +
> +locations = list()
> +
> +
> +class SystemdFile():
> + """Class representing a single systemd configuration file"""
> + def __init__(self, root, path):
> + self.sections = dict()
> + self._parse(root, path)
> + dirname = os.path.basename(path.name) + ".d"
> + for location in locations:
> + for path2 in sorted((root / location / "system" /
> dirname).glob("*.conf")):
> + self._parse(root, path2)
> +
> + def _parse(self, root, path):
> + """Parse a systemd syntax configuration file
> +
> + Args:
> + path: A pathlib.Path object pointing to the file
> +
> + """
> + skip_re = re.compile(r"^\s*([#;]|$)")
> + section_re = re.compile(r"^\s*\[(?P<section>.*)\]")
> + kv_re = re.compile(r"^\s*(?P<key>[^\s]+)\s*=\s*(?P<value>.*)")
> + section = None
> +
> + if path.is_symlink():
> + try:
> + path.resolve()
> + except FileNotFoundError:
> + # broken symlink, try relative to root
> + path = root /
> Path(os.readlink(str(path))).relative_to(ROOT)
> +
> + with path.open() as f:
> + for line in f:
> + if skip_re.match(line):
> + continue
> +
> + line = line.strip()
> + m = section_re.match(line)
> + if m:
> + if m.group('section') not in self.sections:
> + section = dict()
> + self.sections[m.group('section')] = section
> + else:
> + section = self.sections[m.group('section')]
> + continue
> +
> + while line.endswith("\\"):
> + line += f.readline().rstrip("\n")
> +
> + m = kv_re.match(line)
> + k = m.group('key')
> + v = m.group('value')
> + if k not in section:
> + section[k] = list()
> + section[k].extend(v.split())
> +
> + def get(self, section, prop):
> + """Get a property from section
> +
> + Args:
> + section: Section to retrieve property from
> + prop: Property to retrieve
> +
> + Returns:
> + List representing all properties of type prop in section.
> +
> + Raises:
> + KeyError: if ``section`` or ``prop`` not found
> + """
> + return self.sections[section][prop]
> +
> +
> +class Presets():
> + """Class representing all systemd presets"""
> + def __init__(self, scope, root):
> + self.directives = list()
> + self._collect_presets(scope, root)
> +
> + def _parse_presets(self, presets):
> + """Parse presets out of a set of preset files"""
> + skip_re = re.compile(r"^\s*([#;]|$)")
> + directive_re =
> re.compile(r"^\s*(?P<action>enable|disable)\s+(?P<unit_name>(.+))")
> +
> + Directive = namedtuple("Directive", "action unit_name")
> + for preset in presets:
> + with preset.open() as f:
> + for line in f:
> + m = directive_re.match(line)
> + if m:
> + directive = Directive(action=m.group('action'),
> +
> unit_name=m.group('unit_name'))
> + self.directives.append(directive)
> + elif skip_re.match(line):
> + pass
> + else:
> + sys.exit("Unparsed preset line in
> {}".format(preset))
> +
> + def _collect_presets(self, scope, root):
> + """Collect list of preset files"""
> + presets = dict()
> + for location in locations:
> + paths = (root / location / scope).glob("*.preset")
> + for path in paths:
> + # earlier names override later ones
> + if path.name not in presets:
> + presets[path.name] = path
> +
> + self._parse_presets([v for k, v in sorted(presets.items())])
> +
> + def state(self, unit_name):
> + """Return state of preset for unit_name
> +
> + Args:
> + presets: set of presets
> + unit_name: name of the unit
> +
> + Returns:
> + None: no matching preset
> + `enable`: unit_name is enabled
> + `disable`: unit_name is disabled
> + """
> + for directive in self.directives:
> + if fnmatch.fnmatch(unit_name, directive.unit_name):
> + return directive.action
> +
> + return None
> +
> +
> +def add_link(path, target):
> + try:
> + path.parent.mkdir(parents=True)
> + except FileExistsError:
> + pass
> + if not path.is_symlink():
> + print("ln -s {} {}".format(target, path))
> + path.symlink_to(target)
> +
> +
> +class SystemdUnitNotFoundError(Exception):
> + def __init__(self, path, unit):
> + self.path = path
> + self.unit = unit
> +
> +
> +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() or path.is_symlink():
> + return path
> +
> + raise SystemdUnitNotFoundError(self.root, unit)
> +
> + 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, caller_unit=None):
> + # 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(self.root, path)
> + if instance == "":
> + try:
> + default_instance = config.get('Install',
> 'DefaultInstance')[0]
> + except KeyError:
> + # no default instance, so nothing to enable
> + return
> +
> + 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')
> +
> + try:
> + for also in config.get('Install', 'Also'):
> + try:
> + if caller_unit != also:
> + SystemdUnit(self.root, also).enable(unit)
> + except SystemdUnitNotFoundError as e:
> + sys.exit("Error: Systemctl also enable issue with %s
> (%s)" % (service, e.unit))
> +
> + 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 in services:
> + state = presets.state(service)
> +
> + if state == "enable" or state is None:
> + try:
> + SystemdUnit(root, service).enable()
> + except SystemdUnitNotFoundError:
> + sys.exit("Error: Systemctl preset_all issue in %s" %
> service)
> +
> + # If we populate the systemd links we also create /etc/machine-id,
> which
> + # allows systemd to boot with the filesystem read-only before
> generating
> + # a real value and then committing it back.
> + #
> + # For the stateless configuration, where /etc is generated at runtime
> + # (for example on a tmpfs), this script shouldn't run at all and we
> + # allow systemd to completely populate /etc.
> + (root / SYSCONFDIR / "machine-id").touch()
> +
> +
> +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='?', choices=['enable', 'mask',
> + 'preset-all'])
> + parser.add_argument('service', nargs=argparse.REMAINDER)
> + parser.add_argument('--root')
> + parser.add_argument('--preset-mode',
> + choices=['full', 'enable-only', 'disable-only'],
> + default='full')
> +
> + 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
> + if not command:
> + parser.print_help()
> + return 0
> +
> + if command == "mask":
> + for service in args.service:
> + try:
> + SystemdUnit(root, service).mask()
> + except SystemdUnitNotFoundError as e:
> + sys.exit("Error: Systemctl main mask issue in %s (%s)" %
> (service, e.unit))
> + elif command == "enable":
> + for service in args.service:
> + try:
> + SystemdUnit(root, service).enable()
> + except SystemdUnitNotFoundError as e:
> + sys.exit("Error: Systemctl main enable issue in %s (%s)"
> % (service, e.unit))
> + elif command == "preset-all":
> + if len(args.service) != 0:
> + sys.exit("Too many arguments.")
> + if args.preset_mode != "enable-only":
> + sys.exit("Only enable-only is supported as preset-mode.")
> + preset_all(root)
> + else:
> + raise RuntimeError()
> +
> +
> +if __name__ == '__main__':
> + main()
> --
> 2.25.1
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#99142):
> https://lists.openembedded.org/g/openembedded-devel/message/99142
> Mute This Topic: https://lists.openembedded.org/mt/94275098/5052612
> Group Owner: openembedded-devel+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [
> quaresma.jose@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
>
--
Best regards,
José Quaresma
[-- Attachment #2: Type: text/html, Size: 19466 bytes --]
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui
2022-10-12 17:20 ` Jose Quaresma
@ 2022-10-12 22:27 ` Khem Raj
2022-10-13 10:09 ` Ross Burton
0 siblings, 1 reply; 9+ messages in thread
From: Khem Raj @ 2022-10-12 22:27 UTC (permalink / raw)
To: Jose Quaresma; +Cc: wangmy, openembedded-devel
On Wed, Oct 12, 2022 at 10:20 AM Jose Quaresma <quaresma.jose@gmail.com> wrote:
>
> Hi wangmy,
>
> wangmy <wangmy@fujitsu.com> escreveu no dia quarta, 12/10/2022 à(s) 05:04:
>>
>> Signed-off-by: Wang Mingyu <wangmy@fujitsu.com>
>> ---
>> .../systemd/nativesdk-systemd-systemctl.bb | 17 +
>> .../systemd/systemd-systemctl/systemctl | 340 ++++++++++++++++++
>> 2 files changed, 357 insertions(+)
>> create mode 100644 meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
>> create mode 100755 meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
>>
>> diff --git a/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
>> new file mode 100644
>> index 0000000000..7ac21aa260
>> --- /dev/null
>> +++ b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
>> @@ -0,0 +1,17 @@
>> +SUMMARY = "Wrapper for enabling systemd services"
>> +
>> +LICENSE = "MIT"
>> +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
>> +
>> +PR = "r6"
>
>
> Why is this recipe starting with the package release 6 ?
>
Right, I have fixed it before accepting.
> Jose
>
>>
>> +
>> +inherit nativesdk
>> +
>> +SRC_URI = "file://systemctl"
>> +
>> +S = "${WORKDIR}"
>> +
>> +do_install() {
>> + install -d ${D}${bindir}
>> + install -m 0755 ${WORKDIR}/systemctl ${D}${bindir}
>> +}
>> diff --git a/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
>> new file mode 100755
>> index 0000000000..6324319a45
>> --- /dev/null
>> +++ b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
>> @@ -0,0 +1,340 @@
>> +#!/usr/bin/env python3
>> +"""systemctl: subset of systemctl used for image construction
>> +
>> +Mask/preset systemd units
>> +"""
>> +
>> +import argparse
>> +import fnmatch
>> +import os
>> +import re
>> +import sys
>> +
>> +from collections import namedtuple
>> +from pathlib import Path
>> +
>> +version = 1.0
>> +
>> +ROOT = Path("/")
>> +SYSCONFDIR = Path("etc")
>> +BASE_LIBDIR = Path("lib")
>> +LIBDIR = Path("usr", "lib")
>> +
>> +locations = list()
>> +
>> +
>> +class SystemdFile():
>> + """Class representing a single systemd configuration file"""
>> + def __init__(self, root, path):
>> + self.sections = dict()
>> + self._parse(root, path)
>> + dirname = os.path.basename(path.name) + ".d"
>> + for location in locations:
>> + for path2 in sorted((root / location / "system" / dirname).glob("*.conf")):
>> + self._parse(root, path2)
>> +
>> + def _parse(self, root, path):
>> + """Parse a systemd syntax configuration file
>> +
>> + Args:
>> + path: A pathlib.Path object pointing to the file
>> +
>> + """
>> + skip_re = re.compile(r"^\s*([#;]|$)")
>> + section_re = re.compile(r"^\s*\[(?P<section>.*)\]")
>> + kv_re = re.compile(r"^\s*(?P<key>[^\s]+)\s*=\s*(?P<value>.*)")
>> + section = None
>> +
>> + if path.is_symlink():
>> + try:
>> + path.resolve()
>> + except FileNotFoundError:
>> + # broken symlink, try relative to root
>> + path = root / Path(os.readlink(str(path))).relative_to(ROOT)
>> +
>> + with path.open() as f:
>> + for line in f:
>> + if skip_re.match(line):
>> + continue
>> +
>> + line = line.strip()
>> + m = section_re.match(line)
>> + if m:
>> + if m.group('section') not in self.sections:
>> + section = dict()
>> + self.sections[m.group('section')] = section
>> + else:
>> + section = self.sections[m.group('section')]
>> + continue
>> +
>> + while line.endswith("\\"):
>> + line += f.readline().rstrip("\n")
>> +
>> + m = kv_re.match(line)
>> + k = m.group('key')
>> + v = m.group('value')
>> + if k not in section:
>> + section[k] = list()
>> + section[k].extend(v.split())
>> +
>> + def get(self, section, prop):
>> + """Get a property from section
>> +
>> + Args:
>> + section: Section to retrieve property from
>> + prop: Property to retrieve
>> +
>> + Returns:
>> + List representing all properties of type prop in section.
>> +
>> + Raises:
>> + KeyError: if ``section`` or ``prop`` not found
>> + """
>> + return self.sections[section][prop]
>> +
>> +
>> +class Presets():
>> + """Class representing all systemd presets"""
>> + def __init__(self, scope, root):
>> + self.directives = list()
>> + self._collect_presets(scope, root)
>> +
>> + def _parse_presets(self, presets):
>> + """Parse presets out of a set of preset files"""
>> + skip_re = re.compile(r"^\s*([#;]|$)")
>> + directive_re = re.compile(r"^\s*(?P<action>enable|disable)\s+(?P<unit_name>(.+))")
>> +
>> + Directive = namedtuple("Directive", "action unit_name")
>> + for preset in presets:
>> + with preset.open() as f:
>> + for line in f:
>> + m = directive_re.match(line)
>> + if m:
>> + directive = Directive(action=m.group('action'),
>> + unit_name=m.group('unit_name'))
>> + self.directives.append(directive)
>> + elif skip_re.match(line):
>> + pass
>> + else:
>> + sys.exit("Unparsed preset line in {}".format(preset))
>> +
>> + def _collect_presets(self, scope, root):
>> + """Collect list of preset files"""
>> + presets = dict()
>> + for location in locations:
>> + paths = (root / location / scope).glob("*.preset")
>> + for path in paths:
>> + # earlier names override later ones
>> + if path.name not in presets:
>> + presets[path.name] = path
>> +
>> + self._parse_presets([v for k, v in sorted(presets.items())])
>> +
>> + def state(self, unit_name):
>> + """Return state of preset for unit_name
>> +
>> + Args:
>> + presets: set of presets
>> + unit_name: name of the unit
>> +
>> + Returns:
>> + None: no matching preset
>> + `enable`: unit_name is enabled
>> + `disable`: unit_name is disabled
>> + """
>> + for directive in self.directives:
>> + if fnmatch.fnmatch(unit_name, directive.unit_name):
>> + return directive.action
>> +
>> + return None
>> +
>> +
>> +def add_link(path, target):
>> + try:
>> + path.parent.mkdir(parents=True)
>> + except FileExistsError:
>> + pass
>> + if not path.is_symlink():
>> + print("ln -s {} {}".format(target, path))
>> + path.symlink_to(target)
>> +
>> +
>> +class SystemdUnitNotFoundError(Exception):
>> + def __init__(self, path, unit):
>> + self.path = path
>> + self.unit = unit
>> +
>> +
>> +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() or path.is_symlink():
>> + return path
>> +
>> + raise SystemdUnitNotFoundError(self.root, unit)
>> +
>> + 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, caller_unit=None):
>> + # 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(self.root, path)
>> + if instance == "":
>> + try:
>> + default_instance = config.get('Install', 'DefaultInstance')[0]
>> + except KeyError:
>> + # no default instance, so nothing to enable
>> + return
>> +
>> + 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')
>> +
>> + try:
>> + for also in config.get('Install', 'Also'):
>> + try:
>> + if caller_unit != also:
>> + SystemdUnit(self.root, also).enable(unit)
>> + except SystemdUnitNotFoundError as e:
>> + sys.exit("Error: Systemctl also enable issue with %s (%s)" % (service, e.unit))
>> +
>> + 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 in services:
>> + state = presets.state(service)
>> +
>> + if state == "enable" or state is None:
>> + try:
>> + SystemdUnit(root, service).enable()
>> + except SystemdUnitNotFoundError:
>> + sys.exit("Error: Systemctl preset_all issue in %s" % service)
>> +
>> + # If we populate the systemd links we also create /etc/machine-id, which
>> + # allows systemd to boot with the filesystem read-only before generating
>> + # a real value and then committing it back.
>> + #
>> + # For the stateless configuration, where /etc is generated at runtime
>> + # (for example on a tmpfs), this script shouldn't run at all and we
>> + # allow systemd to completely populate /etc.
>> + (root / SYSCONFDIR / "machine-id").touch()
>> +
>> +
>> +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='?', choices=['enable', 'mask',
>> + 'preset-all'])
>> + parser.add_argument('service', nargs=argparse.REMAINDER)
>> + parser.add_argument('--root')
>> + parser.add_argument('--preset-mode',
>> + choices=['full', 'enable-only', 'disable-only'],
>> + default='full')
>> +
>> + 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
>> + if not command:
>> + parser.print_help()
>> + return 0
>> +
>> + if command == "mask":
>> + for service in args.service:
>> + try:
>> + SystemdUnit(root, service).mask()
>> + except SystemdUnitNotFoundError as e:
>> + sys.exit("Error: Systemctl main mask issue in %s (%s)" % (service, e.unit))
>> + elif command == "enable":
>> + for service in args.service:
>> + try:
>> + SystemdUnit(root, service).enable()
>> + except SystemdUnitNotFoundError as e:
>> + sys.exit("Error: Systemctl main enable issue in %s (%s)" % (service, e.unit))
>> + elif command == "preset-all":
>> + if len(args.service) != 0:
>> + sys.exit("Too many arguments.")
>> + if args.preset_mode != "enable-only":
>> + sys.exit("Only enable-only is supported as preset-mode.")
>> + preset_all(root)
>> + else:
>> + raise RuntimeError()
>> +
>> +
>> +if __name__ == '__main__':
>> + main()
>> --
>> 2.25.1
>>
>>
>>
>>
>
>
> --
> Best regards,
>
> José Quaresma
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#99146): https://lists.openembedded.org/g/openembedded-devel/message/99146
> Mute This Topic: https://lists.openembedded.org/mt/94275098/1997914
> Group Owner: openembedded-devel+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [raj.khem@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui
2022-10-12 22:27 ` Khem Raj
@ 2022-10-13 10:09 ` Ross Burton
2022-10-13 15:35 ` Khem Raj
2022-10-14 0:23 ` wangmy
0 siblings, 2 replies; 9+ messages in thread
From: Ross Burton @ 2022-10-13 10:09 UTC (permalink / raw)
To: raj.khem; +Cc: Jose Quaresma, wangmy, openembedded-devel
What’s the difference between this python implementation of systemctl, and the shell implementation that’s already in oe-core in systemd-systemctl-native?
Looks like pointless duplication to me. If the Python implementation here is better, then it should be in core.
Also, why would a nativesdk DNF need to manage systemd units?
Ross
> On 12 Oct 2022, at 23:27, Khem Raj via lists.openembedded.org <raj.khem=gmail.com@lists.openembedded.org> wrote:
>
> On Wed, Oct 12, 2022 at 10:20 AM Jose Quaresma <quaresma.jose@gmail.com> wrote:
>>
>> Hi wangmy,
>>
>> wangmy <wangmy@fujitsu.com> escreveu no dia quarta, 12/10/2022 à(s) 05:04:
>>>
>>> Signed-off-by: Wang Mingyu <wangmy@fujitsu.com>
>>> ---
>>> .../systemd/nativesdk-systemd-systemctl.bb | 17 +
>>> .../systemd/systemd-systemctl/systemctl | 340 ++++++++++++++++++
>>> 2 files changed, 357 insertions(+)
>>> create mode 100644 meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
>>> create mode 100755 meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
>>>
>>> diff --git a/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
>>> new file mode 100644
>>> index 0000000000..7ac21aa260
>>> --- /dev/null
>>> +++ b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
>>> @@ -0,0 +1,17 @@
>>> +SUMMARY = "Wrapper for enabling systemd services"
>>> +
>>> +LICENSE = "MIT"
>>> +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
>>> +
>>> +PR = "r6"
>>
>>
>> Why is this recipe starting with the package release 6 ?
>>
>
> Right, I have fixed it before accepting.
>
>> Jose
>>
>>>
>>> +
>>> +inherit nativesdk
>>> +
>>> +SRC_URI = "file://systemctl"
>>> +
>>> +S = "${WORKDIR}"
>>> +
>>> +do_install() {
>>> + install -d ${D}${bindir}
>>> + install -m 0755 ${WORKDIR}/systemctl ${D}${bindir}
>>> +}
>>> diff --git a/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
>>> new file mode 100755
>>> index 0000000000..6324319a45
>>> --- /dev/null
>>> +++ b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
>>> @@ -0,0 +1,340 @@
>>> +#!/usr/bin/env python3
>>> +"""systemctl: subset of systemctl used for image construction
>>> +
>>> +Mask/preset systemd units
>>> +"""
>>> +
>>> +import argparse
>>> +import fnmatch
>>> +import os
>>> +import re
>>> +import sys
>>> +
>>> +from collections import namedtuple
>>> +from pathlib import Path
>>> +
>>> +version = 1.0
>>> +
>>> +ROOT = Path("/")
>>> +SYSCONFDIR = Path("etc")
>>> +BASE_LIBDIR = Path("lib")
>>> +LIBDIR = Path("usr", "lib")
>>> +
>>> +locations = list()
>>> +
>>> +
>>> +class SystemdFile():
>>> + """Class representing a single systemd configuration file"""
>>> + def __init__(self, root, path):
>>> + self.sections = dict()
>>> + self._parse(root, path)
>>> + dirname = os.path.basename(path.name) + ".d"
>>> + for location in locations:
>>> + for path2 in sorted((root / location / "system" / dirname).glob("*.conf")):
>>> + self._parse(root, path2)
>>> +
>>> + def _parse(self, root, path):
>>> + """Parse a systemd syntax configuration file
>>> +
>>> + Args:
>>> + path: A pathlib.Path object pointing to the file
>>> +
>>> + """
>>> + skip_re = re.compile(r"^\s*([#;]|$)")
>>> + section_re = re.compile(r"^\s*\[(?P<section>.*)\]")
>>> + kv_re = re.compile(r"^\s*(?P<key>[^\s]+)\s*=\s*(?P<value>.*)")
>>> + section = None
>>> +
>>> + if path.is_symlink():
>>> + try:
>>> + path.resolve()
>>> + except FileNotFoundError:
>>> + # broken symlink, try relative to root
>>> + path = root / Path(os.readlink(str(path))).relative_to(ROOT)
>>> +
>>> + with path.open() as f:
>>> + for line in f:
>>> + if skip_re.match(line):
>>> + continue
>>> +
>>> + line = line.strip()
>>> + m = section_re.match(line)
>>> + if m:
>>> + if m.group('section') not in self.sections:
>>> + section = dict()
>>> + self.sections[m.group('section')] = section
>>> + else:
>>> + section = self.sections[m.group('section')]
>>> + continue
>>> +
>>> + while line.endswith("\\"):
>>> + line += f.readline().rstrip("\n")
>>> +
>>> + m = kv_re.match(line)
>>> + k = m.group('key')
>>> + v = m.group('value')
>>> + if k not in section:
>>> + section[k] = list()
>>> + section[k].extend(v.split())
>>> +
>>> + def get(self, section, prop):
>>> + """Get a property from section
>>> +
>>> + Args:
>>> + section: Section to retrieve property from
>>> + prop: Property to retrieve
>>> +
>>> + Returns:
>>> + List representing all properties of type prop in section.
>>> +
>>> + Raises:
>>> + KeyError: if ``section`` or ``prop`` not found
>>> + """
>>> + return self.sections[section][prop]
>>> +
>>> +
>>> +class Presets():
>>> + """Class representing all systemd presets"""
>>> + def __init__(self, scope, root):
>>> + self.directives = list()
>>> + self._collect_presets(scope, root)
>>> +
>>> + def _parse_presets(self, presets):
>>> + """Parse presets out of a set of preset files"""
>>> + skip_re = re.compile(r"^\s*([#;]|$)")
>>> + directive_re = re.compile(r"^\s*(?P<action>enable|disable)\s+(?P<unit_name>(.+))")
>>> +
>>> + Directive = namedtuple("Directive", "action unit_name")
>>> + for preset in presets:
>>> + with preset.open() as f:
>>> + for line in f:
>>> + m = directive_re.match(line)
>>> + if m:
>>> + directive = Directive(action=m.group('action'),
>>> + unit_name=m.group('unit_name'))
>>> + self.directives.append(directive)
>>> + elif skip_re.match(line):
>>> + pass
>>> + else:
>>> + sys.exit("Unparsed preset line in {}".format(preset))
>>> +
>>> + def _collect_presets(self, scope, root):
>>> + """Collect list of preset files"""
>>> + presets = dict()
>>> + for location in locations:
>>> + paths = (root / location / scope).glob("*.preset")
>>> + for path in paths:
>>> + # earlier names override later ones
>>> + if path.name not in presets:
>>> + presets[path.name] = path
>>> +
>>> + self._parse_presets([v for k, v in sorted(presets.items())])
>>> +
>>> + def state(self, unit_name):
>>> + """Return state of preset for unit_name
>>> +
>>> + Args:
>>> + presets: set of presets
>>> + unit_name: name of the unit
>>> +
>>> + Returns:
>>> + None: no matching preset
>>> + `enable`: unit_name is enabled
>>> + `disable`: unit_name is disabled
>>> + """
>>> + for directive in self.directives:
>>> + if fnmatch.fnmatch(unit_name, directive.unit_name):
>>> + return directive.action
>>> +
>>> + return None
>>> +
>>> +
>>> +def add_link(path, target):
>>> + try:
>>> + path.parent.mkdir(parents=True)
>>> + except FileExistsError:
>>> + pass
>>> + if not path.is_symlink():
>>> + print("ln -s {} {}".format(target, path))
>>> + path.symlink_to(target)
>>> +
>>> +
>>> +class SystemdUnitNotFoundError(Exception):
>>> + def __init__(self, path, unit):
>>> + self.path = path
>>> + self.unit = unit
>>> +
>>> +
>>> +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() or path.is_symlink():
>>> + return path
>>> +
>>> + raise SystemdUnitNotFoundError(self.root, unit)
>>> +
>>> + 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, caller_unit=None):
>>> + # 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(self.root, path)
>>> + if instance == "":
>>> + try:
>>> + default_instance = config.get('Install', 'DefaultInstance')[0]
>>> + except KeyError:
>>> + # no default instance, so nothing to enable
>>> + return
>>> +
>>> + 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')
>>> +
>>> + try:
>>> + for also in config.get('Install', 'Also'):
>>> + try:
>>> + if caller_unit != also:
>>> + SystemdUnit(self.root, also).enable(unit)
>>> + except SystemdUnitNotFoundError as e:
>>> + sys.exit("Error: Systemctl also enable issue with %s (%s)" % (service, e.unit))
>>> +
>>> + 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 in services:
>>> + state = presets.state(service)
>>> +
>>> + if state == "enable" or state is None:
>>> + try:
>>> + SystemdUnit(root, service).enable()
>>> + except SystemdUnitNotFoundError:
>>> + sys.exit("Error: Systemctl preset_all issue in %s" % service)
>>> +
>>> + # If we populate the systemd links we also create /etc/machine-id, which
>>> + # allows systemd to boot with the filesystem read-only before generating
>>> + # a real value and then committing it back.
>>> + #
>>> + # For the stateless configuration, where /etc is generated at runtime
>>> + # (for example on a tmpfs), this script shouldn't run at all and we
>>> + # allow systemd to completely populate /etc.
>>> + (root / SYSCONFDIR / "machine-id").touch()
>>> +
>>> +
>>> +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='?', choices=['enable', 'mask',
>>> + 'preset-all'])
>>> + parser.add_argument('service', nargs=argparse.REMAINDER)
>>> + parser.add_argument('--root')
>>> + parser.add_argument('--preset-mode',
>>> + choices=['full', 'enable-only', 'disable-only'],
>>> + default='full')
>>> +
>>> + 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
>>> + if not command:
>>> + parser.print_help()
>>> + return 0
>>> +
>>> + if command == "mask":
>>> + for service in args.service:
>>> + try:
>>> + SystemdUnit(root, service).mask()
>>> + except SystemdUnitNotFoundError as e:
>>> + sys.exit("Error: Systemctl main mask issue in %s (%s)" % (service, e.unit))
>>> + elif command == "enable":
>>> + for service in args.service:
>>> + try:
>>> + SystemdUnit(root, service).enable()
>>> + except SystemdUnitNotFoundError as e:
>>> + sys.exit("Error: Systemctl main enable issue in %s (%s)" % (service, e.unit))
>>> + elif command == "preset-all":
>>> + if len(args.service) != 0:
>>> + sys.exit("Too many arguments.")
>>> + if args.preset_mode != "enable-only":
>>> + sys.exit("Only enable-only is supported as preset-mode.")
>>> + preset_all(root)
>>> + else:
>>> + raise RuntimeError()
>>> +
>>> +
>>> +if __name__ == '__main__':
>>> + main()
>>> --
>>> 2.25.1
>>>
>>>
>>>
>>>
>>
>>
>> --
>> Best regards,
>>
>> José Quaresma
>>
>>
>>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#99151): https://lists.openembedded.org/g/openembedded-devel/message/99151
> Mute This Topic: https://lists.openembedded.org/mt/94275098/6875888
> Group Owner: openembedded-devel+owner@lists.openembedded.org
> Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [ross.burton@arm.com]
> -=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui
2022-10-13 10:09 ` Ross Burton
@ 2022-10-13 15:35 ` Khem Raj
2022-10-14 0:26 ` wangmy
2022-10-14 0:23 ` wangmy
1 sibling, 1 reply; 9+ messages in thread
From: Khem Raj @ 2022-10-13 15:35 UTC (permalink / raw)
To: Ross Burton; +Cc: Jose Quaresma, wangmy, openembedded-devel
On Thu, Oct 13, 2022 at 3:10 AM Ross Burton <Ross.Burton@arm.com> wrote:
>
> What’s the difference between this python implementation of systemctl, and the shell implementation that’s already in oe-core in systemd-systemctl-native?
>
> Looks like pointless duplication to me. If the Python implementation here is better, then it should be in core.
agreed. Although its perhaps better to soak it a bit, I thought here.
>
> Also, why would a nativesdk DNF need to manage systemd units?
>
> Ross
>
> > On 12 Oct 2022, at 23:27, Khem Raj via lists.openembedded.org <raj.khem=gmail.com@lists.openembedded.org> wrote:
> >
> > On Wed, Oct 12, 2022 at 10:20 AM Jose Quaresma <quaresma.jose@gmail.com> wrote:
> >>
> >> Hi wangmy,
> >>
> >> wangmy <wangmy@fujitsu.com> escreveu no dia quarta, 12/10/2022 à(s) 05:04:
> >>>
> >>> Signed-off-by: Wang Mingyu <wangmy@fujitsu.com>
> >>> ---
> >>> .../systemd/nativesdk-systemd-systemctl.bb | 17 +
> >>> .../systemd/systemd-systemctl/systemctl | 340 ++++++++++++++++++
> >>> 2 files changed, 357 insertions(+)
> >>> create mode 100644 meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
> >>> create mode 100755 meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> >>>
> >>> diff --git a/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
> >>> new file mode 100644
> >>> index 0000000000..7ac21aa260
> >>> --- /dev/null
> >>> +++ b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
> >>> @@ -0,0 +1,17 @@
> >>> +SUMMARY = "Wrapper for enabling systemd services"
> >>> +
> >>> +LICENSE = "MIT"
> >>> +LIC_FILES_CHKSUM = "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de20420"
> >>> +
> >>> +PR = "r6"
> >>
> >>
> >> Why is this recipe starting with the package release 6 ?
> >>
> >
> > Right, I have fixed it before accepting.
> >
> >> Jose
> >>
> >>>
> >>> +
> >>> +inherit nativesdk
> >>> +
> >>> +SRC_URI = "file://systemctl"
> >>> +
> >>> +S = "${WORKDIR}"
> >>> +
> >>> +do_install() {
> >>> + install -d ${D}${bindir}
> >>> + install -m 0755 ${WORKDIR}/systemctl ${D}${bindir}
> >>> +}
> >>> diff --git a/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> >>> new file mode 100755
> >>> index 0000000000..6324319a45
> >>> --- /dev/null
> >>> +++ b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> >>> @@ -0,0 +1,340 @@
> >>> +#!/usr/bin/env python3
> >>> +"""systemctl: subset of systemctl used for image construction
> >>> +
> >>> +Mask/preset systemd units
> >>> +"""
> >>> +
> >>> +import argparse
> >>> +import fnmatch
> >>> +import os
> >>> +import re
> >>> +import sys
> >>> +
> >>> +from collections import namedtuple
> >>> +from pathlib import Path
> >>> +
> >>> +version = 1.0
> >>> +
> >>> +ROOT = Path("/")
> >>> +SYSCONFDIR = Path("etc")
> >>> +BASE_LIBDIR = Path("lib")
> >>> +LIBDIR = Path("usr", "lib")
> >>> +
> >>> +locations = list()
> >>> +
> >>> +
> >>> +class SystemdFile():
> >>> + """Class representing a single systemd configuration file"""
> >>> + def __init__(self, root, path):
> >>> + self.sections = dict()
> >>> + self._parse(root, path)
> >>> + dirname = os.path.basename(path.name) + ".d"
> >>> + for location in locations:
> >>> + for path2 in sorted((root / location / "system" / dirname).glob("*.conf")):
> >>> + self._parse(root, path2)
> >>> +
> >>> + def _parse(self, root, path):
> >>> + """Parse a systemd syntax configuration file
> >>> +
> >>> + Args:
> >>> + path: A pathlib.Path object pointing to the file
> >>> +
> >>> + """
> >>> + skip_re = re.compile(r"^\s*([#;]|$)")
> >>> + section_re = re.compile(r"^\s*\[(?P<section>.*)\]")
> >>> + kv_re = re.compile(r"^\s*(?P<key>[^\s]+)\s*=\s*(?P<value>.*)")
> >>> + section = None
> >>> +
> >>> + if path.is_symlink():
> >>> + try:
> >>> + path.resolve()
> >>> + except FileNotFoundError:
> >>> + # broken symlink, try relative to root
> >>> + path = root / Path(os.readlink(str(path))).relative_to(ROOT)
> >>> +
> >>> + with path.open() as f:
> >>> + for line in f:
> >>> + if skip_re.match(line):
> >>> + continue
> >>> +
> >>> + line = line.strip()
> >>> + m = section_re.match(line)
> >>> + if m:
> >>> + if m.group('section') not in self.sections:
> >>> + section = dict()
> >>> + self.sections[m.group('section')] = section
> >>> + else:
> >>> + section = self.sections[m.group('section')]
> >>> + continue
> >>> +
> >>> + while line.endswith("\\"):
> >>> + line += f.readline().rstrip("\n")
> >>> +
> >>> + m = kv_re.match(line)
> >>> + k = m.group('key')
> >>> + v = m.group('value')
> >>> + if k not in section:
> >>> + section[k] = list()
> >>> + section[k].extend(v.split())
> >>> +
> >>> + def get(self, section, prop):
> >>> + """Get a property from section
> >>> +
> >>> + Args:
> >>> + section: Section to retrieve property from
> >>> + prop: Property to retrieve
> >>> +
> >>> + Returns:
> >>> + List representing all properties of type prop in section.
> >>> +
> >>> + Raises:
> >>> + KeyError: if ``section`` or ``prop`` not found
> >>> + """
> >>> + return self.sections[section][prop]
> >>> +
> >>> +
> >>> +class Presets():
> >>> + """Class representing all systemd presets"""
> >>> + def __init__(self, scope, root):
> >>> + self.directives = list()
> >>> + self._collect_presets(scope, root)
> >>> +
> >>> + def _parse_presets(self, presets):
> >>> + """Parse presets out of a set of preset files"""
> >>> + skip_re = re.compile(r"^\s*([#;]|$)")
> >>> + directive_re = re.compile(r"^\s*(?P<action>enable|disable)\s+(?P<unit_name>(.+))")
> >>> +
> >>> + Directive = namedtuple("Directive", "action unit_name")
> >>> + for preset in presets:
> >>> + with preset.open() as f:
> >>> + for line in f:
> >>> + m = directive_re.match(line)
> >>> + if m:
> >>> + directive = Directive(action=m.group('action'),
> >>> + unit_name=m.group('unit_name'))
> >>> + self.directives.append(directive)
> >>> + elif skip_re.match(line):
> >>> + pass
> >>> + else:
> >>> + sys.exit("Unparsed preset line in {}".format(preset))
> >>> +
> >>> + def _collect_presets(self, scope, root):
> >>> + """Collect list of preset files"""
> >>> + presets = dict()
> >>> + for location in locations:
> >>> + paths = (root / location / scope).glob("*.preset")
> >>> + for path in paths:
> >>> + # earlier names override later ones
> >>> + if path.name not in presets:
> >>> + presets[path.name] = path
> >>> +
> >>> + self._parse_presets([v for k, v in sorted(presets.items())])
> >>> +
> >>> + def state(self, unit_name):
> >>> + """Return state of preset for unit_name
> >>> +
> >>> + Args:
> >>> + presets: set of presets
> >>> + unit_name: name of the unit
> >>> +
> >>> + Returns:
> >>> + None: no matching preset
> >>> + `enable`: unit_name is enabled
> >>> + `disable`: unit_name is disabled
> >>> + """
> >>> + for directive in self.directives:
> >>> + if fnmatch.fnmatch(unit_name, directive.unit_name):
> >>> + return directive.action
> >>> +
> >>> + return None
> >>> +
> >>> +
> >>> +def add_link(path, target):
> >>> + try:
> >>> + path.parent.mkdir(parents=True)
> >>> + except FileExistsError:
> >>> + pass
> >>> + if not path.is_symlink():
> >>> + print("ln -s {} {}".format(target, path))
> >>> + path.symlink_to(target)
> >>> +
> >>> +
> >>> +class SystemdUnitNotFoundError(Exception):
> >>> + def __init__(self, path, unit):
> >>> + self.path = path
> >>> + self.unit = unit
> >>> +
> >>> +
> >>> +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() or path.is_symlink():
> >>> + return path
> >>> +
> >>> + raise SystemdUnitNotFoundError(self.root, unit)
> >>> +
> >>> + 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, caller_unit=None):
> >>> + # 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(self.root, path)
> >>> + if instance == "":
> >>> + try:
> >>> + default_instance = config.get('Install', 'DefaultInstance')[0]
> >>> + except KeyError:
> >>> + # no default instance, so nothing to enable
> >>> + return
> >>> +
> >>> + 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')
> >>> +
> >>> + try:
> >>> + for also in config.get('Install', 'Also'):
> >>> + try:
> >>> + if caller_unit != also:
> >>> + SystemdUnit(self.root, also).enable(unit)
> >>> + except SystemdUnitNotFoundError as e:
> >>> + sys.exit("Error: Systemctl also enable issue with %s (%s)" % (service, e.unit))
> >>> +
> >>> + 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 in services:
> >>> + state = presets.state(service)
> >>> +
> >>> + if state == "enable" or state is None:
> >>> + try:
> >>> + SystemdUnit(root, service).enable()
> >>> + except SystemdUnitNotFoundError:
> >>> + sys.exit("Error: Systemctl preset_all issue in %s" % service)
> >>> +
> >>> + # If we populate the systemd links we also create /etc/machine-id, which
> >>> + # allows systemd to boot with the filesystem read-only before generating
> >>> + # a real value and then committing it back.
> >>> + #
> >>> + # For the stateless configuration, where /etc is generated at runtime
> >>> + # (for example on a tmpfs), this script shouldn't run at all and we
> >>> + # allow systemd to completely populate /etc.
> >>> + (root / SYSCONFDIR / "machine-id").touch()
> >>> +
> >>> +
> >>> +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='?', choices=['enable', 'mask',
> >>> + 'preset-all'])
> >>> + parser.add_argument('service', nargs=argparse.REMAINDER)
> >>> + parser.add_argument('--root')
> >>> + parser.add_argument('--preset-mode',
> >>> + choices=['full', 'enable-only', 'disable-only'],
> >>> + default='full')
> >>> +
> >>> + 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
> >>> + if not command:
> >>> + parser.print_help()
> >>> + return 0
> >>> +
> >>> + if command == "mask":
> >>> + for service in args.service:
> >>> + try:
> >>> + SystemdUnit(root, service).mask()
> >>> + except SystemdUnitNotFoundError as e:
> >>> + sys.exit("Error: Systemctl main mask issue in %s (%s)" % (service, e.unit))
> >>> + elif command == "enable":
> >>> + for service in args.service:
> >>> + try:
> >>> + SystemdUnit(root, service).enable()
> >>> + except SystemdUnitNotFoundError as e:
> >>> + sys.exit("Error: Systemctl main enable issue in %s (%s)" % (service, e.unit))
> >>> + elif command == "preset-all":
> >>> + if len(args.service) != 0:
> >>> + sys.exit("Too many arguments.")
> >>> + if args.preset_mode != "enable-only":
> >>> + sys.exit("Only enable-only is supported as preset-mode.")
> >>> + preset_all(root)
> >>> + else:
> >>> + raise RuntimeError()
> >>> +
> >>> +
> >>> +if __name__ == '__main__':
> >>> + main()
> >>> --
> >>> 2.25.1
> >>>
> >>>
> >>>
> >>>
> >>
> >>
> >> --
> >> Best regards,
> >>
> >> José Quaresma
> >>
> >>
> >>
> >
> > -=-=-=-=-=-=-=-=-=-=-=-
> > Links: You receive all messages sent to this group.
> > View/Reply Online (#99151): https://lists.openembedded.org/g/openembedded-devel/message/99151
> > Mute This Topic: https://lists.openembedded.org/mt/94275098/6875888
> > Group Owner: openembedded-devel+owner@lists.openembedded.org
> > Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [ross.burton@arm.com]
> > -=-=-=-=-=-=-=-=-=-=-=-
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui
2022-10-13 10:09 ` Ross Burton
2022-10-13 15:35 ` Khem Raj
@ 2022-10-14 0:23 ` wangmy
2022-10-14 10:31 ` Ross Burton
1 sibling, 1 reply; 9+ messages in thread
From: wangmy @ 2022-10-14 0:23 UTC (permalink / raw)
To: Ross Burton, raj.khem; +Cc: Jose Quaresma, openembedded-devel
> Also, why would a nativesdk DNF need to manage systemd units?
When executing postinstall, the system is needed.
--
Best Regards
---------------------------------------------------
Wang Mingyu
Development Dept.I
Nanjing Fujitsu Nanda Software Tech. Co., Ltd.(FNST) No. 6 Wenzhu Road, Nanjing, 210012, China
TEL: +86+25-86630566-8568
COINS: 79988548
FAX: +86+25-83317685
MAIL: wangmy@fujitsu.com
http://www.fujitsu.com/cn/fnst/
> -----Original Message-----
> From: Ross Burton <Ross.Burton@arm.com>
> Sent: Thursday, October 13, 2022 6:10 PM
> To: raj.khem@gmail.com
> Cc: Jose Quaresma <quaresma.jose@gmail.com>; Wang, Mingyu/王 鸣瑜
> <wangmy@fujitsu.com>; openembedded-devel@lists.openembedded.org
> Subject: Re: [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of
> dnf-plugin-tui
>
> What’s the difference between this python implementation of systemctl, and
> the shell implementation that’s already in oe-core in systemd-systemctl-native?
>
> Looks like pointless duplication to me. If the Python implementation here is
> better, then it should be in core.
>
> Also, why would a nativesdk DNF need to manage systemd units?
>
> Ross
>
> > On 12 Oct 2022, at 23:27, Khem Raj via lists.openembedded.org
> <raj.khem=gmail.com@lists.openembedded.org> wrote:
> >
> > On Wed, Oct 12, 2022 at 10:20 AM Jose Quaresma
> <quaresma.jose@gmail.com> wrote:
> >>
> >> Hi wangmy,
> >>
> >> wangmy <wangmy@fujitsu.com> escreveu no dia quarta, 12/10/2022 à(s)
> 05:04:
> >>>
> >>> Signed-off-by: Wang Mingyu <wangmy@fujitsu.com>
> >>> ---
> >>> .../systemd/nativesdk-systemd-systemctl.bb | 17 +
> >>> .../systemd/systemd-systemctl/systemctl | 340
> ++++++++++++++++++
> >>> 2 files changed, 357 insertions(+)
> >>> create mode 100644
> >>> meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
> >>> create mode 100755
> >>> meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> >>>
> >>> diff --git
> >>> a/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
> >>> b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
> >>> new file mode 100644
> >>> index 0000000000..7ac21aa260
> >>> --- /dev/null
> >>> +++ b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.b
> >>> +++ b
> >>> @@ -0,0 +1,17 @@
> >>> +SUMMARY = "Wrapper for enabling systemd services"
> >>> +
> >>> +LICENSE = "MIT"
> >>> +LIC_FILES_CHKSUM =
> "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de
> 20420"
> >>> +
> >>> +PR = "r6"
> >>
> >>
> >> Why is this recipe starting with the package release 6 ?
> >>
> >
> > Right, I have fixed it before accepting.
> >
> >> Jose
> >>
> >>>
> >>> +
> >>> +inherit nativesdk
> >>> +
> >>> +SRC_URI = "file://systemctl"
> >>> +
> >>> +S = "${WORKDIR}"
> >>> +
> >>> +do_install() {
> >>> + install -d ${D}${bindir}
> >>> + install -m 0755 ${WORKDIR}/systemctl ${D}${bindir} }
> >>> diff --git
> >>> a/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> >>> b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> >>> new file mode 100755
> >>> index 0000000000..6324319a45
> >>> --- /dev/null
> >>> +++ b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> >>> @@ -0,0 +1,340 @@
> >>> +#!/usr/bin/env python3
> >>> +"""systemctl: subset of systemctl used for image construction
> >>> +
> >>> +Mask/preset systemd units
> >>> +"""
> >>> +
> >>> +import argparse
> >>> +import fnmatch
> >>> +import os
> >>> +import re
> >>> +import sys
> >>> +
> >>> +from collections import namedtuple
> >>> +from pathlib import Path
> >>> +
> >>> +version = 1.0
> >>> +
> >>> +ROOT = Path("/")
> >>> +SYSCONFDIR = Path("etc")
> >>> +BASE_LIBDIR = Path("lib")
> >>> +LIBDIR = Path("usr", "lib")
> >>> +
> >>> +locations = list()
> >>> +
> >>> +
> >>> +class SystemdFile():
> >>> + """Class representing a single systemd configuration file"""
> >>> + def __init__(self, root, path):
> >>> + self.sections = dict()
> >>> + self._parse(root, path)
> >>> + dirname = os.path.basename(path.name) + ".d"
> >>> + for location in locations:
> >>> + for path2 in sorted((root / location / "system" /
> dirname).glob("*.conf")):
> >>> + self._parse(root, path2)
> >>> +
> >>> + def _parse(self, root, path):
> >>> + """Parse a systemd syntax configuration file
> >>> +
> >>> + Args:
> >>> + path: A pathlib.Path object pointing to the file
> >>> +
> >>> + """
> >>> + skip_re = re.compile(r"^\s*([#;]|$)")
> >>> + section_re = re.compile(r"^\s*\[(?P<section>.*)\]")
> >>> + kv_re = re.compile(r"^\s*(?P<key>[^\s]+)\s*=\s*(?P<value>.*)")
> >>> + section = None
> >>> +
> >>> + if path.is_symlink():
> >>> + try:
> >>> + path.resolve()
> >>> + except FileNotFoundError:
> >>> + # broken symlink, try relative to root
> >>> + path = root /
> >>> + Path(os.readlink(str(path))).relative_to(ROOT)
> >>> +
> >>> + with path.open() as f:
> >>> + for line in f:
> >>> + if skip_re.match(line):
> >>> + continue
> >>> +
> >>> + line = line.strip()
> >>> + m = section_re.match(line)
> >>> + if m:
> >>> + if m.group('section') not in self.sections:
> >>> + section = dict()
> >>> + self.sections[m.group('section')] = section
> >>> + else:
> >>> + section = self.sections[m.group('section')]
> >>> + continue
> >>> +
> >>> + while line.endswith("\\"):
> >>> + line += f.readline().rstrip("\n")
> >>> +
> >>> + m = kv_re.match(line)
> >>> + k = m.group('key')
> >>> + v = m.group('value')
> >>> + if k not in section:
> >>> + section[k] = list()
> >>> + section[k].extend(v.split())
> >>> +
> >>> + def get(self, section, prop):
> >>> + """Get a property from section
> >>> +
> >>> + Args:
> >>> + section: Section to retrieve property from
> >>> + prop: Property to retrieve
> >>> +
> >>> + Returns:
> >>> + List representing all properties of type prop in section.
> >>> +
> >>> + Raises:
> >>> + KeyError: if ``section`` or ``prop`` not found
> >>> + """
> >>> + return self.sections[section][prop]
> >>> +
> >>> +
> >>> +class Presets():
> >>> + """Class representing all systemd presets"""
> >>> + def __init__(self, scope, root):
> >>> + self.directives = list()
> >>> + self._collect_presets(scope, root)
> >>> +
> >>> + def _parse_presets(self, presets):
> >>> + """Parse presets out of a set of preset files"""
> >>> + skip_re = re.compile(r"^\s*([#;]|$)")
> >>> + directive_re =
> >>> + re.compile(r"^\s*(?P<action>enable|disable)\s+(?P<unit_name>(.+))"
> >>> + )
> >>> +
> >>> + Directive = namedtuple("Directive", "action unit_name")
> >>> + for preset in presets:
> >>> + with preset.open() as f:
> >>> + for line in f:
> >>> + m = directive_re.match(line)
> >>> + if m:
> >>> + directive =
> Directive(action=m.group('action'),
> >>> +
> unit_name=m.group('unit_name'))
> >>> + self.directives.append(directive)
> >>> + elif skip_re.match(line):
> >>> + pass
> >>> + else:
> >>> + sys.exit("Unparsed preset line in
> >>> + {}".format(preset))
> >>> +
> >>> + def _collect_presets(self, scope, root):
> >>> + """Collect list of preset files"""
> >>> + presets = dict()
> >>> + for location in locations:
> >>> + paths = (root / location / scope).glob("*.preset")
> >>> + for path in paths:
> >>> + # earlier names override later ones
> >>> + if path.name not in presets:
> >>> + presets[path.name] = path
> >>> +
> >>> + self._parse_presets([v for k, v in
> >>> + sorted(presets.items())])
> >>> +
> >>> + def state(self, unit_name):
> >>> + """Return state of preset for unit_name
> >>> +
> >>> + Args:
> >>> + presets: set of presets
> >>> + unit_name: name of the unit
> >>> +
> >>> + Returns:
> >>> + None: no matching preset
> >>> + `enable`: unit_name is enabled
> >>> + `disable`: unit_name is disabled
> >>> + """
> >>> + for directive in self.directives:
> >>> + if fnmatch.fnmatch(unit_name, directive.unit_name):
> >>> + return directive.action
> >>> +
> >>> + return None
> >>> +
> >>> +
> >>> +def add_link(path, target):
> >>> + try:
> >>> + path.parent.mkdir(parents=True)
> >>> + except FileExistsError:
> >>> + pass
> >>> + if not path.is_symlink():
> >>> + print("ln -s {} {}".format(target, path))
> >>> + path.symlink_to(target)
> >>> +
> >>> +
> >>> +class SystemdUnitNotFoundError(Exception):
> >>> + def __init__(self, path, unit):
> >>> + self.path = path
> >>> + self.unit = unit
> >>> +
> >>> +
> >>> +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() or path.is_symlink():
> >>> + return path
> >>> +
> >>> + raise SystemdUnitNotFoundError(self.root, unit)
> >>> +
> >>> + 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, caller_unit=None):
> >>> + # 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(self.root, path)
> >>> + if instance == "":
> >>> + try:
> >>> + default_instance = config.get('Install',
> 'DefaultInstance')[0]
> >>> + except KeyError:
> >>> + # no default instance, so nothing to enable
> >>> + return
> >>> +
> >>> + 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')
> >>> +
> >>> + try:
> >>> + for also in config.get('Install', 'Also'):
> >>> + try:
> >>> + if caller_unit != also:
> >>> + SystemdUnit(self.root, also).enable(unit)
> >>> + except SystemdUnitNotFoundError as e:
> >>> + sys.exit("Error: Systemctl also enable issue
> >>> + with %s (%s)" % (service, e.unit))
> >>> +
> >>> + 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 in services:
> >>> + state = presets.state(service)
> >>> +
> >>> + if state == "enable" or state is None:
> >>> + try:
> >>> + SystemdUnit(root, service).enable()
> >>> + except SystemdUnitNotFoundError:
> >>> + sys.exit("Error: Systemctl preset_all issue in %s"
> >>> + % service)
> >>> +
> >>> + # If we populate the systemd links we also create /etc/machine-id,
> which
> >>> + # allows systemd to boot with the filesystem read-only before
> generating
> >>> + # a real value and then committing it back.
> >>> + #
> >>> + # For the stateless configuration, where /etc is generated at runtime
> >>> + # (for example on a tmpfs), this script shouldn't run at all and we
> >>> + # allow systemd to completely populate /etc.
> >>> + (root / SYSCONFDIR / "machine-id").touch()
> >>> +
> >>> +
> >>> +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='?', choices=['enable', 'mask',
> >>> +
> 'preset-all'])
> >>> + parser.add_argument('service', nargs=argparse.REMAINDER)
> >>> + parser.add_argument('--root')
> >>> + parser.add_argument('--preset-mode',
> >>> + choices=['full', 'enable-only', 'disable-only'],
> >>> + default='full')
> >>> +
> >>> + 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
> >>> + if not command:
> >>> + parser.print_help()
> >>> + return 0
> >>> +
> >>> + if command == "mask":
> >>> + for service in args.service:
> >>> + try:
> >>> + SystemdUnit(root, service).mask()
> >>> + except SystemdUnitNotFoundError as e:
> >>> + sys.exit("Error: Systemctl main mask issue in %s (%s)" %
> (service, e.unit))
> >>> + elif command == "enable":
> >>> + for service in args.service:
> >>> + try:
> >>> + SystemdUnit(root, service).enable()
> >>> + except SystemdUnitNotFoundError as e:
> >>> + sys.exit("Error: Systemctl main enable issue in %s
> (%s)" % (service, e.unit))
> >>> + elif command == "preset-all":
> >>> + if len(args.service) != 0:
> >>> + sys.exit("Too many arguments.")
> >>> + if args.preset_mode != "enable-only":
> >>> + sys.exit("Only enable-only is supported as preset-mode.")
> >>> + preset_all(root)
> >>> + else:
> >>> + raise RuntimeError()
> >>> +
> >>> +
> >>> +if __name__ == '__main__':
> >>> + main()
> >>> --
> >>> 2.25.1
> >>>
> >>>
> >>>
> >>>
> >>
> >>
> >> --
> >> Best regards,
> >>
> >> José Quaresma
> >>
> >>
> >>
> >
> > -=-=-=-=-=-=-=-=-=-=-=-
> > Links: You receive all messages sent to this group.
> > View/Reply Online (#99151):
> > https://lists.openembedded.org/g/openembedded-devel/message/99151
> > Mute This Topic: https://lists.openembedded.org/mt/94275098/6875888
> > Group Owner: openembedded-devel+owner@lists.openembedded.org
> > Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub
> > [ross.burton@arm.com]
> > -=-=-=-=-=-=-=-=-=-=-=-
^ permalink raw reply [flat|nested] 9+ messages in thread
* RE: [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui
2022-10-13 15:35 ` Khem Raj
@ 2022-10-14 0:26 ` wangmy
0 siblings, 0 replies; 9+ messages in thread
From: wangmy @ 2022-10-14 0:26 UTC (permalink / raw)
To: Khem Raj, Ross Burton; +Cc: Jose Quaresma, openembedded-devel
> agreed. Although its perhaps better to soak it a bit, I thought here.
We will review again to see if we can add this script to the dnf-plugin-ui directory.
Do you think it would be better?
--
Best Regards
---------------------------------------------------
Wang Mingyu
Development Dept.I
Nanjing Fujitsu Nanda Software Tech. Co., Ltd.(FNST) No. 6 Wenzhu Road, Nanjing, 210012, China
TEL: +86+25-86630566-8568
COINS: 79988548
FAX: +86+25-83317685
MAIL: wangmy@fujitsu.com
http://www.fujitsu.com/cn/fnst/
> -----Original Message-----
> From: Khem Raj <raj.khem@gmail.com>
> Sent: Thursday, October 13, 2022 11:36 PM
> To: Ross Burton <Ross.Burton@arm.com>
> Cc: Jose Quaresma <quaresma.jose@gmail.com>; Wang, Mingyu/王 鸣瑜
> <wangmy@fujitsu.com>; openembedded-devel@lists.openembedded.org
> Subject: Re: [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of
> dnf-plugin-tui
>
> On Thu, Oct 13, 2022 at 3:10 AM Ross Burton <Ross.Burton@arm.com> wrote:
> >
> > What’s the difference between this python implementation of systemctl, and
> the shell implementation that’s already in oe-core in systemd-systemctl-native?
> >
> > Looks like pointless duplication to me. If the Python implementation here is
> better, then it should be in core.
>
> agreed. Although its perhaps better to soak it a bit, I thought here.
>
> >
> > Also, why would a nativesdk DNF need to manage systemd units?
>
> >
> > Ross
> >
> > > On 12 Oct 2022, at 23:27, Khem Raj via lists.openembedded.org
> <raj.khem=gmail.com@lists.openembedded.org> wrote:
> > >
> > > On Wed, Oct 12, 2022 at 10:20 AM Jose Quaresma
> <quaresma.jose@gmail.com> wrote:
> > >>
> > >> Hi wangmy,
> > >>
> > >> wangmy <wangmy@fujitsu.com> escreveu no dia quarta, 12/10/2022 à(s)
> 05:04:
> > >>>
> > >>> Signed-off-by: Wang Mingyu <wangmy@fujitsu.com>
> > >>> ---
> > >>> .../systemd/nativesdk-systemd-systemctl.bb | 17 +
> > >>> .../systemd/systemd-systemctl/systemctl | 340
> ++++++++++++++++++
> > >>> 2 files changed, 357 insertions(+) create mode 100644
> > >>> meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
> > >>> create mode 100755
> > >>> meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> > >>>
> > >>> diff --git
> > >>> a/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
> > >>> b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb
> > >>> new file mode 100644
> > >>> index 0000000000..7ac21aa260
> > >>> --- /dev/null
> > >>> +++ b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl
> > >>> +++ .bb
> > >>> @@ -0,0 +1,17 @@
> > >>> +SUMMARY = "Wrapper for enabling systemd services"
> > >>> +
> > >>> +LICENSE = "MIT"
> > >>> +LIC_FILES_CHKSUM =
> "file://${COREBASE}/meta/COPYING.MIT;md5=3da9cfbcb788c80a0384361b4de
> 20420"
> > >>> +
> > >>> +PR = "r6"
> > >>
> > >>
> > >> Why is this recipe starting with the package release 6 ?
> > >>
> > >
> > > Right, I have fixed it before accepting.
> > >
> > >> Jose
> > >>
> > >>>
> > >>> +
> > >>> +inherit nativesdk
> > >>> +
> > >>> +SRC_URI = "file://systemctl"
> > >>> +
> > >>> +S = "${WORKDIR}"
> > >>> +
> > >>> +do_install() {
> > >>> + install -d ${D}${bindir}
> > >>> + install -m 0755 ${WORKDIR}/systemctl ${D}${bindir} }
> > >>> diff --git
> > >>> a/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> > >>> b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> > >>> new file mode 100755
> > >>> index 0000000000..6324319a45
> > >>> --- /dev/null
> > >>> +++ b/meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl
> > >>> @@ -0,0 +1,340 @@
> > >>> +#!/usr/bin/env python3
> > >>> +"""systemctl: subset of systemctl used for image construction
> > >>> +
> > >>> +Mask/preset systemd units
> > >>> +"""
> > >>> +
> > >>> +import argparse
> > >>> +import fnmatch
> > >>> +import os
> > >>> +import re
> > >>> +import sys
> > >>> +
> > >>> +from collections import namedtuple from pathlib import Path
> > >>> +
> > >>> +version = 1.0
> > >>> +
> > >>> +ROOT = Path("/")
> > >>> +SYSCONFDIR = Path("etc")
> > >>> +BASE_LIBDIR = Path("lib")
> > >>> +LIBDIR = Path("usr", "lib")
> > >>> +
> > >>> +locations = list()
> > >>> +
> > >>> +
> > >>> +class SystemdFile():
> > >>> + """Class representing a single systemd configuration file"""
> > >>> + def __init__(self, root, path):
> > >>> + self.sections = dict()
> > >>> + self._parse(root, path)
> > >>> + dirname = os.path.basename(path.name) + ".d"
> > >>> + for location in locations:
> > >>> + for path2 in sorted((root / location / "system" /
> dirname).glob("*.conf")):
> > >>> + self._parse(root, path2)
> > >>> +
> > >>> + def _parse(self, root, path):
> > >>> + """Parse a systemd syntax configuration file
> > >>> +
> > >>> + Args:
> > >>> + path: A pathlib.Path object pointing to the file
> > >>> +
> > >>> + """
> > >>> + skip_re = re.compile(r"^\s*([#;]|$)")
> > >>> + section_re = re.compile(r"^\s*\[(?P<section>.*)\]")
> > >>> + kv_re =
> re.compile(r"^\s*(?P<key>[^\s]+)\s*=\s*(?P<value>.*)")
> > >>> + section = None
> > >>> +
> > >>> + if path.is_symlink():
> > >>> + try:
> > >>> + path.resolve()
> > >>> + except FileNotFoundError:
> > >>> + # broken symlink, try relative to root
> > >>> + path = root /
> > >>> + Path(os.readlink(str(path))).relative_to(ROOT)
> > >>> +
> > >>> + with path.open() as f:
> > >>> + for line in f:
> > >>> + if skip_re.match(line):
> > >>> + continue
> > >>> +
> > >>> + line = line.strip()
> > >>> + m = section_re.match(line)
> > >>> + if m:
> > >>> + if m.group('section') not in self.sections:
> > >>> + section = dict()
> > >>> + self.sections[m.group('section')] = section
> > >>> + else:
> > >>> + section = self.sections[m.group('section')]
> > >>> + continue
> > >>> +
> > >>> + while line.endswith("\\"):
> > >>> + line += f.readline().rstrip("\n")
> > >>> +
> > >>> + m = kv_re.match(line)
> > >>> + k = m.group('key')
> > >>> + v = m.group('value')
> > >>> + if k not in section:
> > >>> + section[k] = list()
> > >>> + section[k].extend(v.split())
> > >>> +
> > >>> + def get(self, section, prop):
> > >>> + """Get a property from section
> > >>> +
> > >>> + Args:
> > >>> + section: Section to retrieve property from
> > >>> + prop: Property to retrieve
> > >>> +
> > >>> + Returns:
> > >>> + List representing all properties of type prop in section.
> > >>> +
> > >>> + Raises:
> > >>> + KeyError: if ``section`` or ``prop`` not found
> > >>> + """
> > >>> + return self.sections[section][prop]
> > >>> +
> > >>> +
> > >>> +class Presets():
> > >>> + """Class representing all systemd presets"""
> > >>> + def __init__(self, scope, root):
> > >>> + self.directives = list()
> > >>> + self._collect_presets(scope, root)
> > >>> +
> > >>> + def _parse_presets(self, presets):
> > >>> + """Parse presets out of a set of preset files"""
> > >>> + skip_re = re.compile(r"^\s*([#;]|$)")
> > >>> + directive_re =
> > >>> + re.compile(r"^\s*(?P<action>enable|disable)\s+(?P<unit_name>(.+)
> > >>> + )")
> > >>> +
> > >>> + Directive = namedtuple("Directive", "action unit_name")
> > >>> + for preset in presets:
> > >>> + with preset.open() as f:
> > >>> + for line in f:
> > >>> + m = directive_re.match(line)
> > >>> + if m:
> > >>> + directive =
> Directive(action=m.group('action'),
> > >>> +
> unit_name=m.group('unit_name'))
> > >>> + self.directives.append(directive)
> > >>> + elif skip_re.match(line):
> > >>> + pass
> > >>> + else:
> > >>> + sys.exit("Unparsed preset line in
> > >>> + {}".format(preset))
> > >>> +
> > >>> + def _collect_presets(self, scope, root):
> > >>> + """Collect list of preset files"""
> > >>> + presets = dict()
> > >>> + for location in locations:
> > >>> + paths = (root / location / scope).glob("*.preset")
> > >>> + for path in paths:
> > >>> + # earlier names override later ones
> > >>> + if path.name not in presets:
> > >>> + presets[path.name] = path
> > >>> +
> > >>> + self._parse_presets([v for k, v in
> > >>> + sorted(presets.items())])
> > >>> +
> > >>> + def state(self, unit_name):
> > >>> + """Return state of preset for unit_name
> > >>> +
> > >>> + Args:
> > >>> + presets: set of presets
> > >>> + unit_name: name of the unit
> > >>> +
> > >>> + Returns:
> > >>> + None: no matching preset
> > >>> + `enable`: unit_name is enabled
> > >>> + `disable`: unit_name is disabled
> > >>> + """
> > >>> + for directive in self.directives:
> > >>> + if fnmatch.fnmatch(unit_name, directive.unit_name):
> > >>> + return directive.action
> > >>> +
> > >>> + return None
> > >>> +
> > >>> +
> > >>> +def add_link(path, target):
> > >>> + try:
> > >>> + path.parent.mkdir(parents=True)
> > >>> + except FileExistsError:
> > >>> + pass
> > >>> + if not path.is_symlink():
> > >>> + print("ln -s {} {}".format(target, path))
> > >>> + path.symlink_to(target)
> > >>> +
> > >>> +
> > >>> +class SystemdUnitNotFoundError(Exception):
> > >>> + def __init__(self, path, unit):
> > >>> + self.path = path
> > >>> + self.unit = unit
> > >>> +
> > >>> +
> > >>> +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() or path.is_symlink():
> > >>> + return path
> > >>> +
> > >>> + raise SystemdUnitNotFoundError(self.root, unit)
> > >>> +
> > >>> + 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, caller_unit=None):
> > >>> + # 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(self.root, path)
> > >>> + if instance == "":
> > >>> + try:
> > >>> + default_instance = config.get('Install',
> 'DefaultInstance')[0]
> > >>> + except KeyError:
> > >>> + # no default instance, so nothing to enable
> > >>> + return
> > >>> +
> > >>> + 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')
> > >>> +
> > >>> + try:
> > >>> + for also in config.get('Install', 'Also'):
> > >>> + try:
> > >>> + if caller_unit != also:
> > >>> + SystemdUnit(self.root, also).enable(unit)
> > >>> + except SystemdUnitNotFoundError as e:
> > >>> + sys.exit("Error: Systemctl also enable issue
> > >>> + with %s (%s)" % (service, e.unit))
> > >>> +
> > >>> + 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 in services:
> > >>> + state = presets.state(service)
> > >>> +
> > >>> + if state == "enable" or state is None:
> > >>> + try:
> > >>> + SystemdUnit(root, service).enable()
> > >>> + except SystemdUnitNotFoundError:
> > >>> + sys.exit("Error: Systemctl preset_all issue in
> > >>> + %s" % service)
> > >>> +
> > >>> + # If we populate the systemd links we also create /etc/machine-id,
> which
> > >>> + # allows systemd to boot with the filesystem read-only before
> generating
> > >>> + # a real value and then committing it back.
> > >>> + #
> > >>> + # For the stateless configuration, where /etc is generated at
> runtime
> > >>> + # (for example on a tmpfs), this script shouldn't run at all and we
> > >>> + # allow systemd to completely populate /etc.
> > >>> + (root / SYSCONFDIR / "machine-id").touch()
> > >>> +
> > >>> +
> > >>> +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='?', choices=['enable',
> 'mask',
> > >>> +
> 'preset-all'])
> > >>> + parser.add_argument('service', nargs=argparse.REMAINDER)
> > >>> + parser.add_argument('--root')
> > >>> + parser.add_argument('--preset-mode',
> > >>> + choices=['full', 'enable-only', 'disable-only'],
> > >>> + default='full')
> > >>> +
> > >>> + 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
> > >>> + if not command:
> > >>> + parser.print_help()
> > >>> + return 0
> > >>> +
> > >>> + if command == "mask":
> > >>> + for service in args.service:
> > >>> + try:
> > >>> + SystemdUnit(root, service).mask()
> > >>> + except SystemdUnitNotFoundError as e:
> > >>> + sys.exit("Error: Systemctl main mask issue in %s
> (%s)" % (service, e.unit))
> > >>> + elif command == "enable":
> > >>> + for service in args.service:
> > >>> + try:
> > >>> + SystemdUnit(root, service).enable()
> > >>> + except SystemdUnitNotFoundError as e:
> > >>> + sys.exit("Error: Systemctl main enable issue in %s
> (%s)" % (service, e.unit))
> > >>> + elif command == "preset-all":
> > >>> + if len(args.service) != 0:
> > >>> + sys.exit("Too many arguments.")
> > >>> + if args.preset_mode != "enable-only":
> > >>> + sys.exit("Only enable-only is supported as preset-mode.")
> > >>> + preset_all(root)
> > >>> + else:
> > >>> + raise RuntimeError()
> > >>> +
> > >>> +
> > >>> +if __name__ == '__main__':
> > >>> + main()
> > >>> --
> > >>> 2.25.1
> > >>>
> > >>>
> > >>>
> > >>>
> > >>
> > >>
> > >> --
> > >> Best regards,
> > >>
> > >> José Quaresma
> > >>
> > >>
> > >>
> > >
> > > -=-=-=-=-=-=-=-=-=-=-=-
> > > Links: You receive all messages sent to this group.
> > > View/Reply Online (#99151):
> > > https://lists.openembedded.org/g/openembedded-devel/message/99151
> > > Mute This Topic: https://lists.openembedded.org/mt/94275098/6875888
> > > Group Owner: openembedded-devel+owner@lists.openembedded.org
> > > Unsubscribe:
> > > https://lists.openembedded.org/g/openembedded-devel/unsub
> > > [ross.burton@arm.com]
> > > -=-=-=-=-=-=-=-=-=-=-=-
> >
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui
2022-10-14 0:23 ` wangmy
@ 2022-10-14 10:31 ` Ross Burton
2022-10-14 15:08 ` Khem Raj
0 siblings, 1 reply; 9+ messages in thread
From: Ross Burton @ 2022-10-14 10:31 UTC (permalink / raw)
To: wangmy; +Cc: raj.khem, Jose Quaresma, openembedded-devel
On 14 Oct 2022, at 01:23, wangmy via lists.openembedded.org <wangmy=fujitsu.com@lists.openembedded.org> wrote:
>
>> Also, why would a nativesdk DNF need to manage systemd units?
> When executing postinstall, the system is needed.
So this is a direct clone of the systemctl in systemd-systemctl-native, which solves exactly the same problem.
We shouldn’t be duplicating code. If this is a better script then it should replace the one in oe-core. If the one in oe-core is sufficient, then it can be simply be extended to build for nativesdk.
Ross
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui
2022-10-14 10:31 ` Ross Burton
@ 2022-10-14 15:08 ` Khem Raj
0 siblings, 0 replies; 9+ messages in thread
From: Khem Raj @ 2022-10-14 15:08 UTC (permalink / raw)
To: Ross Burton; +Cc: wangmy, Jose Quaresma, openembedded-devel
On Fri, Oct 14, 2022 at 3:31 AM Ross Burton <Ross.Burton@arm.com> wrote:
>
> On 14 Oct 2022, at 01:23, wangmy via lists.openembedded.org <wangmy=fujitsu.com@lists.openembedded.org> wrote:
> >
> >> Also, why would a nativesdk DNF need to manage systemd units?
> > When executing postinstall, the system is needed.
>
> So this is a direct clone of the systemctl in systemd-systemctl-native, which solves exactly the same problem.
>
looks like that. Yes, I agree, I guess replacing oe-core script with
python could be an improvement.
> We shouldn’t be duplicating code. If this is a better script then it should replace the one in oe-core. If the one in oe-core is sufficient, then it can be simply be extended to build for nativesdk.
>
> Ross
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2022-10-14 15:08 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-10-12 4:03 [oe] [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui Wang Mingyu
2022-10-12 17:20 ` Jose Quaresma
2022-10-12 22:27 ` Khem Raj
2022-10-13 10:09 ` Ross Burton
2022-10-13 15:35 ` Khem Raj
2022-10-14 0:26 ` wangmy
2022-10-14 0:23 ` wangmy
2022-10-14 10:31 ` Ross Burton
2022-10-14 15:08 ` Khem Raj
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.