Message ID | 1665547429-23508-1-git-send-email-wangmy@fujitsu.com |
---|---|
State | Under Review |
Headers | show |
Series | [meta-oe] Add nativesdk-systemd-systemctl as dependency of dnf-plugin-tui | expand |
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] > -=-=-=-=-=-=-=-=-=-=-=- > >
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] > -=-=-=-=-=-=-=-=-=-=-=- >
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] > -=-=-=-=-=-=-=-=-=-=-=-
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] > > -=-=-=-=-=-=-=-=-=-=-=- >
> 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] > > -=-=-=-=-=-=-=-=-=-=-=-
> 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] > > > -=-=-=-=-=-=-=-=-=-=-=- > >
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
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
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()
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