This API allows callers to pass and store file descriptors inside libvirt
using a hash table, with a provided "name" serving as the key. The stored
file descriptors can be queried and reused later, enabling use cases such as
FD-based live migration.

Signed-off-by: Tejus GK <[email protected]>
---
 include/libvirt/libvirt-domain.h    |  9 +++++
 src/driver-hypervisor.h             |  7 ++++
 src/libvirt-domain.c                | 62 +++++++++++++++++++++++++++++
 src/libvirt_public.syms             |  5 +++
 src/qemu/qemu_conf.h                |  4 ++
 src/qemu/qemu_driver.c              | 41 +++++++++++++++++++
 src/remote/remote_daemon_dispatch.c | 36 +++++++++++++++++
 src/remote/remote_driver.c          | 22 ++++++++++
 src/remote/remote_protocol.x        | 12 +++++-
 9 files changed, 197 insertions(+), 1 deletion(-)

diff --git a/include/libvirt/libvirt-domain.h b/include/libvirt/libvirt-domain.h
index b0acd0083b..be40a127d6 100644
--- a/include/libvirt/libvirt-domain.h
+++ b/include/libvirt/libvirt-domain.h
@@ -8751,5 +8751,14 @@ virDomainDelThrottleGroup(virDomainPtr dom,
                           const char *group,
                           unsigned int flags);
 
+/**
+* virDomainFDStore:
+*
+* since: 12.0.0
+*/
+int virDomainFDStore(virConnectPtr conn,
+    const char *name,
+    unsigned int nfds,
+    int *fds);
 
 #endif /* LIBVIRT_DOMAIN_H */
diff --git a/src/driver-hypervisor.h b/src/driver-hypervisor.h
index 6a43688b0c..0c2a9041fe 100644
--- a/src/driver-hypervisor.h
+++ b/src/driver-hypervisor.h
@@ -1473,6 +1473,12 @@ typedef int
                                 const char *groupname,
                                 unsigned int flags);
 
+typedef int
+(*virDrvDomainFDStore)(virConnectPtr conn,
+                        const char *name,
+                        unsigned int nfds,
+                        int *fds);
+
 typedef struct _virHypervisorDriver virHypervisorDriver;
 
 /**
@@ -1750,4 +1756,5 @@ struct _virHypervisorDriver {
     virDrvDomainGraphicsReload domainGraphicsReload;
     virDrvDomainSetThrottleGroup domainSetThrottleGroup;
     virDrvDomainDelThrottleGroup domainDelThrottleGroup;
+    virDrvDomainFDStore domainFDStore;
 };
diff --git a/src/libvirt-domain.c b/src/libvirt-domain.c
index 74c70a0a43..7e194f7193 100644
--- a/src/libvirt-domain.c
+++ b/src/libvirt-domain.c
@@ -14269,3 +14269,65 @@ virDomainDelThrottleGroup(virDomainPtr dom,
     virDispatchError(dom->conn);
     return -1;
 }
+
+
+/**
+ * virDomainFDStore:
+ * @conn: a connection object
+ * @name: name for the file descriptor group
+ * @nfds: number of fds in @fds
+ * @fds: file descriptors to store
+ *
+ * Store the FDs in @fds with @conn under @name. The FDs persist unitl
+ * explicitly consumed (e.g. during a live migration) or until libvirt
+ * shuts down/ restarts.
+ *
+ * Unlike virDomainFDAssociate, this does not require a domain to exist
+ * at the time of storing the FDs.
+ *
+ * Returns 0 on success, -1 on error.
+ *
+ * Since: 12.0.0
+ */
+ int
+ virDomainFDStore(virConnectPtr conn,
+                   const char *name,
+                   unsigned int nfds,
+                   int *fds)
+ {
+     int rc;
+
+     VIR_DEBUG("conn=%p, name='%s', nfds=%u, fds=%p",
+               conn, name, nfds, fds);
+
+     virResetLastError();
+
+     virCheckConnectReturn(conn, -1);
+     virCheckNonNullArgGoto(name, error);
+     virCheckNonZeroArgGoto(nfds, error);
+     virCheckNonNullArgGoto(fds, error);
+
+     if ((rc = VIR_DRV_SUPPORTS_FEATURE(conn->driver, conn,
+                                        VIR_DRV_FEATURE_FD_PASSING)) < 0)
+         goto error;
+
+     if (rc == 0) {
+         virReportError(VIR_ERR_ARGUMENT_UNSUPPORTED, "%s",
+                        _("fd passing is not supported by this connection"));
+         goto error;
+     }
+
+     if (!conn->driver->domainFDStore) {
+         virReportUnsupportedError();
+         goto error;
+     }
+
+     if ((rc = conn->driver->domainFDStore(conn, name, nfds, fds)) < 0)
+         goto error;
+
+     return rc;
+
+  error:
+     virDispatchError(conn);
+     return -1;
+ }
diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms
index c506acd2ed..3b3a4bb4e1 100644
--- a/src/libvirt_public.syms
+++ b/src/libvirt_public.syms
@@ -956,4 +956,9 @@ LIBVIRT_11.2.0 {
         virDomainDelThrottleGroup;
 } LIBVIRT_10.2.0;
 
