On 1/28/25 15:13, Adam Julis wrote: > 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) One argument per line please. > +{ > + 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) { Surely this g_strdup_printf() must leak memory. > + g_printerr("Couldn't rename old config file, try delete it\n"); > + goto cleanup; > + } Hm.. this is the point where I'd expect config_to_delete to be set. Just consider that rename() succeeds, but ... > + } > + > + 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; > + } ... open() fails. Then, when caller is doing cleanup it needs to rename the file back. This renders the variable name a bit misleading though, how about 'rename_config'? > + > + /* 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; Inventive naming :-) Typically we use nread. > + > + 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; This is pretty useless label. s/goto cleanup/return -1/ (except for a few places where ret is set to 0 before jumping). > +} > + > 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"); Surely this has to be a reason to stop execution and let user fix things. > + > 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); > + } This can made better. Firstly - you can avoid reconstruting path again by simply returning it from qemuAddConfigFile() [1]. Secondly - the way that 'possible_old_file' is reconstructed is very fragile - what if somebody modifies delta_ms? Thirdly - you do not need to remove() the file - just renmae(possible_old_file, to_delete). 1: In fact, to my previous point about 'config_to_delete' rename - qemuAddConfigFile() can simply set a pointer which would be then used as a signal to rename the file. Something like this: int main() { /* this would effectively be $root/etc/qemu.conf */ g_autofree char *old_config_file = NULL; /* this would be $root/etc/qemu_old_XXXX.conf */ g_autofree char *saved_config_file = NULL; ... qemuAddConfigFile(config, root, &old_config_file, &saved_config_file); ... cleanup: ... if (saved_config_file && rename(saved_config_file, old_config_file) < 0) { g_printerr(); } ... } Michal