> Required to use them later in the Windows vsock path.
>
> ga_channel_common_client_add() now takes ownership of client_ch on
> success only; on failure the caller keeps it and disposes of it via the
> new ga_channel_common_gio_destroy() helper.
> The Windows vsock path needs this: it has to release the CRT fd wrapper
> with qemu_close_socket_osfhandle() before channel teardown closes the
> SOCKET, so the caller must still own the channel on the error path.
>
> Signed-off-by: Polina Vishneva <[email protected]>
> Message-ID: <[email protected]>
>
> diff --git a/qga/channel-common.h b/qga/channel-common.h
> new file mode 100644
> index 00000000000..8e82bf09099
> --- /dev/null
> +++ b/qga/channel-common.h
> @@ -0,0 +1,44 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * QEMU Guest Agent common GIOChannel lifecycle
> + *
> + * Copyright (c) 2026 Virtuozzo International GmbH.
> + */
> +#ifndef QGA_CHANNEL_COMMON_H
> +#define QGA_CHANNEL_COMMON_H
> +
> +#include "channel.h"
> +
> +typedef struct GAChannelCommon {
> + GIOChannel *listen_channel;
> + GIOChannel *client_channel;
> + GAChannelMethod method;
> + GAChannelCallback event_cb;
> + gpointer user_data;
> +} GAChannelCommon;
> +
> +/* Common GIOChannel lifecycle (channel-common.c) */
> +void ga_channel_common_listen_close(GAChannelCommon *c);
> +
> +/* Start listening on a vsock "cid:port" path; returns an fd or -1. */
> +int ga_channel_common_vsock_listen(const char *path, Error **errp);
> +
> +/* Shut down and unref a GIOChannel not tracked by a GAChannelCommon. */
> +void ga_channel_common_gio_destroy(GIOChannel *ch);
> +
> +/*
> + * Takes ownership of client_ch on success only; on failure the caller keeps
> + * it and must dispose of it via ga_channel_common_gio_destroy().
> + */
> +int ga_channel_common_client_add(GAChannelCommon *c,
> + GIOChannel *client_ch,
> + GIOFunc event_fn, gpointer data);
> +void ga_channel_common_client_close(GAChannelCommon *c);
> +GIOStatus ga_channel_common_read(GAChannelCommon *c, gchar *buf,
> + gsize size, gsize *count);
> +GIOStatus ga_channel_common_write_all(GAChannelCommon *c,
> + const gchar *buf, gsize size);
> +void ga_channel_common_free(GAChannelCommon *c);
> +
> +#endif /* QGA_CHANNEL_COMMON_H */
> diff --git a/qga/channel-common.c b/qga/channel-common.c
> new file mode 100644
> index 00000000000..9732a194776
> --- /dev/null
> +++ b/qga/channel-common.c
> @@ -0,0 +1,115 @@
> +/*
> + * SPDX-License-Identifier: GPL-2.0-or-later
> + *
> + * QEMU Guest Agent common GIOChannel lifecycle
> + *
> + * Copyright (c) 2026 Virtuozzo International GmbH.
> + */
> +
> +#include "qemu/osdep.h"
> +#include "qapi/error.h"
> +#include "qemu/sockets.h"
> +#include "channel-common.h"
> +
> +void ga_channel_common_gio_destroy(GIOChannel *ch)
> +{
> + g_io_channel_shutdown(ch, true, NULL);
> + g_io_channel_unref(ch);
> +}
> +
> +/* The vsock analogue of unix_listen(): returns a listening fd or -1. */
> +int ga_channel_common_vsock_listen(const char *path, Error **errp)
> +{
> + SocketAddress *addr;
> + char *addr_str;
> + int fd;
> +
> + addr_str = g_strdup_printf("vsock:%s", path);
> + addr = socket_parse(addr_str, errp);
> + g_free(addr_str);
Could use g_autofree here too
> + if (!addr) {
> + return -1;
> + }
> +
> + fd = socket_listen(addr, 1, errp);
> + qapi_free_SocketAddress(addr);
> + return fd;
> +}
> +
> +void ga_channel_common_listen_close(GAChannelCommon *c)
> +{
> + g_assert(c->listen_channel);
> + ga_channel_common_gio_destroy(c->listen_channel);
> + c->listen_channel = NULL;
> +}
> +
> +int ga_channel_common_client_add(GAChannelCommon *c, GIOChannel *client_ch,
> + GIOFunc event_fn, gpointer data)
> +{
> + GError *err = NULL;
> +
> + g_assert(c && !c->client_channel);
> + g_io_channel_set_encoding(client_ch, NULL, &err);
> + if (err != NULL) {
> + g_warning("error setting channel encoding to binary");
> + g_error_free(err);
> + return -1;
> + }
> + g_io_add_watch(client_ch, G_IO_IN | G_IO_HUP, event_fn, data);
> + c->client_channel = client_ch;
> + return 0;
> +}
> +
> +void ga_channel_common_client_close(GAChannelCommon *c)
> +{
> + g_assert(c->client_channel);
> + ga_channel_common_gio_destroy(c->client_channel);
> + c->client_channel = NULL;
> +}
> +
> +GIOStatus ga_channel_common_read(GAChannelCommon *c, gchar *buf,
> + gsize size, gsize *count)
> +{
> + return g_io_channel_read_chars(c->client_channel, buf, size, count,
> NULL);
> +}
> +
> +GIOStatus ga_channel_common_write_all(GAChannelCommon *c,
> + const gchar *buf, gsize size)
> +{
> + GError *err = NULL;
g_error_free() is missing in this function.
Use g_auto?
--
Marc-André Lureau <[email protected]>