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