machinectl: add new commands for copying files from/to containers

This commit is contained in:
Lennart Poettering 2014-12-18 01:35:58 +01:00
parent 20b63d12b5
commit f2cbe59e11
5 changed files with 175 additions and 43 deletions

View file

@ -185,7 +185,7 @@
</varlistentry>
<varlistentry>
<term><command>status</command> <replaceable>ID</replaceable>...</term>
<term><command>status</command> <replaceable>NAME</replaceable>...</term>
<listitem><para>Show terse runtime
status information about one or more
@ -198,14 +198,14 @@
</varlistentry>
<varlistentry>
<term><command>show</command> <replaceable>ID</replaceable>...</term>
<term><command>show</command> <replaceable>NAME</replaceable>...</term>
<listitem><para>Show properties of one
or more registered virtual machines or
containers or the manager itself. If
no argument is specified, properties
of the manager will be shown. If an
ID is specified, properties of this
NAME is specified, properties of this
virtual machine or container are
shown. By default, empty properties
are suppressed. Use
@ -222,7 +222,7 @@
</varlistentry>
<varlistentry>
<term><command>login</command> <replaceable>ID</replaceable></term>
<term><command>login</command> <replaceable>NAME</replaceable></term>
<listitem><para>Open a terminal login
session to a container. This will
@ -235,7 +235,7 @@
</varlistentry>
<varlistentry>
<term><command>reboot</command> <replaceable>ID</replaceable>...</term>
<term><command>reboot</command> <replaceable>NAME</replaceable>...</term>
<listitem><para>Reboot one or more
containers. This will trigger a reboot
@ -248,7 +248,7 @@
</varlistentry>
<varlistentry>
<term><command>poweroff</command> <replaceable>ID</replaceable>...</term>
<term><command>poweroff</command> <replaceable>NAME</replaceable>...</term>
<listitem><para>Power off one or more
containers. This will trigger a reboot
@ -264,7 +264,7 @@
</varlistentry>
<varlistentry>
<term><command>kill</command> <replaceable>ID</replaceable>...</term>
<term><command>kill</command> <replaceable>NAME</replaceable>...</term>
<listitem><para>Send a signal to one
or more processes of the virtual
@ -279,7 +279,7 @@
</varlistentry>
<varlistentry>
<term><command>terminate</command> <replaceable>ID</replaceable>...</term>
<term><command>terminate</command> <replaceable>NAME</replaceable>...</term>
<listitem><para>Terminates a virtual
machine or container. This kills all
@ -290,7 +290,7 @@
</varlistentry>
<varlistentry>
<term><command>bind</command> <replaceable>ID</replaceable> <replaceable>DIRECTORY</replaceable> [<replaceable>DIRECTORY</replaceable>]</term>
<term><command>bind</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
<listitem><para>Bind mounts a
directory from the host into the
@ -314,6 +314,33 @@
containers.</para></listitem>
</varlistentry>
<varlistentry>
<term><command>copy-to</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
<listitem><para>Copies files or
directories from the host system into
a running container. Takes a container
name, followed by the source path on
the host and the destination path in
the container. If the destination path
is omitted the same as the source path
is used.</para></listitem>
</varlistentry>
<varlistentry>
<term><command>copy-from</command> <replaceable>NAME</replaceable> <replaceable>PATH</replaceable> [<replaceable>PATH</replaceable>]</term>
<listitem><para>Copies files or
directories from a container into the
host system. Takes a container name,
followed by the source path in the
container the destination path on the
host. If the destination path is
omitted the same as the source path is
used.</para></listitem>
</varlistentry>
</variablelist>
</refsect1>

View file

