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