From 6ed5a1b9bd6240b8f2736790e48dd1c284c2e0e1 Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Thu, 19 Jul 2012 11:01:07 +0100 Subject: [PATCH] Add public API to register a callback to be invoked on connection close Define new virConnect{Register,Unregister}CloseCallback() public APIs which allows registering/unregistering a callback to be invoked when the connection to a hypervisor is closed. The callback is provided with the reason for the close, which may be 'error', 'eof', 'client' or 'keepalive'. Signed-off-by: Daniel P. Berrange --- include/libvirt/libvirt.h.in | 49 ++++++++++++--- src/datatypes.c | 3 + src/datatypes.h | 5 ++ src/libvirt.c | 114 +++++++++++++++++++++++++++++++++++ src/libvirt_public.syms | 2 + 5 files changed, 163 insertions(+), 10 deletions(-) diff --git a/include/libvirt/libvirt.h.in b/include/libvirt/libvirt.h.in index 71e41e8169..d21d029bf2 100644 --- a/include/libvirt/libvirt.h.in +++ b/include/libvirt/libvirt.h.in @@ -61,6 +61,24 @@ extern "C" { * defines VIR_ENUM_SENTINELS. Enumerations for bit values do not * have a *_LAST value, but additional bits may be defined. */ +/* + * virFreeCallback: + * @opaque: opaque user data provided at registration + * + * Type for a callback cleanup function to be paired with a callback. This + * function will be called as a final chance to clean up the @opaque + * registered with the primary callback, at the time when the primary + * callback is deregistered. + * + * It is forbidden to call any other libvirt APIs from an + * implementation of this callback, since it can be invoked + * from a context which is not re-entrant safe. Failure to + * abide by this requirement may lead to application deadlocks + * or crashes. + */ +typedef void (*virFreeCallback)(void *opaque); + + /** * virConnect: * @@ -1160,6 +1178,27 @@ int virConnectSetKeepAlive(virConnectPtr conn, int interval, unsigned int count); +typedef enum { + VIR_CONNECT_CLOSE_REASON_ERROR = 0, /* Misc I/O error */ + VIR_CONNECT_CLOSE_REASON_EOF = 1, /* End-of-file from server */ + VIR_CONNECT_CLOSE_REASON_KEEPALIVE = 2, /* Keepalive timer triggered */ + VIR_CONNECT_CLOSE_REASON_CLIENT = 3, /* Client requested it */ + +# ifdef VIR_ENUM_SENTINELS + VIR_CONNECT_CLOSE_REASON_LAST +# endif +} virConnectCloseReason; + +typedef void (*virConnectCloseFunc)(virConnectPtr conn, + int reason, + void *opaque); + +int virConnectRegisterCloseCallback(virConnectPtr conn, + virConnectCloseFunc cb, + void *opaque, + virFreeCallback freecb); +int virConnectUnregisterCloseCallback(virConnectPtr conn, + virConnectCloseFunc cb); /* * Capabilities of the connection / driver. @@ -2875,16 +2914,6 @@ typedef int (*virConnectDomainEventCallback)(virConnectPtr conn, int detail, void *opaque); -/* - * virFreeCallback: - * @opaque: opaque user data provided at registration - * - * Type for a domain event callback when the event is deregistered and - * need to be freed, @opaque is provided along with the callback at - * registration time - */ -typedef void (*virFreeCallback)(void *opaque); - int virConnectDomainEventRegister(virConnectPtr conn, virConnectDomainEventCallback cb, void *opaque, diff --git a/src/datatypes.c b/src/datatypes.c index 77dca6f5b1..f1f6e611e5 100644 --- a/src/datatypes.c +++ b/src/datatypes.c @@ -115,6 +115,9 @@ virReleaseConnect(virConnectPtr conn) { virMutexLock(&conn->lock); + if (conn->closeFreeCallback) + conn->closeFreeCallback(conn->closeOpaque); + virResetError(&conn->err); virURIFree(conn->uri); diff --git a/src/datatypes.h b/src/datatypes.h index 9ad2d01728..8ac9171e9f 100644 --- a/src/datatypes.h +++ b/src/datatypes.h @@ -187,6 +187,11 @@ struct _virConnect { virErrorFunc handler; /* associated handlet */ void *userData; /* the user data */ + /* Per-connection close callback */ + virConnectCloseFunc closeCallback; + void *closeOpaque; + virFreeCallback closeFreeCallback; + int refs; /* reference count */ }; diff --git a/src/libvirt.c b/src/libvirt.c index 93c10942d3..e7bab13f04 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -18624,6 +18624,120 @@ error: } +/** + * virConnectRegisterCloseCallback: + * @conn: pointer to connection object + * @cb: callback to invoke upon close + * @opaque: user data to pass to @cb + * @freecb: callback to free @opaque + * + * Registers a callback to be invoked when the connection + * is closed. This callback is invoked when there is any + * condition that causes the socket connection to the + * hypervisor to be closed. + * + * This function is only applicable to hypervisor drivers + * which maintain a persistent open connection. Drivers + * which open a new connection for every operation will + * not invoke this. + * + * The @freecb must not invoke any other libvirt public + * APIs, since it is not called from a re-entrant safe + * context. + * + * Returns 0 on success, -1 on error + */ +int virConnectRegisterCloseCallback(virConnectPtr conn, + virConnectCloseFunc cb, + void *opaque, + virFreeCallback freecb) +{ + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + virMutexLock(&conn->lock); + + virCheckNonNullArgGoto(cb, error); + + if (conn->closeCallback) { + virLibConnError(VIR_ERR_OPERATION_INVALID, "%s", + _("A close callback is already registered")); + goto error; + } + + conn->closeCallback = cb; + conn->closeOpaque = opaque; + conn->closeFreeCallback = freecb; + + virMutexUnlock(&conn->lock); + + return 0; + +error: + virMutexUnlock(&conn->lock); + virDispatchError(NULL); + return -1; +} + +/** + * virConnectUnregisterCloseCallback: + * @conn: pointer to connection object + * @cb: pointer to the current registered callback + * + * Unregisters the callback previously set with the + * virConnectRegisterCloseCallback method. The callback + * will no longer receive notifications when the connection + * closes. If a virFreeCallback was provided at time of + * registration, it will be invoked + * + * Returns 0 on success, -1 on error + */ +int virConnectUnregisterCloseCallback(virConnectPtr conn, + virConnectCloseFunc cb) +{ + VIR_DEBUG("conn=%p", conn); + + virResetLastError(); + + if (!VIR_IS_CONNECT(conn)) { + virLibConnError(VIR_ERR_INVALID_CONN, __FUNCTION__); + virDispatchError(NULL); + return -1; + } + + virMutexLock(&conn->lock); + + virCheckNonNullArgGoto(cb, error); + + if (conn->closeCallback != cb) { + virLibConnError(VIR_ERR_OPERATION_INVALID, "%s", + _("A different callback was requested")); + goto error; + } + + conn->closeCallback = NULL; + if (conn->closeFreeCallback) + conn->closeFreeCallback(conn->closeOpaque); + conn->closeFreeCallback = NULL; + conn->closeOpaque = NULL; + + virMutexUnlock(&conn->lock); + + return 0; + +error: + virMutexUnlock(&conn->lock); + virDispatchError(NULL); + return -1; +} + /** * virDomainSetBlockIoTune: * @dom: pointer to domain object diff --git a/src/libvirt_public.syms b/src/libvirt_public.syms index 1a8e58aed2..5004182763 100644 --- a/src/libvirt_public.syms +++ b/src/libvirt_public.syms @@ -547,6 +547,8 @@ LIBVIRT_0.9.13 { LIBVIRT_0.9.14 { global: virDomainGetHostname; + virConnectRegisterCloseCallback; + virConnectUnregisterCloseCallback; } LIBVIRT_0.9.13; # .... define new API here using predicted next version number ....