@ -31,6 +31,7 @@
#include <arpa/inet.h>
#include <net/if.h>
#include <sys/mount.h>
#include <libgen.h>
#include "sd-bus.h"
#include "log.h"
@ -48,6 +49,7 @@
#include "event-util.h"
#include "path-util.h"
#include "mkdir.h"
#include "copy.h"
static char **arg_property = NULL;
static bool arg_all = false;
@ -626,6 +628,97 @@ static int machine_get_leader(sd_bus *bus, const char *name, pid_t *ret) {
return 0;
}
static int copy_files(sd_bus *bus, char **args, unsigned n) {
char *dest, *host_path, *container_path, *host_dirname, *host_basename, *container_dirname, *container_basename, *t;
_cleanup_close_ int hostfd = -1;
pid_t child, leader;
bool copy_from;
siginfo_t si;
int r;
if (n > 4) {
log_error("Too many arguments.");
return -EINVAL;
}
copy_from = streq(args[0], "copy-from");
dest = args[3] ?: args[2];
host_path = strdupa(copy_from ? dest : args[2]);
container_path = strdupa(copy_from ? args[2] : dest);
if (!path_is_absolute(container_path)) {
log_error("Container path not absolute.");
return -EINVAL;
}
t = strdup(host_path);
host_basename = basename(t);
host_dirname = dirname(host_path);
t = strdup(container_path);
container_basename = basename(t);
container_dirname = dirname(container_path);
r = machine_get_leader(bus, args[1], &leader);
if (r < 0)
return r;
hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
if (r < 0)
return log_error_errno(errno, "Failed to open source directory: %m");
child = fork();
if (child < 0)
return log_error_errno(errno, "Failed to fork(): %m");
if (child == 0) {
int containerfd;
const char *q;
int mntfd;
q = procfs_file_alloca(leader, "ns/mnt");
mntfd = open(q, O_RDONLY|O_NOCTTY|O_CLOEXEC);
if (mntfd < 0) {
log_error_errno(errno, "Failed to open mount namespace of leader: %m");
_exit(EXIT_FAILURE);
}
if (setns(mntfd, CLONE_NEWNS) < 0) {
log_error_errno(errno, "Failed to join namespace of leader: %m");
_exit(EXIT_FAILURE);
}
containerfd = open(container_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY);
if (containerfd < 0) {
log_error_errno(errno, "Failed top open destination directory: %m");
_exit(EXIT_FAILURE);
}
if (copy_from)
r = copy_tree_at(containerfd, container_basename, hostfd, host_basename, true);
else
r = copy_tree_at(hostfd, host_basename, containerfd, container_basename, true);
if (r < 0) {
log_error_errno(errno, "Failed to copy tree: %m");
_exit(EXIT_FAILURE);
}
_exit(EXIT_SUCCESS);
}
r = wait_for_terminate(child, &si);
if (r < 0)
return log_error_errno(r, "Failed to wait for client: %m");
if (si.si_code != CLD_EXITED) {
log_error("Client died abnormally.");
return -EIO;
}
if (si.si_status != EXIT_SUCCESS)
return -EIO;
return 0;
}
static int bind_mount(sd_bus *bus, char **args, unsigned n) {
char mount_slave[] = "/tmp/propagate.XXXXXX", *mount_tmp, *mount_outside, *p;
pid_t child, leader;
@ -998,30 +1091,33 @@ static int login_machine(sd_bus *bus, char **args, unsigned n) {
static void help(void) {
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
"Send control commands to or query the virtual machine and container registration manager.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" -p --property=NAME Show only properties by this name\n"
" -a --all Show all properties, including empty ones\n"
" -l --full Do not ellipsize output\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
" --read-only Create read-only bind mount\n"
" --mkdir Create directory before bind mounting, if missing\n\n"
"Send control commands to or query the virtual machine and container\n"
"registration manager.\n\n"
" -h --help Show this help\n"
" --version Show package version\n"
" --no-pager Do not pipe output into a pager\n"
" --no-legend Do not show the headers and footers\n"
" -H --host=[USER@]HOST Operate on remote host\n"
" -M --machine=CONTAINER Operate on local container\n"
" -p --property=NAME Show only properties by this name\n"
" -a --all Show all properties, including empty ones\n"
" -l --full Do not ellipsize output\n"
" --kill-who=WHO Who to send signal to\n"
" -s --signal=SIGNAL Which signal to send\n"
" --read-only Create read-only bind mount\n"
" --mkdir Create directory before bind mounting, if missing\n\n"
"Commands:\n"
" list List running VMs and containers\n"
" status NAME... Show VM/container status\n"
" show NAME... Show properties of one or more VMs/containers\n"
" login NAME Get a login prompt on a container\n"
" poweroff NAME... Power off one or more containers\n"
" reboot NAME... Reboot one or more containers\n"
" kill NAME... Send signal to processes of a VM/container\n"
" terminate NAME... Terminate one or more VMs/containers\n"
" bind NAME PATH [PATH] Bind mount a path from the host into a container\n",
" list List running VMs and containers\n"
" status NAME... Show VM/container status\n"
" show NAME... Show properties of one or more VMs/containers\n"
" login NAME Get a login prompt on a container\n"
" poweroff NAME... Power off one or more containers\n"
" reboot NAME... Reboot one or more containers\n"
" kill NAME... Send signal to processes of a VM/container\n"
" terminate NAME... Terminate one or more VMs/containers\n"
" bind NAME PATH [PATH] Bind mount a path from the host into a 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",
program_invocation_short_name);
}
@ -1159,6 +1255,8 @@ static int machinectl_main(sd_bus *bus, int argc, char *argv[]) {
{ "kill", MORE, 2, kill_machine },
{ "login", MORE, 2, login_machine },
{ "bind", MORE, 3, bind_mount },
{ "copy-to", MORE, 3, copy_files },
{ "copy-from", MORE, 3, copy_files },
};
int left;

View file

@ -122,7 +122,7 @@ int btrfs_subvol_snapshot(const char *old_path, const char *new_path, bool read_
if (r < 0)
return r;
r = copy_tree_fd(old_fd, new_path, true);
r = copy_directory_fd(old_fd, new_path, true);
if (r < 0) {
btrfs_subvol_remove(new_path);
return r;

View file

@ -25,6 +25,8 @@
#include "btrfs-util.h"
#include "copy.h"
#define COPY_BUFFER_SIZE (16*1024)
int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
bool try_sendfile = true;
int r;
@ -40,7 +42,7 @@ int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink) {
}
for (;;) {
size_t m = PIPE_BUF;
size_t m = COPY_BUFFER_SIZE;
ssize_t n;
if (max_bytes != (off_t) -1) {
@ -279,30 +281,34 @@ static int fd_copy_directory(
return r;
}
int copy_tree(const char *from, const char *to, bool merge) {
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge) {
struct stat st;
assert(from);
assert(to);
if (lstat(from, &st) < 0)
if (fstatat(fdf, from, &st, AT_SYMLINK_NOFOLLOW) < 0)
return -errno;
if (S_ISREG(st.st_mode))
return fd_copy_regular(AT_FDCWD, from, &st, AT_FDCWD, to);
return fd_copy_regular(fdf, from, &st, fdt, to);
else if (S_ISDIR(st.st_mode))
return fd_copy_directory(AT_FDCWD, from, &st, AT_FDCWD, to, st.st_dev, merge);
return fd_copy_directory(fdf, from, &st, fdt, to, st.st_dev, merge);
else if (S_ISLNK(st.st_mode))
return fd_copy_symlink(AT_FDCWD, from, &st, AT_FDCWD, to);
return fd_copy_symlink(fdf, from, &st, fdt, to);
else if (S_ISFIFO(st.st_mode))
return fd_copy_fifo(AT_FDCWD, from, &st, AT_FDCWD, to);
return fd_copy_fifo(fdf, from, &st, fdt, to);
else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode))
return fd_copy_node(AT_FDCWD, from, &st, AT_FDCWD, to);
return fd_copy_node(fdf, from, &st, fdt, to);
else
return -ENOTSUP;
}
int copy_tree_fd(int dirfd, const char *to, bool merge) {
int copy_tree(const char *from, const char *to, bool merge) {
return copy_tree_at(AT_FDCWD, from, AT_FDCWD, to, merge);
}
int copy_directory_fd(int dirfd, const char *to, bool merge) {
struct stat st;

View file

@ -27,5 +27,6 @@
int copy_file_fd(const char *from, int to, bool try_reflink);
int copy_file(const char *from, const char *to, int flags, mode_t mode);
int copy_tree(const char *from, const char *to, bool merge);
int copy_tree_fd(int dirfd, const char *to, bool merge);
int copy_tree_at(int fdf, const char *from, int fdt, const char *to, bool merge);
int copy_directory_fd(int dirfd, const char *to, bool merge);
int copy_bytes(int fdf, int fdt, off_t max_bytes, bool try_reflink);