[libvirt] [PATCH 4/9] admin: Add support for connection close callbacks

Michal Privoznik mprivozn at redhat.com
Wed Oct 14 11:25:27 UTC 2015


On 13.10.2015 15:38, Erik Skultety wrote:
> As we need a client disconnect handler, we need a mechanism to register
> such handlers for a client.
> ---
>  include/libvirt/libvirt-admin.h |  19 ++++++
>  src/datatypes.c                 |  22 +++++++
>  src/datatypes.h                 |  14 +++-
>  src/libvirt-admin.c             | 142 ++++++++++++++++++++++++++++++++++++++++
>  src/libvirt_admin_public.syms   |   2 +
>  tools/virt-admin.c              |   5 ++
>  6 files changed, 203 insertions(+), 1 deletion(-)
> 
> diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h
> index 72671c6..4539ac6 100644
> --- a/include/libvirt/libvirt-admin.h
> +++ b/include/libvirt/libvirt-admin.h
> @@ -54,6 +54,25 @@ int virAdmConnectClose(virAdmConnectPtr conn);
>  int virAdmConnectRef(virAdmConnectPtr conn);
>  int virAdmConnectIsAlive(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
> diff --git a/src/datatypes.c b/src/datatypes.c
> index 12bcfc1..9e374e4 100644
> --- a/src/datatypes.c
> +++ b/src/datatypes.c
> @@ -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,6 +825,9 @@ virAdmConnectNew(void)
>      if (!(ret = virObjectLockableNew(virAdmConnectClass)))
>          return NULL;
>  
> +    if (!(ret->closeCallback = virObjectLockableNew(virAdmConnectCloseCallbackDataClass)))
> +        return NULL;
> +
>      return ret;
>  }
>  
> @@ -832,4 +838,20 @@ virAdmConnectDispose(void *obj)
>  
>      if (conn->privateDataFreeFunc)
>          conn->privateDataFreeFunc(conn);
> +
> +    virObjectUnref(conn->closeCallback);
> +}
> +
> +static void
> +virAdmConnectCloseCallbackDataDispose(void *obj)
> +{
> +    virAdmConnectCloseCallbackDataPtr cb_data = obj;
> +
> +    virObjectLock(cb_data);
> +
> +    cb_data->callback = NULL;

This ^^ shouldn't be needed. We are the last ones to see cb_data alive.

> +    if (cb_data->freeCallback)
> +        cb_data->freeCallback(cb_data->opaque);
> +
> +    virObjectUnlock(cb_data);
>  }
> diff --git a/src/datatypes.h b/src/datatypes.h
> index be108fe..33df476 100644
> --- a/src/datatypes.h
> +++ b/src/datatypes.h
> @@ -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:
>   *
> @@ -400,6 +411,7 @@ struct _virAdmConnect {
>  
>      void *privateData;
>      virFreeCallback privateDataFreeFunc;
> +    virAdmConnectCloseCallbackDataPtr closeCallback;
>  };
>  
>  
> diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c
> index e12e2b8..9ad124e 100644
> --- a/src/libvirt-admin.c
> +++ b/src/libvirt-admin.c
> @@ -64,9 +64,31 @@ remoteAdminPrivDispose(void *opaque)
>      remoteAdminPrivPtr priv = opaque;
>  
>      virObjectUnref(priv->program);
> +    virNetClientClose(priv->client);
>      virObjectUnref(priv->client);
>  }
>  
> +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);
> +}
>  

Again, remoteAdmin** funcs should go into a separate file.

>  static int
>  callFull(virAdmConnectPtr conn ATTRIBUTE_UNUSED,
> @@ -137,6 +159,11 @@ remoteAdminConnectOpen(virAdmConnectPtr conn, unsigned int flags)
>  
>      args.flags = flags;
>  
> +    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) {
> @@ -164,6 +191,9 @@ remoteAdminConnectClose(virAdmConnectPtr conn)
>          goto done;
>      }
>  
> +    virNetClientSetCloseCallback(priv->client, NULL, conn->closeCallback,
> +                                 virObjectFreeCallback);
> +
>      rv = 0;
>  
>   done:
> @@ -462,3 +492,115 @@ virAdmConnectIsAlive(virAdmConnectPtr conn)
>      else
>          return 0;
>  }
> +
> +/**
> + * 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;
> +}
> diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms
> index 16cfd42..5b9ba51 100644
> --- a/src/libvirt_admin_public.syms
> +++ b/src/libvirt_admin_public.syms
> @@ -16,4 +16,6 @@ LIBVIRT_ADMIN_1.3.0 {
>          virAdmConnectClose;
>          virAdmConnectRef;
>          virAdmConnectIsAlive;
> +        virAdmConnectRegisterCloseCallback;
> +        virAdmConnectUnregisterCloseCallback;
>  };
> diff --git a/tools/virt-admin.c b/tools/virt-admin.c
> index 4964e3d..679ae99 100644
> --- a/tools/virt-admin.c
> +++ b/tools/virt-admin.c
> @@ -91,6 +91,10 @@ vshAdmConnect(vshControl *ctl, unsigned int flags)
>              vshError(ctl, "%s", _("Failed to connect to the admin server"));
>          return NULL;
>      } else {
> +        if (virAdmConnectRegisterCloseCallback(priv->conn, NULL, NULL,
> +                                               NULL) < 0)
> +            vshError(ctl, "%s", _("Unable to register disconnect callback"));
> +

This can never succeed. virADmConnectRegisterCloseCallback checks for
NULL cb. So after this you will always see the error.

>          if (priv->wantReconnect)
>              vshPrint(ctl, "%s\n", _("Reconnected to the admin server"));
>          else
> @@ -108,6 +112,7 @@ vshAdmDisconnectInternal(vshControl *ctl, virAdmConnectPtr *conn)
>      if (!*conn)
>          return ret;
>  
> +    virAdmConnectUnregisterCloseCallback(*conn, NULL);
>      ret = virAdmConnectClose(*conn);
>      if (ret < 0)
>          vshError(ctl, "%s", _("Failed to disconnect from the admin server"));
> 

Otherwise looking good.

Michal




More information about the libvir-list mailing list