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