mirror of
https://github.com/systemd/systemd.git
synced 2025-03-08 08:58:27 +03:00
Merge pull request #31023 from poettering/vmspawn-work
vmspawn: make it work on current fedora
This commit is contained in:
commit
afd08d7740
@ -135,7 +135,18 @@
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v255"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>--firmware=</option><replaceable>PATH</replaceable></term>
|
||||
|
||||
<listitem><para>Takes an absolute path, or a relative path beginning with
|
||||
<filename>./</filename>. Specifies a JSON firmware definition file, which allows selecting the
|
||||
firmware to boot in the VM. If not specified a suitable firmware is automatically discovered. If the
|
||||
special string <literal>list</literal> is specified lists all discovered firmwares.</para>
|
||||
|
||||
<xi:include href="version-info.xml" xpointer="v256"/></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
|
||||
</refsect2><refsect2>
|
||||
<title>System Identity Options</title>
|
||||
|
@ -151,3 +151,20 @@ int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handle
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int event_add_child_pidref(
|
||||
sd_event *e,
|
||||
sd_event_source **s,
|
||||
const PidRef *pid,
|
||||
int options,
|
||||
sd_event_child_handler_t callback,
|
||||
void *userdata) {
|
||||
|
||||
if (!pidref_is_set(pid))
|
||||
return -ESRCH;
|
||||
|
||||
if (pid->fd >= 0)
|
||||
return sd_event_add_child_pidfd(e, s, pid->fd, options, callback, userdata);
|
||||
|
||||
return sd_event_add_child(e, s, pid->pid, options, callback, userdata);
|
||||
}
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include "sd-event.h"
|
||||
|
||||
#include "pidref.h"
|
||||
|
||||
int event_reset_time(
|
||||
sd_event *e,
|
||||
sd_event_source **s,
|
||||
@ -32,3 +34,5 @@ static inline int event_source_disable(sd_event_source *s) {
|
||||
}
|
||||
|
||||
int event_add_time_change(sd_event *e, sd_event_source **ret, sd_event_io_handler_t callback, void *userdata);
|
||||
|
||||
int event_add_child_pidref(sd_event *e, sd_event_source **s, const PidRef *pid, int options, sd_event_child_handler_t callback, void *userdata);
|
||||
|
@ -5943,9 +5943,16 @@ static int run(int argc, char *argv[]) {
|
||||
if (arg_console_mode == CONSOLE_PIPE) /* if we pass STDERR on to the container, don't add our own logs into it too */
|
||||
arg_quiet = true;
|
||||
|
||||
if (!arg_quiet)
|
||||
log_info("Spawning container %s on %s.\nPress Ctrl-] three times within 1s to kill container.",
|
||||
arg_machine, arg_image ?: arg_directory);
|
||||
if (!arg_quiet) {
|
||||
const char *t = arg_image ?: arg_directory;
|
||||
_cleanup_free_ char *u = NULL;
|
||||
(void) terminal_urlify_path(t, t, &u);
|
||||
|
||||
log_info("%s %sSpawning container %s on %s.%s\n"
|
||||
"%s %sPress %sCtrl-]%s three times within 1s to kill container.%s",
|
||||
special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), ansi_grey(), arg_machine, u ?: t, ansi_normal(),
|
||||
special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), ansi_grey(), ansi_highlight(), ansi_grey(), ansi_normal());
|
||||
}
|
||||
|
||||
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, SIGWINCH, SIGTERM, SIGINT, SIGRTMIN+18, -1) >= 0);
|
||||
|
||||
|
@ -29,7 +29,9 @@ OvmfConfig* ovmf_config_free(OvmfConfig *config) {
|
||||
return NULL;
|
||||
|
||||
free(config->path);
|
||||
free(config->format);
|
||||
free(config->vars);
|
||||
free(config->vars_format);
|
||||
return mfree(config);
|
||||
}
|
||||
|
||||
@ -40,7 +42,7 @@ int qemu_check_kvm_support(void) {
|
||||
log_debug_errno(errno, "/dev/kvm not found. Not using KVM acceleration.");
|
||||
return false;
|
||||
}
|
||||
if (errno == EPERM) {
|
||||
if (ERRNO_IS_PRIVILEGE(errno)) {
|
||||
log_debug_errno(errno, "Permission denied to access /dev/kvm. Not using KVM acceleration.");
|
||||
return false;
|
||||
}
|
||||
@ -62,11 +64,11 @@ int qemu_check_vsock_support(void) {
|
||||
fd = open("/dev/vhost-vsock", O_RDWR|O_CLOEXEC);
|
||||
if (fd >= 0)
|
||||
return true;
|
||||
if (errno == ENODEV) {
|
||||
if (ERRNO_IS_DEVICE_ABSENT(errno)) {
|
||||
log_debug_errno(errno, "/dev/vhost-vsock device doesn't exist. Not adding a vsock device to the virtual machine.");
|
||||
return false;
|
||||
}
|
||||
if (errno == EPERM) {
|
||||
if (ERRNO_IS_PRIVILEGE(errno)) {
|
||||
log_debug_errno(errno, "Permission denied to access /dev/vhost-vsock. Not adding a vsock device to the virtual machine.");
|
||||
return false;
|
||||
}
|
||||
@ -78,16 +80,26 @@ int qemu_check_vsock_support(void) {
|
||||
typedef struct FirmwareData {
|
||||
char **features;
|
||||
char *firmware;
|
||||
char *firmware_format;
|
||||
char *vars;
|
||||
char *vars_format;
|
||||
} FirmwareData;
|
||||
|
||||
static bool firmware_data_supports_sb(const FirmwareData *fwd) {
|
||||
assert(fwd);
|
||||
|
||||
return strv_contains(fwd->features, "secure-boot");
|
||||
}
|
||||
|
||||
static FirmwareData* firmware_data_free(FirmwareData *fwd) {
|
||||
if (!fwd)
|
||||
return NULL;
|
||||
|
||||
fwd->features = strv_free(fwd->features);
|
||||
fwd->firmware = mfree(fwd->firmware);
|
||||
fwd->vars = mfree(fwd->vars);
|
||||
strv_free(fwd->features);
|
||||
free(fwd->firmware);
|
||||
free(fwd->firmware_format);
|
||||
free(fwd->vars);
|
||||
free(fwd->vars_format);
|
||||
|
||||
return mfree(fwd);
|
||||
}
|
||||
@ -95,8 +107,8 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(FirmwareData*, firmware_data_free);
|
||||
|
||||
static int firmware_executable(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
|
||||
static const JsonDispatch table[] = {
|
||||
{ "filename", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, firmware), JSON_MANDATORY },
|
||||
{ "format", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
|
||||
{ "filename", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, firmware), JSON_MANDATORY },
|
||||
{ "format", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, firmware_format), JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -105,8 +117,8 @@ static int firmware_executable(const char *name, JsonVariant *v, JsonDispatchFla
|
||||
|
||||
static int firmware_nvram_template(const char *name, JsonVariant *v, JsonDispatchFlags flags, void *userdata) {
|
||||
static const JsonDispatch table[] = {
|
||||
{ "filename", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, vars), JSON_MANDATORY },
|
||||
{ "format", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
|
||||
{ "filename", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, vars), JSON_MANDATORY },
|
||||
{ "format", JSON_VARIANT_STRING, json_dispatch_string, offsetof(FirmwareData, vars_format), JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -124,12 +136,136 @@ static int firmware_mapping(const char *name, JsonVariant *v, JsonDispatchFlags
|
||||
return json_dispatch(v, table, flags, userdata);
|
||||
}
|
||||
|
||||
static int get_firmware_search_dirs(char ***ret) {
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
/* Search in:
|
||||
* - $XDG_CONFIG_HOME/qemu/firmware
|
||||
* - /etc/qemu/firmware
|
||||
* - /usr/share/qemu/firmware
|
||||
*
|
||||
* Prioritising entries in "more specific" directories */
|
||||
|
||||
_cleanup_free_ char *user_firmware_dir = NULL;
|
||||
r = xdg_user_config_dir(&user_firmware_dir, "/qemu/firmware");
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
l = strv_new(user_firmware_dir, "/etc/qemu/firmware", "/usr/share/qemu/firmware");
|
||||
if (!l)
|
||||
return log_oom_debug();
|
||||
|
||||
*ret = TAKE_PTR(l);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int list_ovmf_config(char ***ret) {
|
||||
_cleanup_strv_free_ char **search_dirs = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
r = get_firmware_search_dirs(&search_dirs);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = conf_files_list_strv(
|
||||
ret,
|
||||
".json",
|
||||
/* root= */ NULL,
|
||||
CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR,
|
||||
(const char *const*) search_dirs);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to list firmware files: %m");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_firmware_data(const char *path, FirmwareData **ret) {
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(ret);
|
||||
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *json = NULL;
|
||||
r = json_parse_file(
|
||||
/* f= */ NULL,
|
||||
path,
|
||||
/* flags= */ 0,
|
||||
&json,
|
||||
/* ret_line= */ NULL,
|
||||
/* ret_column= */ NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
static const JsonDispatch table[] = {
|
||||
{ "description", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
|
||||
{ "interface-types", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
|
||||
{ "mapping", JSON_VARIANT_OBJECT, firmware_mapping, 0, JSON_MANDATORY },
|
||||
{ "targets", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
|
||||
{ "features", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(FirmwareData, features), JSON_MANDATORY },
|
||||
{ "tags", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
_cleanup_(firmware_data_freep) FirmwareData *fwd = NULL;
|
||||
fwd = new0(FirmwareData, 1);
|
||||
if (!fwd)
|
||||
return -ENOMEM;
|
||||
|
||||
r = json_dispatch(json, table, JSON_ALLOW_EXTENSIONS, fwd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(fwd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ovmf_config_make(FirmwareData *fwd, OvmfConfig **ret) {
|
||||
assert(fwd);
|
||||
assert(ret);
|
||||
|
||||
_cleanup_free_ OvmfConfig *config = NULL;
|
||||
config = new(OvmfConfig, 1);
|
||||
if (!config)
|
||||
return -ENOMEM;
|
||||
|
||||
*config = (OvmfConfig) {
|
||||
.path = TAKE_PTR(fwd->firmware),
|
||||
.format = TAKE_PTR(fwd->firmware_format),
|
||||
.vars = TAKE_PTR(fwd->vars),
|
||||
.vars_format = TAKE_PTR(fwd->vars_format),
|
||||
.supports_sb = firmware_data_supports_sb(fwd),
|
||||
};
|
||||
|
||||
*ret = TAKE_PTR(config);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int load_ovmf_config(const char *path, OvmfConfig **ret) {
|
||||
_cleanup_(firmware_data_freep) FirmwareData *fwd = NULL;
|
||||
int r;
|
||||
|
||||
assert(path);
|
||||
assert(ret);
|
||||
|
||||
r = load_firmware_data(path, &fwd);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return ovmf_config_make(fwd, ret);
|
||||
}
|
||||
|
||||
int find_ovmf_config(int search_sb, OvmfConfig **ret) {
|
||||
_cleanup_(ovmf_config_freep) OvmfConfig *config = NULL;
|
||||
_cleanup_free_ char *user_firmware_dir = NULL;
|
||||
_cleanup_strv_free_ char **conf_files = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
/* Search in:
|
||||
* - $XDG_CONFIG_HOME/qemu/firmware
|
||||
* - /etc/qemu/firmware
|
||||
@ -138,79 +274,35 @@ int find_ovmf_config(int search_sb, OvmfConfig **ret) {
|
||||
* Prioritising entries in "more specific" directories
|
||||
*/
|
||||
|
||||
r = xdg_user_config_dir(&user_firmware_dir, "/qemu/firmware");
|
||||
r = list_ovmf_config(&conf_files);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = conf_files_list_strv(&conf_files, ".json", NULL, CONF_FILES_FILTER_MASKED|CONF_FILES_REGULAR,
|
||||
STRV_MAKE_CONST(user_firmware_dir, "/etc/qemu/firmware", "/usr/share/qemu/firmware"));
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to list config files: %m");
|
||||
|
||||
STRV_FOREACH(file, conf_files) {
|
||||
_cleanup_(firmware_data_freep) FirmwareData *fwd = NULL;
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *config_json = NULL;
|
||||
_cleanup_free_ char *contents = NULL;
|
||||
size_t contents_sz = 0;
|
||||
|
||||
r = read_full_file(*file, &contents, &contents_sz);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
r = load_firmware_data(*file, &fwd);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to read contents of %s - ignoring: %m", *file);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = json_parse(contents, 0, &config_json, NULL, NULL);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to parse the JSON in %s - ignoring: %m", *file);
|
||||
continue;
|
||||
}
|
||||
|
||||
static const JsonDispatch table[] = {
|
||||
{ "description", JSON_VARIANT_STRING, NULL, 0, JSON_MANDATORY },
|
||||
{ "interface-types", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
|
||||
{ "mapping", JSON_VARIANT_OBJECT, firmware_mapping, 0, JSON_MANDATORY },
|
||||
{ "targets", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
|
||||
{ "features", JSON_VARIANT_ARRAY, json_dispatch_strv, offsetof(FirmwareData, features), JSON_MANDATORY },
|
||||
{ "tags", JSON_VARIANT_ARRAY, NULL, 0, JSON_MANDATORY },
|
||||
{}
|
||||
};
|
||||
|
||||
fwd = new0(FirmwareData, 1);
|
||||
if (!fwd)
|
||||
return -ENOMEM;
|
||||
|
||||
r = json_dispatch(config_json, table, JSON_ALLOW_EXTENSIONS, fwd);
|
||||
if (r == -ENOMEM)
|
||||
return r;
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to extract the required fields from the JSON in %s - ignoring: %m", *file);
|
||||
log_debug_errno(r, "Failed to load JSON file '%s', skipping: %m", *file);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strv_contains(fwd->features, "enrolled-keys")) {
|
||||
log_debug("Skipping %s, firmware has enrolled keys which has been known to cause issues", *file);
|
||||
log_debug("Skipping %s, firmware has enrolled keys which has been known to cause issues.", *file);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool sb_present = strv_contains(fwd->features, "secure-boot");
|
||||
|
||||
/* exclude firmware which doesn't match our Secure Boot requirements */
|
||||
if (search_sb >= 0 && search_sb != sb_present) {
|
||||
log_debug("Skipping %s, firmware doesn't fit required Secure Boot configuration", *file);
|
||||
if (search_sb >= 0 && !!search_sb != firmware_data_supports_sb(fwd)) {
|
||||
log_debug("Skipping %s, firmware doesn't fit required Secure Boot configuration.", *file);
|
||||
continue;
|
||||
}
|
||||
|
||||
config = new0(OvmfConfig, 1);
|
||||
if (!config)
|
||||
return -ENOMEM;
|
||||
r = ovmf_config_make(fwd, &config);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
config->path = TAKE_PTR(fwd->firmware);
|
||||
config->vars = TAKE_PTR(fwd->vars);
|
||||
config->supports_sb = sb_present;
|
||||
log_debug("Selected firmware definition %s.", *file);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -22,15 +22,27 @@
|
||||
|
||||
typedef struct OvmfConfig {
|
||||
char *path;
|
||||
char *format;
|
||||
char *vars;
|
||||
char *vars_format;
|
||||
bool supports_sb;
|
||||
} OvmfConfig;
|
||||
|
||||
static inline const char *ovmf_config_format(const OvmfConfig *c) {
|
||||
return ASSERT_PTR(c)->format ?: "raw";
|
||||
}
|
||||
|
||||
static inline const char *ovmf_config_vars_format(const OvmfConfig *c) {
|
||||
return ASSERT_PTR(c)->vars_format ?: "raw";
|
||||
}
|
||||
|
||||
OvmfConfig* ovmf_config_free(OvmfConfig *ovmf_config);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(OvmfConfig*, ovmf_config_free);
|
||||
|
||||
int qemu_check_kvm_support(void);
|
||||
int qemu_check_vsock_support(void);
|
||||
int find_ovmf_config(int search_sb, OvmfConfig **ret_ovmf_config);
|
||||
int list_ovmf_config(char ***ret);
|
||||
int load_ovmf_config(const char *path, OvmfConfig **ret);
|
||||
int find_ovmf_config(int search_sb, OvmfConfig **ret);
|
||||
int find_qemu_binary(char **ret_qemu_binary);
|
||||
int vsock_fix_child_cid(unsigned *machine_cid, const char *machine, int *ret_child_sock);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "copy.h"
|
||||
#include "creds-util.h"
|
||||
#include "escape.h"
|
||||
#include "event-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-util.h"
|
||||
#include "fs-util.h"
|
||||
@ -35,11 +36,12 @@
|
||||
#include "vmspawn-settings.h"
|
||||
#include "vmspawn-util.h"
|
||||
|
||||
static bool arg_quiet = false;
|
||||
static PagerFlags arg_pager_flags = 0;
|
||||
static char *arg_image = NULL;
|
||||
static char *arg_machine = NULL;
|
||||
static char *arg_qemu_smp = NULL;
|
||||
static uint64_t arg_qemu_mem = 2ULL * 1024ULL * 1024ULL * 1024ULL;
|
||||
static uint64_t arg_qemu_mem = UINT64_C(2) * U64_GB;
|
||||
static int arg_qemu_kvm = -1;
|
||||
static int arg_qemu_vsock = -1;
|
||||
static unsigned arg_vsock_cid = VMADDR_CID_ANY;
|
||||
@ -48,12 +50,14 @@ static int arg_secure_boot = -1;
|
||||
static MachineCredentialContext arg_credentials = {};
|
||||
static SettingsMask arg_settings_mask = 0;
|
||||
static char **arg_parameters = NULL;
|
||||
static char *arg_firmware = NULL;
|
||||
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_machine, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_qemu_smp, freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_parameters, strv_freep);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_credentials, machine_credential_context_done);
|
||||
STATIC_DESTRUCTOR_REGISTER(arg_firmware, freep);
|
||||
|
||||
static int help(void) {
|
||||
_cleanup_free_ char *link = NULL;
|
||||
@ -69,6 +73,7 @@ static int help(void) {
|
||||
"%5$sSpawn a command or OS in a virtual machine.%6$s\n\n"
|
||||
" -h --help Show this help\n"
|
||||
" --version Print version string\n"
|
||||
" -q --quiet Do not show status information\n"
|
||||
" --no-pager Do not pipe output into a pager\n"
|
||||
"\n%3$sImage:%4$s\n"
|
||||
" -i --image=PATH Root file system disk image (or device node) for\n"
|
||||
@ -82,6 +87,7 @@ static int help(void) {
|
||||
" --qemu-gui Start QEMU in graphical mode\n"
|
||||
" --secure-boot=BOOL Configure whether to search for firmware which\n"
|
||||
" supports Secure Boot\n"
|
||||
" --firmware=PATH|list Select firmware definition file (or list available)\n"
|
||||
"\n%3$sSystem Identity:%4$s\n"
|
||||
" -M --machine=NAME Set the machine name for the container\n"
|
||||
"\n%3$sCredentials:%4$s\n"
|
||||
@ -114,11 +120,13 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
ARG_SECURE_BOOT,
|
||||
ARG_SET_CREDENTIAL,
|
||||
ARG_LOAD_CREDENTIAL,
|
||||
ARG_FIRMWARE,
|
||||
};
|
||||
|
||||
static const struct option options[] = {
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "version", no_argument, NULL, ARG_VERSION },
|
||||
{ "quiet", no_argument, NULL, 'q' },
|
||||
{ "no-pager", no_argument, NULL, ARG_NO_PAGER },
|
||||
{ "image", required_argument, NULL, 'i' },
|
||||
{ "machine", required_argument, NULL, 'M' },
|
||||
@ -131,6 +139,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
{ "secure-boot", required_argument, NULL, ARG_SECURE_BOOT },
|
||||
{ "set-credential", required_argument, NULL, ARG_SET_CREDENTIAL },
|
||||
{ "load-credential", required_argument, NULL, ARG_LOAD_CREDENTIAL },
|
||||
{ "firmware", required_argument, NULL, ARG_FIRMWARE },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -140,7 +149,7 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
assert(argv);
|
||||
|
||||
optind = 0;
|
||||
while ((c = getopt_long(argc, argv, "+hi:M", options, NULL)) >= 0)
|
||||
while ((c = getopt_long(argc, argv, "+hi:Mq", options, NULL)) >= 0)
|
||||
switch (c) {
|
||||
case 'h':
|
||||
return help();
|
||||
@ -148,6 +157,10 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
case ARG_VERSION:
|
||||
return version();
|
||||
|
||||
case 'q':
|
||||
arg_quiet = true;
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_image);
|
||||
if (r < 0)
|
||||
@ -241,6 +254,31 @@ static int parse_argv(int argc, char *argv[]) {
|
||||
break;
|
||||
}
|
||||
|
||||
case ARG_FIRMWARE:
|
||||
if (streq(optarg, "list")) {
|
||||
_cleanup_strv_free_ char **l = NULL;
|
||||
|
||||
r = list_ovmf_config(&l);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to list firmwares: %m");
|
||||
|
||||
bool nl = false;
|
||||
fputstrv(stdout, l, "\n", &nl);
|
||||
if (nl)
|
||||
putchar('\n');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!isempty(optarg) && !path_is_absolute(optarg) && !startswith(optarg, "./"))
|
||||
return log_error_errno(SYNTHETIC_ERRNO(errno), "Absolute path or path starting with './' required.");
|
||||
|
||||
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_firmware);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
break;
|
||||
|
||||
case '?':
|
||||
return -EINVAL;
|
||||
|
||||
@ -378,25 +416,34 @@ static int vmspawn_dispatch_vsock_connections(sd_event_source *source, int fd, u
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_notify_parent(sd_event *event, int fd, int *exit_status, sd_event_source **notify_event_source) {
|
||||
static int setup_notify_parent(sd_event *event, int fd, int *exit_status, sd_event_source **ret_notify_event_source) {
|
||||
int r;
|
||||
|
||||
r = sd_event_add_io(event, notify_event_source, fd, EPOLLIN, vmspawn_dispatch_vsock_connections, exit_status);
|
||||
assert(event);
|
||||
assert(fd >= 0);
|
||||
assert(exit_status);
|
||||
assert(ret_notify_event_source);
|
||||
|
||||
r = sd_event_add_io(event, ret_notify_event_source, fd, EPOLLIN, vmspawn_dispatch_vsock_connections, exit_status);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to allocate notify socket event source: %m");
|
||||
|
||||
(void) sd_event_source_set_description(*notify_event_source, "vmspawn-notify-sock");
|
||||
(void) sd_event_source_set_description(*ret_notify_event_source, "vmspawn-notify-sock");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_orderly_shutdown(sd_event_source *s, const struct signalfd_siginfo *si, void *userdata) {
|
||||
pid_t pid;
|
||||
PidRef *pidref = userdata;
|
||||
int r;
|
||||
|
||||
pid = PTR_TO_PID(userdata);
|
||||
if (pid > 0) {
|
||||
/* TODO: actually talk to qemu and ask the guest to shutdown here */
|
||||
if (kill(pid, SIGKILL) >= 0) {
|
||||
/* TODO: actually talk to qemu and ask the guest to shutdown here */
|
||||
|
||||
if (pidref) {
|
||||
r = pidref_kill(pidref, SIGKILL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to kill qemu, terminating: %m");
|
||||
else {
|
||||
log_info("Trying to halt qemu. Send SIGTERM again to trigger vmspawn to immediately terminate.");
|
||||
sd_event_source_set_userdata(s, NULL);
|
||||
return 0;
|
||||
@ -450,7 +497,10 @@ static int run_virtual_machine(void) {
|
||||
use_kvm = r;
|
||||
}
|
||||
|
||||
r = find_ovmf_config(arg_secure_boot, &ovmf_config);
|
||||
if (arg_firmware)
|
||||
r = load_ovmf_config(arg_firmware, &ovmf_config);
|
||||
else
|
||||
r = find_ovmf_config(arg_secure_boot, &ovmf_config);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to find OVMF config: %m");
|
||||
|
||||
@ -473,7 +523,7 @@ static int run_virtual_machine(void) {
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to find QEMU binary: %m");
|
||||
|
||||
if (asprintf(&mem, "%.4fM", (double)arg_qemu_mem / (1024.0 * 1024.0)) < 0)
|
||||
if (asprintf(&mem, "%" PRIu64, DIV_ROUND_UP(arg_qemu_mem, U64_MB)) < 0)
|
||||
return log_oom();
|
||||
|
||||
cmdline = strv_new(
|
||||
@ -558,7 +608,7 @@ static int run_virtual_machine(void) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "if=pflash,format=raw,readonly=on,file=%s", ovmf_config->path);
|
||||
r = strv_extendf(&cmdline, "if=pflash,format=%s,readonly=on,file=%s", ovmf_config_format(ovmf_config), ovmf_config->path);
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
@ -596,7 +646,7 @@ static int run_virtual_machine(void) {
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
|
||||
r = strv_extendf(&cmdline, "file=%s,if=pflash,format=raw", ovmf_vars_to);
|
||||
r = strv_extendf(&cmdline, "file=%s,if=pflash,format=%s", ovmf_vars_to, ovmf_config_format(ovmf_config));
|
||||
if (r < 0)
|
||||
return log_oom();
|
||||
}
|
||||
@ -663,15 +713,16 @@ static int run_virtual_machine(void) {
|
||||
|
||||
(void) sd_event_set_watchdog(event, true);
|
||||
|
||||
pid_t child_pid;
|
||||
r = safe_fork_full(
|
||||
_cleanup_(pidref_done) PidRef child_pidref = PIDREF_NULL;
|
||||
|
||||
r = pidref_safe_fork_full(
|
||||
qemu_binary,
|
||||
NULL,
|
||||
/* stdio_fds= */ NULL,
|
||||
&child_vsock_fd, 1, /* pass the vsock fd to qemu */
|
||||
FORK_CLOEXEC_OFF,
|
||||
&child_pid);
|
||||
FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_LOG|FORK_CLOEXEC_OFF|FORK_RLIMIT_NOFILE_SAFE,
|
||||
&child_pidref);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to fork off %s: %m", qemu_binary);
|
||||
return r;
|
||||
if (r == 0) {
|
||||
/* set TERM and LANG if they are missing */
|
||||
if (setenv("TERM", "vt220", 0) < 0)
|
||||
@ -680,11 +731,14 @@ static int run_virtual_machine(void) {
|
||||
if (setenv("LANG", "C.UTF-8", 0) < 0)
|
||||
return log_oom();
|
||||
|
||||
execve(qemu_binary, cmdline, environ);
|
||||
execv(qemu_binary, cmdline);
|
||||
log_error_errno(errno, "Failed to execve %s: %m", qemu_binary);
|
||||
_exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* Close the vsock fd we passed to qemu in the parent. We don't need it anymore. */
|
||||
child_vsock_fd = safe_close(child_vsock_fd);
|
||||
|
||||
int exit_status = INT_MAX;
|
||||
if (use_vsock) {
|
||||
r = setup_notify_parent(event, vsock_fd, &exit_status, ¬ify_event_source);
|
||||
@ -693,13 +747,13 @@ static int run_virtual_machine(void) {
|
||||
}
|
||||
|
||||
/* shutdown qemu when we are shutdown */
|
||||
(void) sd_event_add_signal(event, NULL, SIGINT | SD_EVENT_SIGNAL_PROCMASK, on_orderly_shutdown, PID_TO_PTR(child_pid));
|
||||
(void) sd_event_add_signal(event, NULL, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, on_orderly_shutdown, PID_TO_PTR(child_pid));
|
||||
(void) sd_event_add_signal(event, NULL, SIGINT | SD_EVENT_SIGNAL_PROCMASK, on_orderly_shutdown, &child_pidref);
|
||||
(void) sd_event_add_signal(event, NULL, SIGTERM | SD_EVENT_SIGNAL_PROCMASK, on_orderly_shutdown, &child_pidref);
|
||||
|
||||
(void) sd_event_add_signal(event, NULL, (SIGRTMIN+18) | SD_EVENT_SIGNAL_PROCMASK, sigrtmin18_handler, NULL);
|
||||
|
||||
/* Exit when the child exits */
|
||||
(void) sd_event_add_child(event, NULL, child_pid, WEXITED, on_child_exit, NULL);
|
||||
(void) event_add_child_pidref(event, NULL, &child_pidref, WEXITED, on_child_exit, NULL);
|
||||
|
||||
r = sd_event_loop(event);
|
||||
if (r < 0)
|
||||
@ -757,6 +811,16 @@ static int run(int argc, char *argv[]) {
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!arg_quiet) {
|
||||
_cleanup_free_ char *u = NULL;
|
||||
(void) terminal_urlify_path(arg_image, arg_image, &u);
|
||||
|
||||
log_info("%s %sSpawning VM %s on %s.%s\n"
|
||||
"%s %sPress %sCtrl-a x%s to kill VM.%s",
|
||||
special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), ansi_grey(), arg_machine, u ?: arg_image, ansi_normal(),
|
||||
special_glyph(SPECIAL_GLYPH_LIGHT_SHADE), ansi_grey(), ansi_highlight(), ansi_grey(), ansi_normal());
|
||||
}
|
||||
|
||||
assert_se(sigprocmask_many(SIG_BLOCK, NULL, SIGCHLD, -1) >= 0);
|
||||
|
||||
return run_virtual_machine();
|
||||
|
Loading…
x
Reference in New Issue
Block a user