From nobody Tue Jul 14 21:25:29 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1783604792; cv=none; d=zohomail.com; s=zohoarc; b=dUDJTUGVFfAoRj3GeomFE/HNk9Sm6xz2LHLJT1mZTbNSaQOcrWLe68XzMMC2ou40Cut//JMo29WNtuHs9iAWyjEfF8mRCCTB2DCda1HUT4j56Fdaigb3y8p/qS9lM9KH0Z4YMUvanVo37mdo4Ay+qbuRLMXrm8E+jaQT9+nfge0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783604792; h=Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id:Cc; bh=tv+N6u4ljpvkD+8L6TQNjs0Y2uqEoAoQbWwhb97xi0Y=; b=kd/VMigei6G0q5H5ztq5yT0yE12IT+3/eUxr6Jvrl2bK5EBzADem+oGQVYX4KcyJpVRNdgsQ+Sv1kQabRwT7cSKQF3tpIB2ZcJbsPhtrhmB1fzuhJyI2RI1DIVzZN+F6npW5v/x1jsATASTxm9b67esrRO09Ok7AIQ16sUK6iuU= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [38.145.34.151]) by mx.zohomail.com with SMTPS id 1783604792041302.8250252094185; Thu, 9 Jul 2026 06:46:32 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 070BC41CB4; Thu, 9 Jul 2026 09:46:30 -0400 (EDT) Received: from [172.19.199.10] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id 9885B41D85; Thu, 9 Jul 2026 09:41:12 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 760F841983; Thu, 9 Jul 2026 09:38:27 -0400 (EDT) Received: from relay.virtuozzo.com (relay.virtuozzo.com [130.117.225.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id ADED441AA9 for ; Thu, 9 Jul 2026 09:38:26 -0400 (EDT) Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1whogt-006MF9-1d for devel@lists.libvirt.org; Thu, 09 Jul 2026 15:22:27 +0200 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 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=tv+N6u4ljpvkD+8L6TQNjs0Y2uqEoAoQbWwhb97xi0Y=; b=fdtSrlNrbiDg XgtATjR0XGDo94+eVzFdQmuMoGdOlB3fJ0zH+8ISlwNPDeH3qONg0N2UUNOLcF29IjCm/ZkVlXd2F FW1fjleWU6HioYw3KeBqjHuSxad50eEh6AN6lwNInuiJpUPeLyd5vksvwsNJl8hk+uBg7EhUeXMiR mcyL8N2zkgYDt7Z3pWBeqQnkfcoEDp8azJ7lx9kPc7Kc8GXHDqGev+mmcYnbEUd5skw0SMzMqyO8p Fd9pNSj9fRNAPoXqBU/nTeu9o3FxB8TxnPyUd9q/md1Q1QGGY5tjtsupVmWaQPrbrFUDelwGEECex DUU8H43DnxfQITKqm+8B8Q==; To: devel@lists.libvirt.org Subject: [PATCH 1/6] util: Add virVsockConnectQuiet helper Date: Thu, 9 Jul 2026 15:15:14 +0200 Message-ID: <20260709132213.2392145-2-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> References: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: polina.vishneva@virtuozzo.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation Message-ID-Hash: 44IGYBATNQOQ64Y66ZK6IURZBW4J44YQ X-Message-ID-Hash: 44IGYBATNQOQ64Y66ZK6IURZBW4J44YQ X-Mailman-Approved-At: Thu, 09 Jul 2026 13:40:43 +0000 X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Polina Vishneva via Devel Reply-To: Polina Vishneva X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1783604792913158500 Content-Type: text/plain; charset="utf-8" Add a common helper to connect to a vsock endpoint by CID and port. This can be used by both the daemon (guest agent over vsock) and tools (e.g. SSH-over-vsock command execution). It raises no libvirt errors and preserves errno, making it suitable for probing service availability without spamming the log. An error-reporting counterpart can be added alongside once a caller needs one. We use SO_VM_SOCKETS_CONNECT_TIMEOUT to cap the worst-case wait to 200ms. Best case is still immediate (when the service is present or when the kernel early-returns in case the guest isn't ready). Signed-off-by: Polina Vishneva --- src/libvirt_private.syms | 1 + src/util/virvsock.c | 69 ++++++++++++++++++++++++++++++++++++++++ src/util/virvsock.h | 6 ++++ 3 files changed, 76 insertions(+) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index c76e5cb08a..6a9cd34134 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -3842,6 +3842,7 @@ virPCIVPDResourceUpdateKeyword; =20 # util/virvsock.h virVsockAcquireGuestCid; +virVsockConnectQuiet; virVsockSetGuestCid; =20 =20 diff --git a/src/util/virvsock.c b/src/util/virvsock.c index c6f8b362b8..10ca1d44eb 100644 --- a/src/util/virvsock.c +++ b/src/util/virvsock.c @@ -23,11 +23,15 @@ =20 #ifdef __linux__ # include +# include +# include +# include #endif =20 #include "virvsock.h" =20 #include "virerror.h" +#include "virfile.h" #include "virlog.h" =20 =20 @@ -105,3 +109,68 @@ virVsockAcquireGuestCid(int fd, =20 return 0; } + + +#ifdef __linux__ + +/* Use the _OLD option with a __kernel_old_timeval: _NEW takes a 64-bit + * __kernel_sock_timeval which is more awkward to build, and glibc's struct + * timeval is 64-bit under _TIME_BITS=3D64 on 32-bit, which the _OLD option + * misreads. */ +#ifndef SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD +# define SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD SO_VM_SOCKETS_CONNECT_TIMEOUT +#endif + +/** + * virVsockConnectQuiet: + * @cid: guest context ID + * @port: vsock port number + * + * Connect to a vsock endpoint identified by @cid and @port. + * Doesn't raise any errors. Preserves the actual errno on error. + * + * Returns: connected fd on success, -1 on error. + */ +int +virVsockConnectQuiet(unsigned int cid, + unsigned int port) +{ + struct sockaddr_vm addr =3D { + .svm_family =3D AF_VSOCK, + .svm_cid =3D cid, + .svm_port =3D port, + }; + struct __kernel_old_timeval tv =3D { + .tv_sec =3D VIR_VSOCK_CONNECT_TIMEOUT_MS / 1000, + .tv_usec =3D (VIR_VSOCK_CONNECT_TIMEOUT_MS % 1000) * 1000, + }; + int fd; + + if ((fd =3D socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0)) < 0) + return -1; + + if (setsockopt(fd, AF_VSOCK, SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD, + &tv, sizeof(tv)) < 0) + goto error; + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) + goto error; + + return fd; + + error: + VIR_FORCE_CLOSE(fd); + return -1; +} + +#else /* !__linux__ */ + +int +virVsockConnectQuiet(unsigned int cid G_GNUC_UNUSED, + unsigned int port G_GNUC_UNUSED) +{ + errno =3D ENOSYS; + return -1; +} + +#endif /* !__linux__ */ diff --git a/src/util/virvsock.h b/src/util/virvsock.h index d6ba2faabf..06c251b594 100644 --- a/src/util/virvsock.h +++ b/src/util/virvsock.h @@ -18,6 +18,8 @@ =20 #pragma once =20 +#define VIR_VSOCK_CONNECT_TIMEOUT_MS 200 + int virVsockSetGuestCid(int fd, unsigned int guest_cid); @@ -25,3 +27,7 @@ virVsockSetGuestCid(int fd, int virVsockAcquireGuestCid(int fd, unsigned int *guest_cid); + +int +virVsockConnectQuiet(unsigned int cid, + unsigned int port); --=20 2.54.0 From nobody Tue Jul 14 21:25:29 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1783604847; cv=none; d=zohomail.com; s=zohoarc; b=n3nYjF1pOJ/Sg+TJfTncbOKzu5JNcBtpOhx9cFnT0OuAfdN/9Nju29cQOp8KiJjpGIMrBjnQ+b3q1gbdfbzTkhlqhh+0kTTq8bpXsBSSdItqSMIZV07q53QPIuIXW4KRERowuOv1aBxeU2slwn65E8g02MKkuUYLISoJfnKGxXg= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783604847; h=Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id:Cc; bh=OD1X96Pt1qGWhn57eiE7YXIHZo4UHarM6G02PAWFQ2U=; b=O0tue9pUaGtFWfVdua8IvosVXxFo7LypaZXCKE8Q/7OyJjjsBtq5ySTkhYjDb4hpRiq5WyEFO4yiW7xBsI46QUF3/UP0mje5INqmNzxGO1BmGls5GN4nGb7g7XUcuZns00QqK70iDD3OeZoHQMHirsAT6OYYlg6QeC8IV91G33w= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [38.145.34.151]) by mx.zohomail.com with SMTPS id 1783604847310771.3816845386217; Thu, 9 Jul 2026 06:47:27 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 15DE641BB9; Thu, 9 Jul 2026 09:47:25 -0400 (EDT) Received: from [172.19.199.10] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id 203F4423D1; Thu, 9 Jul 2026 09:41:17 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 1C3D741AA9; Thu, 9 Jul 2026 09:38:29 -0400 (EDT) Received: from relay.virtuozzo.com (relay.virtuozzo.com [130.117.225.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 503BA41AA9 for ; Thu, 9 Jul 2026 09:38:28 -0400 (EDT) Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1whogz-006MF9-2N for devel@lists.libvirt.org; Thu, 09 Jul 2026 15:22:33 +0200 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 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=OD1X96Pt1qGWhn57eiE7YXIHZo4UHarM6G02PAWFQ2U=; b=bZle7UcpFfZO Kce6yBU3BzgW6QeshQ9vNEMvHHuFa754DxQS9+eqkR7xcZKgBIiuTCcAdI1LmDPUJpOg0q56meU9v 3gM6OUNNuruXxp0uG+mIjBIF/VOd3at9Di7S0OJYwQhsizVRRMo6kntcxAeU4Lcd8fvu5i/pLokbr lFzP75LPuS1FL5NvMp3JdRdvI88sMrQMPBo+x51NoyluhBDUO2dDfE2AD2Z79dUYTsV8VgRkiW7T0 pJw0mUtXTaEftgm93ECmxca/aULT3LdOTS4yRS/338KrRZk65qw0QymDqDrXPyPyim3M+zAi5bIFK /La+3fmS0wkYTDVR0T5lDw==; To: devel@lists.libvirt.org Subject: [PATCH 2/6] qemu: Extract qemuProcessResetAgent helper Date: Thu, 9 Jul 2026 15:15:15 +0200 Message-ID: <20260709132213.2392145-3-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> References: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: polina.vishneva@virtuozzo.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation Message-ID-Hash: JMSVRGBMZNROYENTVDBILYZM7QGNYHYV X-Message-ID-Hash: JMSVRGBMZNROYENTVDBILYZM7QGNYHYV X-Mailman-Approved-At: Thu, 09 Jul 2026 13:40:43 +0000 X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Polina Vishneva via Devel Reply-To: Polina Vishneva X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1783604849301158500 Content-Type: text/plain; charset="utf-8" Several paths repeat the same guest-agent teardown sequence; extract it into one helper so it stays in sync. Signed-off-by: Polina Vishneva --- src/qemu/qemu_driver.c | 5 +---- src/qemu/qemu_process.c | 15 +++++++++++---- src/qemu/qemu_process.h | 2 ++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 8ec9e1f9c4..0edc568bd7 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3929,10 +3929,7 @@ processSerialChangedEvent(virQEMUDriver *driver, if (qemuConnectAgent(driver, vm) < 0) goto endjob; } else { - if (priv->agent) { - g_clear_pointer(&priv->agent, qemuAgentClose); - } - priv->agentError =3D false; + qemuProcessResetAgent(vm); } =20 agentEvent =3D virDomainEventAgentLifecycleNewFromObj(vm, newstate, diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 4c94ff3c91..77d9880d14 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -140,6 +140,16 @@ qemuProcessRemoveDomainStatus(virQEMUDriver *driver, } =20 =20 +void +qemuProcessResetAgent(virDomainObj *vm) +{ + qemuDomainObjPrivate *priv =3D vm->privateData; + + g_clear_pointer(&priv->agent, qemuAgentClose); + priv->agentError =3D false; +} + + /* * This is a callback registered with a qemuAgent *instance, * and to be invoked when the agent console hits an end of file @@ -9401,10 +9411,7 @@ qemuProcessStop(virDomainObj *vm, virObjectLock(vm); } =20 - if (priv->agent) { - g_clear_pointer(&priv->agent, qemuAgentClose); - } - priv->agentError =3D false; + qemuProcessResetAgent(vm); =20 if (priv->mon) { g_clear_pointer(&priv->mon, qemuMonitorClose); diff --git a/src/qemu/qemu_process.h b/src/qemu/qemu_process.h index 5874214596..3fc9f844d3 100644 --- a/src/qemu/qemu_process.h +++ b/src/qemu/qemu_process.h @@ -210,6 +210,8 @@ virDomainDiskDef *qemuProcessFindDomainDiskByAliasOrQOM= (virDomainObj *vm, =20 int qemuConnectAgent(virQEMUDriver *driver, virDomainObj *vm); =20 +void qemuProcessResetAgent(virDomainObj *vm); + =20 int qemuProcessSetupVcpu(virDomainObj *vm, unsigned int vcpuid, --=20 2.54.0 From nobody Tue Jul 14 21:25:29 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1783604517; cv=none; d=zohomail.com; s=zohoarc; b=h193/pe04fxa6QF7IFIwotX48BGysVQ6FmxUMCvfrjnpFtB4K0/ZvCjtWDAzS/+599nVtgRqmxlkBvjGh5N6IZBPBTTuOURcXPSOOTZ8NLUmcP2f4W/DOl7JvTPMprMCnFA11K2WXxhyGASDZcN+e1SuTAnxY8mkrN6xFnYe9+A= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783604517; h=Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id:Cc; bh=A2bTF2IM7vB5qIABaRlPySUZSwhTnXVDiG9l/jKSbrY=; b=dEQJHie0dzLAFyrxHq0tBJetv7XGJe/oLWo1+lt71f7LkgMoututiVpGuybWcIcPweThP9vxG3YTTdflQeZoLlGXiKHUoxZeIXT2G9V8zNFVfVP5As0Y/B/g3lP+Vg0RlvexcOyYljc1kqPZZgSNTW7xhmmYiRjIWUfiXD/PqMM= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [38.145.34.151]) by mx.zohomail.com with SMTPS id 1783604517839924.5810858997473; Thu, 9 Jul 2026 06:41:57 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 77DE241CF0; Thu, 9 Jul 2026 09:41:56 -0400 (EDT) Received: from [172.19.199.10] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id AC6C141BAC; Thu, 9 Jul 2026 09:40:54 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id 8E37F41ACF; Thu, 9 Jul 2026 09:38:22 -0400 (EDT) Received: from relay.virtuozzo.com (relay.virtuozzo.com [130.117.225.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 3541441983 for ; Thu, 9 Jul 2026 09:38:19 -0400 (EDT) Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1whoh2-006MF9-0A for devel@lists.libvirt.org; Thu, 09 Jul 2026 15:22:35 +0200 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 X-Greylist: delayed 951 seconds by postgrey-1.37 at lists.libvirt.org; Thu, 09 Jul 2026 09:38:20 EDT 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=A2bTF2IM7vB5qIABaRlPySUZSwhTnXVDiG9l/jKSbrY=; b=rJa8iQuObmJb UWUCYdLZ9I8inGs39uDLMXJCYmVJ+rHk/IG6e+LauxLVmyBP8N9VpMsU3g9TOreOa+c+5+1JVr/DX TN8celaJFWtzOmDi6/3gMEhYPmU/0RpbsU0YyW8GmPLV97/oIYeWDz0z52l2OtDIp5TwIuxvb9Mz/ 4DUu7JOByaAFenCDjKtz/PtiB1BShhofq3ninuVU5hKtsdDF+dnyvmdPJnSG5QtBRJAigsiTY/9AZ 8BWFiOVFUFgAjSdJO13vANa9lLcYJ7alwtY+Mx1P8oWSy9FLTh9OPqd4ThhMRQsHPxOTVd697+ZyC 0K0tmCkPKtEamVkIjQ2cPQ==; To: devel@lists.libvirt.org Subject: [PATCH 3/6] qemu: Add qemuDomainVsockHasGuestCid helper Date: Thu, 9 Jul 2026 15:15:16 +0200 Message-ID: <20260709132213.2392145-4-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> References: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: polina.vishneva@virtuozzo.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation Message-ID-Hash: T65K7HSLZCR2WQ7TM4HK3ZK74EPN6R3F X-Message-ID-Hash: T65K7HSLZCR2WQ7TM4HK3ZK74EPN6R3F X-Mailman-Approved-At: Thu, 09 Jul 2026 13:40:42 +0000 X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Polina Vishneva via Devel Reply-To: Polina Vishneva X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1783604518556158500 Content-Type: text/plain; charset="utf-8" Fold the "vsock present with a usable guest CID" check into one function for the upcoming AF_VSOCK agent transport. Move VIR_VSOCK_GUEST_CID_MIN to the header to make it available to callers outside of the module. Signed-off-by: Polina Vishneva --- src/qemu/qemu_domain.c | 9 +++++++++ src/qemu/qemu_domain.h | 2 ++ src/util/virvsock.c | 2 -- src/util/virvsock.h | 1 + 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index e1b805d906..8539bb72fa 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -59,6 +59,7 @@ #include "virrandom.h" #include "virsystemd.h" #include "virsecret.h" +#include "virvsock.h" #include "logging/log_manager.h" #include "locking/domain_lock.h" #include "virdomainsnapshotobjlist.h" @@ -7387,6 +7388,14 @@ qemuDomainCheckABIStability(virQEMUDriver *driver, #undef COPY_FLAGS =20 =20 +bool +qemuDomainVsockHasGuestCid(const virDomainDef *def) +{ + return def->vsock && + def->vsock->guest_cid >=3D VIR_VSOCK_GUEST_CID_MIN; +} + + bool qemuDomainAgentAvailable(virDomainObj *vm, bool reportError) diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index 50ab492023..e994776996 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -805,6 +805,8 @@ bool qemuDomainCheckABIStability(virQEMUDriver *driver, virDomainObj *vm, virDomainDef *dst); =20 +bool qemuDomainVsockHasGuestCid(const virDomainDef *def); + bool qemuDomainAgentAvailable(virDomainObj *vm, bool reportError); =20 diff --git a/src/util/virvsock.c b/src/util/virvsock.c index 10ca1d44eb..3c088c8648 100644 --- a/src/util/virvsock.c +++ b/src/util/virvsock.c @@ -81,8 +81,6 @@ virVsockSetGuestCid(int fd, return 0; } =20 -#define VIR_VSOCK_GUEST_CID_MIN 3 - /** * virVsockAcquireGuestCid: * @fd: file descriptor of a vsock interface diff --git a/src/util/virvsock.h b/src/util/virvsock.h index 06c251b594..8bf08f9228 100644 --- a/src/util/virvsock.h +++ b/src/util/virvsock.h @@ -18,6 +18,7 @@ =20 #pragma once =20 +#define VIR_VSOCK_GUEST_CID_MIN 3 #define VIR_VSOCK_CONNECT_TIMEOUT_MS 200 =20 int --=20 2.54.0 From nobody Tue Jul 14 21:25:29 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1783605132; cv=none; d=zohomail.com; s=zohoarc; b=B2FCfeVxcSo519pwteTFVV60Hsa+c+O0kmOEVXL81Jqc6mSm7AXSFzkjZFO1DRYIbaUyX8vN4sDsiZ2BGwX8ORZfj/2DzAzR46zpdbPBew5rdxk+1rFZ+nbcjz/IBx8cf+aqnRKm/YoPvzwvg2BkdXuFweRxVFICAhGGlIpPYhU= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783605132; h=Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id:Cc; bh=EMKIw93GCE74DX7RWJx76DaZXenBwklbIDS9tak+G+8=; b=ObBgMGhYLhcQHl2ONs3zOsFa993jsvcn7pkRNLmpe+JPuAzE2f7HuZBKIiKxbm+Vai8Cm11efSslhy7SIXTX9tw5AKH3uFMUq+luuLCMak2KmRaLuLLuUB5RIfOtSXDH1Agl1sP3zCJ9syOQsbydxzZ+MbtibzdXEHg4TTQpBhY= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [38.145.34.151]) by mx.zohomail.com with SMTPS id 1783605132110826.3562340998561; Thu, 9 Jul 2026 06:52:12 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id B39563F976; Thu, 9 Jul 2026 09:52:10 -0400 (EDT) Received: from [172.19.199.10] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id 0517643DF6; Thu, 9 Jul 2026 09:41:31 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id BF50B41AA9; Thu, 9 Jul 2026 09:38:35 -0400 (EDT) Received: from relay.virtuozzo.com (relay.virtuozzo.com [130.117.225.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 3226D41AA9 for ; Thu, 9 Jul 2026 09:38:32 -0400 (EDT) Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1whoh3-006MF9-1X for devel@lists.libvirt.org; Thu, 09 Jul 2026 15:22:37 +0200 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 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=EMKIw93GCE74DX7RWJx76DaZXenBwklbIDS9tak+G+8=; b=eA5Xgp2Qf3QN 2f5PU/Wx27wvIjo1fg3Tkmg3MUJFYFn73Py0tGZy+SgEfO3HZEYNH00G34GzhIvZIvJJfsPD2+5Ta BCPE0K0wHi6NlpnxMeuzPBYpEBl2Pkc4GthWNlXIvGg3HLzHh/Z2U0J5NxYmX6xriNCa5f7R5MIYH c29YpqoyGxQFZ4xRLQ6l3Tc+X8zaSPlj+AK7NQdBW6BigDEHrC/pg7ZrcpsG1fzBylm1lu8mIL5yD sdhRSzksRcCFeDN7YRCc24QL5FQhJyYQqQLvQzgQ8+ZVmReDIJmccJ+xX4pkq08jbC2Hp3bfG062b 5kMCvcPIVISPLuk70vXy1Q==; To: devel@lists.libvirt.org Subject: [PATCH 4/6] qemu: Support AF_VSOCK transport for QGA communication Date: Thu, 9 Jul 2026 15:15:17 +0200 Message-ID: <20260709132213.2392145-5-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> References: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: polina.vishneva@virtuozzo.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation Message-ID-Hash: QPTS63RSORUWCOA3URCORBOBMIMKDLAD X-Message-ID-Hash: QPTS63RSORUWCOA3URCORBOBMIMKDLAD X-Mailman-Approved-At: Thu, 09 Jul 2026 13:40:43 +0000 X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Polina Vishneva via Devel Reply-To: Polina Vishneva X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1783605133581158500 Content-Type: text/plain; charset="utf-8" Add vsock as a second QGA transport alongside the virtio-serial channel. It's attempted when there's no CONNECTED virtio-serial channel. Failed vsock connects retry on a capped exponential backoff (2s..60s, giving up after ~2 min), reset by any lifecycle event (channel-ready, replug, restart). priv->agentIsVsock specifies the type of the currently live transport, so the EOF, channel-state and unplug paths know which transport is dying. Signed-off-by: Polina Vishneva --- src/hypervisor/qemu_agent.c | 73 +++++---- src/hypervisor/qemu_agent.h | 9 ++ src/libvirt_private.syms | 1 + src/qemu/qemu_domain.c | 19 ++- src/qemu/qemu_domain.h | 6 + src/qemu/qemu_driver.c | 13 +- src/qemu/qemu_hotplug.c | 6 + src/qemu/qemu_process.c | 290 ++++++++++++++++++++++++++++++++---- 8 files changed, 357 insertions(+), 60 deletions(-) diff --git a/src/hypervisor/qemu_agent.c b/src/hypervisor/qemu_agent.c index e549947fbf..00bd44b52b 100644 --- a/src/hypervisor/qemu_agent.c +++ b/src/hypervisor/qemu_agent.c @@ -592,52 +592,37 @@ qemuAgentIO(GSocket *socket G_GNUC_UNUSED, =20 =20 qemuAgent * -qemuAgentOpen(virDomainObj *vm, - const virDomainChrSourceDef *config, - GMainContext *context, - qemuAgentCallbacks *cb, - int timeout) +qemuAgentOpenFd(virDomainObj *vm, + int fd, + GMainContext *context, + qemuAgentCallbacks *cb, + int timeout) { - qemuAgent *agent; + qemuAgent *agent =3D NULL; g_autoptr(GError) gerr =3D NULL; =20 if (!cb || !cb->eofNotify) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("EOF notify callback must be supplied")); - return NULL; + goto error; } =20 if (qemuAgentInitialize() < 0) - return NULL; + goto error; =20 if (!(agent =3D virObjectLockableNew(qemuAgentClass))) - return NULL; + goto error; =20 agent->timeout =3D timeout; - agent->fd =3D -1; + agent->fd =3D fd; if (virCondInit(&agent->notify) < 0) { virReportSystemError(errno, "%s", _("cannot initialize agent condition")); - virObjectUnref(agent); - return NULL; + goto error; } agent->vm =3D virObjectRef(vm); agent->cb =3D cb; =20 - if (config->type !=3D VIR_DOMAIN_CHR_TYPE_UNIX) { - virReportError(VIR_ERR_INTERNAL_ERROR, - _("unable to handle agent type: %1$s"), - virDomainChrTypeToString(config->type)); - goto cleanup; - } - - virObjectUnlock(vm); - agent->fd =3D qemuAgentOpenUnix(config->data.nix.path); - virObjectLock(vm); - - if (agent->fd =3D=3D -1) - goto cleanup; - agent->context =3D g_main_context_ref(context); =20 agent->socket =3D g_socket_new_from_fd(agent->fd, &gerr); @@ -645,7 +630,8 @@ qemuAgentOpen(virDomainObj *vm, virReportError(VIR_ERR_INTERNAL_ERROR, _("Unable to create socket object: %1$s"), gerr->message); - goto cleanup; + qemuAgentClose(agent); + return NULL; } =20 qemuAgentRegister(agent); @@ -655,12 +641,41 @@ qemuAgentOpen(virDomainObj *vm, =20 return agent; =20 - cleanup: - qemuAgentClose(agent); + error: + /* qemuAgentDispose() doesn't touch agent->fd, so close it here */ + VIR_FORCE_CLOSE(fd); + virObjectUnref(agent); return NULL; } =20 =20 +qemuAgent * +qemuAgentOpen(virDomainObj *vm, + const virDomainChrSourceDef *config, + GMainContext *context, + qemuAgentCallbacks *cb, + int timeout) +{ + int fd; + + if (config->type !=3D VIR_DOMAIN_CHR_TYPE_UNIX) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("unable to handle agent type: %1$s"), + virDomainChrTypeToString(config->type)); + return NULL; + } + + virObjectUnlock(vm); + fd =3D qemuAgentOpenUnix(config->data.nix.path); + virObjectLock(vm); + + if (fd =3D=3D -1) + return NULL; + + return qemuAgentOpenFd(vm, fd, context, cb, timeout); +} + + static void qemuAgentNotifyCloseLocked(qemuAgent *agent) { diff --git a/src/hypervisor/qemu_agent.h b/src/hypervisor/qemu_agent.h index 3dbc3baec1..ce75ad2829 100644 --- a/src/hypervisor/qemu_agent.h +++ b/src/hypervisor/qemu_agent.h @@ -24,6 +24,9 @@ #include "internal.h" #include "domain_conf.h" =20 +/* Fixed vsock port the guest agent listens on by convention */ +#define QEMU_AGENT_VSOCK_PORT 1024 + typedef struct _qemuAgent qemuAgent; =20 typedef struct _qemuAgentCallbacks qemuAgentCallbacks; @@ -41,6 +44,12 @@ qemuAgent *qemuAgentOpen(virDomainObj *vm, qemuAgentCallbacks *cb, int timeout); =20 +qemuAgent *qemuAgentOpenFd(virDomainObj *vm, + int fd, + GMainContext *context, + qemuAgentCallbacks *cb, + int timeout); + void qemuAgentClose(qemuAgent *mon); =20 void qemuAgentNotifyClose(qemuAgent *mon); diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 6a9cd34134..95db6a885d 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1735,6 +1735,7 @@ qemuAgentGetVCPUs; qemuAgentNotifyClose; qemuAgentNotifyEvent; qemuAgentOpen; +qemuAgentOpenFd; qemuAgentSetResponseTimeout; qemuAgentSetTime; qemuAgentSetUserPassword; diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 8539bb72fa..9560ae78ad 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -1980,6 +1980,22 @@ qemuDomainObjPrivateDataClear(qemuDomainObjPrivate *= priv) priv->iommufdState =3D false; =20 g_clear_pointer(&priv->memoryBackingDir, g_free); + + qemuDomainCancelAgentVsockReconnect(priv); + priv->agentIsVsock =3D false; +} + + +void +qemuDomainCancelAgentVsockReconnect(qemuDomainObjPrivate *priv) +{ + priv->agentVsockReconnectAttempts =3D 0; + + if (!priv->agentVsockReconnectTimer) + return; + + g_source_destroy(priv->agentVsockReconnectTimer); + g_clear_pointer(&priv->agentVsockReconnectTimer, g_source_unref); } =20 =20 @@ -7417,7 +7433,8 @@ qemuDomainAgentAvailable(virDomainObj *vm, return false; } if (!priv->agent) { - if (qemuFindAgentConfig(vm->def)) { + if (qemuFindAgentConfig(vm->def) || + qemuDomainVsockHasGuestCid(vm->def)) { if (reportError) { virReportError(VIR_ERR_AGENT_UNRESPONSIVE, "%s", _("QEMU guest agent is not connected")); diff --git a/src/qemu/qemu_domain.h b/src/qemu/qemu_domain.h index e994776996..a3d8fa7a86 100644 --- a/src/qemu/qemu_domain.h +++ b/src/qemu/qemu_domain.h @@ -120,6 +120,10 @@ struct _qemuDomainObjPrivate { =20 qemuAgent *agent; bool agentError; + bool agentIsVsock; + + GSource *agentVsockReconnectTimer; + unsigned int agentVsockReconnectAttempts; =20 bool beingDestroyed; char *pidfile; @@ -807,6 +811,8 @@ bool qemuDomainCheckABIStability(virQEMUDriver *driver, =20 bool qemuDomainVsockHasGuestCid(const virDomainDef *def); =20 +void qemuDomainCancelAgentVsockReconnect(qemuDomainObjPrivate *priv); + bool qemuDomainAgentAvailable(virDomainObj *vm, bool reportError); =20 diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 0edc568bd7..a4a30627d4 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -3926,15 +3926,20 @@ processSerialChangedEvent(virQEMUDriver *driver, if (STREQ_NULLABLE(dev.data.chr->target.name, "org.qemu.guest_agent.0"= )) { virObjectEvent *agentEvent =3D NULL; if (newstate =3D=3D VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED) { + qemuDomainCancelAgentVsockReconnect(priv); if (qemuConnectAgent(driver, vm) < 0) goto endjob; } else { - qemuProcessResetAgent(vm); + if (!priv->agentIsVsock) + qemuProcessResetAgent(vm); } =20 - agentEvent =3D virDomainEventAgentLifecycleNewFromObj(vm, newstate, - VIR_CONNECT_DO= MAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL); - virObjectEventStateQueue(driver->domainEventState, agentEvent); + if ((newstate =3D=3D VIR_DOMAIN_CHR_DEVICE_STATE_DISCONNECTED && != priv->agent) || + (newstate =3D=3D VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED && priv= ->agent)) { + agentEvent =3D virDomainEventAgentLifecycleNewFromObj(vm, news= tate, + VIR_CONNEC= T_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL); + virObjectEventStateQueue(driver->domainEventState, agentEvent); + } } =20 /* we deliberately allow for goto endjob to skip generic event emission diff --git a/src/qemu/qemu_hotplug.c b/src/qemu/qemu_hotplug.c index 5be567b510..8018b5d3c5 100644 --- a/src/qemu/qemu_hotplug.c +++ b/src/qemu/qemu_hotplug.c @@ -5467,9 +5467,15 @@ static int qemuDomainRemoveVsockDevice(virDomainObj *vm, virDomainVsockDef *dev) { + qemuDomainObjPrivate *priv =3D vm->privateData; + VIR_DEBUG("Removing vsock device %s from domain %p %s", dev->info.alias, vm, vm->def->name); =20 + qemuDomainCancelAgentVsockReconnect(priv); + if (priv->agentIsVsock && priv->agent) + qemuProcessResetAgent(vm); + qemuDomainReleaseDeviceAddress(vm, &dev->info); g_clear_pointer(&vm->def->vsock, virDomainVsockDefFree); return 0; diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 77d9880d14..1d94dc4673 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -140,16 +140,123 @@ qemuProcessRemoveDomainStatus(virQEMUDriver *driver, } =20 =20 +#define QEMU_AGENT_VSOCK_RECONNECT_MIN_MS 2000 /* initial backoff */ +#define QEMU_AGENT_VSOCK_RECONNECT_MAX_MS 60000 /* interval cap */ +#define QEMU_AGENT_VSOCK_RECONNECT_MAX_ATTEMPTS 6 /* give up (~2 min) = */ + + +static int +qemuChannelOpenVsock(virQEMUDriver *driver, + virDomainObj *vm, + unsigned int cid) +{ + int fd; + + if (qemuSecuritySetDaemonSocketLabel(driver->securityManager, vm->def)= < 0) { + VIR_ERROR(_("Failed to set security context for vsock agent for %1= $s"), + vm->def->name); + return -1; + } + + virObjectUnlock(vm); + fd =3D virVsockConnectQuiet(cid, QEMU_AGENT_VSOCK_PORT); + virObjectLock(vm); + + if (qemuSecurityClearSocketLabel(driver->securityManager, vm->def) < 0) + VIR_ERROR(_("Failed to clear security context for vsock agent for = %1$s"), + vm->def->name); + + return fd; +} + + +static gboolean qemuProcessAgentVsockReconnectTimer(gpointer user_data); +static int qemuConnectAgentVsock(virQEMUDriver *driver, virDomainObj *vm); + + +static void +qemuProcessScheduleAgentVsockReconnect(virDomainObj *vm) +{ + qemuDomainObjPrivate *priv =3D vm->privateData; + unsigned int interval; + + if (priv->agentVsockReconnectTimer || !priv->eventThread) + return; + + if (priv->agentVsockReconnectAttempts >=3D QEMU_AGENT_VSOCK_RECONNECT_= MAX_ATTEMPTS) { + VIR_DEBUG("Giving up vsock QGA reconnect for %s after %u attempts", + vm->def->name, priv->agentVsockReconnectAttempts); + return; + } + + interval =3D QEMU_AGENT_VSOCK_RECONNECT_MIN_MS << priv->agentVsockReco= nnectAttempts; + if (interval > QEMU_AGENT_VSOCK_RECONNECT_MAX_MS) + interval =3D QEMU_AGENT_VSOCK_RECONNECT_MAX_MS; + priv->agentVsockReconnectAttempts++; + + VIR_DEBUG("Scheduling vsock QGA reconnect for %s in %u ms (attempt %u)= ", + vm->def->name, interval, priv->agentVsockReconnectAttempts); + + priv->agentVsockReconnectTimer =3D + g_timeout_source_new(interval); + g_source_set_callback(priv->agentVsockReconnectTimer, + qemuProcessAgentVsockReconnectTimer, + virObjectRef(vm), + (GDestroyNotify)virObjectUnref); + g_source_attach(priv->agentVsockReconnectTimer, + virEventThreadGetContext(priv->eventThread)); +} + + +static gboolean +qemuProcessAgentVsockReconnectTimer(gpointer user_data) +{ + virDomainObj *vm =3D user_data; + qemuDomainObjPrivate *priv; + + virObjectLock(vm); + priv =3D vm->privateData; + + /* A concurrent cancel may have already destroyed and cleared the sour= ce + * while we blocked on the lock; drop our ref only if it's still ours.= */ + if (priv->agentVsockReconnectTimer) + g_clear_pointer(&priv->agentVsockReconnectTimer, g_source_unref); + + if (!priv->beingDestroyed && virDomainObjIsActive(vm) && + !priv->agent && qemuDomainVsockHasGuestCid(vm->def)) + ignore_value(qemuConnectAgentVsock(priv->driver, vm)); + + virObjectUnlock(vm); + return G_SOURCE_REMOVE; +} + + void qemuProcessResetAgent(virDomainObj *vm) { qemuDomainObjPrivate *priv =3D vm->privateData; =20 g_clear_pointer(&priv->agent, qemuAgentClose); + priv->agentIsVsock =3D false; priv->agentError =3D false; } =20 =20 +/* The virtio-serial agent lifecycle is reported from the channel event; + * vsock has no such event, so emit it from the connect/EOF paths instead.= */ +static void +qemuProcessEmitVsockAgentLifecycle(virQEMUDriver *driver, + virDomainObj *vm, + virDomainChrDeviceState newstate) +{ + virObjectEvent *event; + + event =3D virDomainEventAgentLifecycleNewFromObj(vm, newstate, + VIR_CONNECT_DOMAIN_EVENT_AGENT_LIFECYCLE_REASON_CHANNEL); + virObjectEventStateQueue(driver->domainEventState, event); +} + + /* * This is a callback registered with a qemuAgent *instance, * and to be invoked when the agent console hits an end of file @@ -161,6 +268,7 @@ qemuProcessHandleAgentEOF(qemuAgent *agent, virDomainObj *vm) { qemuDomainObjPrivate *priv; + bool wasVsock; =20 virObjectLock(vm); VIR_DEBUG("Received EOF from agent on %p '%s'", vm, vm->def->name); @@ -172,14 +280,31 @@ qemuProcessHandleAgentEOF(qemuAgent *agent, goto unlock; } =20 + /* Stale EOF from an instance we already replaced. */ + if (priv->agent !=3D agent) { + VIR_DEBUG("Agent EOF from stale instance %p (current %p)", + agent, priv->agent); + qemuAgentClose(agent); + goto unlock; + } + if (priv->beingDestroyed) { VIR_DEBUG("Domain is being destroyed, agent EOF is expected"); goto unlock; } =20 - qemuAgentClose(agent); - priv->agent =3D NULL; - priv->agentError =3D false; + wasVsock =3D priv->agentIsVsock; + + qemuProcessResetAgent(vm); + + if (wasVsock) { + qemuProcessEmitVsockAgentLifecycle(priv->driver, vm, + VIR_DOMAIN_CHR_DEVICE_STATE_DIS= CONNECTED); + + /* The timer is never set while we hold an agent, so no cancel nee= ded. */ + if (qemuDomainVsockHasGuestCid(vm->def)) + qemuProcessScheduleAgentVsockReconnect(vm); + } =20 virObjectUnlock(vm); return; @@ -219,35 +344,31 @@ static qemuAgentCallbacks agentCallbacks =3D { }; =20 =20 -int -qemuConnectAgent(virQEMUDriver *driver, virDomainObj *vm) +/* + * Connect agent over the UNIX virtio-serial channel. + * + * Writes vm->privateData->agentError on failure. + * + * Returns: -1 if the domain is dead, 0 otherwise. + */ +static int +qemuConnectAgentUnix(virQEMUDriver *driver, virDomainObj *vm) { qemuDomainObjPrivate *priv =3D vm->privateData; qemuAgent *agent =3D NULL; virDomainChrDef *config =3D qemuFindAgentConfig(vm->def); =20 - if (!config) - return 0; - - if (priv->agent) - return 0; - - if (config->state !=3D VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED) { - VIR_DEBUG("Deferring connecting to guest agent"); - return 0; - } - if (qemuSecuritySetDaemonSocketLabel(driver->securityManager, vm->def)= < 0) { VIR_ERROR(_("Failed to set security context for agent for %1$s"), vm->def->name); - goto cleanup; + goto error; } =20 agent =3D qemuAgentOpen(vm, config->source, virEventThreadGetContext(priv->eventThread), &agentCallbacks, - QEMU_DOMAIN_PRIVATE(vm)->agentTimeout); + priv->agentTimeout); =20 if (!virDomainObjIsActive(vm)) { qemuAgentClose(agent); @@ -260,20 +381,120 @@ qemuConnectAgent(virQEMUDriver *driver, virDomainObj= *vm) VIR_ERROR(_("Failed to clear security context for agent for %1$s"), vm->def->name); qemuAgentClose(agent); - goto cleanup; + goto error; + } + + if (!agent) + goto error; + + /* Another caller may have connected while the domain was unlocked. */ + if (priv->agent) { + qemuAgentClose(agent); + return 0; } =20 priv->agent =3D agent; - if (!priv->agent) - VIR_INFO("Failed to connect agent for %s", vm->def->name); + priv->agentIsVsock =3D false; + priv->agentError =3D false; + return 0; =20 - cleanup: - if (!priv->agent) { - VIR_WARN("Cannot connect to QEMU guest agent for %s", vm->def->nam= e); - priv->agentError =3D true; - virResetLastError(); + error: + VIR_WARN("Cannot connect to QEMU guest agent for %s", vm->def->name); + priv->agentError =3D true; + virResetLastError(); + return 0; +} + + +/* + * Connect agent over vsock. + * + * Sets the reconnect timer on failure. + * + * Returns: -1 if the domain is dead, 0 otherwise. + */ +static int +qemuConnectAgentVsock(virQEMUDriver *driver, virDomainObj *vm) +{ + qemuDomainObjPrivate *priv =3D vm->privateData; + qemuAgent *agent =3D NULL; + unsigned int cid =3D vm->def->vsock->guest_cid; + int fd; + + fd =3D qemuChannelOpenVsock(driver, vm, cid); + + if (!virDomainObjIsActive(vm)) { + VIR_FORCE_CLOSE(fd); + return -1; } =20 + /* Another caller may have connected while the domain was unlocked. */ + if (priv->agent) { + VIR_FORCE_CLOSE(fd); + return 0; + } + + /* The vsock device may have been unplugged while unlocked. */ + if (!vm->def->vsock) { + VIR_FORCE_CLOSE(fd); + return 0; + } + + if (fd < 0) { + VIR_DEBUG("vsock QGA connect failed for %s", vm->def->name); + goto retry; + } + + agent =3D qemuAgentOpenFd(vm, fd, + virEventThreadGetContext(priv->eventThread), + &agentCallbacks, + priv->agentTimeout); + if (agent =3D=3D NULL) { + VIR_WARN("Cannot open vsock QGA for %s", vm->def->name); + goto retry; + } + + priv->agent =3D agent; + priv->agentIsVsock =3D true; + priv->agentError =3D false; + qemuDomainCancelAgentVsockReconnect(priv); + qemuProcessEmitVsockAgentLifecycle(driver, vm, + VIR_DOMAIN_CHR_DEVICE_STATE_CONNECT= ED); + VIR_DEBUG("Connected to vsock QGA cid=3D%u for %s", cid, vm->def->name= ); + return 0; + + retry: + virResetLastError(); + qemuProcessScheduleAgentVsockReconnect(vm); + return 0; +} + + +/* + * Connect agent. + * + * Use the virtio-serial channel if it's CONNECTED. + * Otherwise, if a usable CID is configured, try vsock with exponential ba= ckoff. + * + * Returns: -1 if the domain is dead, 0 otherwise. + */ +int +qemuConnectAgent(virQEMUDriver *driver, virDomainObj *vm) +{ + qemuDomainObjPrivate *priv =3D vm->privateData; + virDomainChrDef *config =3D qemuFindAgentConfig(vm->def); + bool unix_ready =3D config && + config->state =3D=3D VIR_DOMAIN_CHR_DEVICE_STATE_CONNECTED; + + if (priv->agent) + return 0; + + if (unix_ready) + return qemuConnectAgentUnix(driver, vm); + + if (qemuDomainVsockHasGuestCid(vm->def)) + return qemuConnectAgentVsock(driver, vm); + return 0; } =20 @@ -448,6 +669,14 @@ qemuProcessHandleReset(qemuMonitor *mon G_GNUC_UNUSED, if (priv->agent) qemuAgentNotifyEvent(priv->agent, QEMU_AGENT_EVENT_RESET); =20 + /* A rebooting guest brings its agent back up; restart vsock QGA recon= nect + * from scratch in case we'd already given up. + * Skip if we're tearing down: the timer would outlive the event threa= d. */ + if (!priv->beingDestroyed && !priv->agent && qemuDomainVsockHasGuestCi= d(vm->def)) { + qemuDomainCancelAgentVsockReconnect(priv); + qemuProcessScheduleAgentVsockReconnect(vm); + } + qemuDomainSetFakeReboot(vm, false); qemuDomainSaveStatus(vm); =20 @@ -9375,6 +9604,10 @@ qemuProcessStop(virDomainObj *vm, virDomainAsyncJobTypeToString(asyncJob)); } =20 + /* Drop the timer before the active-check: Stop can goto endjob early,= and a + * live timer pins a vm ref and may fire after the event thread dies. = */ + qemuDomainCancelAgentVsockReconnect(priv); + if (!virDomainObjIsActive(vm)) { VIR_DEBUG("VM '%s' not active", vm->def->name); goto endjob; @@ -9409,6 +9642,11 @@ qemuProcessStop(virDomainObj *vm, virObjectUnlock(vm); virEventThreadStop(priv->eventThread); virObjectLock(vm); + + /* The event thread is joined now, so no reconnect callback is run= ning. + * Drop any timer one re-armed in the unlock window above; otherwi= se it + * would pin a vm ref forever. */ + qemuDomainCancelAgentVsockReconnect(priv); } =20 qemuProcessResetAgent(vm); --=20 2.54.0 From nobody Tue Jul 14 21:25:29 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1783604973; cv=none; d=zohomail.com; s=zohoarc; b=VAu5bQ0w9F7ypOkU208Y2wutFhO+ZI1E2ONDHT/4cjGXzTyPg+gDHrNyXPWEb81Weh0/Xhpod27q+YROUz2s8e4GGvhPNqfoO9FyDn/RJZDgN8f3CzXyVLim/3YLOGV87Z5tzHst8Z+OTY4BPkDEKt1tqDy6O7fr3ZXg2Ysxj5Q= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783604973; h=Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id:Cc; bh=B4+oPiiSLA8lSn//DmeMuFt13iUudlT/lx4X5aVo5/A=; b=GbdtfIphJmLLiuahhdkgTLSKASTohBlC2xWp9v6WhYyOF/sp0iNqvirTldlmla2DqigIuNT0u6KnIob8q1lOnwftJOGYEo904ySq7/gRAoF6+EdA/mYWp2IswG7QdBV3/AsI6Xa+YBoO4EhsWswHWQxcGG/nu1zVQQ9liOeq/1E= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [38.145.34.151]) by mx.zohomail.com with SMTPS id 1783604973267275.48971423817795; Thu, 9 Jul 2026 06:49:33 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id C7D3441D09; Thu, 9 Jul 2026 09:49:31 -0400 (EDT) Received: from [172.19.199.10] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id C609241CD0; Thu, 9 Jul 2026 09:41:25 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id CC7A941AC1; Thu, 9 Jul 2026 09:38:32 -0400 (EDT) Received: from relay.virtuozzo.com (relay.virtuozzo.com [130.117.225.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id 1ADCF41AC1 for ; Thu, 9 Jul 2026 09:38:30 -0400 (EDT) Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1whoh4-006MF9-2n for devel@lists.libvirt.org; Thu, 09 Jul 2026 15:22:38 +0200 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 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=B4+oPiiSLA8lSn//DmeMuFt13iUudlT/lx4X5aVo5/A=; b=wkOSy0PyluhK JbkpfxpyRMDqLfvluE2DkVWyFIA8mcyzgDrEcdJ1fn5+SAe1aHTYIBdwyjUKg3TJ8FYoG0sSzESp4 zmcOrZgMhMYfbslX1rreevXmSxdN76ZJKYsJKrMYHZyMGG7p4F+7ft6cUPIbS4KTrQ9Q5BHHhpl5u 3c/FX51eiCcccrXKHc0JPKsRdNdCLPtheraXWrJa2QcBrXq6xXhcuEeOQOLoiEAVsOrRGlU+cRedR 3IzLjlU74oaGkwedJmMrLkriEDmj6+hNvZmNX/pdP5wh2pYoYSoG3oQqOcNEERi8ES+eUc1c9KkqV QwsGtfOEVv2WRwQCOxH0+g==; To: devel@lists.libvirt.org Subject: [PATCH 5/6] tests: Add a unit test for virVsockConnectQuiet() Date: Thu, 9 Jul 2026 15:15:18 +0200 Message-ID: <20260709132213.2392145-6-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> References: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: polina.vishneva@virtuozzo.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation Message-ID-Hash: FID56EHSVVUILJRGUA7U5YCFSXWDAS6X X-Message-ID-Hash: FID56EHSVVUILJRGUA7U5YCFSXWDAS6X X-Mailman-Approved-At: Thu, 09 Jul 2026 13:40:43 +0000 X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Polina Vishneva via Devel Reply-To: Polina Vishneva X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1783604974383158500 Content-Type: text/plain; charset="utf-8" Mock the three syscalls so its address construction, connect-timeout option and errno preservation are checked without a real AF_VSOCK socket. Signed-off-by: Polina Vishneva --- tests/meson.build | 2 + tests/virvsockmock.c | 143 +++++++++++++++++++++++++++++++++++++++++++ tests/virvsocktest.c | 133 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 278 insertions(+) create mode 100644 tests/virvsockmock.c create mode 100644 tests/virvsocktest.c diff --git a/tests/meson.build b/tests/meson.build index ea50f89fb5..63e0474a29 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -94,6 +94,7 @@ if host_machine.system() =3D=3D 'linux' { 'name': 'virnetdevbandwidthmock' }, { 'name': 'virtestmock' }, { 'name': 'virusbmock' }, + { 'name': 'virvsockmock' }, ] endif =20 @@ -337,6 +338,7 @@ if host_machine.system() =3D=3D 'linux' { 'name': 'virresctrltest', 'link_whole': [ test_file_wrapper_lib ] }, { 'name': 'virscsitest' }, { 'name': 'virusbtest' }, + { 'name': 'virvsocktest' }, ] if conf.has('WITH_JSON') tests +=3D [ diff --git a/tests/virvsockmock.c b/tests/virvsockmock.c new file mode 100644 index 0000000000..a951ad5845 --- /dev/null +++ b/tests/virvsockmock.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2026 Virtuozzo International GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include "virmock.h" +#include "virstring.h" + +#include +#include +#include + +#ifdef __linux__ +# include +# include +# ifndef SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD +# define SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD SO_VM_SOCKETS_CONNECT_TIMEOUT +# endif +#endif + +#if defined(__linux__) && defined(RTLD_NEXT) + +static int (*real_socket)(int domain, int type, int protocol); +static int (*real_setsockopt)(int sockfd, int level, int optname, + const void *optval, socklen_t optlen); +static int (*real_connect)(int sockfd, const struct sockaddr *addr, + socklen_t addrlen); + +/* Microseconds passed to the connect-timeout setsockopt() during the curr= ent + * attempt, or -1 if it wasn't set. Reset by socket() and checked by conne= ct() + * so a dropped timeout can't pass on a stale value from an earlier attemp= t. */ +static long long appliedTimeoutUs =3D -1; + +static void +init_syms(void) +{ + if (real_socket) + return; + + VIR_MOCK_REAL_INIT(socket); + VIR_MOCK_REAL_INIT(setsockopt); + VIR_MOCK_REAL_INIT(connect); +} + +/* Read a base-10 long long from environment variable @name into @out, + * returning false (leaving @out untouched) when it is unset or unparseabl= e. */ +static bool +get_expected_ll(const char *name, long long *out) +{ + const char *s =3D getenv(name); + + return s && virStrToLong_ll(s, NULL, 10, out) =3D=3D 0; +} + +int +socket(int domain, int type, int protocol) +{ + init_syms(); + + /* Hand back a real, closable fd, and start a fresh attempt by forgett= ing + * any previously applied timeout. */ + if (domain =3D=3D AF_VSOCK) { + appliedTimeoutUs =3D -1; + return real_socket(AF_UNIX, type, protocol); + } + + return real_socket(domain, type, protocol); +} + +int +setsockopt(int sockfd, int level, int optname, + const void *optval, socklen_t optlen) +{ + init_syms(); + + /* vsock options use AF_VSOCK as the level. Decode only the connect + * timeout; swallow any other vsock option so it can't reach the AF_UN= IX + * fd we handed back from socket(). */ + if (level =3D=3D AF_VSOCK) { + if (optname =3D=3D SO_VM_SOCKETS_CONNECT_TIMEOUT_OLD && optval) { + const struct __kernel_old_timeval *tv =3D optval; + + appliedTimeoutUs =3D tv->tv_sec * 1000000LL + tv->tv_usec; + } + return 0; + } + + return real_setsockopt(sockfd, level, optname, optval, optlen); +} + +int +connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) +{ + init_syms(); + + if (addr && addr->sa_family =3D=3D AF_VSOCK) { + const struct sockaddr_vm *svm =3D (const struct sockaddr_vm *)(con= st void *)addr; + const char *errstr =3D getenv("LIBVIRT_VSOCK_MOCK_CONNECT_ERRNO"); + long long expected; + int err =3D 0; + + /* The test declares, via the environment, what production must ha= ve put + * on the wire; a mismatch fails the connect just like a bad reque= st + * would, so the test only needs to check the return value. */ + if ((get_expected_ll("LIBVIRT_VSOCK_MOCK_EXPECT_CID", &expected) && + svm->svm_cid !=3D expected) || + (get_expected_ll("LIBVIRT_VSOCK_MOCK_EXPECT_PORT", &expected) = && + svm->svm_port !=3D expected) || + (get_expected_ll("LIBVIRT_VSOCK_MOCK_EXPECT_TIMEOUT_US", &expe= cted) && + appliedTimeoutUs !=3D expected)) { + errno =3D EINVAL; + return -1; + } + + if (errstr) + ignore_value(virStrToLong_i(errstr, NULL, 10, &err)); + + if (err !=3D 0) { + errno =3D err; + return -1; + } + return 0; + } + + return real_connect(sockfd, addr, addrlen); +} + +#endif /* __linux__ && RTLD_NEXT */ diff --git a/tests/virvsocktest.c b/tests/virvsocktest.c new file mode 100644 index 0000000000..2571db0107 --- /dev/null +++ b/tests/virvsocktest.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2026 Virtuozzo International GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include + +#include "testutils.h" +#include "virvsock.h" +#include "virerror.h" +#include "virfile.h" +#include "virlog.h" + +#define VIR_FROM_THIS VIR_FROM_NONE + +VIR_LOG_INIT("tests.vsocktest"); + +static int +testVsockConnectAddr(const void *opaque G_GNUC_UNUSED) +{ + const unsigned int cid =3D 42; + const unsigned int port =3D 1234; + g_autofree char *cidStr =3D g_strdup_printf("%u", cid); + g_autofree char *portStr =3D g_strdup_printf("%u", port); + VIR_AUTOCLOSE fd =3D -1; + + /* The mock fails the connect unless production reaches it with exactly + * this cid/port, so a successful connect is the assertion. */ + g_setenv("LIBVIRT_VSOCK_MOCK_EXPECT_CID", cidStr, true); + g_setenv("LIBVIRT_VSOCK_MOCK_EXPECT_PORT", portStr, true); + + fd =3D virVsockConnectQuiet(cid, port); + + g_unsetenv("LIBVIRT_VSOCK_MOCK_EXPECT_CID"); + g_unsetenv("LIBVIRT_VSOCK_MOCK_EXPECT_PORT"); + + if (fd < 0) { + fprintf(stderr, "virVsockConnectQuiet failed: wrong cid/port reach= ed the kernel\n"); + return -1; + } + + return 0; +} + + +static int +testVsockConnectTimeout(const void *opaque G_GNUC_UNUSED) +{ + g_autofree char *timeoutStr =3D + g_strdup_printf("%lld", VIR_VSOCK_CONNECT_TIMEOUT_MS * 1000LL); + VIR_AUTOCLOSE fd =3D -1; + + /* The mock fails the connect unless the timeout was applied via + * setsockopt() beforehand. */ + g_setenv("LIBVIRT_VSOCK_MOCK_EXPECT_TIMEOUT_US", timeoutStr, true); + + fd =3D virVsockConnectQuiet(VIR_VSOCK_GUEST_CID_MIN, 1234); + + g_unsetenv("LIBVIRT_VSOCK_MOCK_EXPECT_TIMEOUT_US"); + + if (fd < 0) { + fprintf(stderr, "virVsockConnectQuiet failed: connect timeout not = applied\n"); + return -1; + } + + return 0; +} + + +static int +testVsockConnectErrno(const void *opaque G_GNUC_UNUSED) +{ + g_autofree char *errstr =3D g_strdup_printf("%d", ECONNREFUSED); + int fd; + + g_setenv("LIBVIRT_VSOCK_MOCK_CONNECT_ERRNO", errstr, true); + + virResetLastError(); + errno =3D 0; + fd =3D virVsockConnectQuiet(VIR_VSOCK_GUEST_CID_MIN, 1234); + g_unsetenv("LIBVIRT_VSOCK_MOCK_CONNECT_ERRNO"); + + if (fd !=3D -1) { + VIR_FORCE_CLOSE(fd); + fprintf(stderr, "virVsockConnectQuiet should have failed\n"); + return -1; + } + + if (errno !=3D ECONNREFUSED) { + fprintf(stderr, "expected errno %d, got %d\n", ECONNREFUSED, errno= ); + return -1; + } + + if (virGetLastError()) { + fprintf(stderr, "virVsockConnectQuiet reported a libvirt error\n"); + return -1; + } + + return 0; +} + + +static int +mymain(void) +{ + int ret =3D 0; + + if (virTestRun("vsock connect address", testVsockConnectAddr, NULL) < = 0) + ret =3D -1; + if (virTestRun("vsock connect timeout", testVsockConnectTimeout, NULL)= < 0) + ret =3D -1; + if (virTestRun("vsock connect errno preserved", testVsockConnectErrno,= NULL) < 0) + ret =3D -1; + + return ret =3D=3D 0 ? EXIT_SUCCESS : EXIT_FAILURE; +} + +VIR_TEST_MAIN_PRELOAD(mymain, VIR_TEST_MOCK("virvsock")) --=20 2.54.0 From nobody Tue Jul 14 21:25:29 2026 Delivered-To: importer@patchew.org Received-SPF: pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) client-ip=38.145.34.151; envelope-from=devel-bounces@lists.libvirt.org; helo=lists.libvirt.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass(p=reject dis=none) header.from=lists.libvirt.org ARC-Seal: i=1; a=rsa-sha256; t=1783604665; cv=none; d=zohomail.com; s=zohoarc; b=lNp8ex+3ebki8Fvp70wjb3Ofw2FbyKmnoTvxK9P87YgIaqfnC5sV8dSMbHGARshdFjuk+FAgS419SL82Ujcx1gi9AaVZibHf/ZNV/RK0kmaXWEQxU8StGX/hxUkRz88DU+BOdpJEiAwsX9hMoq65GyV7hS17IrzVbSVLbbAl7e0= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1783604665; h=Content-Transfer-Encoding:Date:Date:From:From:In-Reply-To:List-Subscribe:List-Post:List-Owner:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Reply-To:Reply-To:References:Subject:Subject:To:To:Message-Id:Cc; bh=XxOcISFJxtf0qQH0mBnjNejJ+0O0mnVuW04rvR4hjT8=; b=K3bPvZ7u9YNPz9zjpF4iPm0i9+gS1EQ8iUHiybw9mgoWC6IQSJUHIYLdNZzn16BzKOOAKpnZut/nutTHj6BH0sI349lG7IMg04eEDnzEkrLOqiuuExnetJ9q+sAlH+FpZtzHACz3DDxjUx5qnVHBcGRBxAJQ+FtOEA0b9zgZD0M= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=fail; spf=pass (zohomail.com: domain of lists.libvirt.org designates 38.145.34.151 as permitted sender) smtp.mailfrom=devel-bounces@lists.libvirt.org; dmarc=pass header.from= (p=reject dis=none) Return-Path: Received: from lists.libvirt.org (lists.libvirt.org [38.145.34.151]) by mx.zohomail.com with SMTPS id 1783604665219986.0492104900948; Thu, 9 Jul 2026 06:44:25 -0700 (PDT) Received: by lists.libvirt.org (Postfix, from userid 993) id C253C41CE2; Thu, 9 Jul 2026 09:44:23 -0400 (EDT) Received: from [172.19.199.10] (unknown [10.16.107.18]) by lists.libvirt.org (Postfix) with ESMTP id E576941D9A; Thu, 9 Jul 2026 09:41:00 -0400 (EDT) Received: by lists.libvirt.org (Postfix, from userid 993) id B324041ADF; Thu, 9 Jul 2026 09:38:23 -0400 (EDT) Received: from relay.virtuozzo.com (relay.virtuozzo.com [130.117.225.111]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (3072 bits) server-digest SHA256) (No client certificate requested) by lists.libvirt.org (Postfix) with ESMTPS id E9A4141ADC for ; Thu, 9 Jul 2026 09:38:21 -0400 (EDT) Received: from ch-vpn.virtuozzo.com ([130.117.225.6] helo=mami) by relay.virtuozzo.com with esmtp (Exim 4.96) (envelope-from ) id 1whoh5-006MF9-3C for devel@lists.libvirt.org; Thu, 09 Jul 2026 15:22:39 +0200 X-Spam-Checker-Version: SpamAssassin 4.0.1 (2024-03-26) on lists.libvirt.org X-Spam-Level: X-Spam-Status: No, score=-5.0 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,MAILING_LIST_MULTI,RCVD_IN_DNSWL_MED,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=4.0.1 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=XxOcISFJxtf0qQH0mBnjNejJ+0O0mnVuW04rvR4hjT8=; b=F9f0J6f/imuc AYp7DH0qq4XPDh/TjRksaK7pazFGI2vHufZtTb8LTOWAp490a9TMhFuesha5Avu0gs3Pxo0OvQHqx U8Y6sIfyiX4f1Qdf1C8UG/rRY5BPcBvf54koHUnUBBAy16KHfwm81yG1g/FrXkiJ1vYFk3fmbVSI4 1NNjcvBqSnQpMKc10Uxuz1q9bs6vy/iEp3Ctb+LQRBqLR+O+ATNKcIn+3PvSOqaMVorcD9EVkZDhm r5ZtdJbRTBgv2U6rfQ8NdtUC9rNEfkCHZpYOFlGz/cPc69uiaulSCy3YhgQmk+saEaeyFd5HeXM4X 6GM78Bbv9Bjrmh7H/iqd3Q==; To: devel@lists.libvirt.org Subject: [PATCH 6/6] tests: Add a test for qemuConnectAgent() to cover the vsock path Date: Thu, 9 Jul 2026 15:15:19 +0200 Message-ID: <20260709132213.2392145-7-polina.vishneva@virtuozzo.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> References: <20260709132213.2392145-1-polina.vishneva@virtuozzo.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-MailFrom: polina.vishneva@virtuozzo.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-devel.lists.libvirt.org-0; emergency; member-moderation Message-ID-Hash: CJOT55BDZPEGFOTXY5XVUXG34CKPZDKD X-Message-ID-Hash: CJOT55BDZPEGFOTXY5XVUXG34CKPZDKD X-Mailman-Approved-At: Thu, 09 Jul 2026 13:40:42 +0000 X-Mailman-Version: 3.3.10 Precedence: list List-Id: Development discussions about the libvirt library & tools Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Polina Vishneva via Devel Reply-To: Polina Vishneva X-ZohoMail-DKIM: fail (Header signature does not verify) X-ZM-MESSAGEID: 1783604668135158500 Content-Type: text/plain; charset="utf-8" qemuagenttest hands an fd straight to qemuAgentOpen(). Transport selection logic was added to qemuConnectAgent(), so to test the vsock path and related logic, we need to test it instead. Mock virVsockConnectQuiet() to the scripted unix QGA server, proving the CID-based vsock branch is taken and a command passes through. Signed-off-by: Polina Vishneva --- src/util/virvsock.h | 5 +- tests/meson.build | 1 + tests/qemuagenttest.c | 43 ++++++++++++- tests/qemumonitortestutils.c | 119 ++++++++++++++++++++++++++++++++--- tests/qemumonitortestutils.h | 4 ++ tests/qemuvsockagentmock.c | 65 +++++++++++++++++++ 6 files changed, 228 insertions(+), 9 deletions(-) create mode 100644 tests/qemuvsockagentmock.c diff --git a/src/util/virvsock.h b/src/util/virvsock.h index 8bf08f9228..215bde2d3e 100644 --- a/src/util/virvsock.h +++ b/src/util/virvsock.h @@ -18,6 +18,8 @@ =20 #pragma once =20 +#include "internal.h" + #define VIR_VSOCK_GUEST_CID_MIN 3 #define VIR_VSOCK_CONNECT_TIMEOUT_MS 200 =20 @@ -31,4 +33,5 @@ virVsockAcquireGuestCid(int fd, =20 int virVsockConnectQuiet(unsigned int cid, - unsigned int port); + unsigned int port) + ATTRIBUTE_MOCKABLE; diff --git a/tests/meson.build b/tests/meson.build index 63e0474a29..cd041dd086 100644 --- a/tests/meson.build +++ b/tests/meson.build @@ -178,6 +178,7 @@ if conf.has('WITH_QEMU') { 'name': 'qemucapsprobemock', 'link_with': [ test_qemu_driver_lib ] }, { 'name': 'qemucpumock' }, { 'name': 'qemuhotplugmock', 'link_with': [ test_qemu_driver_lib, test= _utils_qemu_lib, test_utils_lib ] }, + { 'name': 'qemuvsockagentmock' }, { 'name': 'qemuxml2argvmock', 'link_with': [ test_utils_lib ] }, { 'name': 'virhostidmock' }, ] diff --git a/tests/qemuagenttest.c b/tests/qemuagenttest.c index 74cd317e74..c8e11cfd3c 100644 --- a/tests/qemuagenttest.c +++ b/tests/qemuagenttest.c @@ -23,6 +23,7 @@ #include "testutilsqemu.h" #include "qemumonitortestutils.h" #include "qemu/qemu_conf.h" +#include "qemu/qemu_domain.h" #include "hypervisor/qemu_agent.h" #include "virerror.h" =20 @@ -1394,6 +1395,45 @@ testQemuAgentGetLoadAvg(const void *data) } =20 =20 +static int +testQemuAgentVsock(const void *data) +{ + virDomainXMLOption *xmlopt =3D (virDomainXMLOption *)data; + g_autoptr(qemuMonitorTest) test =3D qemuMonitorTestNewAgentVsock(&driv= er, xmlopt); + virDomainObj *vm; + int rc; + + if (!test) + return -1; + + /* The point of this test: qemuConnectAgent() chose vsock and flagged = it. */ + vm =3D qemuMonitorTestGetDomainObj(test); + if (!QEMU_DOMAIN_PRIVATE(vm)->agentIsVsock) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "agent transport is not vsock"); + return -1; + } + + if (qemuMonitorTestAddAgentSyncResponse(test) < 0) + return -1; + + if (qemuMonitorTestAddItem(test, "guest-fsfreeze-thaw", + "{ \"return\" : 5 }") < 0) + return -1; + + if ((rc =3D qemuAgentFSThaw(qemuMonitorTestGetAgent(test))) < 0) + return -1; + + if (rc !=3D 5) { + virReportError(VIR_ERR_INTERNAL_ERROR, + "expected 5 thawed filesystems, got %d", rc); + return -1; + } + + return 0; +} + + static int mymain(void) { @@ -1431,6 +1471,7 @@ mymain(void) DO_TEST(SSHKeys); DO_TEST(GetDisks); DO_TEST(GetLoadAvg); + DO_TEST(Vsock); =20 DO_TEST(Timeout); /* Timeout should always be called last */ =20 @@ -1439,4 +1480,4 @@ mymain(void) return (ret =3D=3D 0) ? EXIT_SUCCESS : EXIT_FAILURE; } =20 -VIR_TEST_MAIN(mymain) +VIR_TEST_MAIN_PRELOAD(mymain, VIR_TEST_MOCK("qemuvsockagent")) diff --git a/tests/qemumonitortestutils.c b/tests/qemumonitortestutils.c index e83dd1d9c4..ffdf55a5f9 100644 --- a/tests/qemumonitortestutils.c +++ b/tests/qemumonitortestutils.c @@ -30,12 +30,14 @@ #include "hypervisor/qemu_agent.h" #include "qemu/qemu_domain.h" #include "qemu/qemu_processpriv.h" +#include "qemu/qemu_process.h" #include "qemu/qemu_monitor.h" #include "rpc/virnetsocket.h" #include "viralloc.h" #include "virlog.h" #include "virerror.h" #include "vireventthread.h" +#include "virvsock.h" =20 #define VIR_FROM_THIS VIR_FROM_NONE =20 @@ -1187,16 +1189,48 @@ qemuMonitorTestNewFromFileFull(const char *fileName, } =20 =20 +/* Build the bare agent test: the common test object plus the event + * thread the agent runs on. @src is filled in by the caller's transport. = */ +static qemuMonitorTest * +qemuMonitorTestNewAgentInit(virDomainXMLOption *xmlopt, + const char *threadName, + virDomainChrSourceDef *src) +{ + g_autoptr(qemuMonitorTest) test =3D NULL; + + if (!(test =3D qemuMonitorCommonTestNew(xmlopt, NULL, src))) + return NULL; + + if (!(test->eventThread =3D virEventThreadNew(threadName))) + return NULL; + + return g_steal_pointer(&test); +} + + +/* Finish an agent test once test->agent is connected: lock it, run the mo= nitor + * init and release @src. On failure the caller's error path frees @test. = */ +static int +qemuMonitorTestAgentStart(qemuMonitorTest *test, + virDomainChrSourceDef *src) +{ + virObjectLock(test->agent); + + if (qemuMonitorCommonTestInit(test) < 0) + return -1; + + virDomainChrSourceDefClear(src); + return 0; +} + + qemuMonitorTest * qemuMonitorTestNewAgent(virDomainXMLOption *xmlopt) { g_autoptr(qemuMonitorTest) test =3D NULL; virDomainChrSourceDef src =3D { 0 }; =20 - if (!(test =3D qemuMonitorCommonTestNew(xmlopt, NULL, &src))) - goto error; - - if (!(test->eventThread =3D virEventThreadNew("agent-test"))) + if (!(test =3D qemuMonitorTestNewAgentInit(xmlopt, "agent-test", &src)= )) goto error; =20 if (!(test->agent =3D qemuAgentOpen(test->vm, @@ -1206,16 +1240,87 @@ qemuMonitorTestNewAgent(virDomainXMLOption *xmlopt) QEMU_DOMAIN_PRIVATE(test->vm)->agent= Timeout))) goto error; =20 - virObjectLock(test->agent); - - if (qemuMonitorCommonTestInit(test) < 0) + if (qemuMonitorTestAgentStart(test, &src) < 0) goto error; =20 + return g_steal_pointer(&test); + + error: virDomainChrSourceDefClear(&src); + return NULL; +} + + +/* + * Like qemuMonitorTestNewAgent(), but drives the real public qemuConnectA= gent() + * so the transport-selection and vsock connect path actually run. The + * qemuvsockagentmock overrides virVsockConnectQuiet() to feed back an fd = wired + * to @test's scripted unix mock-QGA server, so no real AF_VSOCK is involv= ed. + * + * @driver must be the same driver @xmlopt was built for, so the domain's + * private data auto-wires priv->driver. + */ +qemuMonitorTest * +qemuMonitorTestNewAgentVsock(virQEMUDriver *driver, + virDomainXMLOption *xmlopt) +{ + g_autoptr(qemuMonitorTest) test =3D NULL; + virDomainChrSourceDef src =3D { 0 }; + qemuDomainObjPrivate *priv; + + if (!(test =3D qemuMonitorTestNewAgentInit(xmlopt, "agent-vsock-test",= &src))) + goto error; + + /* Make the domain look active and give it a usable guest CID so + * qemuConnectAgent() picks the vsock transport. */ + test->vm->def->id =3D 1; + test->vm->def->name =3D g_strdup("agent-vsock-test"); + + if (!(test->vm->def->vsock =3D virDomainVsockDefNew(xmlopt))) + goto error; + test->vm->def->vsock->guest_cid =3D VIR_VSOCK_GUEST_CID_MIN; + + priv =3D QEMU_DOMAIN_PRIVATE(test->vm); + priv->agentTimeout =3D 5; + /* Alias the event thread without a ref: production only reads it duri= ng the + * connect (the agent takes its own context ref). Severed below before= the + * private-data teardown could unref the thread qemuMonitorTestFree ow= ns. */ + priv->eventThread =3D test->eventThread; + + g_setenv("LIBVIRT_VSOCK_MOCK_PATH", src.data.nix.path, true); + + /* test->vm is already locked (virDomainObjNew() returns it locked), w= hich + * is what the connect path needs: it unlocks/relocks the vm internall= y. */ + + /* Drive the real connect. Do NOT ref the vm: qemuAgentOpenFd() takes = its + * own reference for the agent. */ + if (qemuConnectAgent(driver, test->vm) < 0 || + !priv->agent || !priv->agentIsVsock) { + /* A graceful failure may have armed a reconnect timer holding a vm + * ref; drop it (and the thread alias) before tearing down. */ + qemuDomainCancelAgentVsockReconnect(priv); + priv->eventThread =3D NULL; + virObjectUnlock(test->vm); + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + "guest agent did not connect over vsock"); + goto error; + } + + /* Hand the agent to the test object (matches the unix path's ref acco= unting) + * and sever the thread alias. */ + test->agent =3D g_steal_pointer(&priv->agent); + priv->eventThread =3D NULL; + + virObjectUnlock(test->vm); + + if (qemuMonitorTestAgentStart(test, &src) < 0) + goto error; =20 + g_unsetenv("LIBVIRT_VSOCK_MOCK_PATH"); return g_steal_pointer(&test); =20 error: + g_unsetenv("LIBVIRT_VSOCK_MOCK_PATH"); virDomainChrSourceDefClear(&src); return NULL; } diff --git a/tests/qemumonitortestutils.h b/tests/qemumonitortestutils.h index c87cdf0e6a..1908e91fc0 100644 --- a/tests/qemumonitortestutils.h +++ b/tests/qemumonitortestutils.h @@ -99,6 +99,10 @@ qemuMonitorTestNewFromFileFull(const char *fileName, qemuMonitorTest * qemuMonitorTestNewAgent(virDomainXMLOption *xmlopt); =20 +qemuMonitorTest * +qemuMonitorTestNewAgentVsock(virQEMUDriver *driver, + virDomainXMLOption *xmlopt); + =20 void qemuMonitorTestFree(qemuMonitorTest *test); diff --git a/tests/qemuvsockagentmock.c b/tests/qemuvsockagentmock.c new file mode 100644 index 0000000000..d1c2760302 --- /dev/null +++ b/tests/qemuvsockagentmock.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2026 Virtuozzo International GmbH + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + */ + +#include + +#include "internal.h" +#include "virvsock.h" +#include "virfile.h" +#include "virstring.h" + +#include +#include +#include +#include + +/* + * Override the only genuinely vsock-specific syscall wrapper so the real + * qemuConnectAgent()/qemuChannelOpenVsock() dispatch runs under test. The + * cid/port are irrelevant here: hand back an fd wired to the scripted moc= k-QGA + * unix server the test set up (mirrors qemuMonitorTestOpenChannel()). + */ +int +virVsockConnectQuiet(unsigned int cid G_GNUC_UNUSED, + unsigned int port G_GNUC_UNUSED) +{ + const char *path =3D getenv("LIBVIRT_VSOCK_MOCK_PATH"); + struct sockaddr_un addr =3D { .sun_family =3D AF_UNIX }; + int fd; + + if (!path) { + errno =3D ENOENT; + return -1; + } + + if ((fd =3D socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + return -1; + + if (virStrcpyStatic(addr.sun_path, path) < 0) { + VIR_FORCE_CLOSE(fd); + errno =3D ENAMETOOLONG; + return -1; + } + + if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + VIR_FORCE_CLOSE(fd); + return -1; + } + + return fd; +} --=20 2.54.0