Hi wangmy, wangmy <[email protected]> escreveu no dia quarta, 12/10/2022 à(s) 05:04:
> Signed-off-by: Wang Mingyu <[email protected]> > --- > .../systemd/nativesdk-systemd-systemctl.bb | 17 + > .../systemd/systemd-systemctl/systemctl | 340 ++++++++++++++++++ > 2 files changed, 357 insertions(+) > create mode 100644 meta-oe/recipes-devtools/systemd/ > nativesdk-systemd-systemctl.bb > create mode 100755 > meta-oe/recipes-devtools/systemd/systemd-systemctl/systemctl > > diff --git a/meta-oe/recipes-devtools/systemd/ > nativesdk-systemd-systemctl.bb b/meta-oe/recipes-devtools/systemd/ > nativesdk-systemd-systemctl.bb > new file mode 100644 > index 0000000000..7ac21aa260 > --- /dev/null > +++ b/meta-oe/recipes-devtools/systemd/nativesdk-systemd-systemctl.bb > @@ -0,0 +1,17 @@ > +SUMMARY = "Wrapper for enabling systemd services" > + > +LICENSE = "MIT" > +LIC_FILES_CHKSUM = > "file://${COREBASE}/meta/COPYING.MIT;md5=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 > > > > > -- 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/21656 Group Owner: [email protected] Unsubscribe: https://lists.openembedded.org/g/openembedded-devel/unsub [[email protected]] -=-=-=-=-=-=-=-=-=-=-=-
