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> --- Changes to v1: - formatting - g_strdup - deleted redundant variables, cleanup - naming - error in loading function caused interrupting of program docs/manpages/virt-qemu-run.rst | 9 +++ src/qemu/qemu_shim.c | 120 +++++++++++++++++++++++++++++++- 2 files changed, 128 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..9672b85183 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,101 @@ qemuShimQuench(void *userData G_GNUC_UNUSED, { } +/* Load specific QEMU config file for the -c option */ +static int +qemuAddConfigFile(const char *source_file, + const char *root, + char **old_config_file, + char **saved_config_file, + long long deltams) +{ + struct stat st; + VIR_AUTOCLOSE srcFD = -1; + VIR_AUTOCLOSE dstFD = -1; + g_autofree char *config_dir = NULL; + g_autofree char *config_dir_file = NULL; + g_autofree char *config_dir_file_real = NULL; + g_autofree char *source_file_real = NULL; + + if (source_file == NULL) + return 0; + + if ((srcFD = open(source_file, O_RDONLY)) < 0) { + g_printerr("Couldn't open specific config file\n"); + return -1; + } + + if (fstat(srcFD, &st) != 0) { + g_printerr("Specific config file does not exist\n"); + return -1; + } + if (!S_ISREG(st.st_mode)) { + g_printerr("Specific config is not a regular file\n"); + return -1; + } + + /* 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"); + return -1; + } + + config_dir_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_dir_file_real = realpath(config_dir_file, NULL))) { + if (STREQ(source_file_real, config_dir_file_real)) { + return 0; + } + } + + /* 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_dir_file, R_OK) == 0) { + *old_config_file = g_strdup_printf("%sqemu_old_%lld.conf", + config_dir, deltams); + + if (rename(config_dir_file, *old_config_file) != 0) { + g_printerr("Couldn't rename old config file, try delete it\n"); + return -1; + } + } + + if ((dstFD = open(config_dir_file, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) { + g_printerr("Couldn't open file for define specific config\n"); + return -1; + } + + *saved_config_file = g_steal_pointer(&config_dir_file); + + do { + char buffer[1024]; + ssize_t nread = 0; + + nread = saferead(srcFD, buffer, 1024); + + if (nread < 0) { + g_printerr("Couldn't read from specific config\n"); + return -1; + } else if (nread == 0) { + break; + } else { + ssize_t nwrite = safewrite(dstFD, buffer, nread); + if (nwrite != nread) { + g_printerr("Couldn't write to file for define specific config\n"); + return -1; + } + } + } while (1); + + return 0; +} + int main(int argc, char **argv) { g_autoptr(virIdentity) sysident = NULL; @@ -141,7 +237,10 @@ int main(int argc, char **argv) g_autofree char *xml = NULL; g_autofree char *uri = NULL; g_autofree char *suri = NULL; + g_autofree char *old_config_file = NULL; + g_autofree char *saved_config_file = NULL; const char *root = NULL; + const char *config = NULL; g_autofree char *escaped = NULL; bool tmproot = false; int ret = 1; @@ -156,6 +255,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 +272,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 +334,12 @@ int main(int argc, char **argv) goto cleanup; } + if (config && qemuAddConfigFile(config, root, &old_config_file, + &saved_config_file, deltams()) < 0) { + g_printerr("Specific config file was not loaded, process interupted\n"); + goto cleanup; + } + escaped = g_uri_escape_string(root, NULL, true); virFileActivateDirOverrideForProg(argv[0]); @@ -402,6 +508,18 @@ int main(int argc, char **argv) VIR_FORCE_CLOSE(quitfd[0]); VIR_FORCE_CLOSE(quitfd[1]); + if (saved_config_file) { + if (remove(saved_config_file) != 0) + g_printerr("Deleting specific config failed, located in: %s\n", + saved_config_file); + + if (old_config_file && access(old_config_file, R_OK) == 0) { + if (rename(old_config_file, saved_config_file) != 0) + g_printerr("Renaming your old qemu.conf failed, ups, located in %s\n", + old_config_file); + } + } + if (dom != NULL) virDomainFree(dom); if (sconn != NULL) -- 2.47.1