[libvirt] [PATCH 0/3] Admin API: Add support for client disconnect

This series adds a simple support for a client force disconnect. As with the series about client identity retrieval, this series is rebased on listing clients due to some gendispatch stuff needed in all of them. To test it, you checkout my remote branch https://github.com/eskultety/libvirt/tree/list-clients-info-disconnect Erik Skultety (3): admin: Introduce virAdmServerLookupClient admin: Introduce virAdmClientClose API virt-admin: Introduce client-disconnect command daemon/admin.c | 6 ++++ daemon/admin_server.c | 19 +++++++++++ daemon/admin_server.h | 7 ++++ include/libvirt/libvirt-admin.h | 7 ++++ include/libvirt/virterror.h | 1 + src/admin/admin_protocol.x | 27 +++++++++++++++- src/admin/admin_remote.c | 10 ++++++ src/admin_protocol-structs | 14 ++++++++ src/libvirt-admin.c | 66 +++++++++++++++++++++++++++++++++++++ src/libvirt_admin_private.syms | 3 ++ src/libvirt_admin_public.syms | 2 ++ src/rpc/virnetserver.c | 23 +++++++++++++ src/rpc/virnetserver.h | 3 ++ src/util/virerror.c | 6 ++++ tools/virt-admin.c | 72 +++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.pod | 16 +++++++++ 16 files changed, 281 insertions(+), 1 deletion(-) -- 2.4.11