+LIBVIRT_12.0.0 {
+    global:
+        virDomainFDStore;
+} LIBVIRT_11.2.0;
+
 # .... define new API here using predicted next version number ....
diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h
index c284e108a1..cc655811f7 100644
--- a/src/qemu/qemu_conf.h
+++ b/src/qemu/qemu_conf.h
@@ -349,6 +349,10 @@ struct _virQEMUDriver {
 
     /* Immutable pointer, self-locking APIs */
     virFileCache *nbdkitCapsCache;
+
+    /* Hash table mapping FD names (string) to qemuFDTuple objects */
+    GHashTable *domainFDs;
+
 };
 
 virQEMUDriverConfig *virQEMUDriverConfigNew(bool privileged,
diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c
index 3f154969b8..94a7b17090 100644
--- a/src/qemu/qemu_driver.c
+++ b/src/qemu/qemu_driver.c
@@ -35,6 +35,7 @@
 #include "qemu_alias.h"
 #include "qemu_block.h"
 #include "qemu_conf.h"
+#include "qemu_fd.h"
 #include "qemu_capabilities.h"
 #include "qemu_command.h"
 #include "qemu_hostdev.h"
@@ -558,6 +559,11 @@ qemuStateInitialize(bool privileged,
     if (!(qemu_driver->domains = virDomainObjListNew()))
         goto error;
 
+    qemu_driver->domainFDs = g_hash_table_new_full(g_str_hash,
+                                                   g_str_equal,
+                                                   g_free,
+                                                   (GDestroyNotify) 
qemuFDTupleFree);
+
     /* Init domain events */
     qemu_driver->domainEventState = virObjectEventStateNew();
     if (!qemu_driver->domainEventState)
@@ -1033,6 +1039,7 @@ qemuStateCleanup(void)
     virObjectUnref(qemu_driver->domains);
     virObjectUnref(qemu_driver->nbdkitCapsCache);
     virInhibitorFree(qemu_driver->inhibitor);
+    g_clear_pointer(&qemu_driver->domainFDs, g_hash_table_unref);
 
     if (qemu_driver->lockFD != -1)
         virPidFileRelease(qemu_driver->config->stateDir, "driver", 
qemu_driver->lockFD);
@@ -20259,6 +20266,39 @@ qemuDomainFDAssociate(virDomainPtr domain,
     return ret;
 }
 
+static int
+qemuDomainFDStore(virConnectPtr conn,
+                   const char *name,
+                   unsigned int nfds,
+                   int *fds)
+{
+    virQEMUDriver *driver = conn->privateData;
+    g_autoptr(qemuFDTuple) new = NULL;
+    size_t i;
+
+    if (nfds == 0)
+        return 0;
+
+    new = qemuFDTupleNew();
+    new->nfds = nfds;
+    new->fds = g_new0(int, new->nfds);
+
+    for (i = 0; i < new->nfds; i++) {
+        if ((new->fds[i] = dup(fds[i])) < 0) {
+            virReportSystemError(errno,
+                                 _("failed to duplicate passed fd with index 
'%1$zu'"),
+                                 i);
+            return -1;
+        }
+    }
+
+    VIR_WITH_MUTEX_LOCK_GUARD(&driver->lock) {
+        g_hash_table_insert(driver->domainFDs, g_strdup(name), 
g_steal_pointer(&new));
+    }
+
+    return 0;
+}
+
 static int
 qemuDomainGraphicsReload(virDomainPtr domain,
                          unsigned int type,
@@ -20804,6 +20844,7 @@ static virHypervisorDriver qemuHypervisorDriver = {
     .domainSetAutostartOnce = qemuDomainSetAutostartOnce, /* 11.2.0 */
     .domainSetThrottleGroup = qemuDomainSetThrottleGroup, /* 11.2.0 */
     .domainDelThrottleGroup = qemuDomainDelThrottleGroup, /* 11.2.0 */
+    .domainFDStore = qemuDomainFDStore, /* 12.0.0 */
 };
 
 
diff --git a/src/remote/remote_daemon_dispatch.c 
b/src/remote/remote_daemon_dispatch.c
index 7e74ff063f..bbf39be748 100644
--- a/src/remote/remote_daemon_dispatch.c
+++ b/src/remote/remote_daemon_dispatch.c
@@ -7163,6 +7163,42 @@ remoteDispatchNetworkPortGetParameters(virNetServer 
*server G_GNUC_UNUSED,
     return rv;
 }
 
+static int
+remoteDispatchDomainFdStore(virNetServer *server G_GNUC_UNUSED,
+                             virNetServerClient *client,
+                             virNetMessage *msg G_GNUC_UNUSED,
+                             struct virNetMessageError *rerr,
+                             remote_domain_fd_store_args *args)
+{
+    int *fds = NULL;
+    unsigned int nfds = 0;
+    int rv = -1;
+    virConnectPtr conn = remoteGetHypervisorConn(client);
+    size_t i;
+
+    if (!conn)
+        goto cleanup;
+
+    fds = g_new0(int, msg->nfds);
+    for (i = 0; i < msg->nfds; i++) {
+        if ((fds[i] = virNetMessageDupFD(msg, i)) < 0)
+            goto cleanup;
+        nfds++;
+    }
+
+    if (virDomainFDStore(conn, args->name, nfds, fds) < 0)
+        goto cleanup;
+
+    rv = 0;
+
+ cleanup:
+    for (i = 0; i < nfds; i++)
+        VIR_FORCE_CLOSE(fds[i]);
+    g_free(fds);
+    if (rv < 0)
+        virNetMessageSaveError(rerr);
+    return rv;
+}
 
 /*----- Helpers. -----*/
 
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index ec71eaed87..db255ac125 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -7488,6 +7488,27 @@ remoteDomainFDAssociate(virDomainPtr domain,
     return 0;
 }
 
+static int
+remoteDomainFDStore(virConnectPtr conn,
+                     const char *name,
+                     unsigned int nfds,
+                     int *fds)
+{
+    remote_domain_fd_store_args args = {0};
+    struct private_data *priv = conn->privateData;
+    VIR_LOCK_GUARD lock = remoteDriverLock(priv);
+
+    args.name = (char *)name;
+
+    if (callFull(conn, priv, 0, fds, nfds, NULL, NULL,
+                 REMOTE_PROC_DOMAIN_FD_STORE,
+                 (xdrproc_t) xdr_remote_domain_fd_store_args, (char *) &args,
+                 (xdrproc_t) xdr_void, (char *) NULL) == -1)
+        return -1;
+
+    return 0;
+}
+
 
 /* get_nonnull_domain and get_nonnull_network turn an on-wire
  * (name, uuid) pair into virDomainPtr or virNetworkPtr object.
@@ -7935,6 +7956,7 @@ static virHypervisorDriver hypervisor_driver = {
     .domainGraphicsReload = remoteDomainGraphicsReload, /* 10.2.0 */
     .domainSetThrottleGroup = remoteDomainSetThrottleGroup, /* 11.2.0 */
     .domainDelThrottleGroup = remoteDomainDelThrottleGroup, /* 11.2.0 */
+    .domainFDStore = remoteDomainFDStore, /* 12.0.0 */
 };
 
 static virNetworkDriver network_driver = {
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 3c93203210..3ddfdd8bec 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -4008,6 +4008,10 @@ struct remote_domain_event_nic_mac_change_msg {
     remote_nonnull_string newMAC;
 };
 
+struct remote_domain_fd_store_args {
+    remote_nonnull_string name;
+};
+
 /*----- Protocol. -----*/
 
 /* Define the program number, protocol version and procedure numbers here. */
@@ -7119,5 +7123,11 @@ enum remote_procedure {
      * @generate: both
      * @acl: none
      */
-    REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE = 453
+    REMOTE_PROC_DOMAIN_EVENT_NIC_MAC_CHANGE = 453,
+
+    /**
+     * @generate: none
+     * @acl: none
+     */
+    REMOTE_PROC_DOMAIN_FD_STORE = 454
 };
-- 
2.43.7

Reply via email to