1b4405289SAsias He #include <poll.h> 2b4405289SAsias He #include <stdbool.h> 3b4405289SAsias He #include <termios.h> 4b4405289SAsias He #include <stdio.h> 5b4405289SAsias He #include <unistd.h> 6b4405289SAsias He #include <sys/uio.h> 7f2a0b6a7SAnton Vorontsov #include <signal.h> 81add4b76SSasha Levin #include <pty.h> 91add4b76SSasha Levin #include <utmp.h> 10b4405289SAsias He 11b4405289SAsias He #include "kvm/read-write.h" 12b4405289SAsias He #include "kvm/term.h" 13b4405289SAsias He #include "kvm/util.h" 145358b0e6SSasha Levin #include "kvm/kvm.h" 15629ad9d3SSasha Levin #include "kvm/kvm-cpu.h" 16b4405289SAsias He 171add4b76SSasha Levin #define TERM_FD_IN 0 181add4b76SSasha Levin #define TERM_FD_OUT 1 191add4b76SSasha Levin 20b4405289SAsias He static struct termios orig_term; 21b4405289SAsias He 224d7f252fSOleg Nesterov static int term_fds[TERM_MAX_DEVS][2]; 231add4b76SSasha Levin 2412c406a8SJonathan Austin static pthread_t term_poll_thread; 2512c406a8SJonathan Austin 264d7f252fSOleg Nesterov /* ctrl-a is used for escape */ 274d7f252fSOleg Nesterov #define term_escape_char 0x01 284d7f252fSOleg Nesterov 294346fd8fSSasha Levin int term_getc(struct kvm *kvm, int term) 30b4405289SAsias He { 314d7f252fSOleg Nesterov static bool term_got_escape = false; 32e382487aSMatt Evans unsigned char c; 33b4405289SAsias He 341add4b76SSasha Levin if (read_in_full(term_fds[term][TERM_FD_IN], &c, 1) < 0) 35b4405289SAsias He return -1; 3616588013SAsias He 3716588013SAsias He if (term_got_escape) { 3816588013SAsias He term_got_escape = false; 39c23d9748SSasha Levin if (c == 'x') 40*2aa76b26SWill Deacon kvm__reboot(kvm); 4116588013SAsias He if (c == term_escape_char) 4216588013SAsias He return c; 4316588013SAsias He } 4416588013SAsias He 4516588013SAsias He if (c == term_escape_char) { 4616588013SAsias He term_got_escape = true; 4716588013SAsias He return -1; 4816588013SAsias He } 4916588013SAsias He 50b4405289SAsias He return c; 51b4405289SAsias He } 52b4405289SAsias He 532651ea58SSasha Levin int term_putc(char *addr, int cnt, int term) 54b4405289SAsias He { 551add4b76SSasha Levin int ret; 56a5f4f263SDavid Daney int num_remaining = cnt; 571add4b76SSasha Levin 58a5f4f263SDavid Daney while (num_remaining) { 59a5f4f263SDavid Daney ret = write(term_fds[term][TERM_FD_OUT], addr, num_remaining); 601add4b76SSasha Levin if (ret < 0) 61492aa8f3SAndreas Herrmann return cnt - num_remaining; 62a5f4f263SDavid Daney num_remaining -= ret; 63a5f4f263SDavid Daney addr += ret; 641add4b76SSasha Levin } 65b4405289SAsias He 66b4405289SAsias He return cnt; 67b4405289SAsias He } 68b4405289SAsias He 694346fd8fSSasha Levin int term_getc_iov(struct kvm *kvm, struct iovec *iov, int iovcnt, int term) 70b4405289SAsias He { 7116588013SAsias He int c; 72b4405289SAsias He 734346fd8fSSasha Levin c = term_getc(kvm, term); 7416588013SAsias He 7516588013SAsias He if (c < 0) 7616588013SAsias He return 0; 7716588013SAsias He 78e382487aSMatt Evans *((char *)iov[TERM_FD_IN].iov_base) = (char)c; 7916588013SAsias He 80fbf15b30SAsias He return sizeof(char); 81b4405289SAsias He } 82b4405289SAsias He 832651ea58SSasha Levin int term_putc_iov(struct iovec *iov, int iovcnt, int term) 84b4405289SAsias He { 851add4b76SSasha Levin return writev(term_fds[term][TERM_FD_OUT], iov, iovcnt); 86b4405289SAsias He } 87b4405289SAsias He 882651ea58SSasha Levin bool term_readable(int term) 89b4405289SAsias He { 90b4405289SAsias He struct pollfd pollfd = (struct pollfd) { 911add4b76SSasha Levin .fd = term_fds[term][TERM_FD_IN], 92b4405289SAsias He .events = POLLIN, 93b4405289SAsias He .revents = 0, 94b4405289SAsias He }; 953ad9fad4SMarc Zyngier int err; 96b4405289SAsias He 973ad9fad4SMarc Zyngier err = poll(&pollfd, 1, 0); 983ad9fad4SMarc Zyngier return (err > 0 && (pollfd.revents & POLLIN)); 99b4405289SAsias He } 100b4405289SAsias He 10112c406a8SJonathan Austin static void *term_poll_thread_loop(void *param) 10212c406a8SJonathan Austin { 10312c406a8SJonathan Austin struct pollfd fds[TERM_MAX_DEVS]; 10412c406a8SJonathan Austin struct kvm *kvm = (struct kvm *) param; 10512c406a8SJonathan Austin int i; 10612c406a8SJonathan Austin 107edb4a8a0SSuzuki K. Poulose kvm__set_thread_name("term-poll"); 108edb4a8a0SSuzuki K. Poulose 10912c406a8SJonathan Austin for (i = 0; i < TERM_MAX_DEVS; i++) { 11012c406a8SJonathan Austin fds[i].fd = term_fds[i][TERM_FD_IN]; 11112c406a8SJonathan Austin fds[i].events = POLLIN; 11212c406a8SJonathan Austin fds[i].revents = 0; 11312c406a8SJonathan Austin } 11412c406a8SJonathan Austin 11512c406a8SJonathan Austin while (1) { 11612c406a8SJonathan Austin /* Poll with infinite timeout */ 11712c406a8SJonathan Austin if(poll(fds, TERM_MAX_DEVS, -1) < 1) 11812c406a8SJonathan Austin break; 11912c406a8SJonathan Austin kvm__arch_read_term(kvm); 12012c406a8SJonathan Austin } 12112c406a8SJonathan Austin 12212c406a8SJonathan Austin die("term_poll_thread_loop: error polling device fds %d\n", errno); 12312c406a8SJonathan Austin return NULL; 12412c406a8SJonathan Austin } 12512c406a8SJonathan Austin 126b4405289SAsias He static void term_cleanup(void) 127b4405289SAsias He { 1281add4b76SSasha Levin int i; 1291add4b76SSasha Levin 1307787f0ffSJonathan Austin for (i = 0; i < TERM_MAX_DEVS; i++) 1311add4b76SSasha Levin tcsetattr(term_fds[i][TERM_FD_IN], TCSANOW, &orig_term); 132b4405289SAsias He } 133b4405289SAsias He 134f2a0b6a7SAnton Vorontsov static void term_sig_cleanup(int sig) 135f2a0b6a7SAnton Vorontsov { 136f2a0b6a7SAnton Vorontsov term_cleanup(); 137f2a0b6a7SAnton Vorontsov signal(sig, SIG_DFL); 138f2a0b6a7SAnton Vorontsov raise(sig); 139f2a0b6a7SAnton Vorontsov } 140f2a0b6a7SAnton Vorontsov 1414d7f252fSOleg Nesterov static void term_set_tty(int term) 1421add4b76SSasha Levin { 1431add4b76SSasha Levin struct termios orig_term; 1441add4b76SSasha Levin int master, slave; 1451add4b76SSasha Levin char new_pty[PATH_MAX]; 1461add4b76SSasha Levin 1471add4b76SSasha Levin if (tcgetattr(STDIN_FILENO, &orig_term) < 0) 1481add4b76SSasha Levin die("unable to save initial standard input settings"); 1491add4b76SSasha Levin 1501add4b76SSasha Levin orig_term.c_lflag &= ~(ICANON | ECHO | ISIG); 1511add4b76SSasha Levin 1521add4b76SSasha Levin if (openpty(&master, &slave, new_pty, &orig_term, NULL) < 0) 1531add4b76SSasha Levin return; 1541add4b76SSasha Levin 1551add4b76SSasha Levin close(slave); 1561add4b76SSasha Levin 1571add4b76SSasha Levin pr_info("Assigned terminal %d to pty %s\n", term, new_pty); 1581add4b76SSasha Levin 1591add4b76SSasha Levin term_fds[term][TERM_FD_IN] = term_fds[term][TERM_FD_OUT] = master; 1601add4b76SSasha Levin } 1611add4b76SSasha Levin 162dca745e4SSasha Levin int tty_parser(const struct option *opt, const char *arg, int unset) 163dca745e4SSasha Levin { 164dca745e4SSasha Levin int tty = atoi(arg); 165dca745e4SSasha Levin 166dca745e4SSasha Levin term_set_tty(tty); 167dca745e4SSasha Levin 168dca745e4SSasha Levin return 0; 169dca745e4SSasha Levin } 170dca745e4SSasha Levin 1714d7f252fSOleg Nesterov static int term_init(struct kvm *kvm) 172b4405289SAsias He { 173b4405289SAsias He struct termios term; 174dca745e4SSasha Levin int i, r; 175b4405289SAsias He 1767787f0ffSJonathan Austin for (i = 0; i < TERM_MAX_DEVS; i++) 17736ad4dcfSMichael Ellerman if (term_fds[i][TERM_FD_IN] == 0) { 17836ad4dcfSMichael Ellerman term_fds[i][TERM_FD_IN] = STDIN_FILENO; 17936ad4dcfSMichael Ellerman term_fds[i][TERM_FD_OUT] = STDOUT_FILENO; 18036ad4dcfSMichael Ellerman } 18136ad4dcfSMichael Ellerman 18236ad4dcfSMichael Ellerman if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) 18336ad4dcfSMichael Ellerman return 0; 18436ad4dcfSMichael Ellerman 185dca745e4SSasha Levin r = tcgetattr(STDIN_FILENO, &orig_term); 186dca745e4SSasha Levin if (r < 0) { 187dca745e4SSasha Levin pr_warning("unable to save initial standard input settings"); 188dca745e4SSasha Levin return r; 189dca745e4SSasha Levin } 190dca745e4SSasha Levin 191b4405289SAsias He 192b4405289SAsias He term = orig_term; 193b4405289SAsias He term.c_lflag &= ~(ICANON | ECHO | ISIG); 194b4405289SAsias He tcsetattr(STDIN_FILENO, TCSANOW, &term); 195b4405289SAsias He 19612c406a8SJonathan Austin 19712c406a8SJonathan Austin /* Use our own blocking thread to read stdin, don't require a tick */ 19812c406a8SJonathan Austin if(pthread_create(&term_poll_thread, NULL, term_poll_thread_loop,kvm)) 19912c406a8SJonathan Austin die("Unable to create console input poll thread\n"); 20012c406a8SJonathan Austin 201f2a0b6a7SAnton Vorontsov signal(SIGTERM, term_sig_cleanup); 202b4405289SAsias He atexit(term_cleanup); 203dca745e4SSasha Levin 204dca745e4SSasha Levin return 0; 205dca745e4SSasha Levin } 20649a8afd1SSasha Levin dev_init(term_init); 207dca745e4SSasha Levin 2084d7f252fSOleg Nesterov static int term_exit(struct kvm *kvm) 209dca745e4SSasha Levin { 210dca745e4SSasha Levin return 0; 211b4405289SAsias He } 21249a8afd1SSasha Levin dev_exit(term_exit); 213