From 781ced1a59cc34bf7ceba7153b2e2a6d1b63f4cd Mon Sep 17 00:00:00 2001 From: shivanandvp Date: Tue, 5 Jul 2022 23:08:43 -0500 Subject: [PATCH 1/2] feat: feat: Ungroup systemd units in services-systemd module --- src/modules/services-systemd/main.py | 107 ++++++------------ .../services-systemd/services-systemd.conf | 84 +++++--------- .../services-systemd.schema.yaml | 14 +-- 3 files changed, 70 insertions(+), 135 deletions(-) diff --git a/src/modules/services-systemd/main.py b/src/modules/services-systemd/main.py index 6501aa44c..4eef3f096 100644 --- a/src/modules/services-systemd/main.py +++ b/src/modules/services-systemd/main.py @@ -7,10 +7,10 @@ # SPDX-FileCopyrightText: 2014 Teo Mrnjavac # SPDX-FileCopyrightText: 2017 Alf Gaida # SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot +# SPDX-FileCopyrightText: 2022 shivanandvp # SPDX-License-Identifier: GPL-3.0-or-later # # Calamares is Free Software: see the License-Identifier above. -# import libcalamares @@ -23,96 +23,61 @@ _ = gettext.translation("calamares-python", def pretty_name(): - return _("Configure systemd services") + return _("Configure systemd units") -def systemctl(targets, command, suffix): +def systemctl(units): """ - For each entry in @p targets, run "systemctl ", - where is the entry's name plus the given @p suffix. - (No dot is added between name and suffix; suffix may be empty) + For each entry in @p units, run "systemctl ", + where each unit is a map of unit name, action, and a flag. Returns a failure message, or None if this was successful. - Services that are not mandatory have their failures suppressed + Units that are not mandatory have their failures suppressed silently. """ - for svc in targets: - if isinstance(svc, str): - name = svc + + for unit in units: + if isinstance(unit, str): + name = unit + action = "enable" mandatory = False else: - name = svc["name"] - mandatory = svc.get("mandatory", False) + name = unit["name"] + action = unit.get("action", "enable") + mandatory = unit.get("mandatory", False) - ec = libcalamares.utils.target_env_call( - ['systemctl', command, "{}{}".format(name, suffix)] + exit_code = libcalamares.utils.target_env_call( + ['systemctl', action, name] + ) + + if exit_code != 0: + libcalamares.utils.warning( + "Cannot {} systemd unit {}".format(action, name) ) - - if ec != 0: libcalamares.utils.warning( - "Cannot {} systemd {} {}".format(command, suffix, name) - ) - libcalamares.utils.warning( - "systemctl {} call in chroot returned error code {}".format(command, ec) - ) + "systemctl {} call in chroot returned error code {}".format(action, exit_code) + ) if mandatory: - title = _("Cannot modify service") - diagnostic = _("systemctl {arg!s} call in chroot returned error code {num!s}.").format(arg=command, num=ec) - - if command == "enable" and suffix == ".service": - description = _("Cannot enable systemd service {name!s}.") - elif command == "enable" and suffix == ".target": - description = _("Cannot enable systemd target {name!s}.") - elif command == "enable" and suffix == ".timer": - description = _("Cannot enable systemd timer {name!s}.") - elif command == "disable" and suffix == ".service": - description = _("Cannot enable systemd service {name!s}.") - elif command == "disable" and suffix == ".target": - description = _("Cannot disable systemd target {name!s}.") - elif command == "mask": - description = _("Cannot mask systemd unit {name!s}.") - else: - description = _("Unknown systemd commands {command!s} and {suffix!s} for unit {name!s}.") - - return (title, - description.format(name=name, command=command, suffix=suffix) + " " + diagnostic - ) + title = _("Cannot modify unit") + diagnostic = _("systemctl {_action!s} call in chroot returned error code {_exit_code!s}.").format(_action=action, _exit_code=exit_code) + description = _("Cannot {_action!s} systemd unit {_name!s}.").format(_action=action, _name=name) + return ( + title, + description + " " + diagnostic + ) return None def run(): """ - Setup systemd services + Setup systemd units """ cfg = libcalamares.job.configuration - # note that the "systemctl enable" and "systemctl disable" commands used - # here will work in a chroot; in fact, they are the only systemctl commands - # that support that, see: - # http://0pointer.de/blog/projects/changing-roots.html - - r = systemctl(cfg.get("services", []), "enable", ".service") - if r is not None: - return r - - r = systemctl(cfg.get("targets", []), "enable", ".target") - if r is not None: - return r - - r = systemctl(cfg.get("timers", []), "enable", ".timer") - if r is not None: - return r - - r = systemctl(cfg.get("disable", []), "disable", ".service") - if r is not None: - return r - - r = systemctl(cfg.get("disable-targets", []), "disable", ".target") - if r is not None: - return r - - r = systemctl(cfg.get("mask", []), "mask", "") - if r is not None: - return r + return_value = systemctl( + cfg.get("units", []) + ) + if return_value is not None: + return return_value return None diff --git a/src/modules/services-systemd/services-systemd.conf b/src/modules/services-systemd/services-systemd.conf index 05583967f..d84cd1349 100644 --- a/src/modules/services-systemd/services-systemd.conf +++ b/src/modules/services-systemd/services-systemd.conf @@ -1,79 +1,53 @@ # SPDX-FileCopyrightText: no # SPDX-License-Identifier: CC0-1.0 # -# Systemd services manipulation. +# Systemd units manipulation. # -# This module can enable services, timers and targets for systemd -# (if packaging doesn't already do that). It can also -# disable services and targets as well as mask units. -# -# The order of operations is fixed. Enable services, enable targets, -# enable timers, disable services, disable targets and finally apply masks. +# This module can perform actions using systemd units, +# (for example, enabling, disabling, or masking services, sockets, paths, etc.) --- -# There are several configuration keys for this module: -# *services*, *targets*, *timers*, *disable*, *disable-targets* and *mask*. -# The value of each key is a list of entries. Each entry has two keys: -# - *name* is the (string) name of the service or target that is being -# changed. Use quotes. Don't include unit suffix in the name. For -# example, it should be "NetworkManager", not "NetworkManager.service" +# There is one key for this module: *units*. Its value is a list of entries. +# Each entry has three keys: +# - *name* is the (string) name of the systemd unit that is being changed. +# Use quotes. You can use any valid systemd unit here (for example, +# "NetworkManager.service", "cups.socket", "lightdm", "gdm", etc.) +# - *action* is the (string) action that you want to perform over the unit +# (for example, "enable", "disable", "mask", "unmask", etc.). Please +# ensure that the action can actually run under chroot (otherwise it is +# pointless) # - *mandatory* is a boolean option, which states whether the change # must be done successfully. If systemd reports an error while changing # a mandatory entry, the installation will fail. When mandatory is false, # errors for that systemd unit are ignored. If mandatory # is not specified, the default is false. # -# An entry may also be given as a single string, which is then -# interpreted as the name of the service. In this case, mandatory -# is also set to the default of false. -# -# Use [] to express an empty list. +# The order of operations is the same as the order in which entries +# appear in the list -# # This example enables NetworkManager (and fails if it can't), -# # disables cups (and ignores failure). Then it enables the +# # This example enables NetworkManager.service (and fails if it can't), +# # disables cups.socket (and ignores failure). Then it enables the # # graphical target (e.g. so that SDDM runs for login), and -# # finally disables pacman-init (an ArchLinux-only service). +# # finally masks pacman-init (an ArchLinux-only service). # # -# # Enables .service -# services: +# units: # - name: "NetworkManager" +# action: "enable" # mandatory: true -# - name: "cups" +# +# - name: "cups.socket" +# action: "disable" # mandatory: false # -# # Enables .target -# targets: # - name: "graphical" -# mandatory: true +# action: "enable" +# # The property "mandatory" is taken to be false by default here +# # because it is not specified # -# # Enables .timer -# timers: -# - name: "fstrim" -# mandatory: false -# -# # Disables .service -# disable: # - name: "pacman-init" -# mandatory: false -# -# # Disables .target -# # .. this shows how to use just the name -# disable-targets: -# - graphical -# -# # Masks (stronger version of disable). This section -# # is unusual because you **must** include the suffix -# # (e.g. ".service") as part of the name, so, e.g. to mask -# # NetworkManager (rather than just disable it) you must -# # specify "NetworkManager.service" as name. -# mask: -# - name: "NetworkManager.service" -# - mandatory: true +# action: "mask" +# # The property "mandatory" is taken to be false by default here +# # because it is not specified # By default, no changes are made. -services: [] -targets: [] -timers: [] -disable: [] -disable-targets: [] -mask: [] +units: [] diff --git a/src/modules/services-systemd/services-systemd.schema.yaml b/src/modules/services-systemd/services-systemd.schema.yaml index ee80a80be..7e1fe052e 100644 --- a/src/modules/services-systemd/services-systemd.schema.yaml +++ b/src/modules/services-systemd/services-systemd.schema.yaml @@ -4,22 +4,18 @@ $schema: https://json-schema.org/schema# $id: https://calamares.io/schemas/services-systemd definitions: - service: - $id: 'definitions/service' + unit: + $id: 'definitions/unit' type: object - description: a name and a flag for services, targets, and others + description: a map containing a unit name, an action, and whether it is mandatory additionalProperties: false properties: name: { type: string } + action: { type: string, default: "enable" } mandatory: { type: boolean, default: false } required: [ name ] additionalProperties: false type: object properties: - services: { type: array, items: { $ref: 'definitions/service' } } - targets: { type: array, items: { $ref: 'definitions/service' } } - timers: { type: array, items: { $ref: 'definitions/service' } } - disable: { type: array, items: { $ref: 'definitions/service' } } - disable-targets: { type: array, items: { $ref: 'definitions/service' } } - mask: { type: array, items: { $ref: 'definitions/service' } } + units: { type: array, items: { $ref: 'definitions/unit' } } From 27c187084bd705a9aaa7cddc7d2d4bc663b4bc0e Mon Sep 17 00:00:00 2001 From: shivanandvp Date: Wed, 6 Jul 2022 20:01:50 -0500 Subject: [PATCH 2/2] fix: Check for name key based on comments on PR --- src/modules/services-systemd/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/modules/services-systemd/main.py b/src/modules/services-systemd/main.py index 4eef3f096..ef2984fba 100644 --- a/src/modules/services-systemd/main.py +++ b/src/modules/services-systemd/main.py @@ -29,7 +29,7 @@ def pretty_name(): def systemctl(units): """ For each entry in @p units, run "systemctl ", - where each unit is a map of unit name, action, and a flag. + where each unit is a mapping of unit name, action, and a flag. Returns a failure message, or None if this was successful. Units that are not mandatory have their failures suppressed @@ -42,6 +42,9 @@ def systemctl(units): action = "enable" mandatory = False else: + if not unit.has_key("name"): + libcalamares.utils.error("The key 'name' is missing from the mapping {_unit!s}. Continuing to the next unit.".format(_unit=str(unit))) + continue name = unit["name"] action = unit.get("action", "enable") mandatory = unit.get("mandatory", False)