summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormaanyagoenka <maanyagoenka@microsoft.com>2023-03-29 22:35:18 +0200
committermaanyagoenka <maanyagoenka@microsoft.com>2023-04-05 23:50:04 +0200
commit4da1df42ac419180f8bbfd6bc53a50e02b14af85 (patch)
tree08c230fee28af7765adc2b6af9d2b47c80ad35cf
parentextension-release: establish compatibility between host file and extension-re... (diff)
downloadsystemd-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.build4
-rw-r--r--src/sysext/sysext.c92
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);
}