📄 dev_vtty.c
字号:
/* * Cisco 7200 (Predator) simulation platform. * Copyright (c) 2005,2006 Christophe Fillot (cf@utc.fr) * * Virtual console TTY. * * "Interactive" part idea by Mtve. * TCP console added by Mtve. * Serial console by Peter Ross (suxen_drol@hotmail.com) *//* By default, Cygwin supports only 64 FDs with select()! */#ifdef __CYGWIN__#define FD_SETSIZE 1024#endif#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <termios.h>#include <fcntl.h>#include <errno.h>#include <assert.h>#include <arpa/telnet.h>#include "mips64.h"#include "cp0.h"#include "cpu.h"#include "dynamips.h"#include "mips64_exec.h"#include "device.h"#include "memory.h"#include "dev_c7200.h"#include "dev_c3600.h"#include "dev_vtty.h"#include "vm.h"#include "utils.h"/* VTTY list */static pthread_mutex_t vtty_list_mutex = PTHREAD_MUTEX_INITIALIZER;static vtty_t *vtty_list = NULL;static pthread_t vtty_thread;#define VTTY_LIST_LOCK() pthread_mutex_lock(&vtty_list_mutex);#define VTTY_LIST_UNLOCK() pthread_mutex_unlock(&vtty_list_mutex);static struct termios tios,tios_orig;/* Send Telnet command: WILL TELOPT_ECHO */static void vtty_telnet_will_echo(vtty_t *vtty){ u_char cmd[] = { IAC, WILL, TELOPT_ECHO }; write(vtty->fd,cmd,sizeof(cmd));}/* Send Telnet command: Suppress Go-Ahead */static void vtty_telnet_will_suppress_go_ahead(vtty_t *vtty){ u_char cmd[] = { IAC, WILL, TELOPT_SGA }; write(vtty->fd,cmd,sizeof(cmd));}/* Send Telnet command: Don't use linemode */static void vtty_telnet_dont_linemode(vtty_t *vtty){ u_char cmd[] = { IAC, DONT, TELOPT_LINEMODE }; write(vtty->fd,cmd,sizeof(cmd));}/* Send Telnet command: does the client support terminal type message? */static void vtty_telnet_do_ttype(vtty_t *vtty){ u_char cmd[] = { IAC, DO, TELOPT_TTYPE }; write(vtty->fd,cmd,sizeof(cmd));}/* Restore TTY original settings */static void vtty_term_reset(void){ tcsetattr(STDIN_FILENO,TCSANOW,&tios_orig);}/* Initialize real TTY */static void vtty_term_init(void){ tcgetattr(STDIN_FILENO,&tios); memcpy(&tios_orig,&tios,sizeof(struct termios)); atexit(vtty_term_reset); tios.c_cc[VTIME] = 0; tios.c_cc[VMIN] = 1; /* Disable Ctrl-C, Ctrl-S, Ctrl-Q and Ctrl-Z */ tios.c_cc[VINTR] = 0; tios.c_cc[VSTART] = 0; tios.c_cc[VSTOP] = 0; tios.c_cc[VSUSP] = 0; tios.c_lflag &= ~(ICANON|ECHO); tios.c_iflag &= ~ICRNL; tcsetattr(STDIN_FILENO, TCSANOW, &tios); tcflush(STDIN_FILENO,TCIFLUSH);}/* Wait for a TCP connection */static int vtty_tcp_conn_wait(vtty_t *vtty){ struct sockaddr_in serv; int one = 1; vtty->state = VTTY_STATE_TCP_INVALID; if ((vtty->accept_fd = socket(PF_INET,SOCK_STREAM,0)) < 0) { perror("vtty_tcp_waitcon: socket"); return(-1); } if (setsockopt(vtty->accept_fd,SOL_SOCKET,SO_REUSEADDR, &one,sizeof(one)) < 0) { perror("vtty_tcp_waitcon: setsockopt(SO_REUSEADDR)"); goto error; } memset(&serv,0,sizeof(serv)); serv.sin_family = AF_INET; serv.sin_addr.s_addr = htonl(INADDR_ANY); serv.sin_port = htons(vtty->tcp_port); if (bind(vtty->accept_fd,(struct sockaddr *)&serv,sizeof(serv)) < 0) { perror("vtty_tcp_waitcon: bind"); goto error; } if (listen(vtty->accept_fd,1) < 0) { perror("vtty_tcp_waitcon: listen"); goto error; } vm_log(vtty->vm,"VTTY","%s: waiting connection on tcp port %d (FD %d)\n", vtty->name,vtty->tcp_port,vtty->accept_fd); vtty->select_fd = &vtty->accept_fd; vtty->state = VTTY_STATE_TCP_WAITING; return(0); error: close(vtty->accept_fd); vtty->accept_fd = -1; vtty->select_fd = NULL; return(-1);}/* Accept a TCP connection */static int vtty_tcp_conn_accept(vtty_t *vtty){ if ((vtty->fd = accept(vtty->accept_fd,NULL,NULL)) < 0) { fprintf(stderr,"vtty_tcp_conn_accept: accept on port %d failed %s\n", vtty->tcp_port,strerror(errno)); return(-1); } vm_log(vtty->vm,"VTTY","%s is now connected (accept_fd=%d,conn_fd=%d)\n", vtty->name,vtty->accept_fd,vtty->fd); /* Adapt Telnet settings */ if (vtty->terminal_support) { vtty_telnet_do_ttype(vtty); vtty_telnet_will_echo(vtty); vtty_telnet_will_suppress_go_ahead(vtty); vtty_telnet_dont_linemode(vtty); vtty->input_state = VTTY_INPUT_TELNET; } if (!(vtty->fstream = fdopen(vtty->fd, "wb"))) { close(vtty->fd); vtty->fd = -1; return(-1); } fprintf(vtty->fstream, "Connected to Dynamips VM \"%s\" (ID %u, type %s) - %s\r\n\r\n", vtty->vm->name, vtty->vm->instance_id, vm_get_type(vtty->vm), vtty->name); vtty->select_fd = &vtty->fd; vtty->state = VTTY_STATE_TCP_RUNNING; return(0);}/* * Parse serial interface descriptor string, return 0 if success * string takes the form "device:baudrate:databits:parity:stopbits:hwflow" * device is mandatory, other options are optional (default=9600,8,N,1,0). */int vtty_parse_serial_option(vtty_serial_option_t *option, char *optarg){ char *array[6]; int count; if ((count = m_strtok(optarg, ':', array, 6)) < 1) { fprintf(stderr,"vtty_parse_serial_option: invalid string\n"); return(-1); } if (!(option->device = strdup(array[0]))) { fprintf(stderr,"vtty_parse_serial_option: unable to copy string\n"); return(-1); } option->baudrate = (count>1) ? atoi(array[1]) : 9600; option->databits = (count>2) ? atoi(array[2]) : 8; if (count > 3) { switch(*array[3]) { case 'o': case 'O': option->parity = 1; /* odd */ case 'e': case 'E': option->parity = 2; /* even */ default: option->parity = 0; /* none */ } } else { option->parity = 0; } option->stopbits = (count>4) ? atoi(array[4]) : 1; option->hwflow = (count>5) ? atoi(array[5]) : 0; return(0);}#if defined(__CYGWIN__) || defined(SUNOS)void cfmakeraw(struct termios *termios_p) { termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP| INLCR|IGNCR|ICRNL|IXON); termios_p->c_oflag &= ~OPOST; termios_p->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN); termios_p->c_cflag &= ~(CSIZE|PARENB); termios_p->c_cflag |= CS8;}#endif/* * Setup serial port, return 0 if success. */static int vtty_serial_setup(vtty_t *vtty, const vtty_serial_option_t *option){ struct termios tio; int tio_baudrate; if (tcgetattr(vtty->fd, &tio) != 0) { fprintf(stderr, "error: tcgetattr failed\n"); return(-1); } cfmakeraw(&tio); tio.c_cflag = 0 |CLOCAL // ignore modem control lines ; tio.c_cflag &= ~CREAD; tio.c_cflag |= CREAD; switch(option->baudrate) { case 50 : tio_baudrate = B50; break; case 75 : tio_baudrate = B75; break; case 110 : tio_baudrate = B110; break; case 134 : tio_baudrate = B134; break; case 150 : tio_baudrate = B150; break; case 200 : tio_baudrate = B200; break; case 300 : tio_baudrate = B300; break; case 600 : tio_baudrate = B600; break; case 1200 : tio_baudrate = B1200; break; case 1800 : tio_baudrate = B1800; break; case 2400 : tio_baudrate = B2400; break; case 4800 : tio_baudrate = B4800; break; case 9600 : tio_baudrate = B9600; break; case 19200 : tio_baudrate = B19200; break; case 38400 : tio_baudrate = B38400; break; case 57600 : tio_baudrate = B57600; break;#if defined(B76800) case 76800 : tio_baudrate = B76800; break;#endif case 115200 : tio_baudrate = B115200; break;#if defined(B230400) case 230400 : tio_baudrate = B230400; break;#endif default: fprintf(stderr, "error: unsupported baudrate\n"); return(-1); } cfsetospeed(&tio, tio_baudrate); cfsetispeed(&tio, tio_baudrate); tio.c_cflag &= ~CSIZE; /* clear size flag */ switch(option->databits) { case 5 : tio.c_cflag |= CS5; break; case 6 : tio.c_cflag |= CS6; break; case 7 : tio.c_cflag |= CS7; break; case 8 : tio.c_cflag |= CS8; break; default : fprintf(stderr, "error: unsupported databits\n"); return(-1); } tio.c_iflag &= ~INPCK; /* clear parity flag */ tio.c_cflag &= ~(PARENB|PARODD); switch(option->parity) { case 0 : break; case 2 : tio.c_iflag|=INPCK; tio.c_cflag|=PARENB; break; /* even */ case 1 : tio.c_iflag|=INPCK; tio.c_cflag|=PARENB|PARODD; break; /* odd */ default: fprintf(stderr, "error: unsupported parity\n"); return(-1); } tio.c_cflag &= ~CSTOPB; /* clear stop flag */ switch(option->stopbits) { case 1 : break; case 2 : tio.c_cflag |= CSTOPB; break; default : fprintf(stderr, "error: unsupported stopbits\n"); return(-1); }#if defined(CRTSCTS) tio.c_cflag &= ~CRTSCTS;#endif#if defined(CNEW_RTSCTS) tio.c_cflag &= ~CNEW_RTSCTS;#endif if (option->hwflow) {#if defined(CRTSCTS) tio.c_cflag |= CRTSCTS;#else tio.c_cflag |= CNEW_RTSCTS;#endif } tio.c_cc[VTIME] = 0; tio.c_cc[VMIN] = 1; /* block read() until one character is available */#if 0 /* not neccessary unless O_NONBLOCK used */ if (fcntl(vtty->fd, F_SETFL, 0) != 0) { /* enable blocking mode */ fprintf(stderr, "error: fnctl F_SETFL failed\n"); return(-1); }#endif if (tcflush(vtty->fd, TCIOFLUSH) != 0) { fprintf(stderr, "error: tcflush failed\n"); return(-1); } if (tcsetattr(vtty->fd, TCSANOW, &tio) != 0 ) { fprintf(stderr, "error: tcsetattr failed\n"); return(-1); } return(0);}/* Create a virtual tty */vtty_t *vtty_create(vm_instance_t *vm,char *name,int type,int tcp_port, const vtty_serial_option_t *option){ vtty_t *vtty; if (!(vtty = malloc(sizeof(*vtty)))) { fprintf(stderr,"VTTY: unable to create new virtual tty.\n"); return NULL; } memset(vtty,0,sizeof(*vtty)); vtty->name = name; vtty->type = type; vtty->vm = vm; vtty->fd = -1; vtty->fstream = NULL; vtty->accept_fd = -1; pthread_mutex_init(&vtty->lock,NULL); vtty->terminal_support = 1; vtty->input_state = VTTY_INPUT_TEXT; switch (vtty->type) { case VTTY_TYPE_NONE: vtty->select_fd = NULL; break; case VTTY_TYPE_TERM: vtty_term_init(); vtty->fd = STDIN_FILENO; vtty->select_fd = &vtty->fd; vtty->fstream = stdout; break; case VTTY_TYPE_TCP: vtty->tcp_port = tcp_port; vtty_tcp_conn_wait(vtty); break; case VTTY_TYPE_SERIAL: vtty->fd = open(option->device, O_RDWR); if (vtty->fd < 0) { fprintf(stderr,"VTTY: open failed\n"); free(vtty); return NULL; } if (vtty_serial_setup(vtty,option)) { fprintf(stderr,"VTTY: setup failed\n"); close(vtty->fd); free(vtty); return NULL; } vtty->select_fd = &vtty->fd; vtty->terminal_support = 0; break; default: fprintf(stderr,"tty_create: bad vtty type %d\n",vtty->type); return NULL; } /* Add this new VTTY to the list */ VTTY_LIST_LOCK(); vtty->next = vtty_list; vtty->pprev = &vtty_list; if (vtty_list != NULL) vtty_list->pprev = &vtty->next; vtty_list = vtty; VTTY_LIST_UNLOCK(); return vtty;}/* Delete a virtual tty */void vtty_delete(vtty_t *vtty){ if (vtty != NULL) { if (vtty->pprev != NULL) { VTTY_LIST_LOCK(); if (vtty->next) vtty->next->pprev = vtty->pprev; *(vtty->pprev) = vtty->next; VTTY_LIST_UNLOCK(); } if ((vtty->fstream) && (vtty->fstream != stdout)) fclose(vtty->fstream); /* We don't close FD 0 since it is stdin */ if (vtty->fd > 0) { vm_log(vtty->vm,"VTTY","%s: closing FD %d\n",vtty->name,vtty->fd); close(vtty->fd); } if (vtty->accept_fd != -1) { vm_log(vtty->vm,"VTTY","%s: closing accept FD %d\n", vtty->name,vtty->accept_fd); close(vtty->accept_fd); } free(vtty); }}/* Store a character in the FIFO buffer */static int vtty_store(vtty_t *vtty,u_char c){ u_int nwptr; VTTY_LOCK(vtty); nwptr = vtty->write_ptr + 1; if (nwptr == VTTY_BUFFER_SIZE) nwptr = 0; if (nwptr == vtty->read_ptr) { VTTY_UNLOCK(vtty); return(-1); } vtty->buffer[vtty->write_ptr] = c; vtty->write_ptr = nwptr; VTTY_UNLOCK(vtty); return(0);}/* Store a string in the FIFO buffer */int vtty_store_str(vtty_t *vtty,char *str){ if (!vtty) return(0); while(*str != 0) { if (vtty_store(vtty,*str) == -1) return(-1); str++; } vtty->input_pending = TRUE; return(0);}/* Store CTRL+C in buffer */int vtty_store_ctrlc(vtty_t *vtty){ if (vtty) vtty_store(vtty,0x03); return(0);}/* * Read a character from the terminal. */static int vtty_term_read(vtty_t *vtty){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -