xref: /kvmtool/term.c (revision fc189e9c6833c64d6a29e9196c689f41fca3b241)
1 #include <poll.h>
2 #include <stdbool.h>
3 #include <termios.h>
4 #include <stdio.h>
5 #include <unistd.h>
6 #include <sys/uio.h>
7 #include <signal.h>
8 #include <pty.h>
9 #include <utmp.h>
10 
11 #include "kvm/read-write.h"
12 #include "kvm/term.h"
13 #include "kvm/util.h"
14 #include "kvm/kvm.h"
15 #include "kvm/kvm-cpu.h"
16 
17 #define TERM_FD_IN      0
18 #define TERM_FD_OUT     1
19 
20 static struct termios	orig_term;
21 
22 static int term_fds[TERM_MAX_DEVS][2];
23 
24 static pthread_t term_poll_thread;
25 
26 /* ctrl-a is used for escape */
27 #define term_escape_char	0x01
28 
term_getc(struct kvm * kvm,int term)29 int term_getc(struct kvm *kvm, int term)
30 {
31 	static bool term_got_escape = false;
32 	unsigned char c;
33 
34 	if (read_in_full(term_fds[term][TERM_FD_IN], &c, 1) < 0)
35 		return -1;
36 
37 	if (term_got_escape) {
38 		term_got_escape = false;
39 		if (c == 'x')
40 			kvm__reboot(kvm);
41 		if (c == term_escape_char)
42 			return c;
43 	}
44 
45 	if (c == term_escape_char) {
46 		term_got_escape = true;
47 		return -1;
48 	}
49 
50 	return c;
51 }
52 
term_putc(char * addr,int cnt,int term)53 int term_putc(char *addr, int cnt, int term)
54 {
55 	int ret;
56 	int num_remaining = cnt;
57 
58 	while (num_remaining) {
59 		ret = write(term_fds[term][TERM_FD_OUT], addr, num_remaining);
60 		if (ret < 0)
61 			return cnt - num_remaining;
62 		num_remaining -= ret;
63 		addr += ret;
64 	}
65 
66 	return cnt;
67 }
68 
term_getc_iov(struct kvm * kvm,struct iovec * iov,int iovcnt,int term)69 int term_getc_iov(struct kvm *kvm, struct iovec *iov, int iovcnt, int term)
70 {
71 	int c;
72 
73 	c = term_getc(kvm, term);
74 
75 	if (c < 0)
76 		return 0;
77 
78 	*((char *)iov[TERM_FD_IN].iov_base)	= (char)c;
79 
80 	return sizeof(char);
81 }
82 
term_putc_iov(struct iovec * iov,int iovcnt,int term)83 int term_putc_iov(struct iovec *iov, int iovcnt, int term)
84 {
85 	return writev(term_fds[term][TERM_FD_OUT], iov, iovcnt);
86 }
87 
term_readable(int term)88 bool term_readable(int term)
89 {
90 	struct pollfd pollfd = (struct pollfd) {
91 		.fd	= term_fds[term][TERM_FD_IN],
92 		.events	= POLLIN,
93 		.revents = 0,
94 	};
95 	int err;
96 
97 	err = poll(&pollfd, 1, 0);
98 	return (err > 0 && (pollfd.revents & POLLIN));
99 }
100 
term_poll_thread_loop(void * param)101 static void *term_poll_thread_loop(void *param)
102 {
103 	struct pollfd fds[TERM_MAX_DEVS];
104 	struct kvm *kvm = (struct kvm *) param;
105 	int i;
106 
107 	kvm__set_thread_name("term-poll");
108 
109 	for (i = 0; i < TERM_MAX_DEVS; i++) {
110 		fds[i].fd = term_fds[i][TERM_FD_IN];
111 		fds[i].events = POLLIN;
112 		fds[i].revents = 0;
113 	}
114 
115 	while (1) {
116 		/* Poll with infinite timeout */
117 		if(poll(fds, TERM_MAX_DEVS, -1) < 1)
118 			break;
119 		kvm__arch_read_term(kvm);
120 	}
121 
122 	die("term_poll_thread_loop: error polling device fds %d\n", errno);
123 	return NULL;
124 }
125 
term_cleanup(void)126 static void term_cleanup(void)
127 {
128 	int i;
129 
130 	for (i = 0; i < TERM_MAX_DEVS; i++)
131 		tcsetattr(term_fds[i][TERM_FD_IN], TCSANOW, &orig_term);
132 }
133 
term_sig_cleanup(int sig)134 static void term_sig_cleanup(int sig)
135 {
136 	term_cleanup();
137 	signal(sig, SIG_DFL);
138 	raise(sig);
139 }
140 
term_set_tty(int term)141 static void term_set_tty(int term)
142 {
143 	struct termios orig_term;
144 	int master, slave;
145 	char new_pty[PATH_MAX];
146 
147 	if (tcgetattr(STDIN_FILENO, &orig_term) < 0)
148 		die("unable to save initial standard input settings");
149 
150 	orig_term.c_lflag &= ~(ICANON | ECHO | ISIG);
151 
152 	if (openpty(&master, &slave, new_pty, &orig_term, NULL) < 0)
153 		return;
154 
155 	close(slave);
156 
157 	pr_info("Assigned terminal %d to pty %s\n", term, new_pty);
158 
159 	term_fds[term][TERM_FD_IN] = term_fds[term][TERM_FD_OUT] = master;
160 }
161 
tty_parser(const struct option * opt,const char * arg,int unset)162 int tty_parser(const struct option *opt, const char *arg, int unset)
163 {
164 	int tty = atoi(arg);
165 
166 	term_set_tty(tty);
167 
168 	return 0;
169 }
170 
term_init(struct kvm * kvm)171 static int term_init(struct kvm *kvm)
172 {
173 	struct termios term;
174 	int i, r;
175 
176 	for (i = 0; i < TERM_MAX_DEVS; i++)
177 		if (term_fds[i][TERM_FD_IN] == 0) {
178 			term_fds[i][TERM_FD_IN] = STDIN_FILENO;
179 			term_fds[i][TERM_FD_OUT] = STDOUT_FILENO;
180 		}
181 
182 	if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO))
183 		return 0;
184 
185 	r = tcgetattr(STDIN_FILENO, &orig_term);
186 	if (r < 0) {
187 		pr_warning("unable to save initial standard input settings");
188 		return r;
189 	}
190 
191 
192 	term = orig_term;
193 	term.c_iflag &= ~(ICRNL);
194 	term.c_lflag &= ~(ICANON | ECHO | ISIG);
195 	tcsetattr(STDIN_FILENO, TCSANOW, &term);
196 
197 
198 	/* Use our own blocking thread to read stdin, don't require a tick */
199 	if(pthread_create(&term_poll_thread, NULL, term_poll_thread_loop,kvm))
200 		die("Unable to create console input poll thread\n");
201 
202 	signal(SIGTERM, term_sig_cleanup);
203 	atexit(term_cleanup);
204 
205 	return 0;
206 }
207 dev_init(term_init);
208 
term_exit(struct kvm * kvm)209 static int term_exit(struct kvm *kvm)
210 {
211 	return 0;
212 }
213 dev_exit(term_exit);
214