machinectl: add bind-volume / unbind-volume verbs
machinectl bind-volume MACHINE PROVIDER:VOLUME[:CONFIG][:K=V,...] machinectl unbind-volume MACHINE PROVIDER:VOLUME For bind-volume, machinectl parses the SPEC with the shared bind_volume_parse(), Acquires the storage volume from the named provider on the machinectl side, locates the target machine's io.systemd.MachineInstance control socket via machine_get_control_address(), pushes the fd across, and calls io.systemd.MachineInstance.AddStorage with name='<provider>:<volume>' and the user-supplied config string. For unbind-volume, machinectl just forwards the name string to io.systemd.MachineInstance.RemoveStorage. Volumes attached at machine startup (e.g. via systemd-vmspawn's --bind-volume=) are rejected with StorageImmutable when the user attempts to unbind them at runtime. Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
@@ -48,7 +48,7 @@ _machinectl() {
|
||||
[MACHINES]='status show start stop login shell enable disable poweroff reboot pause resume terminate kill
|
||||
image-status show-image remove export-tar export-raw'
|
||||
[MACHINES_OR_FILES]='edit cat'
|
||||
[MACHINE_ONLY]='clone rename set-limit'
|
||||
[MACHINE_ONLY]='clone rename set-limit bind-volume unbind-volume'
|
||||
[READONLY]='read-only'
|
||||
[FILE]='import-tar import-raw'
|
||||
[MACHINES_AND_FILES]='copy-to copy-from bind'
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
"copy-to:Copy files from the host to a container"
|
||||
"copy-from:Copy files from a container to the host"
|
||||
"bind:Bind mount a path from the host into a container"
|
||||
"bind-volume:Attach a storage volume to a running machine"
|
||||
"unbind-volume:Detach a storage volume from a running machine"
|
||||
|
||||
"list-images:Show available container and VM images"
|
||||
"image-status:Show image details"
|
||||
@@ -115,6 +117,12 @@
|
||||
else stop=1
|
||||
fi ;;
|
||||
|
||||
bind-volume|unbind-volume)
|
||||
if (( CURRENT == 2 )); then _sd_machines
|
||||
elif (( CURRENT == 3 )); then _message "volume spec"
|
||||
else stop=1
|
||||
fi ;;
|
||||
|
||||
read-only)
|
||||
if (( CURRENT == 2 )); then _machinectl_images
|
||||
elif (( CURRENT == 3 )); then _values 'read-only flag' 'true' 'false'
|
||||
|
||||
@@ -29,16 +29,19 @@
|
||||
#include "cgroup-util.h"
|
||||
#include "edit-util.h"
|
||||
#include "env-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-ifname.h"
|
||||
#include "format-table.h"
|
||||
#include "format-util.h"
|
||||
#include "hostname-util.h"
|
||||
#include "import-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "json-util.h"
|
||||
#include "label-util.h"
|
||||
#include "log.h"
|
||||
#include "logs-show.h"
|
||||
#include "machine-dbus.h"
|
||||
#include "machine-util.h"
|
||||
#include "main-func.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "osc-context.h"
|
||||
@@ -55,6 +58,7 @@
|
||||
#include "ptyfwd.h"
|
||||
#include "runtime-scope.h"
|
||||
#include "stdio-util.h"
|
||||
#include "storage-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
@@ -1338,6 +1342,120 @@ static int verb_copy_files(int argc, char *argv[], uintptr_t _data, void *userda
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_bind_volume(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
int r;
|
||||
|
||||
if (arg_transport != BUS_TRANSPORT_LOCAL)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"bind-volume is only supported on the local transport.");
|
||||
|
||||
_cleanup_(bind_volume_freep) BindVolume *bv = NULL;
|
||||
r = bind_volume_parse(argv[2], &bv);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse bind-volume argument '%s': %m", argv[2]);
|
||||
|
||||
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
/* Locate and connect to the target machine before acquiring storage, so a missing
|
||||
* machine doesn't trigger 'create=new' side effects on the StorageProvider. */
|
||||
_cleanup_free_ char *address = NULL;
|
||||
r = machine_get_control_address(argv[1], &address);
|
||||
if (r == -EOPNOTSUPP)
|
||||
return log_error_errno(r, "Machine '%s' does not expose a varlink control socket.", argv[1]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
|
||||
r = sd_varlink_connect_address(&vl, address);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to connect to machine control socket %s: %m", address);
|
||||
|
||||
r = sd_varlink_set_allow_fd_passing_output(vl, true);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to enable fd passing on varlink connection: %m");
|
||||
|
||||
_cleanup_(storage_acquire_reply_done) StorageAcquireReply reply = STORAGE_ACQUIRE_REPLY_INIT;
|
||||
_cleanup_free_ char *acquire_error_id = NULL;
|
||||
r = storage_acquire_volume(arg_runtime_scope, bv, arg_ask_password, &acquire_error_id, &reply);
|
||||
if (r < 0) {
|
||||
if (acquire_error_id)
|
||||
return log_error_errno(r, "Failed to acquire storage volume '%s:%s' from provider: %s",
|
||||
bv->provider, bv->volume, acquire_error_id);
|
||||
return log_error_errno(r, "Failed to acquire storage volume '%s:%s' from provider: %m",
|
||||
bv->provider, bv->volume);
|
||||
}
|
||||
|
||||
int fd_index = sd_varlink_push_fd(vl, reply.fd);
|
||||
if (fd_index < 0)
|
||||
return log_error_errno(fd_index, "Failed to push storage fd onto varlink connection: %m");
|
||||
TAKE_FD(reply.fd);
|
||||
|
||||
_cleanup_free_ char *name = strjoin(bv->provider, ":", bv->volume);
|
||||
if (!name)
|
||||
return log_oom();
|
||||
|
||||
sd_json_variant *vl_reply = NULL;
|
||||
const char *error_id = NULL;
|
||||
r = sd_varlink_callbo(
|
||||
vl,
|
||||
"io.systemd.MachineInstance.AddStorage",
|
||||
&vl_reply, &error_id,
|
||||
SD_JSON_BUILD_PAIR_INTEGER("fileDescriptorIndex", fd_index),
|
||||
SD_JSON_BUILD_PAIR_STRING("name", name),
|
||||
JSON_BUILD_PAIR_STRING_NON_EMPTY("config", bv->config));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to call io.systemd.MachineInstance.AddStorage: %m");
|
||||
if (error_id)
|
||||
return log_error_errno(sd_varlink_error_to_errno(error_id, vl_reply),
|
||||
"AddStorage failed for '%s': %s", name, error_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_unbind_volume(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
int r;
|
||||
|
||||
if (arg_transport != BUS_TRANSPORT_LOCAL)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
|
||||
"unbind-volume is only supported on the local transport.");
|
||||
|
||||
r = machine_storage_name_split(argv[2], /* ret_provider= */ NULL, /* ret_volume= */ NULL);
|
||||
if (r == -ENOMEM)
|
||||
return log_oom();
|
||||
if (r < 0)
|
||||
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
|
||||
"Invalid unbind-volume name '%s', expected '<provider>:<volume>'.", argv[2]);
|
||||
|
||||
(void) polkit_agent_open_if_enabled(arg_transport, arg_ask_password);
|
||||
|
||||
_cleanup_free_ char *address = NULL;
|
||||
r = machine_get_control_address(argv[1], &address);
|
||||
if (r == -EOPNOTSUPP)
|
||||
return log_error_errno(r, "Machine '%s' does not expose a varlink control socket.", argv[1]);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_(sd_varlink_unrefp) sd_varlink *vl = NULL;
|
||||
r = sd_varlink_connect_address(&vl, address);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to connect to machine control socket %s: %m", address);
|
||||
|
||||
sd_json_variant *reply = NULL;
|
||||
const char *error_id = NULL;
|
||||
r = sd_varlink_callbo(
|
||||
vl,
|
||||
"io.systemd.MachineInstance.RemoveStorage",
|
||||
&reply, &error_id,
|
||||
SD_JSON_BUILD_PAIR_STRING("name", argv[2]));
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to call io.systemd.MachineInstance.RemoveStorage: %m");
|
||||
if (error_id)
|
||||
return log_error_errno(sd_varlink_error_to_errno(error_id, reply),
|
||||
"RemoveStorage failed for '%s': %s", argv[2], error_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int verb_bind_mount(int argc, char *argv[], uintptr_t _data, void *userdata) {
|
||||
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
sd_bus *bus = ASSERT_PTR(userdata);
|
||||
@@ -2606,6 +2724,8 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
|
||||
{ "login", VERB_ANY, 2, 0, verb_login_machine },
|
||||
{ "shell", VERB_ANY, VERB_ANY, 0, verb_shell_machine },
|
||||
{ "bind", 3, 4, 0, verb_bind_mount },
|
||||
{ "bind-volume", 3, 3, 0, verb_bind_volume },
|
||||
{ "unbind-volume", 3, 3, 0, verb_unbind_volume },
|
||||
{ "edit", 2, VERB_ANY, 0, verb_edit_settings },
|
||||
{ "cat", 2, VERB_ANY, 0, verb_cat_settings },
|
||||
{ "copy-to", 3, 4, 0, verb_copy_files },
|
||||
|
||||
Reference in New Issue
Block a user