mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 05:35:25 +00:00
admin: Add support for connection close callbacks
As we need a client disconnect handler, we also need a mechanism to register such handlers for a client. This patch introduced both the close callbacks and also the client vshAdmCatchDisconnect handler to be registered with it. By registering the handler we still need to make sure the client can react to daemon's events like disconnect or keepalive, so asynchronous I/O event polling is necessary to be enabled too.
This commit is contained in:
parent
96a96b8433
commit
6dd7e42d89
@ -61,6 +61,25 @@ int virAdmGetVersion(unsigned long long *libVer);
|
||||
|
||||
char *virAdmConnectGetURI(virAdmConnectPtr conn);
|
||||
|
||||
/**
|
||||
* virAdmConnectCloseFunc:
|
||||
* @conn: virAdmConnect connection
|
||||
* @reason: reason why the connection was closed (see virConnectCloseReason)
|
||||
* @opaque: opaque client data
|
||||
*
|
||||
* A callback to be registered, in case a connection was closed.
|
||||
*/
|
||||
typedef void (*virAdmConnectCloseFunc)(virAdmConnectPtr conn,
|
||||
int reason,
|
||||
void *opaque);
|
||||
|
||||
int virAdmConnectRegisterCloseCallback(virAdmConnectPtr conn,
|
||||
virAdmConnectCloseFunc cb,
|
||||
void *opaque,
|
||||
virFreeCallback freecb);
|
||||
int virAdmConnectUnregisterCloseCallback(virAdmConnectPtr conn,
|
||||
virAdmConnectCloseFunc cb);
|
||||
|
||||
# ifdef __cplusplus
|
||||
}
|
||||
# endif
|
||||
|
@ -101,6 +101,28 @@ call(virAdmConnectPtr conn,
|
||||
|
||||
#include "admin_client.h"
|
||||
|
||||
static void
|
||||
remoteAdminClientCloseFunc(virNetClientPtr client ATTRIBUTE_UNUSED,
|
||||
int reason,
|
||||
void *opaque)
|
||||
{
|
||||
virAdmConnectCloseCallbackDataPtr cbdata = opaque;
|
||||
|
||||
virObjectLock(cbdata);
|
||||
|
||||
if (cbdata->callback) {
|
||||
VIR_DEBUG("Triggering connection close callback %p reason=%d, opaque=%p",
|
||||
cbdata->callback, reason, cbdata->opaque);
|
||||
cbdata->callback(cbdata->conn, reason, cbdata->opaque);
|
||||
|
||||
if (cbdata->freeCallback)
|
||||
cbdata->freeCallback(cbdata->opaque);
|
||||
cbdata->callback = NULL;
|
||||
cbdata->freeCallback = NULL;
|
||||
}
|
||||
virObjectUnlock(cbdata);
|
||||
}
|
||||
|
||||
static int
|
||||
remoteAdminConnectOpen(virAdmConnectPtr conn, unsigned int flags)
|
||||
{
|
||||
@ -112,6 +134,17 @@ remoteAdminConnectOpen(virAdmConnectPtr conn, unsigned int flags)
|
||||
|
||||
args.flags = flags;
|
||||
|
||||
if (virNetClientRegisterAsyncIO(priv->client) < 0) {
|
||||
VIR_DEBUG("Failed to add event watch, disabling events and support for"
|
||||
" keepalive messages");
|
||||
virResetLastError();
|
||||
}
|
||||
|
||||
virObjectRef(conn->closeCallback);
|
||||
virNetClientSetCloseCallback(priv->client, remoteAdminClientCloseFunc,
|
||||
conn->closeCallback,
|
||||
virObjectFreeCallback);
|
||||
|
||||
if (call(conn, 0, ADMIN_PROC_CONNECT_OPEN,
|
||||
(xdrproc_t)xdr_admin_connect_open_args, (char *)&args,
|
||||
(xdrproc_t)xdr_void, (char *)NULL) == -1) {
|
||||
@ -139,6 +172,8 @@ remoteAdminConnectClose(virAdmConnectPtr conn)
|
||||
goto done;
|
||||
}
|
||||
|
||||
virNetClientSetCloseCallback(priv->client, NULL, NULL, NULL);
|
||||
|
||||
rv = 0;
|
||||
|
||||
done:
|
||||
|
@ -60,8 +60,10 @@ static void virStorageVolDispose(void *obj);
|
||||
static void virStoragePoolDispose(void *obj);
|
||||
|
||||
virClassPtr virAdmConnectClass;
|
||||
virClassPtr virAdmConnectCloseCallbackDataClass;
|
||||
|
||||
static void virAdmConnectDispose(void *obj);
|
||||
static void virAdmConnectCloseCallbackDataDispose(void *obj);
|
||||
|
||||
static int
|
||||
virDataTypesOnceInit(void)
|
||||
@ -91,6 +93,7 @@ virDataTypesOnceInit(void)
|
||||
DECLARE_CLASS(virStoragePool);
|
||||
|
||||
DECLARE_CLASS_LOCKABLE(virAdmConnect);
|
||||
DECLARE_CLASS_LOCKABLE(virAdmConnectCloseCallbackData);
|
||||
|
||||
#undef DECLARE_CLASS_COMMON
|
||||
#undef DECLARE_CLASS_LOCKABLE
|
||||
@ -822,7 +825,14 @@ virAdmConnectNew(void)
|
||||
if (!(ret = virObjectLockableNew(virAdmConnectClass)))
|
||||
return NULL;
|
||||
|
||||
if (!(ret->closeCallback = virObjectLockableNew(virAdmConnectCloseCallbackDataClass)))
|
||||
goto error;
|
||||
|
||||
return ret;
|
||||
|
||||
error:
|
||||
virObjectUnref(ret);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -834,4 +844,18 @@ virAdmConnectDispose(void *obj)
|
||||
conn->privateDataFreeFunc(conn);
|
||||
|
||||
virURIFree(conn->uri);
|
||||
virObjectUnref(conn->closeCallback);
|
||||
}
|
||||
|
||||
static void
|
||||
virAdmConnectCloseCallbackDataDispose(void *obj)
|
||||
{
|
||||
virAdmConnectCloseCallbackDataPtr cb_data = obj;
|
||||
|
||||
virObjectLock(cb_data);
|
||||
|
||||
if (cb_data->freeCallback)
|
||||
cb_data->freeCallback(cb_data->opaque);
|
||||
|
||||
virObjectUnlock(cb_data);
|
||||
}
|
||||
|
@ -331,9 +331,11 @@ extern virClassPtr virAdmConnectClass;
|
||||
|
||||
typedef struct _virConnectCloseCallbackData virConnectCloseCallbackData;
|
||||
typedef virConnectCloseCallbackData *virConnectCloseCallbackDataPtr;
|
||||
typedef struct _virAdmConnectCloseCallbackData virAdmConnectCloseCallbackData;
|
||||
typedef virAdmConnectCloseCallbackData *virAdmConnectCloseCallbackDataPtr;
|
||||
|
||||
/**
|
||||
* Internal structure holding data related to connection close callbacks.
|
||||
* Internal structures holding data related to connection close callbacks.
|
||||
*/
|
||||
struct _virConnectCloseCallbackData {
|
||||
virObjectLockable parent;
|
||||
@ -344,6 +346,15 @@ struct _virConnectCloseCallbackData {
|
||||
virFreeCallback freeCallback;
|
||||
};
|
||||
|
||||
struct _virAdmConnectCloseCallbackData {
|
||||
virObjectLockable parent;
|
||||
|
||||
virAdmConnectPtr conn;
|
||||
virAdmConnectCloseFunc callback;
|
||||
void *opaque;
|
||||
virFreeCallback freeCallback;
|
||||
};
|
||||
|
||||
/**
|
||||
* _virConnect:
|
||||
*
|
||||
@ -401,6 +412,9 @@ struct _virAdmConnect {
|
||||
|
||||
void *privateData;
|
||||
virFreeCallback privateDataFreeFunc;
|
||||
|
||||
/* Per-connection close callback */
|
||||
virAdmConnectCloseCallbackDataPtr closeCallback;
|
||||
};
|
||||
|
||||
|
||||
|
@ -411,3 +411,115 @@ virAdmConnectGetURI(virAdmConnectPtr conn)
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
/**
|
||||
* virAdmConnectRegisterCloseCallback:
|
||||
* @conn: connection to admin server
|
||||
* @cb: callback to be invoked upon connection 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.
|
||||
*
|
||||
* 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 virAdmConnectRegisterCloseCallback(virAdmConnectPtr conn,
|
||||
virAdmConnectCloseFunc cb,
|
||||
void *opaque,
|
||||
virFreeCallback freecb)
|
||||
{
|
||||
VIR_DEBUG("conn=%p", conn);
|
||||
|
||||
virResetLastError();
|
||||
|
||||
virCheckAdmConnectReturn(conn, -1);
|
||||
|
||||
virObjectRef(conn);
|
||||
|
||||
virObjectLock(conn);
|
||||
virObjectLock(conn->closeCallback);
|
||||
|
||||
virCheckNonNullArgGoto(cb, error);
|
||||
|
||||
if (conn->closeCallback->callback) {
|
||||
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
||||
_("A close callback is already registered"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
conn->closeCallback->conn = conn;
|
||||
conn->closeCallback->callback = cb;
|
||||
conn->closeCallback->opaque = opaque;
|
||||
conn->closeCallback->freeCallback = freecb;
|
||||
|
||||
virObjectUnlock(conn->closeCallback);
|
||||
virObjectUnlock(conn);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
virObjectUnlock(conn->closeCallback);
|
||||
virObjectUnlock(conn);
|
||||
virDispatchError(NULL);
|
||||
virObjectUnref(conn);
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* virAdmConnectUnregisterCloseCallback:
|
||||
* @conn: pointer to connection object
|
||||
* @cb: pointer to the current registered callback
|
||||
*
|
||||
* Unregisters the callback previously set with the
|
||||
* virAdmConnectRegisterCloseCallback 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 virAdmConnectUnregisterCloseCallback(virAdmConnectPtr conn,
|
||||
virAdmConnectCloseFunc cb)
|
||||
{
|
||||
VIR_DEBUG("conn=%p", conn);
|
||||
|
||||
virResetLastError();
|
||||
|
||||
virCheckAdmConnectReturn(conn, -1);
|
||||
|
||||
virObjectLock(conn);
|
||||
virObjectLock(conn->closeCallback);
|
||||
|
||||
virCheckNonNullArgGoto(cb, error);
|
||||
|
||||
if (conn->closeCallback->callback != cb) {
|
||||
virReportError(VIR_ERR_OPERATION_INVALID, "%s",
|
||||
_("A different callback was requested"));
|
||||
goto error;
|
||||
}
|
||||
|
||||
conn->closeCallback->callback = NULL;
|
||||
if (conn->closeCallback->freeCallback)
|
||||
conn->closeCallback->freeCallback(conn->closeCallback->opaque);
|
||||
conn->closeCallback->freeCallback = NULL;
|
||||
|
||||
virObjectUnlock(conn->closeCallback);
|
||||
virObjectUnlock(conn);
|
||||
virObjectUnref(conn);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
virObjectUnlock(conn->closeCallback);
|
||||
virObjectUnlock(conn);
|
||||
virDispatchError(NULL);
|
||||
return -1;
|
||||
}
|
||||
|
@ -18,4 +18,6 @@ LIBVIRT_ADMIN_1.3.0 {
|
||||
virAdmGetVersion;
|
||||
virAdmConnectIsAlive;
|
||||
virAdmConnectGetURI;
|
||||
virAdmConnectRegisterCloseCallback;
|
||||
virAdmConnectUnregisterCloseCallback;
|
||||
};
|
||||
|
@ -52,6 +52,53 @@ static char *progname;
|
||||
static const vshCmdGrp cmdGroups[];
|
||||
static const vshClientHooks hooks;
|
||||
|
||||
/*
|
||||
* vshAdmCatchDisconnect:
|
||||
*
|
||||
* We get here when the connection was closed. Unlike virsh, we do not save
|
||||
* the fact that the event was raised, sice there is virAdmConnectIsAlive to
|
||||
* check if the communication channel has not been closed by remote party.
|
||||
*/
|
||||
static void
|
||||
vshAdmCatchDisconnect(virAdmConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
int reason,
|
||||
void *opaque)
|
||||
{
|
||||
vshControl *ctl = opaque;
|
||||
const char *str = "unknown reason";
|
||||
virErrorPtr error;
|
||||
char *uri = NULL;
|
||||
|
||||
if (reason == VIR_CONNECT_CLOSE_REASON_CLIENT)
|
||||
return;
|
||||
|
||||
error = virSaveLastError();
|
||||
uri = virAdmConnectGetURI(conn);
|
||||
|
||||
switch ((virConnectCloseReason) reason) {
|
||||
case VIR_CONNECT_CLOSE_REASON_ERROR:
|
||||
str = N_("Disconnected from %s due to I/O error");
|
||||
break;
|
||||
case VIR_CONNECT_CLOSE_REASON_EOF:
|
||||
str = N_("Disconnected from %s due to end of file");
|
||||
break;
|
||||
case VIR_CONNECT_CLOSE_REASON_KEEPALIVE:
|
||||
str = N_("Disconnected from %s due to keepalive timeout");
|
||||
break;
|
||||
/* coverity[dead_error_condition] */
|
||||
case VIR_CONNECT_CLOSE_REASON_CLIENT:
|
||||
case VIR_CONNECT_CLOSE_REASON_LAST:
|
||||
break;
|
||||
}
|
||||
|
||||
vshError(ctl, _(str), NULLSTR(uri));
|
||||
|
||||
if (error) {
|
||||
virSetError(error);
|
||||
virFreeError(error);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
vshAdmConnect(vshControl *ctl, unsigned int flags)
|
||||
{
|
||||
@ -66,6 +113,10 @@ vshAdmConnect(vshControl *ctl, unsigned int flags)
|
||||
vshError(ctl, "%s", _("Failed to connect to the admin server"));
|
||||
return -1;
|
||||
} else {
|
||||
if (virAdmConnectRegisterCloseCallback(priv->conn, vshAdmCatchDisconnect,
|
||||
NULL, NULL) < 0)
|
||||
vshError(ctl, "%s", _("Unable to register disconnect callback"));
|
||||
|
||||
if (priv->wantReconnect)
|
||||
vshPrint(ctl, "%s\n", _("Reconnected to the admin server"));
|
||||
else
|
||||
@ -84,6 +135,7 @@ vshAdmDisconnect(vshControl *ctl)
|
||||
if (!priv->conn)
|
||||
return ret;
|
||||
|
||||
virAdmConnectUnregisterCloseCallback(priv->conn, vshAdmCatchDisconnect);
|
||||
ret = virAdmConnectClose(priv->conn);
|
||||
if (ret < 0)
|
||||
vshError(ctl, "%s", _("Failed to disconnect from the admin server"));
|
||||
|
Loading…
Reference in New Issue
Block a user