1 |
dpavlin |
1 |
/* |
2 |
|
|
* Cisco 7200 (Predator) simulation platform. |
3 |
|
|
* Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) |
4 |
|
|
* |
5 |
|
|
* Virtual console TTY. |
6 |
|
|
* |
7 |
|
|
* "Interactive" part idea by Mtve. |
8 |
|
|
* TCP console added by Mtve. |
9 |
|
|
* Serial console by Peter Ross (suxen_drol@hotmail.com) |
10 |
|
|
*/ |
11 |
|
|
|
12 |
|
|
/* By default, Cygwin supports only 64 FDs with select()! */ |
13 |
|
|
#ifdef __CYGWIN__ |
14 |
|
|
#define FD_SETSIZE 1024 |
15 |
|
|
#endif |
16 |
|
|
|
17 |
|
|
#include <stdio.h> |
18 |
|
|
#include <stdlib.h> |
19 |
|
|
#include <string.h> |
20 |
|
|
#include <unistd.h> |
21 |
|
|
#include <sys/types.h> |
22 |
|
|
#include <sys/socket.h> |
23 |
|
|
#include <termios.h> |
24 |
|
|
#include <fcntl.h> |
25 |
|
|
#include <errno.h> |
26 |
|
|
#include <assert.h> |
27 |
|
|
|
28 |
|
|
#include <arpa/telnet.h> |
29 |
|
|
|
30 |
|
|
#include "mips64.h" |
31 |
|
|
#include "cp0.h" |
32 |
|
|
#include "cpu.h" |
33 |
|
|
#include "dynamips.h" |
34 |
|
|
#include "mips64_exec.h" |
35 |
|
|
#include "device.h" |
36 |
|
|
#include "memory.h" |
37 |
|
|
#include "dev_c7200.h" |
38 |
|
|
#include "dev_c3600.h" |
39 |
|
|
#include "dev_vtty.h" |
40 |
|
|
#include "vm.h" |
41 |
|
|
#include "utils.h" |
42 |
|
|
|
43 |
|
|
/* VTTY list */ |
44 |
|
|
static pthread_mutex_t vtty_list_mutex = PTHREAD_MUTEX_INITIALIZER; |
45 |
|
|
static vtty_t *vtty_list = NULL; |
46 |
|
|
static pthread_t vtty_thread; |
47 |
|
|
|
48 |
|
|
#define VTTY_LIST_LOCK() pthread_mutex_lock(&vtty_list_mutex); |
49 |
|
|
#define VTTY_LIST_UNLOCK() pthread_mutex_unlock(&vtty_list_mutex); |
50 |
|
|
|
51 |
|
|
static struct termios tios,tios_orig; |
52 |
|
|
|
53 |
|
|
/* Send Telnet command: WILL TELOPT_ECHO */ |
54 |
|
|
static void vtty_telnet_will_echo(vtty_t *vtty) |
55 |
|
|
{ |
56 |
|
|
u_char cmd[] = { IAC, WILL, TELOPT_ECHO }; |
57 |
|
|
write(vtty->fd,cmd,sizeof(cmd)); |
58 |
|
|
} |
59 |
|
|
|
60 |
|
|
/* Send Telnet command: Suppress Go-Ahead */ |
61 |
|
|
static void vtty_telnet_will_suppress_go_ahead(vtty_t *vtty) |
62 |
|
|
{ |
63 |
|
|
u_char cmd[] = { IAC, WILL, TELOPT_SGA }; |
64 |
|
|
write(vtty->fd,cmd,sizeof(cmd)); |
65 |
|
|
} |
66 |
|
|
|
67 |
|
|
/* Send Telnet command: Don't use linemode */ |
68 |
|
|
static void vtty_telnet_dont_linemode(vtty_t *vtty) |
69 |
|
|
{ |
70 |
|
|
u_char cmd[] = { IAC, DONT, TELOPT_LINEMODE }; |
71 |
|
|
write(vtty->fd,cmd,sizeof(cmd)); |
72 |
|
|
} |
73 |
|
|
|
74 |
|
|
/* Send Telnet command: does the client support terminal type message? */ |
75 |
|
|
static void vtty_telnet_do_ttype(vtty_t *vtty) |
76 |
|
|
{ |
77 |
|
|
u_char cmd[] = { IAC, DO, TELOPT_TTYPE }; |
78 |
|
|
write(vtty->fd,cmd,sizeof(cmd)); |
79 |
|
|
} |
80 |
|
|
|
81 |
|
|
/* Restore TTY original settings */ |
82 |
|
|
static void vtty_term_reset(void) |
83 |
|
|
{ |
84 |
|
|
tcsetattr(STDIN_FILENO,TCSANOW,&tios_orig); |
85 |
|
|
} |
86 |
|
|
|
87 |
|
|
/* Initialize real TTY */ |
88 |
|
|
static void vtty_term_init(void) |
89 |
|
|
{ |
90 |
|
|
tcgetattr(STDIN_FILENO,&tios); |
91 |
|
|
|
92 |
|
|
memcpy(&tios_orig,&tios,sizeof(struct termios)); |
93 |
|
|
atexit(vtty_term_reset); |
94 |
|
|
|
95 |
|
|
tios.c_cc[VTIME] = 0; |
96 |
|
|
tios.c_cc[VMIN] = 1; |
97 |
|
|
|
98 |
|
|
/* Disable Ctrl-C, Ctrl-S, Ctrl-Q and Ctrl-Z */ |
99 |
|
|
tios.c_cc[VINTR] = 0; |
100 |
|
|
tios.c_cc[VSTART] = 0; |
101 |
|
|
tios.c_cc[VSTOP] = 0; |
102 |
|
|
tios.c_cc[VSUSP] = 0; |
103 |
|
|
|
104 |
|
|
tios.c_lflag &= ~(ICANON|ECHO); |
105 |
|
|
tios.c_iflag &= ~ICRNL; |
106 |
|
|
tcsetattr(STDIN_FILENO, TCSANOW, &tios); |
107 |
|
|
tcflush(STDIN_FILENO,TCIFLUSH); |
108 |
|
|
} |
109 |
|
|
|
110 |
|
|
/* Wait for a TCP connection */ |
111 |
|
|
static int vtty_tcp_conn_wait(vtty_t *vtty) |
112 |
|
|
{ |
113 |
|
|
struct sockaddr_in serv; |
114 |
|
|
int one = 1; |
115 |
|
|
|
116 |
|
|
vtty->state = VTTY_STATE_TCP_INVALID; |
117 |
|
|
|
118 |
|
|
if ((vtty->accept_fd = socket(PF_INET,SOCK_STREAM,0)) < 0) { |
119 |
|
|
perror("vtty_tcp_waitcon: socket"); |
120 |
|
|
return(-1); |
121 |
|
|
} |
122 |
|
|
|
123 |
|
|
if (setsockopt(vtty->accept_fd,SOL_SOCKET,SO_REUSEADDR, |
124 |
|
|
&one,sizeof(one)) < 0) |
125 |
|
|
{ |
126 |
|
|
perror("vtty_tcp_waitcon: setsockopt(SO_REUSEADDR)"); |
127 |
|
|
goto error; |
128 |
|
|
} |
129 |
|
|
|
130 |
|
|
memset(&serv,0,sizeof(serv)); |
131 |
|
|
serv.sin_family = AF_INET; |
132 |
|
|
serv.sin_addr.s_addr = htonl(INADDR_ANY); |
133 |
|
|
serv.sin_port = htons(vtty->tcp_port); |
134 |
|
|
|
135 |
|
|
if (bind(vtty->accept_fd,(struct sockaddr *)&serv,sizeof(serv)) < 0) { |
136 |
|
|
perror("vtty_tcp_waitcon: bind"); |
137 |
|
|
goto error; |
138 |
|
|
} |
139 |
|
|
|
140 |
|
|
if (listen(vtty->accept_fd,1) < 0) { |
141 |
|
|
perror("vtty_tcp_waitcon: listen"); |
142 |
|
|
goto error; |
143 |
|
|
} |
144 |
|
|
|
145 |
|
|
vm_log(vtty->vm,"VTTY","%s: waiting connection on tcp port %d (FD %d)\n", |
146 |
|
|
vtty->name,vtty->tcp_port,vtty->accept_fd); |
147 |
|
|
|
148 |
|
|
vtty->select_fd = &vtty->accept_fd; |
149 |
|
|
vtty->state = VTTY_STATE_TCP_WAITING; |
150 |
|
|
return(0); |
151 |
|
|
|
152 |
|
|
error: |
153 |
|
|
close(vtty->accept_fd); |
154 |
|
|
vtty->accept_fd = -1; |
155 |
|
|
vtty->select_fd = NULL; |
156 |
|
|
return(-1); |
157 |
|
|
} |
158 |
|
|
|
159 |
|
|
/* Accept a TCP connection */ |
160 |
|
|
static int vtty_tcp_conn_accept(vtty_t *vtty) |
161 |
|
|
{ |
162 |
|
|
if ((vtty->fd = accept(vtty->accept_fd,NULL,NULL)) < 0) { |
163 |
|
|
fprintf(stderr,"vtty_tcp_conn_accept: accept on port %d failed %s\n", |
164 |
|
|
vtty->tcp_port,strerror(errno)); |
165 |
|
|
return(-1); |
166 |
|
|
} |
167 |
|
|
|
168 |
|
|
vm_log(vtty->vm,"VTTY","%s is now connected (accept_fd=%d,conn_fd=%d)\n", |
169 |
|
|
vtty->name,vtty->accept_fd,vtty->fd); |
170 |
|
|
|
171 |
|
|
/* Adapt Telnet settings */ |
172 |
|
|
if (vtty->terminal_support) { |
173 |
|
|
vtty_telnet_do_ttype(vtty); |
174 |
|
|
vtty_telnet_will_echo(vtty); |
175 |
|
|
vtty_telnet_will_suppress_go_ahead(vtty); |
176 |
|
|
vtty_telnet_dont_linemode(vtty); |
177 |
|
|
vtty->input_state = VTTY_INPUT_TELNET; |
178 |
|
|
} |
179 |
|
|
|
180 |
|
|
if (!(vtty->fstream = fdopen(vtty->fd, "wb"))) { |
181 |
|
|
close(vtty->fd); |
182 |
|
|
vtty->fd = -1; |
183 |
|
|
return(-1); |
184 |
|
|
} |
185 |
|
|
|
186 |
|
|
fprintf(vtty->fstream, |
187 |
|
|
"Connected to Dynamips VM \"%s\" (ID %u, type %s) - %s\r\n\r\n", |
188 |
|
|
vtty->vm->name, vtty->vm->instance_id, vm_get_type(vtty->vm), |
189 |
|
|
vtty->name); |
190 |
|
|
|
191 |
|
|
vtty->select_fd = &vtty->fd; |
192 |
|
|
vtty->state = VTTY_STATE_TCP_RUNNING; |
193 |
|
|
return(0); |
194 |
|
|
} |
195 |
|
|
|
196 |
|
|
/* |
197 |
|
|
* Parse serial interface descriptor string, return 0 if success |
198 |
|
|
* string takes the form "device:baudrate:databits:parity:stopbits:hwflow" |
199 |
|
|
* device is mandatory, other options are optional (default=9600,8,N,1,0). |
200 |
|
|
*/ |
201 |
|
|
int vtty_parse_serial_option(vtty_serial_option_t *option, char *optarg) |
202 |
|
|
{ |
203 |
|
|
char *array[6]; |
204 |
|
|
int count; |
205 |
|
|
|
206 |
|
|
if ((count = m_strtok(optarg, ':', array, 6)) < 1) { |
207 |
|
|
fprintf(stderr,"vtty_parse_serial_option: invalid string\n"); |
208 |
|
|
return(-1); |
209 |
|
|
} |
210 |
|
|
|
211 |
|
|
if (!(option->device = strdup(array[0]))) { |
212 |
|
|
fprintf(stderr,"vtty_parse_serial_option: unable to copy string\n"); |
213 |
|
|
return(-1); |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
option->baudrate = (count>1) ? atoi(array[1]) : 9600; |
217 |
|
|
option->databits = (count>2) ? atoi(array[2]) : 8; |
218 |
|
|
|
219 |
|
|
if (count > 3) { |
220 |
|
|
switch(*array[3]) { |
221 |
|
|
case 'o': |
222 |
|
|
case 'O': |
223 |
|
|
option->parity = 1; /* odd */ |
224 |
|
|
case 'e': |
225 |
|
|
case 'E': |
226 |
|
|
option->parity = 2; /* even */ |
227 |
|
|
default: |
228 |
|
|
option->parity = 0; /* none */ |
229 |
|
|
} |
230 |
|
|
} else { |
231 |
|
|
option->parity = 0; |
232 |
|
|
} |
233 |
|
|
|
234 |
|
|
option->stopbits = (count>4) ? atoi(array[4]) : 1; |
235 |
|
|
option->hwflow = (count>5) ? atoi(array[5]) : 0; |
236 |
|
|
return(0); |
237 |
|
|
} |
238 |
|
|
|
239 |
|
|
#if defined(__CYGWIN__) |
240 |
|
|
void cfmakeraw(struct termios *termios_p) { |
241 |
|
|
termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP| |
242 |
|
|
INLCR|IGNCR|ICRNL|IXON); |
243 |
|
|
termios_p->c_oflag &= ~OPOST; |
244 |
|
|
termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); |
245 |
|
|
termios_p->c_cflag &= ~(CSIZE|PARENB); |
246 |
|
|
termios_p->c_cflag |= CS8; |
247 |
|
|
} |
248 |
|
|
#endif |
249 |
|
|
|
250 |
|
|
/* |
251 |
|
|
* Setup serial port, return 0 if success. |
252 |
|
|
*/ |
253 |
|
|
static int vtty_serial_setup(vtty_t *vtty, const vtty_serial_option_t *option) |
254 |
|
|
{ |
255 |
|
|
struct termios tio; |
256 |
|
|
int tio_baudrate; |
257 |
|
|
|
258 |
|
|
if (tcgetattr(vtty->fd, &tio) != 0) { |
259 |
|
|
fprintf(stderr, "error: tcgetattr failed\n"); |
260 |
|
|
return(-1); |
261 |
|
|
} |
262 |
|
|
|
263 |
|
|
cfmakeraw(&tio); |
264 |
|
|
|
265 |
|
|
tio.c_cflag = 0 |
266 |
|
|
|CLOCAL // ignore modem control lines |
267 |
|
|
; |
268 |
|
|
|
269 |
|
|
tio.c_cflag &= ~CREAD; |
270 |
|
|
tio.c_cflag |= CREAD; |
271 |
|
|
|
272 |
|
|
switch(option->baudrate) { |
273 |
|
|
case 50 : tio_baudrate = B50; break; |
274 |
|
|
case 75 : tio_baudrate = B75; break; |
275 |
|
|
case 110 : tio_baudrate = B110; break; |
276 |
|
|
case 134 : tio_baudrate = B134; break; |
277 |
|
|
case 150 : tio_baudrate = B150; break; |
278 |
|
|
case 200 : tio_baudrate = B200; break; |
279 |
|
|
case 300 : tio_baudrate = B300; break; |
280 |
|
|
case 600 : tio_baudrate = B600; break; |
281 |
|
|
case 1200 : tio_baudrate = B1200; break; |
282 |
|
|
case 1800 : tio_baudrate = B1800; break; |
283 |
|
|
case 2400 : tio_baudrate = B2400; break; |
284 |
|
|
case 4800 : tio_baudrate = B4800; break; |
285 |
|
|
case 9600 : tio_baudrate = B9600; break; |
286 |
|
|
case 19200 : tio_baudrate = B19200; break; |
287 |
|
|
case 38400 : tio_baudrate = B38400; break; |
288 |
|
|
case 57600 : tio_baudrate = B57600; break; |
289 |
|
|
#if defined(B76800) |
290 |
|
|
case 76800 : tio_baudrate = B76800; break; |
291 |
|
|
#endif |
292 |
|
|
case 115200 : tio_baudrate = B115200; break; |
293 |
|
|
#if defined(B230400) |
294 |
|
|
case 230400 : tio_baudrate = B230400; break; |
295 |
|
|
#endif |
296 |
|
|
default: |
297 |
|
|
fprintf(stderr, "error: unsupported baudrate\n"); |
298 |
|
|
return(-1); |
299 |
|
|
} |
300 |
|
|
|
301 |
|
|
cfsetospeed(&tio, tio_baudrate); |
302 |
|
|
cfsetispeed(&tio, tio_baudrate); |
303 |
|
|
|
304 |
|
|
tio.c_cflag &= ~CSIZE; /* clear size flag */ |
305 |
|
|
switch(option->databits) { |
306 |
|
|
case 5 : tio.c_cflag |= CS5; break; |
307 |
|
|
case 6 : tio.c_cflag |= CS6; break; |
308 |
|
|
case 7 : tio.c_cflag |= CS7; break; |
309 |
|
|
case 8 : tio.c_cflag |= CS8; break; |
310 |
|
|
default : |
311 |
|
|
fprintf(stderr, "error: unsupported databits\n"); |
312 |
|
|
return(-1); |
313 |
|
|
} |
314 |
|
|
|
315 |
|
|
tio.c_iflag &= ~INPCK; /* clear parity flag */ |
316 |
|
|
tio.c_cflag &= ~(PARENB|PARODD); |
317 |
|
|
switch(option->parity) { |
318 |
|
|
case 0 : break; |
319 |
|
|
case 2 : tio.c_iflag|=INPCK; tio.c_cflag|=PARENB; break; /* even */ |
320 |
|
|
case 1 : tio.c_iflag|=INPCK; tio.c_cflag|=PARENB|PARODD; break; /* odd */ |
321 |
|
|
default: |
322 |
|
|
fprintf(stderr, "error: unsupported parity\n"); |
323 |
|
|
return(-1); |
324 |
|
|
} |
325 |
|
|
|
326 |
|
|
tio.c_cflag &= ~CSTOPB; /* clear stop flag */ |
327 |
|
|
switch(option->stopbits) { |
328 |
|
|
case 1 : break; |
329 |
|
|
case 2 : tio.c_cflag |= CSTOPB; break; |
330 |
|
|
default : |
331 |
|
|
fprintf(stderr, "error: unsupported stopbits\n"); |
332 |
|
|
return(-1); |
333 |
|
|
} |
334 |
|
|
|
335 |
|
|
#if defined(CRTSCTS) |
336 |
|
|
tio.c_cflag &= ~CRTSCTS; |
337 |
|
|
#endif |
338 |
|
|
#if defined(CNEW_RTSCTS) |
339 |
|
|
tio.c_cflag &= ~CNEW_RTSCTS; |
340 |
|
|
#endif |
341 |
|
|
if (option->hwflow) { |
342 |
|
|
#if defined(CRTSCTS) |
343 |
|
|
tio.c_cflag |= CRTSCTS; |
344 |
|
|
#else |
345 |
|
|
tio.c_cflag |= CNEW_RTSCTS; |
346 |
|
|
#endif |
347 |
|
|
} |
348 |
|
|
|
349 |
|
|
tio.c_cc[VTIME] = 0; |
350 |
|
|
tio.c_cc[VMIN] = 1; /* block read() until one character is available */ |
351 |
|
|
|
352 |
|
|
#if 0 |
353 |
|
|
/* not neccessary unless O_NONBLOCK used */ |
354 |
|
|
if (fcntl(vtty->fd, F_SETFL, 0) != 0) { /* enable blocking mode */ |
355 |
|
|
fprintf(stderr, "error: fnctl F_SETFL failed\n"); |
356 |
|
|
return(-1); |
357 |
|
|
} |
358 |
|
|
#endif |
359 |
|
|
|
360 |
|
|
if (tcflush(vtty->fd, TCIOFLUSH) != 0) { |
361 |
|
|
fprintf(stderr, "error: tcflush failed\n"); |
362 |
|
|
return(-1); |
363 |
|
|
} |
364 |
|
|
|
365 |
|
|
if (tcsetattr(vtty->fd, TCSANOW, &tio) != 0 ) { |
366 |
|
|
fprintf(stderr, "error: tcsetattr failed\n"); |
367 |
|
|
return(-1); |
368 |
|
|
} |
369 |
|
|
|
370 |
|
|
return(0); |
371 |
|
|
} |
372 |
|
|
|
373 |
|
|
/* Create a virtual tty */ |
374 |
|
|
vtty_t *vtty_create(vm_instance_t *vm,char *name,int type,int tcp_port, |
375 |
|
|
const vtty_serial_option_t *option) |
376 |
|
|
{ |
377 |
|
|
vtty_t *vtty; |
378 |
|
|
|
379 |
|
|
if (!(vtty = malloc(sizeof(*vtty)))) { |
380 |
|
|
fprintf(stderr,"VTTY: unable to create new virtual tty.\n"); |
381 |
|
|
return NULL; |
382 |
|
|
} |
383 |
|
|
|
384 |
|
|
memset(vtty,0,sizeof(*vtty)); |
385 |
|
|
vtty->name = name; |
386 |
|
|
vtty->type = type; |
387 |
|
|
vtty->vm = vm; |
388 |
|
|
vtty->fd = -1; |
389 |
|
|
vtty->fstream = NULL; |
390 |
|
|
vtty->accept_fd = -1; |
391 |
|
|
pthread_mutex_init(&vtty->lock,NULL); |
392 |
|
|
vtty->terminal_support = 1; |
393 |
|
|
vtty->input_state = VTTY_INPUT_TEXT; |
394 |
|
|
|
395 |
|
|
switch (vtty->type) { |
396 |
|
|
case VTTY_TYPE_NONE: |
397 |
|
|
vtty->select_fd = NULL; |
398 |
|
|
break; |
399 |
|
|
|
400 |
|
|
case VTTY_TYPE_TERM: |
401 |
|
|
vtty_term_init(); |
402 |
|
|
vtty->fd = STDIN_FILENO; |
403 |
|
|
vtty->select_fd = &vtty->fd; |
404 |
|
|
vtty->fstream = stdout; |
405 |
|
|
break; |
406 |
|
|
|
407 |
|
|
case VTTY_TYPE_TCP: |
408 |
|
|
vtty->tcp_port = tcp_port; |
409 |
|
|
vtty_tcp_conn_wait(vtty); |
410 |
|
|
break; |
411 |
|
|
|
412 |
|
|
case VTTY_TYPE_SERIAL: |
413 |
|
|
vtty->fd = open(option->device, O_RDWR); |
414 |
|
|
if (vtty->fd < 0) { |
415 |
|
|
fprintf(stderr,"VTTY: open failed\n"); |
416 |
|
|
free(vtty); |
417 |
|
|
return NULL; |
418 |
|
|
} |
419 |
|
|
if (vtty_serial_setup(vtty,option)) { |
420 |
|
|
fprintf(stderr,"VTTY: setup failed\n"); |
421 |
|
|
close(vtty->fd); |
422 |
|
|
free(vtty); |
423 |
|
|
return NULL; |
424 |
|
|
} |
425 |
|
|
vtty->select_fd = &vtty->fd; |
426 |
|
|
vtty->terminal_support = 0; |
427 |
|
|
break; |
428 |
|
|
|
429 |
|
|
default: |
430 |
|
|
fprintf(stderr,"tty_create: bad vtty type %d\n",vtty->type); |
431 |
|
|
return NULL; |
432 |
|
|
} |
433 |
|
|
|
434 |
|
|
/* Add this new VTTY to the list */ |
435 |
|
|
VTTY_LIST_LOCK(); |
436 |
|
|
vtty->next = vtty_list; |
437 |
|
|
vtty->pprev = &vtty_list; |
438 |
|
|
|
439 |
|
|
if (vtty_list != NULL) |
440 |
|
|
vtty_list->pprev = &vtty->next; |
441 |
|
|
|
442 |
|
|
vtty_list = vtty; |
443 |
|
|
VTTY_LIST_UNLOCK(); |
444 |
|
|
return vtty; |
445 |
|
|
} |
446 |
|
|
|
447 |
|
|
/* Delete a virtual tty */ |
448 |
|
|
void vtty_delete(vtty_t *vtty) |
449 |
|
|
{ |
450 |
|
|
if (vtty != NULL) { |
451 |
|
|
if (vtty->pprev != NULL) { |
452 |
|
|
VTTY_LIST_LOCK(); |
453 |
|
|
if (vtty->next) |
454 |
|
|
vtty->next->pprev = vtty->pprev; |
455 |
|
|
*(vtty->pprev) = vtty->next; |
456 |
|
|
VTTY_LIST_UNLOCK(); |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
if ((vtty->fstream) && (vtty->fstream != stdout)) |
460 |
|
|
fclose(vtty->fstream); |
461 |
|
|
|
462 |
|
|
/* We don't close FD 0 since it is stdin */ |
463 |
|
|
if (vtty->fd > 0) { |
464 |
|
|
vm_log(vtty->vm,"VTTY","%s: closing FD %d\n",vtty->name,vtty->fd); |
465 |
|
|
close(vtty->fd); |
466 |
|
|
} |
467 |
|
|
|
468 |
|
|
if (vtty->accept_fd != -1) { |
469 |
|
|
vm_log(vtty->vm,"VTTY","%s: closing accept FD %d\n", |
470 |
|
|
vtty->name,vtty->accept_fd); |
471 |
|
|
close(vtty->accept_fd); |
472 |
|
|
} |
473 |
|
|
|
474 |
|
|
free(vtty); |
475 |
|
|
} |
476 |
|
|
} |
477 |
|
|
|
478 |
|
|
/* Store a character in the FIFO buffer */ |
479 |
|
|
static int vtty_store(vtty_t *vtty,u_char c) |
480 |
|
|
{ |
481 |
|
|
u_int nwptr; |
482 |
|
|
|
483 |
|
|
VTTY_LOCK(vtty); |
484 |
|
|
nwptr = vtty->write_ptr + 1; |
485 |
|
|
if (nwptr == VTTY_BUFFER_SIZE) |
486 |
|
|
nwptr = 0; |
487 |
|
|
|
488 |
|
|
if (nwptr == vtty->read_ptr) { |
489 |
|
|
VTTY_UNLOCK(vtty); |
490 |
|
|
return(-1); |
491 |
|
|
} |
492 |
|
|
|
493 |
|
|
vtty->buffer[vtty->write_ptr] = c; |
494 |
|
|
vtty->write_ptr = nwptr; |
495 |
|
|
VTTY_UNLOCK(vtty); |
496 |
|
|
return(0); |
497 |
|
|
} |
498 |
|
|
|
499 |
|
|
/* Store CTRL+C in buffer */ |
500 |
|
|
int vtty_store_ctrlc(vtty_t *vtty) |
501 |
|
|
{ |
502 |
|
|
if (vtty) |
503 |
|
|
vtty_store(vtty,0x03); |
504 |
|
|
return(0); |
505 |
|
|
} |
506 |
|
|
|
507 |
|
|
/* |
508 |
|
|
* Read a character from the terminal. |
509 |
|
|
*/ |
510 |
|
|
static int vtty_term_read(vtty_t *vtty) |
511 |
|
|
{ |
512 |
|
|
u_char c; |
513 |
|
|
|
514 |
|
|
if (read(vtty->fd,&c,1) == 1) |
515 |
|
|
return(c); |
516 |
|
|
|
517 |
|
|
perror("read from vtty failed"); |
518 |
|
|
return(-1); |
519 |
|
|
} |
520 |
|
|
|
521 |
|
|
/* |
522 |
|
|
* Read a character from the TCP connection. |
523 |
|
|
*/ |
524 |
|
|
static int vtty_tcp_read(vtty_t *vtty) |
525 |
|
|
{ |
526 |
|
|
u_char c; |
527 |
|
|
|
528 |
|
|
switch(vtty->state) { |
529 |
|
|
case VTTY_STATE_TCP_RUNNING: |
530 |
|
|
if (read(vtty->fd,&c,1) == 1) |
531 |
|
|
return(c); |
532 |
|
|
|
533 |
|
|
/* Problem with the connection: Re-enter wait mode */ |
534 |
|
|
shutdown(vtty->fd,2); |
535 |
|
|
fclose(vtty->fstream); |
536 |
|
|
close(vtty->fd); |
537 |
|
|
vtty->fstream = NULL; |
538 |
|
|
vtty->fd = -1; |
539 |
|
|
vtty->select_fd = &vtty->accept_fd; |
540 |
|
|
vtty->state = VTTY_STATE_TCP_WAITING; |
541 |
|
|
return(-1); |
542 |
|
|
|
543 |
|
|
case VTTY_STATE_TCP_WAITING: |
544 |
|
|
/* A new connection has arrived */ |
545 |
|
|
vtty_tcp_conn_accept(vtty); |
546 |
|
|
return(-1); |
547 |
|
|
} |
548 |
|
|
|
549 |
|
|
/* Shouldn't happen... */ |
550 |
|
|
return(-1); |
551 |
|
|
} |
552 |
|
|
|
553 |
|
|
/* |
554 |
|
|
* Read a character from the virtual TTY. |
555 |
|
|
* |
556 |
|
|
* If the VTTY is a TCP connection, restart it in case of error. |
557 |
|
|
*/ |
558 |
|
|
static int vtty_read(vtty_t *vtty) |
559 |
|
|
{ |
560 |
|
|
switch(vtty->type) { |
561 |
|
|
case VTTY_TYPE_TERM: |
562 |
|
|
case VTTY_TYPE_SERIAL: |
563 |
|
|
return(vtty_term_read(vtty)); |
564 |
|
|
case VTTY_TYPE_TCP: |
565 |
|
|
return(vtty_tcp_read(vtty)); |
566 |
|
|
default: |
567 |
|
|
fprintf(stderr,"vtty_read: bad vtty type %d\n",vtty->type); |
568 |
|
|
return(-1); |
569 |
|
|
} |
570 |
|
|
|
571 |
|
|
/* NOTREACHED */ |
572 |
|
|
return(-1); |
573 |
|
|
} |
574 |
|
|
|
575 |
|
|
/* Process remote control char */ |
576 |
|
|
static void remote_control(vtty_t *vtty,u_char c) |
577 |
|
|
{ |
578 |
|
|
vm_instance_t *vm = vtty->vm; |
579 |
|
|
cpu_mips_t *cpu0 = cpu_group_find_id(vm->cpu_group,0); |
580 |
|
|
|
581 |
|
|
switch(c) { |
582 |
|
|
/* Show the object list */ |
583 |
|
|
case 'o': |
584 |
|
|
vm_object_dump(vm); |
585 |
|
|
break; |
586 |
|
|
|
587 |
|
|
/* Stop the MIPS VM */ |
588 |
|
|
case 'q': |
589 |
|
|
vm->status = VM_STATUS_SHUTDOWN; |
590 |
|
|
break; |
591 |
|
|
|
592 |
|
|
/* Reboot the C7200 */ |
593 |
|
|
case 'k': |
594 |
|
|
c7200_boot_ios(VM_C7200(vm)); |
595 |
|
|
break; |
596 |
|
|
|
597 |
|
|
/* Show the device list */ |
598 |
|
|
case 'd': |
599 |
|
|
dev_show_list(vm); |
600 |
|
|
pci_dev_show_list(vm->pci_bus[0]); |
601 |
|
|
pci_dev_show_list(vm->pci_bus[1]); |
602 |
|
|
break; |
603 |
|
|
|
604 |
|
|
/* Dump the MIPS registers */ |
605 |
|
|
case 'r': |
606 |
|
|
if (cpu0) mips64_dump_regs(cpu0); |
607 |
|
|
break; |
608 |
|
|
|
609 |
|
|
/* Dump the latest memory accesses */ |
610 |
|
|
case 'm': |
611 |
|
|
if (cpu0) memlog_dump(cpu0); |
612 |
|
|
break; |
613 |
|
|
|
614 |
|
|
/* Suspend CPU emulation */ |
615 |
|
|
case 's': |
616 |
|
|
vm_suspend(vm); |
617 |
|
|
break; |
618 |
|
|
|
619 |
|
|
/* Resume CPU emulation */ |
620 |
|
|
case 'u': |
621 |
|
|
vm_resume(vm); |
622 |
|
|
break; |
623 |
|
|
|
624 |
|
|
/* Dump the MIPS TLB */ |
625 |
|
|
case 't': |
626 |
|
|
if (cpu0) tlb_dump(cpu0); |
627 |
|
|
break; |
628 |
|
|
|
629 |
|
|
/* Show information about JIT compiled pages */ |
630 |
|
|
case 'b': |
631 |
|
|
if (cpu0) { |
632 |
|
|
printf("\nCPU0: %u JIT compiled pages " |
633 |
|
|
"[Exec Area Pages: %lu/%lu]\n", |
634 |
|
|
cpu0->compiled_pages, |
635 |
|
|
(u_long)cpu0->exec_page_alloc, |
636 |
|
|
(u_long)cpu0->exec_page_count); |
637 |
|
|
} |
638 |
|
|
break; |
639 |
|
|
|
640 |
|
|
/* MTS64 cache statistics */ |
641 |
|
|
case 'l': |
642 |
|
|
if (cpu0) { |
643 |
|
|
mts64_show_stats(cpu0); |
644 |
|
|
} |
645 |
|
|
break; |
646 |
|
|
|
647 |
|
|
/* Extract the configuration from the NVRAM */ |
648 |
|
|
case 'c': |
649 |
|
|
vm_ios_save_config(vm); |
650 |
|
|
break; |
651 |
|
|
|
652 |
|
|
/* Non-JIT mode statistics */ |
653 |
|
|
case 'j': |
654 |
|
|
if (cpu0) mips64_dump_stats(cpu0); |
655 |
|
|
break; |
656 |
|
|
|
657 |
|
|
/* Determine an idle pointer counter */ |
658 |
|
|
case 'i': |
659 |
|
|
if (cpu0) { |
660 |
|
|
mips64_get_idling_pc(cpu0); |
661 |
|
|
} |
662 |
|
|
break; |
663 |
|
|
|
664 |
|
|
/* Experimentations / Tests */ |
665 |
|
|
case 'x': |
666 |
|
|
if (cpu0) { |
667 |
|
|
/* IRQ triggering */ |
668 |
|
|
mips64_set_irq(cpu0,2/*C7200_PA_MGMT_IRQ*/); |
669 |
|
|
} |
670 |
|
|
break; |
671 |
|
|
|
672 |
|
|
/* Twice Ctrl + ']' (0x1d, 29), or Alt-Gr + '*' (0xb3, 179) */ |
673 |
|
|
case 0x1d: |
674 |
|
|
case 0xb3: |
675 |
|
|
vtty_store(vtty,c); |
676 |
|
|
break; |
677 |
|
|
|
678 |
|
|
default: |
679 |
|
|
printf("\n\nInstance %s (ID %d)\n\n",vm->name,vm->instance_id); |
680 |
|
|
|
681 |
|
|
printf("o - Show the VM object list\n" |
682 |
|
|
"d - Show the device list\n" |
683 |
|
|
"r - Dump MIPS CPU registers\n" |
684 |
|
|
"t - Dump MIPS TLB entries\n" |
685 |
|
|
"m - Dump the latest memory accesses\n" |
686 |
|
|
"s - Suspend CPU emulation\n" |
687 |
|
|
"u - Resume CPU emulation\n" |
688 |
|
|
"q - Quit the emulator\n" |
689 |
|
|
"k - Reboot the virtual machine\n" |
690 |
|
|
"b - Show info about JIT compiled pages\n" |
691 |
|
|
"l - MTS64 cache statistics\n" |
692 |
|
|
"c - Write IOS configuration to disk\n" |
693 |
|
|
"j - Non-JIT mode statistics\n" |
694 |
|
|
"i - Determine an idling pointer counter\n" |
695 |
|
|
"x - Experimentations (can crash the box!)\n" |
696 |
|
|
"^] - Send ^]\n" |
697 |
|
|
"Other - This help\n"); |
698 |
|
|
} |
699 |
|
|
} |
700 |
|
|
|
701 |
|
|
|
702 |
|
|
/* Read a character (until one is available) and store it in buffer */ |
703 |
|
|
static void vtty_read_and_store(vtty_t *vtty) |
704 |
|
|
{ |
705 |
|
|
int c; |
706 |
|
|
|
707 |
|
|
/* wait until we get a character input */ |
708 |
|
|
c = vtty_read(vtty); |
709 |
|
|
|
710 |
|
|
/* if read error, do nothing */ |
711 |
|
|
if (c < 0) return; |
712 |
|
|
|
713 |
|
|
if (!vtty->terminal_support) { |
714 |
|
|
vtty_store(vtty,c); |
715 |
|
|
return; |
716 |
|
|
} |
717 |
|
|
|
718 |
|
|
switch(vtty->input_state) { |
719 |
|
|
case VTTY_INPUT_TEXT : |
720 |
|
|
switch(c) { |
721 |
|
|
case 0x1b: |
722 |
|
|
vtty->input_state = VTTY_INPUT_VT1; |
723 |
|
|
return; |
724 |
|
|
|
725 |
|
|
/* Ctrl + ']' (0x1d, 29), or Alt-Gr + '*' (0xb3, 179) */ |
726 |
|
|
case 0x1d: |
727 |
|
|
case 0xb3: |
728 |
|
|
vtty->input_state = VTTY_INPUT_REMOTE; |
729 |
|
|
return; |
730 |
|
|
case IAC : |
731 |
|
|
vtty->input_state = VTTY_INPUT_TELNET; |
732 |
|
|
return; |
733 |
|
|
case 0: /* NULL - Must be ignored - generated by Linux telnet */ |
734 |
|
|
case 10: /* LF (Line Feed) - Must be ignored on Windows platform */ |
735 |
|
|
return; |
736 |
|
|
default: |
737 |
|
|
/* Store a standard character */ |
738 |
|
|
vtty_store(vtty,c); |
739 |
|
|
return; |
740 |
|
|
} |
741 |
|
|
|
742 |
|
|
case VTTY_INPUT_VT1 : |
743 |
|
|
switch(c) { |
744 |
|
|
case 0x5b: |
745 |
|
|
vtty->input_state = VTTY_INPUT_VT2; |
746 |
|
|
return; |
747 |
|
|
default: |
748 |
|
|
vtty_store(vtty,0x1b); |
749 |
|
|
vtty_store(vtty,c); |
750 |
|
|
} |
751 |
|
|
vtty->input_state = VTTY_INPUT_TEXT; |
752 |
|
|
return; |
753 |
|
|
|
754 |
|
|
case VTTY_INPUT_VT2 : |
755 |
|
|
switch(c) { |
756 |
|
|
case 0x41: /* Up Arrow */ |
757 |
|
|
vtty_store(vtty,16); |
758 |
|
|
break; |
759 |
|
|
case 0x42: /* Down Arrow */ |
760 |
|
|
vtty_store(vtty,14); |
761 |
|
|
break; |
762 |
|
|
case 0x43: /* Right Arrow */ |
763 |
|
|
vtty_store(vtty,6); |
764 |
|
|
break; |
765 |
|
|
case 0x44: /* Left Arrow */ |
766 |
|
|
vtty_store(vtty,2); |
767 |
|
|
break; |
768 |
|
|
default: |
769 |
|
|
vtty_store(vtty,0x5b); |
770 |
|
|
vtty_store(vtty,0x1b); |
771 |
|
|
vtty_store(vtty,c); |
772 |
|
|
break; |
773 |
|
|
} |
774 |
|
|
vtty->input_state = VTTY_INPUT_TEXT; |
775 |
|
|
return; |
776 |
|
|
|
777 |
|
|
case VTTY_INPUT_REMOTE : |
778 |
|
|
remote_control(vtty, c); |
779 |
|
|
vtty->input_state = VTTY_INPUT_TEXT; |
780 |
|
|
return; |
781 |
|
|
|
782 |
|
|
case VTTY_INPUT_TELNET : |
783 |
|
|
vtty->telnet_cmd = c; |
784 |
|
|
switch(c) { |
785 |
|
|
case WILL: |
786 |
|
|
case WONT: |
787 |
|
|
case DO: |
788 |
|
|
case DONT: |
789 |
|
|
vtty->input_state = VTTY_INPUT_TELNET_IYOU; |
790 |
|
|
return; |
791 |
|
|
case SB : |
792 |
|
|
vtty->telnet_cmd = c; |
793 |
|
|
vtty->input_state = VTTY_INPUT_TELNET_SB1; |
794 |
|
|
return; |
795 |
|
|
case SE: |
796 |
|
|
break; |
797 |
|
|
case IAC : |
798 |
|
|
vtty_store(vtty, IAC); |
799 |
|
|
break; |
800 |
|
|
} |
801 |
|
|
vtty->input_state = VTTY_INPUT_TEXT; |
802 |
|
|
return; |
803 |
|
|
|
804 |
|
|
case VTTY_INPUT_TELNET_IYOU : |
805 |
|
|
vtty->telnet_opt = c; |
806 |
|
|
/* if telnet client can support ttype, ask it to send ttype string */ |
807 |
|
|
if ((vtty->telnet_cmd == WILL) && |
808 |
|
|
(vtty->telnet_opt == TELOPT_TTYPE)) |
809 |
|
|
{ |
810 |
|
|
vtty_put_char(vtty, IAC); |
811 |
|
|
vtty_put_char(vtty, SB); |
812 |
|
|
vtty_put_char(vtty, TELOPT_TTYPE); |
813 |
|
|
vtty_put_char(vtty, TELQUAL_SEND); |
814 |
|
|
vtty_put_char(vtty, IAC); |
815 |
|
|
vtty_put_char(vtty, SE); |
816 |
|
|
} |
817 |
|
|
vtty->input_state = VTTY_INPUT_TEXT; |
818 |
|
|
return; |
819 |
|
|
|
820 |
|
|
case VTTY_INPUT_TELNET_SB1 : |
821 |
|
|
vtty->telnet_opt = c; |
822 |
|
|
vtty->input_state = VTTY_INPUT_TELNET_SB2; |
823 |
|
|
return; |
824 |
|
|
|
825 |
|
|
case VTTY_INPUT_TELNET_SB2 : |
826 |
|
|
vtty->telnet_qual = c; |
827 |
|
|
if ((vtty->telnet_opt == TELOPT_TTYPE) && |
828 |
|
|
(vtty->telnet_qual == TELQUAL_IS)) |
829 |
|
|
vtty->input_state = VTTY_INPUT_TELNET_SB_TTYPE; |
830 |
|
|
else |
831 |
|
|
vtty->input_state = VTTY_INPUT_TELNET_NEXT; |
832 |
|
|
return; |
833 |
|
|
|
834 |
|
|
case VTTY_INPUT_TELNET_SB_TTYPE : |
835 |
|
|
/* parse ttype string: first char is sufficient */ |
836 |
|
|
/* if client is xterm or vt, set the title bar */ |
837 |
|
|
if ((c == 'x') || (c == 'X') || (c == 'v') || (c == 'V')) { |
838 |
|
|
fprintf(vtty->fstream, "\033]0;Dynamips(%i): %s, %s\07", |
839 |
|
|
vtty->vm->instance_id, vtty->vm->name, vtty->name); |
840 |
|
|
} |
841 |
|
|
vtty->input_state = VTTY_INPUT_TELNET_NEXT; |
842 |
|
|
return; |
843 |
|
|
|
844 |
|
|
case VTTY_INPUT_TELNET_NEXT : |
845 |
|
|
/* ignore all chars until next IAC */ |
846 |
|
|
if (c == IAC) |
847 |
|
|
vtty->input_state = VTTY_INPUT_TELNET; |
848 |
|
|
return; |
849 |
|
|
} |
850 |
|
|
} |
851 |
|
|
|
852 |
|
|
/* Read a character from the buffer (-1 if the buffer is empty) */ |
853 |
|
|
int vtty_get_char(vtty_t *vtty) |
854 |
|
|
{ |
855 |
|
|
u_char c; |
856 |
|
|
|
857 |
|
|
VTTY_LOCK(vtty); |
858 |
|
|
|
859 |
|
|
if (vtty->read_ptr == vtty->write_ptr) { |
860 |
|
|
VTTY_UNLOCK(vtty); |
861 |
|
|
return(-1); |
862 |
|
|
} |
863 |
|
|
|
864 |
|
|
c = vtty->buffer[vtty->read_ptr++]; |
865 |
|
|
|
866 |
|
|
if (vtty->read_ptr == VTTY_BUFFER_SIZE) |
867 |
|
|
vtty->read_ptr = 0; |
868 |
|
|
|
869 |
|
|
VTTY_UNLOCK(vtty); |
870 |
|
|
return(c); |
871 |
|
|
} |
872 |
|
|
|
873 |
|
|
/* Returns TRUE if a character is available in buffer */ |
874 |
|
|
int vtty_is_char_avail(vtty_t *vtty) |
875 |
|
|
{ |
876 |
|
|
int res; |
877 |
|
|
|
878 |
|
|
VTTY_LOCK(vtty); |
879 |
|
|
res = (vtty->read_ptr != vtty->write_ptr); |
880 |
|
|
VTTY_UNLOCK(vtty); |
881 |
|
|
return(res); |
882 |
|
|
} |
883 |
|
|
|
884 |
|
|
/* put char to vtty */ |
885 |
|
|
void vtty_put_char(vtty_t *vtty, char ch) |
886 |
|
|
{ |
887 |
|
|
switch(vtty->type) { |
888 |
|
|
case VTTY_TYPE_NONE: |
889 |
|
|
break; |
890 |
|
|
|
891 |
|
|
case VTTY_TYPE_TERM: |
892 |
|
|
fwrite(&ch, 1, 1, vtty->fstream); |
893 |
|
|
break; |
894 |
|
|
|
895 |
|
|
case VTTY_TYPE_TCP: |
896 |
|
|
if ((vtty->state == VTTY_STATE_TCP_RUNNING) && |
897 |
|
|
(fwrite(&ch, 1, 1, vtty->fstream) != 1)) |
898 |
|
|
{ |
899 |
|
|
vm_log(vtty->vm,"VTTY","%s: put char %d failed (%s)\n", |
900 |
|
|
vtty->name,(int)ch,strerror(errno)); |
901 |
|
|
} |
902 |
|
|
break; |
903 |
|
|
|
904 |
|
|
case VTTY_TYPE_SERIAL: |
905 |
|
|
if (write(vtty->fd,&ch,1) != 1) { |
906 |
|
|
vm_log(vtty->vm,"VTTY","%s: put char 0x%x failed (%s)\n", |
907 |
|
|
vtty->name,(int)ch,strerror(errno)); |
908 |
|
|
} |
909 |
|
|
break; |
910 |
|
|
|
911 |
|
|
default: |
912 |
|
|
fprintf(stderr,"vtty_put_char: bad vtty type %d\n",vtty->type); |
913 |
|
|
exit(1); |
914 |
|
|
} |
915 |
|
|
} |
916 |
|
|
|
917 |
|
|
/* Flush VTTY output */ |
918 |
|
|
void vtty_flush(vtty_t *vtty) |
919 |
|
|
{ |
920 |
|
|
switch(vtty->type) { |
921 |
|
|
case VTTY_TYPE_TERM: |
922 |
|
|
case VTTY_TYPE_TCP: |
923 |
|
|
if (vtty->fstream) |
924 |
|
|
fflush(vtty->fstream); |
925 |
|
|
break; |
926 |
|
|
|
927 |
|
|
case VTTY_TYPE_SERIAL: |
928 |
|
|
fsync(vtty->fd); |
929 |
|
|
break; |
930 |
|
|
} |
931 |
|
|
} |
932 |
|
|
|
933 |
|
|
/* VTTY thread */ |
934 |
|
|
static void *vtty_thread_main(void *arg) |
935 |
|
|
{ |
936 |
|
|
vtty_t *vtty; |
937 |
|
|
struct timeval tv; |
938 |
|
|
int fd,fd_max,res; |
939 |
|
|
fd_set rfds; |
940 |
|
|
|
941 |
|
|
for(;;) { |
942 |
|
|
VTTY_LIST_LOCK(); |
943 |
|
|
|
944 |
|
|
/* Build the FD set */ |
945 |
|
|
FD_ZERO(&rfds); |
946 |
|
|
fd_max = -1; |
947 |
|
|
for(vtty=vtty_list;vtty;vtty=vtty->next) { |
948 |
|
|
if (!vtty->select_fd) |
949 |
|
|
continue; |
950 |
|
|
|
951 |
|
|
if ((fd = *vtty->select_fd) < 0) |
952 |
|
|
continue; |
953 |
|
|
|
954 |
|
|
if (fd > fd_max) fd_max = fd; |
955 |
|
|
FD_SET(fd,&rfds); |
956 |
|
|
} |
957 |
|
|
VTTY_LIST_UNLOCK(); |
958 |
|
|
|
959 |
|
|
/* Wait for incoming data */ |
960 |
|
|
tv.tv_sec = 0; |
961 |
|
|
tv.tv_usec = 50 * 1000; /* 50 ms */ |
962 |
|
|
res = select(fd_max+1,&rfds,NULL,NULL,&tv); |
963 |
|
|
|
964 |
|
|
if (res == -1) { |
965 |
|
|
if (errno != EINTR) { |
966 |
|
|
perror("vtty_thread: select"); |
967 |
|
|
|
968 |
|
|
for(vtty=vtty_list;vtty;vtty=vtty->next) { |
969 |
|
|
fprintf(stderr," %-15s: %s, FD %d\n", |
970 |
|
|
vtty->vm->name,vtty->name,vtty->fd); |
971 |
|
|
} |
972 |
|
|
} |
973 |
|
|
continue; |
974 |
|
|
} |
975 |
|
|
|
976 |
|
|
/* Examine active FDs and call user handlers */ |
977 |
|
|
VTTY_LIST_LOCK(); |
978 |
|
|
for(vtty=vtty_list;vtty;vtty=vtty->next) { |
979 |
|
|
if (!vtty->select_fd) |
980 |
|
|
continue; |
981 |
|
|
|
982 |
|
|
if ((fd = *vtty->select_fd) < 0) |
983 |
|
|
continue; |
984 |
|
|
|
985 |
|
|
if (FD_ISSET(fd,&rfds)) { |
986 |
|
|
vtty_read_and_store(vtty); |
987 |
|
|
|
988 |
|
|
if (vtty->read_notifier != NULL) |
989 |
|
|
vtty->read_notifier(vtty); |
990 |
|
|
} |
991 |
|
|
|
992 |
|
|
/* Flush any pending output */ |
993 |
|
|
if (!vtty->managed_flush) |
994 |
|
|
vtty_flush(vtty); |
995 |
|
|
} |
996 |
|
|
VTTY_LIST_UNLOCK(); |
997 |
|
|
} |
998 |
|
|
|
999 |
|
|
return NULL; |
1000 |
|
|
} |
1001 |
|
|
|
1002 |
|
|
/* Initialize the VTTY thread */ |
1003 |
|
|
int vtty_init(void) |
1004 |
|
|
{ |
1005 |
|
|
if (pthread_create(&vtty_thread,NULL,vtty_thread_main,NULL)) { |
1006 |
|
|
perror("vtty: pthread_create"); |
1007 |
|
|
return(-1); |
1008 |
|
|
} |
1009 |
|
|
|
1010 |
|
|
return(0); |
1011 |
|
|
} |