mirror of
https://github.com/systemd/systemd.git
synced 2024-12-11 19:23:37 +08:00
machinectl: add new commands for copying files from/to containers
This commit is contained in:
parent
20b63d12b5
commit
f2cbe59e11
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user