From: Patrick G. Bridges Date: Thu, 1 Aug 2013 23:14:06 +0000 (-0500) Subject: Added basic escape handling to v3_stream interactive mode X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?p=palacios.git;a=commitdiff_plain;h=eb09f3cd8221d75e00ea0ea8462f3f2e71ccc4e9 Added basic escape handling to v3_stream interactive mode --- diff --git a/linux_usr/v3_stream.c b/linux_usr/v3_stream.c index 45d5eb2..e54d196 100644 --- a/linux_usr/v3_stream.c +++ b/linux_usr/v3_stream.c @@ -1,6 +1,6 @@ /* - * V3 Console utility - * (c) Jack lange & Lei Xia, 2010 + * V3 Stream Utility + * (c) Jack lange, Lei Xia, Peter Dinda 2010, 2013 */ @@ -15,99 +15,260 @@ #include #include #include -#include -#include - +#include +#include +#include #include "v3_ctrl.h" -#define BUF_LEN 1025 +#define BUF_LEN 512 #define STREAM_NAME_LEN 128 +#define ESC_KEY '~' -int main(int argc, char* argv[]) { - int vm_fd; - fd_set rset; - char * vm_dev = NULL; - char stream[STREAM_NAME_LEN]; - char cons_buf[BUF_LEN]; - int stream_fd = 0; - - if (argc < 2) { - printf("usage: v3_stream \n"); - return -1; - } - - vm_dev = argv[1]; +int interactive=0; +int found_esc=0; +int stream_fd=0; +struct termios ourterm, oldterm; - if (strlen(argv[2]) >= STREAM_NAME_LEN) { - printf("ERROR: Stream name longer than maximum size (%d)\n", STREAM_NAME_LEN); - return -1; +int setup_term() +{ + if (interactive) { + if (tcgetattr(0,&oldterm)) { + fprintf(stderr,"Cannot get terminal attributes\n"); + return -1; } + + ourterm = oldterm; // clone existing terminal behavior + ourterm.c_lflag &= ~ICANON; // but without buffering + ourterm.c_lflag &= ~ECHO; // or echoing + ourterm.c_lflag &= ~ISIG; // or interrupt keys + ourterm.c_cc[VMIN] = 0; // or weird delays to compose + ourterm.c_cc[VTIME] = 0; // function keys (e.g., set raw) + fprintf(stderr, "Calling tcsetattr.\n"); - memcpy(stream, argv[2], strlen(argv[2])); + if (tcsetattr(0,TCSANOW,&ourterm)) { + fprintf(stderr,"Cannot set terminal attributes\n"); + return -1; + } + fprintf(stderr,"term setup interactive\n"); + } else { + fprintf(stderr, "term setup noninteractive\n"); + } + + return 0; +} - vm_fd = open(vm_dev, O_RDONLY); - if (vm_fd == -1) { - printf("Error opening VM device: %s\n", vm_dev); - return -1; - } +int restore_term() +{ + if (interactive) { + return -!!tcsetattr(0,TCSANOW,&oldterm); + } else { + return 0; + } +} - stream_fd = ioctl(vm_fd, V3_VM_SERIAL_CONNECT, stream); +int pump(int in, int out) +{ + int bytes_read; + int bytes_written; + int thiswrite; + int check_esc; + char buf[BUF_LEN]; - /* Close the file descriptor. */ - close(vm_fd); - - if (stream_fd < 0) { - printf("Error opening stream Console\n"); - return -1; + // data from the stream + check_esc = interactive && (in == STDIN_FILENO); + bytes_read = read(in, buf, check_esc ? 1 : BUF_LEN); + + if (bytes_read<0) { + return -1; + } + + if (check_esc) { + if (found_esc) { + switch(buf[0]) { + case '.': + close(stream_fd); + restore_term(); + fprintf(stderr,"Bye\n"); + exit(0); + case ESC_KEY: + /* Pass the second escape! */ + break; + default: + fprintf(stderr, "Unknown ESC sequence.\n"); + found_esc = 0; + return; + } + } else if (buf[0] == ESC_KEY) { + found_esc = 1; + return; + } } - while (1) { - int ret; - int bytes_read = 0; - char in_buf[512]; - - memset(cons_buf, 0, BUF_LEN); - + fprintf(stderr,"read %d bytes\n", bytes_read); - FD_ZERO(&rset); - FD_SET(stream_fd, &rset); - FD_SET(STDIN_FILENO, &rset); + bytes_written=0; - ret = select(stream_fd + 1, &rset, NULL, NULL, NULL); - - if (ret == 0) { - continue; - } else if (ret == -1) { - perror("Select returned error\n"); - return -1; - } + while (bytes_written \n\n" + "Connects stdin/stdout to a bidirectional stream on the VM,\n" + "for example a serial port.\n\n" + "If the [-i] option is given, a terminal is assumed\n" + "and it is placed in non-echoing, interactive mode, \n" + "which is what you probably want for use as a console.\n"); + exit(0); + } + + if (!strcasecmp(argv[1],"-i")) { + interactive=1; + argstart=2; + } else { + // noninteractive mode + interactive=0; + argstart=1; + } + + vm_dev = argv[argstart]; + + if (strlen(argv[argstart+1]) >= STREAM_NAME_LEN) { + fprintf(stderr, "ERROR: Stream name longer than maximum size (%d)\n", STREAM_NAME_LEN); + exit(-1); + } - } + memcpy(stream, argv[argstart+1], strlen(argv[argstart+1])); + + vm_fd = open(vm_dev, O_RDONLY); + + if (vm_fd == -1) { + fprintf(stderr,"Error opening VM device: %s\n", vm_dev); + exit(-1); + } + + stream_fd = ioctl(vm_fd, V3_VM_SERIAL_CONNECT, stream); + + close(vm_fd); + + if (stream_fd<0) { + fprintf(stderr,"Error opening VM device: %s\n", vm_dev); + exit(-1); + } + + if (setup_term()) { + fprintf(stderr,"Cannot setup terminal for %s mode\n", interactive ? "interactive" : "noninteraInteractive"); + close(stream_fd); + exit(-1); + } + + signal(SIGINT,signal_handler); + + while (1) { + + FD_ZERO(&rset); + FD_SET(stream_fd, &rset); + FD_SET(STDIN_FILENO, &rset); + + // wait for data from stdin or from the stream + ret = select(stream_fd + 1, &rset, NULL, NULL, NULL); + + fprintf(stderr,"select ret=%d\n", ret); + if (ret==0) { + perror("select returned zero without a timer!"); + goto error; + } else if (ret<0) { + if (errno==EAGAIN) { + continue; + } else { + perror("select failed"); + goto error; + } + } + + // positive return - we have data on one or both + + // check data from stream + if (FD_ISSET(stream_fd, &rset)) { + fprintf(stderr,"stream is readable\n"); + if (pump(stream_fd, STDOUT_FILENO)) { + fprintf(stderr,"Cannot transfer all data from stream to stdout...\n"); + goto error; + } + } + + // check data from stdin + + if (FD_ISSET(STDIN_FILENO, &rset)) { + fprintf(stderr,"stdin is readable\n"); + if (pump(STDIN_FILENO,stream_fd)) { + fprintf(stderr,"Cannot transfer all data from stdin to stream...\n"); + goto error; + } + } + } + + error: + ret = -1; + goto out; + + done: + ret = 0; + goto out; + + out: + + close(stream_fd); + restore_term(); + + return ret; + - return 0; }