machinectl: add verb edit and cat to operate on .nspawn files

This allows operating on .nspawn files using machinectl.

Closes #26246
This commit is contained in:
Mike Yuan 2023-03-12 00:56:13 +08:00
parent d6c623e884
commit 1ed35a0d93
No known key found for this signature in database
GPG key ID: 417471C0A40F58B3
2 changed files with 181 additions and 4 deletions

View file

@ -376,6 +376,21 @@
formatted human-readable output.</para></listitem>
</varlistentry>
<varlistentry>
<term><command>edit</command> <replaceable>NAME|FILE</replaceable></term>
<listitem><para>Edit the settings file of the specified machines. For the format of the settings file, refer to <citerefentry
project='man-pages'><refentrytitle>systemd.nspawn</refentrytitle><manvolnum>5</manvolnum></citerefentry>.
If an existing settings file of the given machine can't be found, <command>edit</command> automatically
create a new settings file from scratch under <filename>/etc/</filename></para></listitem>
</varlistentry>
<varlistentry>
<term><command>cat</command> <replaceable>NAME|FILE</replaceable></term>
<listitem><para>Show the settings file of the specified machines.</para></listitem>
</varlistentry>
<varlistentry>
<term><command>clone</command> <replaceable>NAME</replaceable> <replaceable>NAME</replaceable></term>

View file

@ -27,6 +27,7 @@
#include "cgroup-util.h"
#include "constants.h"
#include "copy.h"
#include "edit-util.h"
#include "env-util.h"
#include "fd-util.h"
#include "format-table.h"
@ -1412,6 +1413,163 @@ static int shell_machine(int argc, char *argv[], void *userdata) {
return process_forward(event, &forward, master, 0, machine);
}
static int normalize_nspawn_filename(const char *name, char **ret_file) {
_cleanup_free_ char *file = NULL;
assert(name);
assert(ret_file);
if (!endswith(name, ".nspawn"))
file = strjoin(name, ".nspawn");
else
file = strdup(name);
if (!file)
return log_oom();
if (!filename_is_valid(file))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Invalid settings file name '%s'.", file);
*ret_file = TAKE_PTR(file);
return 0;
}
static int get_settings_path(const char *name, char **ret_path) {
assert(name);
assert(ret_path);
FOREACH_STRING(i, "/etc/systemd/nspawn", "/run/systemd/nspawn", "/var/lib/machines") {
_cleanup_free_ char *path = NULL;
path = path_join(i, name);
if (!path)
return -ENOMEM;
if (access(path, F_OK) >= 0) {
*ret_path = TAKE_PTR(path);
return 0;
}
}
return -ENOENT;
}
static int edit_settings(int argc, char *argv[], void *userdata) {
_cleanup_(edit_file_context_done) EditFileContext context = {
.remove_parent = false,
};
int r;
if (!on_tty())
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot edit machine settings if not on a tty.");
if (arg_transport != BUS_TRANSPORT_LOCAL)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Edit is only supported on the host machine.");
r = mac_selinux_init();
if (r < 0)
return r;
STRV_FOREACH(name, strv_skip(argv, 1)) {
_cleanup_free_ char *file = NULL, *path = NULL;
if (path_is_absolute(*name)) {
if (!path_is_safe(*name))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid settings file path '%s'.",
*name);
r = edit_files_add(&context, *name, NULL, NULL);
if (r < 0)
return r;
continue;
}
r = normalize_nspawn_filename(*name, &file);
if (r < 0)
return r;
r = get_settings_path(file, &path);
if (r == -ENOENT) {
log_debug("No existing settings file for machine '%s' found, creating a new file.", *name);
path = path_join("/etc/systemd/nspawn", file);
if (!path)
return log_oom();
r = edit_files_add(&context, path, NULL, NULL);
if (r < 0)
return r;
continue;
}
if (r < 0)
return log_error_errno(r, "Failed to get the path of the settings file: %m");
if (path_startswith(path, "/var/lib/machines")) {
_cleanup_free_ char *new_path = NULL;
new_path = path_join("/etc/systemd/nspawn", file);
if (!new_path)
return log_oom();
r = edit_files_add(&context, new_path, path, NULL);
} else
r = edit_files_add(&context, path, NULL, NULL);
if (r < 0)
return r;
}
return do_edit_files_and_install(&context);
}
static int cat_settings(int argc, char *argv[], void *userdata) {
int r = 0;
if (arg_transport != BUS_TRANSPORT_LOCAL)
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"Cat is only supported on the host machine.");
pager_open(arg_pager_flags);
STRV_FOREACH(name, strv_skip(argv, 1)) {
_cleanup_free_ char *file = NULL, *path = NULL;
int q;
if (path_is_absolute(*name)) {
if (!path_is_safe(*name))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Invalid settings file path '%s'.",
*name);
q = cat_files(*name, /* dropins = */ NULL, /* flags = */ 0);
if (q < 0)
return r < 0 ? r : q;
continue;
}
q = normalize_nspawn_filename(*name, &file);
if (q < 0)
return r < 0 ? r : q;
q = get_settings_path(file, &path);
if (q == -ENOENT) {
log_error_errno(q, "No settings file found for machine '%s'.", *name);
r = r < 0 ? r : q;
continue;
}
if (q < 0) {
log_error_errno(q, "Failed to get the path of the settings file: %m");
return r < 0 ? r : q;
}
q = cat_files(path, /* dropins = */ NULL, /* flags = */ 0);
if (q < 0)
return r < 0 ? r : q;
}
return r;
}
static int remove_image(int argc, char *argv[], void *userdata) {
sd_bus *bus = ASSERT_PTR(userdata);
int r;
@ -2443,18 +2601,20 @@ static int help(int argc, char *argv[], void *userdata) {
" kill NAME... Send signal to processes of a VM/container\n"
" copy-to NAME PATH [PATH] Copy files from the host to a container\n"
" copy-from NAME PATH [PATH] Copy files from a container to the host\n"
" bind NAME PATH [PATH] Bind mount a path from the host into a container\n\n"
"Image Commands:\n"
" bind NAME PATH [PATH] Bind mount a path from the host into a container\n"
"\nImage Commands:\n"
" list-images Show available container and VM images\n"
" image-status [NAME...] Show image details\n"
" show-image [NAME...] Show properties of image\n"
" edit NAME|FILE... Edit settings of one or more VMs/containers\n"
" cat NAME|FILE... Show settings of one or more VMs/containers\n"
" clone NAME NAME Clone an image\n"
" rename NAME NAME Rename an image\n"
" read-only NAME [BOOL] Mark or unmark image read-only\n"
" remove NAME... Remove an image\n"
" set-limit [NAME] BYTES Set image or pool size limit (disk quota)\n"
" clean Remove hidden (or all) images\n\n"
"Image Transfer Commands:\n"
" clean Remove hidden (or all) images\n"
"\nImage Transfer Commands:\n"
" pull-tar URL [NAME] Download a TAR container image\n"
" pull-raw URL [NAME] Download a RAW container or VM image\n"
" import-tar FILE [NAME] Import a local TAR container image\n"
@ -2800,6 +2960,8 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
{ "login", VERB_ANY, 2, 0, login_machine },
{ "shell", VERB_ANY, VERB_ANY, 0, shell_machine },
{ "bind", 3, 4, 0, bind_mount },
{ "edit", 2, VERB_ANY, 0, edit_settings },
{ "cat", 2, VERB_ANY, 0, cat_settings },
{ "copy-to", 3, 4, 0, copy_files },
{ "copy-from", 3, 4, 0, copy_files },
{ "remove", 2, VERB_ANY, 0, remove_image },