mirror of
https://github.com/systemd/systemd.git
synced 2024-11-30 13:53:39 +08:00
sysext: make some calls available via varlink
This commit is contained in:
parent
c7fda70716
commit
f5151fb459
@ -1353,6 +1353,24 @@ bool image_in_search_path(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int image_to_json(const struct Image *img, JsonVariant **ret) {
|
||||||
|
assert(img);
|
||||||
|
|
||||||
|
return json_build(ret,
|
||||||
|
JSON_BUILD_OBJECT(
|
||||||
|
JSON_BUILD_PAIR_STRING("Type", image_type_to_string(img->type)),
|
||||||
|
JSON_BUILD_PAIR_STRING("Class", image_class_to_string(img->class)),
|
||||||
|
JSON_BUILD_PAIR_STRING("Name", img->name),
|
||||||
|
JSON_BUILD_PAIR_CONDITION(img->path, "Path", JSON_BUILD_STRING(img->path)),
|
||||||
|
JSON_BUILD_PAIR_BOOLEAN("ReadOnly", img->read_only),
|
||||||
|
JSON_BUILD_PAIR_CONDITION(img->crtime != 0, "CreationTimestamp", JSON_BUILD_UNSIGNED(img->crtime)),
|
||||||
|
JSON_BUILD_PAIR_CONDITION(img->mtime != 0, "ModificationTimestamp", JSON_BUILD_UNSIGNED(img->mtime)),
|
||||||
|
JSON_BUILD_PAIR_CONDITION(img->usage != UINT64_MAX, "Usage", JSON_BUILD_UNSIGNED(img->usage)),
|
||||||
|
JSON_BUILD_PAIR_CONDITION(img->usage_exclusive != UINT64_MAX, "UsageExclusive", JSON_BUILD_UNSIGNED(img->usage_exclusive)),
|
||||||
|
JSON_BUILD_PAIR_CONDITION(img->limit != UINT64_MAX, "Limit", JSON_BUILD_UNSIGNED(img->limit)),
|
||||||
|
JSON_BUILD_PAIR_CONDITION(img->limit_exclusive != UINT64_MAX, "LimitExclusive", JSON_BUILD_UNSIGNED(img->limit_exclusive))));
|
||||||
|
}
|
||||||
|
|
||||||
static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
|
static const char* const image_type_table[_IMAGE_TYPE_MAX] = {
|
||||||
[IMAGE_DIRECTORY] = "directory",
|
[IMAGE_DIRECTORY] = "directory",
|
||||||
[IMAGE_SUBVOLUME] = "subvolume",
|
[IMAGE_SUBVOLUME] = "subvolume",
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "image-policy.h"
|
#include "image-policy.h"
|
||||||
|
#include "json.h"
|
||||||
#include "lock-util.h"
|
#include "lock-util.h"
|
||||||
#include "macro.h"
|
#include "macro.h"
|
||||||
#include "os-util.h"
|
#include "os-util.h"
|
||||||
@ -116,4 +117,6 @@ static inline bool IMAGE_IS_HOST(const struct Image *i) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int image_to_json(const struct Image *i, JsonVariant **ret);
|
||||||
|
|
||||||
extern const struct hash_ops image_hash_ops;
|
extern const struct hash_ops image_hash_ops;
|
||||||
|
@ -175,6 +175,7 @@ shared_sources = files(
|
|||||||
'varlink-io.systemd.Resolve.c',
|
'varlink-io.systemd.Resolve.c',
|
||||||
'varlink-io.systemd.UserDatabase.c',
|
'varlink-io.systemd.UserDatabase.c',
|
||||||
'varlink-io.systemd.oom.c',
|
'varlink-io.systemd.oom.c',
|
||||||
|
'varlink-io.systemd.sysext.c',
|
||||||
'varlink-org.varlink.service.c',
|
'varlink-org.varlink.service.c',
|
||||||
'verb-log-control.c',
|
'verb-log-control.c',
|
||||||
'verbs.c',
|
'verbs.c',
|
||||||
|
67
src/shared/varlink-io.systemd.sysext.c
Normal file
67
src/shared/varlink-io.systemd.sysext.c
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
|
||||||
|
#include "varlink-io.systemd.sysext.h"
|
||||||
|
|
||||||
|
static VARLINK_DEFINE_ENUM_TYPE(
|
||||||
|
ImageClass,
|
||||||
|
VARLINK_DEFINE_ENUM_VALUE(sysext),
|
||||||
|
VARLINK_DEFINE_ENUM_VALUE(confext));
|
||||||
|
|
||||||
|
static VARLINK_DEFINE_ENUM_TYPE(
|
||||||
|
ImageType,
|
||||||
|
VARLINK_DEFINE_ENUM_VALUE(directory),
|
||||||
|
VARLINK_DEFINE_ENUM_VALUE(subvolume),
|
||||||
|
VARLINK_DEFINE_ENUM_VALUE(raw),
|
||||||
|
VARLINK_DEFINE_ENUM_VALUE(block));
|
||||||
|
|
||||||
|
static VARLINK_DEFINE_METHOD(
|
||||||
|
Merge,
|
||||||
|
VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_INPUT(force, VARLINK_BOOL, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_INPUT(noReload, VARLINK_BOOL, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_INPUT(noexec, VARLINK_BOOL, VARLINK_NULLABLE));
|
||||||
|
|
||||||
|
static VARLINK_DEFINE_METHOD(
|
||||||
|
Unmerge,
|
||||||
|
VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_INPUT(noReload, VARLINK_BOOL, VARLINK_NULLABLE));
|
||||||
|
|
||||||
|
static VARLINK_DEFINE_METHOD(
|
||||||
|
Refresh,
|
||||||
|
VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_INPUT(force, VARLINK_BOOL, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_INPUT(noReload, VARLINK_BOOL, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_INPUT(noexec, VARLINK_BOOL, VARLINK_NULLABLE));
|
||||||
|
|
||||||
|
static VARLINK_DEFINE_METHOD(
|
||||||
|
List,
|
||||||
|
VARLINK_DEFINE_INPUT_BY_TYPE(class, ImageClass, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_OUTPUT_BY_TYPE(Class, ImageClass, 0),
|
||||||
|
VARLINK_DEFINE_OUTPUT_BY_TYPE(Type, ImageType, 0),
|
||||||
|
VARLINK_DEFINE_OUTPUT(Name, VARLINK_STRING, 0),
|
||||||
|
VARLINK_DEFINE_OUTPUT(Path, VARLINK_STRING, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_OUTPUT(ReadOnly, VARLINK_BOOL, 0),
|
||||||
|
VARLINK_DEFINE_OUTPUT(CreationTimestamp, VARLINK_INT, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_OUTPUT(ModificationTimestamp, VARLINK_INT, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_OUTPUT(Usage, VARLINK_INT, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_OUTPUT(UsageExclusive, VARLINK_INT, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_OUTPUT(Limit, VARLINK_INT, VARLINK_NULLABLE),
|
||||||
|
VARLINK_DEFINE_OUTPUT(LimitExclusive, VARLINK_INT, VARLINK_NULLABLE));
|
||||||
|
|
||||||
|
static VARLINK_DEFINE_ERROR(NoImagesFound);
|
||||||
|
|
||||||
|
static VARLINK_DEFINE_ERROR(
|
||||||
|
AlreadyMerged,
|
||||||
|
VARLINK_DEFINE_FIELD(hierarchy, VARLINK_STRING, 0));
|
||||||
|
|
||||||
|
VARLINK_DEFINE_INTERFACE(
|
||||||
|
io_systemd_sysext,
|
||||||
|
"io.systemd.sysext",
|
||||||
|
&vl_type_ImageClass,
|
||||||
|
&vl_type_ImageType,
|
||||||
|
&vl_method_Merge,
|
||||||
|
&vl_method_Unmerge,
|
||||||
|
&vl_method_Refresh,
|
||||||
|
&vl_method_List,
|
||||||
|
&vl_error_NoImagesFound,
|
||||||
|
&vl_error_AlreadyMerged);
|
6
src/shared/varlink-io.systemd.sysext.h
Normal file
6
src/shared/varlink-io.systemd.sysext.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "varlink-idl.h"
|
||||||
|
|
||||||
|
extern const VarlinkInterface vl_interface_io_systemd_sysext;
|
@ -44,6 +44,8 @@
|
|||||||
#include "sort-util.h"
|
#include "sort-util.h"
|
||||||
#include "terminal-util.h"
|
#include "terminal-util.h"
|
||||||
#include "user-util.h"
|
#include "user-util.h"
|
||||||
|
#include "varlink.h"
|
||||||
|
#include "varlink-io.systemd.sysext.h"
|
||||||
#include "verbs.h"
|
#include "verbs.h"
|
||||||
|
|
||||||
static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default for sysext and /etc by default for confext */
|
static char **arg_hierarchies = NULL; /* "/usr" + "/opt" by default for sysext and /etc by default for confext */
|
||||||
@ -55,6 +57,7 @@ static bool arg_force = false;
|
|||||||
static bool arg_no_reload = false;
|
static bool arg_no_reload = false;
|
||||||
static int arg_noexec = -1;
|
static int arg_noexec = -1;
|
||||||
static ImagePolicy *arg_image_policy = NULL;
|
static ImagePolicy *arg_image_policy = NULL;
|
||||||
|
static bool arg_varlink = false;
|
||||||
|
|
||||||
/* Is set to IMAGE_CONFEXT when systemd is called with the confext functionality instead of the default */
|
/* 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 ImageClass arg_image_class = IMAGE_SYSEXT;
|
||||||
@ -99,12 +102,17 @@ static const struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static int is_our_mount_point(const char *p) {
|
static int is_our_mount_point(
|
||||||
|
ImageClass image_class,
|
||||||
|
const char *p) {
|
||||||
|
|
||||||
_cleanup_free_ char *buf = NULL, *f = NULL;
|
_cleanup_free_ char *buf = NULL, *f = NULL;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
dev_t dev;
|
dev_t dev;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
|
||||||
r = path_is_mount_point(p, NULL, 0);
|
r = path_is_mount_point(p, NULL, 0);
|
||||||
if (r == -ENOENT) {
|
if (r == -ENOENT) {
|
||||||
log_debug_errno(r, "Hierarchy '%s' doesn't exist.", p);
|
log_debug_errno(r, "Hierarchy '%s' doesn't exist.", p);
|
||||||
@ -125,21 +133,21 @@ static int is_our_mount_point(const char *p) {
|
|||||||
* confused if people tar up one of our merged trees and untar them elsewhere where we might mistake
|
* confused if people tar up one of our merged trees and untar them elsewhere where we might mistake
|
||||||
* them for a live sysext tree. */
|
* them for a live sysext tree. */
|
||||||
|
|
||||||
f = path_join(p, image_class_info[arg_image_class].dot_directory_name, "dev");
|
f = path_join(p, image_class_info[image_class].dot_directory_name, "dev");
|
||||||
if (!f)
|
if (!f)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
r = read_one_line_file(f, &buf);
|
r = read_one_line_file(f, &buf);
|
||||||
if (r == -ENOENT) {
|
if (r == -ENOENT) {
|
||||||
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);
|
log_debug("Hierarchy '%s' does not carry a %s/dev file, not a merged tree.", p, image_class_info[image_class].dot_directory_name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
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);
|
return log_error_errno(r, "Failed to determine whether hierarchy '%s' contains '%s/dev': %m", p, image_class_info[image_class].dot_directory_name);
|
||||||
|
|
||||||
r = parse_devnum(buf, &dev);
|
r = parse_devnum(buf, &dev);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
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);
|
return log_error_errno(r, "Failed to parse device major/minor stored in '%s/dev' file on '%s': %m", image_class_info[image_class].dot_directory_name, p);
|
||||||
|
|
||||||
if (lstat(p, &st) < 0)
|
if (lstat(p, &st) < 0)
|
||||||
return log_error_errno(r, "Failed to stat %s: %m", p);
|
return log_error_errno(r, "Failed to stat %s: %m", p);
|
||||||
@ -152,15 +160,18 @@ static int is_our_mount_point(const char *p) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int need_reload(void) {
|
static int need_reload(
|
||||||
/* Parse the mounted images to find out if we need
|
ImageClass image_class,
|
||||||
to reload the daemon. */
|
char **hierarchies,
|
||||||
|
bool no_reload) {
|
||||||
|
|
||||||
|
/* Parse the mounted images to find out if we need to reload the daemon. */
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
if (arg_no_reload)
|
if (no_reload)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
STRV_FOREACH(p, arg_hierarchies) {
|
STRV_FOREACH(p, hierarchies) {
|
||||||
_cleanup_free_ char *f = NULL, *buf = NULL, *resolved = NULL;
|
_cleanup_free_ char *f = NULL, *buf = NULL, *resolved = NULL;
|
||||||
_cleanup_strv_free_ char **mounted_extensions = NULL;
|
_cleanup_strv_free_ char **mounted_extensions = NULL;
|
||||||
|
|
||||||
@ -174,13 +185,13 @@ static int need_reload(void) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = is_our_mount_point(resolved);
|
r = is_our_mount_point(image_class, resolved);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (!r)
|
if (!r)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
f = path_join(resolved, image_class_info[arg_image_class].dot_directory_name, image_class_info[arg_image_class].short_identifier_plural);
|
f = path_join(resolved, image_class_info[image_class].dot_directory_name, image_class_info[image_class].short_identifier_plural);
|
||||||
if (!f)
|
if (!f)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
@ -197,7 +208,7 @@ static int need_reload(void) {
|
|||||||
const char *extension_reload_manager = NULL;
|
const char *extension_reload_manager = NULL;
|
||||||
int b;
|
int b;
|
||||||
|
|
||||||
r = load_extension_release_pairs(arg_root, arg_image_class, *extension, /* relax_extension_release_check */ true, &extension_release);
|
r = load_extension_release_pairs(arg_root, image_class, *extension, /* relax_extension_release_check */ true, &extension_release);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
log_debug_errno(r, "Failed to parse extension-release metadata of %s, ignoring: %m", *extension);
|
log_debug_errno(r, "Failed to parse extension-release metadata of %s, ignoring: %m", *extension);
|
||||||
continue;
|
continue;
|
||||||
@ -233,14 +244,19 @@ static int daemon_reload(void) {
|
|||||||
return bus_service_manager_reload(bus);
|
return bus_service_manager_reload(bus);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unmerge_hierarchy(const char *p) {
|
static int unmerge_hierarchy(
|
||||||
|
ImageClass image_class,
|
||||||
|
const char *p) {
|
||||||
|
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* We only unmount /usr/ if it is a mount point and really one of ours, in order not to break
|
/* We only unmount /usr/ if it is a mount point and really one of ours, in order not to break
|
||||||
* systems where /usr/ is a mount point of its own already. */
|
* systems where /usr/ is a mount point of its own already. */
|
||||||
|
|
||||||
r = is_our_mount_point(p);
|
r = is_our_mount_point(image_class, p);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0)
|
if (r == 0)
|
||||||
@ -256,16 +272,20 @@ static int unmerge_hierarchy(const char *p) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int unmerge(void) {
|
static int unmerge(
|
||||||
|
ImageClass image_class,
|
||||||
|
char **hierarchies,
|
||||||
|
bool no_reload) {
|
||||||
|
|
||||||
int r, ret = 0;
|
int r, ret = 0;
|
||||||
bool need_to_reload;
|
bool need_to_reload;
|
||||||
|
|
||||||
r = need_reload();
|
r = need_reload(image_class, hierarchies, no_reload);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
need_to_reload = r > 0;
|
need_to_reload = r > 0;
|
||||||
|
|
||||||
STRV_FOREACH(p, arg_hierarchies) {
|
STRV_FOREACH(p, hierarchies) {
|
||||||
_cleanup_free_ char *resolved = NULL;
|
_cleanup_free_ char *resolved = NULL;
|
||||||
|
|
||||||
r = chase(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
|
r = chase(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
|
||||||
@ -281,7 +301,7 @@ static int unmerge(void) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = unmerge_hierarchy(resolved);
|
r = unmerge_hierarchy(image_class, resolved);
|
||||||
if (r < 0 && ret == 0)
|
if (r < 0 && ret == 0)
|
||||||
ret = r;
|
ret = r;
|
||||||
}
|
}
|
||||||
@ -304,7 +324,74 @@ static int verb_unmerge(int argc, char **argv, void *userdata) {
|
|||||||
if (r == 0)
|
if (r == 0)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
|
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
|
||||||
|
|
||||||
return unmerge();
|
return unmerge(arg_image_class,
|
||||||
|
arg_hierarchies,
|
||||||
|
arg_no_reload);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_image_class_parameter(Varlink *link, const char *value, ImageClass *image_class, char ***hierarchies) {
|
||||||
|
_cleanup_strv_free_ char **h = NULL;
|
||||||
|
ImageClass c;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
assert(image_class);
|
||||||
|
|
||||||
|
if (!value)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
c = image_class_from_string(value);
|
||||||
|
if (!IN_SET(c, IMAGE_SYSEXT, IMAGE_CONFEXT))
|
||||||
|
return varlink_errorb(link, VARLINK_ERROR_INVALID_PARAMETER, JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("parameter", "class")));
|
||||||
|
|
||||||
|
if (hierarchies) {
|
||||||
|
r = parse_env_extension_hierarchies(&h, image_class_info[c].name_env);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to parse environment variable: %m");
|
||||||
|
|
||||||
|
strv_free_and_replace(*hierarchies, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
*image_class = c;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct MethodUnmergeParameters {
|
||||||
|
const char *class;
|
||||||
|
int no_reload;
|
||||||
|
} MethodUnmergeParameters;
|
||||||
|
|
||||||
|
static int vl_method_unmerge(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
||||||
|
|
||||||
|
static const JsonDispatch dispatch_table[] = {
|
||||||
|
{ "class", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodUnmergeParameters, class), 0 },
|
||||||
|
{ "noReload", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(MethodUnmergeParameters, no_reload), 0 },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MethodUnmergeParameters p = {
|
||||||
|
.no_reload = -1,
|
||||||
|
};
|
||||||
|
_cleanup_strv_free_ char **hierarchies = NULL;
|
||||||
|
ImageClass image_class = arg_image_class;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
|
||||||
|
r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = parse_image_class_parameter(link, p.class, &image_class, &hierarchies);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = unmerge(image_class,
|
||||||
|
hierarchies ?: arg_hierarchies,
|
||||||
|
p.no_reload >= 0 ? p.no_reload : arg_no_reload);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return varlink_reply(link, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int verb_status(int argc, char **argv, void *userdata) {
|
static int verb_status(int argc, char **argv, void *userdata) {
|
||||||
@ -332,7 +419,7 @@ static int verb_status(int argc, char **argv, void *userdata) {
|
|||||||
goto inner_fail;
|
goto inner_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = is_our_mount_point(resolved);
|
r = is_our_mount_point(arg_image_class, resolved);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
goto inner_fail;
|
goto inner_fail;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
@ -388,6 +475,8 @@ static int verb_status(int argc, char **argv, void *userdata) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int mount_overlayfs(
|
static int mount_overlayfs(
|
||||||
|
ImageClass image_class,
|
||||||
|
int noexec,
|
||||||
const char *where,
|
const char *where,
|
||||||
char **layers) {
|
char **layers) {
|
||||||
|
|
||||||
@ -415,12 +504,12 @@ static int mount_overlayfs(
|
|||||||
separator = true;
|
separator = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
flags = image_class_info[arg_image_class].default_mount_flags;
|
flags = image_class_info[image_class].default_mount_flags;
|
||||||
if (arg_noexec >= 0)
|
if (noexec >= 0)
|
||||||
SET_FLAG(flags, MS_NOEXEC, arg_noexec);
|
SET_FLAG(flags, MS_NOEXEC, noexec);
|
||||||
|
|
||||||
/* Now mount the actual overlayfs */
|
/* Now mount the actual overlayfs */
|
||||||
r = mount_nofollow_verbose(LOG_ERR, image_class_info[arg_image_class].short_identifier, where, "overlay", flags, options);
|
r = mount_nofollow_verbose(LOG_ERR, image_class_info[image_class].short_identifier, where, "overlay", flags, options);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -428,7 +517,9 @@ static int mount_overlayfs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int merge_hierarchy(
|
static int merge_hierarchy(
|
||||||
|
ImageClass image_class,
|
||||||
const char *hierarchy,
|
const char *hierarchy,
|
||||||
|
int noexec,
|
||||||
char **extensions,
|
char **extensions,
|
||||||
char **paths,
|
char **paths,
|
||||||
const char *meta_path,
|
const char *meta_path,
|
||||||
@ -463,7 +554,7 @@ static int merge_hierarchy(
|
|||||||
/* Let's generate a metadata file that lists all extensions we took into account for this
|
/* 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
|
* hierarchy. We include this in the final fs, to make things nicely discoverable and
|
||||||
* recognizable. */
|
* recognizable. */
|
||||||
f = path_join(meta_path, image_class_info[arg_image_class].dot_directory_name, image_class_info[arg_image_class].short_identifier_plural);
|
f = path_join(meta_path, image_class_info[image_class].dot_directory_name, image_class_info[image_class].short_identifier_plural);
|
||||||
if (!f)
|
if (!f)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
@ -519,7 +610,7 @@ static int merge_hierarchy(
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to make directory '%s': %m", overlay_path);
|
return log_error_errno(r, "Failed to make directory '%s': %m", overlay_path);
|
||||||
|
|
||||||
r = mount_overlayfs(overlay_path, layers);
|
r = mount_overlayfs(image_class, noexec, overlay_path, layers);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -536,7 +627,7 @@ static int merge_hierarchy(
|
|||||||
return log_error_errno(r, "Failed to stat mount '%s': %m", overlay_path);
|
return log_error_errno(r, "Failed to stat mount '%s': %m", overlay_path);
|
||||||
|
|
||||||
free(f);
|
free(f);
|
||||||
f = path_join(meta_path, image_class_info[arg_image_class].dot_directory_name, "dev");
|
f = path_join(meta_path, image_class_info[image_class].dot_directory_name, "dev");
|
||||||
if (!f)
|
if (!f)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
@ -574,7 +665,14 @@ static const ImagePolicy *pick_image_policy(const Image *img) {
|
|||||||
return image_class_info[img->class].default_image_policy;
|
return image_class_info[img->class].default_image_policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int merge_subprocess(Hashmap *images, const char *workspace) {
|
static int merge_subprocess(
|
||||||
|
ImageClass image_class,
|
||||||
|
char **hierarchies,
|
||||||
|
bool force,
|
||||||
|
int noexec,
|
||||||
|
Hashmap *images,
|
||||||
|
const char *workspace) {
|
||||||
|
|
||||||
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL, *host_os_release_api_level = NULL, *buf = NULL;
|
_cleanup_free_ char *host_os_release_id = NULL, *host_os_release_version_id = NULL, *host_os_release_api_level = NULL, *buf = NULL;
|
||||||
_cleanup_strv_free_ char **extensions = NULL, **paths = NULL;
|
_cleanup_strv_free_ char **extensions = NULL, **paths = NULL;
|
||||||
size_t n_extensions = 0;
|
size_t n_extensions = 0;
|
||||||
@ -597,7 +695,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
* but let the kernel do that entirely automatically, once our namespace dies. Note that this file
|
* but let the kernel do that entirely automatically, once our namespace dies. Note that this file
|
||||||
* system won't be visible to anyone but us, since we opened our own namespace and then made the
|
* system won't be visible to anyone but us, since we opened our own namespace and then made the
|
||||||
* /run/ hierarchy (which our workspace is contained in) MS_SLAVE, see above. */
|
* /run/ hierarchy (which our workspace is contained in) MS_SLAVE, see above. */
|
||||||
r = mount_nofollow_verbose(LOG_ERR, image_class_info[arg_image_class].short_identifier, workspace, "tmpfs", 0, "mode=0700");
|
r = mount_nofollow_verbose(LOG_ERR, image_class_info[image_class].short_identifier, workspace, "tmpfs", 0, "mode=0700");
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
@ -606,7 +704,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
arg_root,
|
arg_root,
|
||||||
"ID", &host_os_release_id,
|
"ID", &host_os_release_id,
|
||||||
"VERSION_ID", &host_os_release_version_id,
|
"VERSION_ID", &host_os_release_version_id,
|
||||||
image_class_info[arg_image_class].level_env, &host_os_release_api_level);
|
image_class_info[image_class].level_env, &host_os_release_api_level);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to acquire 'os-release' data of OS tree '%s': %m", empty_to_root(arg_root));
|
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))
|
if (isempty(host_os_release_id))
|
||||||
@ -618,7 +716,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
HASHMAP_FOREACH(img, images) {
|
HASHMAP_FOREACH(img, images) {
|
||||||
_cleanup_free_ char *p = NULL;
|
_cleanup_free_ char *p = NULL;
|
||||||
|
|
||||||
p = path_join(workspace, image_class_info[arg_image_class].short_identifier_plural, img->name);
|
p = path_join(workspace, image_class_info[image_class].short_identifier_plural, img->name);
|
||||||
if (!p)
|
if (!p)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
@ -630,7 +728,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
case IMAGE_DIRECTORY:
|
case IMAGE_DIRECTORY:
|
||||||
case IMAGE_SUBVOLUME:
|
case IMAGE_SUBVOLUME:
|
||||||
|
|
||||||
if (!arg_force) {
|
if (!force) {
|
||||||
r = extension_has_forbidden_content(p);
|
r = extension_has_forbidden_content(p);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
@ -672,7 +770,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
if (verity_settings.data_path)
|
if (verity_settings.data_path)
|
||||||
flags |= DISSECT_IMAGE_NO_PARTITION_TABLE;
|
flags |= DISSECT_IMAGE_NO_PARTITION_TABLE;
|
||||||
|
|
||||||
if (!arg_force)
|
if (!force)
|
||||||
flags |= DISSECT_IMAGE_VALIDATE_OS_EXT;
|
flags |= DISSECT_IMAGE_VALIDATE_OS_EXT;
|
||||||
|
|
||||||
r = loop_device_make_by_path(
|
r = loop_device_make_by_path(
|
||||||
@ -718,7 +816,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
flags);
|
flags);
|
||||||
if (r < 0 && r != -ENOMEDIUM)
|
if (r < 0 && r != -ENOMEDIUM)
|
||||||
return r;
|
return r;
|
||||||
if (r == -ENOMEDIUM && !arg_force) {
|
if (r == -ENOMEDIUM && !force) {
|
||||||
n_ignored++;
|
n_ignored++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -732,7 +830,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg_force)
|
if (force)
|
||||||
log_debug("Force mode enabled, skipping version validation.");
|
log_debug("Force mode enabled, skipping version validation.");
|
||||||
else {
|
else {
|
||||||
r = extension_release_validate(
|
r = extension_release_validate(
|
||||||
@ -741,8 +839,8 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
host_os_release_version_id,
|
host_os_release_version_id,
|
||||||
host_os_release_api_level,
|
host_os_release_api_level,
|
||||||
in_initrd() ? "initrd" : "system",
|
in_initrd() ? "initrd" : "system",
|
||||||
image_extension_release(img, arg_image_class),
|
image_extension_release(img, image_class),
|
||||||
arg_image_class);
|
image_class);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
@ -787,7 +885,7 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
|
|
||||||
assert_se(img = hashmap_get(images, extensions[n_extensions - 1 - k]));
|
assert_se(img = hashmap_get(images, extensions[n_extensions - 1 - k]));
|
||||||
|
|
||||||
p = path_join(workspace, image_class_info[arg_image_class].short_identifier_plural, img->name);
|
p = path_join(workspace, image_class_info[image_class].short_identifier_plural, img->name);
|
||||||
if (!p)
|
if (!p)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
@ -796,20 +894,20 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
|
|
||||||
/* Let's now unmerge the status quo ante, since to build the new overlayfs we need a reference to the
|
/* Let's now unmerge the status quo ante, since to build the new overlayfs we need a reference to the
|
||||||
* underlying fs. */
|
* underlying fs. */
|
||||||
STRV_FOREACH(h, arg_hierarchies) {
|
STRV_FOREACH(h, hierarchies) {
|
||||||
_cleanup_free_ char *resolved = NULL;
|
_cleanup_free_ char *resolved = NULL;
|
||||||
|
|
||||||
r = chase(*h, arg_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
|
r = chase(*h, arg_root, CHASE_PREFIX_ROOT|CHASE_NONEXISTENT, &resolved, NULL);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(arg_root), *h);
|
return log_error_errno(r, "Failed to resolve hierarchy '%s%s': %m", strempty(arg_root), *h);
|
||||||
|
|
||||||
r = unmerge_hierarchy(resolved);
|
r = unmerge_hierarchy(image_class, resolved);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Create overlayfs mounts for all hierarchies */
|
/* Create overlayfs mounts for all hierarchies */
|
||||||
STRV_FOREACH(h, arg_hierarchies) {
|
STRV_FOREACH(h, hierarchies) {
|
||||||
_cleanup_free_ char *meta_path = NULL, *overlay_path = NULL;
|
_cleanup_free_ char *meta_path = NULL, *overlay_path = NULL;
|
||||||
|
|
||||||
meta_path = path_join(workspace, "meta", *h); /* The place where to store metadata about this instance */
|
meta_path = path_join(workspace, "meta", *h); /* The place where to store metadata about this instance */
|
||||||
@ -820,13 +918,20 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
if (!overlay_path)
|
if (!overlay_path)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
r = merge_hierarchy(*h, extensions, paths, meta_path, overlay_path);
|
r = merge_hierarchy(
|
||||||
|
image_class,
|
||||||
|
*h,
|
||||||
|
noexec,
|
||||||
|
extensions,
|
||||||
|
paths,
|
||||||
|
meta_path,
|
||||||
|
overlay_path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* And move them all into place. This is where things appear in the host namespace */
|
/* And move them all into place. This is where things appear in the host namespace */
|
||||||
STRV_FOREACH(h, arg_hierarchies) {
|
STRV_FOREACH(h, hierarchies) {
|
||||||
_cleanup_free_ char *p = NULL, *resolved = NULL;
|
_cleanup_free_ char *p = NULL, *resolved = NULL;
|
||||||
|
|
||||||
p = path_join(workspace, "overlay", *h);
|
p = path_join(workspace, "overlay", *h);
|
||||||
@ -859,7 +964,12 @@ static int merge_subprocess(Hashmap *images, const char *workspace) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int merge(Hashmap *images) {
|
static int merge(ImageClass image_class,
|
||||||
|
char **hierarchies,
|
||||||
|
bool force,
|
||||||
|
bool no_reload,
|
||||||
|
int noexec,
|
||||||
|
Hashmap *images) {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
@ -869,7 +979,7 @@ static int merge(Hashmap *images) {
|
|||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
/* Child with its own mount namespace */
|
/* Child with its own mount namespace */
|
||||||
|
|
||||||
r = merge_subprocess(images, "/run/systemd/sysext");
|
r = merge_subprocess(image_class, hierarchies, force, noexec, images, "/run/systemd/sysext");
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
|
|
||||||
@ -886,7 +996,7 @@ static int merge(Hashmap *images) {
|
|||||||
if (r == 123) /* exit code 123 means: didn't do anything */
|
if (r == 123) /* exit code 123 means: didn't do anything */
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
r = need_reload();
|
r = need_reload(image_class, hierarchies, no_reload);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r > 0) {
|
if (r > 0) {
|
||||||
@ -898,7 +1008,9 @@ static int merge(Hashmap *images) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int image_discover_and_read_metadata(Hashmap **ret_images) {
|
static int image_discover_and_read_metadata(
|
||||||
|
ImageClass image_class,
|
||||||
|
Hashmap **ret_images) {
|
||||||
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||||
Image *img;
|
Image *img;
|
||||||
int r;
|
int r;
|
||||||
@ -909,12 +1021,12 @@ static int image_discover_and_read_metadata(Hashmap **ret_images) {
|
|||||||
if (!images)
|
if (!images)
|
||||||
return log_oom();
|
return log_oom();
|
||||||
|
|
||||||
r = image_discover(arg_image_class, arg_root, images);
|
r = image_discover(image_class, arg_root, images);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to discover images: %m");
|
return log_error_errno(r, "Failed to discover images: %m");
|
||||||
|
|
||||||
HASHMAP_FOREACH(img, images) {
|
HASHMAP_FOREACH(img, images) {
|
||||||
r = image_read_metadata(img, image_class_info[arg_image_class].default_image_policy);
|
r = image_read_metadata(img, image_class_info[image_class].default_image_policy);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name);
|
return log_error_errno(r, "Failed to read metadata for image %s: %m", img->name);
|
||||||
}
|
}
|
||||||
@ -924,23 +1036,17 @@ static int image_discover_and_read_metadata(Hashmap **ret_images) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int verb_merge(int argc, char **argv, void *userdata) {
|
static int look_for_merged_hierarchies(
|
||||||
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
ImageClass image_class,
|
||||||
|
char **hierarchies,
|
||||||
|
const char **ret_which) {
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = have_effective_cap(CAP_SYS_ADMIN);
|
assert(ret_which);
|
||||||
if (r < 0)
|
|
||||||
return log_error_errno(r, "Failed to check if we have enough privileges: %m");
|
|
||||||
if (r == 0)
|
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
|
|
||||||
|
|
||||||
r = image_discover_and_read_metadata(&images);
|
|
||||||
if (r < 0)
|
|
||||||
return r;
|
|
||||||
|
|
||||||
/* In merge mode fail if things are already merged. (In --refresh mode below we'll unmerge if we find
|
/* In merge mode fail if things are already merged. (In --refresh mode below we'll unmerge if we find
|
||||||
* things are already merged...) */
|
* things are already merged...) */
|
||||||
STRV_FOREACH(p, arg_hierarchies) {
|
STRV_FOREACH(p, hierarchies) {
|
||||||
_cleanup_free_ char *resolved = NULL;
|
_cleanup_free_ char *resolved = NULL;
|
||||||
|
|
||||||
r = chase(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
|
r = chase(*p, arg_root, CHASE_PREFIX_ROOT, &resolved, NULL);
|
||||||
@ -951,19 +1057,22 @@ static int verb_merge(int argc, char **argv, void *userdata) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *p);
|
return log_error_errno(r, "Failed to resolve path to hierarchy '%s%s': %m", strempty(arg_root), *p);
|
||||||
|
|
||||||
r = is_our_mount_point(resolved);
|
r = is_our_mount_point(image_class, resolved);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r > 0)
|
if (r > 0) {
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EBUSY),
|
*ret_which = *p;
|
||||||
"Hierarchy '%s' is already merged.", *p);
|
return 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return merge(images);
|
*ret_which = NULL;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int verb_refresh(int argc, char **argv, void *userdata) {
|
static int verb_merge(int argc, char **argv, void *userdata) {
|
||||||
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||||
|
const char *which;
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
r = have_effective_cap(CAP_SYS_ADMIN);
|
r = have_effective_cap(CAP_SYS_ADMIN);
|
||||||
@ -972,19 +1081,123 @@ static int verb_refresh(int argc, char **argv, void *userdata) {
|
|||||||
if (r == 0)
|
if (r == 0)
|
||||||
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
|
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
|
||||||
|
|
||||||
r = image_discover_and_read_metadata(&images);
|
r = image_discover_and_read_metadata(arg_image_class, &images);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
r = merge(images); /* Returns > 0 if it did something, i.e. a new overlayfs is mounted now. When it
|
r = look_for_merged_hierarchies(arg_image_class, arg_hierarchies, &which);
|
||||||
* does so it implicitly unmounts any overlayfs placed there before. Returns == 0
|
if (r < 0)
|
||||||
* if it did nothing, i.e. no extension images found. In this case the old
|
return r;
|
||||||
* overlayfs remains in place if there was one. */
|
if (r > 0)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EBUSY), "Hierarchy '%s' is already merged.", which);
|
||||||
|
|
||||||
|
return merge(arg_image_class,
|
||||||
|
arg_hierarchies,
|
||||||
|
arg_force,
|
||||||
|
arg_no_reload,
|
||||||
|
arg_noexec,
|
||||||
|
images);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct MethodMergeParameters {
|
||||||
|
const char *class;
|
||||||
|
int force;
|
||||||
|
int no_reload;
|
||||||
|
int noexec;
|
||||||
|
} MethodMergeParameters;
|
||||||
|
|
||||||
|
static int parse_merge_parameters(Varlink *link, JsonVariant *parameters, MethodMergeParameters *p) {
|
||||||
|
|
||||||
|
static const JsonDispatch dispatch_table[] = {
|
||||||
|
{ "class", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodMergeParameters, class), 0 },
|
||||||
|
{ "force", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(MethodMergeParameters, force), 0 },
|
||||||
|
{ "noReload", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(MethodMergeParameters, no_reload), 0 },
|
||||||
|
{ "noexec", JSON_VARIANT_BOOLEAN, json_dispatch_boolean, offsetof(MethodMergeParameters, noexec), 0 },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
assert(parameters);
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
r = json_dispatch(parameters, dispatch_table, NULL, 0, p);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vl_method_merge(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
||||||
|
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||||
|
MethodMergeParameters p = {
|
||||||
|
.force = -1,
|
||||||
|
.no_reload = -1,
|
||||||
|
.noexec = -1,
|
||||||
|
};
|
||||||
|
_cleanup_strv_free_ char **hierarchies = NULL;
|
||||||
|
ImageClass image_class = arg_image_class;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
|
||||||
|
r = parse_merge_parameters(link, parameters, &p);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = parse_image_class_parameter(link, p.class, &image_class, &hierarchies);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = image_discover_and_read_metadata(image_class, &images);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
const char *which;
|
||||||
|
r = look_for_merged_hierarchies(
|
||||||
|
image_class,
|
||||||
|
hierarchies ?: arg_hierarchies,
|
||||||
|
&which);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r > 0)
|
||||||
|
return varlink_errorb(link, "io.systemd.sysext.AlreadyMerged", JSON_BUILD_OBJECT(JSON_BUILD_PAIR_STRING("hierarchy", which)));
|
||||||
|
|
||||||
|
r = merge(image_class,
|
||||||
|
hierarchies ?: arg_hierarchies,
|
||||||
|
p.force >= 0 ? p.force : arg_force,
|
||||||
|
p.no_reload >= 0 ? p.no_reload : arg_no_reload,
|
||||||
|
p.noexec >= 0 ? p.noexec : arg_noexec,
|
||||||
|
images);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return varlink_reply(link, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int refresh(
|
||||||
|
ImageClass image_class,
|
||||||
|
char **hierarchies,
|
||||||
|
bool force,
|
||||||
|
bool no_reload,
|
||||||
|
int noexec) {
|
||||||
|
|
||||||
|
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = image_discover_and_read_metadata(image_class, &images);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
/* Returns > 0 if it did something, i.e. a new overlayfs is mounted now. When it does so it
|
||||||
|
* implicitly unmounts any overlayfs placed there before. Returns == 0 if it did nothing, i.e. no
|
||||||
|
* extension images found. In this case the old overlayfs remains in place if there was one. */
|
||||||
|
r = merge(image_class, hierarchies, force, no_reload, noexec, images);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
if (r == 0) /* No images found? Then unmerge. The goal of --refresh is after all that after having
|
if (r == 0) /* No images found? Then unmerge. The goal of --refresh is after all that after having
|
||||||
* called there's a guarantee that the merge status matches the installed extensions. */
|
* called there's a guarantee that the merge status matches the installed extensions. */
|
||||||
r = unmerge();
|
r = unmerge(image_class, hierarchies, no_reload);
|
||||||
|
|
||||||
/* Net result here is that:
|
/* Net result here is that:
|
||||||
*
|
*
|
||||||
@ -1002,6 +1215,54 @@ static int verb_refresh(int argc, char **argv, void *userdata) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int verb_refresh(int argc, char **argv, void *userdata) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = have_effective_cap(CAP_SYS_ADMIN);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to check if we have enough privileges: %m");
|
||||||
|
if (r == 0)
|
||||||
|
return log_error_errno(SYNTHETIC_ERRNO(EPERM), "Need to be privileged.");
|
||||||
|
|
||||||
|
return refresh(arg_image_class,
|
||||||
|
arg_hierarchies,
|
||||||
|
arg_force,
|
||||||
|
arg_no_reload,
|
||||||
|
arg_noexec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int vl_method_refresh(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
||||||
|
|
||||||
|
MethodMergeParameters p = {
|
||||||
|
.force = -1,
|
||||||
|
.no_reload = -1,
|
||||||
|
.noexec = -1,
|
||||||
|
};
|
||||||
|
_cleanup_strv_free_ char **hierarchies = NULL;
|
||||||
|
ImageClass image_class = arg_image_class;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
|
||||||
|
r = parse_merge_parameters(link, parameters, &p);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = parse_image_class_parameter(link, p.class, &image_class, &hierarchies);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = refresh(image_class,
|
||||||
|
hierarchies ?: arg_hierarchies,
|
||||||
|
p.force >= 0 ? p.force : arg_force,
|
||||||
|
p.no_reload >= 0 ? p.no_reload : arg_no_reload,
|
||||||
|
p.noexec >= 0 ? p.noexec : arg_noexec);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return varlink_reply(link, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static int verb_list(int argc, char **argv, void *userdata) {
|
static int verb_list(int argc, char **argv, void *userdata) {
|
||||||
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||||
_cleanup_(table_unrefp) Table *t = NULL;
|
_cleanup_(table_unrefp) Table *t = NULL;
|
||||||
@ -1041,6 +1302,63 @@ static int verb_list(int argc, char **argv, void *userdata) {
|
|||||||
return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
|
return table_print_with_pager(t, arg_json_format_flags, arg_pager_flags, arg_legend);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct MethodListParameters {
|
||||||
|
const char *class;
|
||||||
|
} MethodListParameters;
|
||||||
|
|
||||||
|
static int vl_method_list(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
||||||
|
|
||||||
|
static const JsonDispatch dispatch_table[] = {
|
||||||
|
{ "class", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(MethodListParameters, class), 0 },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
MethodListParameters p = {
|
||||||
|
};
|
||||||
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||||
|
_cleanup_hashmap_free_ Hashmap *images = NULL;
|
||||||
|
ImageClass image_class = arg_image_class;
|
||||||
|
Image *img;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(link);
|
||||||
|
|
||||||
|
r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = parse_image_class_parameter(link, p.class, &image_class, NULL);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
images = hashmap_new(&image_hash_ops);
|
||||||
|
if (!images)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
r = image_discover(image_class, arg_root, images);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
HASHMAP_FOREACH(img, images) {
|
||||||
|
if (v) {
|
||||||
|
/* Send previous item with more=true */
|
||||||
|
r = varlink_notify(link, v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = json_variant_unref(v);
|
||||||
|
|
||||||
|
r = image_to_json(img, &v);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v) /* Send final item with more=false */
|
||||||
|
return varlink_reply(link, v);
|
||||||
|
|
||||||
|
return varlink_error(link, "io.systemd.sysext.NoImagesFound", NULL);
|
||||||
|
}
|
||||||
|
|
||||||
static int verb_help(int argc, char **argv, void *userdata) {
|
static int verb_help(int argc, char **argv, void *userdata) {
|
||||||
_cleanup_free_ char *link = NULL;
|
_cleanup_free_ char *link = NULL;
|
||||||
int r;
|
int r;
|
||||||
@ -1176,6 +1494,12 @@ static int parse_argv(int argc, char *argv[]) {
|
|||||||
assert_not_reached();
|
assert_not_reached();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r = varlink_invocation(VARLINK_ALLOW_ACCEPT);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to check if invoked in Varlink mode: %m");
|
||||||
|
if (r > 0)
|
||||||
|
arg_varlink = true;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1196,10 +1520,11 @@ static int sysext_main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
static int run(int argc, char *argv[]) {
|
static int run(int argc, char *argv[]) {
|
||||||
int r;
|
int r;
|
||||||
arg_image_class = invoked_as(argv, "systemd-confext") ? IMAGE_CONFEXT : IMAGE_SYSEXT;
|
|
||||||
|
|
||||||
log_setup();
|
log_setup();
|
||||||
|
|
||||||
|
arg_image_class = invoked_as(argv, "systemd-confext") ? IMAGE_CONFEXT : IMAGE_SYSEXT;
|
||||||
|
|
||||||
r = parse_argv(argc, argv);
|
r = parse_argv(argc, argv);
|
||||||
if (r <= 0)
|
if (r <= 0)
|
||||||
return r;
|
return r;
|
||||||
@ -1211,6 +1536,37 @@ static int run(int argc, char *argv[]) {
|
|||||||
if (r < 0)
|
if (r < 0)
|
||||||
return log_error_errno(r, "Failed to parse environment variable: %m");
|
return log_error_errno(r, "Failed to parse environment variable: %m");
|
||||||
|
|
||||||
|
if (arg_varlink) {
|
||||||
|
_cleanup_(varlink_server_unrefp) VarlinkServer *varlink_server = NULL;
|
||||||
|
|
||||||
|
/* Invocation as Varlink service */
|
||||||
|
|
||||||
|
r = varlink_server_new(&varlink_server, VARLINK_SERVER_ROOT_ONLY);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to allocate Varlink server: %m");
|
||||||
|
|
||||||
|
r = varlink_server_add_interface(varlink_server, &vl_interface_io_systemd_sysext);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to add Varlink interface: %m");
|
||||||
|
|
||||||
|
r = varlink_server_bind_method_many(
|
||||||
|
varlink_server,
|
||||||
|
"io.systemd.sysext.Merge", vl_method_merge,
|
||||||
|
"io.systemd.sysext.Unmerge", vl_method_unmerge,
|
||||||
|
"io.systemd.sysext.Refresh", vl_method_refresh,
|
||||||
|
"io.systemd.sysext.List", vl_method_list);
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to bind Varlink methods: %m");
|
||||||
|
|
||||||
|
r = varlink_server_loop_auto(varlink_server);
|
||||||
|
if (r == -EPERM)
|
||||||
|
return log_error_errno(r, "Invoked by unprivileged Varlink peer, refusing.");
|
||||||
|
if (r < 0)
|
||||||
|
return log_error_errno(r, "Failed to run Varlink event loop: %m");
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
return sysext_main(argc, argv);
|
return sysext_main(argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
#include "varlink-io.systemd.Resolve.h"
|
#include "varlink-io.systemd.Resolve.h"
|
||||||
#include "varlink-io.systemd.UserDatabase.h"
|
#include "varlink-io.systemd.UserDatabase.h"
|
||||||
#include "varlink-io.systemd.oom.h"
|
#include "varlink-io.systemd.oom.h"
|
||||||
|
#include "varlink-io.systemd.sysext.h"
|
||||||
#include "varlink-org.varlink.service.h"
|
#include "varlink-org.varlink.service.h"
|
||||||
|
|
||||||
static VARLINK_DEFINE_ENUM_TYPE(
|
static VARLINK_DEFINE_ENUM_TYPE(
|
||||||
@ -137,6 +138,8 @@ TEST(parse_format) {
|
|||||||
print_separator();
|
print_separator();
|
||||||
test_parse_format_one(&vl_interface_io_systemd_PCRExtend);
|
test_parse_format_one(&vl_interface_io_systemd_PCRExtend);
|
||||||
print_separator();
|
print_separator();
|
||||||
|
test_parse_format_one(&vl_interface_io_systemd_sysext);
|
||||||
|
print_separator();
|
||||||
test_parse_format_one(&vl_interface_xyz_test);
|
test_parse_format_one(&vl_interface_xyz_test);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -457,6 +457,16 @@ test ! -e /usr/lib/systemd/system/some_file
|
|||||||
systemd-sysext unmerge
|
systemd-sysext unmerge
|
||||||
rmdir /etc/extensions/app-nodistro
|
rmdir /etc/extensions/app-nodistro
|
||||||
|
|
||||||
|
# Similar, but go via varlink
|
||||||
|
varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.List '{}'
|
||||||
|
(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
|
||||||
|
varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Merge '{}'
|
||||||
|
grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
|
||||||
|
varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Refresh '{}'
|
||||||
|
grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file
|
||||||
|
varlinkctl call /run/systemd/io.systemd.sysext io.systemd.sysext.Unmerge '{}'
|
||||||
|
(! grep -q -F "MARKER=1" /usr/lib/systemd/system/some_file )
|
||||||
|
|
||||||
# Check that extensions cannot contain os-release
|
# Check that extensions cannot contain os-release
|
||||||
mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
|
mkdir -p /run/extensions/app-reject/usr/lib/{extension-release.d/,systemd/system}
|
||||||
echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
|
echo "ID=_any" >/run/extensions/app-reject/usr/lib/extension-release.d/extension-release.app-reject
|
||||||
|
@ -529,6 +529,15 @@ units = [
|
|||||||
'file' : 'systemd-sysext.service',
|
'file' : 'systemd-sysext.service',
|
||||||
'conditions' : ['ENABLE_SYSEXT'],
|
'conditions' : ['ENABLE_SYSEXT'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
'file' : 'systemd-sysext.socket',
|
||||||
|
'conditions' : ['ENABLE_SYSEXT'],
|
||||||
|
'symlinks' : ['sockets.target.wants/'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'file' : 'systemd-sysext@.service',
|
||||||
|
'conditions' : ['ENABLE_SYSEXT'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
'file' : 'systemd-sysupdate-reboot.service.in',
|
'file' : 'systemd-sysupdate-reboot.service.in',
|
||||||
'conditions' : ['ENABLE_SYSUPDATE'],
|
'conditions' : ['ENABLE_SYSUPDATE'],
|
||||||
|
25
units/systemd-sysext.socket
Normal file
25
units/systemd-sysext.socket
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
#
|
||||||
|
# This file is part of systemd.
|
||||||
|
#
|
||||||
|
# systemd is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU Lesser General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=System Extension Image Management (Varlink)
|
||||||
|
Documentation=man:systemd-sysext(8)
|
||||||
|
DefaultDependencies=no
|
||||||
|
After=local-fs.target
|
||||||
|
Before=sockets.target
|
||||||
|
ConditionCapability=CAP_SYS_ADMIN
|
||||||
|
|
||||||
|
[Socket]
|
||||||
|
ListenStream=/run/systemd/io.systemd.sysext
|
||||||
|
FileDescriptorName=varlink
|
||||||
|
SocketMode=0600
|
||||||
|
Accept=yes
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=sockets.target
|
20
units/systemd-sysext@.service
Normal file
20
units/systemd-sysext@.service
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
#
|
||||||
|
# This file is part of systemd.
|
||||||
|
#
|
||||||
|
# systemd is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of the GNU Lesser General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2.1 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=System Extension Image Management (Varlink)
|
||||||
|
Documentation=man:systemd-sysext(8)
|
||||||
|
DefaultDependencies=no
|
||||||
|
After=local-fs.target
|
||||||
|
Conflicts=shutdown.target initrd-switch-root.target
|
||||||
|
Before=shutdown.target initrd-switch-root.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Environment=LISTEN_FDNAMES=varlink
|
||||||
|
ExecStart=-systemd-sysext
|
Loading…
Reference in New Issue
Block a user