> 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: [email protected] http://www.fujitsu.com/cn/fnst/ > -----Original Message----- > From: Ross Burton <[email protected]> > Sent: Thursday, October 13, 2022 6:10 PM > To: [email protected] > Cc: Jose Quaresma <[email protected]>; Wang, Mingyu/王 鸣瑜 > <[email protected]>; [email protected] > 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 > <[email protected]> wrote: > > > > On Wed, Oct 12, 2022 at 10:20 AM Jose Quaresma > <[email protected]> wrote: > >> > >> Hi wangmy, > >> > >> wangmy <[email protected]> escreveu no dia quarta, 12/10/2022 à(s) > 05:04: > >>> > >>> Signed-off-by: Wang Mingyu <[email protected]> > >>> --- > >>> .../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 (#99165): https://lists.openembedded.org/g/openembedded-devel/message/99165 Mute This Topic: https://lists.openembedded.org/mt/94275098/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
