X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/f69554b9e859f5672afda827a9f8772ff41d0f59..d9bafcd1db602b199dfffc8bcfef08f42f447d38:/qemu-char.c diff --git a/qemu-char.c b/qemu-char.c index 13952355e1..bb9e3f50a8 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -31,7 +31,7 @@ #include "hw/usb.h" #include "hw/baum.h" #include "hw/msmouse.h" -#include "qemu-objects.h" +#include "qmp-commands.h" #include #include @@ -106,7 +106,7 @@ static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs = QTAILQ_HEAD_INITIALIZER(chardevs); -static void qemu_chr_event(CharDriverState *s, int event) +void qemu_chr_be_event(CharDriverState *s, int event) { /* Keep track if the char device is open */ switch (event) { @@ -126,7 +126,7 @@ static void qemu_chr_event(CharDriverState *s, int event) static void qemu_chr_generic_open_bh(void *opaque) { CharDriverState *s = opaque; - qemu_chr_event(s, CHR_EVENT_OPENED); + qemu_chr_be_event(s, CHR_EVENT_OPENED); qemu_bh_delete(s->bh); s->bh = NULL; } @@ -163,7 +163,7 @@ void qemu_chr_be_write(CharDriverState *s, uint8_t *buf, int len) s->chr_read(s->handler_opaque, buf, len); } -int qemu_chr_get_msgfd(CharDriverState *s) +int qemu_chr_fe_get_msgfd(CharDriverState *s) { return s->get_msgfd ? s->get_msgfd(s) : -1; } @@ -189,12 +189,6 @@ void qemu_chr_fe_printf(CharDriverState *s, const char *fmt, ...) va_end(ap); } -void qemu_chr_send_event(CharDriverState *s, int event) -{ - if (s->chr_send_event) - s->chr_send_event(s, event); -} - void qemu_chr_add_handlers(CharDriverState *s, IOCanReadHandler *fd_can_read, IOReadHandler *fd_read, @@ -224,15 +218,13 @@ static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len) return len; } -static int qemu_chr_open_null(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_null(QemuOpts *opts) { CharDriverState *chr; chr = g_malloc0(sizeof(CharDriverState)); chr->chr_write = null_chr_write; - - *_chr= chr; - return 0; + return chr; } /* MUX driver for serial I/O splitting */ @@ -365,7 +357,7 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch) bdrv_commit_all(); break; case 'b': - qemu_chr_event(chr, CHR_EVENT_BREAK); + qemu_chr_be_event(chr, CHR_EVENT_BREAK); break; case 'c': /* Switch to the next registered device */ @@ -544,6 +536,9 @@ int send_all(int fd, const void *_buf, int len1) } #endif /* !_WIN32 */ +#define STDIO_MAX_CLIENTS 1 +static int stdio_nb_clients; + #ifndef _WIN32 typedef struct { @@ -551,8 +546,6 @@ typedef struct { int max_size; } FDCharDriver; -#define STDIO_MAX_CLIENTS 1 -static int stdio_nb_clients = 0; static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len) { @@ -585,7 +578,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); + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); return; } if (size > 0) { @@ -618,7 +611,7 @@ static void fd_chr_close(struct CharDriverState *chr) } g_free(s); - qemu_chr_event(chr, CHR_EVENT_CLOSED); + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } /* open a character device to a unix fd */ @@ -641,21 +634,19 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) return chr; } -static int qemu_chr_open_file_out(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_file_out(QemuOpts *opts) { int fd_out; TFR(fd_out = qemu_open(qemu_opt_get(opts, "path"), O_WRONLY | O_TRUNC | O_CREAT | O_BINARY, 0666)); if (fd_out < 0) { - return -errno; + return NULL; } - - *_chr = qemu_chr_open_fd(-1, fd_out); - return 0; + return qemu_chr_open_fd(-1, fd_out); } -static int qemu_chr_open_pipe(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_pipe(QemuOpts *opts) { int fd_in, fd_out; char filename_in[256], filename_out[256]; @@ -663,7 +654,7 @@ static int qemu_chr_open_pipe(QemuOpts *opts, CharDriverState **_chr) if (filename == NULL) { fprintf(stderr, "chardev: pipe: no filename given\n"); - return -EINVAL; + return NULL; } snprintf(filename_in, 256, "%s.in", filename); @@ -677,12 +668,10 @@ static int qemu_chr_open_pipe(QemuOpts *opts, CharDriverState **_chr) close(fd_out); TFR(fd_in = fd_out = qemu_open(filename, O_RDWR | O_BINARY)); if (fd_in < 0) { - return -errno; + return NULL; } } - - *_chr = qemu_chr_open_fd(fd_in, fd_out); - return 0; + return qemu_chr_open_fd(fd_in, fd_out); } @@ -720,7 +709,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); + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); return; } if (size > 0) { @@ -773,14 +762,13 @@ static void qemu_chr_close_stdio(struct CharDriverState *chr) fd_chr_close(chr); } -static int qemu_chr_open_stdio(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_stdio(QemuOpts *opts) { CharDriverState *chr; if (stdio_nb_clients >= STDIO_MAX_CLIENTS) { - return -EBUSY; + return NULL; } - if (stdio_nb_clients == 0) { old_fd0_flags = fcntl(0, F_GETFL); tcgetattr (0, &oldtty); @@ -797,8 +785,7 @@ static int qemu_chr_open_stdio(QemuOpts *opts, CharDriverState **_chr) display_type != DT_NOGRAPHIC); qemu_chr_fe_set_echo(chr, false); - *_chr = chr; - return 0; + return chr; } #ifdef __sun__ @@ -982,15 +969,15 @@ static void pty_chr_close(struct CharDriverState *chr) qemu_del_timer(s->timer); qemu_free_timer(s->timer); g_free(s); - qemu_chr_event(chr, CHR_EVENT_CLOSED); + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) { CharDriverState *chr; PtyCharDriver *s; struct termios tty; - int slave_fd, len; + int master_fd, slave_fd, len; #if defined(__OpenBSD__) || defined(__DragonFly__) char pty_name[PATH_MAX]; #define q_ptsname(x) pty_name @@ -999,11 +986,8 @@ static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) #define q_ptsname(x) ptsname(x) #endif - chr = g_malloc0(sizeof(CharDriverState)); - s = g_malloc0(sizeof(PtyCharDriver)); - - if (openpty(&s->fd, &slave_fd, pty_name, NULL, NULL) < 0) { - return -errno; + if (openpty(&master_fd, &slave_fd, pty_name, NULL, NULL) < 0) { + return NULL; } /* Set raw attributes on the pty. */ @@ -1012,21 +996,24 @@ static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) tcsetattr(slave_fd, TCSAFLUSH, &tty); close(slave_fd); - len = strlen(q_ptsname(s->fd)) + 5; + chr = g_malloc0(sizeof(CharDriverState)); + + len = strlen(q_ptsname(master_fd)) + 5; chr->filename = g_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)); + snprintf(chr->filename, len, "pty:%s", q_ptsname(master_fd)); + qemu_opt_set(opts, "path", q_ptsname(master_fd)); + fprintf(stderr, "char device redirected to %s\n", q_ptsname(master_fd)); + s = g_malloc0(sizeof(PtyCharDriver)); chr->opaque = s; chr->chr_write = pty_chr_write; chr->chr_update_read_handler = pty_chr_update_read_handler; chr->chr_close = pty_chr_close; + s->fd = master_fd; s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr); - *_chr = chr; - return 0; + return chr; } static void tty_serial_init(int fd, int speed, @@ -1227,7 +1214,7 @@ static void qemu_chr_close_tty(CharDriverState *chr) } } -static int qemu_chr_open_tty(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_tty(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1235,20 +1222,18 @@ static int qemu_chr_open_tty(QemuOpts *opts, CharDriverState **_chr) TFR(fd = qemu_open(filename, O_RDWR | O_NONBLOCK)); if (fd < 0) { - return -errno; + return NULL; } tty_serial_init(fd, 115200, 'N', 8, 1); chr = qemu_chr_open_fd(fd, fd); chr->chr_ioctl = tty_serial_ioctl; chr->chr_close = qemu_chr_close_tty; - - *_chr = chr; - return 0; + return chr; } #else /* ! __linux__ && ! __sun__ */ -static int qemu_chr_open_pty(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_pty(QemuOpts *opts) { - return -ENOTSUP; + return NULL; } #endif /* __linux__ || __sun__ */ @@ -1359,24 +1344,24 @@ static void pp_close(CharDriverState *chr) ioctl(fd, PPRELEASE); close(fd); g_free(drv); - qemu_chr_event(chr, CHR_EVENT_CLOSED); + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; ParallelCharDriver *drv; int fd; - TFR(fd = open(filename, O_RDWR)); + TFR(fd = qemu_open(filename, O_RDWR)); if (fd < 0) { - return -errno; + return NULL; } if (ioctl(fd, PPCLAIM) < 0) { close(fd); - return -errno; + return NULL; } drv = g_malloc0(sizeof(ParallelCharDriver)); @@ -1391,8 +1376,7 @@ static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) qemu_chr_generic_open(chr); - *_chr = chr; - return 0; + return chr; } #endif /* __linux__ */ @@ -1434,7 +1418,7 @@ static int pp_ioctl(CharDriverState *chr, int cmd, void *arg) return 0; } -static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_pp(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1442,21 +1426,21 @@ static int qemu_chr_open_pp(QemuOpts *opts, CharDriverState **_chr) fd = qemu_open(filename, O_RDWR); if (fd < 0) { - return -errno; + return NULL; } chr = g_malloc0(sizeof(CharDriverState)); chr->opaque = (void *)(intptr_t)fd; chr->chr_write = null_chr_write; chr->chr_ioctl = pp_ioctl; - - *_chr = chr; - return 0; + return chr; } #endif #else /* _WIN32 */ +static CharDriverState *stdio_clients[STDIO_MAX_CLIENTS]; + typedef struct { int max_size; HANDLE hcom, hrecv, hsend; @@ -1465,6 +1449,14 @@ typedef struct { DWORD len; } WinCharState; +typedef struct { + HANDLE hStdIn; + HANDLE hInputReadyEvent; + HANDLE hInputDoneEvent; + HANDLE hInputThread; + uint8_t win_stdio_buf; +} WinStdioCharState; + #define NSENDBUF 2048 #define NRECVBUF 2048 #define MAXCONNECT 1 @@ -1494,7 +1486,7 @@ static void win_chr_close(CharDriverState *chr) else qemu_del_polling_cb(win_chr_poll, chr); - qemu_chr_event(chr, CHR_EVENT_CLOSED); + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } static int win_chr_init(CharDriverState *chr, const char *filename) @@ -1657,7 +1649,7 @@ static int win_chr_poll(void *opaque) return 0; } -static int qemu_chr_open_win(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_win(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1670,14 +1662,12 @@ static int qemu_chr_open_win(QemuOpts *opts, CharDriverState **_chr) chr->chr_close = win_chr_close; if (win_chr_init(chr, filename) < 0) { - free(s); - free(chr); - return -EIO; + g_free(s); + g_free(chr); + return NULL; } qemu_chr_generic_open(chr); - - *_chr = chr; - return 0; + return chr; } static int win_chr_pipe_poll(void *opaque) @@ -1759,7 +1749,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename) } -static int qemu_chr_open_win_pipe(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_win_pipe(QemuOpts *opts) { const char *filename = qemu_opt_get(opts, "path"); CharDriverState *chr; @@ -1772,17 +1762,15 @@ static int qemu_chr_open_win_pipe(QemuOpts *opts, CharDriverState **_chr) chr->chr_close = win_chr_close; if (win_chr_pipe_init(chr, filename) < 0) { - free(s); - free(chr); - return -EIO; + g_free(s); + g_free(chr); + return NULL; } qemu_chr_generic_open(chr); - - *_chr = chr; - return 0; + return chr; } -static int qemu_chr_open_win_file(HANDLE fd_out, CharDriverState **pchr) +static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out) { CharDriverState *chr; WinCharState *s; @@ -1793,16 +1781,15 @@ static int qemu_chr_open_win_file(HANDLE fd_out, CharDriverState **pchr) chr->opaque = s; chr->chr_write = win_chr_write; qemu_chr_generic_open(chr); - *pchr = chr; - return 0; + return chr; } -static int qemu_chr_open_win_con(QemuOpts *opts, CharDriverState **chr) +static CharDriverState *qemu_chr_open_win_con(QemuOpts *opts) { - return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE), chr); + return qemu_chr_open_win_file(GetStdHandle(STD_OUTPUT_HANDLE)); } -static int qemu_chr_open_win_file_out(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_win_file_out(QemuOpts *opts) { const char *file_out = qemu_opt_get(opts, "path"); HANDLE fd_out; @@ -1810,10 +1797,219 @@ static int qemu_chr_open_win_file_out(QemuOpts *opts, CharDriverState **_chr) fd_out = CreateFile(file_out, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (fd_out == INVALID_HANDLE_VALUE) { - return -EIO; + return NULL; + } + + return qemu_chr_open_win_file(fd_out); +} + +static int win_stdio_write(CharDriverState *chr, const uint8_t *buf, int len) +{ + HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD dwSize; + int len1; + + len1 = len; + + while (len1 > 0) { + if (!WriteFile(hStdOut, buf, len1, &dwSize, NULL)) { + break; + } + buf += dwSize; + len1 -= dwSize; + } + + return len - len1; +} + +static void win_stdio_wait_func(void *opaque) +{ + CharDriverState *chr = opaque; + WinStdioCharState *stdio = chr->opaque; + INPUT_RECORD buf[4]; + int ret; + DWORD dwSize; + int i; + + ret = ReadConsoleInput(stdio->hStdIn, buf, sizeof(buf) / sizeof(*buf), + &dwSize); + + if (!ret) { + /* Avoid error storm */ + qemu_del_wait_object(stdio->hStdIn, NULL, NULL); + return; + } + + for (i = 0; i < dwSize; i++) { + KEY_EVENT_RECORD *kev = &buf[i].Event.KeyEvent; + + if (buf[i].EventType == KEY_EVENT && kev->bKeyDown) { + int j; + if (kev->uChar.AsciiChar != 0) { + for (j = 0; j < kev->wRepeatCount; j++) { + if (qemu_chr_be_can_write(chr)) { + uint8_t c = kev->uChar.AsciiChar; + qemu_chr_be_write(chr, &c, 1); + } + } + } + } + } +} + +static DWORD WINAPI win_stdio_thread(LPVOID param) +{ + CharDriverState *chr = param; + WinStdioCharState *stdio = chr->opaque; + int ret; + DWORD dwSize; + + while (1) { + + /* Wait for one byte */ + ret = ReadFile(stdio->hStdIn, &stdio->win_stdio_buf, 1, &dwSize, NULL); + + /* Exit in case of error, continue if nothing read */ + if (!ret) { + break; + } + if (!dwSize) { + continue; + } + + /* Some terminal emulator returns \r\n for Enter, just pass \n */ + if (stdio->win_stdio_buf == '\r') { + continue; + } + + /* Signal the main thread and wait until the byte was eaten */ + if (!SetEvent(stdio->hInputReadyEvent)) { + break; + } + if (WaitForSingleObject(stdio->hInputDoneEvent, INFINITE) + != WAIT_OBJECT_0) { + break; + } + } + + qemu_del_wait_object(stdio->hInputReadyEvent, NULL, NULL); + return 0; +} + +static void win_stdio_thread_wait_func(void *opaque) +{ + CharDriverState *chr = opaque; + WinStdioCharState *stdio = chr->opaque; + + if (qemu_chr_be_can_write(chr)) { + qemu_chr_be_write(chr, &stdio->win_stdio_buf, 1); + } + + SetEvent(stdio->hInputDoneEvent); +} + +static void qemu_chr_set_echo_win_stdio(CharDriverState *chr, bool echo) +{ + WinStdioCharState *stdio = chr->opaque; + DWORD dwMode = 0; + + GetConsoleMode(stdio->hStdIn, &dwMode); + + if (echo) { + SetConsoleMode(stdio->hStdIn, dwMode | ENABLE_ECHO_INPUT); + } else { + SetConsoleMode(stdio->hStdIn, dwMode & ~ENABLE_ECHO_INPUT); + } +} + +static void win_stdio_close(CharDriverState *chr) +{ + WinStdioCharState *stdio = chr->opaque; + + if (stdio->hInputReadyEvent != INVALID_HANDLE_VALUE) { + CloseHandle(stdio->hInputReadyEvent); + } + if (stdio->hInputDoneEvent != INVALID_HANDLE_VALUE) { + CloseHandle(stdio->hInputDoneEvent); + } + if (stdio->hInputThread != INVALID_HANDLE_VALUE) { + TerminateThread(stdio->hInputThread, 0); } - return qemu_chr_open_win_file(fd_out, _chr); + g_free(chr->opaque); + g_free(chr); + stdio_nb_clients--; +} + +static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts) +{ + CharDriverState *chr; + WinStdioCharState *stdio; + DWORD dwMode; + int is_console = 0; + + if (stdio_nb_clients >= STDIO_MAX_CLIENTS + || ((display_type != DT_NOGRAPHIC) && (stdio_nb_clients != 0))) { + return NULL; + } + + chr = g_malloc0(sizeof(CharDriverState)); + stdio = g_malloc0(sizeof(WinStdioCharState)); + + stdio->hStdIn = GetStdHandle(STD_INPUT_HANDLE); + if (stdio->hStdIn == INVALID_HANDLE_VALUE) { + fprintf(stderr, "cannot open stdio: invalid handle\n"); + exit(1); + } + + is_console = GetConsoleMode(stdio->hStdIn, &dwMode) != 0; + + chr->opaque = stdio; + chr->chr_write = win_stdio_write; + chr->chr_close = win_stdio_close; + + if (stdio_nb_clients == 0) { + if (is_console) { + if (qemu_add_wait_object(stdio->hStdIn, + win_stdio_wait_func, chr)) { + fprintf(stderr, "qemu_add_wait_object: failed\n"); + } + } else { + DWORD dwId; + + stdio->hInputReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + stdio->hInputDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + stdio->hInputThread = CreateThread(NULL, 0, win_stdio_thread, + chr, 0, &dwId); + + if (stdio->hInputThread == INVALID_HANDLE_VALUE + || stdio->hInputReadyEvent == INVALID_HANDLE_VALUE + || stdio->hInputDoneEvent == INVALID_HANDLE_VALUE) { + fprintf(stderr, "cannot create stdio thread or event\n"); + exit(1); + } + if (qemu_add_wait_object(stdio->hInputReadyEvent, + win_stdio_thread_wait_func, chr)) { + fprintf(stderr, "qemu_add_wait_object: failed\n"); + } + } + } + + dwMode |= ENABLE_LINE_INPUT; + + stdio_clients[stdio_nb_clients++] = chr; + if (stdio_nb_clients == 1 && is_console) { + /* set the terminal in raw mode */ + /* ENABLE_QUICK_EDIT_MODE | ENABLE_EXTENDED_FLAGS */ + dwMode |= ENABLE_PROCESSED_INPUT; + } + + SetConsoleMode(stdio->hStdIn, dwMode); + + chr->chr_set_echo = qemu_chr_set_echo_win_stdio; + qemu_chr_fe_set_echo(chr, false); + + return chr; } #endif /* !_WIN32 */ @@ -1887,19 +2083,18 @@ static void udp_chr_close(CharDriverState *chr) { NetCharDriver *s = chr->opaque; if (s->fd >= 0) { - qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); closesocket(s->fd); } g_free(s); - qemu_chr_event(chr, CHR_EVENT_CLOSED); + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static int qemu_chr_open_udp(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_udp(QemuOpts *opts) { CharDriverState *chr = NULL; NetCharDriver *s = NULL; int fd = -1; - int ret; chr = g_malloc0(sizeof(CharDriverState)); s = g_malloc0(sizeof(NetCharDriver)); @@ -1907,7 +2102,6 @@ static int qemu_chr_open_udp(QemuOpts *opts, CharDriverState **_chr) fd = inet_dgram_opts(opts); if (fd < 0) { fprintf(stderr, "inet_dgram_opts failed\n"); - ret = -errno; goto return_err; } @@ -1918,9 +2112,7 @@ static int qemu_chr_open_udp(QemuOpts *opts, CharDriverState **_chr) chr->chr_write = udp_chr_write; chr->chr_update_read_handler = udp_chr_update_read_handler; chr->chr_close = udp_chr_close; - - *_chr = chr; - return 0; + return chr; return_err: g_free(chr); @@ -1928,7 +2120,7 @@ return_err: if (fd >= 0) { closesocket(fd); } - return ret; + return NULL; } /***********************************************************/ @@ -1996,7 +2188,7 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr, } else { if ((unsigned char)buf[i] == IAC_BREAK && s->do_telnetopt == 2) { /* Handle IAC break commands by sending a serial break */ - qemu_chr_event(chr, CHR_EVENT_BREAK); + qemu_chr_be_event(chr, CHR_EVENT_BREAK); s->do_telnetopt++; } s->do_telnetopt++; @@ -2099,12 +2291,12 @@ static void tcp_chr_read(void *opaque) /* connection closed */ s->connected = 0; if (s->listen_fd >= 0) { - qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); + qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr); } - qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); closesocket(s->fd); s->fd = -1; - qemu_chr_event(chr, CHR_EVENT_CLOSED); + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } else if (size > 0) { if (s->do_telnetopt) tcp_chr_process_IAC_bytes(chr, s, buf, &size); @@ -2162,7 +2354,7 @@ static int tcp_chr_add_client(CharDriverState *chr, int fd) if (s->do_nodelay) socket_set_nodelay(fd); s->fd = fd; - qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); + qemu_set_fd_handler2(s->listen_fd, NULL, NULL, NULL, NULL); tcp_chr_connect(chr); return 0; @@ -2208,18 +2400,18 @@ static void tcp_chr_close(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; if (s->fd >= 0) { - qemu_set_fd_handler(s->fd, NULL, NULL, NULL); + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); closesocket(s->fd); } if (s->listen_fd >= 0) { - qemu_set_fd_handler(s->listen_fd, NULL, NULL, NULL); + qemu_set_fd_handler2(s->listen_fd, NULL, NULL, NULL, NULL); closesocket(s->listen_fd); } g_free(s); - qemu_chr_event(chr, CHR_EVENT_CLOSED); + qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) +static CharDriverState *qemu_chr_open_socket(QemuOpts *opts) { CharDriverState *chr = NULL; TCPCharDriver *s = NULL; @@ -2229,7 +2421,6 @@ static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) int do_nodelay; int is_unix; int is_telnet; - int ret; is_listen = qemu_opt_get_bool(opts, "server", 0); is_waitconnect = qemu_opt_get_bool(opts, "wait", 1); @@ -2256,7 +2447,6 @@ static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) } } if (fd < 0) { - ret = -errno; goto fail; } @@ -2278,7 +2468,7 @@ static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) if (is_listen) { s->listen_fd = fd; - qemu_set_fd_handler(s->listen_fd, tcp_chr_accept, NULL, chr); + qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr); if (is_telnet) s->do_telnetopt = 1; @@ -2311,16 +2501,14 @@ static int qemu_chr_open_socket(QemuOpts *opts, CharDriverState **_chr) tcp_chr_accept(chr); socket_set_nonblock(s->listen_fd); } - - *_chr = chr; - return 0; + return chr; fail: if (fd >= 0) closesocket(fd); g_free(s); g_free(chr); - return ret; + return NULL; } /***********************************************************/ @@ -2370,7 +2558,7 @@ QString *qemu_chr_mem_to_qs(CharDriverState *chr) return qstring_from_substr((char *) d->outbuf, 0, d->outbuf_size - 1); } -/* NOTE: this driver can not be closed with qemu_chr_close()! */ +/* NOTE: this driver can not be closed with qemu_chr_delete()! */ void qemu_chr_close_mem(CharDriverState *chr) { MemoryDriver *d = chr->opaque; @@ -2513,7 +2701,7 @@ fail: static const struct { const char *name; - int (*open)(QemuOpts *opts, CharDriverState **chr); + CharDriverState *(*open)(QemuOpts *opts); } backend_table[] = { { .name = "null", .open = qemu_chr_open_null }, { .name = "socket", .open = qemu_chr_open_socket }, @@ -2525,6 +2713,7 @@ static const struct { { .name = "pipe", .open = qemu_chr_open_win_pipe }, { .name = "console", .open = qemu_chr_open_win_con }, { .name = "serial", .open = qemu_chr_open_win }, + { .name = "stdio", .open = qemu_chr_open_win_stdio }, #else { .name = "file", .open = qemu_chr_open_file_out }, { .name = "pipe", .open = qemu_chr_open_pipe }, @@ -2553,7 +2742,6 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, { CharDriverState *chr; int i; - int ret; if (qemu_opts_id(opts) == NULL) { fprintf(stderr, "chardev: no id specified\n"); @@ -2575,10 +2763,10 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, return NULL; } - ret = backend_table[i].open(opts, &chr); - if (ret < 0) { - fprintf(stderr, "chardev: opening backend \"%s\" failed: %s\n", - qemu_opt_get(opts, "backend"), strerror(-ret)); + chr = backend_table[i].open(opts); + if (!chr) { + fprintf(stderr, "chardev: opening backend \"%s\" failed\n", + qemu_opt_get(opts, "backend")); return NULL; } @@ -2646,7 +2834,7 @@ void qemu_chr_fe_close(struct CharDriverState *chr) } } -void qemu_chr_close(CharDriverState *chr) +void qemu_chr_delete(CharDriverState *chr) { QTAILQ_REMOVE(&chardevs, chr, next); if (chr->chr_close) @@ -2656,35 +2844,22 @@ void qemu_chr_close(CharDriverState *chr) g_free(chr); } -static void qemu_chr_qlist_iter(QObject *obj, void *opaque) -{ - QDict *chr_dict; - Monitor *mon = opaque; - - chr_dict = qobject_to_qdict(obj); - monitor_printf(mon, "%s: filename=%s\n", qdict_get_str(chr_dict, "label"), - qdict_get_str(chr_dict, "filename")); -} - -void qemu_chr_info_print(Monitor *mon, const QObject *ret_data) -{ - qlist_iter(qobject_to_qlist(ret_data), qemu_chr_qlist_iter, mon); -} - -void qemu_chr_info(Monitor *mon, QObject **ret_data) +ChardevInfoList *qmp_query_chardev(Error **errp) { - QList *chr_list; + ChardevInfoList *chr_list = NULL; CharDriverState *chr; - chr_list = qlist_new(); - QTAILQ_FOREACH(chr, &chardevs, next) { - QObject *obj = qobject_from_jsonf("{ 'label': %s, 'filename': %s }", - chr->label, chr->filename); - qlist_append_obj(chr_list, obj); + ChardevInfoList *info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + info->value->label = g_strdup(chr->label); + info->value->filename = g_strdup(chr->filename); + + info->next = chr_list; + chr_list = info; } - *ret_data = QOBJECT(chr_list); + return chr_list; } CharDriverState *qemu_chr_find(const char *name) @@ -2698,3 +2873,13 @@ CharDriverState *qemu_chr_find(const char *name) } return NULL; } + +/* Get a character (serial) device interface. */ +CharDriverState *qemu_char_get_next_serial(void) +{ + static int next_serial; + + /* FIXME: This function needs to go away: use chardev properties! */ + return serial_hds[next_serial++]; +} +