static void virConsoleEventOnStdout(int watch ATTRIBUTE_UNUSED, int fd, int events, void *opaque) { virConsolePtr con = opaque; virObjectLock(con); /* we got late event after console was shutdown */ if (!con->st) goto cleanup; if (events & VIR_EVENT_HANDLE_WRITABLE && con->streamToTerminal.offset) { ssize_t done; size_t avail; done = write(fd, con->streamToTerminal.data, con->streamToTerminal.offset); if (done < 0) { if (errno != EAGAIN) { virReportSystemError(errno, "%s", _("cannot write to stdout")); virConsoleShutdown(con); } goto cleanup; } memmove(con->streamToTerminal.data, con->streamToTerminal.data + done, con->streamToTerminal.offset - done); con->streamToTerminal.offset -= done; avail = con->streamToTerminal.length - con->streamToTerminal.offset; if (avail > 1024) { ignore_value(VIR_REALLOC_N(con->streamToTerminal.data, con->streamToTerminal.offset + 1024)); con->streamToTerminal.length = con->streamToTerminal.offset + 1024; } } if (!con->streamToTerminal.offset) virEventUpdateHandle(con->stdoutWatch, 0); if (events & VIR_EVENT_HANDLE_ERROR) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("IO error stdout")); virConsoleShutdown(con); goto cleanup; } if (events & VIR_EVENT_HANDLE_HANGUP) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("EOF on stdout")); virConsoleShutdown(con); goto cleanup; } cleanup: virObjectUnlock(con); }
static void virConsoleEventOnStdin(int watch ATTRIBUTE_UNUSED, int fd ATTRIBUTE_UNUSED, int events, void *opaque) { virConsolePtr con = opaque; if (events & VIR_EVENT_HANDLE_READABLE) { size_t avail = con->terminalToStream.length - con->terminalToStream.offset; int got; if (avail < 1024) { if (VIR_REALLOC_N(con->terminalToStream.data, con->terminalToStream.length + 1024) < 0) { virReportOOMError(); virConsoleShutdown(con); return; } con->terminalToStream.length += 1024; avail += 1024; } got = read(fd, con->terminalToStream.data + con->terminalToStream.offset, avail); if (got < 0) { if (errno != EAGAIN) { virConsoleShutdown(con); } return; } if (got == 0) { virConsoleShutdown(con); return; } if (con->terminalToStream.data[con->terminalToStream.offset] == con->escapeChar) { virConsoleShutdown(con); return; } con->terminalToStream.offset += got; if (con->terminalToStream.offset) virStreamEventUpdateCallback(con->st, VIR_STREAM_EVENT_READABLE | VIR_STREAM_EVENT_WRITABLE); } if (events & VIR_EVENT_HANDLE_ERROR || events & VIR_EVENT_HANDLE_HANGUP) { virConsoleShutdown(con); } }
static void virConsoleEventOnStdout(int watch ATTRIBUTE_UNUSED, int fd, int events, void *opaque) { virConsolePtr con = opaque; if (events & VIR_EVENT_HANDLE_WRITABLE && con->streamToTerminal.offset) { ssize_t done; size_t avail; done = write(fd, con->streamToTerminal.data, con->streamToTerminal.offset); if (done < 0) { if (errno != EAGAIN) { virConsoleShutdown(con); } return; } memmove(con->streamToTerminal.data, con->streamToTerminal.data + done, con->streamToTerminal.offset - done); con->streamToTerminal.offset -= done; avail = con->streamToTerminal.length - con->streamToTerminal.offset; if (avail > 1024) { if (VIR_REALLOC_N(con->streamToTerminal.data, con->streamToTerminal.offset + 1024) < 0) {} con->streamToTerminal.length = con->streamToTerminal.offset + 1024; } } if (!con->streamToTerminal.offset) virEventUpdateHandle(con->stdoutWatch, 0); if (events & VIR_EVENT_HANDLE_ERROR || events & VIR_EVENT_HANDLE_HANGUP) { virConsoleShutdown(con); } }
int virshRunConsole(vshControl *ctl, virDomainPtr dom, const char *dev_name, unsigned int flags) { virConsolePtr con = NULL; virshControlPtr priv = ctl->privData; int ret = -1; struct sigaction old_sigquit; struct sigaction old_sigterm; struct sigaction old_sigint; struct sigaction old_sighup; struct sigaction old_sigpipe; struct sigaction sighandler = {.sa_handler = virConsoleHandleSignal, .sa_flags = SA_SIGINFO }; sigemptyset(&sighandler.sa_mask); /* Put STDIN into raw mode so that stuff typed does not echo to the screen * (the TTY reads will result in it being echoed back already), and also * ensure Ctrl-C, etc is blocked, and misc other bits */ if (vshTTYMakeRaw(ctl, true) < 0) goto resettty; if (!(con = virConsoleNew())) goto resettty; virObjectLock(con); /* Trap all common signals so that we can safely restore the original * terminal settings on STDIN before the process exits - people don't like * being left with a messed up terminal ! */ sigaction(SIGQUIT, &sighandler, &old_sigquit); sigaction(SIGTERM, &sighandler, &old_sigterm); sigaction(SIGINT, &sighandler, &old_sigint); sigaction(SIGHUP, &sighandler, &old_sighup); sigaction(SIGPIPE, &sighandler, &old_sigpipe); con->escapeChar = virshGetEscapeChar(priv->escapeChar); con->st = virStreamNew(virDomainGetConnect(dom), VIR_STREAM_NONBLOCK); if (!con->st) goto cleanup; if (virDomainOpenConsole(dom, dev_name, con->st, flags) < 0) goto cleanup; virObjectRef(con); if ((con->stdinWatch = virEventAddHandle(STDIN_FILENO, VIR_EVENT_HANDLE_READABLE, virConsoleEventOnStdin, con, virObjectFreeCallback)) < 0) { virObjectUnref(con); goto cleanup; } virObjectRef(con); if ((con->stdoutWatch = virEventAddHandle(STDOUT_FILENO, 0, virConsoleEventOnStdout, con, virObjectFreeCallback)) < 0) { virObjectUnref(con); goto cleanup; } virObjectRef(con); if (virStreamEventAddCallback(con->st, VIR_STREAM_EVENT_READABLE, virConsoleEventOnStream, con, virObjectFreeCallback) < 0) { virObjectUnref(con); goto cleanup; } while (!con->quit) { if (virCondWait(&con->cond, &con->parent.lock) < 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unable to wait on console condition")); goto cleanup; } } if (con->error.code == VIR_ERR_OK) ret = 0; cleanup: virConsoleShutdown(con); if (ret < 0) { vshResetLibvirtError(); virSetError(&con->error); vshSaveLibvirtHelperError(); } virObjectUnlock(con); virObjectUnref(con); /* Restore original signal handlers */ sigaction(SIGQUIT, &old_sigquit, NULL); sigaction(SIGTERM, &old_sigterm, NULL); sigaction(SIGINT, &old_sigint, NULL); sigaction(SIGHUP, &old_sighup, NULL); sigaction(SIGPIPE, &old_sigpipe, NULL); resettty: /* Put STDIN back into the (sane?) state we found it in before starting */ vshTTYRestore(ctl); return ret; }
static void virConsoleEventOnStdin(int watch ATTRIBUTE_UNUSED, int fd ATTRIBUTE_UNUSED, int events, void *opaque) { virConsolePtr con = opaque; virObjectLock(con); /* we got late event after console was shutdown */ if (!con->st) goto cleanup; if (events & VIR_EVENT_HANDLE_READABLE) { size_t avail = con->terminalToStream.length - con->terminalToStream.offset; int got; if (avail < 1024) { if (VIR_REALLOC_N(con->terminalToStream.data, con->terminalToStream.length + 1024) < 0) { virConsoleShutdown(con); goto cleanup; } con->terminalToStream.length += 1024; avail += 1024; } got = read(fd, con->terminalToStream.data + con->terminalToStream.offset, avail); if (got < 0) { if (errno != EAGAIN) { virReportSystemError(errno, "%s", _("cannot read from stdin")); virConsoleShutdown(con); } goto cleanup; } if (got == 0) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("EOF on stdin")); virConsoleShutdown(con); goto cleanup; } if (con->terminalToStream.data[con->terminalToStream.offset] == con->escapeChar) { virConsoleShutdown(con); goto cleanup; } con->terminalToStream.offset += got; if (con->terminalToStream.offset) virStreamEventUpdateCallback(con->st, VIR_STREAM_EVENT_READABLE | VIR_STREAM_EVENT_WRITABLE); } if (events & VIR_EVENT_HANDLE_ERROR) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("IO error on stdin")); virConsoleShutdown(con); goto cleanup; } if (events & VIR_EVENT_HANDLE_HANGUP) { virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("EOF on stdin")); virConsoleShutdown(con); goto cleanup; } cleanup: virObjectUnlock(con); }
static void virConsoleEventOnStream(virStreamPtr st, int events, void *opaque) { virConsolePtr con = opaque; virObjectLock(con); /* we got late event after console was shutdown */ if (!con->st) goto cleanup; if (events & VIR_STREAM_EVENT_READABLE) { size_t avail = con->streamToTerminal.length - con->streamToTerminal.offset; int got; if (avail < 1024) { if (VIR_REALLOC_N(con->streamToTerminal.data, con->streamToTerminal.length + 1024) < 0) { virConsoleShutdown(con); goto cleanup; } con->streamToTerminal.length += 1024; avail += 1024; } got = virStreamRecv(st, con->streamToTerminal.data + con->streamToTerminal.offset, avail); if (got == -2) goto cleanup; /* blocking */ if (got <= 0) { if (got == 0) virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("console stream EOF")); virConsoleShutdown(con); goto cleanup; } con->streamToTerminal.offset += got; if (con->streamToTerminal.offset) virEventUpdateHandle(con->stdoutWatch, VIR_EVENT_HANDLE_WRITABLE); } if (events & VIR_STREAM_EVENT_WRITABLE && con->terminalToStream.offset) { ssize_t done; size_t avail; done = virStreamSend(con->st, con->terminalToStream.data, con->terminalToStream.offset); if (done == -2) goto cleanup; /* blocking */ if (done < 0) { virConsoleShutdown(con); goto cleanup; } memmove(con->terminalToStream.data, con->terminalToStream.data + done, con->terminalToStream.offset - done); con->terminalToStream.offset -= done; avail = con->terminalToStream.length - con->terminalToStream.offset; if (avail > 1024) { ignore_value(VIR_REALLOC_N(con->terminalToStream.data, con->terminalToStream.offset + 1024)); con->terminalToStream.length = con->terminalToStream.offset + 1024; } } if (!con->terminalToStream.offset) virStreamEventUpdateCallback(con->st, VIR_STREAM_EVENT_READABLE); if (events & VIR_STREAM_EVENT_ERROR || events & VIR_STREAM_EVENT_HANGUP) { virConsoleShutdown(con); } cleanup: virObjectUnlock(con); }
static void virConsoleEventOnStream(virStreamPtr st, int events, void *opaque) { virConsolePtr con = opaque; if (events & VIR_STREAM_EVENT_READABLE) { size_t avail = con->streamToTerminal.length - con->streamToTerminal.offset; int got; if (avail < 1024) { if (VIR_REALLOC_N(con->streamToTerminal.data, con->streamToTerminal.length + 1024) < 0) { virReportOOMError(); virConsoleShutdown(con); return; } con->streamToTerminal.length += 1024; avail += 1024; } got = virStreamRecv(st, con->streamToTerminal.data + con->streamToTerminal.offset, avail); if (got == -2) return; /* blocking */ if (got <= 0) { virConsoleShutdown(con); return; } con->streamToTerminal.offset += got; if (con->streamToTerminal.offset) virEventUpdateHandle(con->stdoutWatch, VIR_EVENT_HANDLE_WRITABLE); } if (events & VIR_STREAM_EVENT_WRITABLE && con->terminalToStream.offset) { ssize_t done; size_t avail; done = virStreamSend(con->st, con->terminalToStream.data, con->terminalToStream.offset); if (done == -2) return; /* blocking */ if (done < 0) { virConsoleShutdown(con); return; } memmove(con->terminalToStream.data, con->terminalToStream.data + done, con->terminalToStream.offset - done); con->terminalToStream.offset -= done; avail = con->terminalToStream.length - con->terminalToStream.offset; if (avail > 1024) { if (VIR_REALLOC_N(con->terminalToStream.data, con->terminalToStream.offset + 1024) < 0) {} con->terminalToStream.length = con->terminalToStream.offset + 1024; } } if (!con->terminalToStream.offset) virStreamEventUpdateCallback(con->st, VIR_STREAM_EVENT_READABLE); if (events & VIR_STREAM_EVENT_ERROR || events & VIR_STREAM_EVENT_HANGUP) { virConsoleShutdown(con); } }