Just like with server-related APIs, before any of client-based APIs can be called, a reference to a client-side client object needs to be obtained. For this purpose, a lookup method should exist. Apart from the client retrieval logic, a new error code for non-existent client had to be added as well. Signed-off-by: Erik Skultety <eskultet@redhat.com> --- daemon/admin_server.c | 10 ++++++++++ daemon/admin_server.h | 4 ++++ include/libvirt/libvirt-admin.h | 5 +++++ include/libvirt/virterror.h | 1 + src/admin/admin_protocol.x | 17 ++++++++++++++++- src/admin_protocol-structs | 9 +++++++++ src/libvirt-admin.c | 36 ++++++++++++++++++++++++++++++++++++ src/libvirt_admin_private.syms | 2 ++ src/libvirt_admin_public.syms | 1 + src/rpc/virnetserver.c | 23 +++++++++++++++++++++++ src/rpc/virnetserver.h | 3 +++ src/util/virerror.c | 6 ++++++ 12 files changed, 116 insertions(+), 1 deletion(-) diff --git a/daemon/admin_server.c b/daemon/admin_server.c index 0bd142c..9ea1164 100644 --- a/daemon/admin_server.c +++ b/daemon/admin_server.c @@ -201,3 +201,13 @@ adminServerListClients(virNetServerPtr srv, virObjectListFreeCount(clts, ret); return ret; } + +virNetServerClientPtr +adminServerLookupClient(virNetServerPtr srv, + unsigned long long id, + unsigned int flags) +{ + virCheckFlags(0, NULL); + + return virNetServerGetClient(srv, id); +} diff --git a/daemon/admin_server.h b/daemon/admin_server.h index 3e4786e..a593251 100644 --- a/daemon/admin_server.h +++ b/daemon/admin_server.h @@ -50,4 +50,8 @@ int adminServerListClients(virNetServerPtr srv, virNetServerClientPtr **clients, unsigned int flags); +virNetServerClientPtr adminServerLookupClient(virNetServerPtr srv, + unsigned long long id, + unsigned int flags); + #endif /* __LIBVIRTD_ADMIN_SERVER_H__ */ diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index 20768e2..e51f2ba 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -219,6 +219,11 @@ int virAdmServerListClients(virAdmServerPtr srv, virAdmClientPtr **clients, unsigned int flags); +virAdmClientPtr +virAdmServerLookupClient(virAdmServerPtr srv, + unsigned long long id, + unsigned int flags); + # ifdef __cplusplus } # endif diff --git a/include/libvirt/virterror.h b/include/libvirt/virterror.h index c6abdf7..2ec560e 100644 --- a/include/libvirt/virterror.h +++ b/include/libvirt/virterror.h @@ -314,6 +314,7 @@ typedef enum { VIR_ERR_MIGRATE_FINISH_OK = 93, /* Finish API succeeded but it is expected to return NULL */ VIR_ERR_AUTH_UNAVAILABLE = 94, /* authentication unavailable */ VIR_ERR_NO_SERVER = 95, /* Server was not found */ + VIR_ERR_NO_CLIENT = 96, /* Client was not found */ } virErrorNumber; /** diff --git a/src/admin/admin_protocol.x b/src/admin/admin_protocol.x index e77ce9e..da21db8 100644 --- a/src/admin/admin_protocol.x +++ b/src/admin/admin_protocol.x @@ -138,6 +138,16 @@ struct admin_server_list_clients_ret { /* insert@1 */ unsigned int ret; }; +struct admin_server_lookup_client_args { + admin_nonnull_server srv; + unsigned hyper id; + unsigned int flags; +}; + +struct admin_server_lookup_client_ret { + admin_nonnull_client clnt; +}; + /* Define the program number, protocol version and procedure numbers here. */ const ADMIN_PROGRAM = 0x06900690; const ADMIN_PROTOCOL_VERSION = 1; @@ -198,5 +208,10 @@ enum admin_procedure { /** * @generate: both */ - ADMIN_PROC_SERVER_LIST_CLIENTS = 8 + ADMIN_PROC_SERVER_LIST_CLIENTS = 8, + + /** + * @generate: both + */ + ADMIN_PROC_SERVER_LOOKUP_CLIENT = 9 }; diff --git a/src/admin_protocol-structs b/src/admin_protocol-structs index 055ddd5..dc2220e 100644 --- a/src/admin_protocol-structs +++ b/src/admin_protocol-structs @@ -87,6 +87,14 @@ struct admin_server_list_clients_ret { } clients; u_int ret; }; +struct admin_server_lookup_client_args { + admin_nonnull_server srv; + uint64_t id; + u_int flags; +}; +struct admin_server_lookup_client_ret { + admin_nonnull_client clnt; +}; enum admin_procedure { ADMIN_PROC_CONNECT_OPEN = 1, ADMIN_PROC_CONNECT_CLOSE = 2, @@ -96,4 +104,5 @@ enum admin_procedure { ADMIN_PROC_SERVER_GET_THREADPOOL_PARAMETERS = 6, ADMIN_PROC_SERVER_SET_THREADPOOL_PARAMETERS = 7, ADMIN_PROC_SERVER_LIST_CLIENTS = 8, + ADMIN_PROC_SERVER_LOOKUP_CLIENT = 9, }; diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index d4143e4..5d0755f 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -889,3 +889,39 @@ virAdmServerListClients(virAdmServerPtr srv, virDispatchError(NULL); return -1; } + +/** + * virAdmServerLookupClient: + * @srv: a valid server object reference + * @id: ID of the client to lookup on server @srv + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Try to lookup a client on the given server based on @id. + * + * virAdmClientFree() should be used to free the resources after the + * client object is no longer needed. + * + * Returns the requested client or NULL in case of failure. If the + * client could not be found, then VIR_ERR_NO_CLIENT error is raised. + */ +virAdmClientPtr +virAdmServerLookupClient(virAdmServerPtr srv, + unsigned long long id, + unsigned int flags) +{ + virAdmClientPtr ret = NULL; + + VIR_DEBUG("srv=%p, id=%llu, flags=%x", srv, id, flags); + virResetLastError(); + + virCheckAdmServerGoto(srv, error); + virCheckFlagsGoto(0, error); + + if (!(ret = remoteAdminServerLookupClient(srv, id, flags))) + goto error; + + return ret; + error: + virDispatchError(NULL); + return NULL; +} diff --git a/src/libvirt_admin_private.syms b/src/libvirt_admin_private.syms index 621b502..3d7ecbc 100644 --- a/src/libvirt_admin_private.syms +++ b/src/libvirt_admin_private.syms @@ -16,6 +16,8 @@ xdr_admin_server_get_threadpool_parameters_args; xdr_admin_server_get_threadpool_parameters_ret; xdr_admin_server_list_clients_args; xdr_admin_server_list_clients_ret; +xdr_admin_server_lookup_client_args; +xdr_admin_server_lookup_client_ret; xdr_admin_server_set_threadpool_parameters_args; # datatypes.h diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index bf6643a..066ae0c 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -29,6 +29,7 @@ LIBVIRT_ADMIN_1.3.0 { virAdmServerGetName; virAdmServerGetThreadPoolParameters; virAdmServerFree; + virAdmServerLookupClient; virAdmConnectLookupServer; virAdmServerSetThreadPoolParameters; virAdmServerListClients; diff --git a/src/rpc/virnetserver.c b/src/rpc/virnetserver.c index fcc79f4..60541cb 100644 --- a/src/rpc/virnetserver.c +++ b/src/rpc/virnetserver.c @@ -972,3 +972,26 @@ virNetServerGetClients(virNetServerPtr srv, virObjectUnlock(srv); return ret; } + +virNetServerClientPtr +virNetServerGetClient(virNetServerPtr srv, + unsigned long long id) +{ + size_t i; + virNetServerClientPtr ret = NULL; + + virObjectLock(srv); + + for (i = 0; i < srv->nclients; i++) { + virNetServerClientPtr client = srv->clients[i]; + if (virNetServerClientGetID(client) == id) + ret = virObjectRef(client); + } + + virObjectUnlock(srv); + + if (!ret) + virReportError(VIR_ERR_NO_CLIENT, + _("No client with matching ID '%llu'"), id); + return ret; +} diff --git a/src/rpc/virnetserver.h b/src/rpc/virnetserver.h index f5bb200..993bda7 100644 --- a/src/rpc/virnetserver.h +++ b/src/rpc/virnetserver.h @@ -105,6 +105,9 @@ int virNetServerSetThreadPoolParameters(virNetServerPtr srv, unsigned long long virNetServerNextClientID(virNetServerPtr srv); +virNetServerClientPtr virNetServerGetClient(virNetServerPtr srv, + unsigned long long id); + int virNetServerGetClients(virNetServerPtr srv, virNetServerClientPtr **clients); diff --git a/src/util/virerror.c b/src/util/virerror.c index 4766e13..5d875e3 100644 --- a/src/util/virerror.c +++ b/src/util/virerror.c @@ -1388,6 +1388,12 @@ virErrorMsg(virErrorNumber error, const char *info) else errmsg = _("Server not found: %s"); break; + case VIR_ERR_NO_CLIENT: + if (info == NULL) + errmsg = _("Client not found"); + else + errmsg = _("Client not found: %s"); + break; } return errmsg; } -- 2.4.11

