Re: [PATCH] qemu: introduce load qemu.conf for "virt-qemu-run"

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Virt Tools]     [Libvirt Users]     [Lib OS Info]     [Fedora Users]     [Fedora Desktop]     [Fedora SELinux]     [Big List of Linux Books]     [Yosemite News]     [KDE Users]     [Fedora Tools]

  Powered by Linux