X-Git-Url: http://git.maemo.org/git/?a=blobdiff_plain;f=qemu-char.c;h=a7e6cf77d8338cc5d2dfe8983e5e7e60af366d95;hb=7591c5c19951def341139c3874bdc7289f9813d1;hp=26e8825fec01407b65b8be88581216da142c99dd;hpb=cde76ee16adc33f29802596b3499e4fbfcabd8ff;p=qemu diff --git a/qemu-char.c b/qemu-char.c index 26e8825..a7e6cf7 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -61,12 +61,16 @@ #include #include #include -#ifdef _BSD +#ifdef CONFIG_BSD #include #ifdef __FreeBSD__ #include #include #include +#elif defined(__DragonFly__) +#include +#include +#include #else #include #endif @@ -164,6 +168,11 @@ void qemu_chr_read(CharDriverState *s, uint8_t *buf, int len) s->chr_read(s->handler_opaque, buf, len); } +int qemu_chr_get_msgfd(CharDriverState *s) +{ + return s->get_msgfd ? s->get_msgfd(s) : -1; +} + void qemu_chr_accept_input(CharDriverState *s) { if (s->chr_accept_input) @@ -205,7 +214,7 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return len; } -static CharDriverState *qemu_chr_open_null(void) +static CharDriverState *qemu_chr_open_null(QemuOpts *opts) { CharDriverState *chr; @@ -215,8 +224,6 @@ static CharDriverState *qemu_chr_open_null(void) } /* MUX driver for serial I/O splitting */ -static int term_timestamps; -static int64_t term_timestamps_start; #define MAX_MUX 4 #define MUX_BUFFER_SIZE 32 /* Must be a power of 2. */ #define MUX_BUFFER_MASK (MUX_BUFFER_SIZE - 1) @@ -235,6 +242,9 @@ typedef struct { unsigned char buffer[MAX_MUX][MUX_BUFFER_SIZE]; int prod[MAX_MUX]; int cons[MAX_MUX]; + int timestamps; + int linestart; + int64_t timestamps_start; } MuxDriver; @@ -242,23 +252,22 @@ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { MuxDriver *d = chr->opaque; int ret; - if (!term_timestamps) { + if (!d->timestamps) { ret = d->drv->chr_write(d->drv, buf, len); } else { int i; ret = 0; - for(i = 0; i < len; i++) { - ret += d->drv->chr_write(d->drv, buf+i, 1); - if (buf[i] == '\n') { + for (i = 0; i < len; i++) { + if (d->linestart) { char buf1[64]; int64_t ti; int secs; ti = qemu_get_clock(rt_clock); - if (term_timestamps_start == -1) - term_timestamps_start = ti; - ti -= term_timestamps_start; + if (d->timestamps_start == -1) + d->timestamps_start = ti; + ti -= d->timestamps_start; secs = ti / 1000; snprintf(buf1, sizeof(buf1), "[%02d:%02d:%02d.%03d] ", @@ -267,6 +276,11 @@ static int mux_chr_write(CharDriverState *chr, const uint8_t *buf, int len) secs % 60, (int)(ti % 1000)); d->drv->chr_write(d->drv, (uint8_t *)buf1, strlen(buf1)); + d->linestart = 0; + } + ret += d->drv->chr_write(d->drv, buf+i, 1); + if (buf[i] == '\n') { + d->linestart = 1; } } } @@ -336,9 +350,9 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) } case 's': { - int i; - for (i = 0; i < nb_drives; i++) { - bdrv_commit(drives_table[i].bdrv); + DriveInfo *dinfo; + TAILQ_FOREACH(dinfo, &drives, next) { + bdrv_commit(dinfo->bdrv); } } break; @@ -353,10 +367,11 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) chr->focus = 0; mux_chr_send_event(d, chr->focus, CHR_EVENT_MUX_IN); break; - case 't': - term_timestamps = !term_timestamps; - term_timestamps_start = -1; - break; + case 't': + d->timestamps = !d->timestamps; + d->timestamps_start = -1; + d->linestart = 0; + break; } } else if (ch == term_escape_char) { d->term_got_escape = 1; @@ -555,6 +570,7 @@ static void fd_chr_read(void *opaque) if (size == 0) { /* FD has been closed. Remove it from the active list. */ qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL); + qemu_chr_event(chr, CHR_EVENT_CLOSED); return; } if (size > 0) { @@ -567,7 +583,7 @@ static void fd_chr_update_read_handler(CharDriverState *chr) FDCharDriver *s = chr->opaque; if (s->fd_in >= 0) { - if (nographic && s->fd_in == 0) { + if (display_type == DT_NOGRAPHIC && s->fd_in == 0) { } else { qemu_set_fd_handler2(s->fd_in, fd_chr_read_poll, fd_chr_read, NULL, chr); @@ -580,13 +596,14 @@ static void fd_chr_close(struct CharDriverState *chr) FDCharDriver *s = chr->opaque; if (s->fd_in >= 0) { - if (nographic && s->fd_in == 0) { + if (display_type == DT_NOGRAPHIC && s->fd_in == 0) { } else { qemu_set_fd_handler2(s->fd_in, NULL, NULL, NULL, NULL); } } qemu_free(s); + qemu_chr_event(chr, CHR_EVENT_CLOSED); } /* open a character device to a unix fd */ @@ -609,20 +626,27 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) return chr; } -static CharDriverState *qemu_chr_open_file_out(const char *file_out) +static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts) { int fd_out; - TFR(fd_out = open(file_out, O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); + TFR(fd_out = open(qemu_opt_get(opts, "path"), + O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); if (fd_out < 0) return NULL; return qemu_chr_open_fd(-1, fd_out); } -static CharDriverState *qemu_chr_open_pipe(const char *filename) +static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) { int fd_in, fd_out; char filename_in[256], filename_out[256]; + const char *filename = qemu_opt_get(opts, "path"); + + if (filename == NULL) { + fprintf(stderr, "chardev: pipe: no filename given\n"); + return NULL; + } snprintf(filename_in, 256, "%s.in", filename); snprintf(filename_out, 256, "%s.out", filename); @@ -675,6 +699,7 @@ static void stdio_read(void *opaque) if (size == 0) { /* stdin has been closed. Remove it from the active list. */ qemu_set_fd_handler2(0, NULL, NULL, NULL, NULL); + qemu_chr_event(chr, CHR_EVENT_CLOSED); return; } if (size > 0) { @@ -710,7 +735,7 @@ static void term_init(void) tty.c_oflag |= OPOST; tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN); /* if graphical mode, we allow Ctrl-C handling */ - if (nographic) + if (display_type == DT_NOGRAPHIC) tty.c_lflag &= ~ISIG; tty.c_cflag &= ~(CSIZE|PARENB); tty.c_cflag |= CS8; @@ -733,7 +758,7 @@ static void qemu_chr_close_stdio(struct CharDriverState *chr) fd_chr_close(chr); } -static CharDriverState *qemu_chr_open_stdio(void) +static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) { CharDriverState *chr; @@ -750,8 +775,8 @@ static CharDriverState *qemu_chr_open_stdio(void) #ifdef __sun__ /* Once Solaris has openpty(), this is going to be removed. */ -int openpty(int *amaster, int *aslave, char *name, - struct termios *termp, struct winsize *winp) +static int openpty(int *amaster, int *aslave, char *name, + struct termios *termp, struct winsize *winp) { const char *slave; int mfd = -1, sfd = -1; @@ -791,7 +816,7 @@ err: return -1; } -void cfmakeraw (struct termios *termios_p) +static void cfmakeraw (struct termios *termios_p) { termios_p->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON); @@ -806,7 +831,7 @@ void cfmakeraw (struct termios *termios_p) #endif #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ - || defined(__NetBSD__) || defined(__OpenBSD__) + || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) typedef struct { int fd; @@ -925,16 +950,19 @@ static void pty_chr_close(struct CharDriverState *chr) qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); close(s->fd); + qemu_del_timer(s->timer); + qemu_free_timer(s->timer); qemu_free(s); + qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pty(void) +static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) { CharDriverState *chr; PtyCharDriver *s; struct termios tty; int slave_fd, len; -#if defined(__OpenBSD__) +#if defined(__OpenBSD__) || defined(__DragonFly__) char pty_name[PATH_MAX]; #define q_ptsname(x) pty_name #else @@ -958,6 +986,7 @@ static CharDriverState *qemu_chr_open_pty(void) len = strlen(q_ptsname(s->fd)) + 5; chr->filename = qemu_malloc(len); snprintf(chr->filename, len, "pty:%s", q_ptsname(s->fd)); + qemu_opt_set(opts, "path", q_ptsname(s->fd)); fprintf(stderr, "char device redirected to %s\n", q_ptsname(s->fd)); chr->opaque = s; @@ -1117,8 +1146,9 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static CharDriverState *qemu_chr_open_tty(const char *filename) +static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) { + const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; int fd; @@ -1247,10 +1277,12 @@ static void pp_close(CharDriverState *chr) ioctl(fd, PPRELEASE); close(fd); qemu_free(drv); + qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_pp(const char *filename) +static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) { + const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; ParallelCharDriver *drv; int fd; @@ -1280,7 +1312,7 @@ static CharDriverState *qemu_chr_open_pp(const char *filename) } #endif /* __linux__ */ -#if defined(__FreeBSD__) +#if defined(__FreeBSD__) || defined(__DragonFly__) static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) { int fd = (int)chr->opaque; @@ -1318,8 +1350,9 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static CharDriverState *qemu_chr_open_pp(const char *filename) +static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) { + const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; int fd; @@ -1373,6 +1406,8 @@ static void win_chr_close(CharDriverState *chr) qemu_del_polling_cb(win_chr_pipe_poll, chr); else qemu_del_polling_cb(win_chr_poll, chr); + + qemu_chr_event(chr, CHR_EVENT_CLOSED); } static int win_chr_init(CharDriverState *chr, const char *filename) @@ -1535,8 +1570,9 @@ static int win_chr_poll(void *opaque) return 0; } -static CharDriverState *qemu_chr_open_win(const char *filename) +static CharDriverState *qemu_chr_open_win(QemuOpts *opts) { + const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; WinCharState *s; @@ -1634,8 +1670,9 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename) } -static CharDriverState *qemu_chr_open_win_pipe(const char *filename) +static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) { + const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; WinCharState *s; @@ -1668,13 +1705,14 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) return chr; } -static CharDriverState *qemu_chr_open_win_con(const char *filename) +static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts) { return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); } -static CharDriverState *qemu_chr_open_win_file_out(const char *file_out) +static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts) { + const char *file_out = qemu_opt_get(opts, "path"); HANDLE fd_out; fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, @@ -1702,7 +1740,7 @@ static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { NetCharDriver *s = chr->opaque; - return sendto(s->fd, buf, len, 0, + return sendto(s->fd, (const void *)buf, len, 0, (struct sockaddr *)&s->daddr, sizeof(struct sockaddr_in)); } @@ -1731,7 +1769,7 @@ static void udp_chr_read(void *opaque) if (s->max_size == 0) return; - s->bufcnt = recv(s->fd, s->buf, sizeof(s->buf), 0); + s->bufcnt = recv(s->fd, (void *)s->buf, sizeof(s->buf), 0); s->bufptr = s->bufcnt; if (s->bufcnt <= 0) return; @@ -1754,6 +1792,17 @@ static void udp_chr_update_read_handler(CharDriverState *chr) } } +static void udp_chr_close(CharDriverState *chr) +{ + NetCharDriver *s = chr->opaque; + if (s->fd >= 0) { + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + closesocket(s->fd); + } + qemu_free(s); + qemu_chr_event(chr, CHR_EVENT_CLOSED); +} + static CharDriverState *qemu_chr_open_udp(const char *def) { CharDriverState *chr = NULL; @@ -1787,6 +1836,7 @@ static CharDriverState *qemu_chr_open_udp(const char *def) chr->opaque = s; chr->chr_write = udp_chr_write; chr->chr_update_read_handler = udp_chr_update_read_handler; + chr->chr_close = udp_chr_close; return chr; return_err: @@ -1809,6 +1859,7 @@ typedef struct { int do_telnetopt; int do_nodelay; int is_unix; + int msgfd; } TCPCharDriver; static void tcp_chr_accept(void *opaque); @@ -1884,6 +1935,70 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr, *size = j; } +static int tcp_get_msgfd(CharDriverState *chr) +{ + TCPCharDriver *s = chr->opaque; + + return s->msgfd; +} + +#ifndef _WIN32 +static void unix_process_msgfd(CharDriverState *chr, struct msghdr *msg) +{ + TCPCharDriver *s = chr->opaque; + struct cmsghdr *cmsg; + + for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + int fd; + + if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || + cmsg->cmsg_level != SOL_SOCKET || + cmsg->cmsg_type != SCM_RIGHTS) + continue; + + fd = *((int *)CMSG_DATA(cmsg)); + if (fd < 0) + continue; + + if (s->msgfd != -1) + close(s->msgfd); + s->msgfd = fd; + } +} + +static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) +{ + TCPCharDriver *s = chr->opaque; + struct msghdr msg = { NULL, }; + struct iovec iov[1]; + union { + struct cmsghdr cmsg; + char control[CMSG_SPACE(sizeof(int))]; + } msg_control; + ssize_t ret; + + iov[0].iov_base = buf; + iov[0].iov_len = len; + + msg.msg_iov = iov; + msg.msg_iovlen = 1; + msg.msg_control = &msg_control; + msg.msg_controllen = sizeof(msg_control); + + ret = recvmsg(s->fd, &msg, 0); + if (ret > 0 && s->is_unix) + unix_process_msgfd(chr, &msg); + + return ret; +} +#else +static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len) +{ + TCPCharDriver *s = chr->opaque; + return recv(s->fd, buf, len, 0); +} +#endif + static void tcp_chr_read(void *opaque) { CharDriverState *chr = opaque; @@ -1896,7 +2011,7 @@ static void tcp_chr_read(void *opaque) len = sizeof(buf); if (len > s->max_size) len = s->max_size; - size = recv(s->fd, buf, len, 0); + size = tcp_chr_recv(chr, (void *)buf, len); if (size == 0) { /* connection closed */ s->connected = 0; @@ -1906,11 +2021,16 @@ static void tcp_chr_read(void *opaque) qemu_set_fd_handler(s->fd, NULL, NULL, NULL); closesocket(s->fd); s->fd = -1; + qemu_chr_event(chr, CHR_EVENT_CLOSED); } else if (size > 0) { if (s->do_telnetopt) tcp_chr_process_IAC_bytes(chr, s, buf, &size); if (size > 0) qemu_chr_read(chr, buf, size); + if (s->msgfd != -1) { + close(s->msgfd); + s->msgfd = -1; + } } } @@ -1989,74 +2109,51 @@ static void tcp_chr_accept(void *opaque) static void tcp_chr_close(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; - if (s->fd >= 0) + if (s->fd >= 0) { + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); closesocket(s->fd); - if (s->listen_fd >= 0) + } + if (s->listen_fd >= 0) { + qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); closesocket(s->listen_fd); + } qemu_free(s); + qemu_chr_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_tcp(const char *host_str, - int is_telnet, - int is_unix) +static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) { CharDriverState *chr = NULL; TCPCharDriver *s = NULL; - int fd = -1, offset = 0; - int is_listen = 0; - int is_waitconnect = 1; - int do_nodelay = 0; - const char *ptr; - - ptr = host_str; - while((ptr = strchr(ptr,','))) { - ptr++; - if (!strncmp(ptr,"server",6)) { - is_listen = 1; - } else if (!strncmp(ptr,"nowait",6)) { - is_waitconnect = 0; - } else if (!strncmp(ptr,"nodelay",6)) { - do_nodelay = 1; - } else if (!strncmp(ptr,"to=",3)) { - /* nothing, inet_listen() parses this one */; - } else if (!strncmp(ptr,"ipv4",4)) { - /* nothing, inet_connect() and inet_listen() parse this one */; - } else if (!strncmp(ptr,"ipv6",4)) { - /* nothing, inet_connect() and inet_listen() parse this one */; - } else { - printf("Unknown option: %s\n", ptr); - goto fail; - } - } + int fd = -1; + int is_listen; + int is_waitconnect; + int do_nodelay; + int is_unix; + int is_telnet; + + is_listen = qemu_opt_get_bool(opts, "server", 0); + is_waitconnect = qemu_opt_get_bool(opts, "wait", 1); + is_telnet = qemu_opt_get_bool(opts, "telnet", 0); + do_nodelay = !qemu_opt_get_bool(opts, "delay", 1); + is_unix = qemu_opt_get(opts, "path") != NULL; if (!is_listen) is_waitconnect = 0; chr = qemu_mallocz(sizeof(CharDriverState)); s = qemu_mallocz(sizeof(TCPCharDriver)); - if (is_listen) { - chr->filename = qemu_malloc(256); - if (is_unix) { - pstrcpy(chr->filename, 256, "unix:"); - } else if (is_telnet) { - pstrcpy(chr->filename, 256, "telnet:"); - } else { - pstrcpy(chr->filename, 256, "tcp:"); - } - offset = strlen(chr->filename); - } if (is_unix) { if (is_listen) { - fd = unix_listen(host_str, chr->filename + offset, 256 - offset); + fd = unix_listen_opts(opts); } else { - fd = unix_connect(host_str); + fd = unix_connect_opts(opts); } } else { if (is_listen) { - fd = inet_listen(host_str, chr->filename + offset, 256 - offset, - SOCK_STREAM, 0); + fd = inet_listen_opts(opts, 0); } else { - fd = inet_connect(host_str, SOCK_STREAM); + fd = inet_connect_opts(opts); } } if (fd < 0) @@ -2068,18 +2165,21 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, s->connected = 0; s->fd = -1; s->listen_fd = -1; + s->msgfd = -1; s->is_unix = is_unix; s->do_nodelay = do_nodelay && !is_unix; chr->opaque = s; chr->chr_write = tcp_chr_write; chr->chr_close = tcp_chr_close; + chr->get_msgfd = tcp_get_msgfd; if (is_listen) { s->listen_fd = fd; qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); if (is_telnet) s->do_telnetopt = 1; + } else { s->connected = 1; s->fd = fd; @@ -2087,14 +2187,30 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, tcp_chr_connect(chr); } + /* for "info chardev" monitor command */ + chr->filename = qemu_malloc(256); + if (is_unix) { + snprintf(chr->filename, 256, "unix:%s%s", + qemu_opt_get(opts, "path"), + qemu_opt_get_bool(opts, "server", 0) ? ",server" : ""); + } else if (is_telnet) { + snprintf(chr->filename, 256, "telnet:%s:%s%s", + qemu_opt_get(opts, "host"), qemu_opt_get(opts, "port"), + qemu_opt_get_bool(opts, "server", 0) ? ",server" : ""); + } else { + snprintf(chr->filename, 256, "tcp:%s:%s%s", + qemu_opt_get(opts, "host"), qemu_opt_get(opts, "port"), + qemu_opt_get_bool(opts, "server", 0) ? ",server" : ""); + } + if (is_listen && is_waitconnect) { printf("QEMU waiting for connection on: %s\n", - chr->filename ? chr->filename : host_str); + chr->filename); tcp_chr_accept(chr); socket_set_nonblock(s->listen_fd); } - return chr; + fail: if (fd >= 0) closesocket(fd); @@ -2103,86 +2219,203 @@ static CharDriverState *qemu_chr_open_tcp(const char *host_str, return NULL; } -CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s)) +static QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) { + char host[65], port[33], width[8], height[8]; + int pos; const char *p; - CharDriverState *chr; + QemuOpts *opts; + + opts = qemu_opts_create(&qemu_chardev_opts, label, 1); + if (NULL == opts) + return NULL; - if (!strcmp(filename, "vc")) { - chr = text_console_init(0); - } else - if (strstart(filename, "vc:", &p)) { - chr = text_console_init(p); - } else - if (!strcmp(filename, "null")) { - chr = qemu_chr_open_null(); - } else - if (strstart(filename, "tcp:", &p)) { - chr = qemu_chr_open_tcp(p, 0, 0); - } else - if (strstart(filename, "telnet:", &p)) { - chr = qemu_chr_open_tcp(p, 1, 0); - } else - if (strstart(filename, "udp:", &p)) { - chr = qemu_chr_open_udp(p); - } else if (strstart(filename, "mon:", &p)) { - chr = qemu_chr_open(label, p, NULL); - if (chr) { - chr = qemu_chr_open_mux(chr); - monitor_init(chr, MONITOR_USE_READLINE); - } else { - printf("Unable to open driver: %s\n", p); + filename = p; + qemu_opt_set(opts, "mux", "on"); + } + + if (strcmp(filename, "null") == 0 || + strcmp(filename, "pty") == 0 || + strcmp(filename, "msmouse") == 0 || + strcmp(filename, "braille") == 0 || + strcmp(filename, "stdio") == 0) { + qemu_opt_set(opts, "backend", filename); + return opts; + } + if (strstart(filename, "vc", &p)) { + qemu_opt_set(opts, "backend", "vc"); + if (*p == ':') { + if (sscanf(p+1, "%8[0-9]x%8[0-9]", width, height) == 2) { + /* pixels */ + qemu_opt_set(opts, "width", width); + qemu_opt_set(opts, "height", height); + } else if (sscanf(p+1, "%8[0-9]Cx%8[0-9]C", width, height) == 2) { + /* chars */ + qemu_opt_set(opts, "cols", width); + qemu_opt_set(opts, "rows", height); + } else { + goto fail; + } } - } else if (!strcmp(filename, "msmouse")) { - chr = qemu_chr_open_msmouse(); - } else -#ifndef _WIN32 + return opts; + } + if (strcmp(filename, "con:") == 0) { + qemu_opt_set(opts, "backend", "console"); + return opts; + } + if (strstart(filename, "COM", NULL)) { + qemu_opt_set(opts, "backend", "serial"); + qemu_opt_set(opts, "path", filename); + return opts; + } + if (strstart(filename, "file:", &p)) { + qemu_opt_set(opts, "backend", "file"); + qemu_opt_set(opts, "path", p); + return opts; + } + if (strstart(filename, "pipe:", &p)) { + qemu_opt_set(opts, "backend", "pipe"); + qemu_opt_set(opts, "path", p); + return opts; + } + if (strstart(filename, "tcp:", &p) || + strstart(filename, "telnet:", &p)) { + if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { + host[0] = 0; + if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) + goto fail; + } + qemu_opt_set(opts, "backend", "socket"); + qemu_opt_set(opts, "host", host); + qemu_opt_set(opts, "port", port); + if (p[pos] == ',') { + if (qemu_opts_do_parse(opts, p+pos+1, NULL) != 0) + goto fail; + } + if (strstart(filename, "telnet:", &p)) + qemu_opt_set(opts, "telnet", "on"); + return opts; + } if (strstart(filename, "unix:", &p)) { - chr = qemu_chr_open_tcp(p, 0, 1); - } else if (strstart(filename, "file:", &p)) { - chr = qemu_chr_open_file_out(p); - } else if (strstart(filename, "pipe:", &p)) { - chr = qemu_chr_open_pipe(p); - } else if (!strcmp(filename, "pty")) { - chr = qemu_chr_open_pty(); - } else if (!strcmp(filename, "stdio")) { - chr = qemu_chr_open_stdio(); - } else -#if defined(__linux__) - if (strstart(filename, "/dev/parport", NULL)) { - chr = qemu_chr_open_pp(filename); - } else -#elif defined(__FreeBSD__) - if (strstart(filename, "/dev/ppi", NULL)) { - chr = qemu_chr_open_pp(filename); - } else + qemu_opt_set(opts, "backend", "socket"); + if (qemu_opts_do_parse(opts, p, "path") != 0) + goto fail; + return opts; + } + if (strstart(filename, "/dev/parport", NULL) || + strstart(filename, "/dev/ppi", NULL)) { + qemu_opt_set(opts, "backend", "parport"); + qemu_opt_set(opts, "path", filename); + return opts; + } + if (strstart(filename, "/dev/", NULL)) { + qemu_opt_set(opts, "backend", "tty"); + qemu_opt_set(opts, "path", filename); + return opts; + } + +fail: + fprintf(stderr, "%s: fail on \"%s\"\n", __FUNCTION__, filename); + qemu_opts_del(opts); + return NULL; +} + +static const struct { + const char *name; + CharDriverState *(*open)(QemuOpts *opts); +} backend_table[] = { + { .name = "null", .open = qemu_chr_open_null }, + { .name = "socket", .open = qemu_chr_open_socket }, + { .name = "msmouse", .open = qemu_chr_open_msmouse }, + { .name = "vc", .open = text_console_init }, +#ifdef _WIN32 + { .name = "file", .open = qemu_chr_open_win_file_out }, + { .name = "pipe", .open = qemu_chr_open_win_pipe }, + { .name = "console", .open = qemu_chr_open_win_con }, + { .name = "serial", .open = qemu_chr_open_win }, +#else + { .name = "file", .open = qemu_chr_open_file_out }, + { .name = "pipe", .open = qemu_chr_open_pipe }, + { .name = "pty", .open = qemu_chr_open_pty }, + { .name = "stdio", .open = qemu_chr_open_stdio }, +#endif +#ifdef CONFIG_BRLAPI + { .name = "braille", .open = chr_baum_init }, #endif #if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ - || defined(__NetBSD__) || defined(__OpenBSD__) - if (strstart(filename, "/dev/", NULL)) { - chr = qemu_chr_open_tty(filename); - } else + || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) + { .name = "tty", .open = qemu_chr_open_tty }, #endif -#else /* !_WIN32 */ - if (strstart(filename, "COM", NULL)) { - chr = qemu_chr_open_win(filename); - } else - if (strstart(filename, "pipe:", &p)) { - chr = qemu_chr_open_win_pipe(p); - } else - if (strstart(filename, "con:", NULL)) { - chr = qemu_chr_open_win_con(filename); - } else - if (strstart(filename, "file:", &p)) { - chr = qemu_chr_open_win_file_out(p); - } else +#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) + { .name = "parport", .open = qemu_chr_open_pp }, #endif -#ifdef CONFIG_BRLAPI - if (!strcmp(filename, "braille")) { - chr = chr_baum_init(); +}; + +CharDriverState *qemu_chr_open_opts(QemuOpts *opts, + void (*init)(struct CharDriverState *s)) +{ + CharDriverState *chr; + int i; + + if (qemu_opts_id(opts) == NULL) { + fprintf(stderr, "chardev: no id specified\n"); + return NULL; + } + + for (i = 0; i < ARRAY_SIZE(backend_table); i++) { + if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0) + break; + } + if (i == ARRAY_SIZE(backend_table)) { + fprintf(stderr, "chardev: backend \"%s\" not found\n", + qemu_opt_get(opts, "backend")); + return NULL; + } + + chr = backend_table[i].open(opts); + if (!chr) { + fprintf(stderr, "chardev: opening backend \"%s\" failed\n", + qemu_opt_get(opts, "backend")); + return NULL; + } + + if (!chr->filename) + chr->filename = qemu_strdup(qemu_opt_get(opts, "backend")); + chr->init = init; + TAILQ_INSERT_TAIL(&chardevs, chr, next); + + if (qemu_opt_get_bool(opts, "mux", 0)) { + CharDriverState *base = chr; + int len = strlen(qemu_opts_id(opts)) + 6; + base->label = qemu_malloc(len); + snprintf(base->label, len, "%s-base", qemu_opts_id(opts)); + chr = qemu_chr_open_mux(base); + chr->filename = base->filename; + TAILQ_INSERT_TAIL(&chardevs, chr, next); + } + chr->label = qemu_strdup(qemu_opts_id(opts)); + return chr; +} + +CharDriverState *qemu_chr_open(const char *label, const char *filename, void (*init)(struct CharDriverState *s)) +{ + const char *p; + CharDriverState *chr; + QemuOpts *opts; + + opts = qemu_chr_parse_compat(label, filename); + if (opts) { + chr = qemu_chr_open_opts(opts, init); + if (qemu_opt_get_bool(opts, "mux", 0)) { + monitor_init(chr, MONITOR_USE_READLINE); + } + return chr; + } + + if (strstart(filename, "udp:", &p)) { + chr = qemu_chr_open_udp(p); } else -#endif { chr = NULL; }