Once we're able to list and identify all clients connected to a specific server, we can then support force-closing a connection. This patch introduces a simple API calling virNetServerClientClose on a specific client, which can be later extended easily, e.g. by sending an event once the client is disconnected successfully. Signed-off-by: Erik Skultety <eskultet@redhat.com> --- daemon/admin.c | 6 ++++++ daemon/admin_server.c | 9 +++++++++ daemon/admin_server.h | 3 +++ include/libvirt/libvirt-admin.h | 2 ++ src/admin/admin_protocol.x | 12 +++++++++++- src/admin/admin_remote.c | 10 ++++++++++ src/admin_protocol-structs | 5 +++++ src/libvirt-admin.c | 30 ++++++++++++++++++++++++++++++ src/libvirt_admin_private.syms | 1 + src/libvirt_admin_public.syms | 1 + 10 files changed, 78 insertions(+), 1 deletion(-) diff --git a/daemon/admin.c b/daemon/admin.c index fcbdee5..fe797a6 100644 --- a/daemon/admin.c +++ b/daemon/admin.c @@ -94,6 +94,12 @@ make_nonnull_server(admin_nonnull_server *srv_dst, ignore_value(VIR_STRDUP_QUIET(srv_dst->name, virNetServerGetName(srv_src))); } +static virNetServerClientPtr +get_nonnull_client(virNetServerPtr srv, admin_nonnull_client clnt) +{ + return virNetServerGetClient(srv, clnt.id); +} + static void make_nonnull_client(admin_nonnull_client *clt_dst, virNetServerClientPtr clt_src) diff --git a/daemon/admin_server.c b/daemon/admin_server.c index 9ea1164..c555d6d 100644 --- a/daemon/admin_server.c +++ b/daemon/admin_server.c @@ -211,3 +211,12 @@ adminServerLookupClient(virNetServerPtr srv, return virNetServerGetClient(srv, id); } + +int adminClientClose(virNetServerClientPtr client, + unsigned int flags) +{ + virCheckFlags(0, -1); + + virNetServerClientClose(client); + return 0; +} diff --git a/daemon/admin_server.h b/daemon/admin_server.h index a593251..1ac98a4 100644 --- a/daemon/admin_server.h +++ b/daemon/admin_server.h @@ -54,4 +54,7 @@ virNetServerClientPtr adminServerLookupClient(virNetServerPtr srv, unsigned long long id, unsigned int flags); +int adminClientClose(virNetServerClientPtr client, + unsigned int flags); + #endif /* __LIBVIRTD_ADMIN_SERVER_H__ */ diff --git a/include/libvirt/libvirt-admin.h b/include/libvirt/libvirt-admin.h index e51f2ba..4a58638 100644 --- a/include/libvirt/libvirt-admin.h +++ b/include/libvirt/libvirt-admin.h @@ -224,6 +224,8 @@ virAdmServerLookupClient(virAdmServerPtr srv, unsigned long long id, unsigned int flags); +int virAdmClientClose(virAdmClientPtr client, unsigned int flags); + # ifdef __cplusplus } # endif diff --git a/src/admin/admin_protocol.x b/src/admin/admin_protocol.x index da21db8..d639b1a 100644 --- a/src/admin/admin_protocol.x +++ b/src/admin/admin_protocol.x @@ -148,6 +148,11 @@ struct admin_server_lookup_client_ret { admin_nonnull_client clnt; }; +struct admin_client_close_args { + admin_nonnull_client clnt; + unsigned int flags; +}; + /* Define the program number, protocol version and procedure numbers here. */ const ADMIN_PROGRAM = 0x06900690; const ADMIN_PROTOCOL_VERSION = 1; @@ -213,5 +218,10 @@ enum admin_procedure { /** * @generate: both */ - ADMIN_PROC_SERVER_LOOKUP_CLIENT = 9 + ADMIN_PROC_SERVER_LOOKUP_CLIENT = 9, + + /** + * @generate: both + */ + ADMIN_PROC_CLIENT_CLOSE = 10 }; diff --git a/src/admin/admin_remote.c b/src/admin/admin_remote.c index b833ea4..04906d6 100644 --- a/src/admin/admin_remote.c +++ b/src/admin/admin_remote.c @@ -67,6 +67,16 @@ make_nonnull_server(admin_nonnull_server *srv_dst, virAdmServerPtr srv_src) srv_dst->name = srv_src->name; } +static void +make_nonnull_client(admin_nonnull_client *client_dst, + virAdmClientPtr client_src) +{ + client_dst->id = client_src->id; + client_dst->transport = client_src->transport; + client_dst->timestamp = client_src->timestamp; + make_nonnull_server(&client_dst->srv, client_src->srv); +} + static int callFull(virAdmConnectPtr conn ATTRIBUTE_UNUSED, remoteAdminPrivPtr priv, diff --git a/src/admin_protocol-structs b/src/admin_protocol-structs index dc2220e..5f44b76 100644 --- a/src/admin_protocol-structs +++ b/src/admin_protocol-structs @@ -95,6 +95,10 @@ struct admin_server_lookup_client_args { struct admin_server_lookup_client_ret { admin_nonnull_client clnt; }; +struct admin_client_close_args { + admin_nonnull_client clnt; + u_int flags; +}; enum admin_procedure { ADMIN_PROC_CONNECT_OPEN = 1, ADMIN_PROC_CONNECT_CLOSE = 2, @@ -105,4 +109,5 @@ enum admin_procedure { ADMIN_PROC_SERVER_SET_THREADPOOL_PARAMETERS = 7, ADMIN_PROC_SERVER_LIST_CLIENTS = 8, ADMIN_PROC_SERVER_LOOKUP_CLIENT = 9, + ADMIN_PROC_CLIENT_CLOSE = 10, }; diff --git a/src/libvirt-admin.c b/src/libvirt-admin.c index 5d0755f..a06c5d9 100644 --- a/src/libvirt-admin.c +++ b/src/libvirt-admin.c @@ -925,3 +925,33 @@ virAdmServerLookupClient(virAdmServerPtr srv, virDispatchError(NULL); return NULL; } + +/** + * virAdmClientClose: + * @client: a valid client object reference + * @flags: extra flags; not used yet, so callers should always pass 0 + * + * Close @client's connection to daemon forcefully. + * + * Returns 0 if the daemon's connection with @client was closed successfully + * or -1 in case of an error. + */ +int virAdmClientClose(virAdmClientPtr client, + unsigned int flags) +{ + int ret = -1; + + VIR_DEBUG("client=%p, flags=%x", client, flags); + virResetLastError(); + + virCheckAdmClientGoto(client, error); + virCheckFlagsGoto(0, error); + + if ((ret = remoteAdminClientClose(client, flags)) < 0) + goto error; + + return ret; + error: + virDispatchError(NULL); + return -1; +} diff --git a/src/libvirt_admin_private.syms b/src/libvirt_admin_private.syms index 3d7ecbc..ec4cf39 100644 --- a/src/libvirt_admin_private.syms +++ b/src/libvirt_admin_private.syms @@ -6,6 +6,7 @@ # # admin/admin_protocol.x +xdr_admin_client_close_args; xdr_admin_connect_get_lib_version_ret; xdr_admin_connect_list_servers_args; xdr_admin_connect_list_servers_ret; diff --git a/src/libvirt_admin_public.syms b/src/libvirt_admin_public.syms index 066ae0c..4ee4c65 100644 --- a/src/libvirt_admin_public.syms +++ b/src/libvirt_admin_public.syms @@ -33,4 +33,5 @@ LIBVIRT_ADMIN_1.3.0 { virAdmConnectLookupServer; virAdmServerSetThreadPoolParameters; virAdmServerListClients; + virAdmClientClose; }; -- 2.4.11

