Adding a new option --config (or -c) for specifying a custom qemu.conf file. Previously, virt-qemu-run loaded default configuration values for QEMU via qemuStateInitialize(). The configuration was loaded from a temporary ../etc/ directory using virQEMUDriverConfigLoadFile(), and any qemu.conf file present in that directory was also loaded automatically. This patch allows users to specify a custom configuration file, which is copied into the temporary directory (or a permanent folder if the -r option is used) before loading the configuration. If an existing qemu.conf is present, it is properly backed up and restored in case of a permanent folder. The custom qemu.conf is always removed when the program exits. Resolves: https://gitlab.com/libvirt/libvirt/-/issues/723 Signed-off-by: Adam Julis <ajulis@xxxxxxxxxx> --- docs/manpages/virt-qemu-run.rst | 9 +++ src/qemu/qemu_shim.c | 121 +++++++++++++++++++++++++++++++- 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/docs/manpages/virt-qemu-run.rst b/docs/manpages/virt-qemu-run.rst index 4d546ff8cc..ba1c90b52a 100644 --- a/docs/manpages/virt-qemu-run.rst +++ b/docs/manpages/virt-qemu-run.rst @@ -72,6 +72,15 @@ whose UUID should match a secret referenced in the guest domain XML. Display verbose information about startup. +``-c`` *QEMU-CONF-FILE*, +``--config``\ =\ *QEMU-CONF-FILE* + +Specify the QEMU configuration file to be used for starting the VM. +*QEMU-CONF-FILE* is the full path to the QEMU configuration file. + +If this parameter is omitted, the default configuration values will +be used. + ``-h``, ``--help`` Display the command line help. diff --git a/src/qemu/qemu_shim.c b/src/qemu/qemu_shim.c index 7fdd69b538..64c87ab264 100644 --- a/src/qemu/qemu_shim.c +++ b/src/qemu/qemu_shim.c @@ -23,6 +23,7 @@ #include <stdio.h> #include <stdbool.h> #include <unistd.h> +#include <fcntl.h> #include "virfile.h" #include "virgettext.h" @@ -132,6 +133,100 @@ qemuShimQuench(void *userData G_GNUC_UNUSED, { } +/* Load specific QEMU config file, is the -c option is used */ +static int +qemuAddConfigFile(const char *source_file, const char *root, bool *config_to_delete, long long deltams) +{ + int ret = -1; + struct stat st; + VIR_AUTOCLOSE srcFD = -1; + VIR_AUTOCLOSE dstFD = -1; + g_autofree char *config_dir = NULL; + g_autofree char *source_file_real = NULL; + g_autofree char *config_path_file = NULL; + g_autofree char *config_path_file_real = NULL; + + if ((srcFD = open(source_file, O_RDONLY)) < 0) { + g_printerr("Couldn't open specific config file\n"); + goto cleanup; + } + + if (fstat(srcFD, &st) != 0) { + g_printerr("Specific config file does not exist\n"); + goto cleanup; + } + if (!S_ISREG(st.st_mode)) { + g_printerr("Specific config is not a regular file\n"); + goto cleanup; + } + + /* Since source file exists, make the destination path or + * validate that it already exists */ + config_dir = g_strdup_printf("%s/etc/", root); + + if (g_mkdir_with_parents(config_dir, 0777) < 0) { + g_printerr("Couldn't make the directory for specific config file\n"); + goto cleanup; + } + + config_path_file = g_strdup_printf("%sqemu.conf", config_dir); + + /* If the source file is same as the destination file, no action needed */ + if ((source_file_real = realpath(source_file, NULL)) && + (config_path_file_real = realpath(config_path_file, NULL))) { + if (STREQ(source_file_real, config_path_file_real)) { + ret = 0; + goto cleanup; + } + } + + /* Check already existing qemu.conf in the subfolder, if so, renamed + * (appended via deltams constant - should be unique). Final cleanup + * at main() will revert this change */ + if (access(config_path_file, R_OK) == 0) { + if (rename(config_path_file, g_strdup_printf("%sqemu_old_%lld.conf", + config_dir, deltams)) != 0) { + g_printerr("Couldn't rename old config file, try delete it\n"); + goto cleanup; + } + } + + if ((dstFD = open(config_path_file, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) { + g_printerr("Couldn't open file for define specific config\n"); + goto cleanup; + } + + /* Set the flag for deleting the new/modified config in main() cleanup, + * for ensure that behaviour without the user config will be always + * consistent and take default value */ + *config_to_delete = true; + + do { + char buffer[1024]; + ssize_t readed = 0; + + readed = saferead(srcFD, buffer, 1024); + + if (readed < 0) { + g_printerr("Couldn't read from specific config\n"); + goto cleanup; + } else if (readed == 0) { + break; + } else { + ssize_t writed = safewrite(dstFD, buffer, readed); + if (writed != readed) { + g_printerr("Couldn't write to file for define specific config\n"); + goto cleanup; + } + } + } while (1); + + ret = 0; + + cleanup: + return ret; +} + int main(int argc, char **argv) { g_autoptr(virIdentity) sysident = NULL; @@ -142,8 +237,11 @@ int main(int argc, char **argv) g_autofree char *uri = NULL; g_autofree char *suri = NULL; const char *root = NULL; + const char *config = NULL; + long long delta_ms = 0; g_autofree char *escaped = NULL; bool tmproot = false; + bool config_to_delete = false; int ret = 1; g_autoptr(GError) error = NULL; g_auto(GStrv) secrets = NULL; @@ -156,6 +254,7 @@ int main(int argc, char **argv) { "root", 'r', 0, G_OPTION_ARG_STRING, &root, "Root directory", "DIR" }, { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, "Debug output", NULL }, { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Verbose output", NULL }, + { "config", 'c', 0, G_OPTION_ARG_STRING, &config, "Load specific QEMU configuration file", "QEMU-CONF-FILE"}, { 0 } }; int quitfd[2] = {-1, -1}; @@ -172,7 +271,7 @@ int main(int argc, char **argv) return 1; } - if (argc != 2) { + if (argc != 2 && argc != 3) { g_autofree char *help = g_option_context_get_help(ctx, TRUE, NULL); g_printerr("%s", help); return 1; @@ -234,6 +333,10 @@ int main(int argc, char **argv) goto cleanup; } + delta_ms = deltams(); + if (config && (qemuAddConfigFile(config, root, &config_to_delete, delta_ms) < 0)) + g_printerr("Specific config file was not loaded, default was used\n"); + escaped = g_uri_escape_string(root, NULL, true); virFileActivateDirOverrideForProg(argv[0]); @@ -402,6 +505,22 @@ int main(int argc, char **argv) VIR_FORCE_CLOSE(quitfd[0]); VIR_FORCE_CLOSE(quitfd[1]); + if (config_to_delete) { + g_autofree char *possible_old_file = g_strdup_printf("%s/etc/qemu_old_%lld.conf", + root, delta_ms); + g_autofree char *to_delete = g_strdup_printf("%s/etc/qemu.conf", root); + + if (remove(to_delete) != 0) + g_printerr("Deleting specific config failed, located in: %s\n", + to_delete); + + if (access(possible_old_file, R_OK) == 0) { + if (rename(possible_old_file, to_delete) != 0) + g_printerr("Renaming your old qemu.conf failed, ups, located in %s\n", + possible_old_file); + } + } + if (dom != NULL) virDomainFree(dom); if (sconn != NULL) -- 2.47.1