mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-22 13:45:38 +00:00
Ensure client streams are closed when marking a client for close
Every active stream results in a reference being held on the virNetServerClientPtr object. This meant that if a client quit with any streams active, although all I/O was stopped the virNetServerClientPtr object would leak. This causes libvirtd to leak any file handles associated with open streams when a client quit To fix this, when we call virNetServerClientClose there is a callback invoked which lets the daemon release the streams and thus the extra references * daemon/remote.c: Add a hook to close all streams * daemon/stream.c, daemon/stream.h: Add API for releasing all streams * src/rpc/virnetserverclient.c, src/rpc/virnetserverclient.h: Allow registration of a hook to trigger when closing client
This commit is contained in:
parent
3cf37700cd
commit
f682c25308
@ -439,6 +439,13 @@ static void remoteClientFreeFunc(void *data)
|
||||
}
|
||||
|
||||
|
||||
static void remoteClientCloseFunc(virNetServerClientPtr client)
|
||||
{
|
||||
struct daemonClientPrivate *priv = virNetServerClientGetPrivateData(client);
|
||||
|
||||
daemonRemoveAllClientStreams(priv->streams);
|
||||
}
|
||||
|
||||
|
||||
int remoteClientInitHook(virNetServerPtr srv ATTRIBUTE_UNUSED,
|
||||
virNetServerClientPtr client)
|
||||
@ -460,7 +467,9 @@ int remoteClientInitHook(virNetServerPtr srv ATTRIBUTE_UNUSED,
|
||||
for (i = 0 ; i < VIR_DOMAIN_EVENT_ID_LAST ; i++)
|
||||
priv->domainEventCallbackID[i] = -1;
|
||||
|
||||
virNetServerClientSetPrivateData(client, priv, remoteClientFreeFunc);
|
||||
virNetServerClientSetPrivateData(client, priv,
|
||||
remoteClientFreeFunc);
|
||||
virNetServerClientSetCloseHook(client, remoteClientCloseFunc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -334,13 +334,17 @@ int daemonFreeClientStream(virNetServerClientPtr client,
|
||||
msg = stream->rx;
|
||||
while (msg) {
|
||||
virNetMessagePtr tmp = msg->next;
|
||||
/* Send a dummy reply to free up 'msg' & unblock client rx */
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
msg->header.type = VIR_NET_REPLY;
|
||||
if (virNetServerClientSendMessage(client, msg) < 0) {
|
||||
virNetServerClientImmediateClose(client);
|
||||
if (client) {
|
||||
/* Send a dummy reply to free up 'msg' & unblock client rx */
|
||||
memset(msg, 0, sizeof(*msg));
|
||||
msg->header.type = VIR_NET_REPLY;
|
||||
if (virNetServerClientSendMessage(client, msg) < 0) {
|
||||
virNetServerClientImmediateClose(client);
|
||||
virNetMessageFree(msg);
|
||||
ret = -1;
|
||||
}
|
||||
} else {
|
||||
virNetMessageFree(msg);
|
||||
ret = -1;
|
||||
}
|
||||
msg = tmp;
|
||||
}
|
||||
@ -441,6 +445,28 @@ daemonRemoveClientStream(virNetServerClientPtr client,
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
daemonRemoveAllClientStreams(daemonClientStream *stream)
|
||||
{
|
||||
daemonClientStream *tmp;
|
||||
|
||||
VIR_DEBUG("stream=%p", stream);
|
||||
|
||||
while (stream) {
|
||||
tmp = stream->next;
|
||||
|
||||
if (!stream->closed) {
|
||||
virStreamEventRemoveCallback(stream->st);
|
||||
virStreamAbort(stream->st);
|
||||
}
|
||||
|
||||
daemonFreeClientStream(NULL, stream);
|
||||
|
||||
VIR_DEBUG("next stream=%p", tmp);
|
||||
stream = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns:
|
||||
* -1 if fatal error occurred
|
||||
|
@ -45,4 +45,7 @@ int
|
||||
daemonRemoveClientStream(virNetServerClientPtr client,
|
||||
daemonClientStream *stream);
|
||||
|
||||
void
|
||||
daemonRemoveAllClientStreams(daemonClientStream *stream);
|
||||
|
||||
#endif /* __LIBVIRTD_STREAM_H__ */
|
||||
|
@ -97,6 +97,7 @@ struct _virNetServerClient
|
||||
|
||||
void *privateData;
|
||||
virNetServerClientFreeFunc privateDataFreeFunc;
|
||||
virNetServerClientCloseFunc privateDataCloseFunc;
|
||||
};
|
||||
|
||||
|
||||
@ -492,6 +493,15 @@ void *virNetServerClientGetPrivateData(virNetServerClientPtr client)
|
||||
}
|
||||
|
||||
|
||||
void virNetServerClientSetCloseHook(virNetServerClientPtr client,
|
||||
virNetServerClientCloseFunc cf)
|
||||
{
|
||||
virNetServerClientLock(client);
|
||||
client->privateDataCloseFunc = cf;
|
||||
virNetServerClientUnlock(client);
|
||||
}
|
||||
|
||||
|
||||
void virNetServerClientSetDispatcher(virNetServerClientPtr client,
|
||||
virNetServerClientDispatchFunc func,
|
||||
void *opaque)
|
||||
@ -560,6 +570,8 @@ void virNetServerClientFree(virNetServerClientPtr client)
|
||||
*/
|
||||
void virNetServerClientClose(virNetServerClientPtr client)
|
||||
{
|
||||
virNetServerClientCloseFunc cf;
|
||||
|
||||
virNetServerClientLock(client);
|
||||
VIR_DEBUG("client=%p refs=%d", client, client->refs);
|
||||
if (!client->sock) {
|
||||
@ -567,6 +579,15 @@ void virNetServerClientClose(virNetServerClientPtr client)
|
||||
return;
|
||||
}
|
||||
|
||||
if (client->privateDataCloseFunc) {
|
||||
cf = client->privateDataCloseFunc;
|
||||
client->refs++;
|
||||
virNetServerClientUnlock(client);
|
||||
(cf)(client);
|
||||
virNetServerClientLock(client);
|
||||
client->refs--;
|
||||
}
|
||||
|
||||
/* Do now, even though we don't close the socket
|
||||
* until end, to ensure we don't get invoked
|
||||
* again due to tls shutdown */
|
||||
|
@ -82,6 +82,11 @@ void virNetServerClientSetPrivateData(virNetServerClientPtr client,
|
||||
virNetServerClientFreeFunc ff);
|
||||
void *virNetServerClientGetPrivateData(virNetServerClientPtr client);
|
||||
|
||||
typedef void (*virNetServerClientCloseFunc)(virNetServerClientPtr client);
|
||||
|
||||
void virNetServerClientSetCloseHook(virNetServerClientPtr client,
|
||||
virNetServerClientCloseFunc cf);
|
||||
|
||||
void virNetServerClientSetDispatcher(virNetServerClientPtr client,
|
||||
virNetServerClientDispatchFunc func,
|
||||
void *opaque);
|
||||
|
Loading…
Reference in New Issue
Block a user