diff options
-rw-r--r-- | TODO | 40 | ||||
-rw-r--r-- | src/boot/efi/boot.c | 139 | ||||
-rw-r--r-- | src/core/bpf-foreign.c | 20 | ||||
-rw-r--r-- | src/core/bpf-foreign.h | 5 | ||||
-rw-r--r-- | src/fundamental/bootspec-fundamental.c | 56 | ||||
-rw-r--r-- | src/fundamental/bootspec-fundamental.h | 16 | ||||
-rw-r--r-- | src/fundamental/macro-fundamental.h | 10 | ||||
-rw-r--r-- | src/fundamental/meson.build | 6 | ||||
-rw-r--r-- | src/fundamental/sha256.h | 2 | ||||
-rw-r--r-- | src/fundamental/type.h | 21 | ||||
-rw-r--r-- | src/fundamental/types-fundamental.h | 39 | ||||
-rw-r--r-- | src/shared/bootspec.c | 49 | ||||
-rw-r--r-- | src/shared/bootspec.h | 2 | ||||
-rwxr-xr-x | test/units/testsuite-20.sh | 17 |
14 files changed, 309 insertions, 113 deletions
@@ -81,6 +81,46 @@ Janitorial Clean-ups: Features: +* userdb: when synthesizing NSS records, pick "best" password from defined + passwords, not just the first. i.e. if there are multiple defined, prefer + unlocked over locked and prefer non-empty over empty. + +* maybe add a tool inspired by the GPT auto discovery spec that runs in the + initrd and rearranges the rootfs hierarchy via bind mounts, if + enabled. Specifically in some top-level dir /@auto/ it will look for + dirs/symlinks/subvolumes that are named after their purpose, and optionally + encode a version as well as assessment counters, and then mount them into the + file system tree to boot into, similar to how we do that for the gpt auto + logic. Maybe then bind mount the original root into /.superior or something + like that (so that update tools can look there). Further discussion in this + thread: + https://lists.freedesktop.org/archives/systemd-devel/2021-November/047059.html + The GPT dissection logic should automatically enable this tool whenever we + detect a specially marked root fs (i.e introduce a new generic root gpt type + for this, that is arch independent). The also implement this in the image + dissection logic, so that nspawn/RootImage= and so on grok it. Maybe make + generic enough so that it can also work for ostrees arrangements. + +* if a path ending in ".auto.d/" is set for RootDirectory=/RootImage= then do a + strverscmp() of everything inside that dir and use that. i.e. implement very + simple version control. Also use this in systemd-nspawn --image= and so on. + +* homed: while a home dir is not activated generate slightly different NSS + records for it, that reports the home dir as "/" and the shell as some binary + provided by us. Then, when an SSH login happens and SSH permits it our binary + is invoked. This binary can then talk to homed and activate the homedir if + it's not around yet, prompting the user for a password. Once that succeeded + we'll switch to the real user record, i.e. home dir and shell, and our tool + exec()s the latter. Net effect: ssh'ing into a homed account will just work: + we'll neatly prompt for the homedir's password if its needed. –– Building on + this we could take this even further: since this tool will potentially have + access to the client's ssh-agent (if ssh-agent forwarding is enabled) we + could implement SSH unlocking of a homedir with that: when enrolling a new + ssh pubkey in a user record we'd ask the ssh-agent to sign some random value + with the privkey, then use that as luks key to unlock the home dir. Will not + work for ECDSA keys since their signatures contain a random component, but + will work for RSA and Ed25519 keys. + * add tiny service that decrypts encrypted user records passed via initrd credential logic and drops them into /run where nss-systemd can pick them up, similar to /run/host/userdb/. Usecase: drop a root user JSON record there, diff --git a/src/boot/efi/boot.c b/src/boot/efi/boot.c index 5992a28cdd..0bacfb4bbe 100644 --- a/src/boot/efi/boot.c +++ b/src/boot/efi/boot.c @@ -4,6 +4,7 @@ #include <efigpt.h> #include <efilib.h> +#include "bootspec-fundamental.h" #include "console.h" #include "devicetree.h" #include "disk.h" @@ -28,8 +29,9 @@ #define TEXT_ATTR_SWAP(c) EFI_TEXT_ATTR(((c) & 0b11110000) >> 4, (c) & 0b1111) -/* magic string to find in the binary image */ -_used_ _section_(".sdmagic") static const char magic[] = "#### LoaderInfo: systemd-boot " GIT_VERSION " ####"; +/* Magic string for recognizing our own binaries */ +_used_ _section_(".sdmagic") static const char magic[] = + "#### LoaderInfo: systemd-boot " GIT_VERSION " ####"; /* Makes systemd-boot available from \EFI\Linux\ for testing purposes. */ _used_ _section_(".osrel") static const char osrel[] = @@ -44,10 +46,10 @@ enum loader_type { }; typedef struct { - CHAR16 *id; /* The unique identifier for this entry */ - CHAR16 *title_show; - CHAR16 *title; - CHAR16 *version; + CHAR16 *id; /* The unique identifier for this entry (typically the filename of the file defining the entry) */ + CHAR16 *title_show; /* The string to actually display (this is made unique before showing) */ + CHAR16 *title; /* The raw (human readable) title string of the entry (not necessarily unique) */ + CHAR16 *version; /* The raw (human readable) version string of the entry */ CHAR16 *machine_id; EFI_HANDLE *device; enum loader_type type; @@ -1651,14 +1653,14 @@ static void config_sort_entries(Config *config) { sort_pointer_array((void**) config->entries, config->entry_count, (compare_pointer_func_t) config_entry_compare); } -static INTN config_entry_find(Config *config, CHAR16 *needle) { +static INTN config_entry_find(Config *config, const CHAR16 *needle) { assert(config); if (!needle) return -1; for (UINTN i = 0; i < config->entry_count; i++) - if (MetaiMatch(config->entries[i]->id, needle)) + if (MetaiMatch(config->entries[i]->id, (CHAR16*) needle)) return (INTN) i; return -1; @@ -1732,13 +1734,9 @@ static void config_title_generate(Config *config) { /* set title */ for (UINTN i = 0; i < config->entry_count; i++) { - CHAR16 *title; - FreePool(config->entries[i]->title_show); - title = config->entries[i]->title; - if (!title) - title = config->entries[i]->id; - config->entries[i]->title_show = StrDuplicate(title); + config->entries[i]->title_show = StrDuplicate( + config->entries[i]->title ?: config->entries[i]->id); } if (!find_nonunique(config->entries, config->entry_count)) @@ -2044,8 +2042,10 @@ static void config_entry_add_linux( NULL, }; - _cleanup_freepool_ CHAR16 *os_name_pretty = NULL, *os_name = NULL, *os_id = NULL, - *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL, *os_image_version = NULL; + _cleanup_freepool_ CHAR16 *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL, + *os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL, + *path = NULL; + const CHAR16 *good_name, *good_version; _cleanup_freepool_ CHAR8 *content = NULL; UINTN offs[_SECTION_MAX] = {}; UINTN szs[_SECTION_MAX] = {}; @@ -2077,82 +2077,101 @@ static void config_entry_add_linux( /* read properties from the embedded os-release file */ while ((line = line_get_key_value(content, (CHAR8 *)"=", &pos, &key, &value))) { - if (strcmpa((CHAR8 *)"PRETTY_NAME", key) == 0) { - FreePool(os_name_pretty); - os_name_pretty = stra_to_str(value); + if (strcmpa((const CHAR8*) "PRETTY_NAME", key) == 0) { + FreePool(os_pretty_name); + os_pretty_name = stra_to_str(value); + continue; + } + + if (strcmpa((const CHAR8*) "IMAGE_ID", key) == 0) { + FreePool(os_image_id); + os_image_id = stra_to_str(value); continue; } - if (strcmpa((CHAR8 *)"NAME", key) == 0) { + if (strcmpa((const CHAR8*) "NAME", key) == 0) { FreePool(os_name); os_name = stra_to_str(value); continue; } - if (strcmpa((CHAR8 *)"ID", key) == 0) { + if (strcmpa((const CHAR8*) "ID", key) == 0) { FreePool(os_id); os_id = stra_to_str(value); continue; } - if (strcmpa((CHAR8 *)"VERSION", key) == 0) { + if (strcmpa((const CHAR8*) "IMAGE_VERSION", key) == 0) { + FreePool(os_image_version); + os_image_version = stra_to_str(value); + continue; + } + + if (strcmpa((const CHAR8*) "VERSION", key) == 0) { FreePool(os_version); os_version = stra_to_str(value); continue; } - if (strcmpa((CHAR8 *)"VERSION_ID", key) == 0) { + if (strcmpa((const CHAR8*) "VERSION_ID", key) == 0) { FreePool(os_version_id); os_version_id = stra_to_str(value); continue; } - if (strcmpa((CHAR8 *)"BUILD_ID", key) == 0) { + if (strcmpa((const CHAR8*) "BUILD_ID", key) == 0) { FreePool(os_build_id); os_build_id = stra_to_str(value); continue; } - - if (strcmpa((const CHAR8*) "IMAGE_VERSION", key) == 0) { - FreePool(os_image_version); - os_image_version = stra_to_str(value); - continue; - } } - if ((os_name_pretty || os_name) && os_id && (os_image_version || os_version || os_version_id || os_build_id)) { - _cleanup_freepool_ CHAR16 *path = NULL; - - path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName); - - entry = config_entry_add_loader( - config, - device, - LOADER_LINUX, - f->FileName, - /* key= */ 'l', - os_name_pretty ?: os_name, - path, - os_image_version ?: (os_version ?: (os_version_id ? : os_build_id))); - - config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi"); - - if (szs[SECTION_CMDLINE] == 0) - continue; + if (!bootspec_pick_name_version( + os_pretty_name, + os_image_id, + os_name, + os_id, + os_image_version, + os_version, + os_version_id, + os_build_id, + &good_name, + &good_version)) + continue; - FreePool(content); - content = NULL; + path = PoolPrint(L"\\EFI\\Linux\\%s", f->FileName); + if (!path) + return (void) log_oom(); + + entry = config_entry_add_loader( + config, + device, + LOADER_LINUX, + f->FileName, + /* key= */ 'l', + good_name, + path, + good_version); + if (!entry) + return (void) log_oom(); + + config_entry_parse_tries(entry, L"\\EFI\\Linux", f->FileName, L".efi"); + + if (szs[SECTION_CMDLINE] == 0) + continue; - /* read the embedded cmdline file */ - err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, NULL); - if (!EFI_ERROR(err)) { + content = mfree(content); - /* chomp the newline */ - if (content[szs[SECTION_CMDLINE] - 1] == '\n') - content[szs[SECTION_CMDLINE] - 1] = '\0'; + /* read the embedded cmdline file */ + err = file_read(linux_dir, f->FileName, offs[SECTION_CMDLINE], szs[SECTION_CMDLINE], &content, NULL); + if (!EFI_ERROR(err)) { + /* chomp the newline */ + if (content[szs[SECTION_CMDLINE] - 1] == '\n') + content[szs[SECTION_CMDLINE] - 1] = '\0'; - entry->options = stra_to_str(content); - } + entry->options = stra_to_str(content); + if (!entry->options) + return (void) log_oom(); } } } @@ -2480,7 +2499,7 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) { err = image_start(root_dir, image, &config, entry); if (EFI_ERROR(err)) { graphics_mode(FALSE); - log_error_stall(L"Failed to execute %s (%s): %r", entry->title, entry->loader, err); + log_error_stall(L"Failed to execute %s (%s): %r", entry->title_show, entry->loader, err); goto out; } diff --git a/src/core/bpf-foreign.c b/src/core/bpf-foreign.c index 6b93b9785f..8538792b60 100644 --- a/src/core/bpf-foreign.c +++ b/src/core/bpf-foreign.c @@ -4,8 +4,10 @@ #include "bpf-program.h" #include "cgroup.h" #include "memory-util.h" +#include "missing_magic.h" #include "mountpoint-util.h" #include "set.h" +#include "stat-util.h" typedef struct BPFForeignKey BPFForeignKey; struct BPFForeignKey { @@ -84,6 +86,14 @@ static int bpf_foreign_prepare( assert(u); assert(bpffs_path); + r = path_is_fs_type(bpffs_path, BPF_FS_MAGIC); + if (r < 0) + return log_unit_error_errno(u, r, + "Failed to determine filesystem type of %s: %m", bpffs_path); + if (r == 0) + return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL), + "Path in BPF filesystem is expected."); + r = bpf_program_new_from_bpffs_path(bpffs_path, &prog); if (r < 0) return log_unit_error_errno(u, r, "Failed to create foreign BPFProgram: %m"); @@ -111,16 +121,6 @@ static int bpf_foreign_prepare( return 0; } -int bpf_foreign_supported(void) { - int r; - - r = cg_all_unified(); - if (r <= 0) - return r; - - return path_is_mount_point("/sys/fs/bpf", NULL, 0); -} - int bpf_foreign_install(Unit *u) { _cleanup_free_ char *cgroup_path = NULL; CGroupBPFForeignProgram *p; diff --git a/src/core/bpf-foreign.h b/src/core/bpf-foreign.h index 9559cd7981..e387b1b1d3 100644 --- a/src/core/bpf-foreign.h +++ b/src/core/bpf-foreign.h @@ -4,7 +4,10 @@ #include "unit.h" -int bpf_foreign_supported(void); +static inline int bpf_foreign_supported(void) { + return cg_all_unified(); +} + /* * Attach cgroup-bpf programs foreign to systemd, i.e. loaded to the kernel by an entity * external to systemd. diff --git a/src/fundamental/bootspec-fundamental.c b/src/fundamental/bootspec-fundamental.c new file mode 100644 index 0000000000..9c4aee9744 --- /dev/null +++ b/src/fundamental/bootspec-fundamental.c @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include "bootspec-fundamental.h" + +sd_bool bootspec_pick_name_version( + const sd_char *os_pretty_name, + const sd_char *os_image_id, + const sd_char *os_name, + const sd_char *os_id, + const sd_char *os_image_version, + const sd_char *os_version, + const sd_char *os_version_id, + const sd_char *os_build_id, + const sd_char **ret_name, + const sd_char **ret_version) { + + const sd_char *good_name, *good_version; + + /* Find the best human readable title and version string for a boot entry (using the os-release(5) + * fields). Precise is preferred over vague, and human readable over machine readable. Thus: + * + * 1. First priority gets the PRETTY_NAME field, which is the primary string intended for display, + * and should already contain both a nice description and a version indication (if that concept + * applies). + * + * 2. Otherwise we go for IMAGE_ID and IMAGE_VERSION (thus we show details about the image, + * i.e. specific combination of packages and configuration), if that concept applies. + * + * 3. Otherwise we go for NAME and VERSION (i.e. human readable OS name and version) + * + * 4. Otherwise we go for ID and VERSION_ID (i.e. machine readable OS name and version) + * + * 5. Finally, for the version we'll use BUILD_ID (i.e. a machine readable version that identifies + * the original OS build used during installation) + * + * Note that the display logic will show only the name by default, except if that isn't unique in + * which case the version is shown too. + * + * Note that name/version determined here are used only for display purposes. Boot entry preference + * sorting (i.e. algorithmic ordering of boot entries) is done based on the order of the entry "id" + * string (i.e. not on os-release(5) data). */ + + good_name = os_pretty_name ?: (os_image_id ?: (os_name ?: os_id)); + good_version = os_image_version ?: (os_version ?: (os_version_id ? : os_build_id)); + + if (!good_name || !good_version) + return sd_false; + + if (ret_name) + *ret_name = good_name; + + if (ret_version) + *ret_version = good_version; + + return sd_true; +} diff --git a/src/fundamental/bootspec-fundamental.h b/src/fundamental/bootspec-fundamental.h new file mode 100644 index 0000000000..2cb6d7daa1 --- /dev/null +++ b/src/fundamental/bootspec-fundamental.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include "types-fundamental.h" + +sd_bool bootspec_pick_name_version( + const sd_char *os_pretty_name, + const sd_char *os_image_id, + const sd_char *os_name, + const sd_char *os_id, + const sd_char *os_image_version, + const sd_char *os_version, + const sd_char *os_version_id, + const sd_char *os_build_id, + const sd_char **ret_name, + const sd_char **ret_version); diff --git a/src/fundamental/macro-fundamental.h b/src/fundamental/macro-fundamental.h index 44af0bd0a0..1f640b3ae3 100644 --- a/src/fundamental/macro-fundamental.h +++ b/src/fundamental/macro-fundamental.h @@ -6,7 +6,7 @@ #endif #include <limits.h> -#include "type.h" +#include "types-fundamental.h" #define _align_(x) __attribute__((__aligned__(x))) #define _const_ __attribute__((__const__)) @@ -85,8 +85,8 @@ #define ONCE __ONCE(UNIQ_T(_once_, UNIQ)) #define __ONCE(o) \ ({ \ - static bool (o) = false; \ - __sync_bool_compare_and_swap(&(o), false, true); \ + static sd_bool (o) = sd_false; \ + __sync_bool_compare_and_swap(&(o), sd_false, sd_true); \ }) #undef MAX @@ -236,7 +236,7 @@ #define IN_SET(x, ...) \ ({ \ - sd_bool _found = false; \ + sd_bool _found = sd_false; \ /* If the build breaks in the line below, you need to extend the case macros. (We use "long double" as \ * type for the array, in the hope that checkers such as ubsan don't complain that the initializers for \ * the array are not representable by the base type. Ideally we'd use typeof(x) as base type, but that \ @@ -245,7 +245,7 @@ assert_cc(ELEMENTSOF(__assert_in_set) <= 20); \ switch(x) { \ FOR_EACH_MAKE_CASE(__VA_ARGS__) \ - _found = true; \ + _found = sd_true; \ break; \ default: \ break; \ diff --git a/src/fundamental/meson.build b/src/fundamental/meson.build index 3c9f07b191..287f0fe36a 100644 --- a/src/fundamental/meson.build +++ b/src/fundamental/meson.build @@ -3,13 +3,15 @@ fundamental_path = meson.current_source_dir() fundamental_headers = files( + 'bootspec-fundamental.h', 'efivars-fundamental.h', 'macro-fundamental.h', - 'string-util-fundamental.h', 'sha256.h', - 'type.h') + 'string-util-fundamental.h', + 'types-fundamental.h') sources = ''' + bootspec-fundamental.c efivars-fundamental.c string-util-fundamental.c sha256.c diff --git a/src/fundamental/sha256.h b/src/fundamental/sha256.h index 9fc090b4e0..abc4167628 100644 --- a/src/fundamental/sha256.h +++ b/src/fundamental/sha256.h @@ -6,7 +6,7 @@ #include <efilib.h> #endif -#include "type.h" +#include "types-fundamental.h" struct sha256_ctx { uint32_t H[8]; diff --git a/src/fundamental/type.h b/src/fundamental/type.h deleted file mode 100644 index 2a9a114bbc..0000000000 --- a/src/fundamental/type.h +++ /dev/null @@ -1,21 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.1-or-later */ -#pragma once - -#ifdef SD_BOOT -#include <efi.h> - -typedef BOOLEAN sd_bool; -typedef CHAR16 sd_char; -typedef INTN sd_int; -typedef UINTN size_t; - -#define true TRUE -#define false FALSE -#else -#include <stdbool.h> -#include <stdint.h> - -typedef bool sd_bool; -typedef char sd_char; -typedef int sd_int; -#endif diff --git a/src/fundamental/types-fundamental.h b/src/fundamental/types-fundamental.h new file mode 100644 index 0000000000..5977e40c6c --- /dev/null +++ b/src/fundamental/types-fundamental.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +/* This defines a number of basic types that are one thing in userspace and another in the UEFI environment, + * but mostly the same in concept and behaviour. + * + * Note: if the definition of these types/values has slightly different semantics in userspace and in the + * UEFI environment then please prefix its name with "sd_" to make clear these types have special semantics, + * and *we* defined them. Otherwise, if the types are effectively 100% identical in behaviour in userspace + * and UEFI environment you can omit the prefix. (Examples: sd_char is 8 bit in userspace and 16 bit in UEFI + * space hence it should have the sd_ prefix; but size_t in userspace and UINTN in UEFI environment are 100% + * defined the same way ultimately, hence it's OK to just define size_t as alias to UINTN in UEFI + * environment, so that size_t can be used everywhere, without any "sd_" prefix.) + * + * Note: we generally prefer the userspace names of types and concepts. i.e. if in doubt please name types + * after the userspace vocabulary, and let's keep UEFI vocabulary specific to the UEFI build environment. */ + +#ifdef SD_BOOT +#include <efi.h> + +typedef BOOLEAN sd_bool; +typedef CHAR16 sd_char; +typedef INTN sd_int; +typedef UINTN size_t; + +#define sd_true TRUE +#define sd_false FALSE +#else +#include <stdbool.h> +#include <stdint.h> + +typedef bool sd_bool; +typedef char sd_char; +typedef int sd_int; + +#define sd_true true +#define sd_false false + +#endif diff --git a/src/shared/bootspec.c b/src/shared/bootspec.c index e538e2bd87..8fdb5272e9 100644 --- a/src/shared/bootspec.c +++ b/src/shared/bootspec.c @@ -9,6 +9,7 @@ #include "alloc-util.h" #include "blkid-util.h" +#include "bootspec-fundamental.h" #include "bootspec.h" #include "conf-files.h" #include "def.h" @@ -287,13 +288,13 @@ static int boot_entry_load_unified( const char *cmdline, BootEntry *ret) { - _cleanup_free_ char *os_pretty_name = NULL, *os_id = NULL, *version_id = NULL, *build_id = NULL; + _cleanup_free_ char *os_pretty_name = NULL, *os_image_id = NULL, *os_name = NULL, *os_id = NULL, + *os_image_version = NULL, *os_version = NULL, *os_version_id = NULL, *os_build_id = NULL; _cleanup_(boot_entry_free) BootEntry tmp = { .type = BOOT_ENTRY_UNIFIED, }; + const char *k, *good_name, *good_version; _cleanup_fclose_ FILE *f = NULL; - const char *k; - char *b; int r; assert(root); @@ -310,24 +311,42 @@ static int boot_entry_load_unified( r = parse_env_file(f, "os-release", "PRETTY_NAME", &os_pretty_name, + "IMAGE_ID", &os_image_id, + "NAME", &os_name, "ID", &os_id, - "VERSION_ID", &version_id, - "BUILD_ID", &build_id); + "IMAGE_VERSION", &os_image_version, + "VERSION", &os_version, + "VERSION_ID", &os_version_id, + "BUILD_ID", &os_build_id); if (r < 0) return log_error_errno(r, "Failed to parse os-release data from unified kernel image %s: %m", path); - if (!os_pretty_name || !os_id || !(version_id || build_id)) + if (!bootspec_pick_name_version( + os_pretty_name, + os_image_id, + os_name, + os_id, + os_image_version, + os_version, + os_version_id, + os_build_id, + &good_name, + &good_version)) return log_error_errno(SYNTHETIC_ERRNO(EBADMSG), "Missing fields in os-release data from unified kernel image %s, refusing.", path); - b = basename(path); - tmp.id = strdup(b); - tmp.id_old = strjoin(os_id, "-", version_id ?: build_id); - if (!tmp.id || !tmp.id_old) - return log_oom(); + r = path_extract_filename(path, &tmp.id); + if (r < 0) + return log_error_errno(r, "Failed to extract file name from '%s': %m", path); if (!efi_loader_entry_name_valid(tmp.id)) return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid loader entry name: %s", tmp.id); + if (os_id && os_version_id) { + tmp.id_old = strjoin(os_id, "-", os_version_id); + if (!tmp.id_old) + return log_oom(); + } + tmp.path = strdup(path); if (!tmp.path) return log_oom(); @@ -346,7 +365,13 @@ static int boot_entry_load_unified( delete_trailing_chars(tmp.options[0], WHITESPACE); - tmp.title = TAKE_PTR(os_pretty_name); + tmp.title = strdup(good_name); + if (!tmp.title) + return log_oom(); + + tmp.version = strdup(good_version); + if (!tmp.version) + return log_oom(); *ret = tmp; tmp = (BootEntry) {}; diff --git a/src/shared/bootspec.h b/src/shared/bootspec.h index c7b4a2ee02..81845f47e3 100644 --- a/src/shared/bootspec.h +++ b/src/shared/bootspec.h @@ -20,7 +20,7 @@ typedef enum BootEntryType { typedef struct BootEntry { BootEntryType type; - char *id; /* This is the file basename without extension */ + char *id; /* This is the file basename (including extension!) */ char *id_old; /* Old-style ID, for deduplication purposes. */ char *path; /* This is the full path to the drop-in file */ char *root; /* The root path in which the drop-in was found, i.e. to which 'kernel', 'efi' and 'initrd' are relative */ diff --git a/test/units/testsuite-20.sh b/test/units/testsuite-20.sh index 17a27d7d7c..fc87c18c4c 100755 --- a/test/units/testsuite-20.sh +++ b/test/units/testsuite-20.sh @@ -143,6 +143,23 @@ systemd-run --unit=test20-mainpidsh3.service \ # Test that this failed due to timeout, and not some other error test "$(systemctl show -P Result test20-mainpidsh3.service)" = timeout +# Test that scope units work +systemd-run --scope --unit test20-true.scope /bin/true +test "$(systemctl show -P Result test20-true.scope)" = success + +# Test that user scope units work as well + +runas() { + declare userid=$1 + shift + # shellcheck disable=SC2016 + su "$userid" -s /bin/sh -c 'XDG_RUNTIME_DIR=/run/user/$UID exec "$@"' -- sh "$@" +} + +systemctl start user@4711.service +runas testuser systemd-run --scope --user --unit test20-true.scope /bin/true +test "$(systemctl show -P Result test20-true.scope)" = success + systemd-analyze log-level info echo OK >/testok |