diff options
author | maanyagoenka <maanyagoenka@microsoft.com> | 2023-03-29 22:35:18 +0200 |
---|---|---|
committer | maanyagoenka <maanyagoenka@microsoft.com> | 2023-04-05 23:50:04 +0200 |
commit | 4da1df42ac419180f8bbfd6bc53a50e02b14af85 (patch) | |
tree | 08c230fee28af7765adc2b6af9d2b47c80ad35cf | |
parent | extension-release: establish compatibility between host file and extension-re... (diff) | |
download | systemd-4da1df42ac419180f8bbfd6bc53a50e02b14af85.tar.xz systemd-4da1df42ac419180f8bbfd6bc53a50e02b14af85.zip |
confext: add multi call functionality to sysext
The confext concept is an extension of the existing sysext concept and
allows to extend the host's filesystem or a unit's filesystem with signed
images that add new files to the /etc/ directory using OverlayFS.
-rw-r--r-- | src/sysext/meson.build | 4 | ||||
-rw-r--r-- | src/sysext/sysext.c | 92 |
2 files changed, 68 insertions, 28 deletions
diff --git a/src/sysext/meson.build b/src/sysext/meson.build index f159adb8cc..ede953712c 100644 --- a/src/sysext/meson.build +++ b/src/sysext/meson.build @@ -1,3 +1,7 @@ # SPDX-License-Identifier: LGPL-2.1-or-later systemd_sysext_sources = files('sysext.c') + +meson.add_install_script(meson_make_symlink, + rootbindir / 'systemd-sysext', + rootbindir / 'systemd-confext') diff --git a/src/sysext/sysext.c b/src/sysext/sysext.c index 97cf8ba52e..d235073743 100644 --- a/src/sysext/sysext.c +++ b/src/sysext/sysext.c @@ -39,16 +39,49 @@ #include "user-util.h" #include "verbs.h" -static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default */ +static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default for sysext and /etc by default for confext */ static char *arg_root = NULL; static JsonFormatFlags arg_json_format_flags = JSON_FORMAT_OFF; static PagerFlags arg_pager_flags = 0; static bool arg_legend = true; static bool arg_force = false; +/* Is set to IMAGE_CONFEXT when systemd is called with the confext functionality instead of the default */ +static ImageClass arg_image_class = IMAGE_SYSEXT; + STATIC_DESTRUCTOR_REGISTER(arg_hierarchies, strv_freep); STATIC_DESTRUCTOR_REGISTER(arg_root, freep); +/* Helper struct for naming simplicity and reusability */ +static const struct { + const char *dot_directory_name; + const char *directory_name; + const char *short_identifier; + const char *short_identifier_plural; + const char *level_env; + const char *scope_env; + const char *name_env; +} image_class_info[_IMAGE_CLASS_MAX] = { + [IMAGE_SYSEXT] = { + .dot_directory_name = ".systemd-sysext", + .directory_name = "systemd-sysext", + .short_identifier = "sysext", + .short_identifier_plural = "extensions", + .level_env = "SYSEXT_LEVEL", + .scope_env = "SYSEXT_SCOPE", + .name_env = "SYSTEMD_SYSEXT_HIERARCHIES", + }, + [IMAGE_CONFEXT] = { + .dot_directory_name = ".systemd-confext", + .directory_name = "systemd-confext", + .short_identifier = "confext", + .short_identifier_plural = "confexts", + .level_env = "CONFEXT_LEVEL", + .scope_env = "CONFEXT_SCOPE", + .name_env = "SYSTEMD_CONFEXT_HIERARCHIES", + } +}; + static int is_our_mount_point(const char *p) { _cleanup_free_ char *buf = NULL, *f = NULL; struct stat st; @@ -70,26 +103,26 @@ static int is_our_mount_point(const char *p) { /* So we know now that it's a mount point. Now let's check if it's one of ours, so that we don't * accidentally unmount the user's own /usr/ but just the mounts we established ourselves. We do this * check by looking into the metadata directory we place in merged mounts: if the file - * .systemd-sysext/dev contains the major/minor device pair of the mount we have a good reason to + * ../dev contains the major/minor device pair of the mount we have a good reason to * believe this is one of our mounts. This thorough check has the benefit that we aren't easily * confused if people tar up one of our merged trees and untar them elsewhere where we might mistake * them for a live sysext tree. */ - f = path_join(p, ".systemd-sysext/dev"); + f = path_join(p, image_class_info[arg_image_class].dot_directory_name, "dev"); if (!f) return log_oom(); r = read_one_line_file(f, &buf); if (r == -ENOENT) { - log_debug("Hierarchy '%s' does not carry a .systemd-sysext/dev file, not a sysext merged tree.", p); + log_debug("Hierarchy '%s' does not carry a %s/dev file, not a merged tree.", p, image_class_info[arg_image_class].dot_directory_name); return false; } if (r < 0) - return log_error_errno(r, "Failed to determine whether hierarchy '%s' contains '.systemd-sysext/dev': %m", p); + return log_error_errno(r, "Failed to determine whether hierarchy '%s' contains '%s/dev': %m", p, image_class_info[arg_image_class].dot_directory_name); r = parse_devnum(buf, &dev); if (r < 0) - return log_error_errno(r, "Failed to parse device major/minor stored in '.systemd-sysext/dev' file on '%s': %m", p); + return log_error_errno(r, "Failed to parse device major/minor stored in '%s/dev' file on '%s': %m", image_class_info[arg_image_class].dot_directory_name, p); if (lstat(p, &st) < 0) return log_error_errno(r, "Failed to stat %s: %m", p); @@ -205,7 +238,7 @@ static int verb_status(int argc, char **argv, void *userdata) { continue; } - f = path_join(*p, ".systemd-sysext/extensions"); + f = path_join(*p, image_class_info[arg_image_class].dot_directory_name, image_class_info[arg_image_class].short_identifier_plural); if (!f) return log_oom(); @@ -272,7 +305,7 @@ static int mount_overlayfs( } /* Now mount the actual overlayfs */ - r = mount_nofollow_verbose(LOG_ERR, "sysext", where, "overlay", MS_RDONLY, options); + r = mount_nofollow_verbose(LOG_ERR, image_class_info[arg_image_class].short_identifier, where, "overlay", MS_RDONLY, options); if (r < 0) return r; @@ -315,7 +348,7 @@ static int merge_hierarchy( /* Let's generate a metadata file that lists all extensions we took into account for this * hierarchy. We include this in the final fs, to make things nicely discoverable and * recognizable. */ - f = path_join(meta_path, ".systemd-sysext/extensions"); + f = path_join(meta_path, image_class_info[arg_image_class].dot_directory_name, image_class_info[arg_image_class].short_identifier_plural); if (!f) return log_oom(); @@ -383,12 +416,12 @@ static int merge_hierarchy( /* Now we have mounted the new file system. Let's now figure out its .st_dev field, and make that * available in the metadata directory. This is useful to detect whether the metadata dir actually * belongs to the fs it is found on: if .st_dev of the top-level mount matches it, it's pretty likely - * we are looking at a live sysext tree, and not an unpacked tar or so of one. */ + * we are looking at a live tree, and not an unpacked tar or so of one. */ if (stat(overlay_path, &st) < 0) return log_error_errno(r, "Failed to stat mount '%s': %m", overlay_path); free(f); - f = path_join(meta_path, ".systemd-sysext/dev"); + f = path_join(meta_path, image_class_info[arg_image_class].dot_directory_name, "dev"); if (!f) return log_oom(); @@ -410,7 +443,7 @@ static int strverscmp_improvedp(char *const* a, char *const* b) { static int merge_subprocess(Hashmap *images, const char *workspace) { _cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL, *host_os_release_sysext_level = NULL, - *buf = NULL; + *host_os_release_confext_level = NULL, *buf = NULL; _cleanup_strv_free_ char **extensions = NULL, **paths = NULL; size_t n_extensions = 0; unsigned n_ignored = 0; @@ -437,11 +470,13 @@ static int merge_subprocess(Hashmap *images, const char *workspace) { return r; /* Acquire host OS release info, so that we can compare it with the extension's data */ + char **host_os_release_level = (arg_image_class == IMAGE_CONFEXT) ? &host_os_release_confext_level : &host_os_release_sysext_level; r = parse_os_release( arg_root, "ID", &host_os_release_id, "VERSION_ID", &host_os_release_version_id, - "SYSEXT_LEVEL", &host_os_release_sysext_level); + image_class_info[arg_image_class].level_env, + host_os_release_level); if (r < 0) return log_error_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(arg_root)); if (isempty(host_os_release_id)) @@ -453,7 +488,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) { HASHMAP_FOREACH(img, images) { _cleanup_free_ char *p = NULL; - p = path_join(workspace, "extensions", img->name); + p = path_join(workspace, image_class_info[arg_image_class].short_identifier_plural, img->name); if (!p) return log_oom(); @@ -575,7 +610,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) { host_os_release_sysext_level, in_initrd() ? "initrd" : "system", img->extension_release, - IMAGE_SYSEXT); + arg_image_class); if (r < 0) return r; if (r == 0) { @@ -620,7 +655,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) { assert_se(img = hashmap_get(images, extensions[n_extensions - 1 - k])); - p = path_join(workspace, "extensions", img->name); + p = path_join(workspace, image_class_info[arg_image_class].short_identifier_plural, img->name); if (!p) return log_oom(); @@ -696,7 +731,7 @@ static int merge(Hashmap *images) { pid_t pid; int r; - r = safe_fork("(sd-sysext)", FORK_DEATHSIG|FORK_LOG|FORK_NEW_MOUNTNS, &pid); + r = safe_fork("(sd-merge)", FORK_DEATHSIG|FORK_LOG|FORK_NEW_MOUNTNS, &pid); if (r < 0) return log_error_errno(r, "Failed to fork off child: %m"); if (r == 0) { @@ -712,7 +747,7 @@ static int merge(Hashmap *images) { _exit(r > 0 ? EXIT_SUCCESS : 123); /* 123 means: didn't find any extensions */ } - r = wait_for_terminate_and_check("(sd-sysext)", pid, WAIT_LOG_ABNORMAL); + r = wait_for_terminate_and_check("(sd-merge)", pid, WAIT_LOG_ABNORMAL); if (r < 0) return r; @@ -730,9 +765,9 @@ static int image_discover_and_read_metadata(Hashmap **ret_images) { if (!images) return log_oom(); - r = image_discover(IMAGE_SYSEXT, arg_root, images); + r = image_discover(arg_image_class, arg_root, images); if (r < 0) - return log_error_errno(r, "Failed to discover extension images: %m"); + return log_error_errno(r, "Failed to discover images: %m"); HASHMAP_FOREACH(img, images) { r = image_read_metadata(img); @@ -833,9 +868,9 @@ static int verb_list(int argc, char **argv, void *userdata) { if (!images) return log_oom(); - r = image_discover(IMAGE_SYSEXT, arg_root, images); + r = image_discover(arg_image_class, arg_root, images); if (r < 0) - return log_error_errno(r, "Failed to discover extension images: %m"); + return log_error_errno(r, "Failed to discover images: %m"); if ((arg_json_format_flags & JSON_FORMAT_OFF) && hashmap_isempty(images)) { log_info("No OS extensions found."); @@ -871,11 +906,11 @@ static int verb_help(int argc, char **argv, void *userdata) { return log_oom(); printf("%1$s [OPTIONS...] COMMAND\n" - "\n%5$sMerge extension images into /usr/ and /opt/ hierarchies.%6$s\n" - "\n%3$sCommands:%4$s\n" + "\n%5$sMerge extension images into /usr/ and /opt/ hierarchies for\n" + " sysext and into the /etc/ hierarchy for confext.%6$s\n" " status Show current merge status (default)\n" - " merge Merge extensions into /usr/ and /opt/\n" - " unmerge Unmerge extensions from /usr/ and /opt/\n" + " merge Merge extensions into relevant hierarchies\n" + " unmerge Unmerge extensions from relevant hierarchies\n" " refresh Unmerge/merge extensions again\n" " list List installed extensions\n" " -h --help Show this help\n" @@ -987,6 +1022,7 @@ static int sysext_main(int argc, char *argv[]) { static int run(int argc, char *argv[]) { int r; + arg_image_class = invoked_as(argv, "systemd-confext") ? IMAGE_CONFEXT : IMAGE_SYSEXT; log_setup(); @@ -997,9 +1033,9 @@ static int run(int argc, char *argv[]) { /* For debugging purposes it might make sense to do this for other hierarchies than /usr/ and * /opt/, but let's make that a hacker/debugging feature, i.e. env var instead of cmdline * switch. */ - r = parse_env_extension_hierarchies(&arg_hierarchies, "SYSTEMD_SYSEXT_HIERARCHIES"); + r = parse_env_extension_hierarchies(&arg_hierarchies, image_class_info[arg_image_class].name_env); if (r < 0) - return log_error_errno(r, "Failed to parse $SYSTEMD_SYSEXT_HIERARCHIES environment variable: %m"); + return log_error_errno(r, "Failed to parse environment variable: %m"); return sysext_main(argc, argv); } |