On 08/17/2010 11:02 AM, Daniel P. Berrange wrote: > This re-writes the 'virsh console' command so that it uses > the new streams API. This lets it run remotely and/or as a > non-root user. This requires that virsh be linked against > the simple event loop from libvirtd in daemon/event.c > As an added bonus, it can now connect to any console device, > not just the first one. > > * tools/Makefile.am: Link to event.c > * tools/console.c, tools/console.h: Rewrite to use the > virDomainOpenConsole() APIs with streams > * tools/virsh.c: Support choosing the console name > via --devname $NAME > --- > tools/Makefile.am | 1 + > tools/console.c | 330 ++++++++++++++++++++++++++++++++++++++++------------- > tools/console.h | 2 +- > tools/virsh.c | 76 ++++--------- > 4 files changed, 274 insertions(+), 135 deletions(-) > + > +struct virConsoleBuffer { > + size_t length; > + size_t offset; s/size_t/off_t/ for offset? (but see below) > + char *data; > +}; > + > +typedef struct virConsole virConsole; > +typedef virConsole *virConsolePtr; > +struct virConsole { > + virStreamPtr st; > + int quit; s/int/bool/, and adjust clients to use false/true instead of 0/1 > + > + int stdinWatch; > + int stdoutWatch; > + > + struct virConsoleBuffer streamToTerminal; > + struct virConsoleBuffer terminalToStream; > +}; > + > static int got_signal = 0; > static void do_signal(int sig ATTRIBUTE_UNUSED) { > got_signal = 1; > @@ -61,22 +86,192 @@ cfmakeraw (struct termios *attr) > } > # endif /* !HAVE_CFMAKERAW */ > > -int vshRunConsole(const char *tty) { > - int ttyfd, ret = -1; > +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; Oh, never mind. size_t for offset is probably just fine after all. > + int got; > + > + if (avail < 1024) { > + if (VIR_REALLOC_N(con->streamToTerminal.data, > + con->streamToTerminal.length + 1024) < 0) { /me Note to self - another VIR_EXPAND_N or VIR_RESIZE_N candidate. > + virReportOOMError(); > + con->quit = 1; > + 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) { > + con->quit = 1; > + return; > + } > + con->streamToTerminal.offset += got; > + if (con->streamToTerminal.offset) > + virEventUpdateHandleImpl(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) { > + virErrorPtr err = virGetLastError(); > + con->quit = 1; > + 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) > + {} /me Note to self - a VIR_SHRINK_N candidate. > + 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) { > + con->quit = 1; > + } > +} > + > +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(); > + con->quit = 1; > + return; > + } > + con->terminalToStream.length += 1024; > + avail += 1024; > + } > + > + got = read(fd, > + con->terminalToStream.data + > + con->terminalToStream.offset, > + avail); > + if (got < 0) { > + if (errno != EAGAIN) { > + con->quit = 1; > + } > + return; > + } > + if (got == 0) { > + con->quit = 1; > + return; > + } > + if (con->terminalToStream.data[con->terminalToStream.offset] == CTRL_CLOSE_BRACKET) { > + con->quit = 1; > + 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) { > + con->quit = 1; > + } > +} > + > +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); Do we want to use safewrite here? Conditional ACK, if those are deemed easy nits to fix. -- Eric Blake eblake@xxxxxxxxxx +1-801-349-2682 Libvirt virtualization library http://libvirt.org
Attachment:
signature.asc
Description: OpenPGP digital signature
-- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list