> 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: [email protected] http://www.fujitsu.com/cn/fnst/ > -----Original Message----- > From: Khem Raj <[email protected]> > Sent: Thursday, October 13, 2022 11:36 PM > To: Ross Burton <[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 > > On Thu, Oct 13, 2022 at 3:10 AM Ross Burton <[email protected]> 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 > <[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 > > >>> +++ .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 (#99166): https://lists.openembedded.org/g/openembedded-devel/message/99166 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]] -=-=-=-=-=-=-=-=-=-=-=-
