Files
kvmtool/term.c
T
Sasha Levin 1add4b76d6 kvm tools: Allow remapping guest TTY into host PTS
This patch adds the '-tty' option to 'kvm run' which allows the user to
remap a guest TTY into a PTS on the host.

Usage:
        'kvm run --tty [id]'

The tty will be mapped to a pts and will be printed on the screen:
        '  Info: Assigned terminal 1 to pty /dev/pts/X'

At this point, it is possible to communicate with the guest using that pty.

This is useful for debugging guest kernel using KGDB:

1. Run the guest:
        'kvm run -k [vmlinuz] -p "kgdboc=ttyS1 kgdbwait" --tty 1'

And see which PTY got assigned to ttyS1.

2. Run GDB on the host:
        'gdb [vmlinuz]'

3. Connect to the guest (from within GDB):
        'target remote /dev/pty/X'

4. Start debugging! (enter 'continue' to continue boot).

Cc: David Evensky <evensky@dancer.ca.sandia.gov>
Signed-off-by: Sasha Levin <levinsasha928@gmail.com>
2015-06-01 16:39:47 +01:00

171 lines
2.9 KiB
C

#include <poll.h>
#include <stdbool.h>
#include <termios.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/uio.h>
#include <signal.h>
#include <pty.h>
#include <utmp.h>
#include "kvm/read-write.h"
#include "kvm/term.h"
#include "kvm/util.h"
#include "kvm/kvm.h"
#include "kvm/kvm-cpu.h"
#define TERM_FD_IN 0
#define TERM_FD_OUT 1
extern struct kvm *kvm;
static struct termios orig_term;
int term_escape_char = 0x01; /* ctrl-a is used for escape */
bool term_got_escape = false;
int active_console;
int term_fds[4][2];
int term_getc(int who, int term)
{
int c;
if (who != active_console)
return -1;
if (read_in_full(term_fds[term][TERM_FD_IN], &c, 1) < 0)
return -1;
c &= 0xff;
if (term_got_escape) {
term_got_escape = false;
if (c == 'x')
kvm_cpu__reboot();
if (c == term_escape_char)
return c;
}
if (c == term_escape_char) {
term_got_escape = true;
return -1;
}
return c;
}
int term_putc(int who, char *addr, int cnt, int term)
{
int ret;
if (who != active_console)
return -1;
while (cnt--) {
ret = write(term_fds[term][TERM_FD_OUT], addr++, 1);
if (ret < 0)
return 0;
}
return cnt;
}
int term_getc_iov(int who, struct iovec *iov, int iovcnt, int term)
{
int c;
if (who != active_console)
return 0;
c = term_getc(who, term);
if (c < 0)
return 0;
*((int *)iov[TERM_FD_IN].iov_base) = c;
return sizeof(char);
}
int term_putc_iov(int who, struct iovec *iov, int iovcnt, int term)
{
if (who != active_console)
return 0;
return writev(term_fds[term][TERM_FD_OUT], iov, iovcnt);
}
bool term_readable(int who, int term)
{
struct pollfd pollfd = (struct pollfd) {
.fd = term_fds[term][TERM_FD_IN],
.events = POLLIN,
.revents = 0,
};
if (who != active_console)
return false;
return poll(&pollfd, 1, 0) > 0;
}
static void term_cleanup(void)
{
int i;
for (i = 0; i < 4; i++)
tcsetattr(term_fds[i][TERM_FD_IN], TCSANOW, &orig_term);
}
static void term_sig_cleanup(int sig)
{
term_cleanup();
signal(sig, SIG_DFL);
raise(sig);
}
void term_set_tty(int term)
{
struct termios orig_term;
int master, slave;
char new_pty[PATH_MAX];
if (tcgetattr(STDIN_FILENO, &orig_term) < 0)
die("unable to save initial standard input settings");
orig_term.c_lflag &= ~(ICANON | ECHO | ISIG);
if (openpty(&master, &slave, new_pty, &orig_term, NULL) < 0)
return;
close(slave);
pr_info("Assigned terminal %d to pty %s\n", term, new_pty);
term_fds[term][TERM_FD_IN] = term_fds[term][TERM_FD_OUT] = master;
}
void term_init(void)
{
struct termios term;
int i;
if (tcgetattr(STDIN_FILENO, &orig_term) < 0)
die("unable to save initial standard input settings");
term = orig_term;
term.c_lflag &= ~(ICANON | ECHO | ISIG);
tcsetattr(STDIN_FILENO, TCSANOW, &term);
for (i = 0; i < 4; i++)
if (term_fds[i][TERM_FD_IN] == 0) {
term_fds[i][TERM_FD_IN] = STDIN_FILENO;
term_fds[i][TERM_FD_OUT] = STDOUT_FILENO;
}
signal(SIGTERM, term_sig_cleanup);
atexit(term_cleanup);
}