machine: add support for importing containers from plain directories
Fixes: #2728 This is also supposed to be preparation for doing #10234 eventually, where a very similar operation is requested: instead of importing a tree to /var/lib/machines it would need to be imported into /var/lib/portables/.
This commit is contained in:
parent
b3cade0c27
commit
1d7579c473
|
@ -815,6 +815,15 @@
|
||||||
<command>cancel-transfer</command>.</para></listitem>
|
<command>cancel-transfer</command>.</para></listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><command>import-fs</command> <replaceable>DIRECTORY</replaceable> [<replaceable>NAME</replaceable>]</term>
|
||||||
|
|
||||||
|
<listitem><para>Imports a container image stored in a local directory into
|
||||||
|
<filename>/var/lib/machines/</filename>, operates similar to <command>import-tar</command> or
|
||||||
|
<command>import-raw</command>, but the first argument is the source directory. If supported, this command will
|
||||||
|
create btrfs snapshot or subvolume for the new image.</para></listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><command>export-tar</command> <replaceable>NAME</replaceable> [<replaceable>FILE</replaceable>]</term>
|
<term><command>export-tar</command> <replaceable>NAME</replaceable> [<replaceable>FILE</replaceable>]</term>
|
||||||
<term><command>export-raw</command> <replaceable>NAME</replaceable> [<replaceable>FILE</replaceable>]</term>
|
<term><command>export-raw</command> <replaceable>NAME</replaceable> [<replaceable>FILE</replaceable>]</term>
|
||||||
|
|
12
meson.build
12
meson.build
|
@ -227,6 +227,7 @@ conf.set_quoted('ROOTLIBEXECDIR', rootlibexecdir)
|
||||||
conf.set_quoted('BOOTLIBDIR', bootlibdir)
|
conf.set_quoted('BOOTLIBDIR', bootlibdir)
|
||||||
conf.set_quoted('SYSTEMD_PULL_PATH', join_paths(rootlibexecdir, 'systemd-pull'))
|
conf.set_quoted('SYSTEMD_PULL_PATH', join_paths(rootlibexecdir, 'systemd-pull'))
|
||||||
conf.set_quoted('SYSTEMD_IMPORT_PATH', join_paths(rootlibexecdir, 'systemd-import'))
|
conf.set_quoted('SYSTEMD_IMPORT_PATH', join_paths(rootlibexecdir, 'systemd-import'))
|
||||||
|
conf.set_quoted('SYSTEMD_IMPORT_FS_PATH', join_paths(rootlibexecdir, 'systemd-import-fs'))
|
||||||
conf.set_quoted('SYSTEMD_EXPORT_PATH', join_paths(rootlibexecdir, 'systemd-export'))
|
conf.set_quoted('SYSTEMD_EXPORT_PATH', join_paths(rootlibexecdir, 'systemd-export'))
|
||||||
conf.set_quoted('VENDOR_KEYRING_PATH', join_paths(rootlibexecdir, 'import-pubring.gpg'))
|
conf.set_quoted('VENDOR_KEYRING_PATH', join_paths(rootlibexecdir, 'import-pubring.gpg'))
|
||||||
conf.set_quoted('USER_KEYRING_PATH', join_paths(pkgsysconfdir, 'import-pubring.gpg'))
|
conf.set_quoted('USER_KEYRING_PATH', join_paths(pkgsysconfdir, 'import-pubring.gpg'))
|
||||||
|
@ -2126,6 +2127,14 @@ if conf.get('ENABLE_IMPORTD') == 1
|
||||||
install : true,
|
install : true,
|
||||||
install_dir : rootlibexecdir)
|
install_dir : rootlibexecdir)
|
||||||
|
|
||||||
|
systemd_import_fs = executable('systemd-import-fs',
|
||||||
|
systemd_import_fs_sources,
|
||||||
|
include_directories : includes,
|
||||||
|
link_with : [libshared],
|
||||||
|
install_rpath : rootlibexecdir,
|
||||||
|
install : true,
|
||||||
|
install_dir : rootlibexecdir)
|
||||||
|
|
||||||
systemd_export = executable('systemd-export',
|
systemd_export = executable('systemd-export',
|
||||||
systemd_export_sources,
|
systemd_export_sources,
|
||||||
include_directories : includes,
|
include_directories : includes,
|
||||||
|
@ -2137,7 +2146,8 @@ if conf.get('ENABLE_IMPORTD') == 1
|
||||||
install_rpath : rootlibexecdir,
|
install_rpath : rootlibexecdir,
|
||||||
install : true,
|
install : true,
|
||||||
install_dir : rootlibexecdir)
|
install_dir : rootlibexecdir)
|
||||||
public_programs += [systemd_pull, systemd_import, systemd_export]
|
|
||||||
|
public_programs += [systemd_pull, systemd_import, systemd_import_fs, systemd_export]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_LIBCURL') == 1
|
if conf.get('ENABLE_REMOTE') == 1 and conf.get('HAVE_LIBCURL') == 1
|
||||||
|
|
|
@ -22,3 +22,11 @@ static inline void rm_rf_physical_and_free(char *p) {
|
||||||
free(p);
|
free(p);
|
||||||
}
|
}
|
||||||
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_physical_and_free);
|
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_physical_and_free);
|
||||||
|
|
||||||
|
/* Similar as above, but also has magic btrfs subvolume powers */
|
||||||
|
static inline void rm_rf_subvolume_and_free(char *p) {
|
||||||
|
PROTECT_ERRNO;
|
||||||
|
(void) rm_rf(p, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
|
||||||
|
free(p);
|
||||||
|
}
|
||||||
|
DEFINE_TRIVIAL_CLEANUP_FUNC(char*, rm_rf_subvolume_and_free);
|
||||||
|
|
|
@ -0,0 +1,323 @@
|
||||||
|
/* SPDX-License-Identifier: LGPL-2.1+ */
|
||||||
|
|
||||||
|
#include <getopt.h>
|
||||||
|
|
||||||
|
#include "alloc-util.h"
|
||||||
|
#include "btrfs-util.h"
|
||||||
|
#include "fd-util.h"
|
||||||
|
#include "fileio.h"
|
||||||
|
#include "fs-util.h"
|
||||||
|
#include "hostname-util.h"
|
||||||
|
#include "import-common.h"
|
||||||
|
#include "import-util.h"
|
||||||
|
#include "machine-image.h"
|
||||||
|
#include "mkdir.h"
|
||||||
|
#include "ratelimit.h"
|
||||||
|
#include "rm-rf.h"
|
||||||
|
#include "string-util.h"
|
||||||
|
#include "verbs.h"
|
||||||
|
#include "parse-util.h"
|
||||||
|
|
||||||
|
static bool arg_force = false;
|
||||||
|
static bool arg_read_only = false;
|
||||||
|
static const char *arg_image_root = "/var/lib/machines";
|
||||||
|
|
||||||
|
typedef struct ProgressInfo {
|
||||||
|
RateLimit limit;
|
||||||
|
char *path;
|
||||||
|
uint64_t size;
|
||||||
|
bool started;
|
||||||
|
bool logged_incomplete;
|
||||||
|
} ProgressInfo;
|
||||||
|
|
||||||
|
static volatile sig_atomic_t cancelled = false;
|
||||||
|
|
||||||
|
static void sigterm_sigint(int sig) {
|
||||||
|
cancelled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void progress_info_free(ProgressInfo *p) {
|
||||||
|
free(p->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void progress_show(ProgressInfo *p) {
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
/* Show progress only every now and then. */
|
||||||
|
if (!ratelimit_below(&p->limit))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Suppress the first message, start with the second one */
|
||||||
|
if (!p->started) {
|
||||||
|
p->started = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mention the list is incomplete before showing first output. */
|
||||||
|
if (!p->logged_incomplete) {
|
||||||
|
log_notice("(Note, file list shown below is incomplete, and is intended as sporadic progress report only.)");
|
||||||
|
p->logged_incomplete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p->size == 0)
|
||||||
|
log_info("Copying tree, currently at '%s'...", p->path);
|
||||||
|
else {
|
||||||
|
char buffer[FORMAT_BYTES_MAX];
|
||||||
|
|
||||||
|
log_info("Copying tree, currently at '%s' (@%s)...", p->path, format_bytes(buffer, sizeof(buffer), p->size));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int progress_path(const char *path, const struct stat *st, void *userdata) {
|
||||||
|
ProgressInfo *p = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
|
||||||
|
if (cancelled)
|
||||||
|
return -EOWNERDEAD;
|
||||||
|
|
||||||
|
r = free_and_strdup(&p->path, path);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
p->size = 0;
|
||||||
|
|
||||||
|
progress_show(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int progress_bytes(uint64_t nbytes, void *userdata) {
|
||||||
|
ProgressInfo *p = userdata;
|
||||||
|
|
||||||
|
assert(p);
|
||||||
|
assert(p->size != UINT64_MAX);
|
||||||
|
|
||||||
|
if (cancelled)
|
||||||
|
return -EOWNERDEAD;
|
||||||
|
|
||||||
|
p->size += nbytes;
|
||||||
|
|
||||||
|
progress_show(p);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int import_fs(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(rm_rf_subvolume_and_freep) char *temp_path = NULL;
|
||||||
|
_cleanup_(progress_info_free) ProgressInfo progress = {};
|
||||||
|
const char *path = NULL, *local = NULL, *final_path;
|
||||||
|
_cleanup_close_ int open_fd = -1;
|
||||||
|
struct sigaction old_sigint_sa, old_sigterm_sa;
|
||||||
|
static const struct sigaction sa = {
|
||||||
|
.sa_handler = sigterm_sigint,
|
||||||
|
.sa_flags = SA_RESTART,
|
||||||
|
};
|
||||||
|
int r, fd;
|
||||||
|
|
||||||
|
if (argc >= 2)
|
||||||
|
path = argv[1];
|
||||||
|
if (isempty(path) || streq(path, "-"))
|
||||||
|
path = NULL;
|
||||||
|
|
||||||
|
if (argc >= 3)
|
||||||
|
local = argv[2];
|
||||||
|
else if (path)
|
||||||
|
local = basename(path);
|
||||||
|
if (isempty(local) || streq(local, "-"))
|
||||||
|
local = NULL;
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
if (!machine_name_is_valid(local)) {
|
||||||
|
log_error("Local image name '%s' is not valid.", local);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!arg_force) {
|
||||||
|
r = image_find(IMAGE_MACHINE, local, NULL);
|
||||||
|
if (r < 0) {
|
||||||
|
if (r != -ENOENT)
|
||||||
|
return log_error_errno(r, "Failed to check whether image '%s' exists: %m", local);
|
||||||
|
} else {
|
||||||
|
log_error("Image '%s' already exists.", local);
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
local = "imported";
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
open_fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
|
||||||
|
if (open_fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open directory to import: %m");
|
||||||
|
|
||||||
|
fd = open_fd;
|
||||||
|
|
||||||
|
log_info("Importing '%s', saving as '%s'.", path, local);
|
||||||
|
} else {
|
||||||
|
_cleanup_free_ char *pretty = NULL;
|
||||||
|
|
||||||
|
fd = STDIN_FILENO;
|
||||||
|
|
||||||
|
(void) fd_get_path(fd, &pretty);
|
||||||
|
log_info("Importing '%s', saving as '%s'.", strempty(pretty), local);
|
||||||
|
}
|
||||||
|
|
||||||
|
final_path = strjoina(arg_image_root, "/", local);
|
||||||
|
|
||||||
|
r = tempfn_random(final_path, NULL, &temp_path);
|
||||||
|
if (r < 0)
|
||||||
|
return log_oom();
|
||||||
|
|
||||||
|
(void) mkdir_parents_label(temp_path, 0700);
|
||||||
|
|
||||||
|
RATELIMIT_INIT(progress.limit, 200*USEC_PER_MSEC, 1);
|
||||||
|
|
||||||
|
/* Hook into SIGINT/SIGTERM, so that we can cancel things then */
|
||||||
|
assert(sigaction(SIGINT, &sa, &old_sigint_sa) >= 0);
|
||||||
|
assert(sigaction(SIGTERM, &sa, &old_sigterm_sa) >= 0);
|
||||||
|
|
||||||
|
r = btrfs_subvol_snapshot_fd_full(
|
||||||
|
fd,
|
||||||
|
temp_path,
|
||||||
|
BTRFS_SNAPSHOT_FALLBACK_COPY|BTRFS_SNAPSHOT_RECURSIVE|BTRFS_SNAPSHOT_FALLBACK_DIRECTORY|BTRFS_SNAPSHOT_QUOTA,
|
||||||
|
progress_path,
|
||||||
|
progress_bytes,
|
||||||
|
&progress);
|
||||||
|
if (r == -EOWNERDEAD) { /* SIGINT + SIGTERM cause this, see signal handler above */
|
||||||
|
log_error("Copy cancelled.");
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
if (r < 0) {
|
||||||
|
log_error_errno(r, "Failed to copy directory: %m");
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
(void) import_assign_pool_quota_and_warn(temp_path);
|
||||||
|
|
||||||
|
if (arg_read_only) {
|
||||||
|
r = import_make_read_only(temp_path);
|
||||||
|
if (r < 0) {
|
||||||
|
log_error_errno(r, "Failed to make directory read-only: %m");
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arg_force)
|
||||||
|
(void) rm_rf(final_path, REMOVE_ROOT|REMOVE_PHYSICAL|REMOVE_SUBVOLUME);
|
||||||
|
|
||||||
|
r = rename_noreplace(AT_FDCWD, temp_path, AT_FDCWD, final_path);
|
||||||
|
if (r < 0) {
|
||||||
|
log_error_errno(r, "Failed to move image into place: %m");
|
||||||
|
goto finish;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp_path = mfree(temp_path);
|
||||||
|
|
||||||
|
log_info("Exiting.");
|
||||||
|
|
||||||
|
finish:
|
||||||
|
/* Put old signal handlers into place */
|
||||||
|
assert(sigaction(SIGINT, &old_sigint_sa, NULL) >= 0);
|
||||||
|
assert(sigaction(SIGTERM, &old_sigterm_sa, NULL) >= 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int help(int argc, char *argv[], void *userdata) {
|
||||||
|
|
||||||
|
printf("%s [OPTIONS...] {COMMAND} ...\n\n"
|
||||||
|
"Import container images from a file system.\n\n"
|
||||||
|
" -h --help Show this help\n"
|
||||||
|
" --version Show package version\n"
|
||||||
|
" --force Force creation of image\n"
|
||||||
|
" --image-root=PATH Image root directory\n"
|
||||||
|
" --read-only Create a read-only image\n\n"
|
||||||
|
"Commands:\n"
|
||||||
|
" run DIRECTORY [NAME] Import a directory\n",
|
||||||
|
program_invocation_short_name);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int parse_argv(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ARG_VERSION = 0x100,
|
||||||
|
ARG_FORCE,
|
||||||
|
ARG_IMAGE_ROOT,
|
||||||
|
ARG_READ_ONLY,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct option options[] = {
|
||||||
|
{ "help", no_argument, NULL, 'h' },
|
||||||
|
{ "version", no_argument, NULL, ARG_VERSION },
|
||||||
|
{ "force", no_argument, NULL, ARG_FORCE },
|
||||||
|
{ "image-root", required_argument, NULL, ARG_IMAGE_ROOT },
|
||||||
|
{ "read-only", no_argument, NULL, ARG_READ_ONLY },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
int c;
|
||||||
|
|
||||||
|
assert(argc >= 0);
|
||||||
|
assert(argv);
|
||||||
|
|
||||||
|
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0)
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
return help(0, NULL, NULL);
|
||||||
|
|
||||||
|
case ARG_VERSION:
|
||||||
|
return version();
|
||||||
|
|
||||||
|
case ARG_FORCE:
|
||||||
|
arg_force = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_IMAGE_ROOT:
|
||||||
|
arg_image_root = optarg;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ARG_READ_ONLY:
|
||||||
|
arg_read_only = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '?':
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert_not_reached("Unhandled option");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int import_fs_main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
static const Verb verbs[] = {
|
||||||
|
{ "help", VERB_ANY, VERB_ANY, 0, help },
|
||||||
|
{ "run", 2, 3, 0, import_fs },
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
return dispatch_verb(argc, argv, verbs, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
int r;
|
||||||
|
|
||||||
|
setlocale(LC_ALL, "");
|
||||||
|
log_parse_environment();
|
||||||
|
log_open();
|
||||||
|
|
||||||
|
r = parse_argv(argc, argv);
|
||||||
|
if (r <= 0)
|
||||||
|
goto finish;
|
||||||
|
|
||||||
|
r = import_fs_main(argc, argv);
|
||||||
|
|
||||||
|
finish:
|
||||||
|
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
#include "bus-util.h"
|
#include "bus-util.h"
|
||||||
#include "def.h"
|
#include "def.h"
|
||||||
#include "fd-util.h"
|
#include "fd-util.h"
|
||||||
|
#include "float.h"
|
||||||
#include "hostname-util.h"
|
#include "hostname-util.h"
|
||||||
#include "import-util.h"
|
#include "import-util.h"
|
||||||
#include "machine-pool.h"
|
#include "machine-pool.h"
|
||||||
|
@ -34,6 +35,7 @@ typedef struct Manager Manager;
|
||||||
typedef enum TransferType {
|
typedef enum TransferType {
|
||||||
TRANSFER_IMPORT_TAR,
|
TRANSFER_IMPORT_TAR,
|
||||||
TRANSFER_IMPORT_RAW,
|
TRANSFER_IMPORT_RAW,
|
||||||
|
TRANSFER_IMPORT_FS,
|
||||||
TRANSFER_EXPORT_TAR,
|
TRANSFER_EXPORT_TAR,
|
||||||
TRANSFER_EXPORT_RAW,
|
TRANSFER_EXPORT_RAW,
|
||||||
TRANSFER_PULL_TAR,
|
TRANSFER_PULL_TAR,
|
||||||
|
@ -94,6 +96,7 @@ struct Manager {
|
||||||
static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
|
static const char* const transfer_type_table[_TRANSFER_TYPE_MAX] = {
|
||||||
[TRANSFER_IMPORT_TAR] = "import-tar",
|
[TRANSFER_IMPORT_TAR] = "import-tar",
|
||||||
[TRANSFER_IMPORT_RAW] = "import-raw",
|
[TRANSFER_IMPORT_RAW] = "import-raw",
|
||||||
|
[TRANSFER_IMPORT_FS] = "import-fs",
|
||||||
[TRANSFER_EXPORT_TAR] = "export-tar",
|
[TRANSFER_EXPORT_TAR] = "export-tar",
|
||||||
[TRANSFER_EXPORT_RAW] = "export-raw",
|
[TRANSFER_EXPORT_RAW] = "export-raw",
|
||||||
[TRANSFER_PULL_TAR] = "pull-tar",
|
[TRANSFER_PULL_TAR] = "pull-tar",
|
||||||
|
@ -156,6 +159,7 @@ static int transfer_new(Manager *m, Transfer **ret) {
|
||||||
.stdin_fd = -1,
|
.stdin_fd = -1,
|
||||||
.stdout_fd = -1,
|
.stdout_fd = -1,
|
||||||
.verify = _IMPORT_VERIFY_INVALID,
|
.verify = _IMPORT_VERIFY_INVALID,
|
||||||
|
.progress_percent= (unsigned) -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
id = m->current_transfer_id + 1;
|
id = m->current_transfer_id + 1;
|
||||||
|
@ -177,6 +181,15 @@ static int transfer_new(Manager *m, Transfer **ret) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double transfer_percent_as_double(Transfer *t) {
|
||||||
|
assert(t);
|
||||||
|
|
||||||
|
if (t->progress_percent == (unsigned) -1)
|
||||||
|
return -DBL_MAX;
|
||||||
|
|
||||||
|
return (double) t->progress_percent / 100.0;
|
||||||
|
}
|
||||||
|
|
||||||
static void transfer_send_log_line(Transfer *t, const char *line) {
|
static void transfer_send_log_line(Transfer *t, const char *line) {
|
||||||
int r, priority = LOG_INFO;
|
int r, priority = LOG_INFO;
|
||||||
|
|
||||||
|
@ -357,7 +370,7 @@ static int transfer_start(Transfer *t) {
|
||||||
return r;
|
return r;
|
||||||
if (r == 0) {
|
if (r == 0) {
|
||||||
const char *cmd[] = {
|
const char *cmd[] = {
|
||||||
NULL, /* systemd-import, systemd-export or systemd-pull */
|
NULL, /* systemd-import, systemd-import-fs, systemd-export or systemd-pull */
|
||||||
NULL, /* tar, raw */
|
NULL, /* tar, raw */
|
||||||
NULL, /* --verify= */
|
NULL, /* --verify= */
|
||||||
NULL, /* verify argument */
|
NULL, /* verify argument */
|
||||||
|
@ -390,17 +403,52 @@ static int transfer_start(Transfer *t) {
|
||||||
_exit(EXIT_FAILURE);
|
_exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_IMPORT_RAW))
|
switch (t->type) {
|
||||||
cmd[k++] = SYSTEMD_IMPORT_PATH;
|
|
||||||
else if (IN_SET(t->type, TRANSFER_EXPORT_TAR, TRANSFER_EXPORT_RAW))
|
|
||||||
cmd[k++] = SYSTEMD_EXPORT_PATH;
|
|
||||||
else
|
|
||||||
cmd[k++] = SYSTEMD_PULL_PATH;
|
|
||||||
|
|
||||||
if (IN_SET(t->type, TRANSFER_IMPORT_TAR, TRANSFER_EXPORT_TAR, TRANSFER_PULL_TAR))
|
case TRANSFER_IMPORT_TAR:
|
||||||
|
case TRANSFER_IMPORT_RAW:
|
||||||
|
cmd[k++] = SYSTEMD_IMPORT_PATH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TRANSFER_IMPORT_FS:
|
||||||
|
cmd[k++] = SYSTEMD_IMPORT_FS_PATH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TRANSFER_EXPORT_TAR:
|
||||||
|
case TRANSFER_EXPORT_RAW:
|
||||||
|
cmd[k++] = SYSTEMD_EXPORT_PATH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TRANSFER_PULL_TAR:
|
||||||
|
case TRANSFER_PULL_RAW:
|
||||||
|
cmd[k++] = SYSTEMD_PULL_PATH;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assert_not_reached("Unexpected transfer type");
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (t->type) {
|
||||||
|
|
||||||
|
case TRANSFER_IMPORT_TAR:
|
||||||
|
case TRANSFER_EXPORT_TAR:
|
||||||
|
case TRANSFER_PULL_TAR:
|
||||||
cmd[k++] = "tar";
|
cmd[k++] = "tar";
|
||||||
else
|
break;
|
||||||
|
|
||||||
|
case TRANSFER_IMPORT_RAW:
|
||||||
|
case TRANSFER_EXPORT_RAW:
|
||||||
|
case TRANSFER_PULL_RAW:
|
||||||
cmd[k++] = "raw";
|
cmd[k++] = "raw";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TRANSFER_IMPORT_FS:
|
||||||
|
cmd[k++] = "run";
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (t->verify != _IMPORT_VERIFY_INVALID) {
|
if (t->verify != _IMPORT_VERIFY_INVALID) {
|
||||||
cmd[k++] = "--verify";
|
cmd[k++] = "--verify";
|
||||||
|
@ -704,6 +752,68 @@ static int method_import_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_
|
||||||
return sd_bus_reply_method_return(msg, "uo", id, object);
|
return sd_bus_reply_method_return(msg, "uo", id, object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int method_import_fs(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
|
||||||
|
_cleanup_(transfer_unrefp) Transfer *t = NULL;
|
||||||
|
int fd, force, read_only, r;
|
||||||
|
const char *local, *object;
|
||||||
|
Manager *m = userdata;
|
||||||
|
uint32_t id;
|
||||||
|
|
||||||
|
assert(msg);
|
||||||
|
assert(m);
|
||||||
|
|
||||||
|
r = bus_verify_polkit_async(
|
||||||
|
msg,
|
||||||
|
CAP_SYS_ADMIN,
|
||||||
|
"org.freedesktop.import1.import",
|
||||||
|
NULL,
|
||||||
|
false,
|
||||||
|
UID_INVALID,
|
||||||
|
&m->polkit_registry,
|
||||||
|
error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
if (r == 0)
|
||||||
|
return 1; /* Will call us back */
|
||||||
|
|
||||||
|
r = sd_bus_message_read(msg, "hsbb", &fd, &local, &force, &read_only);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
if (!machine_name_is_valid(local))
|
||||||
|
return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Local name %s is invalid", local);
|
||||||
|
|
||||||
|
r = setup_machine_directory((uint64_t) -1, error);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = transfer_new(m, &t);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
t->type = TRANSFER_IMPORT_FS;
|
||||||
|
t->force_local = force;
|
||||||
|
t->read_only = read_only;
|
||||||
|
|
||||||
|
t->local = strdup(local);
|
||||||
|
if (!t->local)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
t->stdin_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
|
||||||
|
if (t->stdin_fd < 0)
|
||||||
|
return -errno;
|
||||||
|
|
||||||
|
r = transfer_start(t);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
object = t->object_path;
|
||||||
|
id = t->id;
|
||||||
|
t = NULL;
|
||||||
|
|
||||||
|
return sd_bus_reply_method_return(msg, "uo", id, object);
|
||||||
|
}
|
||||||
|
|
||||||
static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
|
static int method_export_tar_or_raw(sd_bus_message *msg, void *userdata, sd_bus_error *error) {
|
||||||
_cleanup_(transfer_unrefp) Transfer *t = NULL;
|
_cleanup_(transfer_unrefp) Transfer *t = NULL;
|
||||||
int fd, r;
|
int fd, r;
|
||||||
|
@ -879,7 +989,7 @@ static int method_list_transfers(sd_bus_message *msg, void *userdata, sd_bus_err
|
||||||
transfer_type_to_string(t->type),
|
transfer_type_to_string(t->type),
|
||||||
t->remote,
|
t->remote,
|
||||||
t->local,
|
t->local,
|
||||||
(double) t->progress_percent / 100.0,
|
transfer_percent_as_double(t),
|
||||||
t->object_path);
|
t->object_path);
|
||||||
if (r < 0)
|
if (r < 0)
|
||||||
return r;
|
return r;
|
||||||
|
@ -975,7 +1085,7 @@ static int property_get_progress(
|
||||||
assert(reply);
|
assert(reply);
|
||||||
assert(t);
|
assert(t);
|
||||||
|
|
||||||
return sd_bus_message_append(reply, "d", (double) t->progress_percent / 100.0);
|
return sd_bus_message_append(reply, "d", transfer_percent_as_double(t));
|
||||||
}
|
}
|
||||||
|
|
||||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
|
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_type, transfer_type, TransferType);
|
||||||
|
@ -998,6 +1108,7 @@ static const sd_bus_vtable manager_vtable[] = {
|
||||||
SD_BUS_VTABLE_START(0),
|
SD_BUS_VTABLE_START(0),
|
||||||
SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
|
SD_BUS_METHOD("ImportTar", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
|
SD_BUS_METHOD("ImportRaw", "hsbb", "uo", method_import_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
SD_BUS_METHOD("ImportFileSystem", "hsbb", "uo", method_import_fs, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
|
SD_BUS_METHOD("ExportTar", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
|
SD_BUS_METHOD("ExportRaw", "shs", "uo", method_export_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
|
SD_BUS_METHOD("PullTar", "sssb", "uo", method_pull_tar_or_raw, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||||
|
|
|
@ -38,6 +38,12 @@ systemd_import_sources = files('''
|
||||||
qcow2-util.h
|
qcow2-util.h
|
||||||
'''.split())
|
'''.split())
|
||||||
|
|
||||||
|
systemd_import_fs_sources = files('''
|
||||||
|
import-fs.c
|
||||||
|
import-common.c
|
||||||
|
import-common.h
|
||||||
|
'''.split())
|
||||||
|
|
||||||
systemd_export_sources = files('''
|
systemd_export_sources = files('''
|
||||||
export.c
|
export.c
|
||||||
export-tar.c
|
export-tar.c
|
||||||
|
|
|
@ -54,6 +54,10 @@
|
||||||
send_interface="org.freedesktop.import1.Manager"
|
send_interface="org.freedesktop.import1.Manager"
|
||||||
send_member="ImportRaw"/>
|
send_member="ImportRaw"/>
|
||||||
|
|
||||||
|
<allow send_destination="org.freedesktop.import1"
|
||||||
|
send_interface="org.freedesktop.import1.Manager"
|
||||||
|
send_member="ImportFileSystem"/>
|
||||||
|
|
||||||
<allow send_destination="org.freedesktop.import1"
|
<allow send_destination="org.freedesktop.import1"
|
||||||
send_interface="org.freedesktop.import1.Manager"
|
send_interface="org.freedesktop.import1.Manager"
|
||||||
send_member="ExportTar"/>
|
send_member="ExportTar"/>
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
|
#include <math.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -2133,6 +2134,66 @@ static int import_raw(int argc, char *argv[], void *userdata) {
|
||||||
return transfer_image_common(bus, m);
|
return transfer_image_common(bus, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int import_fs(int argc, char *argv[], void *userdata) {
|
||||||
|
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||||
|
_cleanup_close_ int fd = -1;
|
||||||
|
const char *local = NULL, *path = NULL;
|
||||||
|
sd_bus *bus = userdata;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
assert(bus);
|
||||||
|
|
||||||
|
if (argc >= 2)
|
||||||
|
path = argv[1];
|
||||||
|
if (isempty(path) || streq(path, "-"))
|
||||||
|
path = NULL;
|
||||||
|
|
||||||
|
if (argc >= 3)
|
||||||
|
local = argv[2];
|
||||||
|
else if (path)
|
||||||
|
local = basename(path);
|
||||||
|
if (isempty(local) || streq(local, "-"))
|
||||||
|
local = NULL;
|
||||||
|
|
||||||
|
if (!local) {
|
||||||
|
log_error("Need either path or local name.");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!machine_name_is_valid(local)) {
|
||||||
|
log_error("Local name %s is not a suitable machine name.", local);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path) {
|
||||||
|
fd = open(path, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
|
||||||
|
if (fd < 0)
|
||||||
|
return log_error_errno(errno, "Failed to open directory '%s': %m", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = sd_bus_message_new_method_call(
|
||||||
|
bus,
|
||||||
|
&m,
|
||||||
|
"org.freedesktop.import1",
|
||||||
|
"/org/freedesktop/import1",
|
||||||
|
"org.freedesktop.import1.Manager",
|
||||||
|
"ImportFileSystem");
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
r = sd_bus_message_append(
|
||||||
|
m,
|
||||||
|
"hsbb",
|
||||||
|
fd >= 0 ? fd : STDIN_FILENO,
|
||||||
|
local,
|
||||||
|
arg_force,
|
||||||
|
arg_read_only);
|
||||||
|
if (r < 0)
|
||||||
|
return bus_log_create_error(r);
|
||||||
|
|
||||||
|
return transfer_image_common(bus, m);
|
||||||
|
}
|
||||||
|
|
||||||
static void determine_compression_from_filename(const char *p) {
|
static void determine_compression_from_filename(const char *p) {
|
||||||
if (arg_format)
|
if (arg_format)
|
||||||
return;
|
return;
|
||||||
|
@ -2464,12 +2525,21 @@ static int list_transfers(int argc, char *argv[], void *userdata) {
|
||||||
(int) max_remote, "REMOTE");
|
(int) max_remote, "REMOTE");
|
||||||
|
|
||||||
for (j = 0; j < n_transfers; j++)
|
for (j = 0; j < n_transfers; j++)
|
||||||
printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
|
|
||||||
(int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
|
if (transfers[j].progress < 0)
|
||||||
(int) 6, (unsigned) (transfers[j].progress * 100),
|
printf("%*" PRIu32 " %*s %-*s %-*s %-*s\n",
|
||||||
(int) max_type, transfers[j].type,
|
(int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
|
||||||
(int) max_local, transfers[j].local,
|
(int) 7, "n/a",
|
||||||
(int) max_remote, transfers[j].remote);
|
(int) max_type, transfers[j].type,
|
||||||
|
(int) max_local, transfers[j].local,
|
||||||
|
(int) max_remote, transfers[j].remote);
|
||||||
|
else
|
||||||
|
printf("%*" PRIu32 " %*u%% %-*s %-*s %-*s\n",
|
||||||
|
(int) MAX(2U, DECIMAL_STR_WIDTH(max_id)), transfers[j].id,
|
||||||
|
(int) 6, (unsigned) (transfers[j].progress * 100),
|
||||||
|
(int) max_type, transfers[j].type,
|
||||||
|
(int) max_local, transfers[j].local,
|
||||||
|
(int) max_remote, transfers[j].remote);
|
||||||
|
|
||||||
if (arg_legend) {
|
if (arg_legend) {
|
||||||
if (n_transfers > 0)
|
if (n_transfers > 0)
|
||||||
|
@ -2687,6 +2757,7 @@ static int help(int argc, char *argv[], void *userdata) {
|
||||||
" pull-raw URL [NAME] Download a RAW container or VM 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"
|
" import-tar FILE [NAME] Import a local TAR container image\n"
|
||||||
" import-raw FILE [NAME] Import a local RAW container or VM image\n"
|
" import-raw FILE [NAME] Import a local RAW container or VM image\n"
|
||||||
|
" import-fs DIRECTORY [NAME] Import a local directory container image\n"
|
||||||
" export-tar NAME [FILE] Export a TAR container image locally\n"
|
" export-tar NAME [FILE] Export a TAR container image locally\n"
|
||||||
" export-raw NAME [FILE] Export a RAW container or VM image locally\n"
|
" export-raw NAME [FILE] Export a RAW container or VM image locally\n"
|
||||||
" list-transfers Show list of downloads in progress\n"
|
" list-transfers Show list of downloads in progress\n"
|
||||||
|
@ -3008,6 +3079,7 @@ static int machinectl_main(int argc, char *argv[], sd_bus *bus) {
|
||||||
{ "disable", 2, VERB_ANY, 0, enable_machine },
|
{ "disable", 2, VERB_ANY, 0, enable_machine },
|
||||||
{ "import-tar", 2, 3, 0, import_tar },
|
{ "import-tar", 2, 3, 0, import_tar },
|
||||||
{ "import-raw", 2, 3, 0, import_raw },
|
{ "import-raw", 2, 3, 0, import_raw },
|
||||||
|
{ "import-fs", 2, 3, 0, import_fs },
|
||||||
{ "export-tar", 2, 3, 0, export_tar },
|
{ "export-tar", 2, 3, 0, export_tar },
|
||||||
{ "export-raw", 2, 3, 0, export_raw },
|
{ "export-raw", 2, 3, 0, export_raw },
|
||||||
{ "pull-tar", 2, 3, 0, pull_tar },
|
{ "pull-tar", 2, 3, 0, pull_tar },
|
||||||
|
|
Loading…
Reference in New Issue