mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-25 01:34:11 +03:00
189 lines
5.5 KiB
C
189 lines
5.5 KiB
C
|
/*
|
||
|
* console.c: A dumb serial console client
|
||
|
*
|
||
|
* Copyright (C) 2007 Red Hat, Inc.
|
||
|
*
|
||
|
* 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>
|
||
|
*/
|
||
|
|
||
|
#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>
|
||
|
|
||
|
#include "console.h"
|
||
|
#include "internal.h"
|
||
|
|
||
|
/* ie Ctrl-] as per telnet */
|
||
|
#define CTRL_CLOSE_BRACKET '\35'
|
||
|
|
||
|
static int got_signal = 0;
|
||
|
static void do_signal(int sig ATTRIBUTE_UNUSED) {
|
||
|
got_signal = 1;
|
||
|
}
|
||
|
|
||
|
int virRunConsole(const char *tty) {
|
||
|
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) {
|
||
|
fprintf(stderr, _("unable to open tty %s: %s\n"),
|
||
|
tty, strerror(errno));
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* 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 (tcgetattr(STDIN_FILENO, &ttyattr) < 0) {
|
||
|
fprintf(stderr, _("unable to get tty attributes: %s\n"),
|
||
|
strerror(errno));
|
||
|
goto closetty;
|
||
|
}
|
||
|
|
||
|
rawattr = ttyattr;
|
||
|
cfmakeraw(&rawattr);
|
||
|
|
||
|
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &rawattr) < 0) {
|
||
|
fprintf(stderr, _("unable to set tty attributes: %s\n"),
|
||
|
strerror(errno));
|
||
|
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;
|
||
|
|
||
|
fprintf(stderr, _("failure waiting for I/O: %s\n"),
|
||
|
strerror(errno));
|
||
|
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) {
|
||
|
fprintf(stderr, _("failure reading input: %s\n"),
|
||
|
strerror(errno));
|
||
|
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;
|
||
|
if ((done = write(destfd, buf + sent, got - sent)) <= 0) {
|
||
|
fprintf(stderr, _("failure writing output: %s\n"),
|
||
|
strerror(errno));
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Local variables:
|
||
|
* indent-tabs-mode: nil
|
||
|
* c-indent-level: 4
|
||
|
* c-basic-offset: 4
|
||
|
* tab-width: 4
|
||
|
* End:
|
||
|
*/
|