[PATCH v2 06/22] qga: move linux memory block command impls to commands-linux.c

Daniel P. Berrangé posted 22 patches 5 months, 2 weeks ago
Only 6 patches received!
There is a newer version of this series
[PATCH v2 06/22] qga: move linux memory block command impls to commands-linux.c
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 06/22] qga: move linux memory block command impls to commands-linux.c
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 06/22] qga: move linux memory block command impls to commands-linux.c
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 07/22] qga: move CONFIG_FSFREEZE/TRIM to be meson defined options
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


[PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 08/22] qga: conditionalize schema for commands unsupported on Windows
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 09/22] qga: conditionalize schema for commands unsupported on non-Linux POSIX
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 10/22] qga: conditionalize schema for commands requiring getifaddrs
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 11/22] qga: conditionalize schema for commands requiring linux/win32
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


[PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 12/22] qga: conditionalize schema for commands only supported on Windows
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 13/22] qga: conditionalize schema for commands requiring fsfreeze
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 14/22] qga: conditionalize schema for commands requiring fstrim
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 15/22] qga: conditionalize schema for commands requiring libudev
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 16/22] qga: conditionalize schema for commands requiring utmpx
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 17/22] qga: conditionalize schema for commands not supported on other UNIX
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 18/22] qga: don't disable fsfreeze commands if vss_init fails
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 18/22] qga: don't disable fsfreeze commands if vss_init fails
Posted by Manos Pitsidianakis 4 months, 3 weeks ago
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>

Re: [PATCH v2 18/22] qga: don't disable fsfreeze commands if vss_init fails
Posted by Daniel P. Berrangé 4 months, 2 weeks ago
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 :|


[PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 19/22] qga: move declare of QGAConfig struct to top of file
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 20/22] qga: remove pointless 'blockrpcs_key' variable
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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>


[PATCH v2 21/22] qga: allow configuration file path via the cli
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 21/22] qga: allow configuration file path via the cli
Posted by Konstantin Kostiuk 4 months, 2 weeks ago
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
>
>
Re: [PATCH v2 21/22] qga: allow configuration file path via the cli
Posted by Daniel P. Berrangé 4 months, 2 weeks ago
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 :|


Re: [PATCH v2 21/22] qga: allow configuration file path via the cli
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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.

[PATCH v2 22/22] qga: centralize logic for disabling/enabling commands
Posted by Daniel P. Berrangé 5 months, 2 weeks ago
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


Re: [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands
Posted by Manos Pitsidianakis 4 months, 3 weeks ago
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
>
>

Re: [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands
Posted by Daniel P. Berrangé 4 months, 2 weeks ago
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 :|


Re: [PATCH v2 22/22] qga: centralize logic for disabling/enabling commands
Posted by Philippe Mathieu-Daudé 4 months, 3 weeks ago
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);
>> +}