From nobody Sun Jul 26 12:39:53 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=virtuozzo.com ARC-Seal: i=1; a=rsa-sha256; t=1781892180; cv=none; d=zohomail.com; s=zohoarc; b=eu21Bwc0DPSaEho38uyRskJYNeg0d/AwwTDCAuprSk0ldtstgD/UixALcuLVmWdYmVIwnG+MAD3kOipdD4YGNIE0VATl2RTMcwCe8NjfydP8CliatRZpf4qqwGQovt+EYrzUExCr1Ic4QIHXKb2M2OwX1yL/FnsxaLmTf+yQ0xY= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781892180; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=DjOyqQnJ6RMF1XS8ej1SAjvRTDMFV0LbW14WnnJ9zz4=; b=Jh09sZfWUsdJJjjhZmDiO6FDaijNn/YUFkBfcwh2pPGfQgMROwDiiDMOvySpDRI3cEt/RWXKYqWxniczhz9mUVHrM3GJG/huVfb9JIxHjyxasGDTwEpchoAlAW1sKGDYOKWV0CObZIbZaXiaBWJSPUM20S7OscG5baAb+g/xwHM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781892180755389.6506561929838; Fri, 19 Jun 2026 11:03:00 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wadY3-0000bn-E8; Fri, 19 Jun 2026 14:02:35 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wadY0-0000bV-V9 for qemu-devel@nongnu.org; Fri, 19 Jun 2026 14:02:32 -0400 Received: from relay.virtuozzo.com ([130.117.225.111]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wadXz-0004Ab-7N for qemu-devel@nongnu.org; Fri, 19 Jun 2026 14:02:32 -0400 Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1wadXM-001Gyc-2U; Fri, 19 Jun 2026 20:02:24 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=MIME-Version:Message-ID:Date:Subject:From: Content-Type; bh=DjOyqQnJ6RMF1XS8ej1SAjvRTDMFV0LbW14WnnJ9zz4=; b=TkZS0R/4A7W9 zAmcGichE1hZL5uof+TzczCsNCc15E9H9d9vef7sSh99nFns0uQOYA0sn8qD0fUQx8eSrSXoY0Pbp ck86GxsuUxd0wzDQrK7AxmqsY+3NAmzR1eddoelHiN+XbAIjXxRIoLJYeK98pJ5PqGzIPMjBaVJVU VZaIxldl/W1uwN2rthjjX6ChD8nSj1XmsmDFLGKlCPTBrG4WUBgiLKdzmHeFRdS9CzzKm/Yek29Wz bn/vB0cnejuAOTN3mvVlhjGQe6yR7QsxuAjMgP0Ty9RIO+YTL8i5LJSP/I5Ssg/y3vmDablyBLbTE ZMENYNmyt8HzhI6HL4ZSQQ==; From: Polina Vishneva To: qemu-devel@nongnu.org Cc: Polina Vishneva , Michael Roth , Kostiantyn Kostiuk , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , =?UTF-8?q?Daniel=20P=20=2E=20Berrang=C3=A9?= , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Pierrick Bouvier , "Denis V . Lunev" , Andrey Drobyshev Subject: [PATCH 1/4] include/system: Add vio_sockets.h with AF_VSOCK support on Windows Date: Fri, 19 Jun 2026 20:00:19 +0200 Message-ID: <20260619180121.2982356-2-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260619180121.2982356-1-polina.vishneva@virtuozzo.com> References: <20260619180121.2982356-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=130.117.225.111; envelope-from=polina.vishneva@virtuozzo.com; helo=relay.virtuozzo.com X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @virtuozzo.com) X-ZM-MESSAGEID: 1781892183369158500 Content-Type: text/plain; charset="utf-8" Vendored from viosock/inc/vio_sockets.h in kvm-guest-drivers-windows (d76a29c1), reformatted to QEMU coding style (SPDX tag, comment rewrapping, brace placement, include guard). The upstream BSD-3-Clause license was retained because no significant changes were made. Signed-off-by: Polina Vishneva --- MAINTAINERS | 1 + include/system/vio_sockets.h | 57 ++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 include/system/vio_sockets.h diff --git a/MAINTAINERS b/MAINTAINERS index 93df53d87f..3ff59ec0a7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3868,6 +3868,7 @@ Sockets M: Daniel P. Berrange S: Maintained F: include/qemu/sockets.h +F: include/system/vio_sockets.h F: util/qemu-sockets.c F: qapi/sockets.json =20 diff --git a/include/system/vio_sockets.h b/include/system/vio_sockets.h new file mode 100644 index 0000000000..480df24d37 --- /dev/null +++ b/include/system/vio_sockets.h @@ -0,0 +1,57 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * Core definitions for virtio VSOCK address family, based on + * linux/vm_sockets.h and kvm-guest-drivers-windows (d76a29c1). + * + * Copyright (c) 2019 Virtuozzo International GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met : + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and / or other materials provided with the distributio= n. + * 3. Neither the names of the copyright holders nor the names of their + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT HOLDE= RS + * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef VIO_SOCKETS_H +#define VIO_SOCKETS_H + +#include + +#ifndef AF_VSOCK +#define AF_VSOCK 40 +#endif + +#ifndef PF_VSOCK +#define PF_VSOCK AF_VSOCK +#endif + +/* + * Address structure for virtio vsockets. The address family should be set= to + * AF_VSOCK. The structure members should all align on their natural + * boundaries without resorting to compiler packing directives. + */ +typedef struct sockaddr_vm { + ADDRESS_FAMILY svm_family; /* Address family: AF_VSOCK */ + USHORT svm_reserved1; + UINT svm_port; /* Port # in host byte order */ + UINT svm_cid; /* Address in host byte order */ +} SOCKADDR_VM, *PSOCKADDR_VM; + +#endif /* VIO_SOCKETS_H */ --=20 2.54.0 From nobody Sun Jul 26 12:39:53 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=virtuozzo.com ARC-Seal: i=1; a=rsa-sha256; t=1781892189; cv=none; d=zohomail.com; s=zohoarc; b=VitRh+aqf2RScGCpqc+BTdY1Tnzx1nMArua+67dZq1Ogt+LhiC/yYo7ZLcoPf9lfgLg3UhY4QRbU9OqAEigjtpujYQg6HTkCoAcquXqAR5ZauUI/74ZfB3+3BBBh7WOAT4QVdYWSj0jrNXKr4tpK4dUCoCIH7/kKvOtUSU6c7Bs= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781892189; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=yDx6HBQomJat4/Na+8CFSdYFcmmpQNnt9dYbWb4Wd/8=; b=UBUSDwPR+wdbCRtW4XLELVC3sjXDWSDs+QhtsL6dhG6aqYRisw0hZ4mciT00SmJRb2bjD99O/+PWwCZ06ni4GTOFDpXTutaKRLV+bwotnsCsmUO14imVjcO3+JI+/00jepgfEYkEL9fvg+LfreWuTFD3nvSg+2Pz4+j0LszjpCc= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781892189645514.1821992521913; Fri, 19 Jun 2026 11:03:09 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wadY5-0000d6-QO; Fri, 19 Jun 2026 14:02:37 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wadY4-0000cs-5W for qemu-devel@nongnu.org; Fri, 19 Jun 2026 14:02:36 -0400 Received: from relay.virtuozzo.com ([130.117.225.111]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wadY2-0004CS-Ig for qemu-devel@nongnu.org; Fri, 19 Jun 2026 14:02:35 -0400 Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1wadXR-001Gyc-1R; Fri, 19 Jun 2026 20:02:29 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=MIME-Version:Message-ID:Date:Subject:From: Content-Type; bh=yDx6HBQomJat4/Na+8CFSdYFcmmpQNnt9dYbWb4Wd/8=; b=TCAkmDfP0HCL Q6CT+dVmzua7F3cK33sKLvao0fEtkoGN3dpdxBu6GryS4FhLmzwtbq14CZEXhVQnlA8DlchyM3JcS aEkfZs59cUVQr/pgDItiKW85OUVatRXl5pSbQawzmif/PHU6mnQORfVz9QKqSgrnQ+WXBtswcAm/6 PanBZgHuGXLBi2fSBXzgmAMq7aWrFm4m3j1fvUXuEcursb5fd+Q0fXznU2NGBaO2IBqCYwK6HvdjL kgu/lIutosqKv+bb0kb0GrV5XrTwQPSRKRRRemRCKxi7sQVUrI4fHmXXsHJ9bqkqmDoh7/EPenR4v k/UC/97/sDIaf4reP7SkHQ==; From: Polina Vishneva To: qemu-devel@nongnu.org Cc: Polina Vishneva , Michael Roth , Kostiantyn Kostiuk , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , =?UTF-8?q?Daniel=20P=20=2E=20Berrang=C3=A9?= , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Pierrick Bouvier , "Denis V . Lunev" , Andrey Drobyshev Subject: [PATCH 2/4] util/qemu-sockets: Enable AF_VSOCK on Windows Date: Fri, 19 Jun 2026 20:00:20 +0200 Message-ID: <20260619180121.2982356-3-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260619180121.2982356-1-polina.vishneva@virtuozzo.com> References: <20260619180121.2982356-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=130.117.225.111; envelope-from=polina.vishneva@virtuozzo.com; helo=relay.virtuozzo.com X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @virtuozzo.com) X-ZM-MESSAGEID: 1781892191400158500 Content-Type: text/plain; charset="utf-8" Include the vendored vio_sockets.h on Windows, and set CONFIG_AF_VSOCK unconditionally since the header is always present in-tree. socket_parse("vsock:...") and the SOCKET_ADDRESS_TYPE_VSOCK dispatch in socket_connect()/socket_listen() now resolve to the real vsock_*_saddr() helpers on Windows instead of the vsock_unsupported() stub. Note that CONFIG_AF_VSOCK is a tree-wide switch: this enables the vsock code paths for every Windows build, not just the guest agent. QMP now accepts vsock socket addresses on Windows; without a viosock provider installed they fail at runtime rather than being rejected up front as unsupported. Signed-off-by: Polina Vishneva --- meson.build | 15 +++++++++++---- util/qemu-sockets.c | 4 ++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index e026851309..34268f0348 100644 --- a/meson.build +++ b/meson.build @@ -3136,10 +3136,17 @@ have_afalg =3D get_option('crypto_afalg') \ '''), error_message: 'AF_ALG requested but could not be detected').allow= ed() config_host_data.set('CONFIG_AF_ALG', have_afalg) =20 -config_host_data.set('CONFIG_AF_VSOCK', cc.has_header_symbol( - 'linux/vm_sockets.h', 'AF_VSOCK', - prefix: '#include ', -)) +if host_os !=3D 'windows' + config_host_data.set('CONFIG_AF_VSOCK', cc.has_header_symbol( + 'linux/vm_sockets.h', 'AF_VSOCK', + prefix: '#include ', + )) +else + # The vendored vio_sockets.h is always available. + # This guarantees compile-time availability only; at run-time, it's + # generally useless if viosock isn't installed (no AF_VSOCK WSP). + config_host_data.set('CONFIG_AF_VSOCK', true) +endif =20 have_vss =3D false have_vss_sdk =3D false # old xp/2003 SDK diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 4773755fd5..80bd5cca7f 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -18,7 +18,11 @@ #include "qemu/osdep.h" =20 #ifdef CONFIG_AF_VSOCK +#ifndef _WIN32 #include +#else +#include "system/vio_sockets.h" +#endif #endif /* CONFIG_AF_VSOCK */ =20 #include "monitor/monitor.h" --=20 2.54.0 From nobody Sun Jul 26 12:39:53 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=virtuozzo.com ARC-Seal: i=1; a=rsa-sha256; t=1781892199; cv=none; d=zohomail.com; s=zohoarc; b=nqVmlrdMI/zZvnz9LeekFxucvUq0LcQz9tCjvPopP3GthW5vr3SKFnvPXtEgf1Fty+rgcS6o/+leVWRVRxESjmVDrwStEI9AG+TbS5xip6xGWoP21wJ/LzNzUxRzYMKh9zCsXJIT1HEhgFB9arnqzhPlzQjjekzSKWYHJSAcuLE= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781892199; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=YqONhlrHRfFvrkeKcF4zVsbsxWXUSytVngRxBICCDsc=; b=lqm6O1g70PAHFqu0Ej5yCe2H3/xTyjfUafS5NmeSSmaTDv+PHH40u19NDjkP8nhEGANK9Kaw4aduKvTkyL2RM0uVQt0HSI7vx1ZQqYloDFsgPh5b4++ZQtcwodsVdPGTCw0zr6i99er6vx0jpId2qjRvciUEfaBfp4gkvFSHf3g= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781892199243254.6017175973551; Fri, 19 Jun 2026 11:03:19 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wadYE-0000eS-DG; Fri, 19 Jun 2026 14:02:48 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wadY9-0000dv-QK for qemu-devel@nongnu.org; Fri, 19 Jun 2026 14:02:41 -0400 Received: from relay.virtuozzo.com ([130.117.225.111]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wadY6-0004F8-IQ for qemu-devel@nongnu.org; Fri, 19 Jun 2026 14:02:41 -0400 Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1wadXV-001Gyc-1d; Fri, 19 Jun 2026 20:02:33 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=MIME-Version:Message-ID:Date:Subject:From: Content-Type; bh=YqONhlrHRfFvrkeKcF4zVsbsxWXUSytVngRxBICCDsc=; b=pPwBcFU04NO0 02srbnqIcop3bUDBEDJN9HjnMVTcRrZemMFLfQYKGM/glfiPRCXcWWY1EENArcbgU3ENUDbmVkUJv JhnV18Q90moVQd7j56ncy/ZRPC4ibboiy8dEGmkpKv+iDmQsAUrJjpheW71efloJv4y+xReYEp8aT YIP4DtBxCchfWHoOxJfYHk6oGQcBOhiftmXO0eou1d/CjZenQj51WRJEwUxjIwTIFmdGeNzJxGuCW ZEkG7euvqi5/Ah8P9D2w4dyerAWxOKKt2EX8PgFKTLjiEKqsI98DMayU93NgCDWv3+ulQGJrSxb67 CwlTZMa5jkwfNccbia4dCw==; From: Polina Vishneva To: qemu-devel@nongnu.org Cc: Polina Vishneva , Michael Roth , Kostiantyn Kostiuk , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , =?UTF-8?q?Daniel=20P=20=2E=20Berrang=C3=A9?= , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Pierrick Bouvier , "Denis V . Lunev" , Andrey Drobyshev Subject: [PATCH 3/4] qga: Move common channel routines to channel-common Date: Fri, 19 Jun 2026 20:00:21 +0200 Message-ID: <20260619180121.2982356-4-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260619180121.2982356-1-polina.vishneva@virtuozzo.com> References: <20260619180121.2982356-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=130.117.225.111; envelope-from=polina.vishneva@virtuozzo.com; helo=relay.virtuozzo.com X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @virtuozzo.com) X-ZM-MESSAGEID: 1781892201322158500 Content-Type: text/plain; charset="utf-8" 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 --- qga/channel-common.c | 115 +++++++++++++++++++++++++++ qga/channel-common.h | 44 +++++++++++ qga/channel-posix.c | 179 +++++++++++++------------------------------ qga/channel-win32.c | 14 ++-- qga/meson.build | 3 +- 5 files changed, 223 insertions(+), 132 deletions(-) create mode 100644 qga/channel-common.c create mode 100644 qga/channel-common.h diff --git a/qga/channel-common.c b/qga/channel-common.c new file mode 100644 index 0000000000..9732a19477 --- /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 =3D g_strdup_printf("vsock:%s", path); + addr =3D socket_parse(addr_str, errp); + g_free(addr_str); + if (!addr) { + return -1; + } + + fd =3D 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 =3D NULL; +} + +int ga_channel_common_client_add(GAChannelCommon *c, GIOChannel *client_ch, + GIOFunc event_fn, gpointer data) +{ + GError *err =3D NULL; + + g_assert(c && !c->client_channel); + g_io_channel_set_encoding(client_ch, NULL, &err); + if (err !=3D 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 =3D 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 =3D 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, NU= LL); +} + +GIOStatus ga_channel_common_write_all(GAChannelCommon *c, + const gchar *buf, gsize size) +{ + GError *err =3D NULL; + gsize written =3D 0; + GIOStatus status =3D G_IO_STATUS_NORMAL; + + while (size) { + g_debug("sending data, count: %d", (int)size); + status =3D g_io_channel_write_chars(c->client_channel, buf, size, + &written, &err); + if (status =3D=3D G_IO_STATUS_NORMAL) { + size -=3D written; + buf +=3D written; + } else if (status !=3D G_IO_STATUS_AGAIN) { + g_warning("error writing to channel: %s", err->message); + return status; + } + } + + do { + status =3D g_io_channel_flush(c->client_channel, &err); + } while (status =3D=3D G_IO_STATUS_AGAIN); + + if (status !=3D G_IO_STATUS_NORMAL) { + g_warning("error flushing channel: %s", err->message); + } + + return status; +} + +void ga_channel_common_free(GAChannelCommon *c) +{ + if (c->listen_channel) { + ga_channel_common_listen_close(c); + } + if (c->client_channel) { + ga_channel_common_client_close(c); + } +} diff --git a/qga/channel-common.h b/qga/channel-common.h new file mode 100644 index 0000000000..8e82bf0909 --- /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 kee= ps + * 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-posix.c b/qga/channel-posix.c index 9ccc8b7bd1..9d346e82b2 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -3,7 +3,7 @@ #include #include "qapi/error.h" #include "qemu/sockets.h" -#include "channel.h" +#include "channel-common.h" #include "cutils.h" =20 #ifdef CONFIG_SOLARIS @@ -13,20 +13,36 @@ #define GA_CHANNEL_BAUDRATE_DEFAULT B38400 /* for isa-serial channels */ =20 struct GAChannel { - GIOChannel *listen_channel; - GIOChannel *client_channel; - GAChannelMethod method; - GAChannelCallback event_cb; - gpointer user_data; + GAChannelCommon common; }; =20 -static int ga_channel_client_add(GAChannel *c, int fd); +static gboolean ga_channel_client_event(GIOChannel *channel, + GIOCondition condition, gpointer d= ata); +static gboolean ga_channel_listen_accept(GIOChannel *channel, + GIOCondition condition, gpointer = data); + +static GIOChannel *ga_channel_make_gio(int fd) +{ + GIOChannel *ch =3D g_io_channel_unix_new(fd); + g_assert(ch); + return ch; +} + +static void ga_channel_listen_add(GAChannel *c, int listen_fd, bool create) +{ + if (create) { + c->common.listen_channel =3D ga_channel_make_gio(listen_fd); + } + g_io_add_watch(c->common.listen_channel, G_IO_IN, + ga_channel_listen_accept, c); +} =20 static gboolean ga_channel_listen_accept(GIOChannel *channel, GIOCondition condition, gpointer = data) { GAChannel *c =3D data; - int ret, client_fd; + int client_fd; + GIOChannel *client_ch; bool accepted =3D false; Error *err =3D NULL; =20 @@ -42,10 +58,11 @@ static gboolean ga_channel_listen_accept(GIOChannel *ch= annel, error_free(err); goto out; } - ret =3D ga_channel_client_add(c, client_fd); - if (ret) { + client_ch =3D ga_channel_make_gio(client_fd); + if (ga_channel_common_client_add(&c->common, client_ch, + ga_channel_client_event, c)) { g_warning("error setting up connection"); - close(client_fd); + ga_channel_common_gio_destroy(client_ch); goto out; } accepted =3D true; @@ -55,39 +72,6 @@ out: return !accepted; } =20 -/* start polling for readable events on listen fd, new=3D=3Dtrue - * indicates we should use the existing s->listen_channel - */ -static void ga_channel_listen_add(GAChannel *c, int listen_fd, bool create) -{ - if (create) { - c->listen_channel =3D g_io_channel_unix_new(listen_fd); - } - g_io_add_watch(c->listen_channel, G_IO_IN, ga_channel_listen_accept, c= ); -} - -static void ga_channel_listen_close(GAChannel *c) -{ - g_assert(c->listen_channel); - g_io_channel_shutdown(c->listen_channel, true, NULL); - g_io_channel_unref(c->listen_channel); - c->listen_channel =3D NULL; -} - -/* cleanup state for closed connection/session, start accepting new - * connections if we're in listening mode - */ -static void ga_channel_client_close(GAChannel *c) -{ - g_assert(c->client_channel); - g_io_channel_shutdown(c->client_channel, true, NULL); - g_io_channel_unref(c->client_channel); - c->client_channel =3D NULL; - if (c->listen_channel) { - ga_channel_listen_add(c, 0, false); - } -} - static gboolean ga_channel_client_event(GIOChannel *channel, GIOCondition condition, gpointer d= ata) { @@ -95,44 +79,28 @@ static gboolean ga_channel_client_event(GIOChannel *cha= nnel, gboolean client_cont; =20 g_assert(c); - if (c->event_cb) { - client_cont =3D c->event_cb(condition, c->user_data); + if (c->common.event_cb) { + client_cont =3D c->common.event_cb(condition, c->common.user_data); if (!client_cont) { - ga_channel_client_close(c); + ga_channel_common_client_close(&c->common); + if (c->common.listen_channel) { + ga_channel_listen_add(c, 0, false); + } return false; } } return true; } =20 -static int ga_channel_client_add(GAChannel *c, int fd) -{ - GIOChannel *client_channel; - GError *err =3D NULL; - - g_assert(c && !c->client_channel); - client_channel =3D g_io_channel_unix_new(fd); - g_assert(client_channel); - g_io_channel_set_encoding(client_channel, NULL, &err); - if (err !=3D NULL) { - g_warning("error setting channel encoding to binary"); - g_error_free(err); - return -1; - } - g_io_add_watch(client_channel, G_IO_IN | G_IO_HUP, - ga_channel_client_event, c); - c->client_channel =3D client_channel; - return 0; -} - static gboolean ga_channel_open(GAChannel *c, const gchar *path, GAChannelMethod method, int fd, Error **er= rp) { - int ret; - c->method =3D method; + c->common.method =3D method; =20 - switch (c->method) { + switch (method) { case GA_CHANNEL_VIRTIO_SERIAL: { + GIOChannel *ch; + assert(fd < 0); fd =3D qga_open_cloexec( path, @@ -147,8 +115,7 @@ static gboolean ga_channel_open(GAChannel *c, const gch= ar *path, return false; } #ifdef CONFIG_SOLARIS - ret =3D ioctl(fd, I_SETSIG, S_OUTPUT | S_INPUT | S_HIPRI); - if (ret =3D=3D -1) { + if (ioctl(fd, I_SETSIG, S_OUTPUT | S_INPUT | S_HIPRI) =3D=3D -1) { error_setg_errno(errp, errno, "error setting event mask for ch= annel"); close(fd); return false; @@ -173,16 +140,18 @@ static gboolean ga_channel_open(GAChannel *c, const g= char *path, return false; } #endif /* __FreeBSD__ */ - ret =3D ga_channel_client_add(c, fd); - if (ret) { + ch =3D ga_channel_make_gio(fd); + if (ga_channel_common_client_add(&c->common, ch, + ga_channel_client_event, c)) { error_setg(errp, "error adding channel to main loop"); - close(fd); + ga_channel_common_gio_destroy(ch); return false; } break; } case GA_CHANNEL_ISA_SERIAL: { struct termios tio; + GIOChannel *ch; =20 assert(fd < 0); fd =3D qga_open_cloexec(path, O_RDWR | O_NOCTTY | O_NONBLOCK, 0); @@ -206,10 +175,11 @@ static gboolean ga_channel_open(GAChannel *c, const g= char *path, /* flush everything waiting for read/xmit, it's garbage at this po= int */ tcflush(fd, TCIFLUSH); tcsetattr(fd, TCSANOW, &tio); - ret =3D ga_channel_client_add(c, fd); - if (ret) { + ch =3D ga_channel_make_gio(fd); + if (ga_channel_common_client_add(&c->common, ch, + ga_channel_client_event, c)) { error_setg(errp, "error adding channel to main loop"); - close(fd); + ga_channel_common_gio_destroy(ch); return false; } break; @@ -226,18 +196,7 @@ static gboolean ga_channel_open(GAChannel *c, const gc= har *path, } case GA_CHANNEL_VSOCK_LISTEN: { if (fd < 0) { - SocketAddress *addr; - char *addr_str; - - addr_str =3D g_strdup_printf("vsock:%s", path); - addr =3D socket_parse(addr_str, errp); - g_free(addr_str); - if (!addr) { - return false; - } - - fd =3D socket_listen(addr, 1, errp); - qapi_free_SocketAddress(addr); + fd =3D ga_channel_common_vsock_listen(path, errp); if (fd < 0) { return false; } @@ -255,37 +214,12 @@ static gboolean ga_channel_open(GAChannel *c, const g= char *path, =20 GIOStatus ga_channel_write_all(GAChannel *c, const gchar *buf, gsize size) { - GError *err =3D NULL; - gsize written =3D 0; - GIOStatus status =3D G_IO_STATUS_NORMAL; - - while (size) { - g_debug("sending data, count: %d", (int)size); - status =3D g_io_channel_write_chars(c->client_channel, buf, size, - &written, &err); - if (status =3D=3D G_IO_STATUS_NORMAL) { - size -=3D written; - buf +=3D written; - } else if (status !=3D G_IO_STATUS_AGAIN) { - g_warning("error writing to channel: %s", err->message); - return status; - } - } - - do { - status =3D g_io_channel_flush(c->client_channel, &err); - } while (status =3D=3D G_IO_STATUS_AGAIN); - - if (status !=3D G_IO_STATUS_NORMAL) { - g_warning("error flushing channel: %s", err->message); - } - - return status; + return ga_channel_common_write_all(&c->common, buf, size); } =20 GIOStatus ga_channel_read(GAChannel *c, gchar *buf, gsize size, gsize *cou= nt) { - return g_io_channel_read_chars(c->client_channel, buf, size, count, NU= LL); + return ga_channel_common_read(&c->common, buf, size, count); } =20 GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path, @@ -293,8 +227,8 @@ GAChannel *ga_channel_new(GAChannelMethod method, const= gchar *path, { Error *err =3D NULL; GAChannel *c =3D g_new0(GAChannel, 1); - c->event_cb =3D cb; - c->user_data =3D opaque; + c->common.event_cb =3D cb; + c->common.user_data =3D opaque; =20 if (!ga_channel_open(c, path, method, listen_fd, &err)) { g_critical("%s", error_get_pretty(err)); @@ -308,11 +242,6 @@ GAChannel *ga_channel_new(GAChannelMethod method, cons= t gchar *path, =20 void ga_channel_free(GAChannel *c) { - if (c->listen_channel) { - ga_channel_listen_close(c); - } - if (c->client_channel) { - ga_channel_client_close(c); - } + ga_channel_common_free(&c->common); g_free(c); } diff --git a/qga/channel-win32.c b/qga/channel-win32.c index 779007e39b..2bd6fc1538 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -2,7 +2,7 @@ #include #include #include "guest-agent-core.h" -#include "channel.h" +#include "channel-common.h" =20 typedef struct GAChannelReadState { guint thread_id; @@ -15,9 +15,9 @@ typedef struct GAChannelReadState { } GAChannelReadState; =20 struct GAChannel { + GAChannelCommon common; + HANDLE handle; - GAChannelCallback cb; - gpointer user_data; GAChannelReadState rstate; GIOCondition pending_events; /* TODO: use GAWatch.pollfd.revents */ GSource *source; @@ -157,7 +157,7 @@ static gboolean ga_channel_dispatch(GSource *source, GS= ourceFunc unused, gboolean success; =20 g_debug("dispatch"); - success =3D c->cb(watch->pollfd.revents, c->user_data); + success =3D c->common.event_cb(watch->pollfd.revents, c->common.user_d= ata); =20 if (c->pending_events & G_IO_ERR) { g_critical("channel error, removing source"); @@ -330,8 +330,8 @@ GAChannel *ga_channel_new(GAChannelMethod method, const= gchar *path, return NULL; } =20 - c->cb =3D cb; - c->user_data =3D opaque; + c->common.event_cb =3D cb; + c->common.user_data =3D opaque; =20 sec_attrs.nLength =3D sizeof(SECURITY_ATTRIBUTES); sec_attrs.lpSecurityDescriptor =3D NULL; @@ -354,6 +354,8 @@ void ga_channel_free(GAChannel *c) if (c->rstate.ov.hEvent) { CloseHandle(c->rstate.ov.hEvent); } + + ga_channel_common_free(&c->common); g_free(c->rstate.buf); g_free(c); } diff --git a/qga/meson.build b/qga/meson.build index cfa2157efb..0e482e690a 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -66,7 +66,8 @@ qga_ss.add(files( 'guest-agent-command-state.c', 'main.c', 'cutils.c', - 'commands-common-ssh.c' + 'commands-common-ssh.c', + 'channel-common.c' )) if host_os =3D=3D 'windows' qga_ss.add(files( --=20 2.54.0 From nobody Sun Jul 26 12:39:53 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=quarantine dis=none) header.from=virtuozzo.com ARC-Seal: i=1; a=rsa-sha256; t=1781892200; cv=none; d=zohomail.com; s=zohoarc; b=MnMhg0cvI9UFYQgTIX6xkb0VLk3L3NXB+Zjsl6MTGzzj5NFxTtx4IlHwrR2jjXY6O39WFMm1Vwa3xbry1Z2C2iL/qGrO9p1VhFY0s+avLAy2vzmAlz/BISkZllVl/IvVVLHUIm5WvMYARbajb7N9fz7cIh22dqU4EMmf2cYnbZ0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1781892200; h=Content-Transfer-Encoding:Cc:Cc:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:Subject:To:To:Message-Id:Reply-To; bh=qMqk5rMPxrFoglWjWnmC7Ahe0HDQf2mbmfBMYYk6VlE=; b=Lrm24MEbYOUMRjl6YZz2/U73JXT/9isKbRUsu6ExA18jvNmCWRP57tEcobzcC9BMlCGBP3GMZY5G9jM+Gt8HPsV3ucjOn1U1ajsnRjcpJFIF+UduMkNryy8vZYiYG2VfaMKo+YHsi6pcWzE+tuY23EUsCxhD4F9+eQnvDhrr+wI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=quarantine dis=none) Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1781892200846234.97873956086028; Fri, 19 Jun 2026 11:03:20 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1wadYK-0000hb-N8; Fri, 19 Jun 2026 14:02:52 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wadYD-0000eT-Ob for qemu-devel@nongnu.org; Fri, 19 Jun 2026 14:02:45 -0400 Received: from relay.virtuozzo.com ([130.117.225.111]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1wadYB-0004Id-CU for qemu-devel@nongnu.org; Fri, 19 Jun 2026 14:02:45 -0400 Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1wadXZ-001Gyc-1L; Fri, 19 Jun 2026 20:02:37 +0200 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=virtuozzo.com; s=relay; h=MIME-Version:Message-ID:Date:Subject:From: Content-Type; bh=qMqk5rMPxrFoglWjWnmC7Ahe0HDQf2mbmfBMYYk6VlE=; b=hRq29yPpZror uukiOJ22ix30w7YlC7dmSUBY/2DpwKIAZiLvdgY+ytGLo1HUp0eLLJkvSXvzOmXMHBkusovwssBPN +2Tc7w1pgHIp8ZcAcR5HBaD1cjoR0WyrbCofcvzmjAU8oYW7/vHNz/FLu2Mpu0p05VttZ3xrgcYz1 Jltry1U9W9CiNWmuUbqCdr/Hp0sbVhZpMfVmw8KFssEuXRsE32TatRUHFhkCLiWn/MxYCyrwwtnBr y+BnWJw1AgJa710LKRziqcRD55qjbtA+YKBu3mSawARaIjaFTOKQ2Nc8lPf/OWYA2gDAQ4XP4NSwL +ss9WGIxdoVKDQ5tjaaEzw==; From: Polina Vishneva To: qemu-devel@nongnu.org Cc: Polina Vishneva , Michael Roth , Kostiantyn Kostiuk , Paolo Bonzini , =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= , =?UTF-8?q?Daniel=20P=20=2E=20Berrang=C3=A9?= , =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= , Pierrick Bouvier , "Denis V . Lunev" , Andrey Drobyshev Subject: [PATCH 4/4] qga: Add AF_VSOCK channel support on Windows Date: Fri, 19 Jun 2026 20:00:22 +0200 Message-ID: <20260619180121.2982356-5-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260619180121.2982356-1-polina.vishneva@virtuozzo.com> References: <20260619180121.2982356-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=130.117.225.111; envelope-from=polina.vishneva@virtuozzo.com; helo=relay.virtuozzo.com X-Spam_score_int: -43 X-Spam_score: -4.4 X-Spam_bar: ---- X-Spam_report: (-4.4 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @virtuozzo.com) X-ZM-MESSAGEID: 1781892201326158500 Content-Type: text/plain; charset="utf-8" Add vsock-listen channel mode to the Windows guest agent. The listen/accept path uses the standard QEMU socket helpers (socket_parse(), socket_listen(), qemu_accept()) and plugs the resulting SOCKET into the GMainLoop via g_io_channel_win32_new_socket(). Signed-off-by: Polina Vishneva --- docs/interop/qemu-ga.rst | 4 + qga/channel-win32.c | 257 ++++++++++++++++++++++++++++++++------- qga/main.c | 6 + 3 files changed, 222 insertions(+), 45 deletions(-) diff --git a/docs/interop/qemu-ga.rst b/docs/interop/qemu-ga.rst index d16cc1b9f0..9c415fc8f8 100644 --- a/docs/interop/qemu-ga.rst +++ b/docs/interop/qemu-ga.rst @@ -59,6 +59,10 @@ Options Transport method: one of ``unix-listen``, ``virtio-serial``, or ``isa-serial``, or ``vsock-listen`` (``virtio-serial`` is the default). =20 + .. note:: + On Windows, ``vsock-listen`` requires the viosock driver from + kvm-guest-drivers-windows recent enough to include commit ``687c776``. + .. option:: -p, --path=3DPATH =20 Device/socket path (the default for virtio-serial is diff --git a/qga/channel-win32.c b/qga/channel-win32.c index 2bd6fc1538..257576815a 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -1,6 +1,8 @@ #include "qemu/osdep.h" #include #include +#include "qapi/error.h" +#include "qemu/sockets.h" #include "guest-agent-core.h" #include "channel-common.h" =20 @@ -17,6 +19,9 @@ typedef struct GAChannelReadState { struct GAChannel { GAChannelCommon common; =20 + int listen_fd; + int client_fd; + HANDLE handle; GAChannelReadState rstate; GIOCondition pending_events; /* TODO: use GAWatch.pollfd.revents */ @@ -198,7 +203,116 @@ static GSource *ga_channel_create_watch(GAChannel *c) return source; } =20 -GIOStatus ga_channel_read(GAChannel *c, char *buf, size_t size, gsize *cou= nt) +/* + * Socket mode: GIOChannel-based listen/accept/client helpers + */ + +static gboolean ga_channel_socket_client_event(GIOChannel *channel, + GIOCondition condition, + gpointer data); +static gboolean ga_channel_socket_listen_accept(GIOChannel *channel, + GIOCondition condition, + gpointer data); + +static void ga_channel_socket_listen_add(GAChannel *c, bool create) +{ + if (create) { + c->common.listen_channel =3D + g_io_channel_win32_new_socket(_get_osfhandle(c->listen_fd)); + g_assert(c->common.listen_channel); + } + g_io_add_watch(c->common.listen_channel, G_IO_IN, + ga_channel_socket_listen_accept, c); +} + +static gboolean ga_channel_socket_client_close(GAChannel *c) +{ + /* Free the CRT fd wrapper while the SOCKET is still valid. */ + if (c->client_fd >=3D 0) { + qemu_close_socket_osfhandle(c->client_fd); + c->client_fd =3D -1; + } + ga_channel_common_client_close(&c->common); + if (c->common.listen_channel) { + ga_channel_socket_listen_add(c, false); + } + return false; +} + +static gboolean ga_channel_socket_listen_accept(GIOChannel *channel, + GIOCondition condition, + gpointer data) +{ + GAChannel *c =3D data; + int client_fd; + GIOChannel *client_ch; + bool accepted =3D false; + + client_fd =3D qemu_accept(c->listen_fd, NULL, NULL); + if (client_fd < 0) { + g_warning("error accepting connection: %s", strerror(errno)); + goto out; + } + + /* + * g_io_channel_win32_new_socket() makes the socket nonblocking via + * WSAEventSelect. + */ + client_ch =3D g_io_channel_win32_new_socket(_get_osfhandle(client_fd)); + g_assert(client_ch); + + if (ga_channel_common_client_add(&c->common, client_ch, + ga_channel_socket_client_event, c)) { + g_warning("error setting up connection"); + /* + * Release the CRT fd wrapper while its SOCKET is still open - only + * then qemu_close_socket_osfhandle() can detach the fd and let the + * channel teardown perform the single closesocket(). + */ + qemu_close_socket_osfhandle(client_fd); + ga_channel_common_gio_destroy(client_ch); + goto out; + } + c->client_fd =3D client_fd; + accepted =3D true; + +out: + /* only accept 1 connection at a time */ + return !accepted; +} + +static gboolean ga_channel_socket_client_event(GIOChannel *channel, + GIOCondition condition, + gpointer data) +{ + GAChannel *c =3D data; + + g_assert(c); + if (!c->common.event_cb(condition, c->common.user_data)) { + return ga_channel_socket_client_close(c); + } + /* + * A peer that sends data and disconnects at once is never reported: + * GLib turns the coalesced FD_READ|FD_CLOSE into a plain G_IO_IN, + * and Winsock records FD_CLOSE only once, so no later event follows. + * Peek after each event to detect the disconnect. + */ + if (c->client_fd >=3D 0) { + char peek; + ssize_t r =3D recv(c->client_fd, &peek, 1, MSG_PEEK); + if (r =3D=3D 0 || (r < 0 && errno !=3D EAGAIN)) { + return ga_channel_socket_client_close(c); + } + } + return true; +} + +/* + * Handle mode: overlapped I/O read/write + */ + +static GIOStatus ga_channel_handle_read(GAChannel *c, char *buf, size_t si= ze, + gsize *count) { GAChannelReadState *rs =3D &c->rstate; GIOStatus status; @@ -221,8 +335,8 @@ GIOStatus ga_channel_read(GAChannel *c, char *buf, size= _t size, gsize *count) return status; } =20 -static GIOStatus ga_channel_write(GAChannel *c, const char *buf, size_t si= ze, - size_t *count) +static GIOStatus ga_channel_handle_write(GAChannel *c, const char *buf, + size_t size, size_t *count) { GIOStatus status; OVERLAPPED ov =3D {0}; @@ -262,13 +376,25 @@ static GIOStatus ga_channel_write(GAChannel *c, const= char *buf, size_t size, return status; } =20 +GIOStatus ga_channel_read(GAChannel *c, char *buf, size_t size, gsize *cou= nt) +{ + if (c->common.method =3D=3D GA_CHANNEL_VSOCK_LISTEN) { + return ga_channel_common_read(&c->common, buf, size, count); + } + return ga_channel_handle_read(c, buf, size, count); +} + GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size) { GIOStatus status =3D G_IO_STATUS_NORMAL; size_t count =3D 0; =20 + if (c->common.method =3D=3D GA_CHANNEL_VSOCK_LISTEN) { + return ga_channel_common_write_all(&c->common, buf, size); + } + while (size) { - status =3D ga_channel_write(c, buf, size, &count); + status =3D ga_channel_handle_write(c, buf, size, &count); if (status =3D=3D G_IO_STATUS_NORMAL) { size -=3D count; buf +=3D count; @@ -276,86 +402,127 @@ GIOStatus ga_channel_write_all(GAChannel *c, const c= har *buf, size_t size) break; } } - return status; } =20 static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, - const gchar *path) + const gchar *path, Error **errp) { COMMTIMEOUTS comTimeOut =3D {0}; gchar newpath[MAXPATHLEN] =3D {0}; comTimeOut.ReadIntervalTimeout =3D 1; =20 - if (method !=3D GA_CHANNEL_VIRTIO_SERIAL && method !=3D GA_CHANNEL_ISA= _SERIAL) { - g_critical("unsupported communication method"); - return false; - } + c->common.method =3D method; =20 - if (method =3D=3D GA_CHANNEL_ISA_SERIAL) { - snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path); - } else { - g_strlcpy(newpath, path, sizeof(newpath)); - } + switch (method) { + case GA_CHANNEL_VIRTIO_SERIAL: + case GA_CHANNEL_ISA_SERIAL: + if (method =3D=3D GA_CHANNEL_ISA_SERIAL) { + snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path); + } else { + g_strlcpy(newpath, path, sizeof(newpath)); + } =20 - c->handle =3D CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0, NUL= L, - OPEN_EXISTING, - FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, = NULL); - if (c->handle =3D=3D INVALID_HANDLE_VALUE) { - g_autofree gchar *emsg =3D g_win32_error_message(GetLastError()); - g_critical("error opening path %s: %s", newpath, emsg); - return false; + c->handle =3D CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0,= NULL, + OPEN_EXISTING, + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPP= ED, + NULL); + if (c->handle =3D=3D INVALID_HANDLE_VALUE) { + g_autofree gchar *emsg =3D g_win32_error_message(GetLastError(= )); + error_setg(errp, "error opening path %s: %s", newpath, emsg); + c->handle =3D NULL; + return false; + } + + if (method =3D=3D GA_CHANNEL_ISA_SERIAL + && !SetCommTimeouts(c->handle, &comTimeOut)) { + g_autofree gchar *emsg =3D g_win32_error_message(GetLastError(= )); + error_setg(errp, "error setting timeout for com port: %s", ems= g); + CloseHandle(c->handle); + c->handle =3D NULL; + return false; + } + return true; + +#ifdef CONFIG_AF_VSOCK + case GA_CHANNEL_VSOCK_LISTEN: { + int fd =3D ga_channel_common_vsock_listen(path, errp); + if (fd < 0) { + return false; + } + + c->listen_fd =3D fd; + ga_channel_socket_listen_add(c, true); + return true; } +#endif =20 - if (method =3D=3D GA_CHANNEL_ISA_SERIAL - && !SetCommTimeouts(c->handle, &comTimeOut)) { - g_autofree gchar *emsg =3D g_win32_error_message(GetLastError()); - g_critical("error setting timeout for com port: %s", emsg); - CloseHandle(c->handle); + default: + error_setg(errp, "unsupported communication method"); return false; } - - return true; } =20 GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path, int listen_fd, GAChannelCallback cb, gpointer op= aque) { + Error *err =3D NULL; GAChannel *c =3D g_new0(GAChannel, 1); - SECURITY_ATTRIBUTES sec_attrs; =20 - if (!ga_channel_open(c, method, path)) { - g_critical("error opening channel"); - g_free(c); + /* listen_fd is for systemd socket activation; not applicable on Windo= ws. */ + (void)listen_fd; + c->listen_fd =3D -1; + c->client_fd =3D -1; + c->common.event_cb =3D cb; + c->common.user_data =3D opaque; + + if (!ga_channel_open(c, method, path, &err)) { + g_critical("%s", error_get_pretty(err)); + error_free(err); + ga_channel_free(c); return NULL; } =20 - c->common.event_cb =3D cb; - c->common.user_data =3D opaque; + if (c->common.method !=3D GA_CHANNEL_VSOCK_LISTEN) { + SECURITY_ATTRIBUTES sec_attrs; + + sec_attrs.nLength =3D sizeof(SECURITY_ATTRIBUTES); + sec_attrs.lpSecurityDescriptor =3D NULL; + sec_attrs.bInheritHandle =3D false; =20 - sec_attrs.nLength =3D sizeof(SECURITY_ATTRIBUTES); - sec_attrs.lpSecurityDescriptor =3D NULL; - sec_attrs.bInheritHandle =3D false; + c->rstate.buf_size =3D QGA_READ_COUNT_DEFAULT; + c->rstate.buf =3D g_malloc(QGA_READ_COUNT_DEFAULT); + c->rstate.ov.hEvent =3D CreateEvent(&sec_attrs, FALSE, FALSE, NULL= ); =20 - c->rstate.buf_size =3D QGA_READ_COUNT_DEFAULT; - c->rstate.buf =3D g_malloc(QGA_READ_COUNT_DEFAULT); - c->rstate.ov.hEvent =3D CreateEvent(&sec_attrs, FALSE, FALSE, NULL); + c->source =3D ga_channel_create_watch(c); + g_source_attach(c->source, NULL); + } =20 - c->source =3D ga_channel_create_watch(c); - g_source_attach(c->source, NULL); return c; } =20 void ga_channel_free(GAChannel *c) { + if (c->common.method =3D=3D GA_CHANNEL_VSOCK_LISTEN) { + /* Free the CRT fd wrappers before the GIOChannels close the SOCKE= Ts. */ + if (c->listen_fd >=3D 0) { + qemu_close_socket_osfhandle(c->listen_fd); + } + if (c->client_fd >=3D 0) { + qemu_close_socket_osfhandle(c->client_fd); + } + ga_channel_common_free(&c->common); + g_free(c); + return; + } + + /* Handle mode */ if (c->source) { g_source_destroy(c->source); } if (c->rstate.ov.hEvent) { CloseHandle(c->rstate.ov.hEvent); } - - ga_channel_common_free(&c->common); g_free(c->rstate.buf); g_free(c); } diff --git a/qga/main.c b/qga/main.c index fd19c7037d..35068ac7bb 100644 --- a/qga/main.c +++ b/qga/main.c @@ -1675,6 +1675,12 @@ int main(int argc, char **argv) =20 config->log_level =3D G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; =20 + /* Required for Windows, harmless otherwise. */ + if (socket_init() < 0) { + g_critical("failed to initialize sockets"); + goto end; + } + qemu_init_exec_dir(argv[0]); qga_qmp_init_marshal(&ga_commands); =20 --=20 2.54.0