2007-01-26 11:54:29 +00:00
|
|
|
/*
|
|
|
|
* console.c: A dumb serial console client
|
|
|
|
*
|
2010-05-03 20:44:12 +00:00
|
|
|
* Copyright (C) 2007, 2008, 2010 Red Hat, Inc.
|
2007-01-26 11:54:29 +00:00
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
* Daniel Berrange <berrange@redhat.com>
|
|
|
|
*/
|
|
|
|
|
2008-01-29 18:15:54 +00:00
|
|
|
#include <config.h>
|
2007-11-26 12:14:32 +00:00
|
|
|
|
2010-05-03 20:44:12 +00:00
|
|
|
#ifndef WIN32
|
2007-12-07 14:56:37 +00:00
|
|
|
|
2010-03-09 18:22:22 +00:00
|
|
|
# include <stdio.h>
|
|
|
|
# include <sys/types.h>
|
|
|
|
# include <sys/stat.h>
|
|
|
|
# include <fcntl.h>
|
|
|
|
# include <termios.h>
|
|
|
|
# include <poll.h>
|
|
|
|
# include <string.h>
|
|
|
|
# include <errno.h>
|
|
|
|
# include <unistd.h>
|
|
|
|
# include <signal.h>
|
2007-01-26 11:54:29 +00:00
|
|
|
|
2010-03-09 18:22:22 +00:00
|
|
|
# include "console.h"
|
|
|
|
# include "internal.h"
|
|
|
|
# include "logging.h"
|
|
|
|
# include "util.h"
|
2007-01-26 11:54:29 +00:00
|
|
|
|
|
|
|
/* ie Ctrl-] as per telnet */
|
2010-03-09 18:22:22 +00:00
|
|
|
# define CTRL_CLOSE_BRACKET '\35'
|
2007-01-26 11:54:29 +00:00
|
|
|
|
|
|
|
static int got_signal = 0;
|
|
|
|
static void do_signal(int sig ATTRIBUTE_UNUSED) {
|
|
|
|
got_signal = 1;
|
|
|
|
}
|
|
|
|
|
2010-03-09 18:22:22 +00:00
|
|
|
# ifndef HAVE_CFMAKERAW
|
2007-11-26 12:14:32 +00:00
|
|
|
static void
|
|
|
|
cfmakeraw (struct termios *attr)
|
|
|
|
{
|
|
|
|
attr->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
|
|
|
|
| INLCR | IGNCR | ICRNL | IXON);
|
|
|
|
attr->c_oflag &= ~OPOST;
|
|
|
|
attr->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
|
|
|
attr->c_cflag &= ~(CSIZE | PARENB);
|
|
|
|
attr->c_cflag |= CS8;
|
|
|
|
}
|
2010-03-09 18:22:22 +00:00
|
|
|
# endif /* !HAVE_CFMAKERAW */
|
2007-11-26 12:14:32 +00:00
|
|
|
|
2007-01-28 19:47:36 +00:00
|
|
|
int vshRunConsole(const char *tty) {
|
2007-01-26 11:54:29 +00:00
|
|
|
int ttyfd, ret = -1;
|
|
|
|
struct termios ttyattr, rawattr;
|
|
|
|
void (*old_sigquit)(int);
|
|
|
|
void (*old_sigterm)(int);
|
|
|
|
void (*old_sigint)(int);
|
|
|
|
void (*old_sighup)(int);
|
|
|
|
void (*old_sigpipe)(int);
|
|
|
|
|
|
|
|
|
|
|
|
/* We do not want this to become the controlling TTY */
|
|
|
|
if ((ttyfd = open(tty, O_NOCTTY | O_RDWR)) < 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR(_("unable to open tty %s: %s"),
|
2009-05-20 13:37:30 +00:00
|
|
|
tty, strerror(errno));
|
2007-01-26 11:54:29 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-08-07 08:56:38 +00:00
|
|
|
/* Put STDIN into raw mode so that stuff typed
|
2007-01-26 11:54:29 +00:00
|
|
|
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 (tcgetattr(STDIN_FILENO, &ttyattr) < 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR(_("unable to get tty attributes: %s"),
|
2009-05-20 13:37:30 +00:00
|
|
|
strerror(errno));
|
2007-01-26 11:54:29 +00:00
|
|
|
goto closetty;
|
|
|
|
}
|
|
|
|
|
|
|
|
rawattr = ttyattr;
|
|
|
|
cfmakeraw(&rawattr);
|
|
|
|
|
|
|
|
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &rawattr) < 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR(_("unable to set tty attributes: %s"),
|
2009-05-20 13:37:30 +00:00
|
|
|
strerror(errno));
|
2007-01-26 11:54:29 +00:00
|
|
|
goto closetty;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 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 ! */
|
|
|
|
old_sigquit = signal(SIGQUIT, do_signal);
|
|
|
|
old_sigterm = signal(SIGTERM, do_signal);
|
|
|
|
old_sigint = signal(SIGINT, do_signal);
|
|
|
|
old_sighup = signal(SIGHUP, do_signal);
|
|
|
|
old_sigpipe = signal(SIGPIPE, do_signal);
|
|
|
|
got_signal = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/* Now lets process STDIN & tty forever.... */
|
|
|
|
for (; !got_signal ;) {
|
|
|
|
unsigned int i;
|
|
|
|
struct pollfd fds[] = {
|
|
|
|
{ STDIN_FILENO, POLLIN, 0 },
|
|
|
|
{ ttyfd, POLLIN, 0 },
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Wait for data to be available for reading on
|
|
|
|
STDIN or the tty */
|
|
|
|
if (poll(fds, (sizeof(fds)/sizeof(struct pollfd)), -1) < 0) {
|
|
|
|
if (got_signal)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
if (errno == EINTR || errno == EAGAIN)
|
|
|
|
continue;
|
|
|
|
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR(_("failure waiting for I/O: %s"), strerror(errno));
|
2007-01-26 11:54:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0 ; i < (sizeof(fds)/sizeof(struct pollfd)) ; i++) {
|
|
|
|
if (!fds[i].revents)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Process incoming data available for read */
|
|
|
|
if (fds[i].revents & POLLIN) {
|
|
|
|
char buf[4096];
|
|
|
|
int got, sent = 0, destfd;
|
|
|
|
|
|
|
|
if ((got = read(fds[i].fd, buf, sizeof(buf))) < 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR(_("failure reading input: %s"),
|
2009-05-20 13:37:30 +00:00
|
|
|
strerror(errno));
|
2007-01-26 11:54:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Quit if end of file, or we got the Ctrl-] key */
|
|
|
|
if (!got ||
|
|
|
|
(got == 1 &&
|
|
|
|
buf[0] == CTRL_CLOSE_BRACKET))
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
/* Data from stdin goes to the TTY,
|
|
|
|
data from the TTY goes to STDOUT */
|
|
|
|
if (fds[i].fd == STDIN_FILENO)
|
|
|
|
destfd = ttyfd;
|
|
|
|
else
|
|
|
|
destfd = STDOUT_FILENO;
|
|
|
|
|
|
|
|
while (sent < got) {
|
|
|
|
int done;
|
Use safewrite in place of write, in many cases.
Also add "make syntax-check" rules to ensure no new uses sneak in.
There are many uses of write like this:
if (write (fd, xml, towrite) != towrite)
return -1;
The problem is that the syscall can succeed, yet write less than
the requested number of bytes, so the caller should retry
rather than simply failing.
This patch changes most of them to use util.c's safewrite wrapper,
which encapsulates the process. Also, there were a few cases in
which the retry loop was open-coded, and I replaced those, too.
* Makefile.maint (sc_avoid_write): New rule, to avoid recurrence.
* .x-sc_avoid_write: New file. Record two legitimate exemptions.
* qemud/qemud.c (sig_handler, qemudClientWriteBuf): Use safewrite, not write.
* src/conf.c (__virConfWriteFile): Likewise.
* src/qemu_conf.c (qemudSaveConfig, qemudSaveNetworkConfig): Likewise.
* src/qemu_driver.c (qemudWaitForMonitor, qemudStartVMDaemon)
(qemudVMData, PROC_IP_FORWARD): Likewise.
* proxy/libvirt_proxy.c: Include "util.h".
(proxyWriteClientSocket): Use safewrite.
* src/test.c (testDomainSave, testDomainCoreDump): Likewise.
* src/proxy_internal.c (virProxyWriteClientSocket): Likewise.
* src/virsh.c: Include "util-lib.h".
(vshOutputLogFile): Use safewrite.
* src/console.c: Include "util-lib.h".
(vshRunConsole): Use safewrite.
2008-02-22 15:55:04 +00:00
|
|
|
if ((done = safewrite(destfd, buf + sent, got - sent))
|
|
|
|
<= 0) {
|
2010-01-19 13:17:20 +00:00
|
|
|
VIR_ERROR(_("failure writing output: %s"),
|
2009-05-20 13:37:30 +00:00
|
|
|
strerror(errno));
|
2007-01-26 11:54:29 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
sent += done;
|
|
|
|
}
|
|
|
|
} else { /* Any other flag from poll is an error condition */
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
done:
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
|
|
|
|
/* Restore original signal handlers */
|
|
|
|
signal(SIGQUIT, old_sigpipe);
|
|
|
|
signal(SIGQUIT, old_sighup);
|
|
|
|
signal(SIGQUIT, old_sigint);
|
|
|
|
signal(SIGQUIT, old_sigterm);
|
|
|
|
signal(SIGQUIT, old_sigquit);
|
|
|
|
|
|
|
|
/* Put STDIN back into the (sane?) state we found
|
|
|
|
it in before starting */
|
|
|
|
tcsetattr(STDIN_FILENO, TCSAFLUSH, &ttyattr);
|
|
|
|
|
|
|
|
closetty:
|
|
|
|
close(ttyfd);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2010-05-03 20:44:12 +00:00
|
|
|
#endif /* !WIN32 */
|