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
#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)
{
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) {
goto done;
}
+ virNetClientSetCloseCallback(priv->client, NULL, NULL, NULL);
+
rv = 0;
done:
static void virStoragePoolDispose(void *obj);
virClassPtr virAdmConnectClass;
+virClassPtr virAdmConnectCloseCallbackDataClass;
static void virAdmConnectDispose(void *obj);
+static void virAdmConnectCloseCallbackDataDispose(void *obj);
static int
virDataTypesOnceInit(void)
DECLARE_CLASS(virStoragePool);
DECLARE_CLASS_LOCKABLE(virAdmConnect);
+ DECLARE_CLASS_LOCKABLE(virAdmConnectCloseCallbackData);
#undef DECLARE_CLASS_COMMON
#undef DECLARE_CLASS_LOCKABLE
if (!(ret = virObjectLockableNew(virAdmConnectClass)))
return NULL;
+ if (!(ret->closeCallback = virObjectLockableNew(virAdmConnectCloseCallbackDataClass)))
+ goto error;
+
return ret;
+
+ error:
+ virObjectUnref(ret);
+ return NULL;
}
static void
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);
}
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;
virFreeCallback freeCallback;
};
+struct _virAdmConnectCloseCallbackData {
+ virObjectLockable parent;
+
+ virAdmConnectPtr conn;
+ virAdmConnectCloseFunc callback;
+ void *opaque;
+ virFreeCallback freeCallback;
+};
+
/**
* _virConnect:
*
void *privateData;
virFreeCallback privateDataFreeFunc;
+
+ /* Per-connection close callback */
+ virAdmConnectCloseCallbackDataPtr closeCallback;
};
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;
+}
virAdmGetVersion;
virAdmConnectIsAlive;
virAdmConnectGetURI;
+ virAdmConnectRegisterCloseCallback;
+ virAdmConnectUnregisterCloseCallback;
};
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)
{
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
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"));