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 |
if (vm->type == VM_TYPE_C7200) |
595 |
c7200_boot_ios(VM_C7200(vm)); |
596 |
break; |
597 |
|
598 |
/* Show the device list */ |
599 |
case 'd': |
600 |
dev_show_list(vm); |
601 |
pci_dev_show_list(vm->pci_bus[0]); |
602 |
pci_dev_show_list(vm->pci_bus[1]); |
603 |
break; |
604 |
|
605 |
/* Show info about Port Adapters or Network Modules */ |
606 |
case 'p': |
607 |
switch(vm->type) { |
608 |
case VM_TYPE_C3600: |
609 |
c3600_nm_show_all_info(VM_C3600(vm)); |
610 |
break; |
611 |
case VM_TYPE_C7200: |
612 |
c7200_pa_show_all_info(VM_C7200(vm)); |
613 |
break; |
614 |
} |
615 |
break; |
616 |
|
617 |
/* Dump the MIPS registers */ |
618 |
case 'r': |
619 |
if (cpu0) mips64_dump_regs(cpu0); |
620 |
break; |
621 |
|
622 |
/* Dump the latest memory accesses */ |
623 |
case 'm': |
624 |
if (cpu0) memlog_dump(cpu0); |
625 |
break; |
626 |
|
627 |
/* Suspend CPU emulation */ |
628 |
case 's': |
629 |
vm_suspend(vm); |
630 |
break; |
631 |
|
632 |
/* Resume CPU emulation */ |
633 |
case 'u': |
634 |
vm_resume(vm); |
635 |
break; |
636 |
|
637 |
/* Dump the MIPS TLB */ |
638 |
case 't': |
639 |
if (cpu0) tlb_dump(cpu0); |
640 |
break; |
641 |
|
642 |
/* Show information about JIT compiled pages */ |
643 |
case 'b': |
644 |
if (cpu0) { |
645 |
printf("\nCPU0: %u JIT compiled pages " |
646 |
"[Exec Area Pages: %lu/%lu]\n", |
647 |
cpu0->compiled_pages, |
648 |
(u_long)cpu0->exec_page_alloc, |
649 |
(u_long)cpu0->exec_page_count); |
650 |
} |
651 |
break; |
652 |
|
653 |
/* MTS64 cache statistics */ |
654 |
case 'l': |
655 |
if (cpu0) { |
656 |
mts64_show_stats(cpu0); |
657 |
} |
658 |
break; |
659 |
|
660 |
/* Extract the configuration from the NVRAM */ |
661 |
case 'c': |
662 |
vm_ios_save_config(vm); |
663 |
break; |
664 |
|
665 |
/* Non-JIT mode statistics */ |
666 |
case 'j': |
667 |
if (cpu0) mips64_dump_stats(cpu0); |
668 |
break; |
669 |
|
670 |
/* Determine an idle pointer counter */ |
671 |
case 'i': |
672 |
if (cpu0) { |
673 |
mips64_get_idling_pc(cpu0); |
674 |
} |
675 |
break; |
676 |
|
677 |
/* Experimentations / Tests */ |
678 |
case 'x': |
679 |
if (cpu0) { |
680 |
/* IRQ triggering */ |
681 |
mips64_set_irq(cpu0,2/*C7200_PA_MGMT_IRQ*/); |
682 |
//mips64_set_irq(cpu0,C7200_PA_MGMT_IRQ); |
683 |
} |
684 |
break; |
685 |
|
686 |
/* Twice Ctrl + ']' (0x1d, 29), or Alt-Gr + '*' (0xb3, 179) */ |
687 |
case 0x1d: |
688 |
case 0xb3: |
689 |
vtty_store(vtty,c); |
690 |
break; |
691 |
|
692 |
default: |
693 |
printf("\n\nInstance %s (ID %d)\n\n",vm->name,vm->instance_id); |
694 |
|
695 |
printf("o - Show the VM object list\n" |
696 |
"d - Show the device list\n" |
697 |
"r - Dump MIPS CPU registers\n" |
698 |
"t - Dump MIPS TLB entries\n" |
699 |
"m - Dump the latest memory accesses\n" |
700 |
"s - Suspend CPU emulation\n" |
701 |
"u - Resume CPU emulation\n" |
702 |
"q - Quit the emulator\n" |
703 |
"k - Reboot the virtual machine\n" |
704 |
"b - Show info about JIT compiled pages\n" |
705 |
"l - MTS64 cache statistics\n" |
706 |
"c - Write IOS configuration to disk\n" |
707 |
"j - Non-JIT mode statistics\n" |
708 |
"i - Determine an idling pointer counter\n" |
709 |
"x - Experimentations (can crash the box!)\n" |
710 |
"^] - Send ^]\n" |
711 |
"Other - This help\n"); |
712 |
} |
713 |
} |
714 |
|
715 |
|
716 |
/* Read a character (until one is available) and store it in buffer */ |
717 |
static void vtty_read_and_store(vtty_t *vtty) |
718 |
{ |
719 |
int c; |
720 |
|
721 |
/* wait until we get a character input */ |
722 |
c = vtty_read(vtty); |
723 |
|
724 |
/* if read error, do nothing */ |
725 |
if (c < 0) return; |
726 |
|
727 |
if (!vtty->terminal_support) { |
728 |
vtty_store(vtty,c); |
729 |
return; |
730 |
} |
731 |
|
732 |
switch(vtty->input_state) { |
733 |
case VTTY_INPUT_TEXT : |
734 |
switch(c) { |
735 |
case 0x1b: |
736 |
vtty->input_state = VTTY_INPUT_VT1; |
737 |
return; |
738 |
|
739 |
/* Ctrl + ']' (0x1d, 29), or Alt-Gr + '*' (0xb3, 179) */ |
740 |
case 0x1d: |
741 |
case 0xb3: |
742 |
vtty->input_state = VTTY_INPUT_REMOTE; |
743 |
return; |
744 |
case IAC : |
745 |
vtty->input_state = VTTY_INPUT_TELNET; |
746 |
return; |
747 |
case 0: /* NULL - Must be ignored - generated by Linux telnet */ |
748 |
case 10: /* LF (Line Feed) - Must be ignored on Windows platform */ |
749 |
return; |
750 |
default: |
751 |
/* Store a standard character */ |
752 |
vtty_store(vtty,c); |
753 |
return; |
754 |
} |
755 |
|
756 |
case VTTY_INPUT_VT1 : |
757 |
switch(c) { |
758 |
case 0x5b: |
759 |
vtty->input_state = VTTY_INPUT_VT2; |
760 |
return; |
761 |
default: |
762 |
vtty_store(vtty,0x1b); |
763 |
vtty_store(vtty,c); |
764 |
} |
765 |
vtty->input_state = VTTY_INPUT_TEXT; |
766 |
return; |
767 |
|
768 |
case VTTY_INPUT_VT2 : |
769 |
switch(c) { |
770 |
case 0x41: /* Up Arrow */ |
771 |
vtty_store(vtty,16); |
772 |
break; |
773 |
case 0x42: /* Down Arrow */ |
774 |
vtty_store(vtty,14); |
775 |
break; |
776 |
case 0x43: /* Right Arrow */ |
777 |
vtty_store(vtty,6); |
778 |
break; |
779 |
case 0x44: /* Left Arrow */ |
780 |
vtty_store(vtty,2); |
781 |
break; |
782 |
default: |
783 |
vtty_store(vtty,0x5b); |
784 |
vtty_store(vtty,0x1b); |
785 |
vtty_store(vtty,c); |
786 |
break; |
787 |
} |
788 |
vtty->input_state = VTTY_INPUT_TEXT; |
789 |
return; |
790 |
|
791 |
case VTTY_INPUT_REMOTE : |
792 |
remote_control(vtty, c); |
793 |
vtty->input_state = VTTY_INPUT_TEXT; |
794 |
return; |
795 |
|
796 |
case VTTY_INPUT_TELNET : |
797 |
vtty->telnet_cmd = c; |
798 |
switch(c) { |
799 |
case WILL: |
800 |
case WONT: |
801 |
case DO: |
802 |
case DONT: |
803 |
vtty->input_state = VTTY_INPUT_TELNET_IYOU; |
804 |
return; |
805 |
case SB : |
806 |
vtty->telnet_cmd = c; |
807 |
vtty->input_state = VTTY_INPUT_TELNET_SB1; |
808 |
return; |
809 |
case SE: |
810 |
break; |
811 |
case IAC : |
812 |
vtty_store(vtty, IAC); |
813 |
break; |
814 |
} |
815 |
vtty->input_state = VTTY_INPUT_TEXT; |
816 |
return; |
817 |
|
818 |
case VTTY_INPUT_TELNET_IYOU : |
819 |
vtty->telnet_opt = c; |
820 |
/* if telnet client can support ttype, ask it to send ttype string */ |
821 |
if ((vtty->telnet_cmd == WILL) && |
822 |
(vtty->telnet_opt == TELOPT_TTYPE)) |
823 |
{ |
824 |
vtty_put_char(vtty, IAC); |
825 |
vtty_put_char(vtty, SB); |
826 |
vtty_put_char(vtty, TELOPT_TTYPE); |
827 |
vtty_put_char(vtty, TELQUAL_SEND); |
828 |
vtty_put_char(vtty, IAC); |
829 |
vtty_put_char(vtty, SE); |
830 |
} |
831 |
vtty->input_state = VTTY_INPUT_TEXT; |
832 |
return; |
833 |
|
834 |
case VTTY_INPUT_TELNET_SB1 : |
835 |
vtty->telnet_opt = c; |
836 |
vtty->input_state = VTTY_INPUT_TELNET_SB2; |
837 |
return; |
838 |
|
839 |
case VTTY_INPUT_TELNET_SB2 : |
840 |
vtty->telnet_qual = c; |
841 |
if ((vtty->telnet_opt == TELOPT_TTYPE) && |
842 |
(vtty->telnet_qual == TELQUAL_IS)) |
843 |
vtty->input_state = VTTY_INPUT_TELNET_SB_TTYPE; |
844 |
else |
845 |
vtty->input_state = VTTY_INPUT_TELNET_NEXT; |
846 |
return; |
847 |
|
848 |
case VTTY_INPUT_TELNET_SB_TTYPE : |
849 |
/* parse ttype string: first char is sufficient */ |
850 |
/* if client is xterm or vt, set the title bar */ |
851 |
if ((c == 'x') || (c == 'X') || (c == 'v') || (c == 'V')) { |
852 |
fprintf(vtty->fstream, "\033]0;Dynamips(%i): %s, %s\07", |
853 |
vtty->vm->instance_id, vtty->vm->name, vtty->name); |
854 |
} |
855 |
vtty->input_state = VTTY_INPUT_TELNET_NEXT; |
856 |
return; |
857 |
|
858 |
case VTTY_INPUT_TELNET_NEXT : |
859 |
/* ignore all chars until next IAC */ |
860 |
if (c == IAC) |
861 |
vtty->input_state = VTTY_INPUT_TELNET; |
862 |
return; |
863 |
} |
864 |
} |
865 |
|
866 |
/* Read a character from the buffer (-1 if the buffer is empty) */ |
867 |
int vtty_get_char(vtty_t *vtty) |
868 |
{ |
869 |
u_char c; |
870 |
|
871 |
VTTY_LOCK(vtty); |
872 |
|
873 |
if (vtty->read_ptr == vtty->write_ptr) { |
874 |
VTTY_UNLOCK(vtty); |
875 |
return(-1); |
876 |
} |
877 |
|
878 |
c = vtty->buffer[vtty->read_ptr++]; |
879 |
|
880 |
if (vtty->read_ptr == VTTY_BUFFER_SIZE) |
881 |
vtty->read_ptr = 0; |
882 |
|
883 |
VTTY_UNLOCK(vtty); |
884 |
return(c); |
885 |
} |
886 |
|
887 |
/* Returns TRUE if a character is available in buffer */ |
888 |
int vtty_is_char_avail(vtty_t *vtty) |
889 |
{ |
890 |
int res; |
891 |
|
892 |
VTTY_LOCK(vtty); |
893 |
res = (vtty->read_ptr != vtty->write_ptr); |
894 |
VTTY_UNLOCK(vtty); |
895 |
return(res); |
896 |
} |
897 |
|
898 |
/* put char to vtty */ |
899 |
void vtty_put_char(vtty_t *vtty, char ch) |
900 |
{ |
901 |
switch(vtty->type) { |
902 |
case VTTY_TYPE_NONE: |
903 |
break; |
904 |
|
905 |
case VTTY_TYPE_TERM: |
906 |
fwrite(&ch, 1, 1, vtty->fstream); |
907 |
break; |
908 |
|
909 |
case VTTY_TYPE_TCP: |
910 |
if ((vtty->state == VTTY_STATE_TCP_RUNNING) && |
911 |
(fwrite(&ch, 1, 1, vtty->fstream) != 1)) |
912 |
{ |
913 |
vm_log(vtty->vm,"VTTY","%s: put char %d failed (%s)\n", |
914 |
vtty->name,(int)ch,strerror(errno)); |
915 |
} |
916 |
break; |
917 |
|
918 |
case VTTY_TYPE_SERIAL: |
919 |
if (write(vtty->fd,&ch,1) != 1) { |
920 |
vm_log(vtty->vm,"VTTY","%s: put char 0x%x failed (%s)\n", |
921 |
vtty->name,(int)ch,strerror(errno)); |
922 |
} |
923 |
break; |
924 |
|
925 |
default: |
926 |
fprintf(stderr,"vtty_put_char: bad vtty type %d\n",vtty->type); |
927 |
exit(1); |
928 |
} |
929 |
} |
930 |
|
931 |
/* Flush VTTY output */ |
932 |
void vtty_flush(vtty_t *vtty) |
933 |
{ |
934 |
switch(vtty->type) { |
935 |
case VTTY_TYPE_TERM: |
936 |
case VTTY_TYPE_TCP: |
937 |
if (vtty->fstream) |
938 |
fflush(vtty->fstream); |
939 |
break; |
940 |
|
941 |
case VTTY_TYPE_SERIAL: |
942 |
fsync(vtty->fd); |
943 |
break; |
944 |
} |
945 |
} |
946 |
|
947 |
/* VTTY thread */ |
948 |
static void *vtty_thread_main(void *arg) |
949 |
{ |
950 |
vtty_t *vtty; |
951 |
struct timeval tv; |
952 |
int fd,fd_max,res; |
953 |
fd_set rfds; |
954 |
|
955 |
for(;;) { |
956 |
VTTY_LIST_LOCK(); |
957 |
|
958 |
/* Build the FD set */ |
959 |
FD_ZERO(&rfds); |
960 |
fd_max = -1; |
961 |
for(vtty=vtty_list;vtty;vtty=vtty->next) { |
962 |
if (!vtty->select_fd) |
963 |
continue; |
964 |
|
965 |
if ((fd = *vtty->select_fd) < 0) |
966 |
continue; |
967 |
|
968 |
if (fd > fd_max) fd_max = fd; |
969 |
FD_SET(fd,&rfds); |
970 |
} |
971 |
VTTY_LIST_UNLOCK(); |
972 |
|
973 |
/* Wait for incoming data */ |
974 |
tv.tv_sec = 0; |
975 |
tv.tv_usec = 50 * 1000; /* 50 ms */ |
976 |
res = select(fd_max+1,&rfds,NULL,NULL,&tv); |
977 |
|
978 |
if (res == -1) { |
979 |
if (errno != EINTR) { |
980 |
perror("vtty_thread: select"); |
981 |
|
982 |
for(vtty=vtty_list;vtty;vtty=vtty->next) { |
983 |
fprintf(stderr," %-15s: %s, FD %d\n", |
984 |
vtty->vm->name,vtty->name,vtty->fd); |
985 |
} |
986 |
} |
987 |
continue; |
988 |
} |
989 |
|
990 |
/* Examine active FDs and call user handlers */ |
991 |
VTTY_LIST_LOCK(); |
992 |
for(vtty=vtty_list;vtty;vtty=vtty->next) { |
993 |
if (!vtty->select_fd) |
994 |
continue; |
995 |
|
996 |
if ((fd = *vtty->select_fd) < 0) |
997 |
continue; |
998 |
|
999 |
if (FD_ISSET(fd,&rfds)) { |
1000 |
vtty_read_and_store(vtty); |
1001 |
|
1002 |
if (vtty->read_notifier != NULL) |
1003 |
vtty->read_notifier(vtty); |
1004 |
} |
1005 |
|
1006 |
/* Flush any pending output */ |
1007 |
if (!vtty->managed_flush) |
1008 |
vtty_flush(vtty); |
1009 |
} |
1010 |
VTTY_LIST_UNLOCK(); |
1011 |
} |
1012 |
|
1013 |
return NULL; |
1014 |
} |
1015 |
|
1016 |
/* Initialize the VTTY thread */ |
1017 |
int vtty_init(void) |
1018 |
{ |
1019 |
if (pthread_create(&vtty_thread,NULL,vtty_thread_main,NULL)) { |
1020 |
perror("vtty: pthread_create"); |
1021 |
return(-1); |
1022 |
} |
1023 |
|
1024 |
return(0); |
1025 |
} |