aboutsummaryrefslogtreecommitdiffstats
path: root/qemu-char.c
diff options
context:
space:
mode:
authorDavid Turner <digit@android.com>2010-09-10 13:02:07 +0200
committerDavid 'Digit' Turner <digit@android.com>2010-09-13 00:30:35 -0700
commit9d1188280fa5294f9438424e5e8eae91f645bfd8 (patch)
treef50f39aa43923a3c407ec55d72e5031a5aa45e21 /qemu-char.c
parent622311e8dfe485ba588b570c69d66e71d9624104 (diff)
downloadexternal_qemu-9d1188280fa5294f9438424e5e8eae91f645bfd8.zip
external_qemu-9d1188280fa5294f9438424e5e8eae91f645bfd8.tar.gz
external_qemu-9d1188280fa5294f9438424e5e8eae91f645bfd8.tar.bz2
upstream: qemu-char updates.
Diffstat (limited to 'qemu-char.c')
-rw-r--r--qemu-char.c799
1 files changed, 573 insertions, 226 deletions
diff --git a/qemu-char.c b/qemu-char.c
index 18e9ac0..d941d37 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -32,6 +32,7 @@
#include "hw/usb.h"
#include "hw/baum.h"
#include "hw/msmouse.h"
+#include "qemu-objects.h"
#include <unistd.h>
#include <fcntl.h>
@@ -103,21 +104,32 @@
#include "qemu_socket.h"
+#define READ_BUF_LEN 4096
+
/***********************************************************/
/* character device */
-static TAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
- TAILQ_HEAD_INITIALIZER(chardevs);
-static int initial_reset_issued;
+static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =
+ QTAILQ_HEAD_INITIALIZER(chardevs);
static void qemu_chr_event(CharDriverState *s, int event)
{
+ /* Keep track if the char device is open */
+ switch (event) {
+ case CHR_EVENT_OPENED:
+ s->opened = 1;
+ break;
+ case CHR_EVENT_CLOSED:
+ s->opened = 0;
+ break;
+ }
+
if (!s->chr_event)
return;
s->chr_event(s->handler_opaque, event);
}
-static void qemu_chr_reset_bh(void *opaque)
+static void qemu_chr_generic_open_bh(void *opaque)
{
CharDriverState *s = opaque;
qemu_chr_event(s, CHR_EVENT_OPENED);
@@ -125,10 +137,18 @@ static void qemu_chr_reset_bh(void *opaque)
s->bh = NULL;
}
+void qemu_chr_generic_open(CharDriverState *s)
+{
+ if (s->bh == NULL) {
+ s->bh = qemu_bh_new(qemu_chr_generic_open_bh, s);
+ qemu_bh_schedule(s->bh);
+ }
+}
+
void qemu_chr_reset(CharDriverState *s)
{
if (s->bh == NULL && initial_reset_issued) {
- s->bh = qemu_bh_new(qemu_chr_reset_bh, s);
+ s->bh = qemu_bh_new(qemu_chr_generic_open_bh, s);
qemu_bh_schedule(s->bh);
}
}
@@ -168,6 +188,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)
@@ -176,7 +201,7 @@ void qemu_chr_accept_input(CharDriverState *s)
void qemu_chr_printf(CharDriverState *s, const char *fmt, ...)
{
- char buf[4096];
+ char buf[READ_BUF_LEN];
va_list ap;
va_start(ap, fmt);
vsnprintf(buf, sizeof(buf), fmt, ap);
@@ -202,6 +227,12 @@ void qemu_chr_add_handlers(CharDriverState *s,
s->handler_opaque = opaque;
if (s->chr_update_read_handler)
s->chr_update_read_handler(s);
+
+ /* We're connecting to an already opened device, so let's make sure we
+ also get the open event */
+ if (s->opened) {
+ qemu_chr_generic_open(s);
+ }
}
static int null_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
@@ -209,7 +240,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;
@@ -228,6 +259,7 @@ typedef struct {
IOEventHandler *chr_event[MAX_MUX];
void *ext_opaque[MAX_MUX];
CharDriverState *drv;
+ int focus;
int mux_cnt;
int term_got_escape;
int max_size;
@@ -356,11 +388,11 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
break;
case 'c':
/* Switch to the next registered device */
- mux_chr_send_event(d, chr->focus, CHR_EVENT_MUX_OUT);
- chr->focus++;
- if (chr->focus >= d->mux_cnt)
- chr->focus = 0;
- mux_chr_send_event(d, chr->focus, CHR_EVENT_MUX_IN);
+ mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
+ d->focus++;
+ if (d->focus >= d->mux_cnt)
+ d->focus = 0;
+ mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
break;
case 't':
d->timestamps = !d->timestamps;
@@ -379,8 +411,8 @@ static int mux_proc_byte(CharDriverState *chr, MuxDriver *d, int ch)
static void mux_chr_accept_input(CharDriverState *chr)
{
- int m = chr->focus;
MuxDriver *d = chr->opaque;
+ int m = d->focus;
while (d->prod[m] != d->cons[m] &&
d->chr_can_read[m] &&
@@ -394,7 +426,7 @@ static int mux_chr_can_read(void *opaque)
{
CharDriverState *chr = opaque;
MuxDriver *d = chr->opaque;
- int m = chr->focus;
+ int m = d->focus;
if ((d->prod[m] - d->cons[m]) < MUX_BUFFER_SIZE)
return 1;
@@ -407,7 +439,7 @@ static void mux_chr_read(void *opaque, const uint8_t *buf, int size)
{
CharDriverState *chr = opaque;
MuxDriver *d = chr->opaque;
- int m = chr->focus;
+ int m = d->focus;
int i;
mux_chr_accept_input (opaque);
@@ -451,8 +483,12 @@ static void mux_chr_update_read_handler(CharDriverState *chr)
qemu_chr_add_handlers(d->drv, mux_chr_can_read, mux_chr_read,
mux_chr_event, chr);
}
- chr->focus = d->mux_cnt;
+ if (d->focus != -1) {
+ mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_OUT);
+ }
+ d->focus = d->mux_cnt;
d->mux_cnt++;
+ mux_chr_send_event(d, d->focus, CHR_EVENT_MUX_IN);
}
static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
@@ -465,10 +501,14 @@ static CharDriverState *qemu_chr_open_mux(CharDriverState *drv)
chr->opaque = d;
d->drv = drv;
- chr->focus = -1;
+ d->focus = -1;
chr->chr_write = mux_chr_write;
chr->chr_update_read_handler = mux_chr_update_read_handler;
chr->chr_accept_input = mux_chr_accept_input;
+
+ /* Muxes are always open on creation */
+ qemu_chr_generic_open(chr);
+
return chr;
}
@@ -554,7 +594,7 @@ static void fd_chr_read(void *opaque)
CharDriverState *chr = opaque;
FDCharDriver *s = chr->opaque;
int size, len;
- uint8_t buf[1024];
+ uint8_t buf[READ_BUF_LEN];
len = sizeof(buf);
if (len > s->max_size)
@@ -565,6 +605,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) {
@@ -597,6 +638,7 @@ static void fd_chr_close(struct CharDriverState *chr)
}
qemu_free(s);
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
}
/* open a character device to a unix fd */
@@ -614,30 +656,37 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
chr->chr_update_read_handler = fd_chr_update_read_handler;
chr->chr_close = fd_chr_close;
- qemu_chr_reset(chr);
+ qemu_chr_generic_open(chr);
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 = qemu_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);
- TFR(fd_in = open(filename_in, O_RDWR | O_BINARY));
- TFR(fd_out = open(filename_out, O_RDWR | O_BINARY));
+ TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY));
+ TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY));
if (fd_in < 0 || fd_out < 0) {
if (fd_in >= 0)
close(fd_in);
@@ -685,6 +734,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) {
@@ -707,7 +757,7 @@ static void term_exit(void)
fcntl(0, F_SETFL, old_fd0_flags);
}
-static void term_init(void)
+static void term_init(QemuOpts *opts)
{
struct termios tty;
@@ -720,7 +770,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 (display_type == DT_NOGRAPHIC)
+ if (!qemu_opt_get_bool(opts, "signal", display_type != DT_NOGRAPHIC))
tty.c_lflag &= ~ISIG;
tty.c_cflag &= ~(CSIZE|PARENB);
tty.c_cflag |= CS8;
@@ -743,7 +793,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;
@@ -753,7 +803,7 @@ static CharDriverState *qemu_chr_open_stdio(void)
chr->chr_close = qemu_chr_close_stdio;
qemu_set_fd_handler2(0, stdio_read_poll, stdio_read, NULL, chr);
stdio_nb_clients++;
- term_init();
+ term_init(opts);
return chr;
}
@@ -816,7 +866,8 @@ static void cfmakeraw (struct termios *termios_p)
#endif
#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
- || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
+ || defined(__GLIBC__)
typedef struct {
int fd;
@@ -855,7 +906,7 @@ static void pty_chr_read(void *opaque)
CharDriverState *chr = opaque;
PtyCharDriver *s = chr->opaque;
int size, len;
- uint8_t buf[1024];
+ uint8_t buf[READ_BUF_LEN];
len = sizeof(buf);
if (len > s->read_bytes)
@@ -906,7 +957,7 @@ static void pty_chr_state(CharDriverState *chr, int connected)
qemu_mod_timer(s->timer, qemu_get_clock(rt_clock) + 1000);
} else {
if (!s->connected)
- qemu_chr_reset(chr);
+ qemu_chr_generic_open(chr);
s->connected = 1;
}
}
@@ -938,9 +989,10 @@ static void pty_chr_close(struct CharDriverState *chr)
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;
@@ -970,6 +1022,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;
@@ -993,34 +1046,72 @@ static void tty_serial_init(int fd, int speed,
speed, parity, data_bits, stop_bits);
#endif
tcgetattr (fd, &tty);
-
-#define MARGIN 1.1
- if (speed <= 50 * MARGIN)
- spd = B50;
- else if (speed <= 75 * MARGIN)
- spd = B75;
- else if (speed <= 300 * MARGIN)
- spd = B300;
- else if (speed <= 600 * MARGIN)
- spd = B600;
- else if (speed <= 1200 * MARGIN)
- spd = B1200;
- else if (speed <= 2400 * MARGIN)
- spd = B2400;
- else if (speed <= 4800 * MARGIN)
- spd = B4800;
- else if (speed <= 9600 * MARGIN)
- spd = B9600;
- else if (speed <= 19200 * MARGIN)
- spd = B19200;
- else if (speed <= 38400 * MARGIN)
- spd = B38400;
- else if (speed <= 57600 * MARGIN)
- spd = B57600;
- else if (speed <= 115200 * MARGIN)
- spd = B115200;
- else
+ if (!term_atexit_done) {
+ oldtty = tty;
+ }
+
+#define check_speed(val) if (speed <= val) { spd = B##val; break; }
+ speed = speed * 10 / 11;
+ do {
+ check_speed(50);
+ check_speed(75);
+ check_speed(110);
+ check_speed(134);
+ check_speed(150);
+ check_speed(200);
+ check_speed(300);
+ check_speed(600);
+ check_speed(1200);
+ check_speed(1800);
+ check_speed(2400);
+ check_speed(4800);
+ check_speed(9600);
+ check_speed(19200);
+ check_speed(38400);
+ /* Non-Posix values follow. They may be unsupported on some systems. */
+ check_speed(57600);
+ check_speed(115200);
+#ifdef B230400
+ check_speed(230400);
+#endif
+#ifdef B460800
+ check_speed(460800);
+#endif
+#ifdef B500000
+ check_speed(500000);
+#endif
+#ifdef B576000
+ check_speed(576000);
+#endif
+#ifdef B921600
+ check_speed(921600);
+#endif
+#ifdef B1000000
+ check_speed(1000000);
+#endif
+#ifdef B1152000
+ check_speed(1152000);
+#endif
+#ifdef B1500000
+ check_speed(1500000);
+#endif
+#ifdef B2000000
+ check_speed(2000000);
+#endif
+#ifdef B2500000
+ check_speed(2500000);
+#endif
+#ifdef B3000000
+ check_speed(3000000);
+#endif
+#ifdef B3500000
+ check_speed(3500000);
+#endif
+#ifdef B4000000
+ check_speed(4000000);
+#endif
spd = B115200;
+ } while (0);
cfsetispeed(&tty, spd);
cfsetospeed(&tty, spd);
@@ -1129,12 +1220,37 @@ static int tty_serial_ioctl(CharDriverState *chr, int cmd, void *arg)
return 0;
}
-static CharDriverState *qemu_chr_open_tty(const char *filename)
+static void tty_exit(void)
+{
+ tcsetattr(0, TCSANOW, &oldtty);
+}
+
+static void qemu_chr_close_tty(CharDriverState *chr)
+{
+ FDCharDriver *s = chr->opaque;
+ int fd = -1;
+
+ if (s) {
+ fd = s->fd_in;
+ }
+
+ fd_chr_close(chr);
+
+ if (fd >= 0) {
+ close(fd);
+ }
+}
+
+static CharDriverState *qemu_chr_open_tty(QemuOpts *opts)
{
+ const char *filename = qemu_opt_get(opts, "path");
CharDriverState *chr;
int fd;
TFR(fd = open(filename, O_RDWR | O_NONBLOCK));
+ if (fd < 0) {
+ return NULL;
+ }
tty_serial_init(fd, 115200, 'N', 8, 1);
chr = qemu_chr_open_fd(fd, fd);
if (!chr) {
@@ -1142,11 +1258,13 @@ static CharDriverState *qemu_chr_open_tty(const char *filename)
return NULL;
}
chr->chr_ioctl = tty_serial_ioctl;
- qemu_chr_reset(chr);
+ chr->chr_close = qemu_chr_close_tty;
+ if (!term_atexit_done++)
+ atexit(tty_exit);
return chr;
}
#else /* ! __linux__ && ! __sun__ */
-static CharDriverState *qemu_chr_open_pty(void)
+static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
{
return NULL;
}
@@ -1259,10 +1377,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;
@@ -1286,16 +1406,16 @@ static CharDriverState *qemu_chr_open_pp(const char *filename)
chr->chr_close = pp_close;
chr->opaque = drv;
- qemu_chr_reset(chr);
+ qemu_chr_generic_open(chr);
return chr;
}
#endif /* __linux__ */
-#if defined(__FreeBSD__) || defined(__DragonFly__)
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__DragonFly__)
static int pp_ioctl(CharDriverState *chr, int cmd, void *arg)
{
- int fd = (int)chr->opaque;
+ int fd = (int)(long)chr->opaque;
uint8_t b;
switch(cmd) {
@@ -1330,8 +1450,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;
@@ -1340,7 +1461,7 @@ static CharDriverState *qemu_chr_open_pp(const char *filename)
return NULL;
chr = qemu_mallocz(sizeof(CharDriverState));
- chr->opaque = (void *)fd;
+ chr->opaque = (void *)(long)fd;
chr->chr_write = null_chr_write;
chr->chr_ioctl = pp_ioctl;
return chr;
@@ -1385,6 +1506,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)
@@ -1500,7 +1623,7 @@ static void win_chr_readfile(CharDriverState *chr)
{
WinCharState *s = chr->opaque;
int ret, err;
- uint8_t buf[1024];
+ uint8_t buf[READ_BUF_LEN];
DWORD size;
ZeroMemory(&s->orecv, sizeof(s->orecv));
@@ -1547,8 +1670,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;
@@ -1563,7 +1687,7 @@ static CharDriverState *qemu_chr_open_win(const char *filename)
free(chr);
return NULL;
}
- qemu_chr_reset(chr);
+ qemu_chr_generic_open(chr);
return chr;
}
@@ -1646,8 +1770,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;
@@ -1662,7 +1787,7 @@ static CharDriverState *qemu_chr_open_win_pipe(const char *filename)
free(chr);
return NULL;
}
- qemu_chr_reset(chr);
+ qemu_chr_generic_open(chr);
return chr;
}
@@ -1676,17 +1801,18 @@ static CharDriverState *qemu_chr_open_win_file(HANDLE fd_out)
s->hcom = fd_out;
chr->opaque = s;
chr->chr_write = win_chr_write;
- qemu_chr_reset(chr);
+ qemu_chr_generic_open(chr);
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,
@@ -1703,8 +1829,7 @@ static CharDriverState *qemu_chr_open_win_file_out(const char *file_out)
typedef struct {
int fd;
- struct sockaddr_in daddr;
- uint8_t buf[1024];
+ uint8_t buf[READ_BUF_LEN];
int bufcnt;
int bufptr;
int max_size;
@@ -1714,8 +1839,7 @@ static int udp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
{
NetCharDriver *s = chr->opaque;
- return sendto(s->fd, (const void *)buf, len, 0,
- (struct sockaddr *)&s->daddr, sizeof(struct sockaddr_in));
+ return send(s->fd, (const void *)buf, len, 0);
}
static int udp_chr_read_poll(void *opaque)
@@ -1774,32 +1898,21 @@ static void udp_chr_close(CharDriverState *chr)
closesocket(s->fd);
}
qemu_free(s);
+ qemu_chr_event(chr, CHR_EVENT_CLOSED);
}
-static CharDriverState *qemu_chr_open_udp(const char *def)
+static CharDriverState *qemu_chr_open_udp(QemuOpts *opts)
{
CharDriverState *chr = NULL;
NetCharDriver *s = NULL;
int fd = -1;
- struct sockaddr_in saddr;
chr = qemu_mallocz(sizeof(CharDriverState));
s = qemu_mallocz(sizeof(NetCharDriver));
- fd = socket(PF_INET, SOCK_DGRAM, 0);
+ fd = inet_dgram_opts(opts);
if (fd < 0) {
- perror("socket(PF_INET, SOCK_DGRAM)");
- goto return_err;
- }
-
- if (parse_host_src_port(&s->daddr, &saddr, def) < 0) {
- printf("Could not parse: %s\n", def);
- goto return_err;
- }
-
- if (bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0)
- {
- perror("bind");
+ fprintf(stderr, "inet_dgram_opts failed\n");
goto return_err;
}
@@ -1832,6 +1945,7 @@ typedef struct {
int do_telnetopt;
int do_nodelay;
int is_unix;
+ int msgfd;
} TCPCharDriver;
static void tcp_chr_accept(void *opaque);
@@ -1907,11 +2021,76 @@ static void tcp_chr_process_IAC_bytes(CharDriverState *chr,
*size = j;
}
+static int tcp_get_msgfd(CharDriverState *chr)
+{
+ TCPCharDriver *s = chr->opaque;
+ int fd = s->msgfd;
+ s->msgfd = -1;
+ return fd;
+}
+
+#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;
TCPCharDriver *s = chr->opaque;
- uint8_t buf[1024];
+ uint8_t buf[READ_BUF_LEN];
int len, size;
if (!s->connected || s->max_size <= 0)
@@ -1919,7 +2098,7 @@ static void tcp_chr_read(void *opaque)
len = sizeof(buf);
if (len > s->max_size)
len = s->max_size;
- size = recv(s->fd, (void *)buf, len, 0);
+ size = tcp_chr_recv(chr, (void *)buf, len);
if (size == 0) {
/* connection closed */
s->connected = 0;
@@ -1929,6 +2108,7 @@ 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);
@@ -1937,6 +2117,13 @@ static void tcp_chr_read(void *opaque)
}
}
+#ifndef _WIN32
+CharDriverState *qemu_chr_open_eventfd(int eventfd)
+{
+ return qemu_chr_open_fd(eventfd, eventfd);
+}
+#endif
+
static void tcp_chr_connect(void *opaque)
{
CharDriverState *chr = opaque;
@@ -1945,7 +2132,7 @@ static void tcp_chr_connect(void *opaque)
s->connected = 1;
qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
tcp_chr_read, NULL, chr);
- qemu_chr_reset(chr);
+ qemu_chr_generic_open(chr);
}
#define IACSET(x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
@@ -1992,7 +2179,7 @@ static void tcp_chr_accept(void *opaque)
len = sizeof(saddr);
addr = (struct sockaddr *)&saddr;
}
- fd = accept(s->listen_fd, addr, &len);
+ fd = qemu_accept(s->listen_fd, addr, &len);
if (fd < 0 && errno != EINTR) {
return;
} else if (fd >= 0) {
@@ -2021,69 +2208,42 @@ static void tcp_chr_close(CharDriverState *chr)
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)
@@ -2095,18 +2255,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;
@@ -2114,14 +2277,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);
@@ -2130,103 +2309,237 @@ 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))
+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_find_opts("chardev"), 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, "udp:", &p)) {
+ qemu_opt_set(opts, "backend", "udp");
+ 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, "host", host);
+ qemu_opt_set(opts, "port", port);
+ if (p[pos] == '@') {
+ p += pos + 1;
+ 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, "localaddr", host);
+ qemu_opt_set(opts, "localport", port);
+ }
+ 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__) || defined(__DragonFly__)
- if (strstart(filename, "/dev/ppi", NULL)) {
- chr = qemu_chr_open_pp(filename);
- } else
-#endif
-#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
- || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
+ 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)) {
- chr = qemu_chr_open_tty(filename);
- } else
+ qemu_opt_set(opts, "backend", "tty");
+ qemu_opt_set(opts, "path", filename);
+ return opts;
+ }
+
+fail:
+ 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 = "udp", .open = qemu_chr_open_udp },
+ { .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
-#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
+#ifdef CONFIG_ANDROID
+ { .name = "fdpair", .open = qemu_chr_open_fdpair },
#endif
#ifdef CONFIG_BRLAPI
- if (!strcmp(filename, "braille")) {
- chr = chr_baum_init();
- } else
+ { .name = "braille", .open = chr_baum_init },
+#endif
+#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \
+ || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \
+ || defined(__FreeBSD_kernel__)
+ { .name = "tty", .open = qemu_chr_open_tty },
+#endif
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) \
+ || defined(__FreeBSD_kernel__)
+ { .name = "parport", .open = qemu_chr_open_pp },
#endif
- {
- chr = NULL;
+};
+
+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;
}
- if (chr) {
- if (!chr->filename)
- chr->filename = qemu_strdup(filename);
- chr->init = init;
- chr->label = qemu_strdup(label);
- TAILQ_INSERT_TAIL(&chardevs, chr, next);
+ 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;
+ QTAILQ_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;
+ QTAILQ_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;
+
+ if (strstart(filename, "chardev:", &p)) {
+ return qemu_chr_find(p);
+ }
+
+ opts = qemu_chr_parse_compat(label, filename);
+ if (!opts)
+ return NULL;
+
+ chr = qemu_chr_open_opts(opts, init);
+ if (chr && qemu_opt_get_bool(opts, "mux", 0)) {
+ monitor_init(chr, MONITOR_USE_READLINE);
}
return chr;
}
void qemu_chr_close(CharDriverState *chr)
{
- TAILQ_REMOVE(&chardevs, chr, next);
+ QTAILQ_REMOVE(&chardevs, chr, next);
if (chr->chr_close)
chr->chr_close(chr);
qemu_free(chr->filename);
@@ -2234,11 +2547,45 @@ void qemu_chr_close(CharDriverState *chr)
qemu_free(chr);
}
-void qemu_chr_info(Monitor *mon)
+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)
+{
+ QList *chr_list;
+ 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);
+ }
+
+ *ret_data = QOBJECT(chr_list);
+}
+
+CharDriverState *qemu_chr_find(const char *name)
{
CharDriverState *chr;
QTAILQ_FOREACH(chr, &chardevs, next) {
- monitor_printf(mon, "%s: filename=%s\n", chr->label, chr->filename);
+ if (strcmp(chr->label, name) != 0)
+ continue;
+ return chr;
}
+ return NULL;
}