#include "random-util.h"
#include "rm-rf.h"
#include "signal-util.h"
+#include "snapshot-util.h"
#include "socket-util.h"
#include "stat-util.h"
#include "stdio-util.h"
static char *arg_bind_user_shell = NULL;
static bool arg_bind_user_shell_copy = false;
static char **arg_bind_user_groups = NULL;
+static bool arg_ephemeral = false;
static RuntimeScope arg_runtime_scope = _RUNTIME_SCOPE_INVALID;
STATIC_DESTRUCTOR_REGISTER(arg_directory, freep);
" --system Interact with system manager\n"
"\n%3$sImage:%4$s\n"
" -D --directory=PATH Root directory for the VM\n"
+ " -x --ephemeral Run VM with snapshot of the disk or directory\n"
" -i --image=FILE|DEVICE Root file system disk image or device for the VM\n"
" --image-format=FORMAT Specify disk image format (raw, qcow2; default: raw)\n"
"\n%3$sHost Configuration:%4$s\n"
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
{ "image", required_argument, NULL, 'i' },
{ "image-format", required_argument, NULL, ARG_IMAGE_FORMAT },
+ { "ephemeral", no_argument, NULL, 'x' },
{ "directory", required_argument, NULL, 'D' },
{ "machine", required_argument, NULL, 'M' },
{ "slice", required_argument, NULL, 'S' },
assert(argv);
optind = 0;
- while ((c = getopt_long(argc, argv, "+hD:i:M:nqs:G:S:", options, NULL)) >= 0)
+ while ((c = getopt_long(argc, argv, "+hD:i:xM:nqs:G:S:", options, NULL)) >= 0)
switch (c) {
case 'h':
return help();
}
break;
+ case 'x':
+ arg_ephemeral = true;
+ break;
+
case ARG_NO_PAGER:
arg_pager_flags |= PAGER_DISABLE;
break;
if (!strv_isempty(arg_bind_user_groups) && strv_isempty(arg_bind_user))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --bind-user-group= without --bind-user=");
+ if (arg_ephemeral && arg_extra_drives.n_drives > 0)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Cannot use --ephemeral with --extra-drive=");
+
if (argc > optind) {
arg_kernel_cmdline_extra = strv_copy(argv + optind);
if (!arg_kernel_cmdline_extra)
_cleanup_(ovmf_config_freep) OvmfConfig *ovmf_config = NULL;
_cleanup_free_ char *qemu_binary = NULL, *mem = NULL, *kernel = NULL;
_cleanup_(rm_rf_physical_and_freep) char *ssh_private_key_path = NULL, *ssh_public_key_path = NULL;
+ _cleanup_(rm_rf_subvolume_and_freep) char *snapshot_directory = NULL;
+ _cleanup_(release_lock_file) LockFile tree_global_lock = LOCK_FILE_INIT, tree_local_lock = LOCK_FILE_INIT;
_cleanup_close_ int notify_sock_fd = -EBADF;
_cleanup_strv_free_ char **cmdline = NULL;
_cleanup_free_ int *pass_fds = NULL;
if (!escaped_image)
return log_oom();
- if (strv_extendf(&cmdline, "if=none,id=vmspawn,file=%s,format=%s,discard=%s", escaped_image, image_format_to_string(arg_image_format), on_off(arg_discard_disk)) < 0)
+ if (strv_extendf(&cmdline, "if=none,id=vmspawn,file=%s,format=%s,discard=%s,snapshot=%s",
+ escaped_image, image_format_to_string(arg_image_format), on_off(arg_discard_disk), on_off(arg_ephemeral)) < 0)
return log_oom();
_cleanup_free_ char *image_fn = NULL;
if (!GREEDY_REALLOC(children, n_children + 1))
return log_oom();
+ if (arg_ephemeral) {
+ r = create_ephemeral_snapshot(arg_directory,
+ arg_runtime_scope,
+ /* read-only */ false,
+ &tree_global_lock,
+ &tree_local_lock,
+ &snapshot_directory);
+ if (r < 0)
+ return r;
+
+ arg_directory = strdup(snapshot_directory);
+ if (!arg_directory)
+ return log_oom();
+ }
+
r = start_virtiofsd(
unit,
arg_directory,
if (r < 0)
return log_error_errno(r, "Failed to extract file name from '%s': %m", arg_directory);
}
+ /* Add a random suffix when this is an ephemeral machine, so that we can run many
+ * instances at once without manually having to specify -M each time. */
+ if (arg_ephemeral)
+ if (strextendf(&arg_machine, "-%016" PRIx64, random_u64()) < 0)
+ return log_oom();
hostname_cleanup(arg_machine);
if (!hostname_is_valid(arg_machine, 0))