xref: /kvmtool/term.c (revision 2aa76b2616bcace1d668a879d834679dfa00dc8c)
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