Enable the client disconnect within virt-admin. Also, update the man page accordingly. Signed-off-by: Erik Skultety <eskultet@redhat.com> --- tools/virt-admin.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.pod | 16 ++++++++++++ 2 files changed, 88 insertions(+) diff --git a/tools/virt-admin.c b/tools/virt-admin.c index f0fecdd..81b8589 100644 --- a/tools/virt-admin.c +++ b/tools/virt-admin.c @@ -655,6 +655,72 @@ cmdSrvClientsList(vshControl *ctl, const vshCmd *cmd) return ret; } +/* ------------------------- + * Command client-disconnect + * ------------------------- + */ + +static const vshCmdInfo info_client_disconnect[] = { + {.name = "help", + .data = N_("force disconnect a client from the given server") + }, + {.name = "desc", + .data = N_("Force close a specific client's connection to the given " + "server.") + }, + {.name = NULL} +}; + +static const vshCmdOptDef opts_client_disconnect[] = { + {.name = "client", + .type = VSH_OT_INT, + .flags = VSH_OFLAG_REQ, + .help = N_("client which to disconnect, specified by ID"), + }, + {.name = "server", + .type = VSH_OT_DATA, + .flags = VSH_OFLAG_REQ, + .help = N_("server which the client is currently connected to"), + }, + {.name = NULL} +}; + +static bool +cmdClientDisconnect(vshControl *ctl, const vshCmd *cmd) +{ + bool ret = false; + const char *srvname = NULL; + unsigned long long id = 0; + virAdmServerPtr srv = NULL; + virAdmClientPtr client = NULL; + vshAdmControlPtr priv = ctl->privData; + + if (vshCommandOptStringReq(ctl, cmd, "server", &srvname) < 0) + return false; + + if (vshCommandOptULongLongWrap(ctl, cmd, "client", &id) < 0) + return false; + + if (!(srv = virAdmConnectLookupServer(priv->conn, srvname, 0))) + goto cleanup; + + if (!(client = virAdmServerLookupClient(srv, id, 0))) + goto cleanup; + + if (virAdmClientClose(client, 0) < 0) { + vshError(ctl, _("Failed to disconnect client '%llu' from server %s"), + id, virAdmServerGetName(srv)); + goto cleanup; + } + + vshPrint(ctl, _("Client '%llu' disconnected"), id); + ret = true; + cleanup: + virAdmClientFree(client); + virAdmServerFree(srv); + return ret; +} + static void * vshAdmConnectionHandler(vshControl *ctl) { @@ -976,6 +1042,12 @@ static const vshCmdDef managementCmds[] = { .info = info_srv_threadpool_set, .flags = 0 }, + {.name = "client-disconnect", + .handler = cmdClientDisconnect, + .opts = opts_client_disconnect, + .info = info_client_disconnect, + .flags = 0 + }, {.name = NULL} }; diff --git a/tools/virt-admin.pod b/tools/virt-admin.pod index 6f9dd63..573f8d9 100644 --- a/tools/virt-admin.pod +++ b/tools/virt-admin.pod @@ -229,6 +229,22 @@ information about client's connection time (system local time is used). =back +=head1 CLIENT COMMANDS + +Following commands provide management and monitoring of clients connected to +one of daemon's available servers. Clients are specified by their numeric ID +which is obtained by listing all clients connected to a specified server +(see command B<srv-clients-list>). + +=over + +=item B<client-disconnect> I<client> I<server> + +Close a connection originating from I<client>. The I<server> argument +specifies the name of the server I<client> is currently connected to. + +=back + =head1 ENVIRONMENT The following environment variables can be set to alter the behaviour -- 2.4.11

