#include #include #include #include #include #include #include #include #include #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__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_iflag &= ~(ICRNL); 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);