mirror of
https://github.com/clearlinux/kvmtool.git
synced 2026-06-15 18:05:49 +00:00
4d7f252f21
According to git grep they can be static. term_got_escape can be static too, and we can even move it into term_getc(). "int term_escape_char" doesn't make sense at least until we allow to redefine it, turn it into preprocessor constant. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Will Deacon <will.deacon@arm.com>
213 lines
4.0 KiB
C
213 lines
4.0 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
|
|
|
|
static struct termios orig_term;
|
|
|
|
static int term_fds[TERM_MAX_DEVS][2];
|
|
|
|
static pthread_t term_poll_thread;
|
|
|
|
/* ctrl-a is used for escape */
|
|
#define term_escape_char 0x01
|
|
|
|
int term_getc(struct kvm *kvm, int term)
|
|
{
|
|
static bool term_got_escape = false;
|
|
unsigned char c;
|
|
|
|
if (read_in_full(term_fds[term][TERM_FD_IN], &c, 1) < 0)
|
|
return -1;
|
|
|
|
if (term_got_escape) {
|
|
term_got_escape = false;
|
|
if (c == 'x')
|
|
kvm_cpu__reboot(kvm);
|
|
if (c == term_escape_char)
|
|
return c;
|
|
}
|
|
|
|
if (c == term_escape_char) {
|
|
term_got_escape = true;
|
|
return -1;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
int term_putc(char *addr, int cnt, int term)
|
|
{
|
|
int ret;
|
|
int num_remaining = cnt;
|
|
|
|
while (num_remaining) {
|
|
ret = write(term_fds[term][TERM_FD_OUT], addr, num_remaining);
|
|
if (ret < 0)
|
|
return cnt - num_remaining;
|
|
num_remaining -= ret;
|
|
addr += ret;
|
|
}
|
|
|
|
return cnt;
|
|
}
|
|
|
|
int term_getc_iov(struct kvm *kvm, struct iovec *iov, int iovcnt, int term)
|
|
{
|
|
int c;
|
|
|
|
c = term_getc(kvm, term);
|
|
|
|
if (c < 0)
|
|
return 0;
|
|
|
|
*((char *)iov[TERM_FD_IN].iov_base) = (char)c;
|
|
|
|
return sizeof(char);
|
|
}
|
|
|
|
int term_putc_iov(struct iovec *iov, int iovcnt, int term)
|
|
{
|
|
return writev(term_fds[term][TERM_FD_OUT], iov, iovcnt);
|
|
}
|
|
|
|
bool term_readable(int term)
|
|
{
|
|
struct pollfd pollfd = (struct pollfd) {
|
|
.fd = term_fds[term][TERM_FD_IN],
|
|
.events = POLLIN,
|
|
.revents = 0,
|
|
};
|
|
int err;
|
|
|
|
err = poll(&pollfd, 1, 0);
|
|
return (err > 0 && (pollfd.revents & POLLIN));
|
|
}
|
|
|
|
static void *term_poll_thread_loop(void *param)
|
|
{
|
|
struct pollfd fds[TERM_MAX_DEVS];
|
|
struct kvm *kvm = (struct kvm *) param;
|
|
int i;
|
|
|
|
kvm__set_thread_name("term-poll");
|
|
|
|
for (i = 0; i < TERM_MAX_DEVS; i++) {
|
|
fds[i].fd = term_fds[i][TERM_FD_IN];
|
|
fds[i].events = POLLIN;
|
|
fds[i].revents = 0;
|
|
}
|
|
|
|
while (1) {
|
|
/* Poll with infinite timeout */
|
|
if(poll(fds, TERM_MAX_DEVS, -1) < 1)
|
|
break;
|
|
kvm__arch_read_term(kvm);
|
|
}
|
|
|
|
die("term_poll_thread_loop: error polling device fds %d\n", errno);
|
|
return NULL;
|
|
}
|
|
|
|
static void term_cleanup(void)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < TERM_MAX_DEVS; 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);
|
|
}
|
|
|
|
static 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;
|
|
}
|
|
|
|
int tty_parser(const struct option *opt, const char *arg, int unset)
|
|
{
|
|
int tty = atoi(arg);
|
|
|
|
term_set_tty(tty);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int term_init(struct kvm *kvm)
|
|
{
|
|
struct termios term;
|
|
int i, r;
|
|
|
|
for (i = 0; i < TERM_MAX_DEVS; 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;
|
|
}
|
|
|
|
if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO))
|
|
return 0;
|
|
|
|
r = tcgetattr(STDIN_FILENO, &orig_term);
|
|
if (r < 0) {
|
|
pr_warning("unable to save initial standard input settings");
|
|
return r;
|
|
}
|
|
|
|
|
|
term = orig_term;
|
|
term.c_lflag &= ~(ICANON | ECHO | ISIG);
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &term);
|
|
|
|
|
|
/* Use our own blocking thread to read stdin, don't require a tick */
|
|
if(pthread_create(&term_poll_thread, NULL, term_poll_thread_loop,kvm))
|
|
die("Unable to create console input poll thread\n");
|
|
|
|
signal(SIGTERM, term_sig_cleanup);
|
|
atexit(term_cleanup);
|
|
|
|
return 0;
|
|
}
|
|
dev_init(term_init);
|
|
|
|
static int term_exit(struct kvm *kvm)
|
|
{
|
|
return 0;
|
|
}
|
|
dev_exit(term_exit);
|