---
daemon/libvirtd.h | 2 +
daemon/remote.c | 206 +++++++++++++++++++++++++++++++++++++++++++
src/remote/remote_driver.c | 139 +++++++++++++++++++++++++++++
src/remote/remote_protocol.x | 43 ++++++++-
src/remote_protocol-structs | 19 ++++
5 files changed, 408 insertions(+), 1 deletion(-)
diff --git a/daemon/libvirtd.h b/daemon/libvirtd.h
index cc91266..09d505d 100644
--- a/daemon/libvirtd.h
+++ b/daemon/libvirtd.h
@@ -62,6 +62,8 @@ struct daemonClientPrivate {
size_t nqemuEventCallbacks;
daemonClientEventCallbackPtr *storageEventCallbacks;
size_t nstorageEventCallbacks;
+ daemonClientEventCallbackPtr *nodeDeviceEventCallbacks;
+ size_t nnodeDeviceEventCallbacks;
bool closeRegistered;
# if WITH_SASL
diff --git a/daemon/remote.c b/daemon/remote.c
index 4aa43c2..93af450 100644
--- a/daemon/remote.c
+++ b/daemon/remote.c
@@ -91,6 +91,7 @@ static virStorageVolPtr get_nonnull_storage_vol(virConnectPtr conn,
remote_nonnu
static virSecretPtr get_nonnull_secret(virConnectPtr conn, remote_nonnull_secret
secret);
static virNWFilterPtr get_nonnull_nwfilter(virConnectPtr conn, remote_nonnull_nwfilter
nwfilter);
static virDomainSnapshotPtr get_nonnull_domain_snapshot(virDomainPtr dom,
remote_nonnull_domain_snapshot snapshot);
+static virNodeDevicePtr get_nonnull_node_device(virConnectPtr conn,
remote_nonnull_node_device dev);
static void make_nonnull_domain(remote_nonnull_domain *dom_dst, virDomainPtr dom_src);
static void make_nonnull_network(remote_nonnull_network *net_dst, virNetworkPtr
net_src);
static void make_nonnull_interface(remote_nonnull_interface *interface_dst,
virInterfacePtr interface_src);
@@ -209,6 +210,32 @@ remoteRelayStoragePoolEventCheckACL(virNetServerClientPtr client,
}
static bool
+remoteRelayNodeDeviceEventCheckACL(virNetServerClientPtr client,
+ virConnectPtr conn,
+ virNodeDevicePtr dev)
+{
+ virNodeDeviceDef def;
+ virIdentityPtr identity = NULL;
+ bool ret = false;
+
+ /* For now, we just create a virNodeDeviceDef with enough contents to
+ * satisfy what viraccessdriverpolkit.c references. This is a bit
+ * fragile, but I don't know of anything better. */
+ def.name = dev->name;
+
+ if (!(identity = virNetServerClientGetIdentity(client)))
+ goto cleanup;
+ if (virIdentitySetCurrent(identity) < 0)
+ goto cleanup;
+ ret = virConnectNodeDeviceEventRegisterAnyCheckACL(conn, &def);
+
+ cleanup:
+ ignore_value(virIdentitySetCurrent(NULL));
+ virObjectUnref(identity);
+ return ret;
+}
+
+static bool
remoteRelayDomainQemuMonitorEventCheckACL(virNetServerClientPtr client,
virConnectPtr conn, virDomainPtr dom)
{
@@ -1329,6 +1356,44 @@ static virConnectStoragePoolEventGenericCallback
storageEventCallbacks[] = {
verify(ARRAY_CARDINALITY(storageEventCallbacks) == VIR_STORAGE_POOL_EVENT_ID_LAST);
+static int
+remoteRelayNodeDeviceEventLifecycle(virConnectPtr conn,
+ virNodeDevicePtr dev,
+ int event,
+ int detail,
+ void *opaque)
+{
+ daemonClientEventCallbackPtr callback = opaque;
+ remote_node_device_event_lifecycle_msg data;
+
+ if (callback->callbackID < 0 ||
+ !remoteRelayNodeDeviceEventCheckACL(callback->client, conn, dev))
+ return -1;
+
+ VIR_DEBUG("Relaying node device lifecycle event %d, detail %d, callback
%d",
+ event, detail, callback->callbackID);
+
+ /* build return data */
+ memset(&data, 0, sizeof(data));
+ make_nonnull_node_device(&data.dev, dev);
+ data.callbackID = callback->callbackID;
+ data.event = event;
+ data.detail = detail;
+
+ remoteDispatchObjectEventSend(callback->client, remoteProgram,
+ REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE,
+ (xdrproc_t)xdr_remote_node_device_event_lifecycle_msg,
+ &data);
+
+ return 0;
+}
+
+static virConnectNodeDeviceEventGenericCallback nodeDeviceEventCallbacks[] = {
+ VIR_NODE_DEVICE_EVENT_CALLBACK(remoteRelayNodeDeviceEventLifecycle),
+};
+
+verify(ARRAY_CARDINALITY(nodeDeviceEventCallbacks) == VIR_NODE_DEVICE_EVENT_ID_LAST);
+
static void
remoteRelayDomainQemuMonitorEvent(virConnectPtr conn,
virDomainPtr dom,
@@ -1451,6 +1516,21 @@ void remoteClientFreeFunc(void *data)
}
VIR_FREE(priv->storageEventCallbacks);
+ for (i = 0; i < priv->nnodeDeviceEventCallbacks; i++) {
+ int callbackID = priv->nodeDeviceEventCallbacks[i]->callbackID;
+ if (callbackID < 0) {
+ VIR_WARN("unexpected incomplete node device callback %zu", i);
+ continue;
+ }
+ VIR_DEBUG("Deregistering remote node device event relay %d",
+ callbackID);
+ priv->nodeDeviceEventCallbacks[i]->callbackID = -1;
+ if (virConnectNodeDeviceEventDeregisterAny(priv->conn,
+ callbackID) < 0)
+ VIR_WARN("unexpected node device event deregister failure");
+ }
+ VIR_FREE(priv->nodeDeviceEventCallbacks);
+
for (i = 0; i < priv->nqemuEventCallbacks; i++) {
int callbackID = priv->qemuEventCallbacks[i]->callbackID;
if (callbackID < 0) {
@@ -5656,6 +5736,126 @@ remoteDispatchConnectStoragePoolEventDeregisterAny(virNetServerPtr
server ATTRIB
return rv;
}
+static int
+remoteDispatchConnectNodeDeviceEventRegisterAny(virNetServerPtr server ATTRIBUTE_UNUSED,
+ virNetServerClientPtr client,
+ virNetMessagePtr msg ATTRIBUTE_UNUSED,
+ virNetMessageErrorPtr rerr
ATTRIBUTE_UNUSED,
+
remote_connect_node_device_event_register_any_args *args,
+
remote_connect_node_device_event_register_any_ret *ret)
+{
+ int callbackID;
+ int rv = -1;
+ daemonClientEventCallbackPtr callback = NULL;
+ daemonClientEventCallbackPtr ref;
+ struct daemonClientPrivate *priv =
+ virNetServerClientGetPrivateData(client);
+ virNodeDevicePtr dev = NULL;
+
+ if (!priv->conn) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not
open"));
+ goto cleanup;
+ }
+
+ virMutexLock(&priv->lock);
+
+ if (args->dev &&
+ !(dev = get_nonnull_node_device(priv->conn, *args->dev)))
+ goto cleanup;
+
+ if (args->eventID >= VIR_NODE_DEVICE_EVENT_ID_LAST || args->eventID < 0)
{
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("unsupported node device event ID %d"),
args->eventID);
+ goto cleanup;
+ }
+
+ /* If we call register first, we could append a complete callback
+ * to our array, but on OOM append failure, we'd have to then hope
+ * deregister works to undo our register. So instead we append an
+ * incomplete callback to our array, then register, then fix up
+ * our callback; but since VIR_APPEND_ELEMENT clears 'callback' on
+ * success, we use 'ref' to save a copy of the pointer. */
+ if (VIR_ALLOC(callback) < 0)
+ goto cleanup;
+ callback->client = client;
+ callback->eventID = args->eventID;
+ callback->callbackID = -1;
+ ref = callback;
+ if (VIR_APPEND_ELEMENT(priv->nodeDeviceEventCallbacks,
+ priv->nnodeDeviceEventCallbacks,
+ callback) < 0)
+ goto cleanup;
+
+ if ((callbackID = virConnectNodeDeviceEventRegisterAny(priv->conn,
+ dev,
+ args->eventID,
+
nodeDeviceEventCallbacks[args->eventID],
+ ref,
+ remoteEventCallbackFree)) <
0) {
+ VIR_SHRINK_N(priv->nodeDeviceEventCallbacks,
+ priv->nnodeDeviceEventCallbacks, 1);
+ callback = ref;
+ goto cleanup;
+ }
+
+ ref->callbackID = callbackID;
+ ret->callbackID = callbackID;
+
+ rv = 0;
+
+ cleanup:
+ VIR_FREE(callback);
+ if (rv < 0)
+ virNetMessageSaveError(rerr);
+ virObjectUnref(dev);
+ virMutexUnlock(&priv->lock);
+ return rv;
+}
+
+static int
+remoteDispatchConnectNodeDeviceEventDeregisterAny(virNetServerPtr server
ATTRIBUTE_UNUSED,
+ virNetServerClientPtr client,
+ virNetMessagePtr msg ATTRIBUTE_UNUSED,
+ virNetMessageErrorPtr rerr
ATTRIBUTE_UNUSED,
+
remote_connect_node_device_event_deregister_any_args *args)
+{
+ int rv = -1;
+ size_t i;
+ struct daemonClientPrivate *priv =
+ virNetServerClientGetPrivateData(client);
+
+ if (!priv->conn) {
+ virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("connection not
open"));
+ goto cleanup;
+ }
+
+ virMutexLock(&priv->lock);
+
+ for (i = 0; i < priv->nnodeDeviceEventCallbacks; i++) {
+ if (priv->nodeDeviceEventCallbacks[i]->callbackID == args->callbackID)
+ break;
+ }
+ if (i == priv->nnodeDeviceEventCallbacks) {
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ _("node device event callback %d not registered"),
+ args->callbackID);
+ goto cleanup;
+ }
+
+ if (virConnectNodeDeviceEventDeregisterAny(priv->conn, args->callbackID) <
0)
+ goto cleanup;
+
+ VIR_DELETE_ELEMENT(priv->nodeDeviceEventCallbacks, i,
+ priv->nnodeDeviceEventCallbacks);
+
+ rv = 0;
+
+ cleanup:
+ if (rv < 0)
+ virNetMessageSaveError(rerr);
+ virMutexUnlock(&priv->lock);
+ return rv;
+}
static int
qemuDispatchConnectDomainMonitorEventRegister(virNetServerPtr server ATTRIBUTE_UNUSED,
@@ -6425,6 +6625,12 @@ get_nonnull_domain_snapshot(virDomainPtr dom,
remote_nonnull_domain_snapshot sna
return virGetDomainSnapshot(dom, snapshot.name);
}
+static virNodeDevicePtr
+get_nonnull_node_device(virConnectPtr conn, remote_nonnull_node_device dev)
+{
+ return virGetNodeDevice(conn, dev.name);
+}
+
/* Make remote_nonnull_domain and remote_nonnull_network. */
static void
make_nonnull_domain(remote_nonnull_domain *dom_dst, virDomainPtr dom_src)
diff --git a/src/remote/remote_driver.c b/src/remote/remote_driver.c
index 29552aa..6637a1d 100644
--- a/src/remote/remote_driver.c
+++ b/src/remote/remote_driver.c
@@ -35,6 +35,7 @@
#include "domain_event.h"
#include "network_event.h"
#include "storage_event.h"
+#include "node_device_event.h"
#include "driver.h"
#include "virbuffer.h"
#include "remote_driver.h"
@@ -151,6 +152,8 @@ static void make_nonnull_network(remote_nonnull_network *net_dst,
virNetworkPtr
static void make_nonnull_interface(remote_nonnull_interface *interface_dst,
virInterfacePtr interface_src);
static void make_nonnull_storage_pool(remote_nonnull_storage_pool *pool_dst,
virStoragePoolPtr vol_src);
static void make_nonnull_storage_vol(remote_nonnull_storage_vol *vol_dst,
virStorageVolPtr vol_src);
+static void
+make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src);
static void make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr
secret_src);
static void make_nonnull_nwfilter(remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr
nwfilter_src);
static void make_nonnull_domain_snapshot(remote_nonnull_domain_snapshot *snapshot_dst,
virDomainSnapshotPtr snapshot_src);
@@ -367,6 +370,11 @@ remoteStoragePoolBuildEventRefresh(virNetClientProgramPtr prog
ATTRIBUTE_UNUSED,
void *evdata, void *opaque);
static void
+remoteNodeDeviceBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+ virNetClientPtr client ATTRIBUTE_UNUSED,
+ void *evdata, void *opaque);
+
+static void
remoteConnectNotifyEventConnectionClosed(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
virNetClientPtr client ATTRIBUTE_UNUSED,
void *evdata, void *opaque);
@@ -553,6 +561,10 @@ static virNetClientProgramEvent remoteEvents[] = {
remoteStoragePoolBuildEventRefresh,
sizeof(remote_storage_pool_event_refresh_msg),
(xdrproc_t)xdr_remote_storage_pool_event_refresh_msg },
+ { REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE,
+ remoteNodeDeviceBuildEventLifecycle,
+ sizeof(remote_node_device_event_lifecycle_msg),
+ (xdrproc_t)xdr_remote_node_device_event_lifecycle_msg },
};
static void
@@ -3147,6 +3159,103 @@ remoteConnectStoragePoolEventDeregisterAny(virConnectPtr conn,
static int
+remoteConnectNodeDeviceEventRegisterAny(virConnectPtr conn,
+ virNodeDevicePtr dev,
+ int eventID,
+ virConnectNodeDeviceEventGenericCallback
callback,
+ void *opaque,
+ virFreeCallback freecb)
+{
+ int rv = -1;
+ struct private_data *priv = conn->privateData;
+ remote_connect_node_device_event_register_any_args args;
+ remote_connect_node_device_event_register_any_ret ret;
+ int callbackID;
+ int count;
+ remote_nonnull_node_device node_device;
+
+ remoteDriverLock(priv);
+
+ if ((count = virNodeDeviceEventStateRegisterClient(conn, priv->eventState,
+ dev, eventID, callback,
+ opaque, freecb,
+ &callbackID)) < 0)
+ goto done;
+
+ /* If this is the first callback for this eventID, we need to enable
+ * events on the server */
+ if (count == 1) {
+ args.eventID = eventID;
+ if (dev) {
+ make_nonnull_node_device(&node_device, dev);
+ args.dev = &node_device;
+ } else {
+ args.dev = NULL;
+ }
+
+ memset(&ret, 0, sizeof(ret));
+ if (call(conn, priv, 0, REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_REGISTER_ANY,
+ (xdrproc_t) xdr_remote_connect_node_device_event_register_any_args,
(char *) &args,
+ (xdrproc_t) xdr_remote_connect_node_device_event_register_any_ret, (char
*) &ret) == -1) {
+ virObjectEventStateDeregisterID(conn, priv->eventState,
+ callbackID);
+ goto done;
+ }
+
+ virObjectEventStateSetRemote(conn, priv->eventState, callbackID,
+ ret.callbackID);
+ }
+
+ rv = callbackID;
+
+ done:
+ remoteDriverUnlock(priv);
+ return rv;
+}
+
+
+static int
+remoteConnectNodeDeviceEventDeregisterAny(virConnectPtr conn,
+ int callbackID)
+{
+ struct private_data *priv = conn->privateData;
+ int rv = -1;
+ remote_connect_node_device_event_deregister_any_args args;
+ int eventID;
+ int remoteID;
+ int count;
+
+ remoteDriverLock(priv);
+
+ if ((eventID = virObjectEventStateEventID(conn, priv->eventState,
+ callbackID, &remoteID)) < 0)
+ goto done;
+
+ if ((count = virObjectEventStateDeregisterID(conn, priv->eventState,
+ callbackID)) < 0)
+ goto done;
+
+ /* If that was the last callback for this eventID, we need to disable
+ * events on the server */
+ if (count == 0) {
+ args.callbackID = remoteID;
+
+ if (call(conn, priv, 0, REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_DEREGISTER_ANY,
+ (xdrproc_t) xdr_remote_connect_node_device_event_deregister_any_args,
(char *) &args,
+ (xdrproc_t) xdr_void, (char *) NULL) == -1)
+ goto done;
+
+ }
+
+ rv = 0;
+
+ done:
+ remoteDriverUnlock(priv);
+ return rv;
+}
+
+
+static int
remoteConnectDomainQemuMonitorEventRegister(virConnectPtr conn,
virDomainPtr dom,
const char *event,
@@ -5155,6 +5264,28 @@ remoteStoragePoolBuildEventRefresh(virNetClientProgramPtr prog
ATTRIBUTE_UNUSED,
}
static void
+remoteNodeDeviceBuildEventLifecycle(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
+ virNetClientPtr client ATTRIBUTE_UNUSED,
+ void *evdata, void *opaque)
+{
+ virConnectPtr conn = opaque;
+ struct private_data *priv = conn->privateData;
+ remote_node_device_event_lifecycle_msg *msg = evdata;
+ virNodeDevicePtr dev;
+ virObjectEventPtr event = NULL;
+
+ dev = get_nonnull_node_device(conn, msg->dev);
+ if (!dev)
+ return;
+
+ event = virNodeDeviceEventLifecycleNew(dev->name, msg->event,
+ msg->detail);
+ virObjectUnref(dev);
+
+ remoteEventQueue(priv, event, msg->callbackID);
+}
+
+static void
remoteDomainBuildQemuMonitorEvent(virNetClientProgramPtr prog ATTRIBUTE_UNUSED,
virNetClientPtr client ATTRIBUTE_UNUSED,
void *evdata, void *opaque)
@@ -7753,6 +7884,12 @@ make_nonnull_secret(remote_nonnull_secret *secret_dst, virSecretPtr
secret_src)
}
static void
+make_nonnull_node_device(remote_nonnull_node_device *dev_dst, virNodeDevicePtr dev_src)
+{
+ dev_dst->name = dev_src->name;
+}
+
+static void
make_nonnull_nwfilter(remote_nonnull_nwfilter *nwfilter_dst, virNWFilterPtr
nwfilter_src)
{
nwfilter_dst->name = nwfilter_src->name;
@@ -8103,6 +8240,8 @@ static virSecretDriver secret_driver = {
};
static virNodeDeviceDriver node_device_driver = {
+ .connectNodeDeviceEventDeregisterAny = remoteConnectNodeDeviceEventDeregisterAny, /*
2.2.0 */
+ .connectNodeDeviceEventRegisterAny = remoteConnectNodeDeviceEventRegisterAny, /*
2.2.0 */
.nodeNumOfDevices = remoteNodeNumOfDevices, /* 0.5.0 */
.nodeListDevices = remoteNodeListDevices, /* 0.5.0 */
.connectListAllNodeDevices = remoteConnectListAllNodeDevices, /* 0.10.2 */
diff --git a/src/remote/remote_protocol.x b/src/remote/remote_protocol.x
index 4403714..b4fd057 100644
--- a/src/remote/remote_protocol.x
+++ b/src/remote/remote_protocol.x
@@ -3131,6 +3131,26 @@ struct remote_storage_pool_event_refresh_msg {
remote_nonnull_storage_pool pool;
};
+struct remote_connect_node_device_event_register_any_args {
+ int eventID;
+ remote_node_device dev;
+};
+
+struct remote_connect_node_device_event_register_any_ret {
+ int callbackID;
+};
+
+struct remote_connect_node_device_event_deregister_any_args {
+ int callbackID;
+};
+
+struct remote_node_device_event_lifecycle_msg {
+ int callbackID;
+ remote_nonnull_node_device dev;
+ int event;
+ int detail;
+};
+
struct remote_domain_fsfreeze_args {
remote_nonnull_domain dom;
remote_nonnull_string mountpoints<REMOTE_DOMAIN_FSFREEZE_MOUNTPOINTS_MAX>; /*
(const char **) */
@@ -5882,5 +5902,26 @@ enum remote_procedure {
* @generate: both
* @acl: none
*/
- REMOTE_PROC_STORAGE_POOL_EVENT_REFRESH = 373
+ REMOTE_PROC_STORAGE_POOL_EVENT_REFRESH = 373,
+
+ /**
+ * @generate: none
+ * @priority: high
+ * @acl: connect:search_node_devices
+ * @aclfilter: node_device:getattr
+ */
+ REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_REGISTER_ANY = 374,
+
+ /**
+ * @generate: none
+ * @priority: high
+ * @acl: connect:read
+ */
+ REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_DEREGISTER_ANY = 375,
+
+ /**
+ * @generate: both
+ * @acl: none
+ */
+ REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE = 376
};
diff --git a/src/remote_protocol-structs b/src/remote_protocol-structs
index 46e798b..0e66fc5 100644
--- a/src/remote_protocol-structs
+++ b/src/remote_protocol-structs
@@ -2571,6 +2571,22 @@ struct remote_storage_pool_event_refresh_msg {
int callbackID;
remote_nonnull_storage_pool pool;
};
+struct remote_connect_node_device_event_register_any_args {
+ int eventID;
+ remote_node_device dev;
+};
+struct remote_connect_node_device_event_register_any_ret {
+ int callbackID;
+};
+struct remote_connect_node_device_event_deregister_any_args {
+ int callbackID;
+};
+struct remote_node_device_event_lifecycle_msg {
+ int callbackID;
+ remote_nonnull_node_device dev;
+ int event;
+ int detail;
+};
struct remote_domain_fsfreeze_args {
remote_nonnull_domain dom;
struct {
@@ -3145,4 +3161,7 @@ enum remote_procedure {
REMOTE_PROC_DOMAIN_GET_GUEST_VCPUS = 371,
REMOTE_PROC_DOMAIN_SET_GUEST_VCPUS = 372,
REMOTE_PROC_STORAGE_POOL_EVENT_REFRESH = 373,
+ REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_REGISTER_ANY = 374,
+ REMOTE_PROC_CONNECT_NODE_DEVICE_EVENT_DEREGISTER_ANY = 375,
+ REMOTE_PROC_NODE_DEVICE_EVENT_LIFECYCLE = 376,
};
--
2.7.4