* [PATCH v2 00/22] qga: clean up command source locations and conditionals
@ 2024-06-13 15:01 Daniel P. Berrangé
2024-06-13 15:01 ` [PATCH v2 01/22] qga: drop blocking of guest-get-memory-block-size command Daniel P. Berrangé
` (7 more replies)
0 siblings, 8 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:01 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Paolo Bonzini,
Daniel P. Berrangé
This series is a side effect of other work I started, to attempt to
make the QGA safe to use in confidential VMs by automatically
restricting the permitted commands. Since this cleanup stands on
its own, I'm sending it now.
The QGA codebase has a very complicated maze of #ifdefs to create
stubs for the various commands that cannot be implemented on certain
platforms. It then has further logic to dynamically disable the stub
commands at runtime, except this is not consistently applied, so
some commands remain enabled despite being merely stubs.
The resulting code is hard to follow, when trying to understand exactly
what commands are available under what circumstances, and when changing
impls it is easy to get the #ifdefs wrong, resulting in stubs getting
missed on platforms without a real impl. In some cases, we have multiple
stubs for the same command, due to the maze of #ifdefs.
The QAPI schema language has support for many years for expressing
conditions against commands when declaring them. This results in the
QAPI code generator omitting their implementation entirely at build
time. This has mutliple benefits
* The unsupported commands are guaranteed to not exist at runtime
* No stubs need ever be defined in the code
* The generated QAPI reference manual documents the build conditions
This series is broadly split into three parts
* Moving tonnes of Linux only commands out of commands-posix.c
into commands-linux.c to remove many #ifdefs.
* Adding 'if' conditions in the QAPI schema to reflect the
build conditions, removing many more #ifdefs
* Sanitizing the logic for disabling/enabling commands at
runtime to guarantee consistency
Changed in v2:
- Make FSFreeze error reporting distinguish inability to enable
VSS from user config choice
- Fully remove ga_command_init_blockedrpcs() methods. No more
special case disabling of commands. Either they're disabled
at build time, or disabled by user config, or by well defined
rule ie not permitted during FS freeze.
- Apply rules later in startup to avoid crash from NULL config
pointer
- Document changed error messages in commit messages
- Add -c / --config command line parameter
- Fix mistaken enabling of fsfreeze hooks on win32
- Remove pointless 'blockrpcs_key' variable
- Allow concurrent setting of allow and block lists for
RPC commands
Daniel P. Berrangé (22):
qga: drop blocking of guest-get-memory-block-size command
qga: move linux vcpu command impls to commands-linux.c
qga: move linux suspend command impls to commands-linux.c
qga: move linux fs/disk command impls to commands-linux.c
qga: move linux disk/cpu stats command impls to commands-linux.c
qga: move linux memory block command impls to commands-linux.c
qga: move CONFIG_FSFREEZE/TRIM to be meson defined options
qga: conditionalize schema for commands unsupported on Windows
qga: conditionalize schema for commands unsupported on non-Linux POSIX
qga: conditionalize schema for commands requiring getifaddrs
qga: conditionalize schema for commands requiring linux/win32
qga: conditionalize schema for commands only supported on Windows
qga: conditionalize schema for commands requiring fsfreeze
qga: conditionalize schema for commands requiring fstrim
qga: conditionalize schema for commands requiring libudev
qga: conditionalize schema for commands requiring utmpx
qga: conditionalize schema for commands not supported on other UNIX
qga: don't disable fsfreeze commands if vss_init fails
qga: move declare of QGAConfig struct to top of file
qga: remove pointless 'blockrpcs_key' variable
qga: allow configuration file path via the cli
qga: centralize logic for disabling/enabling commands
docs/interop/qemu-ga.rst | 19 +
meson.build | 16 +
qga/commands-bsd.c | 24 -
qga/commands-common.h | 9 -
qga/commands-linux.c | 1805 +++++++++++++++++++++++++++++
qga/commands-posix.c | 2373 +++-----------------------------------
qga/commands-win32.c | 78 +-
qga/main.c | 216 ++--
qga/qapi-schema.json | 153 ++-
9 files changed, 2234 insertions(+), 2459 deletions(-)
--
2.45.1
^ permalink raw reply [flat|nested] 70+ messages in thread
* [PATCH v2 01/22] qga: drop blocking of guest-get-memory-block-size command
2024-06-13 15:01 [PATCH v2 00/22] qga: clean up command source locations and conditionals Daniel P. Berrangé
@ 2024-06-13 15:01 ` Daniel P. Berrangé
2024-07-12 9:33 ` Konstantin Kostiuk
2024-06-13 15:01 ` [PATCH v2 02/22] qga: move linux vcpu command impls to commands-linux.c Daniel P. Berrangé
` (6 subsequent siblings)
7 siblings, 1 reply; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:01 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Paolo Bonzini,
Daniel P. Berrangé, Manos Pitsidianakis
This command has never existed in tree, since it was renamed to
guest-get-memory-block-info before being merged.
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-posix.c | 2 +-
qga/commands-win32.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 7f05996495..76af98ba32 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -3099,7 +3099,7 @@ GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
"guest-suspend-disk", "guest-suspend-ram",
"guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
"guest-get-memory-blocks", "guest-set-memory-blocks",
- "guest-get-memory-block-size", "guest-get-memory-block-info",
+ "guest-get-memory-block-info",
NULL};
char **p = (char **)list;
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 0d1b836e87..9fe670d5b4 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -1995,7 +1995,7 @@ GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
"guest-suspend-hybrid",
"guest-set-vcpus",
"guest-get-memory-blocks", "guest-set-memory-blocks",
- "guest-get-memory-block-size", "guest-get-memory-block-info",
+ "guest-get-memory-block-info",
NULL};
char **p = (char **)list_unsupported;
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 02/22] qga: move linux vcpu command impls to commands-linux.c
2024-06-13 15:01 [PATCH v2 00/22] qga: clean up command source locations and conditionals Daniel P. Berrangé
2024-06-13 15:01 ` [PATCH v2 01/22] qga: drop blocking of guest-get-memory-block-size command Daniel P. Berrangé
@ 2024-06-13 15:01 ` Daniel P. Berrangé
2024-07-03 8:45 ` Philippe Mathieu-Daudé
2024-07-12 8:30 ` Konstantin Kostiuk
2024-06-13 15:01 ` [PATCH v2 03/22] qga: move linux suspend " Daniel P. Berrangé
` (5 subsequent siblings)
7 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:01 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Paolo Bonzini,
Daniel P. Berrangé, Manos Pitsidianakis
The qmp_guest_set_vcpus and qmp_guest_get_vcpus command impls in
commands-posix.c are surrounded by '#ifdef __linux__' so should
instead live in commands-linux.c
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-linux.c | 141 +++++++++++++++++++++++++++++++++++++++++++
qga/commands-posix.c | 139 ------------------------------------------
2 files changed, 141 insertions(+), 139 deletions(-)
diff --git a/qga/commands-linux.c b/qga/commands-linux.c
index 214e408fcd..78580ac39d 100644
--- a/qga/commands-linux.c
+++ b/qga/commands-linux.c
@@ -13,6 +13,7 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
+#include "qga-qapi-commands.h"
#include "commands-common.h"
#include "cutils.h"
#include <mntent.h>
@@ -284,3 +285,143 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp)
return i;
}
#endif /* CONFIG_FSFREEZE */
+
+/* Transfer online/offline status between @vcpu and the guest system.
+ *
+ * On input either @errp or *@errp must be NULL.
+ *
+ * In system-to-@vcpu direction, the following @vcpu fields are accessed:
+ * - R: vcpu->logical_id
+ * - W: vcpu->online
+ * - W: vcpu->can_offline
+ *
+ * In @vcpu-to-system direction, the following @vcpu fields are accessed:
+ * - R: vcpu->logical_id
+ * - R: vcpu->online
+ *
+ * Written members remain unmodified on error.
+ */
+static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
+ char *dirpath, Error **errp)
+{
+ int fd;
+ int res;
+ int dirfd;
+ static const char fn[] = "online";
+
+ dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
+ if (dirfd == -1) {
+ error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
+ return;
+ }
+
+ fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
+ if (fd == -1) {
+ if (errno != ENOENT) {
+ error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
+ } else if (sys2vcpu) {
+ vcpu->online = true;
+ vcpu->can_offline = false;
+ } else if (!vcpu->online) {
+ error_setg(errp, "logical processor #%" PRId64 " can't be "
+ "offlined", vcpu->logical_id);
+ } /* otherwise pretend successful re-onlining */
+ } else {
+ unsigned char status;
+
+ res = pread(fd, &status, 1, 0);
+ if (res == -1) {
+ error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn);
+ } else if (res == 0) {
+ error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
+ fn);
+ } else if (sys2vcpu) {
+ vcpu->online = (status != '0');
+ vcpu->can_offline = true;
+ } else if (vcpu->online != (status != '0')) {
+ status = '0' + vcpu->online;
+ if (pwrite(fd, &status, 1, 0) == -1) {
+ error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath,
+ fn);
+ }
+ } /* otherwise pretend successful re-(on|off)-lining */
+
+ res = close(fd);
+ g_assert(res == 0);
+ }
+
+ res = close(dirfd);
+ g_assert(res == 0);
+}
+
+GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
+{
+ GuestLogicalProcessorList *head, **tail;
+ const char *cpu_dir = "/sys/devices/system/cpu";
+ const gchar *line;
+ g_autoptr(GDir) cpu_gdir = NULL;
+ Error *local_err = NULL;
+
+ head = NULL;
+ tail = &head;
+ cpu_gdir = g_dir_open(cpu_dir, 0, NULL);
+
+ if (cpu_gdir == NULL) {
+ error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir);
+ return NULL;
+ }
+
+ while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) {
+ GuestLogicalProcessor *vcpu;
+ int64_t id;
+ if (sscanf(line, "cpu%" PRId64, &id)) {
+ g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/"
+ "cpu%" PRId64 "/", id);
+ vcpu = g_malloc0(sizeof *vcpu);
+ vcpu->logical_id = id;
+ vcpu->has_can_offline = true; /* lolspeak ftw */
+ transfer_vcpu(vcpu, true, path, &local_err);
+ QAPI_LIST_APPEND(tail, vcpu);
+ }
+ }
+
+ if (local_err == NULL) {
+ /* there's no guest with zero VCPUs */
+ g_assert(head != NULL);
+ return head;
+ }
+
+ qapi_free_GuestLogicalProcessorList(head);
+ error_propagate(errp, local_err);
+ return NULL;
+}
+
+int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
+{
+ int64_t processed;
+ Error *local_err = NULL;
+
+ processed = 0;
+ while (vcpus != NULL) {
+ char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
+ vcpus->value->logical_id);
+
+ transfer_vcpu(vcpus->value, false, path, &local_err);
+ g_free(path);
+ if (local_err != NULL) {
+ break;
+ }
+ ++processed;
+ vcpus = vcpus->next;
+ }
+
+ if (local_err != NULL) {
+ if (processed == 0) {
+ error_propagate(errp, local_err);
+ } else {
+ error_free(local_err);
+ }
+ }
+
+ return processed;
+}
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 76af98ba32..a8ef41f175 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -2003,145 +2003,6 @@ void qmp_guest_suspend_hybrid(Error **errp)
guest_suspend(SUSPEND_MODE_HYBRID, errp);
}
-/* Transfer online/offline status between @vcpu and the guest system.
- *
- * On input either @errp or *@errp must be NULL.
- *
- * In system-to-@vcpu direction, the following @vcpu fields are accessed:
- * - R: vcpu->logical_id
- * - W: vcpu->online
- * - W: vcpu->can_offline
- *
- * In @vcpu-to-system direction, the following @vcpu fields are accessed:
- * - R: vcpu->logical_id
- * - R: vcpu->online
- *
- * Written members remain unmodified on error.
- */
-static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
- char *dirpath, Error **errp)
-{
- int fd;
- int res;
- int dirfd;
- static const char fn[] = "online";
-
- dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
- if (dirfd == -1) {
- error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
- return;
- }
-
- fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
- if (fd == -1) {
- if (errno != ENOENT) {
- error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
- } else if (sys2vcpu) {
- vcpu->online = true;
- vcpu->can_offline = false;
- } else if (!vcpu->online) {
- error_setg(errp, "logical processor #%" PRId64 " can't be "
- "offlined", vcpu->logical_id);
- } /* otherwise pretend successful re-onlining */
- } else {
- unsigned char status;
-
- res = pread(fd, &status, 1, 0);
- if (res == -1) {
- error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn);
- } else if (res == 0) {
- error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
- fn);
- } else if (sys2vcpu) {
- vcpu->online = (status != '0');
- vcpu->can_offline = true;
- } else if (vcpu->online != (status != '0')) {
- status = '0' + vcpu->online;
- if (pwrite(fd, &status, 1, 0) == -1) {
- error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath,
- fn);
- }
- } /* otherwise pretend successful re-(on|off)-lining */
-
- res = close(fd);
- g_assert(res == 0);
- }
-
- res = close(dirfd);
- g_assert(res == 0);
-}
-
-GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
-{
- GuestLogicalProcessorList *head, **tail;
- const char *cpu_dir = "/sys/devices/system/cpu";
- const gchar *line;
- g_autoptr(GDir) cpu_gdir = NULL;
- Error *local_err = NULL;
-
- head = NULL;
- tail = &head;
- cpu_gdir = g_dir_open(cpu_dir, 0, NULL);
-
- if (cpu_gdir == NULL) {
- error_setg_errno(errp, errno, "failed to list entries: %s", cpu_dir);
- return NULL;
- }
-
- while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) != NULL) {
- GuestLogicalProcessor *vcpu;
- int64_t id;
- if (sscanf(line, "cpu%" PRId64, &id)) {
- g_autofree char *path = g_strdup_printf("/sys/devices/system/cpu/"
- "cpu%" PRId64 "/", id);
- vcpu = g_malloc0(sizeof *vcpu);
- vcpu->logical_id = id;
- vcpu->has_can_offline = true; /* lolspeak ftw */
- transfer_vcpu(vcpu, true, path, &local_err);
- QAPI_LIST_APPEND(tail, vcpu);
- }
- }
-
- if (local_err == NULL) {
- /* there's no guest with zero VCPUs */
- g_assert(head != NULL);
- return head;
- }
-
- qapi_free_GuestLogicalProcessorList(head);
- error_propagate(errp, local_err);
- return NULL;
-}
-
-int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
-{
- int64_t processed;
- Error *local_err = NULL;
-
- processed = 0;
- while (vcpus != NULL) {
- char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/",
- vcpus->value->logical_id);
-
- transfer_vcpu(vcpus->value, false, path, &local_err);
- g_free(path);
- if (local_err != NULL) {
- break;
- }
- ++processed;
- vcpus = vcpus->next;
- }
-
- if (local_err != NULL) {
- if (processed == 0) {
- error_propagate(errp, local_err);
- } else {
- error_free(local_err);
- }
- }
-
- return processed;
-}
#endif /* __linux__ */
#if defined(__linux__) || defined(__FreeBSD__)
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 03/22] qga: move linux suspend command impls to commands-linux.c
2024-06-13 15:01 [PATCH v2 00/22] qga: clean up command source locations and conditionals Daniel P. Berrangé
2024-06-13 15:01 ` [PATCH v2 01/22] qga: drop blocking of guest-get-memory-block-size command Daniel P. Berrangé
2024-06-13 15:01 ` [PATCH v2 02/22] qga: move linux vcpu command impls to commands-linux.c Daniel P. Berrangé
@ 2024-06-13 15:01 ` Daniel P. Berrangé
2024-07-03 8:45 ` Philippe Mathieu-Daudé
2024-07-12 8:29 ` Konstantin Kostiuk
2024-06-13 15:01 ` [PATCH v2 04/22] qga: move linux fs/disk " Daniel P. Berrangé
` (4 subsequent siblings)
7 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:01 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Paolo Bonzini,
Daniel P. Berrangé, Manos Pitsidianakis
The qmp_guest_suspend_{disk,ram,hybrid} command impls in
commands-posix.c are surrounded by '#ifdef __linux__' so should
instead live in commands-linux.c
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-linux.c | 265 +++++++++++++++++++++++++++++++++++++++++++
qga/commands-posix.c | 265 -------------------------------------------
2 files changed, 265 insertions(+), 265 deletions(-)
diff --git a/qga/commands-linux.c b/qga/commands-linux.c
index 78580ac39d..3fabf54882 100644
--- a/qga/commands-linux.c
+++ b/qga/commands-linux.c
@@ -286,6 +286,271 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp)
}
#endif /* CONFIG_FSFREEZE */
+
+#define LINUX_SYS_STATE_FILE "/sys/power/state"
+#define SUSPEND_SUPPORTED 0
+#define SUSPEND_NOT_SUPPORTED 1
+
+typedef enum {
+ SUSPEND_MODE_DISK = 0,
+ SUSPEND_MODE_RAM = 1,
+ SUSPEND_MODE_HYBRID = 2,
+} SuspendMode;
+
+/*
+ * Executes a command in a child process using g_spawn_sync,
+ * returning an int >= 0 representing the exit status of the
+ * process.
+ *
+ * If the program wasn't found in path, returns -1.
+ *
+ * If a problem happened when creating the child process,
+ * returns -1 and errp is set.
+ */
+static int run_process_child(const char *command[], Error **errp)
+{
+ int exit_status, spawn_flag;
+ GError *g_err = NULL;
+ bool success;
+
+ spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
+ G_SPAWN_STDERR_TO_DEV_NULL;
+
+ success = g_spawn_sync(NULL, (char **)command, NULL, spawn_flag,
+ NULL, NULL, NULL, NULL,
+ &exit_status, &g_err);
+
+ if (success) {
+ return WEXITSTATUS(exit_status);
+ }
+
+ if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
+ error_setg(errp, "failed to create child process, error '%s'",
+ g_err->message);
+ }
+
+ g_error_free(g_err);
+ return -1;
+}
+
+static bool systemd_supports_mode(SuspendMode mode, Error **errp)
+{
+ const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend",
+ "systemd-hybrid-sleep"};
+ const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL};
+ int status;
+
+ status = run_process_child(cmd, errp);
+
+ /*
+ * systemctl status uses LSB return codes so we can expect
+ * status > 0 and be ok. To assert if the guest has support
+ * for the selected suspend mode, status should be < 4. 4 is
+ * the code for unknown service status, the return value when
+ * the service does not exist. A common value is status = 3
+ * (program is not running).
+ */
+ if (status > 0 && status < 4) {
+ return true;
+ }
+
+ return false;
+}
+
+static void systemd_suspend(SuspendMode mode, Error **errp)
+{
+ Error *local_err = NULL;
+ const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"};
+ const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL};
+ int status;
+
+ status = run_process_child(cmd, &local_err);
+
+ if (status == 0) {
+ return;
+ }
+
+ if ((status == -1) && !local_err) {
+ error_setg(errp, "the helper program 'systemctl %s' was not found",
+ systemctl_args[mode]);
+ return;
+ }
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ } else {
+ error_setg(errp, "the helper program 'systemctl %s' returned an "
+ "unexpected exit status code (%d)",
+ systemctl_args[mode], status);
+ }
+}
+
+static bool pmutils_supports_mode(SuspendMode mode, Error **errp)
+{
+ Error *local_err = NULL;
+ const char *pmutils_args[3] = {"--hibernate", "--suspend",
+ "--suspend-hybrid"};
+ const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL};
+ int status;
+
+ status = run_process_child(cmd, &local_err);
+
+ if (status == SUSPEND_SUPPORTED) {
+ return true;
+ }
+
+ if ((status == -1) && !local_err) {
+ return false;
+ }
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ } else {
+ error_setg(errp,
+ "the helper program '%s' returned an unexpected exit"
+ " status code (%d)", "pm-is-supported", status);
+ }
+
+ return false;
+}
+
+static void pmutils_suspend(SuspendMode mode, Error **errp)
+{
+ Error *local_err = NULL;
+ const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
+ "pm-suspend-hybrid"};
+ const char *cmd[2] = {pmutils_binaries[mode], NULL};
+ int status;
+
+ status = run_process_child(cmd, &local_err);
+
+ if (status == 0) {
+ return;
+ }
+
+ if ((status == -1) && !local_err) {
+ error_setg(errp, "the helper program '%s' was not found",
+ pmutils_binaries[mode]);
+ return;
+ }
+
+ if (local_err) {
+ error_propagate(errp, local_err);
+ } else {
+ error_setg(errp,
+ "the helper program '%s' returned an unexpected exit"
+ " status code (%d)", pmutils_binaries[mode], status);
+ }
+}
+
+static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
+{
+ const char *sysfile_strs[3] = {"disk", "mem", NULL};
+ const char *sysfile_str = sysfile_strs[mode];
+ char buf[32]; /* hopefully big enough */
+ int fd;
+ ssize_t ret;
+
+ if (!sysfile_str) {
+ error_setg(errp, "unknown guest suspend mode");
+ return false;
+ }
+
+ fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
+ if (fd < 0) {
+ return false;
+ }
+
+ ret = read(fd, buf, sizeof(buf) - 1);
+ close(fd);
+ if (ret <= 0) {
+ return false;
+ }
+ buf[ret] = '\0';
+
+ if (strstr(buf, sysfile_str)) {
+ return true;
+ }
+ return false;
+}
+
+static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
+{
+ g_autoptr(GError) local_gerr = NULL;
+ const char *sysfile_strs[3] = {"disk", "mem", NULL};
+ const char *sysfile_str = sysfile_strs[mode];
+
+ if (!sysfile_str) {
+ error_setg(errp, "unknown guest suspend mode");
+ return;
+ }
+
+ if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str,
+ -1, &local_gerr)) {
+ error_setg(errp, "suspend: cannot write to '%s': %s",
+ LINUX_SYS_STATE_FILE, local_gerr->message);
+ return;
+ }
+}
+
+static void guest_suspend(SuspendMode mode, Error **errp)
+{
+ Error *local_err = NULL;
+ bool mode_supported = false;
+
+ if (systemd_supports_mode(mode, &local_err)) {
+ mode_supported = true;
+ systemd_suspend(mode, &local_err);
+
+ if (!local_err) {
+ return;
+ }
+ }
+
+ error_free(local_err);
+ local_err = NULL;
+
+ if (pmutils_supports_mode(mode, &local_err)) {
+ mode_supported = true;
+ pmutils_suspend(mode, &local_err);
+
+ if (!local_err) {
+ return;
+ }
+ }
+
+ error_free(local_err);
+ local_err = NULL;
+
+ if (linux_sys_state_supports_mode(mode, &local_err)) {
+ mode_supported = true;
+ linux_sys_state_suspend(mode, &local_err);
+ }
+
+ if (!mode_supported) {
+ error_free(local_err);
+ error_setg(errp,
+ "the requested suspend mode is not supported by the guest");
+ } else {
+ error_propagate(errp, local_err);
+ }
+}
+
+void qmp_guest_suspend_disk(Error **errp)
+{
+ guest_suspend(SUSPEND_MODE_DISK, errp);
+}
+
+void qmp_guest_suspend_ram(Error **errp)
+{
+ guest_suspend(SUSPEND_MODE_RAM, errp);
+}
+
+void qmp_guest_suspend_hybrid(Error **errp)
+{
+ guest_suspend(SUSPEND_MODE_HYBRID, errp);
+}
+
/* Transfer online/offline status between @vcpu and the guest system.
*
* On input either @errp or *@errp must be NULL.
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index a8ef41f175..ef21da63be 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1738,271 +1738,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
}
#endif /* CONFIG_FSTRIM */
-
-#define LINUX_SYS_STATE_FILE "/sys/power/state"
-#define SUSPEND_SUPPORTED 0
-#define SUSPEND_NOT_SUPPORTED 1
-
-typedef enum {
- SUSPEND_MODE_DISK = 0,
- SUSPEND_MODE_RAM = 1,
- SUSPEND_MODE_HYBRID = 2,
-} SuspendMode;
-
-/*
- * Executes a command in a child process using g_spawn_sync,
- * returning an int >= 0 representing the exit status of the
- * process.
- *
- * If the program wasn't found in path, returns -1.
- *
- * If a problem happened when creating the child process,
- * returns -1 and errp is set.
- */
-static int run_process_child(const char *command[], Error **errp)
-{
- int exit_status, spawn_flag;
- GError *g_err = NULL;
- bool success;
-
- spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
- G_SPAWN_STDERR_TO_DEV_NULL;
-
- success = g_spawn_sync(NULL, (char **)command, NULL, spawn_flag,
- NULL, NULL, NULL, NULL,
- &exit_status, &g_err);
-
- if (success) {
- return WEXITSTATUS(exit_status);
- }
-
- if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
- error_setg(errp, "failed to create child process, error '%s'",
- g_err->message);
- }
-
- g_error_free(g_err);
- return -1;
-}
-
-static bool systemd_supports_mode(SuspendMode mode, Error **errp)
-{
- const char *systemctl_args[3] = {"systemd-hibernate", "systemd-suspend",
- "systemd-hybrid-sleep"};
- const char *cmd[4] = {"systemctl", "status", systemctl_args[mode], NULL};
- int status;
-
- status = run_process_child(cmd, errp);
-
- /*
- * systemctl status uses LSB return codes so we can expect
- * status > 0 and be ok. To assert if the guest has support
- * for the selected suspend mode, status should be < 4. 4 is
- * the code for unknown service status, the return value when
- * the service does not exist. A common value is status = 3
- * (program is not running).
- */
- if (status > 0 && status < 4) {
- return true;
- }
-
- return false;
-}
-
-static void systemd_suspend(SuspendMode mode, Error **errp)
-{
- Error *local_err = NULL;
- const char *systemctl_args[3] = {"hibernate", "suspend", "hybrid-sleep"};
- const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL};
- int status;
-
- status = run_process_child(cmd, &local_err);
-
- if (status == 0) {
- return;
- }
-
- if ((status == -1) && !local_err) {
- error_setg(errp, "the helper program 'systemctl %s' was not found",
- systemctl_args[mode]);
- return;
- }
-
- if (local_err) {
- error_propagate(errp, local_err);
- } else {
- error_setg(errp, "the helper program 'systemctl %s' returned an "
- "unexpected exit status code (%d)",
- systemctl_args[mode], status);
- }
-}
-
-static bool pmutils_supports_mode(SuspendMode mode, Error **errp)
-{
- Error *local_err = NULL;
- const char *pmutils_args[3] = {"--hibernate", "--suspend",
- "--suspend-hybrid"};
- const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL};
- int status;
-
- status = run_process_child(cmd, &local_err);
-
- if (status == SUSPEND_SUPPORTED) {
- return true;
- }
-
- if ((status == -1) && !local_err) {
- return false;
- }
-
- if (local_err) {
- error_propagate(errp, local_err);
- } else {
- error_setg(errp,
- "the helper program '%s' returned an unexpected exit"
- " status code (%d)", "pm-is-supported", status);
- }
-
- return false;
-}
-
-static void pmutils_suspend(SuspendMode mode, Error **errp)
-{
- Error *local_err = NULL;
- const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
- "pm-suspend-hybrid"};
- const char *cmd[2] = {pmutils_binaries[mode], NULL};
- int status;
-
- status = run_process_child(cmd, &local_err);
-
- if (status == 0) {
- return;
- }
-
- if ((status == -1) && !local_err) {
- error_setg(errp, "the helper program '%s' was not found",
- pmutils_binaries[mode]);
- return;
- }
-
- if (local_err) {
- error_propagate(errp, local_err);
- } else {
- error_setg(errp,
- "the helper program '%s' returned an unexpected exit"
- " status code (%d)", pmutils_binaries[mode], status);
- }
-}
-
-static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
-{
- const char *sysfile_strs[3] = {"disk", "mem", NULL};
- const char *sysfile_str = sysfile_strs[mode];
- char buf[32]; /* hopefully big enough */
- int fd;
- ssize_t ret;
-
- if (!sysfile_str) {
- error_setg(errp, "unknown guest suspend mode");
- return false;
- }
-
- fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
- if (fd < 0) {
- return false;
- }
-
- ret = read(fd, buf, sizeof(buf) - 1);
- close(fd);
- if (ret <= 0) {
- return false;
- }
- buf[ret] = '\0';
-
- if (strstr(buf, sysfile_str)) {
- return true;
- }
- return false;
-}
-
-static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
-{
- g_autoptr(GError) local_gerr = NULL;
- const char *sysfile_strs[3] = {"disk", "mem", NULL};
- const char *sysfile_str = sysfile_strs[mode];
-
- if (!sysfile_str) {
- error_setg(errp, "unknown guest suspend mode");
- return;
- }
-
- if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str,
- -1, &local_gerr)) {
- error_setg(errp, "suspend: cannot write to '%s': %s",
- LINUX_SYS_STATE_FILE, local_gerr->message);
- return;
- }
-}
-
-static void guest_suspend(SuspendMode mode, Error **errp)
-{
- Error *local_err = NULL;
- bool mode_supported = false;
-
- if (systemd_supports_mode(mode, &local_err)) {
- mode_supported = true;
- systemd_suspend(mode, &local_err);
-
- if (!local_err) {
- return;
- }
- }
-
- error_free(local_err);
- local_err = NULL;
-
- if (pmutils_supports_mode(mode, &local_err)) {
- mode_supported = true;
- pmutils_suspend(mode, &local_err);
-
- if (!local_err) {
- return;
- }
- }
-
- error_free(local_err);
- local_err = NULL;
-
- if (linux_sys_state_supports_mode(mode, &local_err)) {
- mode_supported = true;
- linux_sys_state_suspend(mode, &local_err);
- }
-
- if (!mode_supported) {
- error_free(local_err);
- error_setg(errp,
- "the requested suspend mode is not supported by the guest");
- } else {
- error_propagate(errp, local_err);
- }
-}
-
-void qmp_guest_suspend_disk(Error **errp)
-{
- guest_suspend(SUSPEND_MODE_DISK, errp);
-}
-
-void qmp_guest_suspend_ram(Error **errp)
-{
- guest_suspend(SUSPEND_MODE_RAM, errp);
-}
-
-void qmp_guest_suspend_hybrid(Error **errp)
-{
- guest_suspend(SUSPEND_MODE_HYBRID, errp);
-}
-
#endif /* __linux__ */
#if defined(__linux__) || defined(__FreeBSD__)
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 04/22] qga: move linux fs/disk command impls to commands-linux.c
2024-06-13 15:01 [PATCH v2 00/22] qga: clean up command source locations and conditionals Daniel P. Berrangé
` (2 preceding siblings ...)
2024-06-13 15:01 ` [PATCH v2 03/22] qga: move linux suspend " Daniel P. Berrangé
@ 2024-06-13 15:01 ` Daniel P. Berrangé
2024-07-03 8:46 ` Philippe Mathieu-Daudé
2024-07-12 8:29 ` Konstantin Kostiuk
2024-06-13 15:01 ` [PATCH v2 05/22] qga: move linux disk/cpu stats " Daniel P. Berrangé
` (3 subsequent siblings)
7 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:01 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Paolo Bonzini,
Daniel P. Berrangé, Manos Pitsidianakis
The qmp_guest_{fstrim, get_fsinfo, get_disks} command impls in
commands-posix.c are surrounded by '#ifdef __linux__' so should
instead live in commands-linux.c
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-linux.c | 904 ++++++++++++++++++++++++++++++++++++++++++
qga/commands-posix.c | 909 -------------------------------------------
2 files changed, 904 insertions(+), 909 deletions(-)
diff --git a/qga/commands-linux.c b/qga/commands-linux.c
index 3fabf54882..084e6c9e85 100644
--- a/qga/commands-linux.c
+++ b/qga/commands-linux.c
@@ -14,10 +14,21 @@
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qga-qapi-commands.h"
+#include "qapi/error.h"
+#include "qapi/qmp/qerror.h"
#include "commands-common.h"
#include "cutils.h"
#include <mntent.h>
#include <sys/ioctl.h>
+#include <mntent.h>
+#include <linux/nvme_ioctl.h>
+#include "block/nvme.h"
+
+#ifdef CONFIG_LIBUDEV
+#include <libudev.h>
+#endif
+
+#include <sys/statvfs.h>
#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
static int dev_major_minor(const char *devpath,
@@ -286,6 +297,899 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp)
}
#endif /* CONFIG_FSFREEZE */
+#if defined(CONFIG_FSFREEZE)
+
+static char *get_pci_driver(char const *syspath, int pathlen, Error **errp)
+{
+ char *path;
+ char *dpath;
+ char *driver = NULL;
+ char buf[PATH_MAX];
+ ssize_t len;
+
+ path = g_strndup(syspath, pathlen);
+ dpath = g_strdup_printf("%s/driver", path);
+ len = readlink(dpath, buf, sizeof(buf) - 1);
+ if (len != -1) {
+ buf[len] = 0;
+ driver = g_path_get_basename(buf);
+ }
+ g_free(dpath);
+ g_free(path);
+ return driver;
+}
+
+static int compare_uint(const void *_a, const void *_b)
+{
+ unsigned int a = *(unsigned int *)_a;
+ unsigned int b = *(unsigned int *)_b;
+
+ return a < b ? -1 : a > b ? 1 : 0;
+}
+
+/* Walk the specified sysfs and build a sorted list of host or ata numbers */
+static int build_hosts(char const *syspath, char const *host, bool ata,
+ unsigned int *hosts, int hosts_max, Error **errp)
+{
+ char *path;
+ DIR *dir;
+ struct dirent *entry;
+ int i = 0;
+
+ path = g_strndup(syspath, host - syspath);
+ dir = opendir(path);
+ if (!dir) {
+ error_setg_errno(errp, errno, "opendir(\"%s\")", path);
+ g_free(path);
+ return -1;
+ }
+
+ while (i < hosts_max) {
+ entry = readdir(dir);
+ if (!entry) {
+ break;
+ }
+ if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
+ ++i;
+ } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) {
+ ++i;
+ }
+ }
+
+ qsort(hosts, i, sizeof(hosts[0]), compare_uint);
+
+ g_free(path);
+ closedir(dir);
+ return i;
+}
+
+/*
+ * Store disk device info for devices on the PCI bus.
+ * Returns true if information has been stored, or false for failure.
+ */
+static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
+ GuestDiskAddress *disk,
+ Error **errp)
+{
+ unsigned int pci[4], host, hosts[8], tgt[3];
+ int i, nhosts = 0, pcilen;
+ GuestPCIAddress *pciaddr = disk->pci_controller;
+ bool has_ata = false, has_host = false, has_tgt = false;
+ char *p, *q, *driver = NULL;
+ bool ret = false;
+
+ p = strstr(syspath, "/devices/pci");
+ if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
+ pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
+ g_debug("only pci device is supported: sysfs path '%s'", syspath);
+ return false;
+ }
+
+ p += 12 + pcilen;
+ while (true) {
+ driver = get_pci_driver(syspath, p - syspath, errp);
+ if (driver && (g_str_equal(driver, "ata_piix") ||
+ g_str_equal(driver, "sym53c8xx") ||
+ g_str_equal(driver, "virtio-pci") ||
+ g_str_equal(driver, "ahci") ||
+ g_str_equal(driver, "nvme") ||
+ g_str_equal(driver, "xhci_hcd") ||
+ g_str_equal(driver, "ehci-pci"))) {
+ break;
+ }
+
+ g_free(driver);
+ if (sscanf(p, "/%x:%x:%x.%x%n",
+ pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) {
+ p += pcilen;
+ continue;
+ }
+
+ g_debug("unsupported driver or sysfs path '%s'", syspath);
+ return false;
+ }
+
+ p = strstr(syspath, "/target");
+ if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
+ tgt, tgt + 1, tgt + 2) == 3) {
+ has_tgt = true;
+ }
+
+ p = strstr(syspath, "/ata");
+ if (p) {
+ q = p + 4;
+ has_ata = true;
+ } else {
+ p = strstr(syspath, "/host");
+ q = p + 5;
+ }
+ if (p && sscanf(q, "%u", &host) == 1) {
+ has_host = true;
+ nhosts = build_hosts(syspath, p, has_ata, hosts,
+ ARRAY_SIZE(hosts), errp);
+ if (nhosts < 0) {
+ goto cleanup;
+ }
+ }
+
+ pciaddr->domain = pci[0];
+ pciaddr->bus = pci[1];
+ pciaddr->slot = pci[2];
+ pciaddr->function = pci[3];
+
+ if (strcmp(driver, "ata_piix") == 0) {
+ /* a host per ide bus, target*:0:<unit>:0 */
+ if (!has_host || !has_tgt) {
+ g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
+ goto cleanup;
+ }
+ for (i = 0; i < nhosts; i++) {
+ if (host == hosts[i]) {
+ disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
+ disk->bus = i;
+ disk->unit = tgt[1];
+ break;
+ }
+ }
+ if (i >= nhosts) {
+ g_debug("no host for '%s' (driver '%s')", syspath, driver);
+ goto cleanup;
+ }
+ } else if (strcmp(driver, "sym53c8xx") == 0) {
+ /* scsi(LSI Logic): target*:0:<unit>:0 */
+ if (!has_tgt) {
+ g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
+ goto cleanup;
+ }
+ disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
+ disk->unit = tgt[1];
+ } else if (strcmp(driver, "virtio-pci") == 0) {
+ if (has_tgt) {
+ /* virtio-scsi: target*:0:0:<unit> */
+ disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
+ disk->unit = tgt[2];
+ } else {
+ /* virtio-blk: 1 disk per 1 device */
+ disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
+ }
+ } else if (strcmp(driver, "ahci") == 0) {
+ /* ahci: 1 host per 1 unit */
+ if (!has_host || !has_tgt) {
+ g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
+ goto cleanup;
+ }
+ for (i = 0; i < nhosts; i++) {
+ if (host == hosts[i]) {
+ disk->unit = i;
+ disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
+ break;
+ }
+ }
+ if (i >= nhosts) {
+ g_debug("no host for '%s' (driver '%s')", syspath, driver);
+ goto cleanup;
+ }
+ } else if (strcmp(driver, "nvme") == 0) {
+ disk->bus_type = GUEST_DISK_BUS_TYPE_NVME;
+ } else if (strcmp(driver, "ehci-pci") == 0 || strcmp(driver, "xhci_hcd") == 0) {
+ disk->bus_type = GUEST_DISK_BUS_TYPE_USB;
+ } else {
+ g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
+ goto cleanup;
+ }
+
+ ret = true;
+
+cleanup:
+ g_free(driver);
+ return ret;
+}
+
+/*
+ * Store disk device info for non-PCI virtio devices (for example s390x
+ * channel I/O devices). Returns true if information has been stored, or
+ * false for failure.
+ */
+static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath,
+ GuestDiskAddress *disk,
+ Error **errp)
+{
+ unsigned int tgt[3];
+ char *p;
+
+ if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) {
+ g_debug("Unsupported virtio device '%s'", syspath);
+ return false;
+ }
+
+ p = strstr(syspath, "/target");
+ if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
+ &tgt[0], &tgt[1], &tgt[2]) == 3) {
+ /* virtio-scsi: target*:0:<target>:<unit> */
+ disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
+ disk->bus = tgt[0];
+ disk->target = tgt[1];
+ disk->unit = tgt[2];
+ } else {
+ /* virtio-blk: 1 disk per 1 device */
+ disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
+ }
+
+ return true;
+}
+
+/*
+ * Store disk device info for CCW devices (s390x channel I/O devices).
+ * Returns true if information has been stored, or false for failure.
+ */
+static bool build_guest_fsinfo_for_ccw_dev(char const *syspath,
+ GuestDiskAddress *disk,
+ Error **errp)
+{
+ unsigned int cssid, ssid, subchno, devno;
+ char *p;
+
+ p = strstr(syspath, "/devices/css");
+ if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/",
+ &cssid, &ssid, &subchno, &devno) < 4) {
+ g_debug("could not parse ccw device sysfs path: %s", syspath);
+ return false;
+ }
+
+ disk->ccw_address = g_new0(GuestCCWAddress, 1);
+ disk->ccw_address->cssid = cssid;
+ disk->ccw_address->ssid = ssid;
+ disk->ccw_address->subchno = subchno;
+ disk->ccw_address->devno = devno;
+
+ if (strstr(p, "/virtio")) {
+ build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
+ }
+
+ return true;
+}
+
+/* Store disk device info specified by @sysfs into @fs */
+static void build_guest_fsinfo_for_real_device(char const *syspath,
+ GuestFilesystemInfo *fs,
+ Error **errp)
+{
+ GuestDiskAddress *disk;
+ GuestPCIAddress *pciaddr;
+ bool has_hwinf;
+#ifdef CONFIG_LIBUDEV
+ struct udev *udev = NULL;
+ struct udev_device *udevice = NULL;
+#endif
+
+ pciaddr = g_new0(GuestPCIAddress, 1);
+ pciaddr->domain = -1; /* -1 means field is invalid */
+ pciaddr->bus = -1;
+ pciaddr->slot = -1;
+ pciaddr->function = -1;
+
+ disk = g_new0(GuestDiskAddress, 1);
+ disk->pci_controller = pciaddr;
+ disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN;
+
+#ifdef CONFIG_LIBUDEV
+ udev = udev_new();
+ udevice = udev_device_new_from_syspath(udev, syspath);
+ if (udev == NULL || udevice == NULL) {
+ g_debug("failed to query udev");
+ } else {
+ const char *devnode, *serial;
+ devnode = udev_device_get_devnode(udevice);
+ if (devnode != NULL) {
+ disk->dev = g_strdup(devnode);
+ }
+ serial = udev_device_get_property_value(udevice, "ID_SERIAL");
+ if (serial != NULL && *serial != 0) {
+ disk->serial = g_strdup(serial);
+ }
+ }
+
+ udev_unref(udev);
+ udev_device_unref(udevice);
+#endif
+
+ if (strstr(syspath, "/devices/pci")) {
+ has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp);
+ } else if (strstr(syspath, "/devices/css")) {
+ has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp);
+ } else if (strstr(syspath, "/virtio")) {
+ has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
+ } else {
+ g_debug("Unsupported device type for '%s'", syspath);
+ has_hwinf = false;
+ }
+
+ if (has_hwinf || disk->dev || disk->serial) {
+ QAPI_LIST_PREPEND(fs->disk, disk);
+ } else {
+ qapi_free_GuestDiskAddress(disk);
+ }
+}
+
+static void build_guest_fsinfo_for_device(char const *devpath,
+ GuestFilesystemInfo *fs,
+ Error **errp);
+
+/* Store a list of slave devices of virtual volume specified by @syspath into
+ * @fs */
+static void build_guest_fsinfo_for_virtual_device(char const *syspath,
+ GuestFilesystemInfo *fs,
+ Error **errp)
+{
+ Error *err = NULL;
+ DIR *dir;
+ char *dirpath;
+ struct dirent *entry;
+
+ dirpath = g_strdup_printf("%s/slaves", syspath);
+ dir = opendir(dirpath);
+ if (!dir) {
+ if (errno != ENOENT) {
+ error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
+ }
+ g_free(dirpath);
+ return;
+ }
+
+ for (;;) {
+ errno = 0;
+ entry = readdir(dir);
+ if (entry == NULL) {
+ if (errno) {
+ error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath);
+ }
+ break;
+ }
+
+ if (entry->d_type == DT_LNK) {
+ char *path;
+
+ g_debug(" slave device '%s'", entry->d_name);
+ path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name);
+ build_guest_fsinfo_for_device(path, fs, &err);
+ g_free(path);
+
+ if (err) {
+ error_propagate(errp, err);
+ break;
+ }
+ }
+ }
+
+ g_free(dirpath);
+ closedir(dir);
+}
+
+static bool is_disk_virtual(const char *devpath, Error **errp)
+{
+ g_autofree char *syspath = realpath(devpath, NULL);
+
+ if (!syspath) {
+ error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
+ return false;
+ }
+ return strstr(syspath, "/devices/virtual/block/") != NULL;
+}
+
+/* Dispatch to functions for virtual/real device */
+static void build_guest_fsinfo_for_device(char const *devpath,
+ GuestFilesystemInfo *fs,
+ Error **errp)
+{
+ ERRP_GUARD();
+ g_autofree char *syspath = NULL;
+ bool is_virtual = false;
+
+ syspath = realpath(devpath, NULL);
+ if (!syspath) {
+ if (errno != ENOENT) {
+ error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
+ return;
+ }
+
+ /* ENOENT: This devpath may not exist because of container config */
+ if (!fs->name) {
+ fs->name = g_path_get_basename(devpath);
+ }
+ return;
+ }
+
+ if (!fs->name) {
+ fs->name = g_path_get_basename(syspath);
+ }
+
+ g_debug(" parse sysfs path '%s'", syspath);
+ is_virtual = is_disk_virtual(syspath, errp);
+ if (*errp != NULL) {
+ return;
+ }
+ if (is_virtual) {
+ build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
+ } else {
+ build_guest_fsinfo_for_real_device(syspath, fs, errp);
+ }
+}
+
+#ifdef CONFIG_LIBUDEV
+
+/*
+ * Wrapper around build_guest_fsinfo_for_device() for getting just
+ * the disk address.
+ */
+static GuestDiskAddress *get_disk_address(const char *syspath, Error **errp)
+{
+ g_autoptr(GuestFilesystemInfo) fs = NULL;
+
+ fs = g_new0(GuestFilesystemInfo, 1);
+ build_guest_fsinfo_for_device(syspath, fs, errp);
+ if (fs->disk != NULL) {
+ return g_steal_pointer(&fs->disk->value);
+ }
+ return NULL;
+}
+
+static char *get_alias_for_syspath(const char *syspath)
+{
+ struct udev *udev = NULL;
+ struct udev_device *udevice = NULL;
+ char *ret = NULL;
+
+ udev = udev_new();
+ if (udev == NULL) {
+ g_debug("failed to query udev");
+ goto out;
+ }
+ udevice = udev_device_new_from_syspath(udev, syspath);
+ if (udevice == NULL) {
+ g_debug("failed to query udev for path: %s", syspath);
+ goto out;
+ } else {
+ const char *alias = udev_device_get_property_value(
+ udevice, "DM_NAME");
+ /*
+ * NULL means there was an error and empty string means there is no
+ * alias. In case of no alias we return NULL instead of empty string.
+ */
+ if (alias == NULL) {
+ g_debug("failed to query udev for device alias for: %s",
+ syspath);
+ } else if (*alias != 0) {
+ ret = g_strdup(alias);
+ }
+ }
+
+out:
+ udev_unref(udev);
+ udev_device_unref(udevice);
+ return ret;
+}
+
+static char *get_device_for_syspath(const char *syspath)
+{
+ struct udev *udev = NULL;
+ struct udev_device *udevice = NULL;
+ char *ret = NULL;
+
+ udev = udev_new();
+ if (udev == NULL) {
+ g_debug("failed to query udev");
+ goto out;
+ }
+ udevice = udev_device_new_from_syspath(udev, syspath);
+ if (udevice == NULL) {
+ g_debug("failed to query udev for path: %s", syspath);
+ goto out;
+ } else {
+ ret = g_strdup(udev_device_get_devnode(udevice));
+ }
+
+out:
+ udev_unref(udev);
+ udev_device_unref(udevice);
+ return ret;
+}
+
+static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk)
+{
+ g_autofree char *deps_dir = NULL;
+ const gchar *dep;
+ GDir *dp_deps = NULL;
+
+ /* List dependent disks */
+ deps_dir = g_strdup_printf("%s/slaves", disk_dir);
+ g_debug(" listing entries in: %s", deps_dir);
+ dp_deps = g_dir_open(deps_dir, 0, NULL);
+ if (dp_deps == NULL) {
+ g_debug("failed to list entries in %s", deps_dir);
+ return;
+ }
+ disk->has_dependencies = true;
+ while ((dep = g_dir_read_name(dp_deps)) != NULL) {
+ g_autofree char *dep_dir = NULL;
+ char *dev_name;
+
+ /* Add dependent disks */
+ dep_dir = g_strdup_printf("%s/%s", deps_dir, dep);
+ dev_name = get_device_for_syspath(dep_dir);
+ if (dev_name != NULL) {
+ g_debug(" adding dependent device: %s", dev_name);
+ QAPI_LIST_PREPEND(disk->dependencies, dev_name);
+ }
+ }
+ g_dir_close(dp_deps);
+}
+
+/*
+ * Detect partitions subdirectory, name is "<disk_name><number>" or
+ * "<disk_name>p<number>"
+ *
+ * @disk_name -- last component of /sys path (e.g. sda)
+ * @disk_dir -- sys path of the disk (e.g. /sys/block/sda)
+ * @disk_dev -- device node of the disk (e.g. /dev/sda)
+ */
+static GuestDiskInfoList *get_disk_partitions(
+ GuestDiskInfoList *list,
+ const char *disk_name, const char *disk_dir,
+ const char *disk_dev)
+{
+ GuestDiskInfoList *ret = list;
+ struct dirent *de_disk;
+ DIR *dp_disk = NULL;
+ size_t len = strlen(disk_name);
+
+ dp_disk = opendir(disk_dir);
+ while ((de_disk = readdir(dp_disk)) != NULL) {
+ g_autofree char *partition_dir = NULL;
+ char *dev_name;
+ GuestDiskInfo *partition;
+
+ if (!(de_disk->d_type & DT_DIR)) {
+ continue;
+ }
+
+ if (!(strncmp(disk_name, de_disk->d_name, len) == 0 &&
+ ((*(de_disk->d_name + len) == 'p' &&
+ isdigit(*(de_disk->d_name + len + 1))) ||
+ isdigit(*(de_disk->d_name + len))))) {
+ continue;
+ }
+
+ partition_dir = g_strdup_printf("%s/%s",
+ disk_dir, de_disk->d_name);
+ dev_name = get_device_for_syspath(partition_dir);
+ if (dev_name == NULL) {
+ g_debug("Failed to get device name for syspath: %s",
+ disk_dir);
+ continue;
+ }
+ partition = g_new0(GuestDiskInfo, 1);
+ partition->name = dev_name;
+ partition->partition = true;
+ partition->has_dependencies = true;
+ /* Add parent disk as dependent for easier tracking of hierarchy */
+ QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev));
+
+ QAPI_LIST_PREPEND(ret, partition);
+ }
+ closedir(dp_disk);
+
+ return ret;
+}
+
+static void get_nvme_smart(GuestDiskInfo *disk)
+{
+ int fd;
+ GuestNVMeSmart *smart;
+ NvmeSmartLog log = {0};
+ struct nvme_admin_cmd cmd = {
+ .opcode = NVME_ADM_CMD_GET_LOG_PAGE,
+ .nsid = NVME_NSID_BROADCAST,
+ .addr = (uintptr_t)&log,
+ .data_len = sizeof(log),
+ .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */
+ | (((sizeof(log) >> 2) - 1) << 16)
+ };
+
+ fd = qga_open_cloexec(disk->name, O_RDONLY, 0);
+ if (fd == -1) {
+ g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno));
+ return;
+ }
+
+ if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) {
+ g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno));
+ close(fd);
+ return;
+ }
+
+ disk->smart = g_new0(GuestDiskSmart, 1);
+ disk->smart->type = GUEST_DISK_BUS_TYPE_NVME;
+
+ smart = &disk->smart->u.nvme;
+ smart->critical_warning = log.critical_warning;
+ smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */
+ smart->available_spare = log.available_spare;
+ smart->available_spare_threshold = log.available_spare_threshold;
+ smart->percentage_used = log.percentage_used;
+ smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]);
+ smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]);
+ smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]);
+ smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]);
+ smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]);
+ smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]);
+ smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]);
+ smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]);
+ smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]);
+ smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]);
+ smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]);
+ smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]);
+ smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]);
+ smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]);
+ smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]);
+ smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]);
+ smart->media_errors_lo = le64_to_cpu(log.media_errors[0]);
+ smart->media_errors_hi = le64_to_cpu(log.media_errors[1]);
+ smart->number_of_error_log_entries_lo =
+ le64_to_cpu(log.number_of_error_log_entries[0]);
+ smart->number_of_error_log_entries_hi =
+ le64_to_cpu(log.number_of_error_log_entries[1]);
+
+ close(fd);
+}
+
+static void get_disk_smart(GuestDiskInfo *disk)
+{
+ if (disk->address
+ && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) {
+ get_nvme_smart(disk);
+ }
+}
+
+GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
+{
+ GuestDiskInfoList *ret = NULL;
+ GuestDiskInfo *disk;
+ DIR *dp = NULL;
+ struct dirent *de = NULL;
+
+ g_debug("listing /sys/block directory");
+ dp = opendir("/sys/block");
+ if (dp == NULL) {
+ error_setg_errno(errp, errno, "Can't open directory \"/sys/block\"");
+ return NULL;
+ }
+ while ((de = readdir(dp)) != NULL) {
+ g_autofree char *disk_dir = NULL, *line = NULL,
+ *size_path = NULL;
+ char *dev_name;
+ Error *local_err = NULL;
+ if (de->d_type != DT_LNK) {
+ g_debug(" skipping entry: %s", de->d_name);
+ continue;
+ }
+
+ /* Check size and skip zero-sized disks */
+ g_debug(" checking disk size");
+ size_path = g_strdup_printf("/sys/block/%s/size", de->d_name);
+ if (!g_file_get_contents(size_path, &line, NULL, NULL)) {
+ g_debug(" failed to read disk size");
+ continue;
+ }
+ if (g_strcmp0(line, "0\n") == 0) {
+ g_debug(" skipping zero-sized disk");
+ continue;
+ }
+
+ g_debug(" adding %s", de->d_name);
+ disk_dir = g_strdup_printf("/sys/block/%s", de->d_name);
+ dev_name = get_device_for_syspath(disk_dir);
+ if (dev_name == NULL) {
+ g_debug("Failed to get device name for syspath: %s",
+ disk_dir);
+ continue;
+ }
+ disk = g_new0(GuestDiskInfo, 1);
+ disk->name = dev_name;
+ disk->partition = false;
+ disk->alias = get_alias_for_syspath(disk_dir);
+ QAPI_LIST_PREPEND(ret, disk);
+
+ /* Get address for non-virtual devices */
+ bool is_virtual = is_disk_virtual(disk_dir, &local_err);
+ if (local_err != NULL) {
+ g_debug(" failed to check disk path, ignoring error: %s",
+ error_get_pretty(local_err));
+ error_free(local_err);
+ local_err = NULL;
+ /* Don't try to get the address */
+ is_virtual = true;
+ }
+ if (!is_virtual) {
+ disk->address = get_disk_address(disk_dir, &local_err);
+ if (local_err != NULL) {
+ g_debug(" failed to get device info, ignoring error: %s",
+ error_get_pretty(local_err));
+ error_free(local_err);
+ local_err = NULL;
+ }
+ }
+
+ get_disk_deps(disk_dir, disk);
+ get_disk_smart(disk);
+ ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
+ }
+
+ closedir(dp);
+
+ return ret;
+}
+
+#else
+
+GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
+{
+ error_setg(errp, QERR_UNSUPPORTED);
+ return NULL;
+}
+
+#endif
+
+/* Return a list of the disk device(s)' info which @mount lies on */
+static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
+ Error **errp)
+{
+ GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
+ struct statvfs buf;
+ unsigned long used, nonroot_total, fr_size;
+ char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
+ mount->devmajor, mount->devminor);
+
+ fs->mountpoint = g_strdup(mount->dirname);
+ fs->type = g_strdup(mount->devtype);
+ build_guest_fsinfo_for_device(devpath, fs, errp);
+
+ if (statvfs(fs->mountpoint, &buf) == 0) {
+ fr_size = buf.f_frsize;
+ used = buf.f_blocks - buf.f_bfree;
+ nonroot_total = used + buf.f_bavail;
+ fs->used_bytes = used * fr_size;
+ fs->total_bytes = nonroot_total * fr_size;
+ fs->total_bytes_privileged = buf.f_blocks * fr_size;
+
+ fs->has_total_bytes = true;
+ fs->has_total_bytes_privileged = true;
+ fs->has_used_bytes = true;
+ }
+
+ g_free(devpath);
+
+ return fs;
+}
+
+GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
+{
+ FsMountList mounts;
+ struct FsMount *mount;
+ GuestFilesystemInfoList *ret = NULL;
+ Error *local_err = NULL;
+
+ QTAILQ_INIT(&mounts);
+ if (!build_fs_mount_list(&mounts, &local_err)) {
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
+ QTAILQ_FOREACH(mount, &mounts, next) {
+ g_debug("Building guest fsinfo for '%s'", mount->dirname);
+
+ QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err));
+ if (local_err) {
+ error_propagate(errp, local_err);
+ qapi_free_GuestFilesystemInfoList(ret);
+ ret = NULL;
+ break;
+ }
+ }
+
+ free_fs_mount_list(&mounts);
+ return ret;
+}
+#endif /* CONFIG_FSFREEZE */
+
+#if defined(CONFIG_FSTRIM)
+/*
+ * Walk list of mounted file systems in the guest, and trim them.
+ */
+GuestFilesystemTrimResponse *
+qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
+{
+ GuestFilesystemTrimResponse *response;
+ GuestFilesystemTrimResult *result;
+ int ret = 0;
+ FsMountList mounts;
+ struct FsMount *mount;
+ int fd;
+ struct fstrim_range r;
+
+ slog("guest-fstrim called");
+
+ QTAILQ_INIT(&mounts);
+ if (!build_fs_mount_list(&mounts, errp)) {
+ return NULL;
+ }
+
+ response = g_malloc0(sizeof(*response));
+
+ QTAILQ_FOREACH(mount, &mounts, next) {
+ result = g_malloc0(sizeof(*result));
+ result->path = g_strdup(mount->dirname);
+
+ QAPI_LIST_PREPEND(response->paths, result);
+
+ fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0);
+ if (fd == -1) {
+ result->error = g_strdup_printf("failed to open: %s",
+ strerror(errno));
+ continue;
+ }
+
+ /* We try to cull filesystems we know won't work in advance, but other
+ * filesystems may not implement fstrim for less obvious reasons.
+ * These will report EOPNOTSUPP; while in some other cases ENOTTY
+ * will be reported (e.g. CD-ROMs).
+ * Any other error means an unexpected error.
+ */
+ r.start = 0;
+ r.len = -1;
+ r.minlen = has_minimum ? minimum : 0;
+ ret = ioctl(fd, FITRIM, &r);
+ if (ret == -1) {
+ if (errno == ENOTTY || errno == EOPNOTSUPP) {
+ result->error = g_strdup("trim not supported");
+ } else {
+ result->error = g_strdup_printf("failed to trim: %s",
+ strerror(errno));
+ }
+ close(fd);
+ continue;
+ }
+
+ result->has_minimum = true;
+ result->minimum = r.minlen;
+ result->has_trimmed = true;
+ result->trimmed = r.len;
+ close(fd);
+ }
+
+ free_fs_mount_list(&mounts);
+ return response;
+}
+#endif /* CONFIG_FSTRIM */
#define LINUX_SYS_STATE_FILE "/sys/power/state"
#define SUSPEND_SUPPORTED 0
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index ef21da63be..98aafc45f3 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -24,23 +24,12 @@
#include "qemu/base64.h"
#include "qemu/cutils.h"
#include "commands-common.h"
-#include "block/nvme.h"
#include "cutils.h"
#ifdef HAVE_UTMPX
#include <utmpx.h>
#endif
-#if defined(__linux__)
-#include <mntent.h>
-#include <sys/statvfs.h>
-#include <linux/nvme_ioctl.h>
-
-#ifdef CONFIG_LIBUDEV
-#include <libudev.h>
-#endif
-#endif
-
#ifdef HAVE_GETIFADDRS
#include <arpa/inet.h>
#include <sys/socket.h>
@@ -842,904 +831,6 @@ static void guest_fsfreeze_cleanup(void)
}
#endif
-/* linux-specific implementations. avoid this if at all possible. */
-#if defined(__linux__)
-#if defined(CONFIG_FSFREEZE)
-
-static char *get_pci_driver(char const *syspath, int pathlen, Error **errp)
-{
- char *path;
- char *dpath;
- char *driver = NULL;
- char buf[PATH_MAX];
- ssize_t len;
-
- path = g_strndup(syspath, pathlen);
- dpath = g_strdup_printf("%s/driver", path);
- len = readlink(dpath, buf, sizeof(buf) - 1);
- if (len != -1) {
- buf[len] = 0;
- driver = g_path_get_basename(buf);
- }
- g_free(dpath);
- g_free(path);
- return driver;
-}
-
-static int compare_uint(const void *_a, const void *_b)
-{
- unsigned int a = *(unsigned int *)_a;
- unsigned int b = *(unsigned int *)_b;
-
- return a < b ? -1 : a > b ? 1 : 0;
-}
-
-/* Walk the specified sysfs and build a sorted list of host or ata numbers */
-static int build_hosts(char const *syspath, char const *host, bool ata,
- unsigned int *hosts, int hosts_max, Error **errp)
-{
- char *path;
- DIR *dir;
- struct dirent *entry;
- int i = 0;
-
- path = g_strndup(syspath, host - syspath);
- dir = opendir(path);
- if (!dir) {
- error_setg_errno(errp, errno, "opendir(\"%s\")", path);
- g_free(path);
- return -1;
- }
-
- while (i < hosts_max) {
- entry = readdir(dir);
- if (!entry) {
- break;
- }
- if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
- ++i;
- } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) == 1) {
- ++i;
- }
- }
-
- qsort(hosts, i, sizeof(hosts[0]), compare_uint);
-
- g_free(path);
- closedir(dir);
- return i;
-}
-
-/*
- * Store disk device info for devices on the PCI bus.
- * Returns true if information has been stored, or false for failure.
- */
-static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
- GuestDiskAddress *disk,
- Error **errp)
-{
- unsigned int pci[4], host, hosts[8], tgt[3];
- int i, nhosts = 0, pcilen;
- GuestPCIAddress *pciaddr = disk->pci_controller;
- bool has_ata = false, has_host = false, has_tgt = false;
- char *p, *q, *driver = NULL;
- bool ret = false;
-
- p = strstr(syspath, "/devices/pci");
- if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
- pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
- g_debug("only pci device is supported: sysfs path '%s'", syspath);
- return false;
- }
-
- p += 12 + pcilen;
- while (true) {
- driver = get_pci_driver(syspath, p - syspath, errp);
- if (driver && (g_str_equal(driver, "ata_piix") ||
- g_str_equal(driver, "sym53c8xx") ||
- g_str_equal(driver, "virtio-pci") ||
- g_str_equal(driver, "ahci") ||
- g_str_equal(driver, "nvme") ||
- g_str_equal(driver, "xhci_hcd") ||
- g_str_equal(driver, "ehci-pci"))) {
- break;
- }
-
- g_free(driver);
- if (sscanf(p, "/%x:%x:%x.%x%n",
- pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) {
- p += pcilen;
- continue;
- }
-
- g_debug("unsupported driver or sysfs path '%s'", syspath);
- return false;
- }
-
- p = strstr(syspath, "/target");
- if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
- tgt, tgt + 1, tgt + 2) == 3) {
- has_tgt = true;
- }
-
- p = strstr(syspath, "/ata");
- if (p) {
- q = p + 4;
- has_ata = true;
- } else {
- p = strstr(syspath, "/host");
- q = p + 5;
- }
- if (p && sscanf(q, "%u", &host) == 1) {
- has_host = true;
- nhosts = build_hosts(syspath, p, has_ata, hosts,
- ARRAY_SIZE(hosts), errp);
- if (nhosts < 0) {
- goto cleanup;
- }
- }
-
- pciaddr->domain = pci[0];
- pciaddr->bus = pci[1];
- pciaddr->slot = pci[2];
- pciaddr->function = pci[3];
-
- if (strcmp(driver, "ata_piix") == 0) {
- /* a host per ide bus, target*:0:<unit>:0 */
- if (!has_host || !has_tgt) {
- g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
- goto cleanup;
- }
- for (i = 0; i < nhosts; i++) {
- if (host == hosts[i]) {
- disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
- disk->bus = i;
- disk->unit = tgt[1];
- break;
- }
- }
- if (i >= nhosts) {
- g_debug("no host for '%s' (driver '%s')", syspath, driver);
- goto cleanup;
- }
- } else if (strcmp(driver, "sym53c8xx") == 0) {
- /* scsi(LSI Logic): target*:0:<unit>:0 */
- if (!has_tgt) {
- g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
- goto cleanup;
- }
- disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
- disk->unit = tgt[1];
- } else if (strcmp(driver, "virtio-pci") == 0) {
- if (has_tgt) {
- /* virtio-scsi: target*:0:0:<unit> */
- disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
- disk->unit = tgt[2];
- } else {
- /* virtio-blk: 1 disk per 1 device */
- disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
- }
- } else if (strcmp(driver, "ahci") == 0) {
- /* ahci: 1 host per 1 unit */
- if (!has_host || !has_tgt) {
- g_debug("invalid sysfs path '%s' (driver '%s')", syspath, driver);
- goto cleanup;
- }
- for (i = 0; i < nhosts; i++) {
- if (host == hosts[i]) {
- disk->unit = i;
- disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
- break;
- }
- }
- if (i >= nhosts) {
- g_debug("no host for '%s' (driver '%s')", syspath, driver);
- goto cleanup;
- }
- } else if (strcmp(driver, "nvme") == 0) {
- disk->bus_type = GUEST_DISK_BUS_TYPE_NVME;
- } else if (strcmp(driver, "ehci-pci") == 0 || strcmp(driver, "xhci_hcd") == 0) {
- disk->bus_type = GUEST_DISK_BUS_TYPE_USB;
- } else {
- g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
- goto cleanup;
- }
-
- ret = true;
-
-cleanup:
- g_free(driver);
- return ret;
-}
-
-/*
- * Store disk device info for non-PCI virtio devices (for example s390x
- * channel I/O devices). Returns true if information has been stored, or
- * false for failure.
- */
-static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath,
- GuestDiskAddress *disk,
- Error **errp)
-{
- unsigned int tgt[3];
- char *p;
-
- if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) {
- g_debug("Unsupported virtio device '%s'", syspath);
- return false;
- }
-
- p = strstr(syspath, "/target");
- if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
- &tgt[0], &tgt[1], &tgt[2]) == 3) {
- /* virtio-scsi: target*:0:<target>:<unit> */
- disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
- disk->bus = tgt[0];
- disk->target = tgt[1];
- disk->unit = tgt[2];
- } else {
- /* virtio-blk: 1 disk per 1 device */
- disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
- }
-
- return true;
-}
-
-/*
- * Store disk device info for CCW devices (s390x channel I/O devices).
- * Returns true if information has been stored, or false for failure.
- */
-static bool build_guest_fsinfo_for_ccw_dev(char const *syspath,
- GuestDiskAddress *disk,
- Error **errp)
-{
- unsigned int cssid, ssid, subchno, devno;
- char *p;
-
- p = strstr(syspath, "/devices/css");
- if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/",
- &cssid, &ssid, &subchno, &devno) < 4) {
- g_debug("could not parse ccw device sysfs path: %s", syspath);
- return false;
- }
-
- disk->ccw_address = g_new0(GuestCCWAddress, 1);
- disk->ccw_address->cssid = cssid;
- disk->ccw_address->ssid = ssid;
- disk->ccw_address->subchno = subchno;
- disk->ccw_address->devno = devno;
-
- if (strstr(p, "/virtio")) {
- build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
- }
-
- return true;
-}
-
-/* Store disk device info specified by @sysfs into @fs */
-static void build_guest_fsinfo_for_real_device(char const *syspath,
- GuestFilesystemInfo *fs,
- Error **errp)
-{
- GuestDiskAddress *disk;
- GuestPCIAddress *pciaddr;
- bool has_hwinf;
-#ifdef CONFIG_LIBUDEV
- struct udev *udev = NULL;
- struct udev_device *udevice = NULL;
-#endif
-
- pciaddr = g_new0(GuestPCIAddress, 1);
- pciaddr->domain = -1; /* -1 means field is invalid */
- pciaddr->bus = -1;
- pciaddr->slot = -1;
- pciaddr->function = -1;
-
- disk = g_new0(GuestDiskAddress, 1);
- disk->pci_controller = pciaddr;
- disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN;
-
-#ifdef CONFIG_LIBUDEV
- udev = udev_new();
- udevice = udev_device_new_from_syspath(udev, syspath);
- if (udev == NULL || udevice == NULL) {
- g_debug("failed to query udev");
- } else {
- const char *devnode, *serial;
- devnode = udev_device_get_devnode(udevice);
- if (devnode != NULL) {
- disk->dev = g_strdup(devnode);
- }
- serial = udev_device_get_property_value(udevice, "ID_SERIAL");
- if (serial != NULL && *serial != 0) {
- disk->serial = g_strdup(serial);
- }
- }
-
- udev_unref(udev);
- udev_device_unref(udevice);
-#endif
-
- if (strstr(syspath, "/devices/pci")) {
- has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp);
- } else if (strstr(syspath, "/devices/css")) {
- has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp);
- } else if (strstr(syspath, "/virtio")) {
- has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
- } else {
- g_debug("Unsupported device type for '%s'", syspath);
- has_hwinf = false;
- }
-
- if (has_hwinf || disk->dev || disk->serial) {
- QAPI_LIST_PREPEND(fs->disk, disk);
- } else {
- qapi_free_GuestDiskAddress(disk);
- }
-}
-
-static void build_guest_fsinfo_for_device(char const *devpath,
- GuestFilesystemInfo *fs,
- Error **errp);
-
-/* Store a list of slave devices of virtual volume specified by @syspath into
- * @fs */
-static void build_guest_fsinfo_for_virtual_device(char const *syspath,
- GuestFilesystemInfo *fs,
- Error **errp)
-{
- Error *err = NULL;
- DIR *dir;
- char *dirpath;
- struct dirent *entry;
-
- dirpath = g_strdup_printf("%s/slaves", syspath);
- dir = opendir(dirpath);
- if (!dir) {
- if (errno != ENOENT) {
- error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
- }
- g_free(dirpath);
- return;
- }
-
- for (;;) {
- errno = 0;
- entry = readdir(dir);
- if (entry == NULL) {
- if (errno) {
- error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath);
- }
- break;
- }
-
- if (entry->d_type == DT_LNK) {
- char *path;
-
- g_debug(" slave device '%s'", entry->d_name);
- path = g_strdup_printf("%s/slaves/%s", syspath, entry->d_name);
- build_guest_fsinfo_for_device(path, fs, &err);
- g_free(path);
-
- if (err) {
- error_propagate(errp, err);
- break;
- }
- }
- }
-
- g_free(dirpath);
- closedir(dir);
-}
-
-static bool is_disk_virtual(const char *devpath, Error **errp)
-{
- g_autofree char *syspath = realpath(devpath, NULL);
-
- if (!syspath) {
- error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
- return false;
- }
- return strstr(syspath, "/devices/virtual/block/") != NULL;
-}
-
-/* Dispatch to functions for virtual/real device */
-static void build_guest_fsinfo_for_device(char const *devpath,
- GuestFilesystemInfo *fs,
- Error **errp)
-{
- ERRP_GUARD();
- g_autofree char *syspath = NULL;
- bool is_virtual = false;
-
- syspath = realpath(devpath, NULL);
- if (!syspath) {
- if (errno != ENOENT) {
- error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
- return;
- }
-
- /* ENOENT: This devpath may not exist because of container config */
- if (!fs->name) {
- fs->name = g_path_get_basename(devpath);
- }
- return;
- }
-
- if (!fs->name) {
- fs->name = g_path_get_basename(syspath);
- }
-
- g_debug(" parse sysfs path '%s'", syspath);
- is_virtual = is_disk_virtual(syspath, errp);
- if (*errp != NULL) {
- return;
- }
- if (is_virtual) {
- build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
- } else {
- build_guest_fsinfo_for_real_device(syspath, fs, errp);
- }
-}
-
-#ifdef CONFIG_LIBUDEV
-
-/*
- * Wrapper around build_guest_fsinfo_for_device() for getting just
- * the disk address.
- */
-static GuestDiskAddress *get_disk_address(const char *syspath, Error **errp)
-{
- g_autoptr(GuestFilesystemInfo) fs = NULL;
-
- fs = g_new0(GuestFilesystemInfo, 1);
- build_guest_fsinfo_for_device(syspath, fs, errp);
- if (fs->disk != NULL) {
- return g_steal_pointer(&fs->disk->value);
- }
- return NULL;
-}
-
-static char *get_alias_for_syspath(const char *syspath)
-{
- struct udev *udev = NULL;
- struct udev_device *udevice = NULL;
- char *ret = NULL;
-
- udev = udev_new();
- if (udev == NULL) {
- g_debug("failed to query udev");
- goto out;
- }
- udevice = udev_device_new_from_syspath(udev, syspath);
- if (udevice == NULL) {
- g_debug("failed to query udev for path: %s", syspath);
- goto out;
- } else {
- const char *alias = udev_device_get_property_value(
- udevice, "DM_NAME");
- /*
- * NULL means there was an error and empty string means there is no
- * alias. In case of no alias we return NULL instead of empty string.
- */
- if (alias == NULL) {
- g_debug("failed to query udev for device alias for: %s",
- syspath);
- } else if (*alias != 0) {
- ret = g_strdup(alias);
- }
- }
-
-out:
- udev_unref(udev);
- udev_device_unref(udevice);
- return ret;
-}
-
-static char *get_device_for_syspath(const char *syspath)
-{
- struct udev *udev = NULL;
- struct udev_device *udevice = NULL;
- char *ret = NULL;
-
- udev = udev_new();
- if (udev == NULL) {
- g_debug("failed to query udev");
- goto out;
- }
- udevice = udev_device_new_from_syspath(udev, syspath);
- if (udevice == NULL) {
- g_debug("failed to query udev for path: %s", syspath);
- goto out;
- } else {
- ret = g_strdup(udev_device_get_devnode(udevice));
- }
-
-out:
- udev_unref(udev);
- udev_device_unref(udevice);
- return ret;
-}
-
-static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk)
-{
- g_autofree char *deps_dir = NULL;
- const gchar *dep;
- GDir *dp_deps = NULL;
-
- /* List dependent disks */
- deps_dir = g_strdup_printf("%s/slaves", disk_dir);
- g_debug(" listing entries in: %s", deps_dir);
- dp_deps = g_dir_open(deps_dir, 0, NULL);
- if (dp_deps == NULL) {
- g_debug("failed to list entries in %s", deps_dir);
- return;
- }
- disk->has_dependencies = true;
- while ((dep = g_dir_read_name(dp_deps)) != NULL) {
- g_autofree char *dep_dir = NULL;
- char *dev_name;
-
- /* Add dependent disks */
- dep_dir = g_strdup_printf("%s/%s", deps_dir, dep);
- dev_name = get_device_for_syspath(dep_dir);
- if (dev_name != NULL) {
- g_debug(" adding dependent device: %s", dev_name);
- QAPI_LIST_PREPEND(disk->dependencies, dev_name);
- }
- }
- g_dir_close(dp_deps);
-}
-
-/*
- * Detect partitions subdirectory, name is "<disk_name><number>" or
- * "<disk_name>p<number>"
- *
- * @disk_name -- last component of /sys path (e.g. sda)
- * @disk_dir -- sys path of the disk (e.g. /sys/block/sda)
- * @disk_dev -- device node of the disk (e.g. /dev/sda)
- */
-static GuestDiskInfoList *get_disk_partitions(
- GuestDiskInfoList *list,
- const char *disk_name, const char *disk_dir,
- const char *disk_dev)
-{
- GuestDiskInfoList *ret = list;
- struct dirent *de_disk;
- DIR *dp_disk = NULL;
- size_t len = strlen(disk_name);
-
- dp_disk = opendir(disk_dir);
- while ((de_disk = readdir(dp_disk)) != NULL) {
- g_autofree char *partition_dir = NULL;
- char *dev_name;
- GuestDiskInfo *partition;
-
- if (!(de_disk->d_type & DT_DIR)) {
- continue;
- }
-
- if (!(strncmp(disk_name, de_disk->d_name, len) == 0 &&
- ((*(de_disk->d_name + len) == 'p' &&
- isdigit(*(de_disk->d_name + len + 1))) ||
- isdigit(*(de_disk->d_name + len))))) {
- continue;
- }
-
- partition_dir = g_strdup_printf("%s/%s",
- disk_dir, de_disk->d_name);
- dev_name = get_device_for_syspath(partition_dir);
- if (dev_name == NULL) {
- g_debug("Failed to get device name for syspath: %s",
- disk_dir);
- continue;
- }
- partition = g_new0(GuestDiskInfo, 1);
- partition->name = dev_name;
- partition->partition = true;
- partition->has_dependencies = true;
- /* Add parent disk as dependent for easier tracking of hierarchy */
- QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev));
-
- QAPI_LIST_PREPEND(ret, partition);
- }
- closedir(dp_disk);
-
- return ret;
-}
-
-static void get_nvme_smart(GuestDiskInfo *disk)
-{
- int fd;
- GuestNVMeSmart *smart;
- NvmeSmartLog log = {0};
- struct nvme_admin_cmd cmd = {
- .opcode = NVME_ADM_CMD_GET_LOG_PAGE,
- .nsid = NVME_NSID_BROADCAST,
- .addr = (uintptr_t)&log,
- .data_len = sizeof(log),
- .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */
- | (((sizeof(log) >> 2) - 1) << 16)
- };
-
- fd = qga_open_cloexec(disk->name, O_RDONLY, 0);
- if (fd == -1) {
- g_debug("Failed to open device: %s: %s", disk->name, g_strerror(errno));
- return;
- }
-
- if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) {
- g_debug("Failed to get smart: %s: %s", disk->name, g_strerror(errno));
- close(fd);
- return;
- }
-
- disk->smart = g_new0(GuestDiskSmart, 1);
- disk->smart->type = GUEST_DISK_BUS_TYPE_NVME;
-
- smart = &disk->smart->u.nvme;
- smart->critical_warning = log.critical_warning;
- smart->temperature = lduw_le_p(&log.temperature); /* unaligned field */
- smart->available_spare = log.available_spare;
- smart->available_spare_threshold = log.available_spare_threshold;
- smart->percentage_used = log.percentage_used;
- smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]);
- smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]);
- smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]);
- smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]);
- smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]);
- smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]);
- smart->host_write_commands_lo = le64_to_cpu(log.host_write_commands[0]);
- smart->host_write_commands_hi = le64_to_cpu(log.host_write_commands[1]);
- smart->controller_busy_time_lo = le64_to_cpu(log.controller_busy_time[0]);
- smart->controller_busy_time_hi = le64_to_cpu(log.controller_busy_time[1]);
- smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]);
- smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]);
- smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]);
- smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]);
- smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]);
- smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]);
- smart->media_errors_lo = le64_to_cpu(log.media_errors[0]);
- smart->media_errors_hi = le64_to_cpu(log.media_errors[1]);
- smart->number_of_error_log_entries_lo =
- le64_to_cpu(log.number_of_error_log_entries[0]);
- smart->number_of_error_log_entries_hi =
- le64_to_cpu(log.number_of_error_log_entries[1]);
-
- close(fd);
-}
-
-static void get_disk_smart(GuestDiskInfo *disk)
-{
- if (disk->address
- && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) {
- get_nvme_smart(disk);
- }
-}
-
-GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
-{
- GuestDiskInfoList *ret = NULL;
- GuestDiskInfo *disk;
- DIR *dp = NULL;
- struct dirent *de = NULL;
-
- g_debug("listing /sys/block directory");
- dp = opendir("/sys/block");
- if (dp == NULL) {
- error_setg_errno(errp, errno, "Can't open directory \"/sys/block\"");
- return NULL;
- }
- while ((de = readdir(dp)) != NULL) {
- g_autofree char *disk_dir = NULL, *line = NULL,
- *size_path = NULL;
- char *dev_name;
- Error *local_err = NULL;
- if (de->d_type != DT_LNK) {
- g_debug(" skipping entry: %s", de->d_name);
- continue;
- }
-
- /* Check size and skip zero-sized disks */
- g_debug(" checking disk size");
- size_path = g_strdup_printf("/sys/block/%s/size", de->d_name);
- if (!g_file_get_contents(size_path, &line, NULL, NULL)) {
- g_debug(" failed to read disk size");
- continue;
- }
- if (g_strcmp0(line, "0\n") == 0) {
- g_debug(" skipping zero-sized disk");
- continue;
- }
-
- g_debug(" adding %s", de->d_name);
- disk_dir = g_strdup_printf("/sys/block/%s", de->d_name);
- dev_name = get_device_for_syspath(disk_dir);
- if (dev_name == NULL) {
- g_debug("Failed to get device name for syspath: %s",
- disk_dir);
- continue;
- }
- disk = g_new0(GuestDiskInfo, 1);
- disk->name = dev_name;
- disk->partition = false;
- disk->alias = get_alias_for_syspath(disk_dir);
- QAPI_LIST_PREPEND(ret, disk);
-
- /* Get address for non-virtual devices */
- bool is_virtual = is_disk_virtual(disk_dir, &local_err);
- if (local_err != NULL) {
- g_debug(" failed to check disk path, ignoring error: %s",
- error_get_pretty(local_err));
- error_free(local_err);
- local_err = NULL;
- /* Don't try to get the address */
- is_virtual = true;
- }
- if (!is_virtual) {
- disk->address = get_disk_address(disk_dir, &local_err);
- if (local_err != NULL) {
- g_debug(" failed to get device info, ignoring error: %s",
- error_get_pretty(local_err));
- error_free(local_err);
- local_err = NULL;
- }
- }
-
- get_disk_deps(disk_dir, disk);
- get_disk_smart(disk);
- ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
- }
-
- closedir(dp);
-
- return ret;
-}
-
-#else
-
-GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-#endif
-
-/* Return a list of the disk device(s)' info which @mount lies on */
-static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
- Error **errp)
-{
- GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
- struct statvfs buf;
- unsigned long used, nonroot_total, fr_size;
- char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
- mount->devmajor, mount->devminor);
-
- fs->mountpoint = g_strdup(mount->dirname);
- fs->type = g_strdup(mount->devtype);
- build_guest_fsinfo_for_device(devpath, fs, errp);
-
- if (statvfs(fs->mountpoint, &buf) == 0) {
- fr_size = buf.f_frsize;
- used = buf.f_blocks - buf.f_bfree;
- nonroot_total = used + buf.f_bavail;
- fs->used_bytes = used * fr_size;
- fs->total_bytes = nonroot_total * fr_size;
- fs->total_bytes_privileged = buf.f_blocks * fr_size;
-
- fs->has_total_bytes = true;
- fs->has_total_bytes_privileged = true;
- fs->has_used_bytes = true;
- }
-
- g_free(devpath);
-
- return fs;
-}
-
-GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
-{
- FsMountList mounts;
- struct FsMount *mount;
- GuestFilesystemInfoList *ret = NULL;
- Error *local_err = NULL;
-
- QTAILQ_INIT(&mounts);
- if (!build_fs_mount_list(&mounts, &local_err)) {
- error_propagate(errp, local_err);
- return NULL;
- }
-
- QTAILQ_FOREACH(mount, &mounts, next) {
- g_debug("Building guest fsinfo for '%s'", mount->dirname);
-
- QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err));
- if (local_err) {
- error_propagate(errp, local_err);
- qapi_free_GuestFilesystemInfoList(ret);
- ret = NULL;
- break;
- }
- }
-
- free_fs_mount_list(&mounts);
- return ret;
-}
-#endif /* CONFIG_FSFREEZE */
-
-#if defined(CONFIG_FSTRIM)
-/*
- * Walk list of mounted file systems in the guest, and trim them.
- */
-GuestFilesystemTrimResponse *
-qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
-{
- GuestFilesystemTrimResponse *response;
- GuestFilesystemTrimResult *result;
- int ret = 0;
- FsMountList mounts;
- struct FsMount *mount;
- int fd;
- struct fstrim_range r;
-
- slog("guest-fstrim called");
-
- QTAILQ_INIT(&mounts);
- if (!build_fs_mount_list(&mounts, errp)) {
- return NULL;
- }
-
- response = g_malloc0(sizeof(*response));
-
- QTAILQ_FOREACH(mount, &mounts, next) {
- result = g_malloc0(sizeof(*result));
- result->path = g_strdup(mount->dirname);
-
- QAPI_LIST_PREPEND(response->paths, result);
-
- fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0);
- if (fd == -1) {
- result->error = g_strdup_printf("failed to open: %s",
- strerror(errno));
- continue;
- }
-
- /* We try to cull filesystems we know won't work in advance, but other
- * filesystems may not implement fstrim for less obvious reasons.
- * These will report EOPNOTSUPP; while in some other cases ENOTTY
- * will be reported (e.g. CD-ROMs).
- * Any other error means an unexpected error.
- */
- r.start = 0;
- r.len = -1;
- r.minlen = has_minimum ? minimum : 0;
- ret = ioctl(fd, FITRIM, &r);
- if (ret == -1) {
- if (errno == ENOTTY || errno == EOPNOTSUPP) {
- result->error = g_strdup("trim not supported");
- } else {
- result->error = g_strdup_printf("failed to trim: %s",
- strerror(errno));
- }
- close(fd);
- continue;
- }
-
- result->has_minimum = true;
- result->minimum = r.minlen;
- result->has_trimmed = true;
- result->trimmed = r.len;
- close(fd);
- }
-
- free_fs_mount_list(&mounts);
- return response;
-}
-#endif /* CONFIG_FSTRIM */
-
-#endif /* __linux__ */
-
#if defined(__linux__) || defined(__FreeBSD__)
void qmp_guest_set_user_password(const char *username,
const char *password,
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 05/22] qga: move linux disk/cpu stats command impls to commands-linux.c
2024-06-13 15:01 [PATCH v2 00/22] qga: clean up command source locations and conditionals Daniel P. Berrangé
` (3 preceding siblings ...)
2024-06-13 15:01 ` [PATCH v2 04/22] qga: move linux fs/disk " Daniel P. Berrangé
@ 2024-06-13 15:01 ` Daniel P. Berrangé
2024-07-03 8:25 ` Philippe Mathieu-Daudé
2024-07-12 8:33 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (2 subsequent siblings)
7 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:01 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Paolo Bonzini,
Daniel P. Berrangé
The qmp_guest_{diskstats,cpustats} command impls in
commands-posix.c are surrounded by '#ifdef __linux__' so should
instead live in commands-linux.c
This also removes a "#ifdef CONFIG_LINUX" that was nested inside
a "#ifdef __linux__".
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-linux.c | 195 ++++++++++++++++++++++++++++++++++++++++++
qga/commands-posix.c | 199 -------------------------------------------
2 files changed, 195 insertions(+), 199 deletions(-)
diff --git a/qga/commands-linux.c b/qga/commands-linux.c
index 084e6c9e85..c0e8bd4062 100644
--- a/qga/commands-linux.c
+++ b/qga/commands-linux.c
@@ -1594,3 +1594,198 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
return processed;
}
+
+#define MAX_NAME_LEN 128
+static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp)
+{
+ GuestDiskStatsInfoList *head = NULL, **tail = &head;
+ const char *diskstats = "/proc/diskstats";
+ FILE *fp;
+ size_t n;
+ char *line = NULL;
+
+ fp = fopen(diskstats, "r");
+ if (fp == NULL) {
+ error_setg_errno(errp, errno, "open(\"%s\")", diskstats);
+ return NULL;
+ }
+
+ while (getline(&line, &n, fp) != -1) {
+ g_autofree GuestDiskStatsInfo *diskstatinfo = NULL;
+ g_autofree GuestDiskStats *diskstat = NULL;
+ char dev_name[MAX_NAME_LEN];
+ unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks;
+ unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios;
+ unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec;
+ unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
+ unsigned int major, minor;
+ int i;
+
+ i = sscanf(line, "%u %u %s %lu %lu %lu"
+ "%lu %lu %lu %lu %u %u %u %u"
+ "%lu %lu %lu %u %lu %u",
+ &major, &minor, dev_name,
+ &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios,
+ &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec,
+ &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
+ &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
+ &fl_ios, &fl_ticks);
+
+ if (i < 7) {
+ continue;
+ }
+
+ diskstatinfo = g_new0(GuestDiskStatsInfo, 1);
+ diskstatinfo->name = g_strdup(dev_name);
+ diskstatinfo->major = major;
+ diskstatinfo->minor = minor;
+
+ diskstat = g_new0(GuestDiskStats, 1);
+ if (i == 7) {
+ diskstat->has_read_ios = true;
+ diskstat->read_ios = rd_ios;
+ diskstat->has_read_sectors = true;
+ diskstat->read_sectors = rd_merges_or_rd_sec;
+ diskstat->has_write_ios = true;
+ diskstat->write_ios = rd_sec_or_wr_ios;
+ diskstat->has_write_sectors = true;
+ diskstat->write_sectors = rd_ticks_or_wr_sec;
+ }
+ if (i >= 14) {
+ diskstat->has_read_ios = true;
+ diskstat->read_ios = rd_ios;
+ diskstat->has_read_sectors = true;
+ diskstat->read_sectors = rd_sec_or_wr_ios;
+ diskstat->has_read_merges = true;
+ diskstat->read_merges = rd_merges_or_rd_sec;
+ diskstat->has_read_ticks = true;
+ diskstat->read_ticks = rd_ticks_or_wr_sec;
+ diskstat->has_write_ios = true;
+ diskstat->write_ios = wr_ios;
+ diskstat->has_write_sectors = true;
+ diskstat->write_sectors = wr_sec;
+ diskstat->has_write_merges = true;
+ diskstat->write_merges = wr_merges;
+ diskstat->has_write_ticks = true;
+ diskstat->write_ticks = wr_ticks;
+ diskstat->has_ios_pgr = true;
+ diskstat->ios_pgr = ios_pgr;
+ diskstat->has_total_ticks = true;
+ diskstat->total_ticks = tot_ticks;
+ diskstat->has_weight_ticks = true;
+ diskstat->weight_ticks = rq_ticks;
+ }
+ if (i >= 18) {
+ diskstat->has_discard_ios = true;
+ diskstat->discard_ios = dc_ios;
+ diskstat->has_discard_merges = true;
+ diskstat->discard_merges = dc_merges;
+ diskstat->has_discard_sectors = true;
+ diskstat->discard_sectors = dc_sec;
+ diskstat->has_discard_ticks = true;
+ diskstat->discard_ticks = dc_ticks;
+ }
+ if (i >= 20) {
+ diskstat->has_flush_ios = true;
+ diskstat->flush_ios = fl_ios;
+ diskstat->has_flush_ticks = true;
+ diskstat->flush_ticks = fl_ticks;
+ }
+
+ diskstatinfo->stats = g_steal_pointer(&diskstat);
+ QAPI_LIST_APPEND(tail, diskstatinfo);
+ diskstatinfo = NULL;
+ }
+ free(line);
+ fclose(fp);
+ return head;
+}
+
+GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
+{
+ return guest_get_diskstats(errp);
+}
+
+GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
+{
+ GuestCpuStatsList *head = NULL, **tail = &head;
+ const char *cpustats = "/proc/stat";
+ int clk_tck = sysconf(_SC_CLK_TCK);
+ FILE *fp;
+ size_t n;
+ char *line = NULL;
+
+ fp = fopen(cpustats, "r");
+ if (fp == NULL) {
+ error_setg_errno(errp, errno, "open(\"%s\")", cpustats);
+ return NULL;
+ }
+
+ while (getline(&line, &n, fp) != -1) {
+ GuestCpuStats *cpustat = NULL;
+ GuestLinuxCpuStats *linuxcpustat;
+ int i;
+ unsigned long user, system, idle, iowait, irq, softirq, steal, guest;
+ unsigned long nice, guest_nice;
+ char name[64];
+
+ i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+ name, &user, &nice, &system, &idle, &iowait, &irq, &softirq,
+ &steal, &guest, &guest_nice);
+
+ /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */
+ if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) {
+ continue;
+ }
+
+ if (i < 5) {
+ slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats);
+ break;
+ }
+
+ cpustat = g_new0(GuestCpuStats, 1);
+ cpustat->type = GUEST_CPU_STATS_TYPE_LINUX;
+
+ linuxcpustat = &cpustat->u.q_linux;
+ linuxcpustat->cpu = atoi(&name[3]);
+ linuxcpustat->user = user * 1000 / clk_tck;
+ linuxcpustat->nice = nice * 1000 / clk_tck;
+ linuxcpustat->system = system * 1000 / clk_tck;
+ linuxcpustat->idle = idle * 1000 / clk_tck;
+
+ if (i > 5) {
+ linuxcpustat->has_iowait = true;
+ linuxcpustat->iowait = iowait * 1000 / clk_tck;
+ }
+
+ if (i > 6) {
+ linuxcpustat->has_irq = true;
+ linuxcpustat->irq = irq * 1000 / clk_tck;
+ linuxcpustat->has_softirq = true;
+ linuxcpustat->softirq = softirq * 1000 / clk_tck;
+ }
+
+ if (i > 8) {
+ linuxcpustat->has_steal = true;
+ linuxcpustat->steal = steal * 1000 / clk_tck;
+ }
+
+ if (i > 9) {
+ linuxcpustat->has_guest = true;
+ linuxcpustat->guest = guest * 1000 / clk_tck;
+ }
+
+ if (i > 10) {
+ linuxcpustat->has_guest = true;
+ linuxcpustat->guest = guest * 1000 / clk_tck;
+ linuxcpustat->has_guestnice = true;
+ linuxcpustat->guestnice = guest_nice * 1000 / clk_tck;
+ }
+
+ QAPI_LIST_APPEND(tail, cpustat);
+ }
+
+ free(line);
+ fclose(fp);
+ return head;
+}
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 98aafc45f3..5da60e65ab 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1195,205 +1195,6 @@ GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
return info;
}
-#define MAX_NAME_LEN 128
-static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp)
-{
-#ifdef CONFIG_LINUX
- GuestDiskStatsInfoList *head = NULL, **tail = &head;
- const char *diskstats = "/proc/diskstats";
- FILE *fp;
- size_t n;
- char *line = NULL;
-
- fp = fopen(diskstats, "r");
- if (fp == NULL) {
- error_setg_errno(errp, errno, "open(\"%s\")", diskstats);
- return NULL;
- }
-
- while (getline(&line, &n, fp) != -1) {
- g_autofree GuestDiskStatsInfo *diskstatinfo = NULL;
- g_autofree GuestDiskStats *diskstat = NULL;
- char dev_name[MAX_NAME_LEN];
- unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks, fl_ticks;
- unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec, wr_ios;
- unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec;
- unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
- unsigned int major, minor;
- int i;
-
- i = sscanf(line, "%u %u %s %lu %lu %lu"
- "%lu %lu %lu %lu %u %u %u %u"
- "%lu %lu %lu %u %lu %u",
- &major, &minor, dev_name,
- &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios,
- &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec,
- &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
- &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
- &fl_ios, &fl_ticks);
-
- if (i < 7) {
- continue;
- }
-
- diskstatinfo = g_new0(GuestDiskStatsInfo, 1);
- diskstatinfo->name = g_strdup(dev_name);
- diskstatinfo->major = major;
- diskstatinfo->minor = minor;
-
- diskstat = g_new0(GuestDiskStats, 1);
- if (i == 7) {
- diskstat->has_read_ios = true;
- diskstat->read_ios = rd_ios;
- diskstat->has_read_sectors = true;
- diskstat->read_sectors = rd_merges_or_rd_sec;
- diskstat->has_write_ios = true;
- diskstat->write_ios = rd_sec_or_wr_ios;
- diskstat->has_write_sectors = true;
- diskstat->write_sectors = rd_ticks_or_wr_sec;
- }
- if (i >= 14) {
- diskstat->has_read_ios = true;
- diskstat->read_ios = rd_ios;
- diskstat->has_read_sectors = true;
- diskstat->read_sectors = rd_sec_or_wr_ios;
- diskstat->has_read_merges = true;
- diskstat->read_merges = rd_merges_or_rd_sec;
- diskstat->has_read_ticks = true;
- diskstat->read_ticks = rd_ticks_or_wr_sec;
- diskstat->has_write_ios = true;
- diskstat->write_ios = wr_ios;
- diskstat->has_write_sectors = true;
- diskstat->write_sectors = wr_sec;
- diskstat->has_write_merges = true;
- diskstat->write_merges = wr_merges;
- diskstat->has_write_ticks = true;
- diskstat->write_ticks = wr_ticks;
- diskstat->has_ios_pgr = true;
- diskstat->ios_pgr = ios_pgr;
- diskstat->has_total_ticks = true;
- diskstat->total_ticks = tot_ticks;
- diskstat->has_weight_ticks = true;
- diskstat->weight_ticks = rq_ticks;
- }
- if (i >= 18) {
- diskstat->has_discard_ios = true;
- diskstat->discard_ios = dc_ios;
- diskstat->has_discard_merges = true;
- diskstat->discard_merges = dc_merges;
- diskstat->has_discard_sectors = true;
- diskstat->discard_sectors = dc_sec;
- diskstat->has_discard_ticks = true;
- diskstat->discard_ticks = dc_ticks;
- }
- if (i >= 20) {
- diskstat->has_flush_ios = true;
- diskstat->flush_ios = fl_ios;
- diskstat->has_flush_ticks = true;
- diskstat->flush_ticks = fl_ticks;
- }
-
- diskstatinfo->stats = g_steal_pointer(&diskstat);
- QAPI_LIST_APPEND(tail, diskstatinfo);
- diskstatinfo = NULL;
- }
- free(line);
- fclose(fp);
- return head;
-#else
- g_debug("disk stats reporting available only for Linux");
- return NULL;
-#endif
-}
-
-GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
-{
- return guest_get_diskstats(errp);
-}
-
-GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
-{
- GuestCpuStatsList *head = NULL, **tail = &head;
- const char *cpustats = "/proc/stat";
- int clk_tck = sysconf(_SC_CLK_TCK);
- FILE *fp;
- size_t n;
- char *line = NULL;
-
- fp = fopen(cpustats, "r");
- if (fp == NULL) {
- error_setg_errno(errp, errno, "open(\"%s\")", cpustats);
- return NULL;
- }
-
- while (getline(&line, &n, fp) != -1) {
- GuestCpuStats *cpustat = NULL;
- GuestLinuxCpuStats *linuxcpustat;
- int i;
- unsigned long user, system, idle, iowait, irq, softirq, steal, guest;
- unsigned long nice, guest_nice;
- char name[64];
-
- i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
- name, &user, &nice, &system, &idle, &iowait, &irq, &softirq,
- &steal, &guest, &guest_nice);
-
- /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */
- if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) {
- continue;
- }
-
- if (i < 5) {
- slog("Parsing cpu stat from %s failed, see \"man proc\"", cpustats);
- break;
- }
-
- cpustat = g_new0(GuestCpuStats, 1);
- cpustat->type = GUEST_CPU_STATS_TYPE_LINUX;
-
- linuxcpustat = &cpustat->u.q_linux;
- linuxcpustat->cpu = atoi(&name[3]);
- linuxcpustat->user = user * 1000 / clk_tck;
- linuxcpustat->nice = nice * 1000 / clk_tck;
- linuxcpustat->system = system * 1000 / clk_tck;
- linuxcpustat->idle = idle * 1000 / clk_tck;
-
- if (i > 5) {
- linuxcpustat->has_iowait = true;
- linuxcpustat->iowait = iowait * 1000 / clk_tck;
- }
-
- if (i > 6) {
- linuxcpustat->has_irq = true;
- linuxcpustat->irq = irq * 1000 / clk_tck;
- linuxcpustat->has_softirq = true;
- linuxcpustat->softirq = softirq * 1000 / clk_tck;
- }
-
- if (i > 8) {
- linuxcpustat->has_steal = true;
- linuxcpustat->steal = steal * 1000 / clk_tck;
- }
-
- if (i > 9) {
- linuxcpustat->has_guest = true;
- linuxcpustat->guest = guest * 1000 / clk_tck;
- }
-
- if (i > 10) {
- linuxcpustat->has_guest = true;
- linuxcpustat->guest = guest * 1000 / clk_tck;
- linuxcpustat->has_guestnice = true;
- linuxcpustat->guestnice = guest_nice * 1000 / clk_tck;
- }
-
- QAPI_LIST_APPEND(tail, cpustat);
- }
-
- free(line);
- fclose(fp);
- return head;
-}
#else /* defined(__linux__) */
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 06/22] qga: move linux memory block command impls to commands-linux.c
2024-06-13 15:01 [PATCH v2 00/22] qga: clean up command source locations and conditionals Daniel P. Berrangé
` (4 preceding siblings ...)
2024-06-13 15:01 ` [PATCH v2 05/22] qga: move linux disk/cpu stats " Daniel P. Berrangé
@ 2024-06-13 15:43 ` Daniel P. Berrangé
2024-06-13 15:43 ` [PATCH v2 07/22] qga: move CONFIG_FSFREEZE/TRIM to be meson defined options Daniel P. Berrangé
` (17 more replies)
2024-06-14 8:34 ` [PATCH v2 00/22] qga: clean up command source locations and conditionals Marc-André Lureau
2024-07-02 18:00 ` Daniel P. Berrangé
7 siblings, 18 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:43 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
The qmp_guest_{set,get}_{memory_blocks,block_info} command impls in
commands-posix.c are surrounded by '#ifdef __linux__' so should
instead live in commands-linux.c
This also removes a "#ifdef CONFIG_LINUX" that was nested inside
a "#ifdef __linux__".
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-linux.c | 308 ++++++++++++++++++++++++++++++++++++++++++
qga/commands-posix.c | 311 +------------------------------------------
2 files changed, 309 insertions(+), 310 deletions(-)
diff --git a/qga/commands-linux.c b/qga/commands-linux.c
index c0e8bd4062..73b13fbaf6 100644
--- a/qga/commands-linux.c
+++ b/qga/commands-linux.c
@@ -1595,6 +1595,314 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
return processed;
}
+
+static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
+ int size, Error **errp)
+{
+ int fd;
+ int res;
+
+ errno = 0;
+ fd = openat(dirfd, pathname, O_RDONLY);
+ if (fd == -1) {
+ error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
+ return;
+ }
+
+ res = pread(fd, buf, size, 0);
+ if (res == -1) {
+ error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname);
+ } else if (res == 0) {
+ error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname);
+ }
+ close(fd);
+}
+
+static void ga_write_sysfs_file(int dirfd, const char *pathname,
+ const char *buf, int size, Error **errp)
+{
+ int fd;
+
+ errno = 0;
+ fd = openat(dirfd, pathname, O_WRONLY);
+ if (fd == -1) {
+ error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
+ return;
+ }
+
+ if (pwrite(fd, buf, size, 0) == -1) {
+ error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname);
+ }
+
+ close(fd);
+}
+
+/* Transfer online/offline status between @mem_blk and the guest system.
+ *
+ * On input either @errp or *@errp must be NULL.
+ *
+ * In system-to-@mem_blk direction, the following @mem_blk fields are accessed:
+ * - R: mem_blk->phys_index
+ * - W: mem_blk->online
+ * - W: mem_blk->can_offline
+ *
+ * In @mem_blk-to-system direction, the following @mem_blk fields are accessed:
+ * - R: mem_blk->phys_index
+ * - R: mem_blk->online
+ *- R: mem_blk->can_offline
+ * Written members remain unmodified on error.
+ */
+static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk,
+ GuestMemoryBlockResponse *result,
+ Error **errp)
+{
+ char *dirpath;
+ int dirfd;
+ char *status;
+ Error *local_err = NULL;
+
+ if (!sys2memblk) {
+ DIR *dp;
+
+ if (!result) {
+ error_setg(errp, "Internal error, 'result' should not be NULL");
+ return;
+ }
+ errno = 0;
+ dp = opendir("/sys/devices/system/memory/");
+ /* if there is no 'memory' directory in sysfs,
+ * we think this VM does not support online/offline memory block,
+ * any other solution?
+ */
+ if (!dp) {
+ if (errno == ENOENT) {
+ result->response =
+ GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
+ }
+ goto out1;
+ }
+ closedir(dp);
+ }
+
+ dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/",
+ mem_blk->phys_index);
+ dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
+ if (dirfd == -1) {
+ if (sys2memblk) {
+ error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
+ } else {
+ if (errno == ENOENT) {
+ result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
+ } else {
+ result->response =
+ GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
+ }
+ }
+ g_free(dirpath);
+ goto out1;
+ }
+ g_free(dirpath);
+
+ status = g_malloc0(10);
+ ga_read_sysfs_file(dirfd, "state", status, 10, &local_err);
+ if (local_err) {
+ /* treat with sysfs file that not exist in old kernel */
+ if (errno == ENOENT) {
+ error_free(local_err);
+ if (sys2memblk) {
+ mem_blk->online = true;
+ mem_blk->can_offline = false;
+ } else if (!mem_blk->online) {
+ result->response =
+ GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
+ }
+ } else {
+ if (sys2memblk) {
+ error_propagate(errp, local_err);
+ } else {
+ error_free(local_err);
+ result->response =
+ GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
+ }
+ }
+ goto out2;
+ }
+
+ if (sys2memblk) {
+ char removable = '0';
+
+ mem_blk->online = (strncmp(status, "online", 6) == 0);
+
+ ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
+ if (local_err) {
+ /* if no 'removable' file, it doesn't support offline mem blk */
+ if (errno == ENOENT) {
+ error_free(local_err);
+ mem_blk->can_offline = false;
+ } else {
+ error_propagate(errp, local_err);
+ }
+ } else {
+ mem_blk->can_offline = (removable != '0');
+ }
+ } else {
+ if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
+ const char *new_state = mem_blk->online ? "online" : "offline";
+
+ ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state),
+ &local_err);
+ if (local_err) {
+ error_free(local_err);
+ result->response =
+ GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
+ goto out2;
+ }
+
+ result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
+ result->has_error_code = false;
+ } /* otherwise pretend successful re-(on|off)-lining */
+ }
+ g_free(status);
+ close(dirfd);
+ return;
+
+out2:
+ g_free(status);
+ close(dirfd);
+out1:
+ if (!sys2memblk) {
+ result->has_error_code = true;
+ result->error_code = errno;
+ }
+}
+
+GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
+{
+ GuestMemoryBlockList *head, **tail;
+ Error *local_err = NULL;
+ struct dirent *de;
+ DIR *dp;
+
+ head = NULL;
+ tail = &head;
+
+ dp = opendir("/sys/devices/system/memory/");
+ if (!dp) {
+ /* it's ok if this happens to be a system that doesn't expose
+ * memory blocks via sysfs, but otherwise we should report
+ * an error
+ */
+ if (errno != ENOENT) {
+ error_setg_errno(errp, errno, "Can't open directory"
+ "\"/sys/devices/system/memory/\"");
+ }
+ return NULL;
+ }
+
+ /* Note: the phys_index of memory block may be discontinuous,
+ * this is because a memblk is the unit of the Sparse Memory design, which
+ * allows discontinuous memory ranges (ex. NUMA), so here we should
+ * traverse the memory block directory.
+ */
+ while ((de = readdir(dp)) != NULL) {
+ GuestMemoryBlock *mem_blk;
+
+ if ((strncmp(de->d_name, "memory", 6) != 0) ||
+ !(de->d_type & DT_DIR)) {
+ continue;
+ }
+
+ mem_blk = g_malloc0(sizeof *mem_blk);
+ /* The d_name is "memoryXXX", phys_index is block id, same as XXX */
+ mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
+ mem_blk->has_can_offline = true; /* lolspeak ftw */
+ transfer_memory_block(mem_blk, true, NULL, &local_err);
+ if (local_err) {
+ break;
+ }
+
+ QAPI_LIST_APPEND(tail, mem_blk);
+ }
+
+ closedir(dp);
+ if (local_err == NULL) {
+ /* there's no guest with zero memory blocks */
+ if (head == NULL) {
+ error_setg(errp, "guest reported zero memory blocks!");
+ }
+ return head;
+ }
+
+ qapi_free_GuestMemoryBlockList(head);
+ error_propagate(errp, local_err);
+ return NULL;
+}
+
+GuestMemoryBlockResponseList *
+qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
+{
+ GuestMemoryBlockResponseList *head, **tail;
+ Error *local_err = NULL;
+
+ head = NULL;
+ tail = &head;
+
+ while (mem_blks != NULL) {
+ GuestMemoryBlockResponse *result;
+ GuestMemoryBlock *current_mem_blk = mem_blks->value;
+
+ result = g_malloc0(sizeof(*result));
+ result->phys_index = current_mem_blk->phys_index;
+ transfer_memory_block(current_mem_blk, false, result, &local_err);
+ if (local_err) { /* should never happen */
+ goto err;
+ }
+
+ QAPI_LIST_APPEND(tail, result);
+ mem_blks = mem_blks->next;
+ }
+
+ return head;
+err:
+ qapi_free_GuestMemoryBlockResponseList(head);
+ error_propagate(errp, local_err);
+ return NULL;
+}
+
+GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
+{
+ Error *local_err = NULL;
+ char *dirpath;
+ int dirfd;
+ char *buf;
+ GuestMemoryBlockInfo *info;
+
+ dirpath = g_strdup_printf("/sys/devices/system/memory/");
+ dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
+ if (dirfd == -1) {
+ error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
+ g_free(dirpath);
+ return NULL;
+ }
+ g_free(dirpath);
+
+ buf = g_malloc0(20);
+ ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err);
+ close(dirfd);
+ if (local_err) {
+ g_free(buf);
+ error_propagate(errp, local_err);
+ return NULL;
+ }
+
+ info = g_new0(GuestMemoryBlockInfo, 1);
+ info->size = strtol(buf, NULL, 16); /* the unit is bytes */
+
+ g_free(buf);
+
+ return info;
+}
+
#define MAX_NAME_LEN 128
static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp)
{
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 5da60e65ab..2a3bef7445 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -887,316 +887,7 @@ void qmp_guest_set_user_password(const char *username,
}
#endif /* __linux__ || __FreeBSD__ */
-#ifdef __linux__
-static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
- int size, Error **errp)
-{
- int fd;
- int res;
-
- errno = 0;
- fd = openat(dirfd, pathname, O_RDONLY);
- if (fd == -1) {
- error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
- return;
- }
-
- res = pread(fd, buf, size, 0);
- if (res == -1) {
- error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname);
- } else if (res == 0) {
- error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname);
- }
- close(fd);
-}
-
-static void ga_write_sysfs_file(int dirfd, const char *pathname,
- const char *buf, int size, Error **errp)
-{
- int fd;
-
- errno = 0;
- fd = openat(dirfd, pathname, O_WRONLY);
- if (fd == -1) {
- error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
- return;
- }
-
- if (pwrite(fd, buf, size, 0) == -1) {
- error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname);
- }
-
- close(fd);
-}
-
-/* Transfer online/offline status between @mem_blk and the guest system.
- *
- * On input either @errp or *@errp must be NULL.
- *
- * In system-to-@mem_blk direction, the following @mem_blk fields are accessed:
- * - R: mem_blk->phys_index
- * - W: mem_blk->online
- * - W: mem_blk->can_offline
- *
- * In @mem_blk-to-system direction, the following @mem_blk fields are accessed:
- * - R: mem_blk->phys_index
- * - R: mem_blk->online
- *- R: mem_blk->can_offline
- * Written members remain unmodified on error.
- */
-static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk,
- GuestMemoryBlockResponse *result,
- Error **errp)
-{
- char *dirpath;
- int dirfd;
- char *status;
- Error *local_err = NULL;
-
- if (!sys2memblk) {
- DIR *dp;
-
- if (!result) {
- error_setg(errp, "Internal error, 'result' should not be NULL");
- return;
- }
- errno = 0;
- dp = opendir("/sys/devices/system/memory/");
- /* if there is no 'memory' directory in sysfs,
- * we think this VM does not support online/offline memory block,
- * any other solution?
- */
- if (!dp) {
- if (errno == ENOENT) {
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
- }
- goto out1;
- }
- closedir(dp);
- }
-
- dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/",
- mem_blk->phys_index);
- dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
- if (dirfd == -1) {
- if (sys2memblk) {
- error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
- } else {
- if (errno == ENOENT) {
- result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
- } else {
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
- }
- }
- g_free(dirpath);
- goto out1;
- }
- g_free(dirpath);
-
- status = g_malloc0(10);
- ga_read_sysfs_file(dirfd, "state", status, 10, &local_err);
- if (local_err) {
- /* treat with sysfs file that not exist in old kernel */
- if (errno == ENOENT) {
- error_free(local_err);
- if (sys2memblk) {
- mem_blk->online = true;
- mem_blk->can_offline = false;
- } else if (!mem_blk->online) {
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
- }
- } else {
- if (sys2memblk) {
- error_propagate(errp, local_err);
- } else {
- error_free(local_err);
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
- }
- }
- goto out2;
- }
-
- if (sys2memblk) {
- char removable = '0';
-
- mem_blk->online = (strncmp(status, "online", 6) == 0);
-
- ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
- if (local_err) {
- /* if no 'removable' file, it doesn't support offline mem blk */
- if (errno == ENOENT) {
- error_free(local_err);
- mem_blk->can_offline = false;
- } else {
- error_propagate(errp, local_err);
- }
- } else {
- mem_blk->can_offline = (removable != '0');
- }
- } else {
- if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
- const char *new_state = mem_blk->online ? "online" : "offline";
-
- ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state),
- &local_err);
- if (local_err) {
- error_free(local_err);
- result->response =
- GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
- goto out2;
- }
-
- result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
- result->has_error_code = false;
- } /* otherwise pretend successful re-(on|off)-lining */
- }
- g_free(status);
- close(dirfd);
- return;
-
-out2:
- g_free(status);
- close(dirfd);
-out1:
- if (!sys2memblk) {
- result->has_error_code = true;
- result->error_code = errno;
- }
-}
-
-GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
-{
- GuestMemoryBlockList *head, **tail;
- Error *local_err = NULL;
- struct dirent *de;
- DIR *dp;
-
- head = NULL;
- tail = &head;
-
- dp = opendir("/sys/devices/system/memory/");
- if (!dp) {
- /* it's ok if this happens to be a system that doesn't expose
- * memory blocks via sysfs, but otherwise we should report
- * an error
- */
- if (errno != ENOENT) {
- error_setg_errno(errp, errno, "Can't open directory"
- "\"/sys/devices/system/memory/\"");
- }
- return NULL;
- }
-
- /* Note: the phys_index of memory block may be discontinuous,
- * this is because a memblk is the unit of the Sparse Memory design, which
- * allows discontinuous memory ranges (ex. NUMA), so here we should
- * traverse the memory block directory.
- */
- while ((de = readdir(dp)) != NULL) {
- GuestMemoryBlock *mem_blk;
-
- if ((strncmp(de->d_name, "memory", 6) != 0) ||
- !(de->d_type & DT_DIR)) {
- continue;
- }
-
- mem_blk = g_malloc0(sizeof *mem_blk);
- /* The d_name is "memoryXXX", phys_index is block id, same as XXX */
- mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
- mem_blk->has_can_offline = true; /* lolspeak ftw */
- transfer_memory_block(mem_blk, true, NULL, &local_err);
- if (local_err) {
- break;
- }
-
- QAPI_LIST_APPEND(tail, mem_blk);
- }
-
- closedir(dp);
- if (local_err == NULL) {
- /* there's no guest with zero memory blocks */
- if (head == NULL) {
- error_setg(errp, "guest reported zero memory blocks!");
- }
- return head;
- }
-
- qapi_free_GuestMemoryBlockList(head);
- error_propagate(errp, local_err);
- return NULL;
-}
-
-GuestMemoryBlockResponseList *
-qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
-{
- GuestMemoryBlockResponseList *head, **tail;
- Error *local_err = NULL;
-
- head = NULL;
- tail = &head;
-
- while (mem_blks != NULL) {
- GuestMemoryBlockResponse *result;
- GuestMemoryBlock *current_mem_blk = mem_blks->value;
-
- result = g_malloc0(sizeof(*result));
- result->phys_index = current_mem_blk->phys_index;
- transfer_memory_block(current_mem_blk, false, result, &local_err);
- if (local_err) { /* should never happen */
- goto err;
- }
-
- QAPI_LIST_APPEND(tail, result);
- mem_blks = mem_blks->next;
- }
-
- return head;
-err:
- qapi_free_GuestMemoryBlockResponseList(head);
- error_propagate(errp, local_err);
- return NULL;
-}
-
-GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
-{
- Error *local_err = NULL;
- char *dirpath;
- int dirfd;
- char *buf;
- GuestMemoryBlockInfo *info;
-
- dirpath = g_strdup_printf("/sys/devices/system/memory/");
- dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
- if (dirfd == -1) {
- error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
- g_free(dirpath);
- return NULL;
- }
- g_free(dirpath);
-
- buf = g_malloc0(20);
- ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err);
- close(dirfd);
- if (local_err) {
- g_free(buf);
- error_propagate(errp, local_err);
- return NULL;
- }
-
- info = g_new0(GuestMemoryBlockInfo, 1);
- info->size = strtol(buf, NULL, 16); /* the unit is bytes */
-
- g_free(buf);
-
- return info;
-}
-
-
-#else /* defined(__linux__) */
+#ifndef __linux__
void qmp_guest_suspend_disk(Error **errp)
{
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 07/22] qga: move CONFIG_FSFREEZE/TRIM to be meson defined options
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
@ 2024-06-13 15:43 ` Daniel P. Berrangé
2024-06-13 15:43 ` [PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows Daniel P. Berrangé
` (16 subsequent siblings)
17 siblings, 0 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:43 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Defining these at the meson level allows them to be used a conditional
tests in the QAPI schemas.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
meson.build | 15 +++++++++++++++
qga/commands-common.h | 9 ---------
2 files changed, 15 insertions(+), 9 deletions(-)
diff --git a/meson.build b/meson.build
index 91278667ea..d9f3349b0a 100644
--- a/meson.build
+++ b/meson.build
@@ -2145,6 +2145,19 @@ have_virtfs_proxy_helper = get_option('virtfs_proxy_helper') \
.require(libcap_ng.found(), error_message: 'the virtfs proxy helper requires libcap-ng') \
.allowed()
+qga_fsfreeze = false
+qga_fstrim = false
+if host_os == 'linux'
+ if cc.has_header_symbol('linux/fs.h', 'FIFREEZE')
+ qga_fsfreeze = true
+ endif
+ if cc.has_header_symbol('linux/fs.h', 'FITRIM')
+ qga_fstrim = true
+ endif
+elif host_os == 'freebsd' and cc.has_header_symbol('ufs/ffs/fs.h', 'UFSSUSPEND')
+ qga_fsfreeze = true
+endif
+
if get_option('block_drv_ro_whitelist') == ''
config_host_data.set('CONFIG_BDRV_RO_WHITELIST', '')
else
@@ -2379,6 +2392,8 @@ config_host_data.set('CONFIG_DEBUG_TCG', get_option('debug_tcg'))
config_host_data.set('CONFIG_DEBUG_REMAP', get_option('debug_remap'))
config_host_data.set('CONFIG_QOM_CAST_DEBUG', get_option('qom_cast_debug'))
config_host_data.set('CONFIG_REPLICATION', get_option('replication').allowed())
+config_host_data.set('CONFIG_FSFREEZE', qga_fsfreeze)
+config_host_data.set('CONFIG_FSTRIM', qga_fstrim)
# has_header
config_host_data.set('CONFIG_EPOLL', cc.has_header('sys/epoll.h'))
diff --git a/qga/commands-common.h b/qga/commands-common.h
index 8c1c56aac9..263e7c0525 100644
--- a/qga/commands-common.h
+++ b/qga/commands-common.h
@@ -15,19 +15,10 @@
#if defined(__linux__)
#include <linux/fs.h>
-#ifdef FIFREEZE
-#define CONFIG_FSFREEZE
-#endif
-#ifdef FITRIM
-#define CONFIG_FSTRIM
-#endif
#endif /* __linux__ */
#ifdef __FreeBSD__
#include <ufs/ffs/fs.h>
-#ifdef UFSSUSPEND
-#define CONFIG_FSFREEZE
-#endif
#endif /* __FreeBSD__ */
#if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
2024-06-13 15:43 ` [PATCH v2 07/22] qga: move CONFIG_FSFREEZE/TRIM to be meson defined options Daniel P. Berrangé
@ 2024-06-13 15:43 ` Daniel P. Berrangé
2024-07-03 8:30 ` Philippe Mathieu-Daudé
2024-07-12 8:34 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX Daniel P. Berrangé
` (15 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:43 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Rather than creating stubs for every command that just return
QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
fully exclude generation of the commands on Windows.
The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from
{"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
This also fixes an accidental inconsistency where some commands
(guest-get-diskstats & guest-get-cpustats) are implemented as
stubs, yet not added to the blockedrpc list. Those change their
error message from
{"class": "GenericError, "desc": "this feature or command is not currently supported"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
The final additional benefit is that the QGA protocol reference
now documents what conditions enable use of the command.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-posix.c | 2 +-
qga/commands-win32.c | 56 +-------------------------------------------
qga/qapi-schema.json | 45 +++++++++++++++++++++++------------
3 files changed, 32 insertions(+), 71 deletions(-)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 2a3bef7445..0dd8555867 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1280,7 +1280,7 @@ GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
"guest-get-memory-blocks", "guest-set-memory-blocks",
"guest-get-memory-block-info",
NULL};
- char **p = (char **)list;
+ const char **p = list;
while (*p) {
blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 9fe670d5b4..2533e4c748 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -1494,11 +1494,6 @@ out:
}
}
-void qmp_guest_suspend_hybrid(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-}
-
static IP_ADAPTER_ADDRESSES *guest_get_adapters_addresses(Error **errp)
{
IP_ADAPTER_ADDRESSES *adptr_addrs = NULL;
@@ -1862,12 +1857,6 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
return NULL;
}
-int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return -1;
-}
-
static gchar *
get_net_error_message(gint error)
{
@@ -1969,46 +1958,15 @@ done:
g_free(rawpasswddata);
}
-GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestMemoryBlockResponseList *
-qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
/* add unsupported commands to the list of blocked RPCs */
GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
{
- const char *list_unsupported[] = {
- "guest-suspend-hybrid",
- "guest-set-vcpus",
- "guest-get-memory-blocks", "guest-set-memory-blocks",
- "guest-get-memory-block-info",
- NULL};
- char **p = (char **)list_unsupported;
-
- while (*p) {
- blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
- }
-
if (!vss_init(true)) {
g_debug("vss_init failed, vss commands are going to be disabled");
const char *list[] = {
"guest-get-fsinfo", "guest-fsfreeze-status",
"guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL};
- p = (char **)list;
+ char **p = (char **)list;
while (*p) {
blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
@@ -2505,15 +2463,3 @@ char *qga_get_host_name(Error **errp)
return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL);
}
-
-GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index b3de1fb6b3..b91456e9ad 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -636,7 +636,8 @@
#
# Since: 1.1
##
-{ 'command': 'guest-suspend-hybrid', 'success-response': false }
+{ 'command': 'guest-suspend-hybrid', 'success-response': false,
+ 'if': 'CONFIG_POSIX' }
##
# @GuestIpAddressType:
@@ -806,7 +807,8 @@
##
{ 'command': 'guest-set-vcpus',
'data': {'vcpus': ['GuestLogicalProcessor'] },
- 'returns': 'int' }
+ 'returns': 'int',
+ 'if': 'CONFIG_POSIX' }
##
# @GuestDiskBusType:
@@ -1099,7 +1101,8 @@
{ 'struct': 'GuestMemoryBlock',
'data': {'phys-index': 'uint64',
'online': 'bool',
- '*can-offline': 'bool'} }
+ '*can-offline': 'bool'},
+ 'if': 'CONFIG_POSIX' }
##
# @guest-get-memory-blocks:
@@ -1115,7 +1118,8 @@
# Since: 2.3
##
{ 'command': 'guest-get-memory-blocks',
- 'returns': ['GuestMemoryBlock'] }
+ 'returns': ['GuestMemoryBlock'],
+ 'if': 'CONFIG_POSIX' }
##
# @GuestMemoryBlockResponseType:
@@ -1138,7 +1142,8 @@
##
{ 'enum': 'GuestMemoryBlockResponseType',
'data': ['success', 'not-found', 'operation-not-supported',
- 'operation-failed'] }
+ 'operation-failed'],
+ 'if': 'CONFIG_POSIX' }
##
# @GuestMemoryBlockResponse:
@@ -1156,7 +1161,8 @@
{ 'struct': 'GuestMemoryBlockResponse',
'data': { 'phys-index': 'uint64',
'response': 'GuestMemoryBlockResponseType',
- '*error-code': 'int' }}
+ '*error-code': 'int' },
+ 'if': 'CONFIG_POSIX'}
##
# @guest-set-memory-blocks:
@@ -1187,7 +1193,8 @@
##
{ 'command': 'guest-set-memory-blocks',
'data': {'mem-blks': ['GuestMemoryBlock'] },
- 'returns': ['GuestMemoryBlockResponse'] }
+ 'returns': ['GuestMemoryBlockResponse'],
+ 'if': 'CONFIG_POSIX' }
##
# @GuestMemoryBlockInfo:
@@ -1199,7 +1206,8 @@
# Since: 2.3
##
{ 'struct': 'GuestMemoryBlockInfo',
- 'data': {'size': 'uint64'} }
+ 'data': {'size': 'uint64'},
+ 'if': 'CONFIG_POSIX' }
##
# @guest-get-memory-block-info:
@@ -1211,7 +1219,8 @@
# Since: 2.3
##
{ 'command': 'guest-get-memory-block-info',
- 'returns': 'GuestMemoryBlockInfo' }
+ 'returns': 'GuestMemoryBlockInfo',
+ 'if': 'CONFIG_POSIX' }
##
# @GuestExecStatus:
@@ -1702,7 +1711,8 @@
'data': {'name': 'str',
'major': 'uint64',
'minor': 'uint64',
- 'stats': 'GuestDiskStats' } }
+ 'stats': 'GuestDiskStats' },
+ 'if': 'CONFIG_POSIX' }
##
# @guest-get-diskstats:
@@ -1714,7 +1724,8 @@
# Since: 7.1
##
{ 'command': 'guest-get-diskstats',
- 'returns': ['GuestDiskStatsInfo']
+ 'returns': ['GuestDiskStatsInfo'],
+ 'if': 'CONFIG_POSIX'
}
##
@@ -1727,7 +1738,8 @@
# Since: 7.1
##
{ 'enum': 'GuestCpuStatsType',
- 'data': [ 'linux' ] }
+ 'data': [ 'linux' ],
+ 'if': 'CONFIG_POSIX' }
##
@@ -1772,7 +1784,8 @@
'*steal': 'uint64',
'*guest': 'uint64',
'*guestnice': 'uint64'
- } }
+ },
+ 'if': 'CONFIG_POSIX' }
##
# @GuestCpuStats:
@@ -1786,7 +1799,8 @@
{ 'union': 'GuestCpuStats',
'base': { 'type': 'GuestCpuStatsType' },
'discriminator': 'type',
- 'data': { 'linux': 'GuestLinuxCpuStats' } }
+ 'data': { 'linux': 'GuestLinuxCpuStats' },
+ 'if': 'CONFIG_POSIX' }
##
# @guest-get-cpustats:
@@ -1798,5 +1812,6 @@
# Since: 7.1
##
{ 'command': 'guest-get-cpustats',
- 'returns': ['GuestCpuStats']
+ 'returns': ['GuestCpuStats'],
+ 'if': 'CONFIG_POSIX'
}
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
2024-06-13 15:43 ` [PATCH v2 07/22] qga: move CONFIG_FSFREEZE/TRIM to be meson defined options Daniel P. Berrangé
2024-06-13 15:43 ` [PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows Daniel P. Berrangé
@ 2024-06-13 15:43 ` Daniel P. Berrangé
2024-07-03 8:31 ` Philippe Mathieu-Daudé
2024-07-12 8:35 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs Daniel P. Berrangé
` (14 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:43 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Rather than creating stubs for every command that just return
QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
fully exclude generation of the commands on non-Linux POSIX
platforms
The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from
{"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
This has the additional benefit that the QGA protocol reference
now documents what conditions enable use of the command.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-posix.c | 66 --------------------------------------------
qga/qapi-schema.json | 30 +++++++++++---------
2 files changed, 17 insertions(+), 79 deletions(-)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 0dd8555867..559d71ffae 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -887,56 +887,6 @@ void qmp_guest_set_user_password(const char *username,
}
#endif /* __linux__ || __FreeBSD__ */
-#ifndef __linux__
-
-void qmp_guest_suspend_disk(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-}
-
-void qmp_guest_suspend_ram(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-}
-
-void qmp_guest_suspend_hybrid(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-}
-
-GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return -1;
-}
-
-GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestMemoryBlockResponseList *
-qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-#endif
-
#ifdef HAVE_GETIFADDRS
static GuestNetworkInterface *
guest_find_interface(GuestNetworkInterfaceList *head,
@@ -1272,22 +1222,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
/* add unsupported commands to the list of blocked RPCs */
GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
{
-#if !defined(__linux__)
- {
- const char *list[] = {
- "guest-suspend-disk", "guest-suspend-ram",
- "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
- "guest-get-memory-blocks", "guest-set-memory-blocks",
- "guest-get-memory-block-info",
- NULL};
- const char **p = list;
-
- while (*p) {
- blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
- }
- }
-#endif
-
#if !defined(HAVE_GETIFADDRS)
blockedrpcs = g_list_append(blockedrpcs,
g_strdup("guest-network-get-interfaces"));
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index b91456e9ad..d164c30ec3 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -565,7 +565,8 @@
#
# Since: 1.1
##
-{ 'command': 'guest-suspend-disk', 'success-response': false }
+{ 'command': 'guest-suspend-disk', 'success-response': false,
+ 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } }
##
# @guest-suspend-ram:
@@ -601,7 +602,8 @@
#
# Since: 1.1
##
-{ 'command': 'guest-suspend-ram', 'success-response': false }
+{ 'command': 'guest-suspend-ram', 'success-response': false,
+ 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } }
##
# @guest-suspend-hybrid:
@@ -637,7 +639,7 @@
# Since: 1.1
##
{ 'command': 'guest-suspend-hybrid', 'success-response': false,
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @GuestIpAddressType:
@@ -750,7 +752,8 @@
{ 'struct': 'GuestLogicalProcessor',
'data': {'logical-id': 'int',
'online': 'bool',
- '*can-offline': 'bool'} }
+ '*can-offline': 'bool'},
+ 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } }
##
# @guest-get-vcpus:
@@ -765,7 +768,8 @@
# Since: 1.5
##
{ 'command': 'guest-get-vcpus',
- 'returns': ['GuestLogicalProcessor'] }
+ 'returns': ['GuestLogicalProcessor'],
+ 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } }
##
# @guest-set-vcpus:
@@ -808,7 +812,7 @@
{ 'command': 'guest-set-vcpus',
'data': {'vcpus': ['GuestLogicalProcessor'] },
'returns': 'int',
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @GuestDiskBusType:
@@ -1102,7 +1106,7 @@
'data': {'phys-index': 'uint64',
'online': 'bool',
'*can-offline': 'bool'},
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @guest-get-memory-blocks:
@@ -1119,7 +1123,7 @@
##
{ 'command': 'guest-get-memory-blocks',
'returns': ['GuestMemoryBlock'],
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @GuestMemoryBlockResponseType:
@@ -1143,7 +1147,7 @@
{ 'enum': 'GuestMemoryBlockResponseType',
'data': ['success', 'not-found', 'operation-not-supported',
'operation-failed'],
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @GuestMemoryBlockResponse:
@@ -1162,7 +1166,7 @@
'data': { 'phys-index': 'uint64',
'response': 'GuestMemoryBlockResponseType',
'*error-code': 'int' },
- 'if': 'CONFIG_POSIX'}
+ 'if': 'CONFIG_LINUX'}
##
# @guest-set-memory-blocks:
@@ -1194,7 +1198,7 @@
{ 'command': 'guest-set-memory-blocks',
'data': {'mem-blks': ['GuestMemoryBlock'] },
'returns': ['GuestMemoryBlockResponse'],
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @GuestMemoryBlockInfo:
@@ -1207,7 +1211,7 @@
##
{ 'struct': 'GuestMemoryBlockInfo',
'data': {'size': 'uint64'},
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @guest-get-memory-block-info:
@@ -1220,7 +1224,7 @@
##
{ 'command': 'guest-get-memory-block-info',
'returns': 'GuestMemoryBlockInfo',
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @GuestExecStatus:
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (2 preceding siblings ...)
2024-06-13 15:43 ` [PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX Daniel P. Berrangé
@ 2024-06-13 15:43 ` Daniel P. Berrangé
2024-07-03 8:32 ` Philippe Mathieu-Daudé
2024-07-12 8:35 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 11/22] qga: conditionalize schema for commands requiring linux/win32 Daniel P. Berrangé
` (13 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:43 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Rather than creating stubs for every comamnd that just return
QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
fully exclude generation of the network interface command on
POSIX platforms lacking getifaddrs().
The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from
{"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
This has the additional benefit that the QGA protocol reference
now documents what conditions enable use of the command.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-posix.c | 13 -------------
qga/qapi-schema.json | 15 ++++++++++-----
2 files changed, 10 insertions(+), 18 deletions(-)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 559d71ffae..09d08ee2ca 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1142,14 +1142,6 @@ error:
return NULL;
}
-#else
-
-GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
#endif /* HAVE_GETIFADDRS */
#if !defined(CONFIG_FSFREEZE)
@@ -1222,11 +1214,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
/* add unsupported commands to the list of blocked RPCs */
GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
{
-#if !defined(HAVE_GETIFADDRS)
- blockedrpcs = g_list_append(blockedrpcs,
- g_strdup("guest-network-get-interfaces"));
-#endif
-
#if !defined(CONFIG_FSFREEZE)
{
const char *list[] = {
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index d164c30ec3..c37c904aae 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -653,7 +653,8 @@
# Since: 1.1
##
{ 'enum': 'GuestIpAddressType',
- 'data': [ 'ipv4', 'ipv6' ] }
+ 'data': [ 'ipv4', 'ipv6' ],
+ 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } }
##
# @GuestIpAddress:
@@ -669,7 +670,8 @@
{ 'struct': 'GuestIpAddress',
'data': {'ip-address': 'str',
'ip-address-type': 'GuestIpAddressType',
- 'prefix': 'int'} }
+ 'prefix': 'int'},
+ 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } }
##
# @GuestNetworkInterfaceStat:
@@ -701,7 +703,8 @@
'tx-packets': 'uint64',
'tx-errs': 'uint64',
'tx-dropped': 'uint64'
- } }
+ },
+ 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } }
##
# @GuestNetworkInterface:
@@ -721,7 +724,8 @@
'data': {'name': 'str',
'*hardware-address': 'str',
'*ip-addresses': ['GuestIpAddress'],
- '*statistics': 'GuestNetworkInterfaceStat' } }
+ '*statistics': 'GuestNetworkInterfaceStat' },
+ 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } }
##
# @guest-network-get-interfaces:
@@ -733,7 +737,8 @@
# Since: 1.1
##
{ 'command': 'guest-network-get-interfaces',
- 'returns': ['GuestNetworkInterface'] }
+ 'returns': ['GuestNetworkInterface'],
+ 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } }
##
# @GuestLogicalProcessor:
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 11/22] qga: conditionalize schema for commands requiring linux/win32
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (3 preceding siblings ...)
2024-06-13 15:43 ` [PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs Daniel P. Berrangé
@ 2024-06-13 15:43 ` Daniel P. Berrangé
2024-06-13 15:43 ` [PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows Daniel P. Berrangé
` (12 subsequent siblings)
17 siblings, 0 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:43 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Some commands were blocked based on CONFIG_FSFREEZE, but their
impl had nothing todo with CONFIG_FSFREEZE, and were instead
either Linux-only, or Win+Linux-only.
Rather than creating stubs for every command that just return
QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
fully exclude generation of the stats and fsinfo commands on
platforms that can't support them.
The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from
{"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
This has the additional benefit that the QGA protocol reference
now documents what conditions enable use of the command.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-bsd.c | 24 -----------------------
qga/commands-posix.c | 30 ++---------------------------
qga/qapi-schema.json | 45 +++++++++++++++++++++++++++-----------------
3 files changed, 30 insertions(+), 69 deletions(-)
diff --git a/qga/commands-bsd.c b/qga/commands-bsd.c
index 17bddda1cf..9ce48af311 100644
--- a/qga/commands-bsd.c
+++ b/qga/commands-bsd.c
@@ -149,30 +149,6 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp)
}
return ret;
}
-
-GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
#endif /* CONFIG_FSFREEZE */
#ifdef HAVE_GETIFADDRS
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 09d08ee2ca..838dc3cf98 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1146,12 +1146,6 @@ error:
#if !defined(CONFIG_FSFREEZE)
-GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
{
error_setg(errp, QERR_UNSUPPORTED);
@@ -1181,25 +1175,6 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp)
return 0;
}
-
-GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
#endif /* CONFIG_FSFREEZE */
#if !defined(CONFIG_FSTRIM)
@@ -1217,10 +1192,9 @@ GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
#if !defined(CONFIG_FSFREEZE)
{
const char *list[] = {
- "guest-get-fsinfo", "guest-fsfreeze-status",
+ "guest-fsfreeze-status",
"guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
- "guest-fsfreeze-thaw", "guest-get-fsinfo",
- "guest-get-disks", NULL};
+ "guest-fsfreeze-thaw", NULL};
char **p = (char **)list;
while (*p) {
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index c37c904aae..700c5baa5a 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -869,7 +869,8 @@
{ 'enum': 'GuestDiskBusType',
'data': [ 'ide', 'fdc', 'scsi', 'virtio', 'xen', 'usb', 'uml', 'sata',
'sd', 'unknown', 'ieee1394', 'ssa', 'fibre', 'raid', 'iscsi',
- 'sas', 'mmc', 'virtual', 'file-backed-virtual', 'nvme' ] }
+ 'sas', 'mmc', 'virtual', 'file-backed-virtual', 'nvme' ],
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
##
@@ -887,7 +888,8 @@
##
{ 'struct': 'GuestPCIAddress',
'data': {'domain': 'int', 'bus': 'int',
- 'slot': 'int', 'function': 'int'} }
+ 'slot': 'int', 'function': 'int'},
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
##
# @GuestCCWAddress:
@@ -906,7 +908,8 @@
'data': {'cssid': 'int',
'ssid': 'int',
'subchno': 'int',
- 'devno': 'int'} }
+ 'devno': 'int'},
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
##
# @GuestDiskAddress:
@@ -935,7 +938,8 @@
'bus-type': 'GuestDiskBusType',
'bus': 'int', 'target': 'int', 'unit': 'int',
'*serial': 'str', '*dev': 'str',
- '*ccw-address': 'GuestCCWAddress'} }
+ '*ccw-address': 'GuestCCWAddress'},
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
##
# @GuestNVMeSmart:
@@ -972,7 +976,8 @@
'media-errors-lo': 'uint64',
'media-errors-hi': 'uint64',
'number-of-error-log-entries-lo': 'uint64',
- 'number-of-error-log-entries-hi': 'uint64' } }
+ 'number-of-error-log-entries-hi': 'uint64' },
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
##
# @GuestDiskSmart:
@@ -986,7 +991,8 @@
{ 'union': 'GuestDiskSmart',
'base': { 'type': 'GuestDiskBusType' },
'discriminator': 'type',
- 'data': { 'nvme': 'GuestNVMeSmart' } }
+ 'data': { 'nvme': 'GuestNVMeSmart' },
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
##
# @GuestDiskInfo:
@@ -1011,7 +1017,8 @@
{ 'struct': 'GuestDiskInfo',
'data': {'name': 'str', 'partition': 'bool', '*dependencies': ['str'],
'*address': 'GuestDiskAddress', '*alias': 'str',
- '*smart': 'GuestDiskSmart'} }
+ '*smart': 'GuestDiskSmart'},
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
##
# @guest-get-disks:
@@ -1024,7 +1031,8 @@
# Since: 5.2
##
{ 'command': 'guest-get-disks',
- 'returns': ['GuestDiskInfo'] }
+ 'returns': ['GuestDiskInfo'],
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
##
# @GuestFilesystemInfo:
@@ -1050,7 +1058,8 @@
{ 'struct': 'GuestFilesystemInfo',
'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str',
'*used-bytes': 'uint64', '*total-bytes': 'uint64',
- '*total-bytes-privileged': 'uint64', 'disk': ['GuestDiskAddress']} }
+ '*total-bytes-privileged': 'uint64', 'disk': ['GuestDiskAddress']},
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
##
# @guest-get-fsinfo:
@@ -1063,7 +1072,8 @@
# Since: 2.2
##
{ 'command': 'guest-get-fsinfo',
- 'returns': ['GuestFilesystemInfo'] }
+ 'returns': ['GuestFilesystemInfo'],
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
##
# @guest-set-user-password:
@@ -1703,7 +1713,8 @@
'*ios-pgr': 'uint64',
'*total-ticks': 'uint64',
'*weight-ticks': 'uint64'
- } }
+ },
+ 'if': 'CONFIG_LINUX' }
##
# @GuestDiskStatsInfo:
@@ -1721,7 +1732,7 @@
'major': 'uint64',
'minor': 'uint64',
'stats': 'GuestDiskStats' },
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @guest-get-diskstats:
@@ -1734,7 +1745,7 @@
##
{ 'command': 'guest-get-diskstats',
'returns': ['GuestDiskStatsInfo'],
- 'if': 'CONFIG_POSIX'
+ 'if': 'CONFIG_LINUX'
}
##
@@ -1748,7 +1759,7 @@
##
{ 'enum': 'GuestCpuStatsType',
'data': [ 'linux' ],
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
@@ -1794,7 +1805,7 @@
'*guest': 'uint64',
'*guestnice': 'uint64'
},
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @GuestCpuStats:
@@ -1809,7 +1820,7 @@
'base': { 'type': 'GuestCpuStatsType' },
'discriminator': 'type',
'data': { 'linux': 'GuestLinuxCpuStats' },
- 'if': 'CONFIG_POSIX' }
+ 'if': 'CONFIG_LINUX' }
##
# @guest-get-cpustats:
@@ -1822,5 +1833,5 @@
##
{ 'command': 'guest-get-cpustats',
'returns': ['GuestCpuStats'],
- 'if': 'CONFIG_POSIX'
+ 'if': 'CONFIG_LINUX'
}
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (4 preceding siblings ...)
2024-06-13 15:43 ` [PATCH v2 11/22] qga: conditionalize schema for commands requiring linux/win32 Daniel P. Berrangé
@ 2024-06-13 15:43 ` Daniel P. Berrangé
2024-07-03 8:35 ` Philippe Mathieu-Daudé
2024-07-12 8:37 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze Daniel P. Berrangé
` (11 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:43 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Rather than creating stubs for every command that just return
QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
fully exclude generation of the commands on non-Windows.
The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from
{"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
This has the additional benefit that the QGA protocol reference
now documents what conditions enable use of the command.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-posix.c | 9 ---------
qga/qapi-schema.json | 15 ++++++++++-----
2 files changed, 10 insertions(+), 14 deletions(-)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 838dc3cf98..b7f96aa005 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1207,8 +1207,6 @@ GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim"));
#endif
- blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-get-devices"));
-
return blockedrpcs;
}
@@ -1419,13 +1417,6 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
return info;
}
-GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return NULL;
-}
-
#ifndef HOST_NAME_MAX
# ifdef _POSIX_HOST_NAME_MAX
# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 700c5baa5a..2704b814ab 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1527,7 +1527,8 @@
# @pci: PCI device
##
{ 'enum': 'GuestDeviceType',
- 'data': [ 'pci' ] }
+ 'data': [ 'pci' ],
+ 'if': 'CONFIG_WIN32' }
##
# @GuestDeviceIdPCI:
@@ -1539,7 +1540,8 @@
# Since: 5.2
##
{ 'struct': 'GuestDeviceIdPCI',
- 'data': { 'vendor-id': 'uint16', 'device-id': 'uint16' } }
+ 'data': { 'vendor-id': 'uint16', 'device-id': 'uint16' },
+ 'if': 'CONFIG_WIN32' }
##
# @GuestDeviceId:
@@ -1553,7 +1555,8 @@
{ 'union': 'GuestDeviceId',
'base': { 'type': 'GuestDeviceType' },
'discriminator': 'type',
- 'data': { 'pci': 'GuestDeviceIdPCI' } }
+ 'data': { 'pci': 'GuestDeviceIdPCI' },
+ 'if': 'CONFIG_WIN32' }
##
# @GuestDeviceInfo:
@@ -1574,7 +1577,8 @@
'*driver-date': 'int',
'*driver-version': 'str',
'*id': 'GuestDeviceId'
- } }
+ },
+ 'if': 'CONFIG_WIN32' }
##
# @guest-get-devices:
@@ -1586,7 +1590,8 @@
# Since: 5.2
##
{ 'command': 'guest-get-devices',
- 'returns': ['GuestDeviceInfo'] }
+ 'returns': ['GuestDeviceInfo'],
+ 'if': 'CONFIG_WIN32' }
##
# @GuestAuthorizedKeys:
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (5 preceding siblings ...)
2024-06-13 15:43 ` [PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows Daniel P. Berrangé
@ 2024-06-13 15:43 ` Daniel P. Berrangé
2024-07-03 8:37 ` Philippe Mathieu-Daudé
2024-07-12 8:37 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim Daniel P. Berrangé
` (10 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:43 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Rather than creating stubs for every command that just return
QERR_UNSUPPORTED, use 'if' conditions in the schema to fully
exclude generation of the filesystem freezing commands on POSIX
platforms lacking the required APIs.
The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from
{"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
This has the additional benefit that the QGA protocol reference
now documents what conditions enable use of the command.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-posix.c | 47 --------------------------------------------
qga/qapi-schema.json | 15 +++++++++-----
2 files changed, 10 insertions(+), 52 deletions(-)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index b7f96aa005..9207cb7a8f 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1144,39 +1144,6 @@ error:
#endif /* HAVE_GETIFADDRS */
-#if !defined(CONFIG_FSFREEZE)
-
-GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-
-int64_t qmp_guest_fsfreeze_freeze(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-
-int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
- strList *mountpoints,
- Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-
-int64_t qmp_guest_fsfreeze_thaw(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-
- return 0;
-}
-#endif /* CONFIG_FSFREEZE */
-
#if !defined(CONFIG_FSTRIM)
GuestFilesystemTrimResponse *
qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
@@ -1189,20 +1156,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
/* add unsupported commands to the list of blocked RPCs */
GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
{
-#if !defined(CONFIG_FSFREEZE)
- {
- const char *list[] = {
- "guest-fsfreeze-status",
- "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
- "guest-fsfreeze-thaw", NULL};
- char **p = (char **)list;
-
- while (*p) {
- blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
- }
- }
-#endif
-
#if !defined(CONFIG_FSTRIM)
blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim"));
#endif
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 2704b814ab..098fa7a08b 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -412,7 +412,8 @@
# Since: 0.15.0
##
{ 'enum': 'GuestFsfreezeStatus',
- 'data': [ 'thawed', 'frozen' ] }
+ 'data': [ 'thawed', 'frozen' ],
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } }
##
# @guest-fsfreeze-status:
@@ -428,7 +429,8 @@
# Since: 0.15.0
##
{ 'command': 'guest-fsfreeze-status',
- 'returns': 'GuestFsfreezeStatus' }
+ 'returns': 'GuestFsfreezeStatus',
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } }
##
# @guest-fsfreeze-freeze:
@@ -450,7 +452,8 @@
# Since: 0.15.0
##
{ 'command': 'guest-fsfreeze-freeze',
- 'returns': 'int' }
+ 'returns': 'int',
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } }
##
# @guest-fsfreeze-freeze-list:
@@ -470,7 +473,8 @@
##
{ 'command': 'guest-fsfreeze-freeze-list',
'data': { '*mountpoints': ['str'] },
- 'returns': 'int' }
+ 'returns': 'int',
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } }
##
# @guest-fsfreeze-thaw:
@@ -487,7 +491,8 @@
# Since: 0.15.0
##
{ 'command': 'guest-fsfreeze-thaw',
- 'returns': 'int' }
+ 'returns': 'int',
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } }
##
# @GuestFilesystemTrimResult:
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (6 preceding siblings ...)
2024-06-13 15:43 ` [PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze Daniel P. Berrangé
@ 2024-06-13 15:43 ` Daniel P. Berrangé
2024-07-03 8:36 ` Philippe Mathieu-Daudé
2024-07-12 8:38 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev Daniel P. Berrangé
` (9 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:43 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Rather than creating stubs for every command that just return
QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
fully exclude generation of the filesystem trimming commands
on POSIX platforms lacking required APIs.
The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from
{"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
This has the additional benefit that the QGA protocol reference
now documents what conditions enable use of the command.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-posix.c | 13 -------------
qga/qapi-schema.json | 9 ++++++---
2 files changed, 6 insertions(+), 16 deletions(-)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 9207cb7a8f..d92fa0ec87 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1144,22 +1144,9 @@ error:
#endif /* HAVE_GETIFADDRS */
-#if !defined(CONFIG_FSTRIM)
-GuestFilesystemTrimResponse *
-qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-#endif
-
/* add unsupported commands to the list of blocked RPCs */
GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
{
-#if !defined(CONFIG_FSTRIM)
- blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim"));
-#endif
-
return blockedrpcs;
}
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 098fa7a08b..0f27375ea0 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -509,7 +509,8 @@
##
{ 'struct': 'GuestFilesystemTrimResult',
'data': {'path': 'str',
- '*trimmed': 'int', '*minimum': 'int', '*error': 'str'} }
+ '*trimmed': 'int', '*minimum': 'int', '*error': 'str'},
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } }
##
# @GuestFilesystemTrimResponse:
@@ -519,7 +520,8 @@
# Since: 2.4
##
{ 'struct': 'GuestFilesystemTrimResponse',
- 'data': {'paths': ['GuestFilesystemTrimResult']} }
+ 'data': {'paths': ['GuestFilesystemTrimResult']},
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } }
##
# @guest-fstrim:
@@ -541,7 +543,8 @@
##
{ 'command': 'guest-fstrim',
'data': { '*minimum': 'int' },
- 'returns': 'GuestFilesystemTrimResponse' }
+ 'returns': 'GuestFilesystemTrimResponse',
+ 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } }
##
# @guest-suspend-disk:
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (7 preceding siblings ...)
2024-06-13 15:43 ` [PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim Daniel P. Berrangé
@ 2024-06-13 15:43 ` Daniel P. Berrangé
2024-07-03 8:37 ` Philippe Mathieu-Daudé
2024-07-12 8:40 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx Daniel P. Berrangé
` (8 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:43 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Rather than creating stubs for every command that just return
QERR_UNSUPPORTED, use 'if' conditions in the schema to fully
exclude generation of the filesystem trimming commands on POSIX
platforms lacking required APIs.
The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from
{"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
This has the additional benefit that the QGA protocol reference
now documents what conditions enable use of the command.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-linux.c | 8 --------
qga/qapi-schema.json | 8 ++++----
2 files changed, 4 insertions(+), 12 deletions(-)
diff --git a/qga/commands-linux.c b/qga/commands-linux.c
index 73b13fbaf6..89bdcded01 100644
--- a/qga/commands-linux.c
+++ b/qga/commands-linux.c
@@ -1049,14 +1049,6 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
return ret;
}
-#else
-
-GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
#endif
/* Return a list of the disk device(s)' info which @mount lies on */
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 0f27375ea0..0b7f911ca5 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -985,7 +985,7 @@
'media-errors-hi': 'uint64',
'number-of-error-log-entries-lo': 'uint64',
'number-of-error-log-entries-hi': 'uint64' },
- 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } }
##
# @GuestDiskSmart:
@@ -1000,7 +1000,7 @@
'base': { 'type': 'GuestDiskBusType' },
'discriminator': 'type',
'data': { 'nvme': 'GuestNVMeSmart' },
- 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } }
##
# @GuestDiskInfo:
@@ -1026,7 +1026,7 @@
'data': {'name': 'str', 'partition': 'bool', '*dependencies': ['str'],
'*address': 'GuestDiskAddress', '*alias': 'str',
'*smart': 'GuestDiskSmart'},
- 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } }
##
# @guest-get-disks:
@@ -1040,7 +1040,7 @@
##
{ 'command': 'guest-get-disks',
'returns': ['GuestDiskInfo'],
- 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } }
##
# @GuestFilesystemInfo:
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (8 preceding siblings ...)
2024-06-13 15:43 ` [PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev Daniel P. Berrangé
@ 2024-06-13 15:44 ` Daniel P. Berrangé
2024-07-03 8:38 ` Philippe Mathieu-Daudé
2024-07-12 8:43 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX Daniel P. Berrangé
` (7 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:44 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Rather than creating stubs for every command that just return
QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
fully exclude generation of the get-users command on POSIX
platforms lacking required APIs.
The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from
{"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
This has the additional benefit that the QGA protocol reference
now documents what conditions enable use of the command.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-posix.c | 10 +---------
qga/qapi-schema.json | 6 ++++--
2 files changed, 5 insertions(+), 11 deletions(-)
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index d92fa0ec87..a353f64ae6 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1212,15 +1212,7 @@ GuestUserList *qmp_guest_get_users(Error **errp)
return head;
}
-#else
-
-GuestUserList *qmp_guest_get_users(Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
- return NULL;
-}
-
-#endif
+#endif /* HAVE_UTMPX */
/* Replace escaped special characters with their real values. The replacement
* is done in place -- returned value is in the original string.
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 0b7f911ca5..70d4f173ad 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1413,7 +1413,8 @@
# Since: 2.10
##
{ 'struct': 'GuestUser',
- 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' } }
+ 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' },
+ 'if': { 'any': ['CONFIG_WIN32', 'HAVE_UTMPX' ] } }
##
# @guest-get-users:
@@ -1425,7 +1426,8 @@
# Since: 2.10
##
{ 'command': 'guest-get-users',
- 'returns': ['GuestUser'] }
+ 'returns': ['GuestUser'],
+ 'if': { 'any': ['CONFIG_WIN32', 'HAVE_UTMPX' ] } }
##
# @GuestTimezone:
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (9 preceding siblings ...)
2024-06-13 15:44 ` [PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx Daniel P. Berrangé
@ 2024-06-13 15:44 ` Daniel P. Berrangé
2024-07-03 8:39 ` Philippe Mathieu-Daudé
2024-07-12 8:43 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 18/22] qga: don't disable fsfreeze commands if vss_init fails Daniel P. Berrangé
` (6 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:44 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Rather than creating stubs for every command that just return
QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
fully exclude generation of the commands on other UNIX.
The command will be rejected at QMP dispatch time instead,
avoiding reimplementing rejection by blocking the stub commands.
This changes the error message for affected commands from
{"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
to
{"class": "CommandNotFound", "desc": "The command FOO has not been found"}
This has the additional benefit that the QGA protocol reference
now documents what conditions enable use of the command.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
meson.build | 1 +
qga/commands-posix.c | 8 --------
qga/qapi-schema.json | 3 ++-
3 files changed, 3 insertions(+), 9 deletions(-)
diff --git a/meson.build b/meson.build
index d9f3349b0a..92b8c02582 100644
--- a/meson.build
+++ b/meson.build
@@ -2234,6 +2234,7 @@ config_host_data.set('CONFIG_ATTR', libattr.found())
config_host_data.set('CONFIG_BDRV_WHITELIST_TOOLS', get_option('block_drv_whitelist_in_tools'))
config_host_data.set('CONFIG_BRLAPI', brlapi.found())
config_host_data.set('CONFIG_BSD', host_os in bsd_oses)
+config_host_data.set('CONFIG_FREEBSD', host_os == 'freebsd')
config_host_data.set('CONFIG_CAPSTONE', capstone.found())
config_host_data.set('CONFIG_COCOA', cocoa.found())
config_host_data.set('CONFIG_DARWIN', host_os == 'darwin')
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index a353f64ae6..f4104f2760 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -877,14 +877,6 @@ void qmp_guest_set_user_password(const char *username,
return;
}
}
-#else /* __linux__ || __FreeBSD__ */
-void qmp_guest_set_user_password(const char *username,
- const char *password,
- bool crypted,
- Error **errp)
-{
- error_setg(errp, QERR_UNSUPPORTED);
-}
#endif /* __linux__ || __FreeBSD__ */
#ifdef HAVE_GETIFADDRS
diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
index 70d4f173ad..571be3a914 100644
--- a/qga/qapi-schema.json
+++ b/qga/qapi-schema.json
@@ -1108,7 +1108,8 @@
# Since: 2.3
##
{ 'command': 'guest-set-user-password',
- 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' } }
+ 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' },
+ 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX', 'CONFIG_FREEBSD'] } }
##
# @GuestMemoryBlock:
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 18/22] qga: don't disable fsfreeze commands if vss_init fails
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (10 preceding siblings ...)
2024-06-13 15:44 ` [PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX Daniel P. Berrangé
@ 2024-06-13 15:44 ` Daniel P. Berrangé
2024-07-03 10:21 ` Manos Pitsidianakis
2024-06-13 15:44 ` [PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file Daniel P. Berrangé
` (5 subsequent siblings)
17 siblings, 1 reply; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:44 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
The fsfreeze commands are already written to report an error if
vss_init() fails. Reporting a more specific error message is more
helpful than a generic "command is disabled" message, which cannot
beteween an admin config decision and lack of platform support.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/commands-win32.c | 18 +++---------------
qga/main.c | 4 ++++
2 files changed, 7 insertions(+), 15 deletions(-)
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 2533e4c748..5866cc2e3c 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -1203,7 +1203,7 @@ GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
{
if (!vss_initialized()) {
- error_setg(errp, QERR_UNSUPPORTED);
+ error_setg(errp, "fsfreeze not possible as VSS failed to initialize");
return 0;
}
@@ -1231,7 +1231,7 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
Error *local_err = NULL;
if (!vss_initialized()) {
- error_setg(errp, QERR_UNSUPPORTED);
+ error_setg(errp, "fsfreeze not possible as VSS failed to initialize");
return 0;
}
@@ -1266,7 +1266,7 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp)
int i;
if (!vss_initialized()) {
- error_setg(errp, QERR_UNSUPPORTED);
+ error_setg(errp, "fsfreeze not possible as VSS failed to initialize");
return 0;
}
@@ -1961,18 +1961,6 @@ done:
/* add unsupported commands to the list of blocked RPCs */
GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
{
- if (!vss_init(true)) {
- g_debug("vss_init failed, vss commands are going to be disabled");
- const char *list[] = {
- "guest-get-fsinfo", "guest-fsfreeze-status",
- "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL};
- char **p = (char **)list;
-
- while (*p) {
- blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
- }
- }
-
return blockedrpcs;
}
diff --git a/qga/main.c b/qga/main.c
index f4d5f15bb3..17b6ce18ac 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -1395,6 +1395,10 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
" '%s': %s", config->state_dir, strerror(errno));
return NULL;
}
+
+ if (!vss_init(true)) {
+ g_debug("vss_init failed, vss commands will not function");
+ }
#endif
if (ga_is_frozen(s)) {
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (11 preceding siblings ...)
2024-06-13 15:44 ` [PATCH v2 18/22] qga: don't disable fsfreeze commands if vss_init fails Daniel P. Berrangé
@ 2024-06-13 15:44 ` Daniel P. Berrangé
2024-07-03 8:40 ` Philippe Mathieu-Daudé
2024-07-12 8:44 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable Daniel P. Berrangé
` (4 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:44 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
It is referenced by QGAState already, and it is clearer to declare all
data types at the top of the file, rather than have them mixed with
code later.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/main.c | 44 ++++++++++++++++++++++----------------------
1 file changed, 22 insertions(+), 22 deletions(-)
diff --git a/qga/main.c b/qga/main.c
index 17b6ce18ac..647d27037c 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -70,6 +70,28 @@ typedef struct GAPersistentState {
typedef struct GAConfig GAConfig;
+struct GAConfig {
+ char *channel_path;
+ char *method;
+ char *log_filepath;
+ char *pid_filepath;
+#ifdef CONFIG_FSFREEZE
+ char *fsfreeze_hook;
+#endif
+ char *state_dir;
+#ifdef _WIN32
+ const char *service;
+#endif
+ gchar *bliststr; /* blockedrpcs may point to this string */
+ gchar *aliststr; /* allowedrpcs may point to this string */
+ GList *blockedrpcs;
+ GList *allowedrpcs;
+ int daemonize;
+ GLogLevelFlags log_level;
+ int dumpconf;
+ bool retry_path;
+};
+
struct GAState {
JSONMessageParser parser;
GMainLoop *main_loop;
@@ -996,28 +1018,6 @@ static GList *split_list(const gchar *str, const gchar *delim)
return list;
}
-struct GAConfig {
- char *channel_path;
- char *method;
- char *log_filepath;
- char *pid_filepath;
-#ifdef CONFIG_FSFREEZE
- char *fsfreeze_hook;
-#endif
- char *state_dir;
-#ifdef _WIN32
- const char *service;
-#endif
- gchar *bliststr; /* blockedrpcs may point to this string */
- gchar *aliststr; /* allowedrpcs may point to this string */
- GList *blockedrpcs;
- GList *allowedrpcs;
- int daemonize;
- GLogLevelFlags log_level;
- int dumpconf;
- bool retry_path;
-};
-
static void config_load(GAConfig *config)
{
GError *gerr = NULL;
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (12 preceding siblings ...)
2024-06-13 15:44 ` [PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file Daniel P. Berrangé
@ 2024-06-13 15:44 ` Daniel P. Berrangé
2024-07-03 8:41 ` Philippe Mathieu-Daudé
2024-07-12 8:46 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 21/22] qga: allow configuration file path via the cli Daniel P. Berrangé
` (3 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:44 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
This variable was used to support back compat for the old config
file key name, and became redundant after the following change:
commit a7a2d636ae4549ef0551134d4bf8e084a14431c4
Author: Philippe Mathieu-Daudé <philmd@linaro.org>
Date: Thu May 30 08:36:43 2024 +0200
qga: Remove deprecated 'blacklist' argument / config key
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
qga/main.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/qga/main.c b/qga/main.c
index 647d27037c..6ff022a85d 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -1023,7 +1023,6 @@ static void config_load(GAConfig *config)
GError *gerr = NULL;
GKeyFile *keyfile;
g_autofree char *conf = g_strdup(g_getenv("QGA_CONF")) ?: get_relocated_path(QGA_CONF_DEFAULT);
- const gchar *blockrpcs_key = "block-rpcs";
/* read system config */
keyfile = g_key_file_new();
@@ -1071,9 +1070,9 @@ static void config_load(GAConfig *config)
g_key_file_get_boolean(keyfile, "general", "retry-path", &gerr);
}
- if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL)) {
+ if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL)) {
config->bliststr =
- g_key_file_get_string(keyfile, "general", blockrpcs_key, &gerr);
+ g_key_file_get_string(keyfile, "general", "block-rpcs", &gerr);
config->blockedrpcs = g_list_concat(config->blockedrpcs,
split_list(config->bliststr, ","));
}
@@ -1084,7 +1083,7 @@ static void config_load(GAConfig *config)
split_list(config->aliststr, ","));
}
- if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL) &&
+ if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL) &&
g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) {
g_critical("wrong config, using 'block-rpcs' and 'allow-rpcs' keys at"
" the same time is not allowed");
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 21/22] qga: allow configuration file path via the cli
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (13 preceding siblings ...)
2024-06-13 15:44 ` [PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable Daniel P. Berrangé
@ 2024-06-13 15:44 ` Daniel P. Berrangé
2024-07-03 8:44 ` Philippe Mathieu-Daudé
2024-07-12 9:05 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands Daniel P. Berrangé
` (2 subsequent siblings)
17 siblings, 2 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:44 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
Allowing the user to set the QGA_CONF environment variable to change
the default configuration file path is very unusual practice, made
more obscure since this ability is not documented.
This introduces the more normal '-c PATH' / '--config=PATH' command
line argument approach. This requires that we parse the comamnd line
twice, since we want the command line arguments to take priority over
the configuration file settings in general.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
docs/interop/qemu-ga.rst | 5 +++++
qga/main.c | 35 +++++++++++++++++++++++++++--------
2 files changed, 32 insertions(+), 8 deletions(-)
diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst
index 72fb75a6f5..e42b370319 100644
--- a/docs/interop/qemu-ga.rst
+++ b/docs/interop/qemu-ga.rst
@@ -33,6 +33,11 @@ Options
.. program:: qemu-ga
+.. option:: -c, --config=PATH
+
+ Configuration file path (the default is |CONFDIR|\ ``/qemu-ga.conf``,
+ unless overriden by the QGA_CONF environment variable)
+
.. option:: -m, --method=METHOD
Transport method: one of ``unix-listen``, ``virtio-serial``, or
diff --git a/qga/main.c b/qga/main.c
index 6ff022a85d..f68a32bf7b 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -1018,15 +1018,14 @@ static GList *split_list(const gchar *str, const gchar *delim)
return list;
}
-static void config_load(GAConfig *config)
+static void config_load(GAConfig *config, const char *confpath, bool required)
{
GError *gerr = NULL;
GKeyFile *keyfile;
- g_autofree char *conf = g_strdup(g_getenv("QGA_CONF")) ?: get_relocated_path(QGA_CONF_DEFAULT);
/* read system config */
keyfile = g_key_file_new();
- if (!g_key_file_load_from_file(keyfile, conf, 0, &gerr)) {
+ if (!g_key_file_load_from_file(keyfile, confpath, 0, &gerr)) {
goto end;
}
if (g_key_file_has_key(keyfile, "general", "daemon", NULL)) {
@@ -1092,10 +1091,10 @@ static void config_load(GAConfig *config)
end:
g_key_file_free(keyfile);
- if (gerr &&
- !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT)) {
+ if (gerr && (required ||
+ !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT))) {
g_critical("error loading configuration from path: %s, %s",
- conf, gerr->message);
+ confpath, gerr->message);
exit(EXIT_FAILURE);
}
g_clear_error(&gerr);
@@ -1167,12 +1166,13 @@ static void config_dump(GAConfig *config)
static void config_parse(GAConfig *config, int argc, char **argv)
{
- const char *sopt = "hVvdm:p:l:f:F::b:a:s:t:Dr";
+ const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr";
int opt_ind = 0, ch;
bool block_rpcs = false, allow_rpcs = false;
const struct option lopt[] = {
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
+ { "config", 1, NULL, 'c' },
{ "dump-conf", 0, NULL, 'D' },
{ "logfile", 1, NULL, 'l' },
{ "pidfile", 1, NULL, 'f' },
@@ -1192,6 +1192,26 @@ static void config_parse(GAConfig *config, int argc, char **argv)
{ "retry-path", 0, NULL, 'r' },
{ NULL, 0, NULL, 0 }
};
+ g_autofree char *confpath = g_strdup(g_getenv("QGA_CONF")) ?:
+ get_relocated_path(QGA_CONF_DEFAULT);
+ bool confrequired = false;
+
+ while ((ch = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
+ switch (ch) {
+ case 'c':
+ g_free(confpath);
+ confpath = g_strdup(optarg);
+ confrequired = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ config_load(config, confpath, confrequired);
+
+ /* Reset for second pass */
+ optind = 1;
while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
switch (ch) {
@@ -1582,7 +1602,6 @@ int main(int argc, char **argv)
qga_qmp_init_marshal(&ga_commands);
init_dfl_pathnames();
- config_load(config);
config_parse(config, argc, argv);
if (config->pid_filepath == NULL) {
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (14 preceding siblings ...)
2024-06-13 15:44 ` [PATCH v2 21/22] qga: allow configuration file path via the cli Daniel P. Berrangé
@ 2024-06-13 15:44 ` Daniel P. Berrangé
2024-07-03 10:01 ` Manos Pitsidianakis
2024-07-03 8:26 ` [PATCH v2 06/22] qga: move linux memory block command impls to commands-linux.c Philippe Mathieu-Daudé
2024-07-12 8:34 ` Konstantin Kostiuk
17 siblings, 1 reply; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-13 15:44 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Daniel P. Berrangé, Thomas Huth,
Michael Roth, Paolo Bonzini
It is confusing having many different pieces of code enabling and
disabling commands, and it is not clear that they all have the same
semantics, especially wrt prioritization of the block/allow lists.
The code attempted to prevent the user from setting both the block
and allow lists concurrently, however, the logic was flawed as it
checked settings in the configuration file separately from the
command line arguments. Thus it was possible to set a block list
in the config file and an allow list via a command line argument.
The --dump-conf option also creates a configuration file with both
keys present, even if unset, which means it is creating a config
that cannot actually be loaded again.
Centralizing the code in a single method "ga_apply_command_filters"
will provide a strong guarantee of consistency and clarify the
intended behaviour. With this there is no compelling technical
reason to prevent concurrent setting of both the allow and block
lists, so this flawed restriction is removed.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
---
docs/interop/qemu-ga.rst | 14 +++++
qga/commands-posix.c | 6 --
qga/commands-win32.c | 6 --
qga/main.c | 128 +++++++++++++++++----------------------
4 files changed, 70 insertions(+), 84 deletions(-)
diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst
index e42b370319..e35dcaf0e7 100644
--- a/docs/interop/qemu-ga.rst
+++ b/docs/interop/qemu-ga.rst
@@ -28,6 +28,20 @@ configuration options on the command line. For the same key, the last
option wins, but the lists accumulate (see below for configuration
file format).
+If an allowed RPCs list is defined in the configuration, then all
+RPCs will be blocked by default, except for the allowed list.
+
+If a blocked RPCs list is defined in the configuration, then all
+RPCs will be allowed by default, except for the blocked list.
+
+If both allowed and blocked RPCs lists are defined in the configuration,
+then all RPCs will be blocked by default, and then allowed list will
+be applied, followed by the blocked list.
+
+While filesystems are frozen, all except for a designated safe set
+of RPCs will blocked, regardless of what the general configuration
+declares.
+
Options
-------
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index f4104f2760..578d29f228 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -1136,12 +1136,6 @@ error:
#endif /* HAVE_GETIFADDRS */
-/* add unsupported commands to the list of blocked RPCs */
-GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
-{
- return blockedrpcs;
-}
-
/* register init/cleanup routines for stateful command groups */
void ga_command_state_init(GAState *s, GACommandState *cs)
{
diff --git a/qga/commands-win32.c b/qga/commands-win32.c
index 5866cc2e3c..61b36da469 100644
--- a/qga/commands-win32.c
+++ b/qga/commands-win32.c
@@ -1958,12 +1958,6 @@ done:
g_free(rawpasswddata);
}
-/* add unsupported commands to the list of blocked RPCs */
-GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
-{
- return blockedrpcs;
-}
-
/* register init/cleanup routines for stateful command groups */
void ga_command_state_init(GAState *s, GACommandState *cs)
{
diff --git a/qga/main.c b/qga/main.c
index f68a32bf7b..72c16fead8 100644
--- a/qga/main.c
+++ b/qga/main.c
@@ -419,60 +419,79 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2)
return strcmp(str1, str2);
}
-/* disable commands that aren't safe for fsfreeze */
-static void ga_disable_not_allowed_freeze(const QmpCommand *cmd, void *opaque)
+static bool ga_command_is_allowed(const QmpCommand *cmd, GAState *state)
{
- bool allowed = false;
int i = 0;
+ GAConfig *config = state->config;
const char *name = qmp_command_name(cmd);
+ /* Fallback policy is allow everything */
+ bool allowed = true;
- while (ga_freeze_allowlist[i] != NULL) {
- if (strcmp(name, ga_freeze_allowlist[i]) == 0) {
+ if (config->allowedrpcs) {
+ /*
+ * If an allow-list is given, this changes the fallback
+ * policy to deny everything
+ */
+ allowed = false;
+
+ if (g_list_find_custom(config->allowedrpcs, name, ga_strcmp) != NULL) {
allowed = true;
}
- i++;
}
- if (!allowed) {
- g_debug("disabling command: %s", name);
- qmp_disable_command(&ga_commands, name, "the agent is in frozen state");
- }
-}
-/* [re-]enable all commands, except those explicitly blocked by user */
-static void ga_enable_non_blocked(const QmpCommand *cmd, void *opaque)
-{
- GAState *s = opaque;
- GList *blockedrpcs = s->blockedrpcs;
- GList *allowedrpcs = s->allowedrpcs;
- const char *name = qmp_command_name(cmd);
-
- if (g_list_find_custom(blockedrpcs, name, ga_strcmp) == NULL) {
- if (qmp_command_is_enabled(cmd)) {
- return;
+ /*
+ * If both allowedrpcs and blockedrpcs are set, the blocked
+ * list will take priority
+ */
+ if (config->blockedrpcs) {
+ if (g_list_find_custom(config->blockedrpcs, name, ga_strcmp) != NULL) {
+ allowed = false;
}
+ }
- if (allowedrpcs &&
- g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) {
- return;
- }
+ /*
+ * If frozen, this filtering must take priority over
+ * absolutely everything
+ */
+ if (state->frozen) {
+ allowed = false;
- g_debug("enabling command: %s", name);
- qmp_enable_command(&ga_commands, name);
+ while (ga_freeze_allowlist[i] != NULL) {
+ if (strcmp(name, ga_freeze_allowlist[i]) == 0) {
+ allowed = true;
+ }
+ i++;
+ }
}
+
+ return allowed;
}
-/* disable commands that aren't allowed */
-static void ga_disable_not_allowed(const QmpCommand *cmd, void *opaque)
+static void ga_apply_command_filters_iter(const QmpCommand *cmd, void *opaque)
{
- GList *allowedrpcs = opaque;
+ GAState *state = opaque;
+ bool want = ga_command_is_allowed(cmd, state);
+ bool have = qmp_command_is_enabled(cmd);
const char *name = qmp_command_name(cmd);
- if (g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) {
+ if (want == have) {
+ return;
+ }
+
+ if (qmp_command_is_enabled(cmd)) {
g_debug("disabling command: %s", name);
qmp_disable_command(&ga_commands, name, "the command is not allowed");
+ } else {
+ g_debug("enabling command: %s", name);
+ qmp_enable_command(&ga_commands, name);
}
}
+static void ga_apply_command_filters(GAState *state)
+{
+ qmp_for_each_command(&ga_commands, ga_apply_command_filters_iter, state);
+}
+
static bool ga_create_file(const char *path)
{
int fd = open(path, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR);
@@ -505,15 +524,14 @@ void ga_set_frozen(GAState *s)
if (ga_is_frozen(s)) {
return;
}
- /* disable all forbidden (for frozen state) commands */
- qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL);
g_warning("disabling logging due to filesystem freeze");
- ga_disable_logging(s);
s->frozen = true;
if (!ga_create_file(s->state_filepath_isfrozen)) {
g_warning("unable to create %s, fsfreeze may not function properly",
s->state_filepath_isfrozen);
}
+ ga_apply_command_filters(s);
+ ga_disable_logging(s);
}
void ga_unset_frozen(GAState *s)
@@ -545,12 +563,12 @@ void ga_unset_frozen(GAState *s)
}
/* enable all disabled, non-blocked and allowed commands */
- qmp_for_each_command(&ga_commands, ga_enable_non_blocked, s);
s->frozen = false;
if (!ga_delete_file(s->state_filepath_isfrozen)) {
g_warning("unable to delete %s, fsfreeze may not function properly",
s->state_filepath_isfrozen);
}
+ ga_apply_command_filters(s);
}
#ifdef CONFIG_FSFREEZE
@@ -1082,13 +1100,6 @@ static void config_load(GAConfig *config, const char *confpath, bool required)
split_list(config->aliststr, ","));
}
- if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL) &&
- g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) {
- g_critical("wrong config, using 'block-rpcs' and 'allow-rpcs' keys at"
- " the same time is not allowed");
- exit(EXIT_FAILURE);
- }
-
end:
g_key_file_free(keyfile);
if (gerr && (required ||
@@ -1168,7 +1179,6 @@ static void config_parse(GAConfig *config, int argc, char **argv)
{
const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr";
int opt_ind = 0, ch;
- bool block_rpcs = false, allow_rpcs = false;
const struct option lopt[] = {
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'V' },
@@ -1264,7 +1274,6 @@ static void config_parse(GAConfig *config, int argc, char **argv)
}
config->blockedrpcs = g_list_concat(config->blockedrpcs,
split_list(optarg, ","));
- block_rpcs = true;
break;
}
case 'a': {
@@ -1274,7 +1283,6 @@ static void config_parse(GAConfig *config, int argc, char **argv)
}
config->allowedrpcs = g_list_concat(config->allowedrpcs,
split_list(optarg, ","));
- allow_rpcs = true;
break;
}
#ifdef _WIN32
@@ -1315,12 +1323,6 @@ static void config_parse(GAConfig *config, int argc, char **argv)
exit(EXIT_FAILURE);
}
}
-
- if (block_rpcs && allow_rpcs) {
- g_critical("wrong commandline, using --block-rpcs and --allow-rpcs at the"
- " same time is not allowed");
- exit(EXIT_FAILURE);
- }
}
static void config_free(GAConfig *config)
@@ -1431,7 +1433,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
s->deferred_options.log_filepath = config->log_filepath;
}
ga_disable_logging(s);
- qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL);
} else {
if (config->daemonize) {
become_daemon(config->pid_filepath);
@@ -1455,25 +1456,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
return NULL;
}
- if (config->allowedrpcs) {
- qmp_for_each_command(&ga_commands, ga_disable_not_allowed, config->allowedrpcs);
- s->allowedrpcs = config->allowedrpcs;
- }
-
- /*
- * Some commands can be blocked due to system limitation.
- * Initialize blockedrpcs list even if allowedrpcs specified.
- */
- config->blockedrpcs = ga_command_init_blockedrpcs(config->blockedrpcs);
- if (config->blockedrpcs) {
- GList *l = config->blockedrpcs;
- s->blockedrpcs = config->blockedrpcs;
- do {
- g_debug("disabling command: %s", (char *)l->data);
- qmp_disable_command(&ga_commands, l->data, NULL);
- l = g_list_next(l);
- } while (l);
- }
s->command_state = ga_command_state_new();
ga_command_state_init(s, s->command_state);
ga_command_state_init_all(s->command_state);
@@ -1499,6 +1481,8 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
}
#endif
+ ga_apply_command_filters(s);
+
ga_state = s;
return s;
}
--
2.45.1
^ permalink raw reply related [flat|nested] 70+ messages in thread
* Re: [PATCH v2 00/22] qga: clean up command source locations and conditionals
2024-06-13 15:01 [PATCH v2 00/22] qga: clean up command source locations and conditionals Daniel P. Berrangé
` (5 preceding siblings ...)
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
@ 2024-06-14 8:34 ` Marc-André Lureau
2024-06-14 9:19 ` Daniel P. Berrangé
2024-07-02 18:00 ` Daniel P. Berrangé
7 siblings, 1 reply; 70+ messages in thread
From: Marc-André Lureau @ 2024-06-14 8:34 UTC (permalink / raw)
To: Daniel P. Berrangé, Paolo Bonzini
Cc: qemu-devel, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth
[-- Attachment #1: Type: text/plain, Size: 4249 bytes --]
Hi
On Thu, Jun 13, 2024 at 7:02 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> This series is a side effect of other work I started, to attempt to
> make the QGA safe to use in confidential VMs by automatically
> restricting the permitted commands. Since this cleanup stands on
> its own, I'm sending it now.
>
> The QGA codebase has a very complicated maze of #ifdefs to create
> stubs for the various commands that cannot be implemented on certain
> platforms. It then has further logic to dynamically disable the stub
> commands at runtime, except this is not consistently applied, so
> some commands remain enabled despite being merely stubs.
>
> The resulting code is hard to follow, when trying to understand exactly
> what commands are available under what circumstances, and when changing
> impls it is easy to get the #ifdefs wrong, resulting in stubs getting
> missed on platforms without a real impl. In some cases, we have multiple
> stubs for the same command, due to the maze of #ifdefs.
>
> The QAPI schema language has support for many years for expressing
> conditions against commands when declaring them. This results in the
> QAPI code generator omitting their implementation entirely at build
> time. This has mutliple benefits
>
> * The unsupported commands are guaranteed to not exist at runtime
> * No stubs need ever be defined in the code
> * The generated QAPI reference manual documents the build conditions
>
> This series is broadly split into three parts
>
> * Moving tonnes of Linux only commands out of commands-posix.c
> into commands-linux.c to remove many #ifdefs.
> * Adding 'if' conditions in the QAPI schema to reflect the
> build conditions, removing many more #ifdefs
> * Sanitizing the logic for disabling/enabling commands at
> runtime to guarantee consistency
>
> Changed in v2:
>
> - Make FSFreeze error reporting distinguish inability to enable
> VSS from user config choice
>
> - Fully remove ga_command_init_blockedrpcs() methods. No more
> special case disabling of commands. Either they're disabled
> at build time, or disabled by user config, or by well defined
> rule ie not permitted during FS freeze.
>
> - Apply rules later in startup to avoid crash from NULL config
> pointer
>
> - Document changed error messages in commit messages
>
> - Add -c / --config command line parameter
>
> - Fix mistaken enabling of fsfreeze hooks on win32
>
> - Remove pointless 'blockrpcs_key' variable
>
> - Allow concurrent setting of allow and block lists for
> RPC commands
>
> Daniel P. Berrangé (22):
> qga: drop blocking of guest-get-memory-block-size command
> qga: move linux vcpu command impls to commands-linux.c
> qga: move linux suspend command impls to commands-linux.c
> qga: move linux fs/disk command impls to commands-linux.c
> qga: move linux disk/cpu stats command impls to commands-linux.c
> qga: move linux memory block command impls to commands-linux.c
> qga: move CONFIG_FSFREEZE/TRIM to be meson defined options
> qga: conditionalize schema for commands unsupported on Windows
> qga: conditionalize schema for commands unsupported on non-Linux POSIX
> qga: conditionalize schema for commands requiring getifaddrs
> qga: conditionalize schema for commands requiring linux/win32
> qga: conditionalize schema for commands only supported on Windows
> qga: conditionalize schema for commands requiring fsfreeze
> qga: conditionalize schema for commands requiring fstrim
> qga: conditionalize schema for commands requiring libudev
> qga: conditionalize schema for commands requiring utmpx
> qga: conditionalize schema for commands not supported on other UNIX
> qga: don't disable fsfreeze commands if vss_init fails
> qga: move declare of QGAConfig struct to top of file
> qga: remove pointless 'blockrpcs_key' variable
> qga: allow configuration file path via the cli
> qga: centralize logic for disabling/enabling commands
>
>
Something broke patchew handling:
https://patchew.org/QEMU/20240613150127.1361931-1-berrange@redhat.com/20240613154406.1365469-1-berrange@redhat.com/
--
Marc-André Lureau
[-- Attachment #2: Type: text/html, Size: 5083 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 00/22] qga: clean up command source locations and conditionals
2024-06-14 8:34 ` [PATCH v2 00/22] qga: clean up command source locations and conditionals Marc-André Lureau
@ 2024-06-14 9:19 ` Daniel P. Berrangé
0 siblings, 0 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-06-14 9:19 UTC (permalink / raw)
To: Marc-André Lureau
Cc: Paolo Bonzini, qemu-devel, Michael Roth,
Philippe Mathieu-Daudé, Konstantin Kostiuk, Thomas Huth
On Fri, Jun 14, 2024 at 12:34:52PM +0400, Marc-André Lureau wrote:
> Hi
>
> On Thu, Jun 13, 2024 at 7:02 PM Daniel P. Berrangé <berrange@redhat.com>
> wrote:
>
> > This series is a side effect of other work I started, to attempt to
> > make the QGA safe to use in confidential VMs by automatically
> > restricting the permitted commands. Since this cleanup stands on
> > its own, I'm sending it now.
> >
> > The QGA codebase has a very complicated maze of #ifdefs to create
> > stubs for the various commands that cannot be implemented on certain
> > platforms. It then has further logic to dynamically disable the stub
> > commands at runtime, except this is not consistently applied, so
> > some commands remain enabled despite being merely stubs.
> >
> > The resulting code is hard to follow, when trying to understand exactly
> > what commands are available under what circumstances, and when changing
> > impls it is easy to get the #ifdefs wrong, resulting in stubs getting
> > missed on platforms without a real impl. In some cases, we have multiple
> > stubs for the same command, due to the maze of #ifdefs.
> >
> > The QAPI schema language has support for many years for expressing
> > conditions against commands when declaring them. This results in the
> > QAPI code generator omitting their implementation entirely at build
> > time. This has mutliple benefits
> >
> > * The unsupported commands are guaranteed to not exist at runtime
> > * No stubs need ever be defined in the code
> > * The generated QAPI reference manual documents the build conditions
> >
> > This series is broadly split into three parts
> >
> > * Moving tonnes of Linux only commands out of commands-posix.c
> > into commands-linux.c to remove many #ifdefs.
> > * Adding 'if' conditions in the QAPI schema to reflect the
> > build conditions, removing many more #ifdefs
> > * Sanitizing the logic for disabling/enabling commands at
> > runtime to guarantee consistency
> >
> > Changed in v2:
> >
> > - Make FSFreeze error reporting distinguish inability to enable
> > VSS from user config choice
> >
> > - Fully remove ga_command_init_blockedrpcs() methods. No more
> > special case disabling of commands. Either they're disabled
> > at build time, or disabled by user config, or by well defined
> > rule ie not permitted during FS freeze.
> >
> > - Apply rules later in startup to avoid crash from NULL config
> > pointer
> >
> > - Document changed error messages in commit messages
> >
> > - Add -c / --config command line parameter
> >
> > - Fix mistaken enabling of fsfreeze hooks on win32
> >
> > - Remove pointless 'blockrpcs_key' variable
> >
> > - Allow concurrent setting of allow and block lists for
> > RPC commands
> >
> > Daniel P. Berrangé (22):
> > qga: drop blocking of guest-get-memory-block-size command
> > qga: move linux vcpu command impls to commands-linux.c
> > qga: move linux suspend command impls to commands-linux.c
> > qga: move linux fs/disk command impls to commands-linux.c
> > qga: move linux disk/cpu stats command impls to commands-linux.c
> > qga: move linux memory block command impls to commands-linux.c
> > qga: move CONFIG_FSFREEZE/TRIM to be meson defined options
> > qga: conditionalize schema for commands unsupported on Windows
> > qga: conditionalize schema for commands unsupported on non-Linux POSIX
> > qga: conditionalize schema for commands requiring getifaddrs
> > qga: conditionalize schema for commands requiring linux/win32
> > qga: conditionalize schema for commands only supported on Windows
> > qga: conditionalize schema for commands requiring fsfreeze
> > qga: conditionalize schema for commands requiring fstrim
> > qga: conditionalize schema for commands requiring libudev
> > qga: conditionalize schema for commands requiring utmpx
> > qga: conditionalize schema for commands not supported on other UNIX
> > qga: don't disable fsfreeze commands if vss_init fails
> > qga: move declare of QGAConfig struct to top of file
> > qga: remove pointless 'blockrpcs_key' variable
> > qga: allow configuration file path via the cli
> > qga: centralize logic for disabling/enabling commands
> >
> >
> Something broke patchew handling:
> https://patchew.org/QEMU/20240613150127.1361931-1-berrange@redhat.com/20240613154406.1365469-1-berrange@redhat.com/
gmail refused further mail delivery from me part way through sending
the series, so I had to send the 2nd half of it separately.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 00/22] qga: clean up command source locations and conditionals
2024-06-13 15:01 [PATCH v2 00/22] qga: clean up command source locations and conditionals Daniel P. Berrangé
` (6 preceding siblings ...)
2024-06-14 8:34 ` [PATCH v2 00/22] qga: clean up command source locations and conditionals Marc-André Lureau
@ 2024-07-02 18:00 ` Daniel P. Berrangé
2024-07-03 6:15 ` Marc-André Lureau
7 siblings, 1 reply; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-07-02 18:00 UTC (permalink / raw)
To: qemu-devel
Cc: Marc-André Lureau, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Paolo Bonzini
Ping: for any review comments from QGA maintainers ?
On Thu, Jun 13, 2024 at 04:01:05PM +0100, Daniel P. Berrangé wrote:
> This series is a side effect of other work I started, to attempt to
> make the QGA safe to use in confidential VMs by automatically
> restricting the permitted commands. Since this cleanup stands on
> its own, I'm sending it now.
>
> The QGA codebase has a very complicated maze of #ifdefs to create
> stubs for the various commands that cannot be implemented on certain
> platforms. It then has further logic to dynamically disable the stub
> commands at runtime, except this is not consistently applied, so
> some commands remain enabled despite being merely stubs.
>
> The resulting code is hard to follow, when trying to understand exactly
> what commands are available under what circumstances, and when changing
> impls it is easy to get the #ifdefs wrong, resulting in stubs getting
> missed on platforms without a real impl. In some cases, we have multiple
> stubs for the same command, due to the maze of #ifdefs.
>
> The QAPI schema language has support for many years for expressing
> conditions against commands when declaring them. This results in the
> QAPI code generator omitting their implementation entirely at build
> time. This has mutliple benefits
>
> * The unsupported commands are guaranteed to not exist at runtime
> * No stubs need ever be defined in the code
> * The generated QAPI reference manual documents the build conditions
>
> This series is broadly split into three parts
>
> * Moving tonnes of Linux only commands out of commands-posix.c
> into commands-linux.c to remove many #ifdefs.
> * Adding 'if' conditions in the QAPI schema to reflect the
> build conditions, removing many more #ifdefs
> * Sanitizing the logic for disabling/enabling commands at
> runtime to guarantee consistency
>
> Changed in v2:
>
> - Make FSFreeze error reporting distinguish inability to enable
> VSS from user config choice
>
> - Fully remove ga_command_init_blockedrpcs() methods. No more
> special case disabling of commands. Either they're disabled
> at build time, or disabled by user config, or by well defined
> rule ie not permitted during FS freeze.
>
> - Apply rules later in startup to avoid crash from NULL config
> pointer
>
> - Document changed error messages in commit messages
>
> - Add -c / --config command line parameter
>
> - Fix mistaken enabling of fsfreeze hooks on win32
>
> - Remove pointless 'blockrpcs_key' variable
>
> - Allow concurrent setting of allow and block lists for
> RPC commands
>
> Daniel P. Berrangé (22):
> qga: drop blocking of guest-get-memory-block-size command
> qga: move linux vcpu command impls to commands-linux.c
> qga: move linux suspend command impls to commands-linux.c
> qga: move linux fs/disk command impls to commands-linux.c
> qga: move linux disk/cpu stats command impls to commands-linux.c
> qga: move linux memory block command impls to commands-linux.c
> qga: move CONFIG_FSFREEZE/TRIM to be meson defined options
> qga: conditionalize schema for commands unsupported on Windows
> qga: conditionalize schema for commands unsupported on non-Linux POSIX
> qga: conditionalize schema for commands requiring getifaddrs
> qga: conditionalize schema for commands requiring linux/win32
> qga: conditionalize schema for commands only supported on Windows
> qga: conditionalize schema for commands requiring fsfreeze
> qga: conditionalize schema for commands requiring fstrim
> qga: conditionalize schema for commands requiring libudev
> qga: conditionalize schema for commands requiring utmpx
> qga: conditionalize schema for commands not supported on other UNIX
> qga: don't disable fsfreeze commands if vss_init fails
> qga: move declare of QGAConfig struct to top of file
> qga: remove pointless 'blockrpcs_key' variable
> qga: allow configuration file path via the cli
> qga: centralize logic for disabling/enabling commands
>
> docs/interop/qemu-ga.rst | 19 +
> meson.build | 16 +
> qga/commands-bsd.c | 24 -
> qga/commands-common.h | 9 -
> qga/commands-linux.c | 1805 +++++++++++++++++++++++++++++
> qga/commands-posix.c | 2373 +++-----------------------------------
> qga/commands-win32.c | 78 +-
> qga/main.c | 216 ++--
> qga/qapi-schema.json | 153 ++-
> 9 files changed, 2234 insertions(+), 2459 deletions(-)
>
> --
> 2.45.1
>
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 00/22] qga: clean up command source locations and conditionals
2024-07-02 18:00 ` Daniel P. Berrangé
@ 2024-07-03 6:15 ` Marc-André Lureau
2024-07-03 8:06 ` Daniel P. Berrangé
0 siblings, 1 reply; 70+ messages in thread
From: Marc-André Lureau @ 2024-07-03 6:15 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Paolo Bonzini
Hi Daniel
On Tue, Jul 2, 2024 at 10:00 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> Ping: for any review comments from QGA maintainers ?
Maybe you could resend for patchew to correctly handle the series.
thanks
>
> On Thu, Jun 13, 2024 at 04:01:05PM +0100, Daniel P. Berrangé wrote:
> > This series is a side effect of other work I started, to attempt to
> > make the QGA safe to use in confidential VMs by automatically
> > restricting the permitted commands. Since this cleanup stands on
> > its own, I'm sending it now.
> >
> > The QGA codebase has a very complicated maze of #ifdefs to create
> > stubs for the various commands that cannot be implemented on certain
> > platforms. It then has further logic to dynamically disable the stub
> > commands at runtime, except this is not consistently applied, so
> > some commands remain enabled despite being merely stubs.
> >
> > The resulting code is hard to follow, when trying to understand exactly
> > what commands are available under what circumstances, and when changing
> > impls it is easy to get the #ifdefs wrong, resulting in stubs getting
> > missed on platforms without a real impl. In some cases, we have multiple
> > stubs for the same command, due to the maze of #ifdefs.
> >
> > The QAPI schema language has support for many years for expressing
> > conditions against commands when declaring them. This results in the
> > QAPI code generator omitting their implementation entirely at build
> > time. This has mutliple benefits
> >
> > * The unsupported commands are guaranteed to not exist at runtime
> > * No stubs need ever be defined in the code
> > * The generated QAPI reference manual documents the build conditions
> >
> > This series is broadly split into three parts
> >
> > * Moving tonnes of Linux only commands out of commands-posix.c
> > into commands-linux.c to remove many #ifdefs.
> > * Adding 'if' conditions in the QAPI schema to reflect the
> > build conditions, removing many more #ifdefs
> > * Sanitizing the logic for disabling/enabling commands at
> > runtime to guarantee consistency
> >
> > Changed in v2:
> >
> > - Make FSFreeze error reporting distinguish inability to enable
> > VSS from user config choice
> >
> > - Fully remove ga_command_init_blockedrpcs() methods. No more
> > special case disabling of commands. Either they're disabled
> > at build time, or disabled by user config, or by well defined
> > rule ie not permitted during FS freeze.
> >
> > - Apply rules later in startup to avoid crash from NULL config
> > pointer
> >
> > - Document changed error messages in commit messages
> >
> > - Add -c / --config command line parameter
> >
> > - Fix mistaken enabling of fsfreeze hooks on win32
> >
> > - Remove pointless 'blockrpcs_key' variable
> >
> > - Allow concurrent setting of allow and block lists for
> > RPC commands
> >
> > Daniel P. Berrangé (22):
> > qga: drop blocking of guest-get-memory-block-size command
> > qga: move linux vcpu command impls to commands-linux.c
> > qga: move linux suspend command impls to commands-linux.c
> > qga: move linux fs/disk command impls to commands-linux.c
> > qga: move linux disk/cpu stats command impls to commands-linux.c
> > qga: move linux memory block command impls to commands-linux.c
> > qga: move CONFIG_FSFREEZE/TRIM to be meson defined options
> > qga: conditionalize schema for commands unsupported on Windows
> > qga: conditionalize schema for commands unsupported on non-Linux POSIX
> > qga: conditionalize schema for commands requiring getifaddrs
> > qga: conditionalize schema for commands requiring linux/win32
> > qga: conditionalize schema for commands only supported on Windows
> > qga: conditionalize schema for commands requiring fsfreeze
> > qga: conditionalize schema for commands requiring fstrim
> > qga: conditionalize schema for commands requiring libudev
> > qga: conditionalize schema for commands requiring utmpx
> > qga: conditionalize schema for commands not supported on other UNIX
> > qga: don't disable fsfreeze commands if vss_init fails
> > qga: move declare of QGAConfig struct to top of file
> > qga: remove pointless 'blockrpcs_key' variable
> > qga: allow configuration file path via the cli
> > qga: centralize logic for disabling/enabling commands
> >
> > docs/interop/qemu-ga.rst | 19 +
> > meson.build | 16 +
> > qga/commands-bsd.c | 24 -
> > qga/commands-common.h | 9 -
> > qga/commands-linux.c | 1805 +++++++++++++++++++++++++++++
> > qga/commands-posix.c | 2373 +++-----------------------------------
> > qga/commands-win32.c | 78 +-
> > qga/main.c | 216 ++--
> > qga/qapi-schema.json | 153 ++-
> > 9 files changed, 2234 insertions(+), 2459 deletions(-)
> >
> > --
> > 2.45.1
> >
>
> With regards,
> Daniel
> --
> |: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
> |: https://libvirt.org -o- https://fstop138.berrange.com :|
> |: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 00/22] qga: clean up command source locations and conditionals
2024-07-03 6:15 ` Marc-André Lureau
@ 2024-07-03 8:06 ` Daniel P. Berrangé
2024-07-03 8:17 ` Marc-André Lureau
0 siblings, 1 reply; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-07-03 8:06 UTC (permalink / raw)
To: Marc-André Lureau
Cc: qemu-devel, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Paolo Bonzini
On Wed, Jul 03, 2024 at 10:15:44AM +0400, Marc-André Lureau wrote:
> Hi Daniel
>
> On Tue, Jul 2, 2024 at 10:00 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
> >
> > Ping: for any review comments from QGA maintainers ?
>
> Maybe you could resend for patchew to correctly handle the series.
I don't want to spam the list again just because patchew didn't
handle it. If anyone can't just pull the messages from mail
though, they are also at https://gitlab.com/berrange/qemu/-/tags/qga-conditions-v2
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 00/22] qga: clean up command source locations and conditionals
2024-07-03 8:06 ` Daniel P. Berrangé
@ 2024-07-03 8:17 ` Marc-André Lureau
0 siblings, 0 replies; 70+ messages in thread
From: Marc-André Lureau @ 2024-07-03 8:17 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Michael Roth, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Paolo Bonzini
Hi
On Wed, Jul 3, 2024 at 12:06 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
>
> On Wed, Jul 03, 2024 at 10:15:44AM +0400, Marc-André Lureau wrote:
> > Hi Daniel
> >
> > On Tue, Jul 2, 2024 at 10:00 PM Daniel P. Berrangé <berrange@redhat.com> wrote:
> > >
> > > Ping: for any review comments from QGA maintainers ?
> >
> > Maybe you could resend for patchew to correctly handle the series.
>
> I don't want to spam the list again just because patchew didn't
> handle it. If anyone can't just pull the messages from mail
> though, they are also at https://gitlab.com/berrange/qemu/-/tags/qga-conditions-v2
patchew also handles R-b/A-b/T-b tags, mail ref, versioning etc..
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 05/22] qga: move linux disk/cpu stats command impls to commands-linux.c
2024-06-13 15:01 ` [PATCH v2 05/22] qga: move linux disk/cpu stats " Daniel P. Berrangé
@ 2024-07-03 8:25 ` Philippe Mathieu-Daudé
2024-07-12 8:33 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:25 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Michael Roth, Konstantin Kostiuk,
Thomas Huth, Paolo Bonzini
On 13/6/24 17:01, Daniel P. Berrangé wrote:
> The qmp_guest_{diskstats,cpustats} command impls in
> commands-posix.c are surrounded by '#ifdef __linux__' so should
> instead live in commands-linux.c
>
> This also removes a "#ifdef CONFIG_LINUX" that was nested inside
> a "#ifdef __linux__".
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 195 ++++++++++++++++++++++++++++++++++++++++++
> qga/commands-posix.c | 199 -------------------------------------------
> 2 files changed, 195 insertions(+), 199 deletions(-)
Easy to review using 'git-diff --color-moved=dimmed-zebra'.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 06/22] qga: move linux memory block command impls to commands-linux.c
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (15 preceding siblings ...)
2024-06-13 15:44 ` [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands Daniel P. Berrangé
@ 2024-07-03 8:26 ` Philippe Mathieu-Daudé
2024-07-12 8:34 ` Konstantin Kostiuk
17 siblings, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:26 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:43, Daniel P. Berrangé wrote:
> The qmp_guest_{set,get}_{memory_blocks,block_info} command impls in
> commands-posix.c are surrounded by '#ifdef __linux__' so should
> instead live in commands-linux.c
>
> This also removes a "#ifdef CONFIG_LINUX" that was nested inside
> a "#ifdef __linux__".
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 308 ++++++++++++++++++++++++++++++++++++++++++
> qga/commands-posix.c | 311 +------------------------------------------
> 2 files changed, 309 insertions(+), 310 deletions(-)
Reviewed using 'git-diff --color-moved=dimmed-zebra'.
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows
2024-06-13 15:43 ` [PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows Daniel P. Berrangé
@ 2024-07-03 8:30 ` Philippe Mathieu-Daudé
2024-07-12 8:34 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:30 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:43, Daniel P. Berrangé wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the commands on Windows.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been found"}
>
> This also fixes an accidental inconsistency where some commands
> (guest-get-diskstats & guest-get-cpustats) are implemented as
> stubs, yet not added to the blockedrpc list. Those change their
> error message from
>
> {"class": "GenericError, "desc": "this feature or command is not currently supported"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been found"}
>
> The final additional benefit is that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 2 +-
> qga/commands-win32.c | 56 +-------------------------------------------
> qga/qapi-schema.json | 45 +++++++++++++++++++++++------------
> 3 files changed, 32 insertions(+), 71 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX
2024-06-13 15:43 ` [PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX Daniel P. Berrangé
@ 2024-07-03 8:31 ` Philippe Mathieu-Daudé
2024-07-12 8:35 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:31 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:43, Daniel P. Berrangé wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the commands on non-Linux POSIX
> platforms
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 66 --------------------------------------------
> qga/qapi-schema.json | 30 +++++++++++---------
> 2 files changed, 17 insertions(+), 79 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs
2024-06-13 15:43 ` [PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs Daniel P. Berrangé
@ 2024-07-03 8:32 ` Philippe Mathieu-Daudé
2024-07-12 8:35 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:32 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:43, Daniel P. Berrangé wrote:
> Rather than creating stubs for every comamnd that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the network interface command on
> POSIX platforms lacking getifaddrs().
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 13 -------------
> qga/qapi-schema.json | 15 ++++++++++-----
> 2 files changed, 10 insertions(+), 18 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows
2024-06-13 15:43 ` [PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows Daniel P. Berrangé
@ 2024-07-03 8:35 ` Philippe Mathieu-Daudé
2024-07-12 8:37 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:35 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:43, Daniel P. Berrangé wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the commands on non-Windows.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 9 ---------
> qga/qapi-schema.json | 15 ++++++++++-----
> 2 files changed, 10 insertions(+), 14 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim
2024-06-13 15:43 ` [PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim Daniel P. Berrangé
@ 2024-07-03 8:36 ` Philippe Mathieu-Daudé
2024-07-12 8:38 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:36 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:43, Daniel P. Berrangé wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the filesystem trimming commands
> on POSIX platforms lacking required APIs.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 13 -------------
> qga/qapi-schema.json | 9 ++++++---
> 2 files changed, 6 insertions(+), 16 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze
2024-06-13 15:43 ` [PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze Daniel P. Berrangé
@ 2024-07-03 8:37 ` Philippe Mathieu-Daudé
2024-07-12 8:37 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:37 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:43, Daniel P. Berrangé wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the schema to fully
> exclude generation of the filesystem freezing commands on POSIX
> platforms lacking the required APIs.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 47 --------------------------------------------
> qga/qapi-schema.json | 15 +++++++++-----
> 2 files changed, 10 insertions(+), 52 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev
2024-06-13 15:43 ` [PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev Daniel P. Berrangé
@ 2024-07-03 8:37 ` Philippe Mathieu-Daudé
2024-07-12 8:40 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:37 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:43, Daniel P. Berrangé wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the schema to fully
> exclude generation of the filesystem trimming commands on POSIX
> platforms lacking required APIs.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 8 --------
> qga/qapi-schema.json | 8 ++++----
> 2 files changed, 4 insertions(+), 12 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx
2024-06-13 15:44 ` [PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx Daniel P. Berrangé
@ 2024-07-03 8:38 ` Philippe Mathieu-Daudé
2024-07-12 8:43 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:38 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:44, Daniel P. Berrangé wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the get-users command on POSIX
> platforms lacking required APIs.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 10 +---------
> qga/qapi-schema.json | 6 ++++--
> 2 files changed, 5 insertions(+), 11 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX
2024-06-13 15:44 ` [PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX Daniel P. Berrangé
@ 2024-07-03 8:39 ` Philippe Mathieu-Daudé
2024-07-12 8:43 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:39 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:44, Daniel P. Berrangé wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the commands on other UNIX.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> meson.build | 1 +
> qga/commands-posix.c | 8 --------
> qga/qapi-schema.json | 3 ++-
> 3 files changed, 3 insertions(+), 9 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file
2024-06-13 15:44 ` [PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file Daniel P. Berrangé
@ 2024-07-03 8:40 ` Philippe Mathieu-Daudé
2024-07-12 8:44 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:40 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:44, Daniel P. Berrangé wrote:
> It is referenced by QGAState already, and it is clearer to declare all
> data types at the top of the file, rather than have them mixed with
> code later.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/main.c | 44 ++++++++++++++++++++++----------------------
> 1 file changed, 22 insertions(+), 22 deletions(-)
>
> diff --git a/qga/main.c b/qga/main.c
> index 17b6ce18ac..647d27037c 100644
> --- a/qga/main.c
> +++ b/qga/main.c
> @@ -70,6 +70,28 @@ typedef struct GAPersistentState {
>
> typedef struct GAConfig GAConfig;
Matter of style, personally I'd squash within the
typedef.
> +struct GAConfig {
> + char *channel_path;
> + char *method;
> + char *log_filepath;
> + char *pid_filepath;
> +#ifdef CONFIG_FSFREEZE
> + char *fsfreeze_hook;
> +#endif
> + char *state_dir;
> +#ifdef _WIN32
> + const char *service;
> +#endif
> + gchar *bliststr; /* blockedrpcs may point to this string */
> + gchar *aliststr; /* allowedrpcs may point to this string */
> + GList *blockedrpcs;
> + GList *allowedrpcs;
> + int daemonize;
> + GLogLevelFlags log_level;
> + int dumpconf;
> + bool retry_path;
> +};
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable
2024-06-13 15:44 ` [PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable Daniel P. Berrangé
@ 2024-07-03 8:41 ` Philippe Mathieu-Daudé
2024-07-12 8:46 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:41 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:44, Daniel P. Berrangé wrote:
> This variable was used to support back compat for the old config
> file key name, and became redundant after the following change:
>
> commit a7a2d636ae4549ef0551134d4bf8e084a14431c4
> Author: Philippe Mathieu-Daudé <philmd@linaro.org>
> Date: Thu May 30 08:36:43 2024 +0200
>
> qga: Remove deprecated 'blacklist' argument / config key
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/main.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 21/22] qga: allow configuration file path via the cli
2024-06-13 15:44 ` [PATCH v2 21/22] qga: allow configuration file path via the cli Daniel P. Berrangé
@ 2024-07-03 8:44 ` Philippe Mathieu-Daudé
2024-07-12 9:05 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:44 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 13/6/24 17:44, Daniel P. Berrangé wrote:
> Allowing the user to set the QGA_CONF environment variable to change
> the default configuration file path is very unusual practice, made
> more obscure since this ability is not documented.
>
> This introduces the more normal '-c PATH' / '--config=PATH' command
> line argument approach. This requires that we parse the comamnd line
> twice, since we want the command line arguments to take priority over
> the configuration file settings in general.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> docs/interop/qemu-ga.rst | 5 +++++
> qga/main.c | 35 +++++++++++++++++++++++++++--------
> 2 files changed, 32 insertions(+), 8 deletions(-)
>
> diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst
> index 72fb75a6f5..e42b370319 100644
> --- a/docs/interop/qemu-ga.rst
> +++ b/docs/interop/qemu-ga.rst
> @@ -33,6 +33,11 @@ Options
>
> .. program:: qemu-ga
>
> +.. option:: -c, --config=PATH
> +
> + Configuration file path (the default is |CONFDIR|\ ``/qemu-ga.conf``,
> + unless overriden by the QGA_CONF environment variable)
> +
> .. option:: -m, --method=METHOD
>
> Transport method: one of ``unix-listen``, ``virtio-serial``, or
> diff --git a/qga/main.c b/qga/main.c
> index 6ff022a85d..f68a32bf7b 100644
> --- a/qga/main.c
> +++ b/qga/main.c
> @@ -1018,15 +1018,14 @@ static GList *split_list(const gchar *str, const gchar *delim)
> return list;
> }
>
> -static void config_load(GAConfig *config)
> +static void config_load(GAConfig *config, const char *confpath, bool required)
> {
> GError *gerr = NULL;
> GKeyFile *keyfile;
> - g_autofree char *conf = g_strdup(g_getenv("QGA_CONF")) ?: get_relocated_path(QGA_CONF_DEFAULT);
>
> /* read system config */
> keyfile = g_key_file_new();
> - if (!g_key_file_load_from_file(keyfile, conf, 0, &gerr)) {
> + if (!g_key_file_load_from_file(keyfile, confpath, 0, &gerr)) {
> goto end;
> }
> if (g_key_file_has_key(keyfile, "general", "daemon", NULL)) {
> @@ -1092,10 +1091,10 @@ static void config_load(GAConfig *config)
>
> end:
> g_key_file_free(keyfile);
> - if (gerr &&
> - !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT)) {
> + if (gerr && (required ||
> + !(gerr->domain == G_FILE_ERROR && gerr->code == G_FILE_ERROR_NOENT))) {
> g_critical("error loading configuration from path: %s, %s",
> - conf, gerr->message);
> + confpath, gerr->message);
> exit(EXIT_FAILURE);
> }
> g_clear_error(&gerr);
> @@ -1167,12 +1166,13 @@ static void config_dump(GAConfig *config)
>
> static void config_parse(GAConfig *config, int argc, char **argv)
> {
> - const char *sopt = "hVvdm:p:l:f:F::b:a:s:t:Dr";
> + const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr";
> int opt_ind = 0, ch;
> bool block_rpcs = false, allow_rpcs = false;
> const struct option lopt[] = {
> { "help", 0, NULL, 'h' },
> { "version", 0, NULL, 'V' },
> + { "config", 1, NULL, 'c' },
> { "dump-conf", 0, NULL, 'D' },
> { "logfile", 1, NULL, 'l' },
> { "pidfile", 1, NULL, 'f' },
> @@ -1192,6 +1192,26 @@ static void config_parse(GAConfig *config, int argc, char **argv)
> { "retry-path", 0, NULL, 'r' },
> { NULL, 0, NULL, 0 }
> };
> + g_autofree char *confpath = g_strdup(g_getenv("QGA_CONF")) ?:
> + get_relocated_path(QGA_CONF_DEFAULT);
> + bool confrequired = false;
> +
> + while ((ch = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
> + switch (ch) {
> + case 'c':
> + g_free(confpath);
> + confpath = g_strdup(optarg);
> + confrequired = true;
> + break;
> + default:
> + break;
> + }
> + }
> +
> + config_load(config, confpath, confrequired);
> +
> + /* Reset for second pass */
> + optind = 1;
>
> while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
> switch (ch) {
> @@ -1582,7 +1602,6 @@ int main(int argc, char **argv)
> qga_qmp_init_marshal(&ga_commands);
>
> init_dfl_pathnames();
> - config_load(config);
> config_parse(config, argc, argv);
>
> if (config->pid_filepath == NULL) {
This looks like a trivial (correct) CLI change, but
I don't feel confident anymore reviewing this area,
so will let others have a look.
Regards,
Phil.
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 02/22] qga: move linux vcpu command impls to commands-linux.c
2024-06-13 15:01 ` [PATCH v2 02/22] qga: move linux vcpu command impls to commands-linux.c Daniel P. Berrangé
@ 2024-07-03 8:45 ` Philippe Mathieu-Daudé
2024-07-12 8:30 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:45 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Michael Roth, Konstantin Kostiuk,
Thomas Huth, Paolo Bonzini, Manos Pitsidianakis
On 13/6/24 17:01, Daniel P. Berrangé wrote:
> The qmp_guest_set_vcpus and qmp_guest_get_vcpus command impls in
> commands-posix.c are surrounded by '#ifdef __linux__' so should
> instead live in commands-linux.c
>
> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 141 +++++++++++++++++++++++++++++++++++++++++++
> qga/commands-posix.c | 139 ------------------------------------------
> 2 files changed, 141 insertions(+), 139 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 03/22] qga: move linux suspend command impls to commands-linux.c
2024-06-13 15:01 ` [PATCH v2 03/22] qga: move linux suspend " Daniel P. Berrangé
@ 2024-07-03 8:45 ` Philippe Mathieu-Daudé
2024-07-12 8:29 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:45 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Michael Roth, Konstantin Kostiuk,
Thomas Huth, Paolo Bonzini, Manos Pitsidianakis
On 13/6/24 17:01, Daniel P. Berrangé wrote:
> The qmp_guest_suspend_{disk,ram,hybrid} command impls in
> commands-posix.c are surrounded by '#ifdef __linux__' so should
> instead live in commands-linux.c
>
> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 265 +++++++++++++++++++++++++++++++++++++++++++
> qga/commands-posix.c | 265 -------------------------------------------
> 2 files changed, 265 insertions(+), 265 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 04/22] qga: move linux fs/disk command impls to commands-linux.c
2024-06-13 15:01 ` [PATCH v2 04/22] qga: move linux fs/disk " Daniel P. Berrangé
@ 2024-07-03 8:46 ` Philippe Mathieu-Daudé
2024-07-12 8:29 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 8:46 UTC (permalink / raw)
To: Daniel P. Berrangé, qemu-devel
Cc: Marc-André Lureau, Michael Roth, Konstantin Kostiuk,
Thomas Huth, Paolo Bonzini, Manos Pitsidianakis
On 13/6/24 17:01, Daniel P. Berrangé wrote:
> The qmp_guest_{fstrim, get_fsinfo, get_disks} command impls in
> commands-posix.c are surrounded by '#ifdef __linux__' so should
> instead live in commands-linux.c
>
> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 904 ++++++++++++++++++++++++++++++++++++++++++
> qga/commands-posix.c | 909 -------------------------------------------
> 2 files changed, 904 insertions(+), 909 deletions(-)
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands
2024-06-13 15:44 ` [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands Daniel P. Berrangé
@ 2024-07-03 10:01 ` Manos Pitsidianakis
2024-07-03 12:09 ` Philippe Mathieu-Daudé
2024-07-12 13:01 ` Daniel P. Berrangé
0 siblings, 2 replies; 70+ messages in thread
From: Manos Pitsidianakis @ 2024-07-03 10:01 UTC (permalink / raw)
To: qemu-devel, Daniel P. Berrangé
Cc: Marc-André Lureau, Philippe Mathieu-Daudé ,
Konstantin Kostiuk, Daniel P. Berrangé , Thomas Huth,
Michael Roth, Paolo Bonzini
Hello Daniel,
This cleanup seems like a good idea,
On Thu, 13 Jun 2024 18:44, "Daniel P. Berrangé" <berrange@redhat.com> wrote:
>It is confusing having many different pieces of code enabling and
>disabling commands, and it is not clear that they all have the same
>semantics, especially wrt prioritization of the block/allow lists.
>The code attempted to prevent the user from setting both the block
>and allow lists concurrently, however, the logic was flawed as it
>checked settings in the configuration file separately from the
>command line arguments. Thus it was possible to set a block list
>in the config file and an allow list via a command line argument.
>The --dump-conf option also creates a configuration file with both
>keys present, even if unset, which means it is creating a config
>that cannot actually be loaded again.
>
>Centralizing the code in a single method "ga_apply_command_filters"
>will provide a strong guarantee of consistency and clarify the
>intended behaviour. With this there is no compelling technical
>reason to prevent concurrent setting of both the allow and block
>lists, so this flawed restriction is removed.
>
>Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>---
> docs/interop/qemu-ga.rst | 14 +++++
> qga/commands-posix.c | 6 --
> qga/commands-win32.c | 6 --
> qga/main.c | 128 +++++++++++++++++----------------------
> 4 files changed, 70 insertions(+), 84 deletions(-)
>
>diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst
>index e42b370319..e35dcaf0e7 100644
>--- a/docs/interop/qemu-ga.rst
>+++ b/docs/interop/qemu-ga.rst
>@@ -28,6 +28,20 @@ configuration options on the command line. For the same key, the last
> option wins, but the lists accumulate (see below for configuration
> file format).
>
>+If an allowed RPCs list is defined in the configuration, then all
>+RPCs will be blocked by default, except for the allowed list.
>+
>+If a blocked RPCs list is defined in the configuration, then all
>+RPCs will be allowed by default, except for the blocked list.
>+
>+If both allowed and blocked RPCs lists are defined in the configuration,
>+then all RPCs will be blocked by default, and then allowed list will
>+be applied, followed by the blocked list.
Nit: Missing an article here
-then all RPCs will be blocked by default, and then allowed list will
+then all RPCs will be blocked by default, then the allowed list will
>+
>+While filesystems are frozen, all except for a designated safe set
>+of RPCs will blocked, regardless of what the general configuration
>+declares.
>+
> Options
> -------
>
>diff --git a/qga/commands-posix.c b/qga/commands-posix.c
>index f4104f2760..578d29f228 100644
>--- a/qga/commands-posix.c
>+++ b/qga/commands-posix.c
>@@ -1136,12 +1136,6 @@ error:
>
> #endif /* HAVE_GETIFADDRS */
>
>-/* add unsupported commands to the list of blocked RPCs */
>-GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
>-{
>- return blockedrpcs;
>-}
>-
> /* register init/cleanup routines for stateful command groups */
> void ga_command_state_init(GAState *s, GACommandState *cs)
> {
>diff --git a/qga/commands-win32.c b/qga/commands-win32.c
>index 5866cc2e3c..61b36da469 100644
>--- a/qga/commands-win32.c
>+++ b/qga/commands-win32.c
>@@ -1958,12 +1958,6 @@ done:
> g_free(rawpasswddata);
> }
>
>-/* add unsupported commands to the list of blocked RPCs */
>-GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
>-{
>- return blockedrpcs;
>-}
>-
> /* register init/cleanup routines for stateful command groups */
> void ga_command_state_init(GAState *s, GACommandState *cs)
> {
>diff --git a/qga/main.c b/qga/main.c
>index f68a32bf7b..72c16fead8 100644
>--- a/qga/main.c
>+++ b/qga/main.c
>@@ -419,60 +419,79 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2)
> return strcmp(str1, str2);
> }
>
>-/* disable commands that aren't safe for fsfreeze */
>-static void ga_disable_not_allowed_freeze(const QmpCommand *cmd, void *opaque)
>+static bool ga_command_is_allowed(const QmpCommand *cmd, GAState *state)
> {
>- bool allowed = false;
> int i = 0;
>+ GAConfig *config = state->config;
> const char *name = qmp_command_name(cmd);
>+ /* Fallback policy is allow everything */
>+ bool allowed = true;
>
>- while (ga_freeze_allowlist[i] != NULL) {
>- if (strcmp(name, ga_freeze_allowlist[i]) == 0) {
>+ if (config->allowedrpcs) {
>+ /*
>+ * If an allow-list is given, this changes the fallback
>+ * policy to deny everything
>+ */
>+ allowed = false;
>+
>+ if (g_list_find_custom(config->allowedrpcs, name, ga_strcmp) != NULL) {
> allowed = true;
> }
>- i++;
> }
>- if (!allowed) {
>- g_debug("disabling command: %s", name);
>- qmp_disable_command(&ga_commands, name, "the agent is in frozen state");
>- }
>-}
>
>-/* [re-]enable all commands, except those explicitly blocked by user */
>-static void ga_enable_non_blocked(const QmpCommand *cmd, void *opaque)
>-{
>- GAState *s = opaque;
>- GList *blockedrpcs = s->blockedrpcs;
>- GList *allowedrpcs = s->allowedrpcs;
>- const char *name = qmp_command_name(cmd);
>-
>- if (g_list_find_custom(blockedrpcs, name, ga_strcmp) == NULL) {
>- if (qmp_command_is_enabled(cmd)) {
>- return;
>+ /*
>+ * If both allowedrpcs and blockedrpcs are set, the blocked
>+ * list will take priority
>+ */
>+ if (config->blockedrpcs) {
>+ if (g_list_find_custom(config->blockedrpcs, name, ga_strcmp) != NULL) {
>+ allowed = false;
> }
>+ }
>
>- if (allowedrpcs &&
>- g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) {
>- return;
>- }
>+ /*
>+ * If frozen, this filtering must take priority over
>+ * absolutely everything
>+ */
>+ if (state->frozen) {
>+ allowed = false;
>
>- g_debug("enabling command: %s", name);
>- qmp_enable_command(&ga_commands, name);
>+ while (ga_freeze_allowlist[i] != NULL) {
>+ if (strcmp(name, ga_freeze_allowlist[i]) == 0) {
>+ allowed = true;
>+ }
>+ i++;
>+ }
> }
>+
>+ return allowed;
> }
IUUC, we can check by priority here: first check if (state->frozen),
then blockedrpcs, then allowedrpcs and then return a default fallback
value allowed = config->blockedrpcs != NULL && config->allowedrpcs !=
NULL
This way the function will sort of document what is written on the docs
as well.
>
>-/* disable commands that aren't allowed */
>-static void ga_disable_not_allowed(const QmpCommand *cmd, void *opaque)
>+static void ga_apply_command_filters_iter(const QmpCommand *cmd, void *opaque)
> {
>- GList *allowedrpcs = opaque;
>+ GAState *state = opaque;
>+ bool want = ga_command_is_allowed(cmd, state);
>+ bool have = qmp_command_is_enabled(cmd);
> const char *name = qmp_command_name(cmd);
>
>- if (g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) {
>+ if (want == have) {
>+ return;
>+ }
>+
>+ if (qmp_command_is_enabled(cmd)) {
Nit:
if (have) {
Since it's already declared.
> g_debug("disabling command: %s", name);
> qmp_disable_command(&ga_commands, name, "the command is not allowed");
>+ } else {
>+ g_debug("enabling command: %s", name);
>+ qmp_enable_command(&ga_commands, name);
> }
> }
>
>+static void ga_apply_command_filters(GAState *state)
Nit: inline?
>+{
>+ qmp_for_each_command(&ga_commands, ga_apply_command_filters_iter, state);
>+}
>+
> static bool ga_create_file(const char *path)
> {
> int fd = open(path, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR);
>@@ -505,15 +524,14 @@ void ga_set_frozen(GAState *s)
> if (ga_is_frozen(s)) {
> return;
> }
>- /* disable all forbidden (for frozen state) commands */
>- qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL);
> g_warning("disabling logging due to filesystem freeze");
>- ga_disable_logging(s);
> s->frozen = true;
> if (!ga_create_file(s->state_filepath_isfrozen)) {
> g_warning("unable to create %s, fsfreeze may not function properly",
> s->state_filepath_isfrozen);
> }
>+ ga_apply_command_filters(s);
>+ ga_disable_logging(s);
> }
>
> void ga_unset_frozen(GAState *s)
>@@ -545,12 +563,12 @@ void ga_unset_frozen(GAState *s)
> }
>
> /* enable all disabled, non-blocked and allowed commands */
>- qmp_for_each_command(&ga_commands, ga_enable_non_blocked, s);
> s->frozen = false;
> if (!ga_delete_file(s->state_filepath_isfrozen)) {
> g_warning("unable to delete %s, fsfreeze may not function properly",
> s->state_filepath_isfrozen);
> }
>+ ga_apply_command_filters(s);
> }
>
> #ifdef CONFIG_FSFREEZE
>@@ -1082,13 +1100,6 @@ static void config_load(GAConfig *config, const char *confpath, bool required)
> split_list(config->aliststr, ","));
> }
>
>- if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL) &&
>- g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) {
>- g_critical("wrong config, using 'block-rpcs' and 'allow-rpcs' keys at"
>- " the same time is not allowed");
>- exit(EXIT_FAILURE);
>- }
>-
> end:
> g_key_file_free(keyfile);
> if (gerr && (required ||
>@@ -1168,7 +1179,6 @@ static void config_parse(GAConfig *config, int argc, char **argv)
> {
> const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr";
> int opt_ind = 0, ch;
>- bool block_rpcs = false, allow_rpcs = false;
> const struct option lopt[] = {
> { "help", 0, NULL, 'h' },
> { "version", 0, NULL, 'V' },
>@@ -1264,7 +1274,6 @@ static void config_parse(GAConfig *config, int argc, char **argv)
> }
> config->blockedrpcs = g_list_concat(config->blockedrpcs,
> split_list(optarg, ","));
>- block_rpcs = true;
> break;
> }
> case 'a': {
>@@ -1274,7 +1283,6 @@ static void config_parse(GAConfig *config, int argc, char **argv)
> }
> config->allowedrpcs = g_list_concat(config->allowedrpcs,
> split_list(optarg, ","));
>- allow_rpcs = true;
> break;
> }
> #ifdef _WIN32
>@@ -1315,12 +1323,6 @@ static void config_parse(GAConfig *config, int argc, char **argv)
> exit(EXIT_FAILURE);
> }
> }
>-
>- if (block_rpcs && allow_rpcs) {
>- g_critical("wrong commandline, using --block-rpcs and --allow-rpcs at the"
>- " same time is not allowed");
>- exit(EXIT_FAILURE);
>- }
> }
>
> static void config_free(GAConfig *config)
>@@ -1431,7 +1433,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
> s->deferred_options.log_filepath = config->log_filepath;
> }
> ga_disable_logging(s);
>- qmp_for_each_command(&ga_commands, ga_disable_not_allowed_freeze, NULL);
> } else {
> if (config->daemonize) {
> become_daemon(config->pid_filepath);
>@@ -1455,25 +1456,6 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
> return NULL;
> }
>
>- if (config->allowedrpcs) {
>- qmp_for_each_command(&ga_commands, ga_disable_not_allowed, config->allowedrpcs);
>- s->allowedrpcs = config->allowedrpcs;
>- }
>-
>- /*
>- * Some commands can be blocked due to system limitation.
>- * Initialize blockedrpcs list even if allowedrpcs specified.
>- */
>- config->blockedrpcs = ga_command_init_blockedrpcs(config->blockedrpcs);
>- if (config->blockedrpcs) {
>- GList *l = config->blockedrpcs;
>- s->blockedrpcs = config->blockedrpcs;
>- do {
>- g_debug("disabling command: %s", (char *)l->data);
>- qmp_disable_command(&ga_commands, l->data, NULL);
>- l = g_list_next(l);
>- } while (l);
>- }
> s->command_state = ga_command_state_new();
> ga_command_state_init(s, s->command_state);
> ga_command_state_init_all(s->command_state);
>@@ -1499,6 +1481,8 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
> }
> #endif
>
>+ ga_apply_command_filters(s);
>+
> ga_state = s;
> return s;
> }
>--
>2.45.1
>
>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 18/22] qga: don't disable fsfreeze commands if vss_init fails
2024-06-13 15:44 ` [PATCH v2 18/22] qga: don't disable fsfreeze commands if vss_init fails Daniel P. Berrangé
@ 2024-07-03 10:21 ` Manos Pitsidianakis
2024-07-12 12:45 ` Daniel P. Berrangé
0 siblings, 1 reply; 70+ messages in thread
From: Manos Pitsidianakis @ 2024-07-03 10:21 UTC (permalink / raw)
To: qemu-devel, Daniel P. Berrangé
Cc: Marc-André Lureau, Philippe Mathieu-Daudé ,
Konstantin Kostiuk, Daniel P. Berrangé , Thomas Huth,
Michael Roth, Paolo Bonzini
On Thu, 13 Jun 2024 18:44, "Daniel P. Berrangé" <berrange@redhat.com> wrote:
>The fsfreeze commands are already written to report an error if
>vss_init() fails. Reporting a more specific error message is more
>helpful than a generic "command is disabled" message, which cannot
>beteween an admin config decision and lack of platform support.
s/beteween/between
>
>Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>---
> qga/commands-win32.c | 18 +++---------------
> qga/main.c | 4 ++++
> 2 files changed, 7 insertions(+), 15 deletions(-)
>
>diff --git a/qga/commands-win32.c b/qga/commands-win32.c
>index 2533e4c748..5866cc2e3c 100644
>--- a/qga/commands-win32.c
>+++ b/qga/commands-win32.c
>@@ -1203,7 +1203,7 @@ GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
> GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
> {
> if (!vss_initialized()) {
>- error_setg(errp, QERR_UNSUPPORTED);
>+ error_setg(errp, "fsfreeze not possible as VSS failed to initialize");
> return 0;
Should this be return -1 by the way?
> }
>
>@@ -1231,7 +1231,7 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
> Error *local_err = NULL;
>
> if (!vss_initialized()) {
>- error_setg(errp, QERR_UNSUPPORTED);
>+ error_setg(errp, "fsfreeze not possible as VSS failed to initialize");
> return 0;
> }
>
>@@ -1266,7 +1266,7 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp)
> int i;
>
> if (!vss_initialized()) {
>- error_setg(errp, QERR_UNSUPPORTED);
>+ error_setg(errp, "fsfreeze not possible as VSS failed to initialize");
> return 0;
> }
>
>@@ -1961,18 +1961,6 @@ done:
> /* add unsupported commands to the list of blocked RPCs */
> GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
> {
>- if (!vss_init(true)) {
>- g_debug("vss_init failed, vss commands are going to be disabled");
>- const char *list[] = {
>- "guest-get-fsinfo", "guest-fsfreeze-status",
>- "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL};
>- char **p = (char **)list;
>-
>- while (*p) {
>- blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
>- }
>- }
>-
> return blockedrpcs;
> }
>
>diff --git a/qga/main.c b/qga/main.c
>index f4d5f15bb3..17b6ce18ac 100644
>--- a/qga/main.c
>+++ b/qga/main.c
>@@ -1395,6 +1395,10 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation)
> " '%s': %s", config->state_dir, strerror(errno));
> return NULL;
> }
>+
>+ if (!vss_init(true)) {
>+ g_debug("vss_init failed, vss commands will not function");
>+ }
> #endif
>
> if (ga_is_frozen(s)) {
>--
>2.45.1
>
>
Otherwise LGTM:
Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands
2024-07-03 10:01 ` Manos Pitsidianakis
@ 2024-07-03 12:09 ` Philippe Mathieu-Daudé
2024-07-12 13:01 ` Daniel P. Berrangé
1 sibling, 0 replies; 70+ messages in thread
From: Philippe Mathieu-Daudé @ 2024-07-03 12:09 UTC (permalink / raw)
To: Manos Pitsidianakis, qemu-devel, Daniel P. Berrangé
Cc: Marc-Andr é Lureau, Konstantin Kostiuk, Thomas Huth,
Michael Roth, Paolo Bonzini
On 3/7/24 12:01, Manos Pitsidianakis wrote:
> Hello Daniel,
>
> This cleanup seems like a good idea,
>
> On Thu, 13 Jun 2024 18:44, "Daniel P. Berrangé" <berrange@redhat.com>
> wrote:
>> It is confusing having many different pieces of code enabling and
>> disabling commands, and it is not clear that they all have the same
>> semantics, especially wrt prioritization of the block/allow lists.
>> The code attempted to prevent the user from setting both the block
>> and allow lists concurrently, however, the logic was flawed as it
>> checked settings in the configuration file separately from the
>> command line arguments. Thus it was possible to set a block list
>> in the config file and an allow list via a command line argument.
>> The --dump-conf option also creates a configuration file with both
>> keys present, even if unset, which means it is creating a config
>> that cannot actually be loaded again.
>>
>> Centralizing the code in a single method "ga_apply_command_filters"
>> will provide a strong guarantee of consistency and clarify the
>> intended behaviour. With this there is no compelling technical
>> reason to prevent concurrent setting of both the allow and block
>> lists, so this flawed restriction is removed.
>>
>> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
>> ---
>> docs/interop/qemu-ga.rst | 14 +++++
>> qga/commands-posix.c | 6 --
>> qga/commands-win32.c | 6 --
>> qga/main.c | 128 +++++++++++++++++----------------------
>> 4 files changed, 70 insertions(+), 84 deletions(-)
>> +static void ga_apply_command_filters(GAState *state)
>
> Nit: inline?
No, consensus is today's compilers are smart enough to
notice inlining, developers shouldn't worry about this
anymore.
>> +{
>> + qmp_for_each_command(&ga_commands, ga_apply_command_filters_iter,
>> state);
>> +}
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 04/22] qga: move linux fs/disk command impls to commands-linux.c
2024-06-13 15:01 ` [PATCH v2 04/22] qga: move linux fs/disk " Daniel P. Berrangé
2024-07-03 8:46 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:29 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:29 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Michael Roth,
Philippe Mathieu-Daudé, Thomas Huth, Paolo Bonzini,
Manos Pitsidianakis
[-- Attachment #1: Type: text/plain, Size: 65083 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:02 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> The qmp_guest_{fstrim, get_fsinfo, get_disks} command impls in
> commands-posix.c are surrounded by '#ifdef __linux__' so should
> instead live in commands-linux.c
>
> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 904 ++++++++++++++++++++++++++++++++++++++++++
> qga/commands-posix.c | 909 -------------------------------------------
> 2 files changed, 904 insertions(+), 909 deletions(-)
>
> diff --git a/qga/commands-linux.c b/qga/commands-linux.c
> index 3fabf54882..084e6c9e85 100644
> --- a/qga/commands-linux.c
> +++ b/qga/commands-linux.c
> @@ -14,10 +14,21 @@
> #include "qemu/osdep.h"
> #include "qapi/error.h"
> #include "qga-qapi-commands.h"
> +#include "qapi/error.h"
> +#include "qapi/qmp/qerror.h"
> #include "commands-common.h"
> #include "cutils.h"
> #include <mntent.h>
> #include <sys/ioctl.h>
> +#include <mntent.h>
> +#include <linux/nvme_ioctl.h>
> +#include "block/nvme.h"
> +
> +#ifdef CONFIG_LIBUDEV
> +#include <libudev.h>
> +#endif
> +
> +#include <sys/statvfs.h>
>
> #if defined(CONFIG_FSFREEZE) || defined(CONFIG_FSTRIM)
> static int dev_major_minor(const char *devpath,
> @@ -286,6 +297,899 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp)
> }
> #endif /* CONFIG_FSFREEZE */
>
> +#if defined(CONFIG_FSFREEZE)
> +
> +static char *get_pci_driver(char const *syspath, int pathlen, Error
> **errp)
> +{
> + char *path;
> + char *dpath;
> + char *driver = NULL;
> + char buf[PATH_MAX];
> + ssize_t len;
> +
> + path = g_strndup(syspath, pathlen);
> + dpath = g_strdup_printf("%s/driver", path);
> + len = readlink(dpath, buf, sizeof(buf) - 1);
> + if (len != -1) {
> + buf[len] = 0;
> + driver = g_path_get_basename(buf);
> + }
> + g_free(dpath);
> + g_free(path);
> + return driver;
> +}
> +
> +static int compare_uint(const void *_a, const void *_b)
> +{
> + unsigned int a = *(unsigned int *)_a;
> + unsigned int b = *(unsigned int *)_b;
> +
> + return a < b ? -1 : a > b ? 1 : 0;
> +}
> +
> +/* Walk the specified sysfs and build a sorted list of host or ata
> numbers */
> +static int build_hosts(char const *syspath, char const *host, bool ata,
> + unsigned int *hosts, int hosts_max, Error **errp)
> +{
> + char *path;
> + DIR *dir;
> + struct dirent *entry;
> + int i = 0;
> +
> + path = g_strndup(syspath, host - syspath);
> + dir = opendir(path);
> + if (!dir) {
> + error_setg_errno(errp, errno, "opendir(\"%s\")", path);
> + g_free(path);
> + return -1;
> + }
> +
> + while (i < hosts_max) {
> + entry = readdir(dir);
> + if (!entry) {
> + break;
> + }
> + if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
> + ++i;
> + } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) ==
> 1) {
> + ++i;
> + }
> + }
> +
> + qsort(hosts, i, sizeof(hosts[0]), compare_uint);
> +
> + g_free(path);
> + closedir(dir);
> + return i;
> +}
> +
> +/*
> + * Store disk device info for devices on the PCI bus.
> + * Returns true if information has been stored, or false for failure.
> + */
> +static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
> + GuestDiskAddress *disk,
> + Error **errp)
> +{
> + unsigned int pci[4], host, hosts[8], tgt[3];
> + int i, nhosts = 0, pcilen;
> + GuestPCIAddress *pciaddr = disk->pci_controller;
> + bool has_ata = false, has_host = false, has_tgt = false;
> + char *p, *q, *driver = NULL;
> + bool ret = false;
> +
> + p = strstr(syspath, "/devices/pci");
> + if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
> + pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
> + g_debug("only pci device is supported: sysfs path '%s'", syspath);
> + return false;
> + }
> +
> + p += 12 + pcilen;
> + while (true) {
> + driver = get_pci_driver(syspath, p - syspath, errp);
> + if (driver && (g_str_equal(driver, "ata_piix") ||
> + g_str_equal(driver, "sym53c8xx") ||
> + g_str_equal(driver, "virtio-pci") ||
> + g_str_equal(driver, "ahci") ||
> + g_str_equal(driver, "nvme") ||
> + g_str_equal(driver, "xhci_hcd") ||
> + g_str_equal(driver, "ehci-pci"))) {
> + break;
> + }
> +
> + g_free(driver);
> + if (sscanf(p, "/%x:%x:%x.%x%n",
> + pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) {
> + p += pcilen;
> + continue;
> + }
> +
> + g_debug("unsupported driver or sysfs path '%s'", syspath);
> + return false;
> + }
> +
> + p = strstr(syspath, "/target");
> + if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
> + tgt, tgt + 1, tgt + 2) == 3) {
> + has_tgt = true;
> + }
> +
> + p = strstr(syspath, "/ata");
> + if (p) {
> + q = p + 4;
> + has_ata = true;
> + } else {
> + p = strstr(syspath, "/host");
> + q = p + 5;
> + }
> + if (p && sscanf(q, "%u", &host) == 1) {
> + has_host = true;
> + nhosts = build_hosts(syspath, p, has_ata, hosts,
> + ARRAY_SIZE(hosts), errp);
> + if (nhosts < 0) {
> + goto cleanup;
> + }
> + }
> +
> + pciaddr->domain = pci[0];
> + pciaddr->bus = pci[1];
> + pciaddr->slot = pci[2];
> + pciaddr->function = pci[3];
> +
> + if (strcmp(driver, "ata_piix") == 0) {
> + /* a host per ide bus, target*:0:<unit>:0 */
> + if (!has_host || !has_tgt) {
> + g_debug("invalid sysfs path '%s' (driver '%s')", syspath,
> driver);
> + goto cleanup;
> + }
> + for (i = 0; i < nhosts; i++) {
> + if (host == hosts[i]) {
> + disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
> + disk->bus = i;
> + disk->unit = tgt[1];
> + break;
> + }
> + }
> + if (i >= nhosts) {
> + g_debug("no host for '%s' (driver '%s')", syspath, driver);
> + goto cleanup;
> + }
> + } else if (strcmp(driver, "sym53c8xx") == 0) {
> + /* scsi(LSI Logic): target*:0:<unit>:0 */
> + if (!has_tgt) {
> + g_debug("invalid sysfs path '%s' (driver '%s')", syspath,
> driver);
> + goto cleanup;
> + }
> + disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
> + disk->unit = tgt[1];
> + } else if (strcmp(driver, "virtio-pci") == 0) {
> + if (has_tgt) {
> + /* virtio-scsi: target*:0:0:<unit> */
> + disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
> + disk->unit = tgt[2];
> + } else {
> + /* virtio-blk: 1 disk per 1 device */
> + disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
> + }
> + } else if (strcmp(driver, "ahci") == 0) {
> + /* ahci: 1 host per 1 unit */
> + if (!has_host || !has_tgt) {
> + g_debug("invalid sysfs path '%s' (driver '%s')", syspath,
> driver);
> + goto cleanup;
> + }
> + for (i = 0; i < nhosts; i++) {
> + if (host == hosts[i]) {
> + disk->unit = i;
> + disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
> + break;
> + }
> + }
> + if (i >= nhosts) {
> + g_debug("no host for '%s' (driver '%s')", syspath, driver);
> + goto cleanup;
> + }
> + } else if (strcmp(driver, "nvme") == 0) {
> + disk->bus_type = GUEST_DISK_BUS_TYPE_NVME;
> + } else if (strcmp(driver, "ehci-pci") == 0 || strcmp(driver,
> "xhci_hcd") == 0) {
> + disk->bus_type = GUEST_DISK_BUS_TYPE_USB;
> + } else {
> + g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
> + goto cleanup;
> + }
> +
> + ret = true;
> +
> +cleanup:
> + g_free(driver);
> + return ret;
> +}
> +
> +/*
> + * Store disk device info for non-PCI virtio devices (for example s390x
> + * channel I/O devices). Returns true if information has been stored, or
> + * false for failure.
> + */
> +static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath,
> + GuestDiskAddress *disk,
> + Error **errp)
> +{
> + unsigned int tgt[3];
> + char *p;
> +
> + if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) {
> + g_debug("Unsupported virtio device '%s'", syspath);
> + return false;
> + }
> +
> + p = strstr(syspath, "/target");
> + if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
> + &tgt[0], &tgt[1], &tgt[2]) == 3) {
> + /* virtio-scsi: target*:0:<target>:<unit> */
> + disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
> + disk->bus = tgt[0];
> + disk->target = tgt[1];
> + disk->unit = tgt[2];
> + } else {
> + /* virtio-blk: 1 disk per 1 device */
> + disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
> + }
> +
> + return true;
> +}
> +
> +/*
> + * Store disk device info for CCW devices (s390x channel I/O devices).
> + * Returns true if information has been stored, or false for failure.
> + */
> +static bool build_guest_fsinfo_for_ccw_dev(char const *syspath,
> + GuestDiskAddress *disk,
> + Error **errp)
> +{
> + unsigned int cssid, ssid, subchno, devno;
> + char *p;
> +
> + p = strstr(syspath, "/devices/css");
> + if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/",
> + &cssid, &ssid, &subchno, &devno) < 4) {
> + g_debug("could not parse ccw device sysfs path: %s", syspath);
> + return false;
> + }
> +
> + disk->ccw_address = g_new0(GuestCCWAddress, 1);
> + disk->ccw_address->cssid = cssid;
> + disk->ccw_address->ssid = ssid;
> + disk->ccw_address->subchno = subchno;
> + disk->ccw_address->devno = devno;
> +
> + if (strstr(p, "/virtio")) {
> + build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
> + }
> +
> + return true;
> +}
> +
> +/* Store disk device info specified by @sysfs into @fs */
> +static void build_guest_fsinfo_for_real_device(char const *syspath,
> + GuestFilesystemInfo *fs,
> + Error **errp)
> +{
> + GuestDiskAddress *disk;
> + GuestPCIAddress *pciaddr;
> + bool has_hwinf;
> +#ifdef CONFIG_LIBUDEV
> + struct udev *udev = NULL;
> + struct udev_device *udevice = NULL;
> +#endif
> +
> + pciaddr = g_new0(GuestPCIAddress, 1);
> + pciaddr->domain = -1; /* -1 means field is
> invalid */
> + pciaddr->bus = -1;
> + pciaddr->slot = -1;
> + pciaddr->function = -1;
> +
> + disk = g_new0(GuestDiskAddress, 1);
> + disk->pci_controller = pciaddr;
> + disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN;
> +
> +#ifdef CONFIG_LIBUDEV
> + udev = udev_new();
> + udevice = udev_device_new_from_syspath(udev, syspath);
> + if (udev == NULL || udevice == NULL) {
> + g_debug("failed to query udev");
> + } else {
> + const char *devnode, *serial;
> + devnode = udev_device_get_devnode(udevice);
> + if (devnode != NULL) {
> + disk->dev = g_strdup(devnode);
> + }
> + serial = udev_device_get_property_value(udevice, "ID_SERIAL");
> + if (serial != NULL && *serial != 0) {
> + disk->serial = g_strdup(serial);
> + }
> + }
> +
> + udev_unref(udev);
> + udev_device_unref(udevice);
> +#endif
> +
> + if (strstr(syspath, "/devices/pci")) {
> + has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp);
> + } else if (strstr(syspath, "/devices/css")) {
> + has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp);
> + } else if (strstr(syspath, "/virtio")) {
> + has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk,
> errp);
> + } else {
> + g_debug("Unsupported device type for '%s'", syspath);
> + has_hwinf = false;
> + }
> +
> + if (has_hwinf || disk->dev || disk->serial) {
> + QAPI_LIST_PREPEND(fs->disk, disk);
> + } else {
> + qapi_free_GuestDiskAddress(disk);
> + }
> +}
> +
> +static void build_guest_fsinfo_for_device(char const *devpath,
> + GuestFilesystemInfo *fs,
> + Error **errp);
> +
> +/* Store a list of slave devices of virtual volume specified by @syspath
> into
> + * @fs */
> +static void build_guest_fsinfo_for_virtual_device(char const *syspath,
> + GuestFilesystemInfo *fs,
> + Error **errp)
> +{
> + Error *err = NULL;
> + DIR *dir;
> + char *dirpath;
> + struct dirent *entry;
> +
> + dirpath = g_strdup_printf("%s/slaves", syspath);
> + dir = opendir(dirpath);
> + if (!dir) {
> + if (errno != ENOENT) {
> + error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
> + }
> + g_free(dirpath);
> + return;
> + }
> +
> + for (;;) {
> + errno = 0;
> + entry = readdir(dir);
> + if (entry == NULL) {
> + if (errno) {
> + error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath);
> + }
> + break;
> + }
> +
> + if (entry->d_type == DT_LNK) {
> + char *path;
> +
> + g_debug(" slave device '%s'", entry->d_name);
> + path = g_strdup_printf("%s/slaves/%s", syspath,
> entry->d_name);
> + build_guest_fsinfo_for_device(path, fs, &err);
> + g_free(path);
> +
> + if (err) {
> + error_propagate(errp, err);
> + break;
> + }
> + }
> + }
> +
> + g_free(dirpath);
> + closedir(dir);
> +}
> +
> +static bool is_disk_virtual(const char *devpath, Error **errp)
> +{
> + g_autofree char *syspath = realpath(devpath, NULL);
> +
> + if (!syspath) {
> + error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
> + return false;
> + }
> + return strstr(syspath, "/devices/virtual/block/") != NULL;
> +}
> +
> +/* Dispatch to functions for virtual/real device */
> +static void build_guest_fsinfo_for_device(char const *devpath,
> + GuestFilesystemInfo *fs,
> + Error **errp)
> +{
> + ERRP_GUARD();
> + g_autofree char *syspath = NULL;
> + bool is_virtual = false;
> +
> + syspath = realpath(devpath, NULL);
> + if (!syspath) {
> + if (errno != ENOENT) {
> + error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
> + return;
> + }
> +
> + /* ENOENT: This devpath may not exist because of container config
> */
> + if (!fs->name) {
> + fs->name = g_path_get_basename(devpath);
> + }
> + return;
> + }
> +
> + if (!fs->name) {
> + fs->name = g_path_get_basename(syspath);
> + }
> +
> + g_debug(" parse sysfs path '%s'", syspath);
> + is_virtual = is_disk_virtual(syspath, errp);
> + if (*errp != NULL) {
> + return;
> + }
> + if (is_virtual) {
> + build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
> + } else {
> + build_guest_fsinfo_for_real_device(syspath, fs, errp);
> + }
> +}
> +
> +#ifdef CONFIG_LIBUDEV
> +
> +/*
> + * Wrapper around build_guest_fsinfo_for_device() for getting just
> + * the disk address.
> + */
> +static GuestDiskAddress *get_disk_address(const char *syspath, Error
> **errp)
> +{
> + g_autoptr(GuestFilesystemInfo) fs = NULL;
> +
> + fs = g_new0(GuestFilesystemInfo, 1);
> + build_guest_fsinfo_for_device(syspath, fs, errp);
> + if (fs->disk != NULL) {
> + return g_steal_pointer(&fs->disk->value);
> + }
> + return NULL;
> +}
> +
> +static char *get_alias_for_syspath(const char *syspath)
> +{
> + struct udev *udev = NULL;
> + struct udev_device *udevice = NULL;
> + char *ret = NULL;
> +
> + udev = udev_new();
> + if (udev == NULL) {
> + g_debug("failed to query udev");
> + goto out;
> + }
> + udevice = udev_device_new_from_syspath(udev, syspath);
> + if (udevice == NULL) {
> + g_debug("failed to query udev for path: %s", syspath);
> + goto out;
> + } else {
> + const char *alias = udev_device_get_property_value(
> + udevice, "DM_NAME");
> + /*
> + * NULL means there was an error and empty string means there is
> no
> + * alias. In case of no alias we return NULL instead of empty
> string.
> + */
> + if (alias == NULL) {
> + g_debug("failed to query udev for device alias for: %s",
> + syspath);
> + } else if (*alias != 0) {
> + ret = g_strdup(alias);
> + }
> + }
> +
> +out:
> + udev_unref(udev);
> + udev_device_unref(udevice);
> + return ret;
> +}
> +
> +static char *get_device_for_syspath(const char *syspath)
> +{
> + struct udev *udev = NULL;
> + struct udev_device *udevice = NULL;
> + char *ret = NULL;
> +
> + udev = udev_new();
> + if (udev == NULL) {
> + g_debug("failed to query udev");
> + goto out;
> + }
> + udevice = udev_device_new_from_syspath(udev, syspath);
> + if (udevice == NULL) {
> + g_debug("failed to query udev for path: %s", syspath);
> + goto out;
> + } else {
> + ret = g_strdup(udev_device_get_devnode(udevice));
> + }
> +
> +out:
> + udev_unref(udev);
> + udev_device_unref(udevice);
> + return ret;
> +}
> +
> +static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk)
> +{
> + g_autofree char *deps_dir = NULL;
> + const gchar *dep;
> + GDir *dp_deps = NULL;
> +
> + /* List dependent disks */
> + deps_dir = g_strdup_printf("%s/slaves", disk_dir);
> + g_debug(" listing entries in: %s", deps_dir);
> + dp_deps = g_dir_open(deps_dir, 0, NULL);
> + if (dp_deps == NULL) {
> + g_debug("failed to list entries in %s", deps_dir);
> + return;
> + }
> + disk->has_dependencies = true;
> + while ((dep = g_dir_read_name(dp_deps)) != NULL) {
> + g_autofree char *dep_dir = NULL;
> + char *dev_name;
> +
> + /* Add dependent disks */
> + dep_dir = g_strdup_printf("%s/%s", deps_dir, dep);
> + dev_name = get_device_for_syspath(dep_dir);
> + if (dev_name != NULL) {
> + g_debug(" adding dependent device: %s", dev_name);
> + QAPI_LIST_PREPEND(disk->dependencies, dev_name);
> + }
> + }
> + g_dir_close(dp_deps);
> +}
> +
> +/*
> + * Detect partitions subdirectory, name is "<disk_name><number>" or
> + * "<disk_name>p<number>"
> + *
> + * @disk_name -- last component of /sys path (e.g. sda)
> + * @disk_dir -- sys path of the disk (e.g. /sys/block/sda)
> + * @disk_dev -- device node of the disk (e.g. /dev/sda)
> + */
> +static GuestDiskInfoList *get_disk_partitions(
> + GuestDiskInfoList *list,
> + const char *disk_name, const char *disk_dir,
> + const char *disk_dev)
> +{
> + GuestDiskInfoList *ret = list;
> + struct dirent *de_disk;
> + DIR *dp_disk = NULL;
> + size_t len = strlen(disk_name);
> +
> + dp_disk = opendir(disk_dir);
> + while ((de_disk = readdir(dp_disk)) != NULL) {
> + g_autofree char *partition_dir = NULL;
> + char *dev_name;
> + GuestDiskInfo *partition;
> +
> + if (!(de_disk->d_type & DT_DIR)) {
> + continue;
> + }
> +
> + if (!(strncmp(disk_name, de_disk->d_name, len) == 0 &&
> + ((*(de_disk->d_name + len) == 'p' &&
> + isdigit(*(de_disk->d_name + len + 1))) ||
> + isdigit(*(de_disk->d_name + len))))) {
> + continue;
> + }
> +
> + partition_dir = g_strdup_printf("%s/%s",
> + disk_dir, de_disk->d_name);
> + dev_name = get_device_for_syspath(partition_dir);
> + if (dev_name == NULL) {
> + g_debug("Failed to get device name for syspath: %s",
> + disk_dir);
> + continue;
> + }
> + partition = g_new0(GuestDiskInfo, 1);
> + partition->name = dev_name;
> + partition->partition = true;
> + partition->has_dependencies = true;
> + /* Add parent disk as dependent for easier tracking of hierarchy
> */
> + QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev));
> +
> + QAPI_LIST_PREPEND(ret, partition);
> + }
> + closedir(dp_disk);
> +
> + return ret;
> +}
> +
> +static void get_nvme_smart(GuestDiskInfo *disk)
> +{
> + int fd;
> + GuestNVMeSmart *smart;
> + NvmeSmartLog log = {0};
> + struct nvme_admin_cmd cmd = {
> + .opcode = NVME_ADM_CMD_GET_LOG_PAGE,
> + .nsid = NVME_NSID_BROADCAST,
> + .addr = (uintptr_t)&log,
> + .data_len = sizeof(log),
> + .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */
> + | (((sizeof(log) >> 2) - 1) << 16)
> + };
> +
> + fd = qga_open_cloexec(disk->name, O_RDONLY, 0);
> + if (fd == -1) {
> + g_debug("Failed to open device: %s: %s", disk->name,
> g_strerror(errno));
> + return;
> + }
> +
> + if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) {
> + g_debug("Failed to get smart: %s: %s", disk->name,
> g_strerror(errno));
> + close(fd);
> + return;
> + }
> +
> + disk->smart = g_new0(GuestDiskSmart, 1);
> + disk->smart->type = GUEST_DISK_BUS_TYPE_NVME;
> +
> + smart = &disk->smart->u.nvme;
> + smart->critical_warning = log.critical_warning;
> + smart->temperature = lduw_le_p(&log.temperature); /* unaligned field
> */
> + smart->available_spare = log.available_spare;
> + smart->available_spare_threshold = log.available_spare_threshold;
> + smart->percentage_used = log.percentage_used;
> + smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]);
> + smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]);
> + smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]);
> + smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]);
> + smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]);
> + smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]);
> + smart->host_write_commands_lo =
> le64_to_cpu(log.host_write_commands[0]);
> + smart->host_write_commands_hi =
> le64_to_cpu(log.host_write_commands[1]);
> + smart->controller_busy_time_lo =
> le64_to_cpu(log.controller_busy_time[0]);
> + smart->controller_busy_time_hi =
> le64_to_cpu(log.controller_busy_time[1]);
> + smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]);
> + smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]);
> + smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]);
> + smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]);
> + smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]);
> + smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]);
> + smart->media_errors_lo = le64_to_cpu(log.media_errors[0]);
> + smart->media_errors_hi = le64_to_cpu(log.media_errors[1]);
> + smart->number_of_error_log_entries_lo =
> + le64_to_cpu(log.number_of_error_log_entries[0]);
> + smart->number_of_error_log_entries_hi =
> + le64_to_cpu(log.number_of_error_log_entries[1]);
> +
> + close(fd);
> +}
> +
> +static void get_disk_smart(GuestDiskInfo *disk)
> +{
> + if (disk->address
> + && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) {
> + get_nvme_smart(disk);
> + }
> +}
> +
> +GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
> +{
> + GuestDiskInfoList *ret = NULL;
> + GuestDiskInfo *disk;
> + DIR *dp = NULL;
> + struct dirent *de = NULL;
> +
> + g_debug("listing /sys/block directory");
> + dp = opendir("/sys/block");
> + if (dp == NULL) {
> + error_setg_errno(errp, errno, "Can't open directory
> \"/sys/block\"");
> + return NULL;
> + }
> + while ((de = readdir(dp)) != NULL) {
> + g_autofree char *disk_dir = NULL, *line = NULL,
> + *size_path = NULL;
> + char *dev_name;
> + Error *local_err = NULL;
> + if (de->d_type != DT_LNK) {
> + g_debug(" skipping entry: %s", de->d_name);
> + continue;
> + }
> +
> + /* Check size and skip zero-sized disks */
> + g_debug(" checking disk size");
> + size_path = g_strdup_printf("/sys/block/%s/size", de->d_name);
> + if (!g_file_get_contents(size_path, &line, NULL, NULL)) {
> + g_debug(" failed to read disk size");
> + continue;
> + }
> + if (g_strcmp0(line, "0\n") == 0) {
> + g_debug(" skipping zero-sized disk");
> + continue;
> + }
> +
> + g_debug(" adding %s", de->d_name);
> + disk_dir = g_strdup_printf("/sys/block/%s", de->d_name);
> + dev_name = get_device_for_syspath(disk_dir);
> + if (dev_name == NULL) {
> + g_debug("Failed to get device name for syspath: %s",
> + disk_dir);
> + continue;
> + }
> + disk = g_new0(GuestDiskInfo, 1);
> + disk->name = dev_name;
> + disk->partition = false;
> + disk->alias = get_alias_for_syspath(disk_dir);
> + QAPI_LIST_PREPEND(ret, disk);
> +
> + /* Get address for non-virtual devices */
> + bool is_virtual = is_disk_virtual(disk_dir, &local_err);
> + if (local_err != NULL) {
> + g_debug(" failed to check disk path, ignoring error: %s",
> + error_get_pretty(local_err));
> + error_free(local_err);
> + local_err = NULL;
> + /* Don't try to get the address */
> + is_virtual = true;
> + }
> + if (!is_virtual) {
> + disk->address = get_disk_address(disk_dir, &local_err);
> + if (local_err != NULL) {
> + g_debug(" failed to get device info, ignoring error: %s",
> + error_get_pretty(local_err));
> + error_free(local_err);
> + local_err = NULL;
> + }
> + }
> +
> + get_disk_deps(disk_dir, disk);
> + get_disk_smart(disk);
> + ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
> + }
> +
> + closedir(dp);
> +
> + return ret;
> +}
> +
> +#else
> +
> +GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
> +{
> + error_setg(errp, QERR_UNSUPPORTED);
> + return NULL;
> +}
> +
> +#endif
> +
> +/* Return a list of the disk device(s)' info which @mount lies on */
> +static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
> + Error **errp)
> +{
> + GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
> + struct statvfs buf;
> + unsigned long used, nonroot_total, fr_size;
> + char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
> + mount->devmajor, mount->devminor);
> +
> + fs->mountpoint = g_strdup(mount->dirname);
> + fs->type = g_strdup(mount->devtype);
> + build_guest_fsinfo_for_device(devpath, fs, errp);
> +
> + if (statvfs(fs->mountpoint, &buf) == 0) {
> + fr_size = buf.f_frsize;
> + used = buf.f_blocks - buf.f_bfree;
> + nonroot_total = used + buf.f_bavail;
> + fs->used_bytes = used * fr_size;
> + fs->total_bytes = nonroot_total * fr_size;
> + fs->total_bytes_privileged = buf.f_blocks * fr_size;
> +
> + fs->has_total_bytes = true;
> + fs->has_total_bytes_privileged = true;
> + fs->has_used_bytes = true;
> + }
> +
> + g_free(devpath);
> +
> + return fs;
> +}
> +
> +GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
> +{
> + FsMountList mounts;
> + struct FsMount *mount;
> + GuestFilesystemInfoList *ret = NULL;
> + Error *local_err = NULL;
> +
> + QTAILQ_INIT(&mounts);
> + if (!build_fs_mount_list(&mounts, &local_err)) {
> + error_propagate(errp, local_err);
> + return NULL;
> + }
> +
> + QTAILQ_FOREACH(mount, &mounts, next) {
> + g_debug("Building guest fsinfo for '%s'", mount->dirname);
> +
> + QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err));
> + if (local_err) {
> + error_propagate(errp, local_err);
> + qapi_free_GuestFilesystemInfoList(ret);
> + ret = NULL;
> + break;
> + }
> + }
> +
> + free_fs_mount_list(&mounts);
> + return ret;
> +}
> +#endif /* CONFIG_FSFREEZE */
> +
> +#if defined(CONFIG_FSTRIM)
> +/*
> + * Walk list of mounted file systems in the guest, and trim them.
> + */
> +GuestFilesystemTrimResponse *
> +qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
> +{
> + GuestFilesystemTrimResponse *response;
> + GuestFilesystemTrimResult *result;
> + int ret = 0;
> + FsMountList mounts;
> + struct FsMount *mount;
> + int fd;
> + struct fstrim_range r;
> +
> + slog("guest-fstrim called");
> +
> + QTAILQ_INIT(&mounts);
> + if (!build_fs_mount_list(&mounts, errp)) {
> + return NULL;
> + }
> +
> + response = g_malloc0(sizeof(*response));
> +
> + QTAILQ_FOREACH(mount, &mounts, next) {
> + result = g_malloc0(sizeof(*result));
> + result->path = g_strdup(mount->dirname);
> +
> + QAPI_LIST_PREPEND(response->paths, result);
> +
> + fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0);
> + if (fd == -1) {
> + result->error = g_strdup_printf("failed to open: %s",
> + strerror(errno));
> + continue;
> + }
> +
> + /* We try to cull filesystems we know won't work in advance, but
> other
> + * filesystems may not implement fstrim for less obvious reasons.
> + * These will report EOPNOTSUPP; while in some other cases ENOTTY
> + * will be reported (e.g. CD-ROMs).
> + * Any other error means an unexpected error.
> + */
> + r.start = 0;
> + r.len = -1;
> + r.minlen = has_minimum ? minimum : 0;
> + ret = ioctl(fd, FITRIM, &r);
> + if (ret == -1) {
> + if (errno == ENOTTY || errno == EOPNOTSUPP) {
> + result->error = g_strdup("trim not supported");
> + } else {
> + result->error = g_strdup_printf("failed to trim: %s",
> + strerror(errno));
> + }
> + close(fd);
> + continue;
> + }
> +
> + result->has_minimum = true;
> + result->minimum = r.minlen;
> + result->has_trimmed = true;
> + result->trimmed = r.len;
> + close(fd);
> + }
> +
> + free_fs_mount_list(&mounts);
> + return response;
> +}
> +#endif /* CONFIG_FSTRIM */
>
> #define LINUX_SYS_STATE_FILE "/sys/power/state"
> #define SUSPEND_SUPPORTED 0
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index ef21da63be..98aafc45f3 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -24,23 +24,12 @@
> #include "qemu/base64.h"
> #include "qemu/cutils.h"
> #include "commands-common.h"
> -#include "block/nvme.h"
> #include "cutils.h"
>
> #ifdef HAVE_UTMPX
> #include <utmpx.h>
> #endif
>
> -#if defined(__linux__)
> -#include <mntent.h>
> -#include <sys/statvfs.h>
> -#include <linux/nvme_ioctl.h>
> -
> -#ifdef CONFIG_LIBUDEV
> -#include <libudev.h>
> -#endif
> -#endif
> -
> #ifdef HAVE_GETIFADDRS
> #include <arpa/inet.h>
> #include <sys/socket.h>
> @@ -842,904 +831,6 @@ static void guest_fsfreeze_cleanup(void)
> }
> #endif
>
> -/* linux-specific implementations. avoid this if at all possible. */
> -#if defined(__linux__)
> -#if defined(CONFIG_FSFREEZE)
> -
> -static char *get_pci_driver(char const *syspath, int pathlen, Error
> **errp)
> -{
> - char *path;
> - char *dpath;
> - char *driver = NULL;
> - char buf[PATH_MAX];
> - ssize_t len;
> -
> - path = g_strndup(syspath, pathlen);
> - dpath = g_strdup_printf("%s/driver", path);
> - len = readlink(dpath, buf, sizeof(buf) - 1);
> - if (len != -1) {
> - buf[len] = 0;
> - driver = g_path_get_basename(buf);
> - }
> - g_free(dpath);
> - g_free(path);
> - return driver;
> -}
> -
> -static int compare_uint(const void *_a, const void *_b)
> -{
> - unsigned int a = *(unsigned int *)_a;
> - unsigned int b = *(unsigned int *)_b;
> -
> - return a < b ? -1 : a > b ? 1 : 0;
> -}
> -
> -/* Walk the specified sysfs and build a sorted list of host or ata
> numbers */
> -static int build_hosts(char const *syspath, char const *host, bool ata,
> - unsigned int *hosts, int hosts_max, Error **errp)
> -{
> - char *path;
> - DIR *dir;
> - struct dirent *entry;
> - int i = 0;
> -
> - path = g_strndup(syspath, host - syspath);
> - dir = opendir(path);
> - if (!dir) {
> - error_setg_errno(errp, errno, "opendir(\"%s\")", path);
> - g_free(path);
> - return -1;
> - }
> -
> - while (i < hosts_max) {
> - entry = readdir(dir);
> - if (!entry) {
> - break;
> - }
> - if (ata && sscanf(entry->d_name, "ata%d", hosts + i) == 1) {
> - ++i;
> - } else if (!ata && sscanf(entry->d_name, "host%d", hosts + i) ==
> 1) {
> - ++i;
> - }
> - }
> -
> - qsort(hosts, i, sizeof(hosts[0]), compare_uint);
> -
> - g_free(path);
> - closedir(dir);
> - return i;
> -}
> -
> -/*
> - * Store disk device info for devices on the PCI bus.
> - * Returns true if information has been stored, or false for failure.
> - */
> -static bool build_guest_fsinfo_for_pci_dev(char const *syspath,
> - GuestDiskAddress *disk,
> - Error **errp)
> -{
> - unsigned int pci[4], host, hosts[8], tgt[3];
> - int i, nhosts = 0, pcilen;
> - GuestPCIAddress *pciaddr = disk->pci_controller;
> - bool has_ata = false, has_host = false, has_tgt = false;
> - char *p, *q, *driver = NULL;
> - bool ret = false;
> -
> - p = strstr(syspath, "/devices/pci");
> - if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n",
> - pci, pci + 1, pci + 2, pci + 3, &pcilen) < 4) {
> - g_debug("only pci device is supported: sysfs path '%s'", syspath);
> - return false;
> - }
> -
> - p += 12 + pcilen;
> - while (true) {
> - driver = get_pci_driver(syspath, p - syspath, errp);
> - if (driver && (g_str_equal(driver, "ata_piix") ||
> - g_str_equal(driver, "sym53c8xx") ||
> - g_str_equal(driver, "virtio-pci") ||
> - g_str_equal(driver, "ahci") ||
> - g_str_equal(driver, "nvme") ||
> - g_str_equal(driver, "xhci_hcd") ||
> - g_str_equal(driver, "ehci-pci"))) {
> - break;
> - }
> -
> - g_free(driver);
> - if (sscanf(p, "/%x:%x:%x.%x%n",
> - pci, pci + 1, pci + 2, pci + 3, &pcilen) == 4) {
> - p += pcilen;
> - continue;
> - }
> -
> - g_debug("unsupported driver or sysfs path '%s'", syspath);
> - return false;
> - }
> -
> - p = strstr(syspath, "/target");
> - if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
> - tgt, tgt + 1, tgt + 2) == 3) {
> - has_tgt = true;
> - }
> -
> - p = strstr(syspath, "/ata");
> - if (p) {
> - q = p + 4;
> - has_ata = true;
> - } else {
> - p = strstr(syspath, "/host");
> - q = p + 5;
> - }
> - if (p && sscanf(q, "%u", &host) == 1) {
> - has_host = true;
> - nhosts = build_hosts(syspath, p, has_ata, hosts,
> - ARRAY_SIZE(hosts), errp);
> - if (nhosts < 0) {
> - goto cleanup;
> - }
> - }
> -
> - pciaddr->domain = pci[0];
> - pciaddr->bus = pci[1];
> - pciaddr->slot = pci[2];
> - pciaddr->function = pci[3];
> -
> - if (strcmp(driver, "ata_piix") == 0) {
> - /* a host per ide bus, target*:0:<unit>:0 */
> - if (!has_host || !has_tgt) {
> - g_debug("invalid sysfs path '%s' (driver '%s')", syspath,
> driver);
> - goto cleanup;
> - }
> - for (i = 0; i < nhosts; i++) {
> - if (host == hosts[i]) {
> - disk->bus_type = GUEST_DISK_BUS_TYPE_IDE;
> - disk->bus = i;
> - disk->unit = tgt[1];
> - break;
> - }
> - }
> - if (i >= nhosts) {
> - g_debug("no host for '%s' (driver '%s')", syspath, driver);
> - goto cleanup;
> - }
> - } else if (strcmp(driver, "sym53c8xx") == 0) {
> - /* scsi(LSI Logic): target*:0:<unit>:0 */
> - if (!has_tgt) {
> - g_debug("invalid sysfs path '%s' (driver '%s')", syspath,
> driver);
> - goto cleanup;
> - }
> - disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
> - disk->unit = tgt[1];
> - } else if (strcmp(driver, "virtio-pci") == 0) {
> - if (has_tgt) {
> - /* virtio-scsi: target*:0:0:<unit> */
> - disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
> - disk->unit = tgt[2];
> - } else {
> - /* virtio-blk: 1 disk per 1 device */
> - disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
> - }
> - } else if (strcmp(driver, "ahci") == 0) {
> - /* ahci: 1 host per 1 unit */
> - if (!has_host || !has_tgt) {
> - g_debug("invalid sysfs path '%s' (driver '%s')", syspath,
> driver);
> - goto cleanup;
> - }
> - for (i = 0; i < nhosts; i++) {
> - if (host == hosts[i]) {
> - disk->unit = i;
> - disk->bus_type = GUEST_DISK_BUS_TYPE_SATA;
> - break;
> - }
> - }
> - if (i >= nhosts) {
> - g_debug("no host for '%s' (driver '%s')", syspath, driver);
> - goto cleanup;
> - }
> - } else if (strcmp(driver, "nvme") == 0) {
> - disk->bus_type = GUEST_DISK_BUS_TYPE_NVME;
> - } else if (strcmp(driver, "ehci-pci") == 0 || strcmp(driver,
> "xhci_hcd") == 0) {
> - disk->bus_type = GUEST_DISK_BUS_TYPE_USB;
> - } else {
> - g_debug("unknown driver '%s' (sysfs path '%s')", driver, syspath);
> - goto cleanup;
> - }
> -
> - ret = true;
> -
> -cleanup:
> - g_free(driver);
> - return ret;
> -}
> -
> -/*
> - * Store disk device info for non-PCI virtio devices (for example s390x
> - * channel I/O devices). Returns true if information has been stored, or
> - * false for failure.
> - */
> -static bool build_guest_fsinfo_for_nonpci_virtio(char const *syspath,
> - GuestDiskAddress *disk,
> - Error **errp)
> -{
> - unsigned int tgt[3];
> - char *p;
> -
> - if (!strstr(syspath, "/virtio") || !strstr(syspath, "/block")) {
> - g_debug("Unsupported virtio device '%s'", syspath);
> - return false;
> - }
> -
> - p = strstr(syspath, "/target");
> - if (p && sscanf(p + 7, "%*u:%*u:%*u/%*u:%u:%u:%u",
> - &tgt[0], &tgt[1], &tgt[2]) == 3) {
> - /* virtio-scsi: target*:0:<target>:<unit> */
> - disk->bus_type = GUEST_DISK_BUS_TYPE_SCSI;
> - disk->bus = tgt[0];
> - disk->target = tgt[1];
> - disk->unit = tgt[2];
> - } else {
> - /* virtio-blk: 1 disk per 1 device */
> - disk->bus_type = GUEST_DISK_BUS_TYPE_VIRTIO;
> - }
> -
> - return true;
> -}
> -
> -/*
> - * Store disk device info for CCW devices (s390x channel I/O devices).
> - * Returns true if information has been stored, or false for failure.
> - */
> -static bool build_guest_fsinfo_for_ccw_dev(char const *syspath,
> - GuestDiskAddress *disk,
> - Error **errp)
> -{
> - unsigned int cssid, ssid, subchno, devno;
> - char *p;
> -
> - p = strstr(syspath, "/devices/css");
> - if (!p || sscanf(p + 12, "%*x/%x.%x.%x/%*x.%*x.%x/",
> - &cssid, &ssid, &subchno, &devno) < 4) {
> - g_debug("could not parse ccw device sysfs path: %s", syspath);
> - return false;
> - }
> -
> - disk->ccw_address = g_new0(GuestCCWAddress, 1);
> - disk->ccw_address->cssid = cssid;
> - disk->ccw_address->ssid = ssid;
> - disk->ccw_address->subchno = subchno;
> - disk->ccw_address->devno = devno;
> -
> - if (strstr(p, "/virtio")) {
> - build_guest_fsinfo_for_nonpci_virtio(syspath, disk, errp);
> - }
> -
> - return true;
> -}
> -
> -/* Store disk device info specified by @sysfs into @fs */
> -static void build_guest_fsinfo_for_real_device(char const *syspath,
> - GuestFilesystemInfo *fs,
> - Error **errp)
> -{
> - GuestDiskAddress *disk;
> - GuestPCIAddress *pciaddr;
> - bool has_hwinf;
> -#ifdef CONFIG_LIBUDEV
> - struct udev *udev = NULL;
> - struct udev_device *udevice = NULL;
> -#endif
> -
> - pciaddr = g_new0(GuestPCIAddress, 1);
> - pciaddr->domain = -1; /* -1 means field is
> invalid */
> - pciaddr->bus = -1;
> - pciaddr->slot = -1;
> - pciaddr->function = -1;
> -
> - disk = g_new0(GuestDiskAddress, 1);
> - disk->pci_controller = pciaddr;
> - disk->bus_type = GUEST_DISK_BUS_TYPE_UNKNOWN;
> -
> -#ifdef CONFIG_LIBUDEV
> - udev = udev_new();
> - udevice = udev_device_new_from_syspath(udev, syspath);
> - if (udev == NULL || udevice == NULL) {
> - g_debug("failed to query udev");
> - } else {
> - const char *devnode, *serial;
> - devnode = udev_device_get_devnode(udevice);
> - if (devnode != NULL) {
> - disk->dev = g_strdup(devnode);
> - }
> - serial = udev_device_get_property_value(udevice, "ID_SERIAL");
> - if (serial != NULL && *serial != 0) {
> - disk->serial = g_strdup(serial);
> - }
> - }
> -
> - udev_unref(udev);
> - udev_device_unref(udevice);
> -#endif
> -
> - if (strstr(syspath, "/devices/pci")) {
> - has_hwinf = build_guest_fsinfo_for_pci_dev(syspath, disk, errp);
> - } else if (strstr(syspath, "/devices/css")) {
> - has_hwinf = build_guest_fsinfo_for_ccw_dev(syspath, disk, errp);
> - } else if (strstr(syspath, "/virtio")) {
> - has_hwinf = build_guest_fsinfo_for_nonpci_virtio(syspath, disk,
> errp);
> - } else {
> - g_debug("Unsupported device type for '%s'", syspath);
> - has_hwinf = false;
> - }
> -
> - if (has_hwinf || disk->dev || disk->serial) {
> - QAPI_LIST_PREPEND(fs->disk, disk);
> - } else {
> - qapi_free_GuestDiskAddress(disk);
> - }
> -}
> -
> -static void build_guest_fsinfo_for_device(char const *devpath,
> - GuestFilesystemInfo *fs,
> - Error **errp);
> -
> -/* Store a list of slave devices of virtual volume specified by @syspath
> into
> - * @fs */
> -static void build_guest_fsinfo_for_virtual_device(char const *syspath,
> - GuestFilesystemInfo *fs,
> - Error **errp)
> -{
> - Error *err = NULL;
> - DIR *dir;
> - char *dirpath;
> - struct dirent *entry;
> -
> - dirpath = g_strdup_printf("%s/slaves", syspath);
> - dir = opendir(dirpath);
> - if (!dir) {
> - if (errno != ENOENT) {
> - error_setg_errno(errp, errno, "opendir(\"%s\")", dirpath);
> - }
> - g_free(dirpath);
> - return;
> - }
> -
> - for (;;) {
> - errno = 0;
> - entry = readdir(dir);
> - if (entry == NULL) {
> - if (errno) {
> - error_setg_errno(errp, errno, "readdir(\"%s\")", dirpath);
> - }
> - break;
> - }
> -
> - if (entry->d_type == DT_LNK) {
> - char *path;
> -
> - g_debug(" slave device '%s'", entry->d_name);
> - path = g_strdup_printf("%s/slaves/%s", syspath,
> entry->d_name);
> - build_guest_fsinfo_for_device(path, fs, &err);
> - g_free(path);
> -
> - if (err) {
> - error_propagate(errp, err);
> - break;
> - }
> - }
> - }
> -
> - g_free(dirpath);
> - closedir(dir);
> -}
> -
> -static bool is_disk_virtual(const char *devpath, Error **errp)
> -{
> - g_autofree char *syspath = realpath(devpath, NULL);
> -
> - if (!syspath) {
> - error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
> - return false;
> - }
> - return strstr(syspath, "/devices/virtual/block/") != NULL;
> -}
> -
> -/* Dispatch to functions for virtual/real device */
> -static void build_guest_fsinfo_for_device(char const *devpath,
> - GuestFilesystemInfo *fs,
> - Error **errp)
> -{
> - ERRP_GUARD();
> - g_autofree char *syspath = NULL;
> - bool is_virtual = false;
> -
> - syspath = realpath(devpath, NULL);
> - if (!syspath) {
> - if (errno != ENOENT) {
> - error_setg_errno(errp, errno, "realpath(\"%s\")", devpath);
> - return;
> - }
> -
> - /* ENOENT: This devpath may not exist because of container config
> */
> - if (!fs->name) {
> - fs->name = g_path_get_basename(devpath);
> - }
> - return;
> - }
> -
> - if (!fs->name) {
> - fs->name = g_path_get_basename(syspath);
> - }
> -
> - g_debug(" parse sysfs path '%s'", syspath);
> - is_virtual = is_disk_virtual(syspath, errp);
> - if (*errp != NULL) {
> - return;
> - }
> - if (is_virtual) {
> - build_guest_fsinfo_for_virtual_device(syspath, fs, errp);
> - } else {
> - build_guest_fsinfo_for_real_device(syspath, fs, errp);
> - }
> -}
> -
> -#ifdef CONFIG_LIBUDEV
> -
> -/*
> - * Wrapper around build_guest_fsinfo_for_device() for getting just
> - * the disk address.
> - */
> -static GuestDiskAddress *get_disk_address(const char *syspath, Error
> **errp)
> -{
> - g_autoptr(GuestFilesystemInfo) fs = NULL;
> -
> - fs = g_new0(GuestFilesystemInfo, 1);
> - build_guest_fsinfo_for_device(syspath, fs, errp);
> - if (fs->disk != NULL) {
> - return g_steal_pointer(&fs->disk->value);
> - }
> - return NULL;
> -}
> -
> -static char *get_alias_for_syspath(const char *syspath)
> -{
> - struct udev *udev = NULL;
> - struct udev_device *udevice = NULL;
> - char *ret = NULL;
> -
> - udev = udev_new();
> - if (udev == NULL) {
> - g_debug("failed to query udev");
> - goto out;
> - }
> - udevice = udev_device_new_from_syspath(udev, syspath);
> - if (udevice == NULL) {
> - g_debug("failed to query udev for path: %s", syspath);
> - goto out;
> - } else {
> - const char *alias = udev_device_get_property_value(
> - udevice, "DM_NAME");
> - /*
> - * NULL means there was an error and empty string means there is
> no
> - * alias. In case of no alias we return NULL instead of empty
> string.
> - */
> - if (alias == NULL) {
> - g_debug("failed to query udev for device alias for: %s",
> - syspath);
> - } else if (*alias != 0) {
> - ret = g_strdup(alias);
> - }
> - }
> -
> -out:
> - udev_unref(udev);
> - udev_device_unref(udevice);
> - return ret;
> -}
> -
> -static char *get_device_for_syspath(const char *syspath)
> -{
> - struct udev *udev = NULL;
> - struct udev_device *udevice = NULL;
> - char *ret = NULL;
> -
> - udev = udev_new();
> - if (udev == NULL) {
> - g_debug("failed to query udev");
> - goto out;
> - }
> - udevice = udev_device_new_from_syspath(udev, syspath);
> - if (udevice == NULL) {
> - g_debug("failed to query udev for path: %s", syspath);
> - goto out;
> - } else {
> - ret = g_strdup(udev_device_get_devnode(udevice));
> - }
> -
> -out:
> - udev_unref(udev);
> - udev_device_unref(udevice);
> - return ret;
> -}
> -
> -static void get_disk_deps(const char *disk_dir, GuestDiskInfo *disk)
> -{
> - g_autofree char *deps_dir = NULL;
> - const gchar *dep;
> - GDir *dp_deps = NULL;
> -
> - /* List dependent disks */
> - deps_dir = g_strdup_printf("%s/slaves", disk_dir);
> - g_debug(" listing entries in: %s", deps_dir);
> - dp_deps = g_dir_open(deps_dir, 0, NULL);
> - if (dp_deps == NULL) {
> - g_debug("failed to list entries in %s", deps_dir);
> - return;
> - }
> - disk->has_dependencies = true;
> - while ((dep = g_dir_read_name(dp_deps)) != NULL) {
> - g_autofree char *dep_dir = NULL;
> - char *dev_name;
> -
> - /* Add dependent disks */
> - dep_dir = g_strdup_printf("%s/%s", deps_dir, dep);
> - dev_name = get_device_for_syspath(dep_dir);
> - if (dev_name != NULL) {
> - g_debug(" adding dependent device: %s", dev_name);
> - QAPI_LIST_PREPEND(disk->dependencies, dev_name);
> - }
> - }
> - g_dir_close(dp_deps);
> -}
> -
> -/*
> - * Detect partitions subdirectory, name is "<disk_name><number>" or
> - * "<disk_name>p<number>"
> - *
> - * @disk_name -- last component of /sys path (e.g. sda)
> - * @disk_dir -- sys path of the disk (e.g. /sys/block/sda)
> - * @disk_dev -- device node of the disk (e.g. /dev/sda)
> - */
> -static GuestDiskInfoList *get_disk_partitions(
> - GuestDiskInfoList *list,
> - const char *disk_name, const char *disk_dir,
> - const char *disk_dev)
> -{
> - GuestDiskInfoList *ret = list;
> - struct dirent *de_disk;
> - DIR *dp_disk = NULL;
> - size_t len = strlen(disk_name);
> -
> - dp_disk = opendir(disk_dir);
> - while ((de_disk = readdir(dp_disk)) != NULL) {
> - g_autofree char *partition_dir = NULL;
> - char *dev_name;
> - GuestDiskInfo *partition;
> -
> - if (!(de_disk->d_type & DT_DIR)) {
> - continue;
> - }
> -
> - if (!(strncmp(disk_name, de_disk->d_name, len) == 0 &&
> - ((*(de_disk->d_name + len) == 'p' &&
> - isdigit(*(de_disk->d_name + len + 1))) ||
> - isdigit(*(de_disk->d_name + len))))) {
> - continue;
> - }
> -
> - partition_dir = g_strdup_printf("%s/%s",
> - disk_dir, de_disk->d_name);
> - dev_name = get_device_for_syspath(partition_dir);
> - if (dev_name == NULL) {
> - g_debug("Failed to get device name for syspath: %s",
> - disk_dir);
> - continue;
> - }
> - partition = g_new0(GuestDiskInfo, 1);
> - partition->name = dev_name;
> - partition->partition = true;
> - partition->has_dependencies = true;
> - /* Add parent disk as dependent for easier tracking of hierarchy
> */
> - QAPI_LIST_PREPEND(partition->dependencies, g_strdup(disk_dev));
> -
> - QAPI_LIST_PREPEND(ret, partition);
> - }
> - closedir(dp_disk);
> -
> - return ret;
> -}
> -
> -static void get_nvme_smart(GuestDiskInfo *disk)
> -{
> - int fd;
> - GuestNVMeSmart *smart;
> - NvmeSmartLog log = {0};
> - struct nvme_admin_cmd cmd = {
> - .opcode = NVME_ADM_CMD_GET_LOG_PAGE,
> - .nsid = NVME_NSID_BROADCAST,
> - .addr = (uintptr_t)&log,
> - .data_len = sizeof(log),
> - .cdw10 = NVME_LOG_SMART_INFO | (1 << 15) /* RAE bit */
> - | (((sizeof(log) >> 2) - 1) << 16)
> - };
> -
> - fd = qga_open_cloexec(disk->name, O_RDONLY, 0);
> - if (fd == -1) {
> - g_debug("Failed to open device: %s: %s", disk->name,
> g_strerror(errno));
> - return;
> - }
> -
> - if (ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd)) {
> - g_debug("Failed to get smart: %s: %s", disk->name,
> g_strerror(errno));
> - close(fd);
> - return;
> - }
> -
> - disk->smart = g_new0(GuestDiskSmart, 1);
> - disk->smart->type = GUEST_DISK_BUS_TYPE_NVME;
> -
> - smart = &disk->smart->u.nvme;
> - smart->critical_warning = log.critical_warning;
> - smart->temperature = lduw_le_p(&log.temperature); /* unaligned field
> */
> - smart->available_spare = log.available_spare;
> - smart->available_spare_threshold = log.available_spare_threshold;
> - smart->percentage_used = log.percentage_used;
> - smart->data_units_read_lo = le64_to_cpu(log.data_units_read[0]);
> - smart->data_units_read_hi = le64_to_cpu(log.data_units_read[1]);
> - smart->data_units_written_lo = le64_to_cpu(log.data_units_written[0]);
> - smart->data_units_written_hi = le64_to_cpu(log.data_units_written[1]);
> - smart->host_read_commands_lo = le64_to_cpu(log.host_read_commands[0]);
> - smart->host_read_commands_hi = le64_to_cpu(log.host_read_commands[1]);
> - smart->host_write_commands_lo =
> le64_to_cpu(log.host_write_commands[0]);
> - smart->host_write_commands_hi =
> le64_to_cpu(log.host_write_commands[1]);
> - smart->controller_busy_time_lo =
> le64_to_cpu(log.controller_busy_time[0]);
> - smart->controller_busy_time_hi =
> le64_to_cpu(log.controller_busy_time[1]);
> - smart->power_cycles_lo = le64_to_cpu(log.power_cycles[0]);
> - smart->power_cycles_hi = le64_to_cpu(log.power_cycles[1]);
> - smart->power_on_hours_lo = le64_to_cpu(log.power_on_hours[0]);
> - smart->power_on_hours_hi = le64_to_cpu(log.power_on_hours[1]);
> - smart->unsafe_shutdowns_lo = le64_to_cpu(log.unsafe_shutdowns[0]);
> - smart->unsafe_shutdowns_hi = le64_to_cpu(log.unsafe_shutdowns[1]);
> - smart->media_errors_lo = le64_to_cpu(log.media_errors[0]);
> - smart->media_errors_hi = le64_to_cpu(log.media_errors[1]);
> - smart->number_of_error_log_entries_lo =
> - le64_to_cpu(log.number_of_error_log_entries[0]);
> - smart->number_of_error_log_entries_hi =
> - le64_to_cpu(log.number_of_error_log_entries[1]);
> -
> - close(fd);
> -}
> -
> -static void get_disk_smart(GuestDiskInfo *disk)
> -{
> - if (disk->address
> - && (disk->address->bus_type == GUEST_DISK_BUS_TYPE_NVME)) {
> - get_nvme_smart(disk);
> - }
> -}
> -
> -GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
> -{
> - GuestDiskInfoList *ret = NULL;
> - GuestDiskInfo *disk;
> - DIR *dp = NULL;
> - struct dirent *de = NULL;
> -
> - g_debug("listing /sys/block directory");
> - dp = opendir("/sys/block");
> - if (dp == NULL) {
> - error_setg_errno(errp, errno, "Can't open directory
> \"/sys/block\"");
> - return NULL;
> - }
> - while ((de = readdir(dp)) != NULL) {
> - g_autofree char *disk_dir = NULL, *line = NULL,
> - *size_path = NULL;
> - char *dev_name;
> - Error *local_err = NULL;
> - if (de->d_type != DT_LNK) {
> - g_debug(" skipping entry: %s", de->d_name);
> - continue;
> - }
> -
> - /* Check size and skip zero-sized disks */
> - g_debug(" checking disk size");
> - size_path = g_strdup_printf("/sys/block/%s/size", de->d_name);
> - if (!g_file_get_contents(size_path, &line, NULL, NULL)) {
> - g_debug(" failed to read disk size");
> - continue;
> - }
> - if (g_strcmp0(line, "0\n") == 0) {
> - g_debug(" skipping zero-sized disk");
> - continue;
> - }
> -
> - g_debug(" adding %s", de->d_name);
> - disk_dir = g_strdup_printf("/sys/block/%s", de->d_name);
> - dev_name = get_device_for_syspath(disk_dir);
> - if (dev_name == NULL) {
> - g_debug("Failed to get device name for syspath: %s",
> - disk_dir);
> - continue;
> - }
> - disk = g_new0(GuestDiskInfo, 1);
> - disk->name = dev_name;
> - disk->partition = false;
> - disk->alias = get_alias_for_syspath(disk_dir);
> - QAPI_LIST_PREPEND(ret, disk);
> -
> - /* Get address for non-virtual devices */
> - bool is_virtual = is_disk_virtual(disk_dir, &local_err);
> - if (local_err != NULL) {
> - g_debug(" failed to check disk path, ignoring error: %s",
> - error_get_pretty(local_err));
> - error_free(local_err);
> - local_err = NULL;
> - /* Don't try to get the address */
> - is_virtual = true;
> - }
> - if (!is_virtual) {
> - disk->address = get_disk_address(disk_dir, &local_err);
> - if (local_err != NULL) {
> - g_debug(" failed to get device info, ignoring error: %s",
> - error_get_pretty(local_err));
> - error_free(local_err);
> - local_err = NULL;
> - }
> - }
> -
> - get_disk_deps(disk_dir, disk);
> - get_disk_smart(disk);
> - ret = get_disk_partitions(ret, de->d_name, disk_dir, dev_name);
> - }
> -
> - closedir(dp);
> -
> - return ret;
> -}
> -
> -#else
> -
> -GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> -#endif
> -
> -/* Return a list of the disk device(s)' info which @mount lies on */
> -static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
> - Error **errp)
> -{
> - GuestFilesystemInfo *fs = g_malloc0(sizeof(*fs));
> - struct statvfs buf;
> - unsigned long used, nonroot_total, fr_size;
> - char *devpath = g_strdup_printf("/sys/dev/block/%u:%u",
> - mount->devmajor, mount->devminor);
> -
> - fs->mountpoint = g_strdup(mount->dirname);
> - fs->type = g_strdup(mount->devtype);
> - build_guest_fsinfo_for_device(devpath, fs, errp);
> -
> - if (statvfs(fs->mountpoint, &buf) == 0) {
> - fr_size = buf.f_frsize;
> - used = buf.f_blocks - buf.f_bfree;
> - nonroot_total = used + buf.f_bavail;
> - fs->used_bytes = used * fr_size;
> - fs->total_bytes = nonroot_total * fr_size;
> - fs->total_bytes_privileged = buf.f_blocks * fr_size;
> -
> - fs->has_total_bytes = true;
> - fs->has_total_bytes_privileged = true;
> - fs->has_used_bytes = true;
> - }
> -
> - g_free(devpath);
> -
> - return fs;
> -}
> -
> -GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
> -{
> - FsMountList mounts;
> - struct FsMount *mount;
> - GuestFilesystemInfoList *ret = NULL;
> - Error *local_err = NULL;
> -
> - QTAILQ_INIT(&mounts);
> - if (!build_fs_mount_list(&mounts, &local_err)) {
> - error_propagate(errp, local_err);
> - return NULL;
> - }
> -
> - QTAILQ_FOREACH(mount, &mounts, next) {
> - g_debug("Building guest fsinfo for '%s'", mount->dirname);
> -
> - QAPI_LIST_PREPEND(ret, build_guest_fsinfo(mount, &local_err));
> - if (local_err) {
> - error_propagate(errp, local_err);
> - qapi_free_GuestFilesystemInfoList(ret);
> - ret = NULL;
> - break;
> - }
> - }
> -
> - free_fs_mount_list(&mounts);
> - return ret;
> -}
> -#endif /* CONFIG_FSFREEZE */
> -
> -#if defined(CONFIG_FSTRIM)
> -/*
> - * Walk list of mounted file systems in the guest, and trim them.
> - */
> -GuestFilesystemTrimResponse *
> -qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
> -{
> - GuestFilesystemTrimResponse *response;
> - GuestFilesystemTrimResult *result;
> - int ret = 0;
> - FsMountList mounts;
> - struct FsMount *mount;
> - int fd;
> - struct fstrim_range r;
> -
> - slog("guest-fstrim called");
> -
> - QTAILQ_INIT(&mounts);
> - if (!build_fs_mount_list(&mounts, errp)) {
> - return NULL;
> - }
> -
> - response = g_malloc0(sizeof(*response));
> -
> - QTAILQ_FOREACH(mount, &mounts, next) {
> - result = g_malloc0(sizeof(*result));
> - result->path = g_strdup(mount->dirname);
> -
> - QAPI_LIST_PREPEND(response->paths, result);
> -
> - fd = qga_open_cloexec(mount->dirname, O_RDONLY, 0);
> - if (fd == -1) {
> - result->error = g_strdup_printf("failed to open: %s",
> - strerror(errno));
> - continue;
> - }
> -
> - /* We try to cull filesystems we know won't work in advance, but
> other
> - * filesystems may not implement fstrim for less obvious reasons.
> - * These will report EOPNOTSUPP; while in some other cases ENOTTY
> - * will be reported (e.g. CD-ROMs).
> - * Any other error means an unexpected error.
> - */
> - r.start = 0;
> - r.len = -1;
> - r.minlen = has_minimum ? minimum : 0;
> - ret = ioctl(fd, FITRIM, &r);
> - if (ret == -1) {
> - if (errno == ENOTTY || errno == EOPNOTSUPP) {
> - result->error = g_strdup("trim not supported");
> - } else {
> - result->error = g_strdup_printf("failed to trim: %s",
> - strerror(errno));
> - }
> - close(fd);
> - continue;
> - }
> -
> - result->has_minimum = true;
> - result->minimum = r.minlen;
> - result->has_trimmed = true;
> - result->trimmed = r.len;
> - close(fd);
> - }
> -
> - free_fs_mount_list(&mounts);
> - return response;
> -}
> -#endif /* CONFIG_FSTRIM */
> -
> -#endif /* __linux__ */
> -
> #if defined(__linux__) || defined(__FreeBSD__)
> void qmp_guest_set_user_password(const char *username,
> const char *password,
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 78833 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 03/22] qga: move linux suspend command impls to commands-linux.c
2024-06-13 15:01 ` [PATCH v2 03/22] qga: move linux suspend " Daniel P. Berrangé
2024-07-03 8:45 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:29 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:29 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Michael Roth,
Philippe Mathieu-Daudé, Thomas Huth, Paolo Bonzini,
Manos Pitsidianakis
[-- Attachment #1: Type: text/plain, Size: 17431 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:02 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> The qmp_guest_suspend_{disk,ram,hybrid} command impls in
> commands-posix.c are surrounded by '#ifdef __linux__' so should
> instead live in commands-linux.c
>
> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 265 +++++++++++++++++++++++++++++++++++++++++++
> qga/commands-posix.c | 265 -------------------------------------------
> 2 files changed, 265 insertions(+), 265 deletions(-)
>
> diff --git a/qga/commands-linux.c b/qga/commands-linux.c
> index 78580ac39d..3fabf54882 100644
> --- a/qga/commands-linux.c
> +++ b/qga/commands-linux.c
> @@ -286,6 +286,271 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp)
> }
> #endif /* CONFIG_FSFREEZE */
>
> +
> +#define LINUX_SYS_STATE_FILE "/sys/power/state"
> +#define SUSPEND_SUPPORTED 0
> +#define SUSPEND_NOT_SUPPORTED 1
> +
> +typedef enum {
> + SUSPEND_MODE_DISK = 0,
> + SUSPEND_MODE_RAM = 1,
> + SUSPEND_MODE_HYBRID = 2,
> +} SuspendMode;
> +
> +/*
> + * Executes a command in a child process using g_spawn_sync,
> + * returning an int >= 0 representing the exit status of the
> + * process.
> + *
> + * If the program wasn't found in path, returns -1.
> + *
> + * If a problem happened when creating the child process,
> + * returns -1 and errp is set.
> + */
> +static int run_process_child(const char *command[], Error **errp)
> +{
> + int exit_status, spawn_flag;
> + GError *g_err = NULL;
> + bool success;
> +
> + spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
> + G_SPAWN_STDERR_TO_DEV_NULL;
> +
> + success = g_spawn_sync(NULL, (char **)command, NULL, spawn_flag,
> + NULL, NULL, NULL, NULL,
> + &exit_status, &g_err);
> +
> + if (success) {
> + return WEXITSTATUS(exit_status);
> + }
> +
> + if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
> + error_setg(errp, "failed to create child process, error '%s'",
> + g_err->message);
> + }
> +
> + g_error_free(g_err);
> + return -1;
> +}
> +
> +static bool systemd_supports_mode(SuspendMode mode, Error **errp)
> +{
> + const char *systemctl_args[3] = {"systemd-hibernate",
> "systemd-suspend",
> + "systemd-hybrid-sleep"};
> + const char *cmd[4] = {"systemctl", "status", systemctl_args[mode],
> NULL};
> + int status;
> +
> + status = run_process_child(cmd, errp);
> +
> + /*
> + * systemctl status uses LSB return codes so we can expect
> + * status > 0 and be ok. To assert if the guest has support
> + * for the selected suspend mode, status should be < 4. 4 is
> + * the code for unknown service status, the return value when
> + * the service does not exist. A common value is status = 3
> + * (program is not running).
> + */
> + if (status > 0 && status < 4) {
> + return true;
> + }
> +
> + return false;
> +}
> +
> +static void systemd_suspend(SuspendMode mode, Error **errp)
> +{
> + Error *local_err = NULL;
> + const char *systemctl_args[3] = {"hibernate", "suspend",
> "hybrid-sleep"};
> + const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL};
> + int status;
> +
> + status = run_process_child(cmd, &local_err);
> +
> + if (status == 0) {
> + return;
> + }
> +
> + if ((status == -1) && !local_err) {
> + error_setg(errp, "the helper program 'systemctl %s' was not
> found",
> + systemctl_args[mode]);
> + return;
> + }
> +
> + if (local_err) {
> + error_propagate(errp, local_err);
> + } else {
> + error_setg(errp, "the helper program 'systemctl %s' returned an "
> + "unexpected exit status code (%d)",
> + systemctl_args[mode], status);
> + }
> +}
> +
> +static bool pmutils_supports_mode(SuspendMode mode, Error **errp)
> +{
> + Error *local_err = NULL;
> + const char *pmutils_args[3] = {"--hibernate", "--suspend",
> + "--suspend-hybrid"};
> + const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL};
> + int status;
> +
> + status = run_process_child(cmd, &local_err);
> +
> + if (status == SUSPEND_SUPPORTED) {
> + return true;
> + }
> +
> + if ((status == -1) && !local_err) {
> + return false;
> + }
> +
> + if (local_err) {
> + error_propagate(errp, local_err);
> + } else {
> + error_setg(errp,
> + "the helper program '%s' returned an unexpected exit"
> + " status code (%d)", "pm-is-supported", status);
> + }
> +
> + return false;
> +}
> +
> +static void pmutils_suspend(SuspendMode mode, Error **errp)
> +{
> + Error *local_err = NULL;
> + const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
> + "pm-suspend-hybrid"};
> + const char *cmd[2] = {pmutils_binaries[mode], NULL};
> + int status;
> +
> + status = run_process_child(cmd, &local_err);
> +
> + if (status == 0) {
> + return;
> + }
> +
> + if ((status == -1) && !local_err) {
> + error_setg(errp, "the helper program '%s' was not found",
> + pmutils_binaries[mode]);
> + return;
> + }
> +
> + if (local_err) {
> + error_propagate(errp, local_err);
> + } else {
> + error_setg(errp,
> + "the helper program '%s' returned an unexpected exit"
> + " status code (%d)", pmutils_binaries[mode], status);
> + }
> +}
> +
> +static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
> +{
> + const char *sysfile_strs[3] = {"disk", "mem", NULL};
> + const char *sysfile_str = sysfile_strs[mode];
> + char buf[32]; /* hopefully big enough */
> + int fd;
> + ssize_t ret;
> +
> + if (!sysfile_str) {
> + error_setg(errp, "unknown guest suspend mode");
> + return false;
> + }
> +
> + fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
> + if (fd < 0) {
> + return false;
> + }
> +
> + ret = read(fd, buf, sizeof(buf) - 1);
> + close(fd);
> + if (ret <= 0) {
> + return false;
> + }
> + buf[ret] = '\0';
> +
> + if (strstr(buf, sysfile_str)) {
> + return true;
> + }
> + return false;
> +}
> +
> +static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
> +{
> + g_autoptr(GError) local_gerr = NULL;
> + const char *sysfile_strs[3] = {"disk", "mem", NULL};
> + const char *sysfile_str = sysfile_strs[mode];
> +
> + if (!sysfile_str) {
> + error_setg(errp, "unknown guest suspend mode");
> + return;
> + }
> +
> + if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str,
> + -1, &local_gerr)) {
> + error_setg(errp, "suspend: cannot write to '%s': %s",
> + LINUX_SYS_STATE_FILE, local_gerr->message);
> + return;
> + }
> +}
> +
> +static void guest_suspend(SuspendMode mode, Error **errp)
> +{
> + Error *local_err = NULL;
> + bool mode_supported = false;
> +
> + if (systemd_supports_mode(mode, &local_err)) {
> + mode_supported = true;
> + systemd_suspend(mode, &local_err);
> +
> + if (!local_err) {
> + return;
> + }
> + }
> +
> + error_free(local_err);
> + local_err = NULL;
> +
> + if (pmutils_supports_mode(mode, &local_err)) {
> + mode_supported = true;
> + pmutils_suspend(mode, &local_err);
> +
> + if (!local_err) {
> + return;
> + }
> + }
> +
> + error_free(local_err);
> + local_err = NULL;
> +
> + if (linux_sys_state_supports_mode(mode, &local_err)) {
> + mode_supported = true;
> + linux_sys_state_suspend(mode, &local_err);
> + }
> +
> + if (!mode_supported) {
> + error_free(local_err);
> + error_setg(errp,
> + "the requested suspend mode is not supported by the
> guest");
> + } else {
> + error_propagate(errp, local_err);
> + }
> +}
> +
> +void qmp_guest_suspend_disk(Error **errp)
> +{
> + guest_suspend(SUSPEND_MODE_DISK, errp);
> +}
> +
> +void qmp_guest_suspend_ram(Error **errp)
> +{
> + guest_suspend(SUSPEND_MODE_RAM, errp);
> +}
> +
> +void qmp_guest_suspend_hybrid(Error **errp)
> +{
> + guest_suspend(SUSPEND_MODE_HYBRID, errp);
> +}
> +
> /* Transfer online/offline status between @vcpu and the guest system.
> *
> * On input either @errp or *@errp must be NULL.
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index a8ef41f175..ef21da63be 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -1738,271 +1738,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t
> minimum, Error **errp)
> }
> #endif /* CONFIG_FSTRIM */
>
> -
> -#define LINUX_SYS_STATE_FILE "/sys/power/state"
> -#define SUSPEND_SUPPORTED 0
> -#define SUSPEND_NOT_SUPPORTED 1
> -
> -typedef enum {
> - SUSPEND_MODE_DISK = 0,
> - SUSPEND_MODE_RAM = 1,
> - SUSPEND_MODE_HYBRID = 2,
> -} SuspendMode;
> -
> -/*
> - * Executes a command in a child process using g_spawn_sync,
> - * returning an int >= 0 representing the exit status of the
> - * process.
> - *
> - * If the program wasn't found in path, returns -1.
> - *
> - * If a problem happened when creating the child process,
> - * returns -1 and errp is set.
> - */
> -static int run_process_child(const char *command[], Error **errp)
> -{
> - int exit_status, spawn_flag;
> - GError *g_err = NULL;
> - bool success;
> -
> - spawn_flag = G_SPAWN_SEARCH_PATH | G_SPAWN_STDOUT_TO_DEV_NULL |
> - G_SPAWN_STDERR_TO_DEV_NULL;
> -
> - success = g_spawn_sync(NULL, (char **)command, NULL, spawn_flag,
> - NULL, NULL, NULL, NULL,
> - &exit_status, &g_err);
> -
> - if (success) {
> - return WEXITSTATUS(exit_status);
> - }
> -
> - if (g_err && (g_err->code != G_SPAWN_ERROR_NOENT)) {
> - error_setg(errp, "failed to create child process, error '%s'",
> - g_err->message);
> - }
> -
> - g_error_free(g_err);
> - return -1;
> -}
> -
> -static bool systemd_supports_mode(SuspendMode mode, Error **errp)
> -{
> - const char *systemctl_args[3] = {"systemd-hibernate",
> "systemd-suspend",
> - "systemd-hybrid-sleep"};
> - const char *cmd[4] = {"systemctl", "status", systemctl_args[mode],
> NULL};
> - int status;
> -
> - status = run_process_child(cmd, errp);
> -
> - /*
> - * systemctl status uses LSB return codes so we can expect
> - * status > 0 and be ok. To assert if the guest has support
> - * for the selected suspend mode, status should be < 4. 4 is
> - * the code for unknown service status, the return value when
> - * the service does not exist. A common value is status = 3
> - * (program is not running).
> - */
> - if (status > 0 && status < 4) {
> - return true;
> - }
> -
> - return false;
> -}
> -
> -static void systemd_suspend(SuspendMode mode, Error **errp)
> -{
> - Error *local_err = NULL;
> - const char *systemctl_args[3] = {"hibernate", "suspend",
> "hybrid-sleep"};
> - const char *cmd[3] = {"systemctl", systemctl_args[mode], NULL};
> - int status;
> -
> - status = run_process_child(cmd, &local_err);
> -
> - if (status == 0) {
> - return;
> - }
> -
> - if ((status == -1) && !local_err) {
> - error_setg(errp, "the helper program 'systemctl %s' was not
> found",
> - systemctl_args[mode]);
> - return;
> - }
> -
> - if (local_err) {
> - error_propagate(errp, local_err);
> - } else {
> - error_setg(errp, "the helper program 'systemctl %s' returned an "
> - "unexpected exit status code (%d)",
> - systemctl_args[mode], status);
> - }
> -}
> -
> -static bool pmutils_supports_mode(SuspendMode mode, Error **errp)
> -{
> - Error *local_err = NULL;
> - const char *pmutils_args[3] = {"--hibernate", "--suspend",
> - "--suspend-hybrid"};
> - const char *cmd[3] = {"pm-is-supported", pmutils_args[mode], NULL};
> - int status;
> -
> - status = run_process_child(cmd, &local_err);
> -
> - if (status == SUSPEND_SUPPORTED) {
> - return true;
> - }
> -
> - if ((status == -1) && !local_err) {
> - return false;
> - }
> -
> - if (local_err) {
> - error_propagate(errp, local_err);
> - } else {
> - error_setg(errp,
> - "the helper program '%s' returned an unexpected exit"
> - " status code (%d)", "pm-is-supported", status);
> - }
> -
> - return false;
> -}
> -
> -static void pmutils_suspend(SuspendMode mode, Error **errp)
> -{
> - Error *local_err = NULL;
> - const char *pmutils_binaries[3] = {"pm-hibernate", "pm-suspend",
> - "pm-suspend-hybrid"};
> - const char *cmd[2] = {pmutils_binaries[mode], NULL};
> - int status;
> -
> - status = run_process_child(cmd, &local_err);
> -
> - if (status == 0) {
> - return;
> - }
> -
> - if ((status == -1) && !local_err) {
> - error_setg(errp, "the helper program '%s' was not found",
> - pmutils_binaries[mode]);
> - return;
> - }
> -
> - if (local_err) {
> - error_propagate(errp, local_err);
> - } else {
> - error_setg(errp,
> - "the helper program '%s' returned an unexpected exit"
> - " status code (%d)", pmutils_binaries[mode], status);
> - }
> -}
> -
> -static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
> -{
> - const char *sysfile_strs[3] = {"disk", "mem", NULL};
> - const char *sysfile_str = sysfile_strs[mode];
> - char buf[32]; /* hopefully big enough */
> - int fd;
> - ssize_t ret;
> -
> - if (!sysfile_str) {
> - error_setg(errp, "unknown guest suspend mode");
> - return false;
> - }
> -
> - fd = open(LINUX_SYS_STATE_FILE, O_RDONLY);
> - if (fd < 0) {
> - return false;
> - }
> -
> - ret = read(fd, buf, sizeof(buf) - 1);
> - close(fd);
> - if (ret <= 0) {
> - return false;
> - }
> - buf[ret] = '\0';
> -
> - if (strstr(buf, sysfile_str)) {
> - return true;
> - }
> - return false;
> -}
> -
> -static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
> -{
> - g_autoptr(GError) local_gerr = NULL;
> - const char *sysfile_strs[3] = {"disk", "mem", NULL};
> - const char *sysfile_str = sysfile_strs[mode];
> -
> - if (!sysfile_str) {
> - error_setg(errp, "unknown guest suspend mode");
> - return;
> - }
> -
> - if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str,
> - -1, &local_gerr)) {
> - error_setg(errp, "suspend: cannot write to '%s': %s",
> - LINUX_SYS_STATE_FILE, local_gerr->message);
> - return;
> - }
> -}
> -
> -static void guest_suspend(SuspendMode mode, Error **errp)
> -{
> - Error *local_err = NULL;
> - bool mode_supported = false;
> -
> - if (systemd_supports_mode(mode, &local_err)) {
> - mode_supported = true;
> - systemd_suspend(mode, &local_err);
> -
> - if (!local_err) {
> - return;
> - }
> - }
> -
> - error_free(local_err);
> - local_err = NULL;
> -
> - if (pmutils_supports_mode(mode, &local_err)) {
> - mode_supported = true;
> - pmutils_suspend(mode, &local_err);
> -
> - if (!local_err) {
> - return;
> - }
> - }
> -
> - error_free(local_err);
> - local_err = NULL;
> -
> - if (linux_sys_state_supports_mode(mode, &local_err)) {
> - mode_supported = true;
> - linux_sys_state_suspend(mode, &local_err);
> - }
> -
> - if (!mode_supported) {
> - error_free(local_err);
> - error_setg(errp,
> - "the requested suspend mode is not supported by the
> guest");
> - } else {
> - error_propagate(errp, local_err);
> - }
> -}
> -
> -void qmp_guest_suspend_disk(Error **errp)
> -{
> - guest_suspend(SUSPEND_MODE_DISK, errp);
> -}
> -
> -void qmp_guest_suspend_ram(Error **errp)
> -{
> - guest_suspend(SUSPEND_MODE_RAM, errp);
> -}
> -
> -void qmp_guest_suspend_hybrid(Error **errp)
> -{
> - guest_suspend(SUSPEND_MODE_HYBRID, errp);
> -}
> -
> #endif /* __linux__ */
>
> #if defined(__linux__) || defined(__FreeBSD__)
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 21310 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 02/22] qga: move linux vcpu command impls to commands-linux.c
2024-06-13 15:01 ` [PATCH v2 02/22] qga: move linux vcpu command impls to commands-linux.c Daniel P. Berrangé
2024-07-03 8:45 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:30 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:30 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Michael Roth,
Philippe Mathieu-Daudé, Thomas Huth, Paolo Bonzini,
Manos Pitsidianakis
[-- Attachment #1: Type: text/plain, Size: 11158 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:02 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> The qmp_guest_set_vcpus and qmp_guest_get_vcpus command impls in
> commands-posix.c are surrounded by '#ifdef __linux__' so should
> instead live in commands-linux.c
>
> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 141 +++++++++++++++++++++++++++++++++++++++++++
> qga/commands-posix.c | 139 ------------------------------------------
> 2 files changed, 141 insertions(+), 139 deletions(-)
>
> diff --git a/qga/commands-linux.c b/qga/commands-linux.c
> index 214e408fcd..78580ac39d 100644
> --- a/qga/commands-linux.c
> +++ b/qga/commands-linux.c
> @@ -13,6 +13,7 @@
>
> #include "qemu/osdep.h"
> #include "qapi/error.h"
> +#include "qga-qapi-commands.h"
> #include "commands-common.h"
> #include "cutils.h"
> #include <mntent.h>
> @@ -284,3 +285,143 @@ int qmp_guest_fsfreeze_do_thaw(Error **errp)
> return i;
> }
> #endif /* CONFIG_FSFREEZE */
> +
> +/* Transfer online/offline status between @vcpu and the guest system.
> + *
> + * On input either @errp or *@errp must be NULL.
> + *
> + * In system-to-@vcpu direction, the following @vcpu fields are accessed:
> + * - R: vcpu->logical_id
> + * - W: vcpu->online
> + * - W: vcpu->can_offline
> + *
> + * In @vcpu-to-system direction, the following @vcpu fields are accessed:
> + * - R: vcpu->logical_id
> + * - R: vcpu->online
> + *
> + * Written members remain unmodified on error.
> + */
> +static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
> + char *dirpath, Error **errp)
> +{
> + int fd;
> + int res;
> + int dirfd;
> + static const char fn[] = "online";
> +
> + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
> + if (dirfd == -1) {
> + error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
> + return;
> + }
> +
> + fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
> + if (fd == -1) {
> + if (errno != ENOENT) {
> + error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
> + } else if (sys2vcpu) {
> + vcpu->online = true;
> + vcpu->can_offline = false;
> + } else if (!vcpu->online) {
> + error_setg(errp, "logical processor #%" PRId64 " can't be "
> + "offlined", vcpu->logical_id);
> + } /* otherwise pretend successful re-onlining */
> + } else {
> + unsigned char status;
> +
> + res = pread(fd, &status, 1, 0);
> + if (res == -1) {
> + error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath,
> fn);
> + } else if (res == 0) {
> + error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
> + fn);
> + } else if (sys2vcpu) {
> + vcpu->online = (status != '0');
> + vcpu->can_offline = true;
> + } else if (vcpu->online != (status != '0')) {
> + status = '0' + vcpu->online;
> + if (pwrite(fd, &status, 1, 0) == -1) {
> + error_setg_errno(errp, errno, "pwrite(\"%s/%s\")",
> dirpath,
> + fn);
> + }
> + } /* otherwise pretend successful re-(on|off)-lining */
> +
> + res = close(fd);
> + g_assert(res == 0);
> + }
> +
> + res = close(dirfd);
> + g_assert(res == 0);
> +}
> +
> +GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
> +{
> + GuestLogicalProcessorList *head, **tail;
> + const char *cpu_dir = "/sys/devices/system/cpu";
> + const gchar *line;
> + g_autoptr(GDir) cpu_gdir = NULL;
> + Error *local_err = NULL;
> +
> + head = NULL;
> + tail = &head;
> + cpu_gdir = g_dir_open(cpu_dir, 0, NULL);
> +
> + if (cpu_gdir == NULL) {
> + error_setg_errno(errp, errno, "failed to list entries: %s",
> cpu_dir);
> + return NULL;
> + }
> +
> + while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) !=
> NULL) {
> + GuestLogicalProcessor *vcpu;
> + int64_t id;
> + if (sscanf(line, "cpu%" PRId64, &id)) {
> + g_autofree char *path =
> g_strdup_printf("/sys/devices/system/cpu/"
> + "cpu%" PRId64 "/",
> id);
> + vcpu = g_malloc0(sizeof *vcpu);
> + vcpu->logical_id = id;
> + vcpu->has_can_offline = true; /* lolspeak ftw */
> + transfer_vcpu(vcpu, true, path, &local_err);
> + QAPI_LIST_APPEND(tail, vcpu);
> + }
> + }
> +
> + if (local_err == NULL) {
> + /* there's no guest with zero VCPUs */
> + g_assert(head != NULL);
> + return head;
> + }
> +
> + qapi_free_GuestLogicalProcessorList(head);
> + error_propagate(errp, local_err);
> + return NULL;
> +}
> +
> +int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error
> **errp)
> +{
> + int64_t processed;
> + Error *local_err = NULL;
> +
> + processed = 0;
> + while (vcpus != NULL) {
> + char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%"
> PRId64 "/",
> + vcpus->value->logical_id);
> +
> + transfer_vcpu(vcpus->value, false, path, &local_err);
> + g_free(path);
> + if (local_err != NULL) {
> + break;
> + }
> + ++processed;
> + vcpus = vcpus->next;
> + }
> +
> + if (local_err != NULL) {
> + if (processed == 0) {
> + error_propagate(errp, local_err);
> + } else {
> + error_free(local_err);
> + }
> + }
> +
> + return processed;
> +}
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 76af98ba32..a8ef41f175 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -2003,145 +2003,6 @@ void qmp_guest_suspend_hybrid(Error **errp)
> guest_suspend(SUSPEND_MODE_HYBRID, errp);
> }
>
> -/* Transfer online/offline status between @vcpu and the guest system.
> - *
> - * On input either @errp or *@errp must be NULL.
> - *
> - * In system-to-@vcpu direction, the following @vcpu fields are accessed:
> - * - R: vcpu->logical_id
> - * - W: vcpu->online
> - * - W: vcpu->can_offline
> - *
> - * In @vcpu-to-system direction, the following @vcpu fields are accessed:
> - * - R: vcpu->logical_id
> - * - R: vcpu->online
> - *
> - * Written members remain unmodified on error.
> - */
> -static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu,
> - char *dirpath, Error **errp)
> -{
> - int fd;
> - int res;
> - int dirfd;
> - static const char fn[] = "online";
> -
> - dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
> - if (dirfd == -1) {
> - error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
> - return;
> - }
> -
> - fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR);
> - if (fd == -1) {
> - if (errno != ENOENT) {
> - error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn);
> - } else if (sys2vcpu) {
> - vcpu->online = true;
> - vcpu->can_offline = false;
> - } else if (!vcpu->online) {
> - error_setg(errp, "logical processor #%" PRId64 " can't be "
> - "offlined", vcpu->logical_id);
> - } /* otherwise pretend successful re-onlining */
> - } else {
> - unsigned char status;
> -
> - res = pread(fd, &status, 1, 0);
> - if (res == -1) {
> - error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath,
> fn);
> - } else if (res == 0) {
> - error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath,
> - fn);
> - } else if (sys2vcpu) {
> - vcpu->online = (status != '0');
> - vcpu->can_offline = true;
> - } else if (vcpu->online != (status != '0')) {
> - status = '0' + vcpu->online;
> - if (pwrite(fd, &status, 1, 0) == -1) {
> - error_setg_errno(errp, errno, "pwrite(\"%s/%s\")",
> dirpath,
> - fn);
> - }
> - } /* otherwise pretend successful re-(on|off)-lining */
> -
> - res = close(fd);
> - g_assert(res == 0);
> - }
> -
> - res = close(dirfd);
> - g_assert(res == 0);
> -}
> -
> -GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
> -{
> - GuestLogicalProcessorList *head, **tail;
> - const char *cpu_dir = "/sys/devices/system/cpu";
> - const gchar *line;
> - g_autoptr(GDir) cpu_gdir = NULL;
> - Error *local_err = NULL;
> -
> - head = NULL;
> - tail = &head;
> - cpu_gdir = g_dir_open(cpu_dir, 0, NULL);
> -
> - if (cpu_gdir == NULL) {
> - error_setg_errno(errp, errno, "failed to list entries: %s",
> cpu_dir);
> - return NULL;
> - }
> -
> - while (local_err == NULL && (line = g_dir_read_name(cpu_gdir)) !=
> NULL) {
> - GuestLogicalProcessor *vcpu;
> - int64_t id;
> - if (sscanf(line, "cpu%" PRId64, &id)) {
> - g_autofree char *path =
> g_strdup_printf("/sys/devices/system/cpu/"
> - "cpu%" PRId64 "/",
> id);
> - vcpu = g_malloc0(sizeof *vcpu);
> - vcpu->logical_id = id;
> - vcpu->has_can_offline = true; /* lolspeak ftw */
> - transfer_vcpu(vcpu, true, path, &local_err);
> - QAPI_LIST_APPEND(tail, vcpu);
> - }
> - }
> -
> - if (local_err == NULL) {
> - /* there's no guest with zero VCPUs */
> - g_assert(head != NULL);
> - return head;
> - }
> -
> - qapi_free_GuestLogicalProcessorList(head);
> - error_propagate(errp, local_err);
> - return NULL;
> -}
> -
> -int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error
> **errp)
> -{
> - int64_t processed;
> - Error *local_err = NULL;
> -
> - processed = 0;
> - while (vcpus != NULL) {
> - char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%"
> PRId64 "/",
> - vcpus->value->logical_id);
> -
> - transfer_vcpu(vcpus->value, false, path, &local_err);
> - g_free(path);
> - if (local_err != NULL) {
> - break;
> - }
> - ++processed;
> - vcpus = vcpus->next;
> - }
> -
> - if (local_err != NULL) {
> - if (processed == 0) {
> - error_propagate(errp, local_err);
> - } else {
> - error_free(local_err);
> - }
> - }
> -
> - return processed;
> -}
> #endif /* __linux__ */
>
> #if defined(__linux__) || defined(__FreeBSD__)
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 13855 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 05/22] qga: move linux disk/cpu stats command impls to commands-linux.c
2024-06-13 15:01 ` [PATCH v2 05/22] qga: move linux disk/cpu stats " Daniel P. Berrangé
2024-07-03 8:25 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:33 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:33 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Michael Roth,
Philippe Mathieu-Daudé, Thomas Huth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 16346 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:02 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> The qmp_guest_{diskstats,cpustats} command impls in
> commands-posix.c are surrounded by '#ifdef __linux__' so should
> instead live in commands-linux.c
>
> This also removes a "#ifdef CONFIG_LINUX" that was nested inside
> a "#ifdef __linux__".
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 195 ++++++++++++++++++++++++++++++++++++++++++
> qga/commands-posix.c | 199 -------------------------------------------
> 2 files changed, 195 insertions(+), 199 deletions(-)
>
> diff --git a/qga/commands-linux.c b/qga/commands-linux.c
> index 084e6c9e85..c0e8bd4062 100644
> --- a/qga/commands-linux.c
> +++ b/qga/commands-linux.c
> @@ -1594,3 +1594,198 @@ int64_t
> qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
>
> return processed;
> }
> +
> +#define MAX_NAME_LEN 128
> +static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp)
> +{
> + GuestDiskStatsInfoList *head = NULL, **tail = &head;
> + const char *diskstats = "/proc/diskstats";
> + FILE *fp;
> + size_t n;
> + char *line = NULL;
> +
> + fp = fopen(diskstats, "r");
> + if (fp == NULL) {
> + error_setg_errno(errp, errno, "open(\"%s\")", diskstats);
> + return NULL;
> + }
> +
> + while (getline(&line, &n, fp) != -1) {
> + g_autofree GuestDiskStatsInfo *diskstatinfo = NULL;
> + g_autofree GuestDiskStats *diskstat = NULL;
> + char dev_name[MAX_NAME_LEN];
> + unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks,
> fl_ticks;
> + unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec,
> wr_ios;
> + unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec;
> + unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
> + unsigned int major, minor;
> + int i;
> +
> + i = sscanf(line, "%u %u %s %lu %lu %lu"
> + "%lu %lu %lu %lu %u %u %u %u"
> + "%lu %lu %lu %u %lu %u",
> + &major, &minor, dev_name,
> + &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios,
> + &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec,
> + &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
> + &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
> + &fl_ios, &fl_ticks);
> +
> + if (i < 7) {
> + continue;
> + }
> +
> + diskstatinfo = g_new0(GuestDiskStatsInfo, 1);
> + diskstatinfo->name = g_strdup(dev_name);
> + diskstatinfo->major = major;
> + diskstatinfo->minor = minor;
> +
> + diskstat = g_new0(GuestDiskStats, 1);
> + if (i == 7) {
> + diskstat->has_read_ios = true;
> + diskstat->read_ios = rd_ios;
> + diskstat->has_read_sectors = true;
> + diskstat->read_sectors = rd_merges_or_rd_sec;
> + diskstat->has_write_ios = true;
> + diskstat->write_ios = rd_sec_or_wr_ios;
> + diskstat->has_write_sectors = true;
> + diskstat->write_sectors = rd_ticks_or_wr_sec;
> + }
> + if (i >= 14) {
> + diskstat->has_read_ios = true;
> + diskstat->read_ios = rd_ios;
> + diskstat->has_read_sectors = true;
> + diskstat->read_sectors = rd_sec_or_wr_ios;
> + diskstat->has_read_merges = true;
> + diskstat->read_merges = rd_merges_or_rd_sec;
> + diskstat->has_read_ticks = true;
> + diskstat->read_ticks = rd_ticks_or_wr_sec;
> + diskstat->has_write_ios = true;
> + diskstat->write_ios = wr_ios;
> + diskstat->has_write_sectors = true;
> + diskstat->write_sectors = wr_sec;
> + diskstat->has_write_merges = true;
> + diskstat->write_merges = wr_merges;
> + diskstat->has_write_ticks = true;
> + diskstat->write_ticks = wr_ticks;
> + diskstat->has_ios_pgr = true;
> + diskstat->ios_pgr = ios_pgr;
> + diskstat->has_total_ticks = true;
> + diskstat->total_ticks = tot_ticks;
> + diskstat->has_weight_ticks = true;
> + diskstat->weight_ticks = rq_ticks;
> + }
> + if (i >= 18) {
> + diskstat->has_discard_ios = true;
> + diskstat->discard_ios = dc_ios;
> + diskstat->has_discard_merges = true;
> + diskstat->discard_merges = dc_merges;
> + diskstat->has_discard_sectors = true;
> + diskstat->discard_sectors = dc_sec;
> + diskstat->has_discard_ticks = true;
> + diskstat->discard_ticks = dc_ticks;
> + }
> + if (i >= 20) {
> + diskstat->has_flush_ios = true;
> + diskstat->flush_ios = fl_ios;
> + diskstat->has_flush_ticks = true;
> + diskstat->flush_ticks = fl_ticks;
> + }
> +
> + diskstatinfo->stats = g_steal_pointer(&diskstat);
> + QAPI_LIST_APPEND(tail, diskstatinfo);
> + diskstatinfo = NULL;
> + }
> + free(line);
> + fclose(fp);
> + return head;
> +}
> +
> +GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
> +{
> + return guest_get_diskstats(errp);
> +}
> +
> +GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
> +{
> + GuestCpuStatsList *head = NULL, **tail = &head;
> + const char *cpustats = "/proc/stat";
> + int clk_tck = sysconf(_SC_CLK_TCK);
> + FILE *fp;
> + size_t n;
> + char *line = NULL;
> +
> + fp = fopen(cpustats, "r");
> + if (fp == NULL) {
> + error_setg_errno(errp, errno, "open(\"%s\")", cpustats);
> + return NULL;
> + }
> +
> + while (getline(&line, &n, fp) != -1) {
> + GuestCpuStats *cpustat = NULL;
> + GuestLinuxCpuStats *linuxcpustat;
> + int i;
> + unsigned long user, system, idle, iowait, irq, softirq, steal,
> guest;
> + unsigned long nice, guest_nice;
> + char name[64];
> +
> + i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
> + name, &user, &nice, &system, &idle, &iowait, &irq,
> &softirq,
> + &steal, &guest, &guest_nice);
> +
> + /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */
> + if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) {
> + continue;
> + }
> +
> + if (i < 5) {
> + slog("Parsing cpu stat from %s failed, see \"man proc\"",
> cpustats);
> + break;
> + }
> +
> + cpustat = g_new0(GuestCpuStats, 1);
> + cpustat->type = GUEST_CPU_STATS_TYPE_LINUX;
> +
> + linuxcpustat = &cpustat->u.q_linux;
> + linuxcpustat->cpu = atoi(&name[3]);
> + linuxcpustat->user = user * 1000 / clk_tck;
> + linuxcpustat->nice = nice * 1000 / clk_tck;
> + linuxcpustat->system = system * 1000 / clk_tck;
> + linuxcpustat->idle = idle * 1000 / clk_tck;
> +
> + if (i > 5) {
> + linuxcpustat->has_iowait = true;
> + linuxcpustat->iowait = iowait * 1000 / clk_tck;
> + }
> +
> + if (i > 6) {
> + linuxcpustat->has_irq = true;
> + linuxcpustat->irq = irq * 1000 / clk_tck;
> + linuxcpustat->has_softirq = true;
> + linuxcpustat->softirq = softirq * 1000 / clk_tck;
> + }
> +
> + if (i > 8) {
> + linuxcpustat->has_steal = true;
> + linuxcpustat->steal = steal * 1000 / clk_tck;
> + }
> +
> + if (i > 9) {
> + linuxcpustat->has_guest = true;
> + linuxcpustat->guest = guest * 1000 / clk_tck;
> + }
> +
> + if (i > 10) {
> + linuxcpustat->has_guest = true;
> + linuxcpustat->guest = guest * 1000 / clk_tck;
> + linuxcpustat->has_guestnice = true;
> + linuxcpustat->guestnice = guest_nice * 1000 / clk_tck;
> + }
> +
> + QAPI_LIST_APPEND(tail, cpustat);
> + }
> +
> + free(line);
> + fclose(fp);
> + return head;
> +}
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 98aafc45f3..5da60e65ab 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -1195,205 +1195,6 @@ GuestMemoryBlockInfo
> *qmp_guest_get_memory_block_info(Error **errp)
> return info;
> }
>
> -#define MAX_NAME_LEN 128
> -static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp)
> -{
> -#ifdef CONFIG_LINUX
> - GuestDiskStatsInfoList *head = NULL, **tail = &head;
> - const char *diskstats = "/proc/diskstats";
> - FILE *fp;
> - size_t n;
> - char *line = NULL;
> -
> - fp = fopen(diskstats, "r");
> - if (fp == NULL) {
> - error_setg_errno(errp, errno, "open(\"%s\")", diskstats);
> - return NULL;
> - }
> -
> - while (getline(&line, &n, fp) != -1) {
> - g_autofree GuestDiskStatsInfo *diskstatinfo = NULL;
> - g_autofree GuestDiskStats *diskstat = NULL;
> - char dev_name[MAX_NAME_LEN];
> - unsigned int ios_pgr, tot_ticks, rq_ticks, wr_ticks, dc_ticks,
> fl_ticks;
> - unsigned long rd_ios, rd_merges_or_rd_sec, rd_ticks_or_wr_sec,
> wr_ios;
> - unsigned long wr_merges, rd_sec_or_wr_ios, wr_sec;
> - unsigned long dc_ios, dc_merges, dc_sec, fl_ios;
> - unsigned int major, minor;
> - int i;
> -
> - i = sscanf(line, "%u %u %s %lu %lu %lu"
> - "%lu %lu %lu %lu %u %u %u %u"
> - "%lu %lu %lu %u %lu %u",
> - &major, &minor, dev_name,
> - &rd_ios, &rd_merges_or_rd_sec, &rd_sec_or_wr_ios,
> - &rd_ticks_or_wr_sec, &wr_ios, &wr_merges, &wr_sec,
> - &wr_ticks, &ios_pgr, &tot_ticks, &rq_ticks,
> - &dc_ios, &dc_merges, &dc_sec, &dc_ticks,
> - &fl_ios, &fl_ticks);
> -
> - if (i < 7) {
> - continue;
> - }
> -
> - diskstatinfo = g_new0(GuestDiskStatsInfo, 1);
> - diskstatinfo->name = g_strdup(dev_name);
> - diskstatinfo->major = major;
> - diskstatinfo->minor = minor;
> -
> - diskstat = g_new0(GuestDiskStats, 1);
> - if (i == 7) {
> - diskstat->has_read_ios = true;
> - diskstat->read_ios = rd_ios;
> - diskstat->has_read_sectors = true;
> - diskstat->read_sectors = rd_merges_or_rd_sec;
> - diskstat->has_write_ios = true;
> - diskstat->write_ios = rd_sec_or_wr_ios;
> - diskstat->has_write_sectors = true;
> - diskstat->write_sectors = rd_ticks_or_wr_sec;
> - }
> - if (i >= 14) {
> - diskstat->has_read_ios = true;
> - diskstat->read_ios = rd_ios;
> - diskstat->has_read_sectors = true;
> - diskstat->read_sectors = rd_sec_or_wr_ios;
> - diskstat->has_read_merges = true;
> - diskstat->read_merges = rd_merges_or_rd_sec;
> - diskstat->has_read_ticks = true;
> - diskstat->read_ticks = rd_ticks_or_wr_sec;
> - diskstat->has_write_ios = true;
> - diskstat->write_ios = wr_ios;
> - diskstat->has_write_sectors = true;
> - diskstat->write_sectors = wr_sec;
> - diskstat->has_write_merges = true;
> - diskstat->write_merges = wr_merges;
> - diskstat->has_write_ticks = true;
> - diskstat->write_ticks = wr_ticks;
> - diskstat->has_ios_pgr = true;
> - diskstat->ios_pgr = ios_pgr;
> - diskstat->has_total_ticks = true;
> - diskstat->total_ticks = tot_ticks;
> - diskstat->has_weight_ticks = true;
> - diskstat->weight_ticks = rq_ticks;
> - }
> - if (i >= 18) {
> - diskstat->has_discard_ios = true;
> - diskstat->discard_ios = dc_ios;
> - diskstat->has_discard_merges = true;
> - diskstat->discard_merges = dc_merges;
> - diskstat->has_discard_sectors = true;
> - diskstat->discard_sectors = dc_sec;
> - diskstat->has_discard_ticks = true;
> - diskstat->discard_ticks = dc_ticks;
> - }
> - if (i >= 20) {
> - diskstat->has_flush_ios = true;
> - diskstat->flush_ios = fl_ios;
> - diskstat->has_flush_ticks = true;
> - diskstat->flush_ticks = fl_ticks;
> - }
> -
> - diskstatinfo->stats = g_steal_pointer(&diskstat);
> - QAPI_LIST_APPEND(tail, diskstatinfo);
> - diskstatinfo = NULL;
> - }
> - free(line);
> - fclose(fp);
> - return head;
> -#else
> - g_debug("disk stats reporting available only for Linux");
> - return NULL;
> -#endif
> -}
> -
> -GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
> -{
> - return guest_get_diskstats(errp);
> -}
> -
> -GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
> -{
> - GuestCpuStatsList *head = NULL, **tail = &head;
> - const char *cpustats = "/proc/stat";
> - int clk_tck = sysconf(_SC_CLK_TCK);
> - FILE *fp;
> - size_t n;
> - char *line = NULL;
> -
> - fp = fopen(cpustats, "r");
> - if (fp == NULL) {
> - error_setg_errno(errp, errno, "open(\"%s\")", cpustats);
> - return NULL;
> - }
> -
> - while (getline(&line, &n, fp) != -1) {
> - GuestCpuStats *cpustat = NULL;
> - GuestLinuxCpuStats *linuxcpustat;
> - int i;
> - unsigned long user, system, idle, iowait, irq, softirq, steal,
> guest;
> - unsigned long nice, guest_nice;
> - char name[64];
> -
> - i = sscanf(line, "%s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
> - name, &user, &nice, &system, &idle, &iowait, &irq,
> &softirq,
> - &steal, &guest, &guest_nice);
> -
> - /* drop "cpu 1 2 3 ...", get "cpuX 1 2 3 ..." only */
> - if ((i == EOF) || strncmp(name, "cpu", 3) || (name[3] == '\0')) {
> - continue;
> - }
> -
> - if (i < 5) {
> - slog("Parsing cpu stat from %s failed, see \"man proc\"",
> cpustats);
> - break;
> - }
> -
> - cpustat = g_new0(GuestCpuStats, 1);
> - cpustat->type = GUEST_CPU_STATS_TYPE_LINUX;
> -
> - linuxcpustat = &cpustat->u.q_linux;
> - linuxcpustat->cpu = atoi(&name[3]);
> - linuxcpustat->user = user * 1000 / clk_tck;
> - linuxcpustat->nice = nice * 1000 / clk_tck;
> - linuxcpustat->system = system * 1000 / clk_tck;
> - linuxcpustat->idle = idle * 1000 / clk_tck;
> -
> - if (i > 5) {
> - linuxcpustat->has_iowait = true;
> - linuxcpustat->iowait = iowait * 1000 / clk_tck;
> - }
> -
> - if (i > 6) {
> - linuxcpustat->has_irq = true;
> - linuxcpustat->irq = irq * 1000 / clk_tck;
> - linuxcpustat->has_softirq = true;
> - linuxcpustat->softirq = softirq * 1000 / clk_tck;
> - }
> -
> - if (i > 8) {
> - linuxcpustat->has_steal = true;
> - linuxcpustat->steal = steal * 1000 / clk_tck;
> - }
> -
> - if (i > 9) {
> - linuxcpustat->has_guest = true;
> - linuxcpustat->guest = guest * 1000 / clk_tck;
> - }
> -
> - if (i > 10) {
> - linuxcpustat->has_guest = true;
> - linuxcpustat->guest = guest * 1000 / clk_tck;
> - linuxcpustat->has_guestnice = true;
> - linuxcpustat->guestnice = guest_nice * 1000 / clk_tck;
> - }
> -
> - QAPI_LIST_APPEND(tail, cpustat);
> - }
> -
> - free(line);
> - fclose(fp);
> - return head;
> -}
>
> #else /* defined(__linux__) */
>
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 20274 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 06/22] qga: move linux memory block command impls to commands-linux.c
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
` (16 preceding siblings ...)
2024-07-03 8:26 ` [PATCH v2 06/22] qga: move linux memory block command impls to commands-linux.c Philippe Mathieu-Daudé
@ 2024-07-12 8:34 ` Konstantin Kostiuk
17 siblings, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:34 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 22043 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> The qmp_guest_{set,get}_{memory_blocks,block_info} command impls in
> commands-posix.c are surrounded by '#ifdef __linux__' so should
> instead live in commands-linux.c
>
> This also removes a "#ifdef CONFIG_LINUX" that was nested inside
> a "#ifdef __linux__".
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 308 ++++++++++++++++++++++++++++++++++++++++++
> qga/commands-posix.c | 311 +------------------------------------------
> 2 files changed, 309 insertions(+), 310 deletions(-)
>
> diff --git a/qga/commands-linux.c b/qga/commands-linux.c
> index c0e8bd4062..73b13fbaf6 100644
> --- a/qga/commands-linux.c
> +++ b/qga/commands-linux.c
> @@ -1595,6 +1595,314 @@ int64_t
> qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp)
> return processed;
> }
>
> +
> +static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
> + int size, Error **errp)
> +{
> + int fd;
> + int res;
> +
> + errno = 0;
> + fd = openat(dirfd, pathname, O_RDONLY);
> + if (fd == -1) {
> + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
> + return;
> + }
> +
> + res = pread(fd, buf, size, 0);
> + if (res == -1) {
> + error_setg_errno(errp, errno, "pread sysfs file \"%s\"",
> pathname);
> + } else if (res == 0) {
> + error_setg(errp, "pread sysfs file \"%s\": unexpected EOF",
> pathname);
> + }
> + close(fd);
> +}
> +
> +static void ga_write_sysfs_file(int dirfd, const char *pathname,
> + const char *buf, int size, Error **errp)
> +{
> + int fd;
> +
> + errno = 0;
> + fd = openat(dirfd, pathname, O_WRONLY);
> + if (fd == -1) {
> + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
> + return;
> + }
> +
> + if (pwrite(fd, buf, size, 0) == -1) {
> + error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"",
> pathname);
> + }
> +
> + close(fd);
> +}
> +
> +/* Transfer online/offline status between @mem_blk and the guest system.
> + *
> + * On input either @errp or *@errp must be NULL.
> + *
> + * In system-to-@mem_blk direction, the following @mem_blk fields are
> accessed:
> + * - R: mem_blk->phys_index
> + * - W: mem_blk->online
> + * - W: mem_blk->can_offline
> + *
> + * In @mem_blk-to-system direction, the following @mem_blk fields are
> accessed:
> + * - R: mem_blk->phys_index
> + * - R: mem_blk->online
> + *- R: mem_blk->can_offline
> + * Written members remain unmodified on error.
> + */
> +static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool
> sys2memblk,
> + GuestMemoryBlockResponse *result,
> + Error **errp)
> +{
> + char *dirpath;
> + int dirfd;
> + char *status;
> + Error *local_err = NULL;
> +
> + if (!sys2memblk) {
> + DIR *dp;
> +
> + if (!result) {
> + error_setg(errp, "Internal error, 'result' should not be
> NULL");
> + return;
> + }
> + errno = 0;
> + dp = opendir("/sys/devices/system/memory/");
> + /* if there is no 'memory' directory in sysfs,
> + * we think this VM does not support online/offline memory block,
> + * any other solution?
> + */
> + if (!dp) {
> + if (errno == ENOENT) {
> + result->response =
> +
> GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
> + }
> + goto out1;
> + }
> + closedir(dp);
> + }
> +
> + dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64
> "/",
> + mem_blk->phys_index);
> + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
> + if (dirfd == -1) {
> + if (sys2memblk) {
> + error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
> + } else {
> + if (errno == ENOENT) {
> + result->response =
> GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
> + } else {
> + result->response =
> + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
> + }
> + }
> + g_free(dirpath);
> + goto out1;
> + }
> + g_free(dirpath);
> +
> + status = g_malloc0(10);
> + ga_read_sysfs_file(dirfd, "state", status, 10, &local_err);
> + if (local_err) {
> + /* treat with sysfs file that not exist in old kernel */
> + if (errno == ENOENT) {
> + error_free(local_err);
> + if (sys2memblk) {
> + mem_blk->online = true;
> + mem_blk->can_offline = false;
> + } else if (!mem_blk->online) {
> + result->response =
> +
> GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
> + }
> + } else {
> + if (sys2memblk) {
> + error_propagate(errp, local_err);
> + } else {
> + error_free(local_err);
> + result->response =
> + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
> + }
> + }
> + goto out2;
> + }
> +
> + if (sys2memblk) {
> + char removable = '0';
> +
> + mem_blk->online = (strncmp(status, "online", 6) == 0);
> +
> + ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
> + if (local_err) {
> + /* if no 'removable' file, it doesn't support offline mem blk
> */
> + if (errno == ENOENT) {
> + error_free(local_err);
> + mem_blk->can_offline = false;
> + } else {
> + error_propagate(errp, local_err);
> + }
> + } else {
> + mem_blk->can_offline = (removable != '0');
> + }
> + } else {
> + if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
> + const char *new_state = mem_blk->online ? "online" :
> "offline";
> +
> + ga_write_sysfs_file(dirfd, "state", new_state,
> strlen(new_state),
> + &local_err);
> + if (local_err) {
> + error_free(local_err);
> + result->response =
> + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
> + goto out2;
> + }
> +
> + result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
> + result->has_error_code = false;
> + } /* otherwise pretend successful re-(on|off)-lining */
> + }
> + g_free(status);
> + close(dirfd);
> + return;
> +
> +out2:
> + g_free(status);
> + close(dirfd);
> +out1:
> + if (!sys2memblk) {
> + result->has_error_code = true;
> + result->error_code = errno;
> + }
> +}
> +
> +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
> +{
> + GuestMemoryBlockList *head, **tail;
> + Error *local_err = NULL;
> + struct dirent *de;
> + DIR *dp;
> +
> + head = NULL;
> + tail = &head;
> +
> + dp = opendir("/sys/devices/system/memory/");
> + if (!dp) {
> + /* it's ok if this happens to be a system that doesn't expose
> + * memory blocks via sysfs, but otherwise we should report
> + * an error
> + */
> + if (errno != ENOENT) {
> + error_setg_errno(errp, errno, "Can't open directory"
> + "\"/sys/devices/system/memory/\"");
> + }
> + return NULL;
> + }
> +
> + /* Note: the phys_index of memory block may be discontinuous,
> + * this is because a memblk is the unit of the Sparse Memory design,
> which
> + * allows discontinuous memory ranges (ex. NUMA), so here we should
> + * traverse the memory block directory.
> + */
> + while ((de = readdir(dp)) != NULL) {
> + GuestMemoryBlock *mem_blk;
> +
> + if ((strncmp(de->d_name, "memory", 6) != 0) ||
> + !(de->d_type & DT_DIR)) {
> + continue;
> + }
> +
> + mem_blk = g_malloc0(sizeof *mem_blk);
> + /* The d_name is "memoryXXX", phys_index is block id, same as
> XXX */
> + mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
> + mem_blk->has_can_offline = true; /* lolspeak ftw */
> + transfer_memory_block(mem_blk, true, NULL, &local_err);
> + if (local_err) {
> + break;
> + }
> +
> + QAPI_LIST_APPEND(tail, mem_blk);
> + }
> +
> + closedir(dp);
> + if (local_err == NULL) {
> + /* there's no guest with zero memory blocks */
> + if (head == NULL) {
> + error_setg(errp, "guest reported zero memory blocks!");
> + }
> + return head;
> + }
> +
> + qapi_free_GuestMemoryBlockList(head);
> + error_propagate(errp, local_err);
> + return NULL;
> +}
> +
> +GuestMemoryBlockResponseList *
> +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
> +{
> + GuestMemoryBlockResponseList *head, **tail;
> + Error *local_err = NULL;
> +
> + head = NULL;
> + tail = &head;
> +
> + while (mem_blks != NULL) {
> + GuestMemoryBlockResponse *result;
> + GuestMemoryBlock *current_mem_blk = mem_blks->value;
> +
> + result = g_malloc0(sizeof(*result));
> + result->phys_index = current_mem_blk->phys_index;
> + transfer_memory_block(current_mem_blk, false, result, &local_err);
> + if (local_err) { /* should never happen */
> + goto err;
> + }
> +
> + QAPI_LIST_APPEND(tail, result);
> + mem_blks = mem_blks->next;
> + }
> +
> + return head;
> +err:
> + qapi_free_GuestMemoryBlockResponseList(head);
> + error_propagate(errp, local_err);
> + return NULL;
> +}
> +
> +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
> +{
> + Error *local_err = NULL;
> + char *dirpath;
> + int dirfd;
> + char *buf;
> + GuestMemoryBlockInfo *info;
> +
> + dirpath = g_strdup_printf("/sys/devices/system/memory/");
> + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
> + if (dirfd == -1) {
> + error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
> + g_free(dirpath);
> + return NULL;
> + }
> + g_free(dirpath);
> +
> + buf = g_malloc0(20);
> + ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err);
> + close(dirfd);
> + if (local_err) {
> + g_free(buf);
> + error_propagate(errp, local_err);
> + return NULL;
> + }
> +
> + info = g_new0(GuestMemoryBlockInfo, 1);
> + info->size = strtol(buf, NULL, 16); /* the unit is bytes */
> +
> + g_free(buf);
> +
> + return info;
> +}
> +
> #define MAX_NAME_LEN 128
> static GuestDiskStatsInfoList *guest_get_diskstats(Error **errp)
> {
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 5da60e65ab..2a3bef7445 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -887,316 +887,7 @@ void qmp_guest_set_user_password(const char
> *username,
> }
> #endif /* __linux__ || __FreeBSD__ */
>
> -#ifdef __linux__
> -static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf,
> - int size, Error **errp)
> -{
> - int fd;
> - int res;
> -
> - errno = 0;
> - fd = openat(dirfd, pathname, O_RDONLY);
> - if (fd == -1) {
> - error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
> - return;
> - }
> -
> - res = pread(fd, buf, size, 0);
> - if (res == -1) {
> - error_setg_errno(errp, errno, "pread sysfs file \"%s\"",
> pathname);
> - } else if (res == 0) {
> - error_setg(errp, "pread sysfs file \"%s\": unexpected EOF",
> pathname);
> - }
> - close(fd);
> -}
> -
> -static void ga_write_sysfs_file(int dirfd, const char *pathname,
> - const char *buf, int size, Error **errp)
> -{
> - int fd;
> -
> - errno = 0;
> - fd = openat(dirfd, pathname, O_WRONLY);
> - if (fd == -1) {
> - error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname);
> - return;
> - }
> -
> - if (pwrite(fd, buf, size, 0) == -1) {
> - error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"",
> pathname);
> - }
> -
> - close(fd);
> -}
> -
> -/* Transfer online/offline status between @mem_blk and the guest system.
> - *
> - * On input either @errp or *@errp must be NULL.
> - *
> - * In system-to-@mem_blk direction, the following @mem_blk fields are
> accessed:
> - * - R: mem_blk->phys_index
> - * - W: mem_blk->online
> - * - W: mem_blk->can_offline
> - *
> - * In @mem_blk-to-system direction, the following @mem_blk fields are
> accessed:
> - * - R: mem_blk->phys_index
> - * - R: mem_blk->online
> - *- R: mem_blk->can_offline
> - * Written members remain unmodified on error.
> - */
> -static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool
> sys2memblk,
> - GuestMemoryBlockResponse *result,
> - Error **errp)
> -{
> - char *dirpath;
> - int dirfd;
> - char *status;
> - Error *local_err = NULL;
> -
> - if (!sys2memblk) {
> - DIR *dp;
> -
> - if (!result) {
> - error_setg(errp, "Internal error, 'result' should not be
> NULL");
> - return;
> - }
> - errno = 0;
> - dp = opendir("/sys/devices/system/memory/");
> - /* if there is no 'memory' directory in sysfs,
> - * we think this VM does not support online/offline memory block,
> - * any other solution?
> - */
> - if (!dp) {
> - if (errno == ENOENT) {
> - result->response =
> -
> GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
> - }
> - goto out1;
> - }
> - closedir(dp);
> - }
> -
> - dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64
> "/",
> - mem_blk->phys_index);
> - dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
> - if (dirfd == -1) {
> - if (sys2memblk) {
> - error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
> - } else {
> - if (errno == ENOENT) {
> - result->response =
> GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND;
> - } else {
> - result->response =
> - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
> - }
> - }
> - g_free(dirpath);
> - goto out1;
> - }
> - g_free(dirpath);
> -
> - status = g_malloc0(10);
> - ga_read_sysfs_file(dirfd, "state", status, 10, &local_err);
> - if (local_err) {
> - /* treat with sysfs file that not exist in old kernel */
> - if (errno == ENOENT) {
> - error_free(local_err);
> - if (sys2memblk) {
> - mem_blk->online = true;
> - mem_blk->can_offline = false;
> - } else if (!mem_blk->online) {
> - result->response =
> -
> GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED;
> - }
> - } else {
> - if (sys2memblk) {
> - error_propagate(errp, local_err);
> - } else {
> - error_free(local_err);
> - result->response =
> - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
> - }
> - }
> - goto out2;
> - }
> -
> - if (sys2memblk) {
> - char removable = '0';
> -
> - mem_blk->online = (strncmp(status, "online", 6) == 0);
> -
> - ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err);
> - if (local_err) {
> - /* if no 'removable' file, it doesn't support offline mem blk
> */
> - if (errno == ENOENT) {
> - error_free(local_err);
> - mem_blk->can_offline = false;
> - } else {
> - error_propagate(errp, local_err);
> - }
> - } else {
> - mem_blk->can_offline = (removable != '0');
> - }
> - } else {
> - if (mem_blk->online != (strncmp(status, "online", 6) == 0)) {
> - const char *new_state = mem_blk->online ? "online" :
> "offline";
> -
> - ga_write_sysfs_file(dirfd, "state", new_state,
> strlen(new_state),
> - &local_err);
> - if (local_err) {
> - error_free(local_err);
> - result->response =
> - GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED;
> - goto out2;
> - }
> -
> - result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS;
> - result->has_error_code = false;
> - } /* otherwise pretend successful re-(on|off)-lining */
> - }
> - g_free(status);
> - close(dirfd);
> - return;
> -
> -out2:
> - g_free(status);
> - close(dirfd);
> -out1:
> - if (!sys2memblk) {
> - result->has_error_code = true;
> - result->error_code = errno;
> - }
> -}
> -
> -GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
> -{
> - GuestMemoryBlockList *head, **tail;
> - Error *local_err = NULL;
> - struct dirent *de;
> - DIR *dp;
> -
> - head = NULL;
> - tail = &head;
> -
> - dp = opendir("/sys/devices/system/memory/");
> - if (!dp) {
> - /* it's ok if this happens to be a system that doesn't expose
> - * memory blocks via sysfs, but otherwise we should report
> - * an error
> - */
> - if (errno != ENOENT) {
> - error_setg_errno(errp, errno, "Can't open directory"
> - "\"/sys/devices/system/memory/\"");
> - }
> - return NULL;
> - }
> -
> - /* Note: the phys_index of memory block may be discontinuous,
> - * this is because a memblk is the unit of the Sparse Memory design,
> which
> - * allows discontinuous memory ranges (ex. NUMA), so here we should
> - * traverse the memory block directory.
> - */
> - while ((de = readdir(dp)) != NULL) {
> - GuestMemoryBlock *mem_blk;
> -
> - if ((strncmp(de->d_name, "memory", 6) != 0) ||
> - !(de->d_type & DT_DIR)) {
> - continue;
> - }
> -
> - mem_blk = g_malloc0(sizeof *mem_blk);
> - /* The d_name is "memoryXXX", phys_index is block id, same as
> XXX */
> - mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10);
> - mem_blk->has_can_offline = true; /* lolspeak ftw */
> - transfer_memory_block(mem_blk, true, NULL, &local_err);
> - if (local_err) {
> - break;
> - }
> -
> - QAPI_LIST_APPEND(tail, mem_blk);
> - }
> -
> - closedir(dp);
> - if (local_err == NULL) {
> - /* there's no guest with zero memory blocks */
> - if (head == NULL) {
> - error_setg(errp, "guest reported zero memory blocks!");
> - }
> - return head;
> - }
> -
> - qapi_free_GuestMemoryBlockList(head);
> - error_propagate(errp, local_err);
> - return NULL;
> -}
> -
> -GuestMemoryBlockResponseList *
> -qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
> -{
> - GuestMemoryBlockResponseList *head, **tail;
> - Error *local_err = NULL;
> -
> - head = NULL;
> - tail = &head;
> -
> - while (mem_blks != NULL) {
> - GuestMemoryBlockResponse *result;
> - GuestMemoryBlock *current_mem_blk = mem_blks->value;
> -
> - result = g_malloc0(sizeof(*result));
> - result->phys_index = current_mem_blk->phys_index;
> - transfer_memory_block(current_mem_blk, false, result, &local_err);
> - if (local_err) { /* should never happen */
> - goto err;
> - }
> -
> - QAPI_LIST_APPEND(tail, result);
> - mem_blks = mem_blks->next;
> - }
> -
> - return head;
> -err:
> - qapi_free_GuestMemoryBlockResponseList(head);
> - error_propagate(errp, local_err);
> - return NULL;
> -}
> -
> -GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
> -{
> - Error *local_err = NULL;
> - char *dirpath;
> - int dirfd;
> - char *buf;
> - GuestMemoryBlockInfo *info;
> -
> - dirpath = g_strdup_printf("/sys/devices/system/memory/");
> - dirfd = open(dirpath, O_RDONLY | O_DIRECTORY);
> - if (dirfd == -1) {
> - error_setg_errno(errp, errno, "open(\"%s\")", dirpath);
> - g_free(dirpath);
> - return NULL;
> - }
> - g_free(dirpath);
> -
> - buf = g_malloc0(20);
> - ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err);
> - close(dirfd);
> - if (local_err) {
> - g_free(buf);
> - error_propagate(errp, local_err);
> - return NULL;
> - }
> -
> - info = g_new0(GuestMemoryBlockInfo, 1);
> - info->size = strtol(buf, NULL, 16); /* the unit is bytes */
> -
> - g_free(buf);
> -
> - return info;
> -}
> -
> -
> -#else /* defined(__linux__) */
> +#ifndef __linux__
>
> void qmp_guest_suspend_disk(Error **errp)
> {
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 26834 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows
2024-06-13 15:43 ` [PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows Daniel P. Berrangé
2024-07-03 8:30 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:34 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:34 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 8850 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the commands on Windows.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been
> found"}
>
> This also fixes an accidental inconsistency where some commands
> (guest-get-diskstats & guest-get-cpustats) are implemented as
> stubs, yet not added to the blockedrpc list. Those change their
> error message from
>
> {"class": "GenericError, "desc": "this feature or command is not
> currently supported"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been
> found"}
>
> The final additional benefit is that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 2 +-
> qga/commands-win32.c | 56 +-------------------------------------------
> qga/qapi-schema.json | 45 +++++++++++++++++++++++------------
> 3 files changed, 32 insertions(+), 71 deletions(-)
>
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 2a3bef7445..0dd8555867 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -1280,7 +1280,7 @@ GList *ga_command_init_blockedrpcs(GList
> *blockedrpcs)
> "guest-get-memory-blocks", "guest-set-memory-blocks",
> "guest-get-memory-block-info",
> NULL};
> - char **p = (char **)list;
> + const char **p = list;
>
> while (*p) {
> blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
> diff --git a/qga/commands-win32.c b/qga/commands-win32.c
> index 9fe670d5b4..2533e4c748 100644
> --- a/qga/commands-win32.c
> +++ b/qga/commands-win32.c
> @@ -1494,11 +1494,6 @@ out:
> }
> }
>
> -void qmp_guest_suspend_hybrid(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> -}
> -
> static IP_ADAPTER_ADDRESSES *guest_get_adapters_addresses(Error **errp)
> {
> IP_ADAPTER_ADDRESSES *adptr_addrs = NULL;
> @@ -1862,12 +1857,6 @@ GuestLogicalProcessorList
> *qmp_guest_get_vcpus(Error **errp)
> return NULL;
> }
>
> -int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error
> **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return -1;
> -}
> -
> static gchar *
> get_net_error_message(gint error)
> {
> @@ -1969,46 +1958,15 @@ done:
> g_free(rawpasswddata);
> }
>
> -GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> -GuestMemoryBlockResponseList *
> -qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> -GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> /* add unsupported commands to the list of blocked RPCs */
> GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
> {
> - const char *list_unsupported[] = {
> - "guest-suspend-hybrid",
> - "guest-set-vcpus",
> - "guest-get-memory-blocks", "guest-set-memory-blocks",
> - "guest-get-memory-block-info",
> - NULL};
> - char **p = (char **)list_unsupported;
> -
> - while (*p) {
> - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
> - }
> -
> if (!vss_init(true)) {
> g_debug("vss_init failed, vss commands are going to be disabled");
> const char *list[] = {
> "guest-get-fsinfo", "guest-fsfreeze-status",
> "guest-fsfreeze-freeze", "guest-fsfreeze-thaw", NULL};
> - p = (char **)list;
> + char **p = (char **)list;
>
> while (*p) {
> blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
> @@ -2505,15 +2463,3 @@ char *qga_get_host_name(Error **errp)
>
> return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL);
> }
> -
> -GuestDiskStatsInfoList *qmp_guest_get_diskstats(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> -GuestCpuStatsList *qmp_guest_get_cpustats(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index b3de1fb6b3..b91456e9ad 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -636,7 +636,8 @@
> #
> # Since: 1.1
> ##
> -{ 'command': 'guest-suspend-hybrid', 'success-response': false }
> +{ 'command': 'guest-suspend-hybrid', 'success-response': false,
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @GuestIpAddressType:
> @@ -806,7 +807,8 @@
> ##
> { 'command': 'guest-set-vcpus',
> 'data': {'vcpus': ['GuestLogicalProcessor'] },
> - 'returns': 'int' }
> + 'returns': 'int',
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @GuestDiskBusType:
> @@ -1099,7 +1101,8 @@
> { 'struct': 'GuestMemoryBlock',
> 'data': {'phys-index': 'uint64',
> 'online': 'bool',
> - '*can-offline': 'bool'} }
> + '*can-offline': 'bool'},
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @guest-get-memory-blocks:
> @@ -1115,7 +1118,8 @@
> # Since: 2.3
> ##
> { 'command': 'guest-get-memory-blocks',
> - 'returns': ['GuestMemoryBlock'] }
> + 'returns': ['GuestMemoryBlock'],
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @GuestMemoryBlockResponseType:
> @@ -1138,7 +1142,8 @@
> ##
> { 'enum': 'GuestMemoryBlockResponseType',
> 'data': ['success', 'not-found', 'operation-not-supported',
> - 'operation-failed'] }
> + 'operation-failed'],
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @GuestMemoryBlockResponse:
> @@ -1156,7 +1161,8 @@
> { 'struct': 'GuestMemoryBlockResponse',
> 'data': { 'phys-index': 'uint64',
> 'response': 'GuestMemoryBlockResponseType',
> - '*error-code': 'int' }}
> + '*error-code': 'int' },
> + 'if': 'CONFIG_POSIX'}
>
> ##
> # @guest-set-memory-blocks:
> @@ -1187,7 +1193,8 @@
> ##
> { 'command': 'guest-set-memory-blocks',
> 'data': {'mem-blks': ['GuestMemoryBlock'] },
> - 'returns': ['GuestMemoryBlockResponse'] }
> + 'returns': ['GuestMemoryBlockResponse'],
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @GuestMemoryBlockInfo:
> @@ -1199,7 +1206,8 @@
> # Since: 2.3
> ##
> { 'struct': 'GuestMemoryBlockInfo',
> - 'data': {'size': 'uint64'} }
> + 'data': {'size': 'uint64'},
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @guest-get-memory-block-info:
> @@ -1211,7 +1219,8 @@
> # Since: 2.3
> ##
> { 'command': 'guest-get-memory-block-info',
> - 'returns': 'GuestMemoryBlockInfo' }
> + 'returns': 'GuestMemoryBlockInfo',
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @GuestExecStatus:
> @@ -1702,7 +1711,8 @@
> 'data': {'name': 'str',
> 'major': 'uint64',
> 'minor': 'uint64',
> - 'stats': 'GuestDiskStats' } }
> + 'stats': 'GuestDiskStats' },
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @guest-get-diskstats:
> @@ -1714,7 +1724,8 @@
> # Since: 7.1
> ##
> { 'command': 'guest-get-diskstats',
> - 'returns': ['GuestDiskStatsInfo']
> + 'returns': ['GuestDiskStatsInfo'],
> + 'if': 'CONFIG_POSIX'
> }
>
> ##
> @@ -1727,7 +1738,8 @@
> # Since: 7.1
> ##
> { 'enum': 'GuestCpuStatsType',
> - 'data': [ 'linux' ] }
> + 'data': [ 'linux' ],
> + 'if': 'CONFIG_POSIX' }
>
>
> ##
> @@ -1772,7 +1784,8 @@
> '*steal': 'uint64',
> '*guest': 'uint64',
> '*guestnice': 'uint64'
> - } }
> + },
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @GuestCpuStats:
> @@ -1786,7 +1799,8 @@
> { 'union': 'GuestCpuStats',
> 'base': { 'type': 'GuestCpuStatsType' },
> 'discriminator': 'type',
> - 'data': { 'linux': 'GuestLinuxCpuStats' } }
> + 'data': { 'linux': 'GuestLinuxCpuStats' },
> + 'if': 'CONFIG_POSIX' }
>
> ##
> # @guest-get-cpustats:
> @@ -1798,5 +1812,6 @@
> # Since: 7.1
> ##
> { 'command': 'guest-get-cpustats',
> - 'returns': ['GuestCpuStats']
> + 'returns': ['GuestCpuStats'],
> + 'if': 'CONFIG_POSIX'
> }
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 11752 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX
2024-06-13 15:43 ` [PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX Daniel P. Berrangé
2024-07-03 8:31 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:35 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:35 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 7036 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the commands on non-Linux POSIX
> platforms
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been
> found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 66 --------------------------------------------
> qga/qapi-schema.json | 30 +++++++++++---------
> 2 files changed, 17 insertions(+), 79 deletions(-)
>
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 0dd8555867..559d71ffae 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -887,56 +887,6 @@ void qmp_guest_set_user_password(const char *username,
> }
> #endif /* __linux__ || __FreeBSD__ */
>
> -#ifndef __linux__
> -
> -void qmp_guest_suspend_disk(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> -}
> -
> -void qmp_guest_suspend_ram(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> -}
> -
> -void qmp_guest_suspend_hybrid(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> -}
> -
> -GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> -int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error
> **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return -1;
> -}
> -
> -GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> -GuestMemoryBlockResponseList *
> -qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> -GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> -#endif
> -
> #ifdef HAVE_GETIFADDRS
> static GuestNetworkInterface *
> guest_find_interface(GuestNetworkInterfaceList *head,
> @@ -1272,22 +1222,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum,
> Error **errp)
> /* add unsupported commands to the list of blocked RPCs */
> GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
> {
> -#if !defined(__linux__)
> - {
> - const char *list[] = {
> - "guest-suspend-disk", "guest-suspend-ram",
> - "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
> - "guest-get-memory-blocks", "guest-set-memory-blocks",
> - "guest-get-memory-block-info",
> - NULL};
> - const char **p = list;
> -
> - while (*p) {
> - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
> - }
> - }
> -#endif
> -
> #if !defined(HAVE_GETIFADDRS)
> blockedrpcs = g_list_append(blockedrpcs,
> g_strdup("guest-network-get-interfaces"));
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index b91456e9ad..d164c30ec3 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -565,7 +565,8 @@
> #
> # Since: 1.1
> ##
> -{ 'command': 'guest-suspend-disk', 'success-response': false }
> +{ 'command': 'guest-suspend-disk', 'success-response': false,
> + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } }
>
> ##
> # @guest-suspend-ram:
> @@ -601,7 +602,8 @@
> #
> # Since: 1.1
> ##
> -{ 'command': 'guest-suspend-ram', 'success-response': false }
> +{ 'command': 'guest-suspend-ram', 'success-response': false,
> + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } }
>
> ##
> # @guest-suspend-hybrid:
> @@ -637,7 +639,7 @@
> # Since: 1.1
> ##
> { 'command': 'guest-suspend-hybrid', 'success-response': false,
> - 'if': 'CONFIG_POSIX' }
> + 'if': 'CONFIG_LINUX' }
>
> ##
> # @GuestIpAddressType:
> @@ -750,7 +752,8 @@
> { 'struct': 'GuestLogicalProcessor',
> 'data': {'logical-id': 'int',
> 'online': 'bool',
> - '*can-offline': 'bool'} }
> + '*can-offline': 'bool'},
> + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } }
>
> ##
> # @guest-get-vcpus:
> @@ -765,7 +768,8 @@
> # Since: 1.5
> ##
> { 'command': 'guest-get-vcpus',
> - 'returns': ['GuestLogicalProcessor'] }
> + 'returns': ['GuestLogicalProcessor'],
> + 'if': { 'any': ['CONFIG_LINUX', 'CONFIG_WIN32'] } }
>
> ##
> # @guest-set-vcpus:
> @@ -808,7 +812,7 @@
> { 'command': 'guest-set-vcpus',
> 'data': {'vcpus': ['GuestLogicalProcessor'] },
> 'returns': 'int',
> - 'if': 'CONFIG_POSIX' }
> + 'if': 'CONFIG_LINUX' }
>
> ##
> # @GuestDiskBusType:
> @@ -1102,7 +1106,7 @@
> 'data': {'phys-index': 'uint64',
> 'online': 'bool',
> '*can-offline': 'bool'},
> - 'if': 'CONFIG_POSIX' }
> + 'if': 'CONFIG_LINUX' }
>
> ##
> # @guest-get-memory-blocks:
> @@ -1119,7 +1123,7 @@
> ##
> { 'command': 'guest-get-memory-blocks',
> 'returns': ['GuestMemoryBlock'],
> - 'if': 'CONFIG_POSIX' }
> + 'if': 'CONFIG_LINUX' }
>
> ##
> # @GuestMemoryBlockResponseType:
> @@ -1143,7 +1147,7 @@
> { 'enum': 'GuestMemoryBlockResponseType',
> 'data': ['success', 'not-found', 'operation-not-supported',
> 'operation-failed'],
> - 'if': 'CONFIG_POSIX' }
> + 'if': 'CONFIG_LINUX' }
>
> ##
> # @GuestMemoryBlockResponse:
> @@ -1162,7 +1166,7 @@
> 'data': { 'phys-index': 'uint64',
> 'response': 'GuestMemoryBlockResponseType',
> '*error-code': 'int' },
> - 'if': 'CONFIG_POSIX'}
> + 'if': 'CONFIG_LINUX'}
>
> ##
> # @guest-set-memory-blocks:
> @@ -1194,7 +1198,7 @@
> { 'command': 'guest-set-memory-blocks',
> 'data': {'mem-blks': ['GuestMemoryBlock'] },
> 'returns': ['GuestMemoryBlockResponse'],
> - 'if': 'CONFIG_POSIX' }
> + 'if': 'CONFIG_LINUX' }
>
> ##
> # @GuestMemoryBlockInfo:
> @@ -1207,7 +1211,7 @@
> ##
> { 'struct': 'GuestMemoryBlockInfo',
> 'data': {'size': 'uint64'},
> - 'if': 'CONFIG_POSIX' }
> + 'if': 'CONFIG_LINUX' }
>
> ##
> # @guest-get-memory-block-info:
> @@ -1220,7 +1224,7 @@
> ##
> { 'command': 'guest-get-memory-block-info',
> 'returns': 'GuestMemoryBlockInfo',
> - 'if': 'CONFIG_POSIX' }
> + 'if': 'CONFIG_LINUX' }
>
> ##
> # @GuestExecStatus:
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 9402 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs
2024-06-13 15:43 ` [PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs Daniel P. Berrangé
2024-07-03 8:32 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:35 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:35 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 3730 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> Rather than creating stubs for every comamnd that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the network interface command on
> POSIX platforms lacking getifaddrs().
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been
> found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 13 -------------
> qga/qapi-schema.json | 15 ++++++++++-----
> 2 files changed, 10 insertions(+), 18 deletions(-)
>
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 559d71ffae..09d08ee2ca 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -1142,14 +1142,6 @@ error:
> return NULL;
> }
>
> -#else
> -
> -GuestNetworkInterfaceList *qmp_guest_network_get_interfaces(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> #endif /* HAVE_GETIFADDRS */
>
> #if !defined(CONFIG_FSFREEZE)
> @@ -1222,11 +1214,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum,
> Error **errp)
> /* add unsupported commands to the list of blocked RPCs */
> GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
> {
> -#if !defined(HAVE_GETIFADDRS)
> - blockedrpcs = g_list_append(blockedrpcs,
> - g_strdup("guest-network-get-interfaces"));
> -#endif
> -
> #if !defined(CONFIG_FSFREEZE)
> {
> const char *list[] = {
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index d164c30ec3..c37c904aae 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -653,7 +653,8 @@
> # Since: 1.1
> ##
> { 'enum': 'GuestIpAddressType',
> - 'data': [ 'ipv4', 'ipv6' ] }
> + 'data': [ 'ipv4', 'ipv6' ],
> + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } }
>
> ##
> # @GuestIpAddress:
> @@ -669,7 +670,8 @@
> { 'struct': 'GuestIpAddress',
> 'data': {'ip-address': 'str',
> 'ip-address-type': 'GuestIpAddressType',
> - 'prefix': 'int'} }
> + 'prefix': 'int'},
> + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } }
>
> ##
> # @GuestNetworkInterfaceStat:
> @@ -701,7 +703,8 @@
> 'tx-packets': 'uint64',
> 'tx-errs': 'uint64',
> 'tx-dropped': 'uint64'
> - } }
> + },
> + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } }
>
> ##
> # @GuestNetworkInterface:
> @@ -721,7 +724,8 @@
> 'data': {'name': 'str',
> '*hardware-address': 'str',
> '*ip-addresses': ['GuestIpAddress'],
> - '*statistics': 'GuestNetworkInterfaceStat' } }
> + '*statistics': 'GuestNetworkInterfaceStat' },
> + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } }
>
> ##
> # @guest-network-get-interfaces:
> @@ -733,7 +737,8 @@
> # Since: 1.1
> ##
> { 'command': 'guest-network-get-interfaces',
> - 'returns': ['GuestNetworkInterface'] }
> + 'returns': ['GuestNetworkInterface'],
> + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_GETIFADDRS'] } }
>
> ##
> # @GuestLogicalProcessor:
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 5090 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows
2024-06-13 15:43 ` [PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows Daniel P. Berrangé
2024-07-03 8:35 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:37 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:37 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 3253 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the commands on non-Windows.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been
> found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 9 ---------
> qga/qapi-schema.json | 15 ++++++++++-----
> 2 files changed, 10 insertions(+), 14 deletions(-)
>
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 838dc3cf98..b7f96aa005 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -1207,8 +1207,6 @@ GList *ga_command_init_blockedrpcs(GList
> *blockedrpcs)
> blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim"));
> #endif
>
> - blockedrpcs = g_list_append(blockedrpcs,
> g_strdup("guest-get-devices"));
> -
> return blockedrpcs;
> }
>
> @@ -1419,13 +1417,6 @@ GuestOSInfo *qmp_guest_get_osinfo(Error **errp)
> return info;
> }
>
> -GuestDeviceInfoList *qmp_guest_get_devices(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> -
> - return NULL;
> -}
> -
> #ifndef HOST_NAME_MAX
> # ifdef _POSIX_HOST_NAME_MAX
> # define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index 700c5baa5a..2704b814ab 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -1527,7 +1527,8 @@
> # @pci: PCI device
> ##
> { 'enum': 'GuestDeviceType',
> - 'data': [ 'pci' ] }
> + 'data': [ 'pci' ],
> + 'if': 'CONFIG_WIN32' }
>
> ##
> # @GuestDeviceIdPCI:
> @@ -1539,7 +1540,8 @@
> # Since: 5.2
> ##
> { 'struct': 'GuestDeviceIdPCI',
> - 'data': { 'vendor-id': 'uint16', 'device-id': 'uint16' } }
> + 'data': { 'vendor-id': 'uint16', 'device-id': 'uint16' },
> + 'if': 'CONFIG_WIN32' }
>
> ##
> # @GuestDeviceId:
> @@ -1553,7 +1555,8 @@
> { 'union': 'GuestDeviceId',
> 'base': { 'type': 'GuestDeviceType' },
> 'discriminator': 'type',
> - 'data': { 'pci': 'GuestDeviceIdPCI' } }
> + 'data': { 'pci': 'GuestDeviceIdPCI' },
> + 'if': 'CONFIG_WIN32' }
>
> ##
> # @GuestDeviceInfo:
> @@ -1574,7 +1577,8 @@
> '*driver-date': 'int',
> '*driver-version': 'str',
> '*id': 'GuestDeviceId'
> - } }
> + },
> + 'if': 'CONFIG_WIN32' }
>
> ##
> # @guest-get-devices:
> @@ -1586,7 +1590,8 @@
> # Since: 5.2
> ##
> { 'command': 'guest-get-devices',
> - 'returns': ['GuestDeviceInfo'] }
> + 'returns': ['GuestDeviceInfo'],
> + 'if': 'CONFIG_WIN32' }
>
> ##
> # @GuestAuthorizedKeys:
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 4469 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze
2024-06-13 15:43 ` [PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze Daniel P. Berrangé
2024-07-03 8:37 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:37 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:37 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 4500 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the schema to fully
> exclude generation of the filesystem freezing commands on POSIX
> platforms lacking the required APIs.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been
> found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 47 --------------------------------------------
> qga/qapi-schema.json | 15 +++++++++-----
> 2 files changed, 10 insertions(+), 52 deletions(-)
>
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index b7f96aa005..9207cb7a8f 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -1144,39 +1144,6 @@ error:
>
> #endif /* HAVE_GETIFADDRS */
>
> -#if !defined(CONFIG_FSFREEZE)
> -
> -GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> -
> - return 0;
> -}
> -
> -int64_t qmp_guest_fsfreeze_freeze(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> -
> - return 0;
> -}
> -
> -int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints,
> - strList *mountpoints,
> - Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> -
> - return 0;
> -}
> -
> -int64_t qmp_guest_fsfreeze_thaw(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> -
> - return 0;
> -}
> -#endif /* CONFIG_FSFREEZE */
> -
> #if !defined(CONFIG_FSTRIM)
> GuestFilesystemTrimResponse *
> qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
> @@ -1189,20 +1156,6 @@ qmp_guest_fstrim(bool has_minimum, int64_t minimum,
> Error **errp)
> /* add unsupported commands to the list of blocked RPCs */
> GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
> {
> -#if !defined(CONFIG_FSFREEZE)
> - {
> - const char *list[] = {
> - "guest-fsfreeze-status",
> - "guest-fsfreeze-freeze", "guest-fsfreeze-freeze-list",
> - "guest-fsfreeze-thaw", NULL};
> - char **p = (char **)list;
> -
> - while (*p) {
> - blockedrpcs = g_list_append(blockedrpcs, g_strdup(*p++));
> - }
> - }
> -#endif
> -
> #if !defined(CONFIG_FSTRIM)
> blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim"));
> #endif
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index 2704b814ab..098fa7a08b 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -412,7 +412,8 @@
> # Since: 0.15.0
> ##
> { 'enum': 'GuestFsfreezeStatus',
> - 'data': [ 'thawed', 'frozen' ] }
> + 'data': [ 'thawed', 'frozen' ],
> + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } }
>
> ##
> # @guest-fsfreeze-status:
> @@ -428,7 +429,8 @@
> # Since: 0.15.0
> ##
> { 'command': 'guest-fsfreeze-status',
> - 'returns': 'GuestFsfreezeStatus' }
> + 'returns': 'GuestFsfreezeStatus',
> + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } }
>
> ##
> # @guest-fsfreeze-freeze:
> @@ -450,7 +452,8 @@
> # Since: 0.15.0
> ##
> { 'command': 'guest-fsfreeze-freeze',
> - 'returns': 'int' }
> + 'returns': 'int',
> + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } }
>
> ##
> # @guest-fsfreeze-freeze-list:
> @@ -470,7 +473,8 @@
> ##
> { 'command': 'guest-fsfreeze-freeze-list',
> 'data': { '*mountpoints': ['str'] },
> - 'returns': 'int' }
> + 'returns': 'int',
> + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } }
>
> ##
> # @guest-fsfreeze-thaw:
> @@ -487,7 +491,8 @@
> # Since: 0.15.0
> ##
> { 'command': 'guest-fsfreeze-thaw',
> - 'returns': 'int' }
> + 'returns': 'int',
> + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSFREEZE'] } }
>
> ##
> # @GuestFilesystemTrimResult:
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 5923 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim
2024-06-13 15:43 ` [PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim Daniel P. Berrangé
2024-07-03 8:36 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:38 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:38 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 2966 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the filesystem trimming commands
> on POSIX platforms lacking required APIs.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been
> found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 13 -------------
> qga/qapi-schema.json | 9 ++++++---
> 2 files changed, 6 insertions(+), 16 deletions(-)
>
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 9207cb7a8f..d92fa0ec87 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -1144,22 +1144,9 @@ error:
>
> #endif /* HAVE_GETIFADDRS */
>
> -#if !defined(CONFIG_FSTRIM)
> -GuestFilesystemTrimResponse *
> -qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -#endif
> -
> /* add unsupported commands to the list of blocked RPCs */
> GList *ga_command_init_blockedrpcs(GList *blockedrpcs)
> {
> -#if !defined(CONFIG_FSTRIM)
> - blockedrpcs = g_list_append(blockedrpcs, g_strdup("guest-fstrim"));
> -#endif
> -
> return blockedrpcs;
> }
>
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index 098fa7a08b..0f27375ea0 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -509,7 +509,8 @@
> ##
> { 'struct': 'GuestFilesystemTrimResult',
> 'data': {'path': 'str',
> - '*trimmed': 'int', '*minimum': 'int', '*error': 'str'} }
> + '*trimmed': 'int', '*minimum': 'int', '*error': 'str'},
> + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } }
>
> ##
> # @GuestFilesystemTrimResponse:
> @@ -519,7 +520,8 @@
> # Since: 2.4
> ##
> { 'struct': 'GuestFilesystemTrimResponse',
> - 'data': {'paths': ['GuestFilesystemTrimResult']} }
> + 'data': {'paths': ['GuestFilesystemTrimResult']},
> + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } }
>
> ##
> # @guest-fstrim:
> @@ -541,7 +543,8 @@
> ##
> { 'command': 'guest-fstrim',
> 'data': { '*minimum': 'int' },
> - 'returns': 'GuestFilesystemTrimResponse' }
> + 'returns': 'GuestFilesystemTrimResponse',
> + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_FSTRIM'] } }
>
> ##
> # @guest-suspend-disk:
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 4059 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev
2024-06-13 15:43 ` [PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev Daniel P. Berrangé
2024-07-03 8:37 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:40 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:40 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 3041 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the schema to fully
> exclude generation of the filesystem trimming commands on POSIX
> platforms lacking required APIs.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been
> found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-linux.c | 8 --------
> qga/qapi-schema.json | 8 ++++----
> 2 files changed, 4 insertions(+), 12 deletions(-)
>
> diff --git a/qga/commands-linux.c b/qga/commands-linux.c
> index 73b13fbaf6..89bdcded01 100644
> --- a/qga/commands-linux.c
> +++ b/qga/commands-linux.c
> @@ -1049,14 +1049,6 @@ GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
> return ret;
> }
>
> -#else
> -
> -GuestDiskInfoList *qmp_guest_get_disks(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> #endif
>
> /* Return a list of the disk device(s)' info which @mount lies on */
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index 0f27375ea0..0b7f911ca5 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -985,7 +985,7 @@
> 'media-errors-hi': 'uint64',
> 'number-of-error-log-entries-lo': 'uint64',
> 'number-of-error-log-entries-hi': 'uint64' },
> - 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
> + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } }
>
> ##
> # @GuestDiskSmart:
> @@ -1000,7 +1000,7 @@
> 'base': { 'type': 'GuestDiskBusType' },
> 'discriminator': 'type',
> 'data': { 'nvme': 'GuestNVMeSmart' },
> - 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
> + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } }
>
> ##
> # @GuestDiskInfo:
> @@ -1026,7 +1026,7 @@
> 'data': {'name': 'str', 'partition': 'bool', '*dependencies': ['str'],
> '*address': 'GuestDiskAddress', '*alias': 'str',
> '*smart': 'GuestDiskSmart'},
> - 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
> + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } }
>
> ##
> # @guest-get-disks:
> @@ -1040,7 +1040,7 @@
> ##
> { 'command': 'guest-get-disks',
> 'returns': ['GuestDiskInfo'],
> - 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX' ] } }
> + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LIBUDEV' ] } }
>
> ##
> # @GuestFilesystemInfo:
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 4277 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx
2024-06-13 15:44 ` [PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx Daniel P. Berrangé
2024-07-03 8:38 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:43 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:43 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 2428 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the get-users command on POSIX
> platforms lacking required APIs.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been
> found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 10 +---------
> qga/qapi-schema.json | 6 ++++--
> 2 files changed, 5 insertions(+), 11 deletions(-)
>
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index d92fa0ec87..a353f64ae6 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -1212,15 +1212,7 @@ GuestUserList *qmp_guest_get_users(Error **errp)
> return head;
> }
>
> -#else
> -
> -GuestUserList *qmp_guest_get_users(Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> - return NULL;
> -}
> -
> -#endif
> +#endif /* HAVE_UTMPX */
>
> /* Replace escaped special characters with their real values. The
> replacement
> * is done in place -- returned value is in the original string.
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index 0b7f911ca5..70d4f173ad 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -1413,7 +1413,8 @@
> # Since: 2.10
> ##
> { 'struct': 'GuestUser',
> - 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' } }
> + 'data': { 'user': 'str', 'login-time': 'number', '*domain': 'str' },
> + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_UTMPX' ] } }
>
> ##
> # @guest-get-users:
> @@ -1425,7 +1426,8 @@
> # Since: 2.10
> ##
> { 'command': 'guest-get-users',
> - 'returns': ['GuestUser'] }
> + 'returns': ['GuestUser'],
> + 'if': { 'any': ['CONFIG_WIN32', 'HAVE_UTMPX' ] } }
>
> ##
> # @GuestTimezone:
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 3322 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX
2024-06-13 15:44 ` [PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX Daniel P. Berrangé
2024-07-03 8:39 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:43 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:43 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 2929 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> Rather than creating stubs for every command that just return
> QERR_UNSUPPORTED, use 'if' conditions in the QAPI schema to
> fully exclude generation of the commands on other UNIX.
>
> The command will be rejected at QMP dispatch time instead,
> avoiding reimplementing rejection by blocking the stub commands.
> This changes the error message for affected commands from
>
> {"class": "CommandNotFound", "desc": "Command FOO has been disabled"}
>
> to
>
> {"class": "CommandNotFound", "desc": "The command FOO has not been
> found"}
>
> This has the additional benefit that the QGA protocol reference
> now documents what conditions enable use of the command.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> meson.build | 1 +
> qga/commands-posix.c | 8 --------
> qga/qapi-schema.json | 3 ++-
> 3 files changed, 3 insertions(+), 9 deletions(-)
>
> diff --git a/meson.build b/meson.build
> index d9f3349b0a..92b8c02582 100644
> --- a/meson.build
> +++ b/meson.build
> @@ -2234,6 +2234,7 @@ config_host_data.set('CONFIG_ATTR', libattr.found())
> config_host_data.set('CONFIG_BDRV_WHITELIST_TOOLS',
> get_option('block_drv_whitelist_in_tools'))
> config_host_data.set('CONFIG_BRLAPI', brlapi.found())
> config_host_data.set('CONFIG_BSD', host_os in bsd_oses)
> +config_host_data.set('CONFIG_FREEBSD', host_os == 'freebsd')
> config_host_data.set('CONFIG_CAPSTONE', capstone.found())
> config_host_data.set('CONFIG_COCOA', cocoa.found())
> config_host_data.set('CONFIG_DARWIN', host_os == 'darwin')
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index a353f64ae6..f4104f2760 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -877,14 +877,6 @@ void qmp_guest_set_user_password(const char *username,
> return;
> }
> }
> -#else /* __linux__ || __FreeBSD__ */
> -void qmp_guest_set_user_password(const char *username,
> - const char *password,
> - bool crypted,
> - Error **errp)
> -{
> - error_setg(errp, QERR_UNSUPPORTED);
> -}
> #endif /* __linux__ || __FreeBSD__ */
>
> #ifdef HAVE_GETIFADDRS
> diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json
> index 70d4f173ad..571be3a914 100644
> --- a/qga/qapi-schema.json
> +++ b/qga/qapi-schema.json
> @@ -1108,7 +1108,8 @@
> # Since: 2.3
> ##
> { 'command': 'guest-set-user-password',
> - 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' } }
> + 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' },
> + 'if': { 'any': [ 'CONFIG_WIN32', 'CONFIG_LINUX', 'CONFIG_FREEBSD'] } }
>
> ##
> # @GuestMemoryBlock:
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 3895 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file
2024-06-13 15:44 ` [PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file Daniel P. Berrangé
2024-07-03 8:40 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:44 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:44 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 2197 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:44 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> It is referenced by QGAState already, and it is clearer to declare all
> data types at the top of the file, rather than have them mixed with
> code later.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/main.c | 44 ++++++++++++++++++++++----------------------
> 1 file changed, 22 insertions(+), 22 deletions(-)
>
> diff --git a/qga/main.c b/qga/main.c
> index 17b6ce18ac..647d27037c 100644
> --- a/qga/main.c
> +++ b/qga/main.c
> @@ -70,6 +70,28 @@ typedef struct GAPersistentState {
>
> typedef struct GAConfig GAConfig;
>
> +struct GAConfig {
> + char *channel_path;
> + char *method;
> + char *log_filepath;
> + char *pid_filepath;
> +#ifdef CONFIG_FSFREEZE
> + char *fsfreeze_hook;
> +#endif
> + char *state_dir;
> +#ifdef _WIN32
> + const char *service;
> +#endif
> + gchar *bliststr; /* blockedrpcs may point to this string */
> + gchar *aliststr; /* allowedrpcs may point to this string */
> + GList *blockedrpcs;
> + GList *allowedrpcs;
> + int daemonize;
> + GLogLevelFlags log_level;
> + int dumpconf;
> + bool retry_path;
> +};
> +
> struct GAState {
> JSONMessageParser parser;
> GMainLoop *main_loop;
> @@ -996,28 +1018,6 @@ static GList *split_list(const gchar *str, const
> gchar *delim)
> return list;
> }
>
> -struct GAConfig {
> - char *channel_path;
> - char *method;
> - char *log_filepath;
> - char *pid_filepath;
> -#ifdef CONFIG_FSFREEZE
> - char *fsfreeze_hook;
> -#endif
> - char *state_dir;
> -#ifdef _WIN32
> - const char *service;
> -#endif
> - gchar *bliststr; /* blockedrpcs may point to this string */
> - gchar *aliststr; /* allowedrpcs may point to this string */
> - GList *blockedrpcs;
> - GList *allowedrpcs;
> - int daemonize;
> - GLogLevelFlags log_level;
> - int dumpconf;
> - bool retry_path;
> -};
> -
> static void config_load(GAConfig *config)
> {
> GError *gerr = NULL;
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 2809 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable
2024-06-13 15:44 ` [PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable Daniel P. Berrangé
2024-07-03 8:41 ` Philippe Mathieu-Daudé
@ 2024-07-12 8:46 ` Konstantin Kostiuk
1 sibling, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 8:46 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 2355 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:45 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> This variable was used to support back compat for the old config
> file key name, and became redundant after the following change:
>
> commit a7a2d636ae4549ef0551134d4bf8e084a14431c4
> Author: Philippe Mathieu-Daudé <philmd@linaro.org>
> Date: Thu May 30 08:36:43 2024 +0200
>
> qga: Remove deprecated 'blacklist' argument / config key
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/main.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/qga/main.c b/qga/main.c
> index 647d27037c..6ff022a85d 100644
> --- a/qga/main.c
> +++ b/qga/main.c
> @@ -1023,7 +1023,6 @@ static void config_load(GAConfig *config)
> GError *gerr = NULL;
> GKeyFile *keyfile;
> g_autofree char *conf = g_strdup(g_getenv("QGA_CONF")) ?:
> get_relocated_path(QGA_CONF_DEFAULT);
> - const gchar *blockrpcs_key = "block-rpcs";
>
> /* read system config */
> keyfile = g_key_file_new();
> @@ -1071,9 +1070,9 @@ static void config_load(GAConfig *config)
> g_key_file_get_boolean(keyfile, "general", "retry-path",
> &gerr);
> }
>
> - if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL)) {
> + if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL)) {
> config->bliststr =
> - g_key_file_get_string(keyfile, "general", blockrpcs_key,
> &gerr);
> + g_key_file_get_string(keyfile, "general", "block-rpcs",
> &gerr);
> config->blockedrpcs = g_list_concat(config->blockedrpcs,
> split_list(config->bliststr,
> ","));
> }
> @@ -1084,7 +1083,7 @@ static void config_load(GAConfig *config)
> split_list(config->aliststr,
> ","));
> }
>
> - if (g_key_file_has_key(keyfile, "general", blockrpcs_key, NULL) &&
> + if (g_key_file_has_key(keyfile, "general", "block-rpcs", NULL) &&
> g_key_file_has_key(keyfile, "general", "allow-rpcs", NULL)) {
> g_critical("wrong config, using 'block-rpcs' and 'allow-rpcs'
> keys at"
> " the same time is not allowed");
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 3278 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 21/22] qga: allow configuration file path via the cli
2024-06-13 15:44 ` [PATCH v2 21/22] qga: allow configuration file path via the cli Daniel P. Berrangé
2024-07-03 8:44 ` Philippe Mathieu-Daudé
@ 2024-07-12 9:05 ` Konstantin Kostiuk
2024-07-12 9:18 ` Daniel P. Berrangé
1 sibling, 1 reply; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 9:05 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 4628 bytes --]
On Thu, Jun 13, 2024 at 6:45 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> Allowing the user to set the QGA_CONF environment variable to change
> the default configuration file path is very unusual practice, made
> more obscure since this ability is not documented.
>
> This introduces the more normal '-c PATH' / '--config=PATH' command
> line argument approach. This requires that we parse the comamnd line
> twice, since we want the command line arguments to take priority over
> the configuration file settings in general.
>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> docs/interop/qemu-ga.rst | 5 +++++
> qga/main.c | 35 +++++++++++++++++++++++++++--------
> 2 files changed, 32 insertions(+), 8 deletions(-)
>
> diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst
> index 72fb75a6f5..e42b370319 100644
> --- a/docs/interop/qemu-ga.rst
> +++ b/docs/interop/qemu-ga.rst
> @@ -33,6 +33,11 @@ Options
>
> .. program:: qemu-ga
>
> +.. option:: -c, --config=PATH
> +
> + Configuration file path (the default is |CONFDIR|\ ``/qemu-ga.conf``,
> + unless overriden by the QGA_CONF environment variable)
> +
> .. option:: -m, --method=METHOD
>
Please also update qga/main.c static void usage(const char *cmd)
>
> Transport method: one of ``unix-listen``, ``virtio-serial``, or
> diff --git a/qga/main.c b/qga/main.c
> index 6ff022a85d..f68a32bf7b 100644
> --- a/qga/main.c
> +++ b/qga/main.c
> @@ -1018,15 +1018,14 @@ static GList *split_list(const gchar *str, const
> gchar *delim)
> return list;
> }
>
> -static void config_load(GAConfig *config)
> +static void config_load(GAConfig *config, const char *confpath, bool
> required)
> {
> GError *gerr = NULL;
> GKeyFile *keyfile;
> - g_autofree char *conf = g_strdup(g_getenv("QGA_CONF")) ?:
> get_relocated_path(QGA_CONF_DEFAULT);
>
> /* read system config */
> keyfile = g_key_file_new();
> - if (!g_key_file_load_from_file(keyfile, conf, 0, &gerr)) {
> + if (!g_key_file_load_from_file(keyfile, confpath, 0, &gerr)) {
> goto end;
> }
> if (g_key_file_has_key(keyfile, "general", "daemon", NULL)) {
> @@ -1092,10 +1091,10 @@ static void config_load(GAConfig *config)
>
> end:
> g_key_file_free(keyfile);
> - if (gerr &&
> - !(gerr->domain == G_FILE_ERROR && gerr->code ==
> G_FILE_ERROR_NOENT)) {
> + if (gerr && (required ||
> + !(gerr->domain == G_FILE_ERROR && gerr->code ==
> G_FILE_ERROR_NOENT))) {
> g_critical("error loading configuration from path: %s, %s",
> - conf, gerr->message);
> + confpath, gerr->message);
> exit(EXIT_FAILURE);
> }
> g_clear_error(&gerr);
> @@ -1167,12 +1166,13 @@ static void config_dump(GAConfig *config)
>
> static void config_parse(GAConfig *config, int argc, char **argv)
> {
> - const char *sopt = "hVvdm:p:l:f:F::b:a:s:t:Dr";
> + const char *sopt = "hVvdc:m:p:l:f:F::b:a:s:t:Dr";
> int opt_ind = 0, ch;
> bool block_rpcs = false, allow_rpcs = false;
> const struct option lopt[] = {
> { "help", 0, NULL, 'h' },
> { "version", 0, NULL, 'V' },
> + { "config", 1, NULL, 'c' },
> { "dump-conf", 0, NULL, 'D' },
> { "logfile", 1, NULL, 'l' },
> { "pidfile", 1, NULL, 'f' },
> @@ -1192,6 +1192,26 @@ static void config_parse(GAConfig *config, int
> argc, char **argv)
> { "retry-path", 0, NULL, 'r' },
> { NULL, 0, NULL, 0 }
> };
> + g_autofree char *confpath = g_strdup(g_getenv("QGA_CONF")) ?:
> + get_relocated_path(QGA_CONF_DEFAULT);
> + bool confrequired = false;
> +
> + while ((ch = getopt_long(argc, argv, sopt, lopt, NULL)) != -1) {
> + switch (ch) {
> + case 'c':
> + g_free(confpath);
> + confpath = g_strdup(optarg);
> + confrequired = true;
> + break;
> + default:
> + break;
> + }
> + }
> +
> + config_load(config, confpath, confrequired);
> +
> + /* Reset for second pass */
> + optind = 1;
>
> while ((ch = getopt_long(argc, argv, sopt, lopt, &opt_ind)) != -1) {
> switch (ch) {
> @@ -1582,7 +1602,6 @@ int main(int argc, char **argv)
> qga_qmp_init_marshal(&ga_commands);
>
> init_dfl_pathnames();
> - config_load(config);
> config_parse(config, argc, argv);
>
> if (config->pid_filepath == NULL) {
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 5941 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 21/22] qga: allow configuration file path via the cli
2024-07-12 9:05 ` Konstantin Kostiuk
@ 2024-07-12 9:18 ` Daniel P. Berrangé
0 siblings, 0 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-07-12 9:18 UTC (permalink / raw)
To: Konstantin Kostiuk
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Thomas Huth, Michael Roth, Paolo Bonzini
On Fri, Jul 12, 2024 at 12:05:23PM +0300, Konstantin Kostiuk wrote:
> On Thu, Jun 13, 2024 at 6:45 PM Daniel P. Berrangé <berrange@redhat.com>
> wrote:
>
> > Allowing the user to set the QGA_CONF environment variable to change
> > the default configuration file path is very unusual practice, made
> > more obscure since this ability is not documented.
> >
> > This introduces the more normal '-c PATH' / '--config=PATH' command
> > line argument approach. This requires that we parse the comamnd line
> > twice, since we want the command line arguments to take priority over
> > the configuration file settings in general.
> >
> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> > ---
> > docs/interop/qemu-ga.rst | 5 +++++
> > qga/main.c | 35 +++++++++++++++++++++++++++--------
> > 2 files changed, 32 insertions(+), 8 deletions(-)
> >
> > diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst
> > index 72fb75a6f5..e42b370319 100644
> > --- a/docs/interop/qemu-ga.rst
> > +++ b/docs/interop/qemu-ga.rst
> > @@ -33,6 +33,11 @@ Options
> >
> > .. program:: qemu-ga
> >
> > +.. option:: -c, --config=PATH
> > +
> > + Configuration file path (the default is |CONFDIR|\ ``/qemu-ga.conf``,
> > + unless overriden by the QGA_CONF environment variable)
> > +
> > .. option:: -m, --method=METHOD
> >
>
> Please also update qga/main.c static void usage(const char *cmd)
Opps, yes, will do.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 01/22] qga: drop blocking of guest-get-memory-block-size command
2024-06-13 15:01 ` [PATCH v2 01/22] qga: drop blocking of guest-get-memory-block-size command Daniel P. Berrangé
@ 2024-07-12 9:33 ` Konstantin Kostiuk
0 siblings, 0 replies; 70+ messages in thread
From: Konstantin Kostiuk @ 2024-07-12 9:33 UTC (permalink / raw)
To: Daniel P. Berrangé
Cc: qemu-devel, Marc-André Lureau, Michael Roth,
Philippe Mathieu-Daudé, Thomas Huth, Paolo Bonzini,
Manos Pitsidianakis
[-- Attachment #1: Type: text/plain, Size: 1742 bytes --]
Reviewed-by: Konstantin Kostiuk <kkostiuk@redhat.com>
On Thu, Jun 13, 2024 at 6:01 PM Daniel P. Berrangé <berrange@redhat.com>
wrote:
> This command has never existed in tree, since it was renamed to
> guest-get-memory-block-info before being merged.
>
> Reviewed-by: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
> Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> ---
> qga/commands-posix.c | 2 +-
> qga/commands-win32.c | 2 +-
> 2 files changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/qga/commands-posix.c b/qga/commands-posix.c
> index 7f05996495..76af98ba32 100644
> --- a/qga/commands-posix.c
> +++ b/qga/commands-posix.c
> @@ -3099,7 +3099,7 @@ GList *ga_command_init_blockedrpcs(GList
> *blockedrpcs)
> "guest-suspend-disk", "guest-suspend-ram",
> "guest-suspend-hybrid", "guest-get-vcpus", "guest-set-vcpus",
> "guest-get-memory-blocks", "guest-set-memory-blocks",
> - "guest-get-memory-block-size", "guest-get-memory-block-info",
> + "guest-get-memory-block-info",
> NULL};
> char **p = (char **)list;
>
> diff --git a/qga/commands-win32.c b/qga/commands-win32.c
> index 0d1b836e87..9fe670d5b4 100644
> --- a/qga/commands-win32.c
> +++ b/qga/commands-win32.c
> @@ -1995,7 +1995,7 @@ GList *ga_command_init_blockedrpcs(GList
> *blockedrpcs)
> "guest-suspend-hybrid",
> "guest-set-vcpus",
> "guest-get-memory-blocks", "guest-set-memory-blocks",
> - "guest-get-memory-block-size", "guest-get-memory-block-info",
> + "guest-get-memory-block-info",
> NULL};
> char **p = (char **)list_unsupported;
>
> --
> 2.45.1
>
>
[-- Attachment #2: Type: text/html, Size: 2524 bytes --]
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 18/22] qga: don't disable fsfreeze commands if vss_init fails
2024-07-03 10:21 ` Manos Pitsidianakis
@ 2024-07-12 12:45 ` Daniel P. Berrangé
0 siblings, 0 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-07-12 12:45 UTC (permalink / raw)
To: Manos Pitsidianakis
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Michael Roth, Paolo Bonzini
On Wed, Jul 03, 2024 at 01:21:29PM +0300, Manos Pitsidianakis wrote:
> On Thu, 13 Jun 2024 18:44, "Daniel P. Berrangé" <berrange@redhat.com> wrote:
> > The fsfreeze commands are already written to report an error if
> > vss_init() fails. Reporting a more specific error message is more
> > helpful than a generic "command is disabled" message, which cannot
> > beteween an admin config decision and lack of platform support.
>
> s/beteween/between
>
> >
> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> > ---
> > qga/commands-win32.c | 18 +++---------------
> > qga/main.c | 4 ++++
> > 2 files changed, 7 insertions(+), 15 deletions(-)
> >
> > diff --git a/qga/commands-win32.c b/qga/commands-win32.c
> > index 2533e4c748..5866cc2e3c 100644
> > --- a/qga/commands-win32.c
> > +++ b/qga/commands-win32.c
> > @@ -1203,7 +1203,7 @@ GuestFilesystemInfoList *qmp_guest_get_fsinfo(Error **errp)
> > GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp)
> > {
> > if (!vss_initialized()) {
> > - error_setg(errp, QERR_UNSUPPORTED);
> > + error_setg(errp, "fsfreeze not possible as VSS failed to initialize");
> > return 0;
>
> Should this be return -1 by the way?
This method has to return GuestFsfreezeStatus and
-1 isn't valid. Not a problem though, as the
QAPI code will check for *errp != NULL and not
consider the return value for error detection.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 70+ messages in thread
* Re: [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands
2024-07-03 10:01 ` Manos Pitsidianakis
2024-07-03 12:09 ` Philippe Mathieu-Daudé
@ 2024-07-12 13:01 ` Daniel P. Berrangé
1 sibling, 0 replies; 70+ messages in thread
From: Daniel P. Berrangé @ 2024-07-12 13:01 UTC (permalink / raw)
To: Manos Pitsidianakis
Cc: qemu-devel, Marc-André Lureau, Philippe Mathieu-Daudé,
Konstantin Kostiuk, Thomas Huth, Michael Roth, Paolo Bonzini
On Wed, Jul 03, 2024 at 01:01:11PM +0300, Manos Pitsidianakis wrote:
> Hello Daniel,
>
> This cleanup seems like a good idea,
>
> On Thu, 13 Jun 2024 18:44, "Daniel P. Berrangé" <berrange@redhat.com> wrote:
> > It is confusing having many different pieces of code enabling and
> > disabling commands, and it is not clear that they all have the same
> > semantics, especially wrt prioritization of the block/allow lists.
> > The code attempted to prevent the user from setting both the block
> > and allow lists concurrently, however, the logic was flawed as it
> > checked settings in the configuration file separately from the
> > command line arguments. Thus it was possible to set a block list
> > in the config file and an allow list via a command line argument.
> > The --dump-conf option also creates a configuration file with both
> > keys present, even if unset, which means it is creating a config
> > that cannot actually be loaded again.
> >
> > Centralizing the code in a single method "ga_apply_command_filters"
> > will provide a strong guarantee of consistency and clarify the
> > intended behaviour. With this there is no compelling technical
> > reason to prevent concurrent setting of both the allow and block
> > lists, so this flawed restriction is removed.
> >
> > Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
> > ---
> > docs/interop/qemu-ga.rst | 14 +++++
> > qga/commands-posix.c | 6 --
> > qga/commands-win32.c | 6 --
> > qga/main.c | 128 +++++++++++++++++----------------------
> > 4 files changed, 70 insertions(+), 84 deletions(-)
> > diff --git a/qga/main.c b/qga/main.c
> > index f68a32bf7b..72c16fead8 100644
> > --- a/qga/main.c
> > +++ b/qga/main.c
> > @@ -419,60 +419,79 @@ static gint ga_strcmp(gconstpointer str1, gconstpointer str2)
> > return strcmp(str1, str2);
> > }
> >
> > -/* disable commands that aren't safe for fsfreeze */
> > -static void ga_disable_not_allowed_freeze(const QmpCommand *cmd, void *opaque)
> > +static bool ga_command_is_allowed(const QmpCommand *cmd, GAState *state)
> > {
> > - bool allowed = false;
> > int i = 0;
> > + GAConfig *config = state->config;
> > const char *name = qmp_command_name(cmd);
> > + /* Fallback policy is allow everything */
> > + bool allowed = true;
> >
> > - while (ga_freeze_allowlist[i] != NULL) {
> > - if (strcmp(name, ga_freeze_allowlist[i]) == 0) {
> > + if (config->allowedrpcs) {
> > + /*
> > + * If an allow-list is given, this changes the fallback
> > + * policy to deny everything
> > + */
> > + allowed = false;
> > +
> > + if (g_list_find_custom(config->allowedrpcs, name, ga_strcmp) != NULL) {
> > allowed = true;
> > }
> > - i++;
> > }
> > - if (!allowed) {
> > - g_debug("disabling command: %s", name);
> > - qmp_disable_command(&ga_commands, name, "the agent is in frozen state");
> > - }
> > -}
> >
> > -/* [re-]enable all commands, except those explicitly blocked by user */
> > -static void ga_enable_non_blocked(const QmpCommand *cmd, void *opaque)
> > -{
> > - GAState *s = opaque;
> > - GList *blockedrpcs = s->blockedrpcs;
> > - GList *allowedrpcs = s->allowedrpcs;
> > - const char *name = qmp_command_name(cmd);
> > -
> > - if (g_list_find_custom(blockedrpcs, name, ga_strcmp) == NULL) {
> > - if (qmp_command_is_enabled(cmd)) {
> > - return;
> > + /*
> > + * If both allowedrpcs and blockedrpcs are set, the blocked
> > + * list will take priority
> > + */
> > + if (config->blockedrpcs) {
> > + if (g_list_find_custom(config->blockedrpcs, name, ga_strcmp) != NULL) {
> > + allowed = false;
> > }
> > + }
> >
> > - if (allowedrpcs &&
> > - g_list_find_custom(allowedrpcs, name, ga_strcmp) == NULL) {
> > - return;
> > - }
> > + /*
> > + * If frozen, this filtering must take priority over
> > + * absolutely everything
> > + */
> > + if (state->frozen) {
> > + allowed = false;
> >
> > - g_debug("enabling command: %s", name);
> > - qmp_enable_command(&ga_commands, name);
> > + while (ga_freeze_allowlist[i] != NULL) {
> > + if (strcmp(name, ga_freeze_allowlist[i]) == 0) {
> > + allowed = true;
> > + }
> > + i++;
> > + }
> > }
> > +
> > + return allowed;
> > }
>
> IUUC, we can check by priority here: first check if (state->frozen), then
> blockedrpcs, then allowedrpcs and then return a default fallback value
> allowed = config->blockedrpcs != NULL && config->allowedrpcs != NULL
That would imply each check does an early return. When I add in the
following series, I have further checks going in this method which
rely on the fallthrough for overrides, which works better as it is
written here.
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
^ permalink raw reply [flat|nested] 70+ messages in thread
end of thread, other threads:[~2024-07-12 13:02 UTC | newest]
Thread overview: 70+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-06-13 15:01 [PATCH v2 00/22] qga: clean up command source locations and conditionals Daniel P. Berrangé
2024-06-13 15:01 ` [PATCH v2 01/22] qga: drop blocking of guest-get-memory-block-size command Daniel P. Berrangé
2024-07-12 9:33 ` Konstantin Kostiuk
2024-06-13 15:01 ` [PATCH v2 02/22] qga: move linux vcpu command impls to commands-linux.c Daniel P. Berrangé
2024-07-03 8:45 ` Philippe Mathieu-Daudé
2024-07-12 8:30 ` Konstantin Kostiuk
2024-06-13 15:01 ` [PATCH v2 03/22] qga: move linux suspend " Daniel P. Berrangé
2024-07-03 8:45 ` Philippe Mathieu-Daudé
2024-07-12 8:29 ` Konstantin Kostiuk
2024-06-13 15:01 ` [PATCH v2 04/22] qga: move linux fs/disk " Daniel P. Berrangé
2024-07-03 8:46 ` Philippe Mathieu-Daudé
2024-07-12 8:29 ` Konstantin Kostiuk
2024-06-13 15:01 ` [PATCH v2 05/22] qga: move linux disk/cpu stats " Daniel P. Berrangé
2024-07-03 8:25 ` Philippe Mathieu-Daudé
2024-07-12 8:33 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 06/22] qga: move linux memory block " Daniel P. Berrangé
2024-06-13 15:43 ` [PATCH v2 07/22] qga: move CONFIG_FSFREEZE/TRIM to be meson defined options Daniel P. Berrangé
2024-06-13 15:43 ` [PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows Daniel P. Berrangé
2024-07-03 8:30 ` Philippe Mathieu-Daudé
2024-07-12 8:34 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX Daniel P. Berrangé
2024-07-03 8:31 ` Philippe Mathieu-Daudé
2024-07-12 8:35 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs Daniel P. Berrangé
2024-07-03 8:32 ` Philippe Mathieu-Daudé
2024-07-12 8:35 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 11/22] qga: conditionalize schema for commands requiring linux/win32 Daniel P. Berrangé
2024-06-13 15:43 ` [PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows Daniel P. Berrangé
2024-07-03 8:35 ` Philippe Mathieu-Daudé
2024-07-12 8:37 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze Daniel P. Berrangé
2024-07-03 8:37 ` Philippe Mathieu-Daudé
2024-07-12 8:37 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim Daniel P. Berrangé
2024-07-03 8:36 ` Philippe Mathieu-Daudé
2024-07-12 8:38 ` Konstantin Kostiuk
2024-06-13 15:43 ` [PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev Daniel P. Berrangé
2024-07-03 8:37 ` Philippe Mathieu-Daudé
2024-07-12 8:40 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx Daniel P. Berrangé
2024-07-03 8:38 ` Philippe Mathieu-Daudé
2024-07-12 8:43 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX Daniel P. Berrangé
2024-07-03 8:39 ` Philippe Mathieu-Daudé
2024-07-12 8:43 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 18/22] qga: don't disable fsfreeze commands if vss_init fails Daniel P. Berrangé
2024-07-03 10:21 ` Manos Pitsidianakis
2024-07-12 12:45 ` Daniel P. Berrangé
2024-06-13 15:44 ` [PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file Daniel P. Berrangé
2024-07-03 8:40 ` Philippe Mathieu-Daudé
2024-07-12 8:44 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable Daniel P. Berrangé
2024-07-03 8:41 ` Philippe Mathieu-Daudé
2024-07-12 8:46 ` Konstantin Kostiuk
2024-06-13 15:44 ` [PATCH v2 21/22] qga: allow configuration file path via the cli Daniel P. Berrangé
2024-07-03 8:44 ` Philippe Mathieu-Daudé
2024-07-12 9:05 ` Konstantin Kostiuk
2024-07-12 9:18 ` Daniel P. Berrangé
2024-06-13 15:44 ` [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands Daniel P. Berrangé
2024-07-03 10:01 ` Manos Pitsidianakis
2024-07-03 12:09 ` Philippe Mathieu-Daudé
2024-07-12 13:01 ` Daniel P. Berrangé
2024-07-03 8:26 ` [PATCH v2 06/22] qga: move linux memory block command impls to commands-linux.c Philippe Mathieu-Daudé
2024-07-12 8:34 ` Konstantin Kostiuk
2024-06-14 8:34 ` [PATCH v2 00/22] qga: clean up command source locations and conditionals Marc-André Lureau
2024-06-14 9:19 ` Daniel P. Berrangé
2024-07-02 18:00 ` Daniel P. Berrangé
2024-07-03 6:15 ` Marc-André Lureau
2024-07-03 8:06 ` Daniel P. Berrangé
2024-07-03 8:17 ` Marc-André Lureau
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).