On 29.04.2016 15:49, Erik Skultety wrote:
This series adds a simple support for a client force disconnect. As with the series about client identity retrieval, this series is rebased on listing clients due to some gendispatch stuff needed in all of them. To test it, you checkout my remote branch https://github.com/eskultety/libvirt/tree/list-clients-info-disconnect
Erik Skultety (3): admin: Introduce virAdmServerLookupClient admin: Introduce virAdmClientClose API virt-admin: Introduce client-disconnect command
daemon/admin.c | 6 ++++ daemon/admin_server.c | 19 +++++++++++ daemon/admin_server.h | 7 ++++ include/libvirt/libvirt-admin.h | 7 ++++ include/libvirt/virterror.h | 1 + src/admin/admin_protocol.x | 27 +++++++++++++++- src/admin/admin_remote.c | 10 ++++++ src/admin_protocol-structs | 14 ++++++++ src/libvirt-admin.c | 66 +++++++++++++++++++++++++++++++++++++ src/libvirt_admin_private.syms | 3 ++ src/libvirt_admin_public.syms | 2 ++ src/rpc/virnetserver.c | 23 +++++++++++++ src/rpc/virnetserver.h | 3 ++ src/util/virerror.c | 6 ++++ tools/virt-admin.c | 72 +++++++++++++++++++++++++++++++++++++++++ tools/virt-admin.pod | 16 +++++++++ 16 files changed, 281 insertions(+), 1 deletion(-)
This is looking good. Well, at least at first glance, but I am unable to apply these patches because of some conflicts. Can you please rebase & resend? Michal
participants (2)
-
Erik Skultety
-
Michal Privoznik