Fix reference counting bug in virsh console

The event watches need to be removed before the event loop
terminates, otherwise they cause a dangling reference to
be held on the virStreamPtr, which in turns holds a reference
on virConnectPtr, which in turn causes errors like

  "Failed to disconnect from the hypervisor"

* tools/console.c: Remove watches before event loop quits
* tools/virsh.c: Print out dangling reference count
This commit is contained in:
Daniel P. Berrange 2010-11-11 15:15:46 +00:00
parent 5fb9db272d
commit a926156792
2 changed files with 30 additions and 19 deletions

View File

@ -88,6 +88,19 @@ cfmakeraw (struct termios *attr)
} }
# endif /* !HAVE_CFMAKERAW */ # endif /* !HAVE_CFMAKERAW */
static void
virConsoleShutdown(virConsolePtr con)
{
con->quit = true;
virStreamEventRemoveCallback(con->st);
if (con->stdinWatch != -1)
virEventRemoveHandleImpl(con->stdinWatch);
if (con->stdinWatch != -1)
virEventRemoveHandleImpl(con->stdoutWatch);
con->stdinWatch = -1;
con->stdoutWatch = -1;
}
static void static void
virConsoleEventOnStream(virStreamPtr st, virConsoleEventOnStream(virStreamPtr st,
int events, void *opaque) int events, void *opaque)
@ -103,7 +116,7 @@ virConsoleEventOnStream(virStreamPtr st,
if (VIR_REALLOC_N(con->streamToTerminal.data, if (VIR_REALLOC_N(con->streamToTerminal.data,
con->streamToTerminal.length + 1024) < 0) { con->streamToTerminal.length + 1024) < 0) {
virReportOOMError(); virReportOOMError();
con->quit = true; virConsoleShutdown(con);
return; return;
} }
con->streamToTerminal.length += 1024; con->streamToTerminal.length += 1024;
@ -117,7 +130,7 @@ virConsoleEventOnStream(virStreamPtr st,
if (got == -2) if (got == -2)
return; /* blocking */ return; /* blocking */
if (got <= 0) { if (got <= 0) {
con->quit = true; virConsoleShutdown(con);
return; return;
} }
con->streamToTerminal.offset += got; con->streamToTerminal.offset += got;
@ -136,7 +149,7 @@ virConsoleEventOnStream(virStreamPtr st,
if (done == -2) if (done == -2)
return; /* blocking */ return; /* blocking */
if (done < 0) { if (done < 0) {
con->quit = true; virConsoleShutdown(con);
return; return;
} }
memmove(con->terminalToStream.data, memmove(con->terminalToStream.data,
@ -158,7 +171,7 @@ virConsoleEventOnStream(virStreamPtr st,
if (events & VIR_STREAM_EVENT_ERROR || if (events & VIR_STREAM_EVENT_ERROR ||
events & VIR_STREAM_EVENT_HANGUP) { events & VIR_STREAM_EVENT_HANGUP) {
con->quit = true; virConsoleShutdown(con);
} }
} }
@ -179,7 +192,7 @@ virConsoleEventOnStdin(int watch ATTRIBUTE_UNUSED,
if (VIR_REALLOC_N(con->terminalToStream.data, if (VIR_REALLOC_N(con->terminalToStream.data,
con->terminalToStream.length + 1024) < 0) { con->terminalToStream.length + 1024) < 0) {
virReportOOMError(); virReportOOMError();
con->quit = true; virConsoleShutdown(con);
return; return;
} }
con->terminalToStream.length += 1024; con->terminalToStream.length += 1024;
@ -192,16 +205,16 @@ virConsoleEventOnStdin(int watch ATTRIBUTE_UNUSED,
avail); avail);
if (got < 0) { if (got < 0) {
if (errno != EAGAIN) { if (errno != EAGAIN) {
con->quit = true; virConsoleShutdown(con);
} }
return; return;
} }
if (got == 0) { if (got == 0) {
con->quit = true; virConsoleShutdown(con);
return; return;
} }
if (con->terminalToStream.data[con->terminalToStream.offset] == CTRL_CLOSE_BRACKET) { if (con->terminalToStream.data[con->terminalToStream.offset] == CTRL_CLOSE_BRACKET) {
con->quit = true; virConsoleShutdown(con);
return; return;
} }
@ -214,7 +227,7 @@ virConsoleEventOnStdin(int watch ATTRIBUTE_UNUSED,
if (events & VIR_EVENT_HANDLE_ERROR || if (events & VIR_EVENT_HANDLE_ERROR ||
events & VIR_EVENT_HANDLE_HANGUP) { events & VIR_EVENT_HANDLE_HANGUP) {
con->quit = true; virConsoleShutdown(con);
} }
} }
@ -235,7 +248,7 @@ virConsoleEventOnStdout(int watch ATTRIBUTE_UNUSED,
con->streamToTerminal.offset); con->streamToTerminal.offset);
if (done < 0) { if (done < 0) {
if (errno != EAGAIN) { if (errno != EAGAIN) {
con->quit = true; virConsoleShutdown(con);
} }
return; return;
} }
@ -258,7 +271,7 @@ virConsoleEventOnStdout(int watch ATTRIBUTE_UNUSED,
if (events & VIR_EVENT_HANDLE_ERROR || if (events & VIR_EVENT_HANDLE_ERROR ||
events & VIR_EVENT_HANDLE_HANGUP) { events & VIR_EVENT_HANDLE_HANGUP) {
con->quit = true; virConsoleShutdown(con);
} }
} }
@ -341,10 +354,6 @@ int vshRunConsole(virDomainPtr dom, const char *devname)
break; break;
} }
virStreamEventRemoveCallback(con->st);
virEventRemoveHandleImpl(con->stdinWatch);
virEventRemoveHandleImpl(con->stdoutWatch);
ret = 0; ret = 0;
cleanup: cleanup:

View File

@ -637,8 +637,9 @@ cmdConnect(vshControl *ctl, const vshCmd *cmd)
char *name; char *name;
if (ctl->conn) { if (ctl->conn) {
if (virConnectClose(ctl->conn) != 0) { int ret;
vshError(ctl, "%s", _("Failed to disconnect from the hypervisor")); if ((ret = virConnectClose(ctl->conn)) != 0) {
vshError(ctl, _("Failed to disconnect from the hypervisor, %d leaked reference(s)"), ret);
return FALSE; return FALSE;
} }
ctl->conn = NULL; ctl->conn = NULL;
@ -11463,8 +11464,9 @@ vshDeinit(vshControl *ctl)
vshCloseLogFile(ctl); vshCloseLogFile(ctl);
VIR_FREE(ctl->name); VIR_FREE(ctl->name);
if (ctl->conn) { if (ctl->conn) {
if (virConnectClose(ctl->conn) != 0) { int ret;
vshError(ctl, "%s", _("failed to disconnect from the hypervisor")); if ((ret = virConnectClose(ctl->conn)) != 0) {
vshError(ctl, _("Failed to disconnect from the hypervisor, %d leaked reference(s)"), ret);
} }
} }
virResetLastError(); virResetLastError();