]> git.proxmox.com Git - qemu.git/blobdiff - qemu-char.c
linux-user: Fix layout of usage table to account for option text
[qemu.git] / qemu-char.c
index 94ff33212a9411af46d7c68e91c38e6125af5817..04aa589c7ebd6e70c64ee3c87eb23ce5978946de 100644 (file)
@@ -28,8 +28,6 @@
 #include "qemu/timer.h"
 #include "char/char.h"
 #include "hw/usb.h"
-#include "hw/baum.h"
-#include "hw/msmouse.h"
 #include "qmp-commands.h"
 
 #include <unistd.h>
@@ -122,20 +120,18 @@ void qemu_chr_be_event(CharDriverState *s, int event)
     s->chr_event(s->handler_opaque, event);
 }
 
-static void qemu_chr_fire_open_event(void *opaque)
+static gboolean qemu_chr_generic_open_bh(gpointer opaque)
 {
     CharDriverState *s = opaque;
     qemu_chr_be_event(s, CHR_EVENT_OPENED);
-    qemu_free_timer(s->open_timer);
-    s->open_timer = NULL;
+    s->idle_tag = 0;
+    return FALSE;
 }
 
 void qemu_chr_generic_open(CharDriverState *s)
 {
-    if (s->open_timer == NULL) {
-        s->open_timer = qemu_new_timer_ms(rt_clock,
-                                          qemu_chr_fire_open_event, s);
-        qemu_mod_timer(s->open_timer, qemu_get_clock_ms(rt_clock) - 1);
+    if (s->idle_tag == 0) {
+        s->idle_tag = g_idle_add(qemu_chr_generic_open_bh, s);
     }
 }
 
@@ -539,8 +535,6 @@ int send_all(int fd, const void *_buf, int len1)
 }
 #endif /* !_WIN32 */
 
-#ifndef _WIN32
-
 typedef struct IOWatchPoll
 {
     GSource *src;
@@ -638,6 +632,7 @@ static guint io_add_watch_poll(GIOChannel *channel,
     return tag;
 }
 
+#ifndef _WIN32
 static GIOChannel *io_channel_from_fd(int fd)
 {
     GIOChannel *chan;
@@ -653,6 +648,7 @@ static GIOChannel *io_channel_from_fd(int fd)
 
     return chan;
 }
+#endif
 
 static GIOChannel *io_channel_from_socket(int fd)
 {
@@ -686,7 +682,11 @@ static int io_channel_send_all(GIOChannel *fd, const void *_buf, int len1)
         status = g_io_channel_write_chars(fd, (const gchar *)buf, len,
                                           &bytes_written, NULL);
         if (status != G_IO_STATUS_NORMAL) {
-            if (status != G_IO_STATUS_AGAIN) {
+            if (status == G_IO_STATUS_AGAIN) {
+                errno = EAGAIN;
+                return -1;
+            } else {
+                errno = EINVAL;
                 return -1;
             }
         } else if (status == G_IO_STATUS_EOF) {
@@ -699,6 +699,8 @@ static int io_channel_send_all(GIOChannel *fd, const void *_buf, int len1)
     return len1 - len;
 }
 
+#ifndef _WIN32
+
 typedef struct FDCharDriver {
     CharDriverState *chr;
     GIOChannel *fd_in, *fd_out;
@@ -753,6 +755,12 @@ static int fd_chr_read_poll(void *opaque)
     return s->max_size;
 }
 
+static GSource *fd_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+{
+    FDCharDriver *s = chr->opaque;
+    return g_io_create_watch(s->fd_out, cond);
+}
+
 static void fd_chr_update_read_handler(CharDriverState *chr)
 {
     FDCharDriver *s = chr->opaque;
@@ -796,8 +804,10 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out)
     s = g_malloc0(sizeof(FDCharDriver));
     s->fd_in = io_channel_from_fd(fd_in);
     s->fd_out = io_channel_from_fd(fd_out);
+    fcntl(fd_out, F_SETFL, O_NONBLOCK);
     s->chr = chr;
     chr->opaque = s;
+    chr->chr_add_watch = fd_chr_add_watch;
     chr->chr_write = fd_chr_write;
     chr->chr_update_read_handler = fd_chr_update_read_handler;
     chr->chr_close = fd_chr_close;
@@ -978,12 +988,50 @@ typedef struct {
     int connected;
     int polling;
     int read_bytes;
-    QEMUTimer *timer;
+    guint timer_tag;
 } PtyCharDriver;
 
 static void pty_chr_update_read_handler(CharDriverState *chr);
 static void pty_chr_state(CharDriverState *chr, int connected);
 
+static gboolean pty_chr_timer(gpointer opaque)
+{
+    struct CharDriverState *chr = opaque;
+    PtyCharDriver *s = chr->opaque;
+
+    if (s->connected) {
+        goto out;
+    }
+    if (s->polling) {
+        /* If we arrive here without polling being cleared due
+         * read returning -EIO, then we are (re-)connected */
+        pty_chr_state(chr, 1);
+        goto out;
+    }
+
+    /* Next poll ... */
+    pty_chr_update_read_handler(chr);
+
+out:
+    return FALSE;
+}
+
+static void pty_chr_rearm_timer(CharDriverState *chr, int ms)
+{
+    PtyCharDriver *s = chr->opaque;
+
+    if (s->timer_tag) {
+        g_source_remove(s->timer_tag);
+        s->timer_tag = 0;
+    }
+
+    if (ms == 1000) {
+        s->timer_tag = g_timeout_add_seconds(1, pty_chr_timer, chr);
+    } else {
+        s->timer_tag = g_timeout_add(ms, pty_chr_timer, chr);
+    }
+}
+
 static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     PtyCharDriver *s = chr->opaque;
@@ -996,6 +1044,12 @@ static int pty_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
     return io_channel_send_all(s->fd, buf, len);
 }
 
+static GSource *pty_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+{
+    PtyCharDriver *s = chr->opaque;
+    return g_io_create_watch(s->fd, cond);
+}
+
 static int pty_chr_read_poll(void *opaque)
 {
     CharDriverState *chr = opaque;
@@ -1047,7 +1101,7 @@ static void pty_chr_update_read_handler(CharDriverState *chr)
      * timeout to the normal (much longer) poll interval before the
      * timer triggers.
      */
-    qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 10);
+    pty_chr_rearm_timer(chr, 10);
 }
 
 static void pty_chr_state(CharDriverState *chr, int connected)
@@ -1062,7 +1116,7 @@ static void pty_chr_state(CharDriverState *chr, int connected)
         /* (re-)connect poll interval for idle guests: once per second.
          * We check more frequently in case the guests sends data to
          * the virtual device linked to our pty. */
-        qemu_mod_timer(s->timer, qemu_get_clock_ms(rt_clock) + 1000);
+        pty_chr_rearm_timer(chr, 1000);
     } else {
         if (!s->connected)
             qemu_chr_generic_open(chr);
@@ -1070,23 +1124,6 @@ static void pty_chr_state(CharDriverState *chr, int connected)
     }
 }
 
-static void pty_chr_timer(void *opaque)
-{
-    struct CharDriverState *chr = opaque;
-    PtyCharDriver *s = chr->opaque;
-
-    if (s->connected)
-        return;
-    if (s->polling) {
-        /* If we arrive here without polling being cleared due
-         * read returning -EIO, then we are (re-)connected */
-        pty_chr_state(chr, 1);
-        return;
-    }
-
-    /* Next poll ... */
-    pty_chr_update_read_handler(chr);
-}
 
 static void pty_chr_close(struct CharDriverState *chr)
 {
@@ -1099,8 +1136,9 @@ static void pty_chr_close(struct CharDriverState *chr)
     fd = g_io_channel_unix_get_fd(s->fd);
     g_io_channel_unref(s->fd);
     close(fd);
-    qemu_del_timer(s->timer);
-    qemu_free_timer(s->timer);
+    if (s->timer_tag) {
+        g_source_remove(s->timer_tag);
+    }
     g_free(s);
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
@@ -1149,9 +1187,10 @@ static CharDriverState *qemu_chr_open_pty(QemuOpts *opts)
     chr->chr_write = pty_chr_write;
     chr->chr_update_read_handler = pty_chr_update_read_handler;
     chr->chr_close = pty_chr_close;
+    chr->chr_add_watch = pty_chr_add_watch;
 
     s->fd = io_channel_from_fd(master_fd);
-    s->timer = qemu_new_timer_ms(rt_clock, pty_chr_timer, chr);
+    s->timer_tag = 0;
 
     return chr;
 }
@@ -2141,6 +2180,7 @@ static CharDriverState *qemu_chr_open_win_stdio(QemuOpts *opts)
 }
 #endif /* !_WIN32 */
 
+
 /***********************************************************/
 /* UDP Net console */
 
@@ -2285,6 +2325,9 @@ return_err:
 /* TCP Net console */
 
 typedef struct {
+
+    GIOChannel *chan, *listen_chan;
+    guint tag, listen_tag;
     int fd, listen_fd;
     int connected;
     int max_size;
@@ -2294,13 +2337,13 @@ typedef struct {
     int msgfd;
 } TCPCharDriver;
 
-static void tcp_chr_accept(void *opaque);
+static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque);
 
 static int tcp_chr_write(CharDriverState *chr, const uint8_t *buf, int len)
 {
     TCPCharDriver *s = chr->opaque;
     if (s->connected) {
-        return send_all(s->fd, buf, len);
+        return io_channel_send_all(s->chan, buf, len);
     } else {
         /* XXX: indicate an error ? */
         return len;
@@ -2440,15 +2483,22 @@ static ssize_t tcp_chr_recv(CharDriverState *chr, char *buf, size_t len)
 }
 #endif
 
-static void tcp_chr_read(void *opaque)
+static GSource *tcp_chr_add_watch(CharDriverState *chr, GIOCondition cond)
+{
+    TCPCharDriver *s = chr->opaque;
+    return g_io_create_watch(s->chan, cond);
+}
+
+static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
     TCPCharDriver *s = chr->opaque;
     uint8_t buf[READ_BUF_LEN];
     int len, size;
 
-    if (!s->connected || s->max_size <= 0)
-        return;
+    if (!s->connected || s->max_size <= 0) {
+        return FALSE;
+    }
     len = sizeof(buf);
     if (len > s->max_size)
         len = s->max_size;
@@ -2456,10 +2506,13 @@ static void tcp_chr_read(void *opaque)
     if (size == 0) {
         /* connection closed */
         s->connected = 0;
-        if (s->listen_fd >= 0) {
-            qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr);
+        if (s->listen_chan) {
+            s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
         }
-        qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+        g_source_remove(s->tag);
+        s->tag = 0;
+        g_io_channel_unref(s->chan);
+        s->chan = NULL;
         closesocket(s->fd);
         s->fd = -1;
         qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
@@ -2469,6 +2522,8 @@ static void tcp_chr_read(void *opaque)
         if (size > 0)
             qemu_chr_be_write(chr, buf, size);
     }
+
+    return TRUE;
 }
 
 #ifndef _WIN32
@@ -2484,9 +2539,8 @@ static void tcp_chr_connect(void *opaque)
     TCPCharDriver *s = chr->opaque;
 
     s->connected = 1;
-    if (s->fd >= 0) {
-        qemu_set_fd_handler2(s->fd, tcp_chr_read_poll,
-                             tcp_chr_read, NULL, chr);
+    if (s->chan) {
+        s->tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, tcp_chr_read, chr);
     }
     qemu_chr_generic_open(chr);
 }
@@ -2516,13 +2570,15 @@ static int tcp_chr_add_client(CharDriverState *chr, int fd)
     if (s->do_nodelay)
         socket_set_nodelay(fd);
     s->fd = fd;
-    qemu_set_fd_handler2(s->listen_fd, NULL, NULL, NULL, NULL);
+    s->chan = io_channel_from_socket(fd);
+    g_source_remove(s->listen_tag);
+    s->listen_tag = 0;
     tcp_chr_connect(chr);
 
     return 0;
 }
 
-static void tcp_chr_accept(void *opaque)
+static gboolean tcp_chr_accept(GIOChannel *channel, GIOCondition cond, void *opaque)
 {
     CharDriverState *chr = opaque;
     TCPCharDriver *s = chr->opaque;
@@ -2547,7 +2603,7 @@ static void tcp_chr_accept(void *opaque)
        }
         fd = qemu_accept(s->listen_fd, addr, &len);
         if (fd < 0 && errno != EINTR) {
-            return;
+            return FALSE;
         } else if (fd >= 0) {
             if (s->do_telnetopt)
                 tcp_chr_telnet_init(fd);
@@ -2556,17 +2612,29 @@ static void tcp_chr_accept(void *opaque)
     }
     if (tcp_chr_add_client(chr, fd) < 0)
        close(fd);
+
+    return TRUE;
 }
 
 static void tcp_chr_close(CharDriverState *chr)
 {
     TCPCharDriver *s = chr->opaque;
     if (s->fd >= 0) {
-        qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL);
+        if (s->tag) {
+            g_source_remove(s->tag);
+        }
+        if (s->chan) {
+            g_io_channel_unref(s->chan);
+        }
         closesocket(s->fd);
     }
     if (s->listen_fd >= 0) {
-        qemu_set_fd_handler2(s->listen_fd, NULL, NULL, NULL, NULL);
+        if (s->listen_tag) {
+            g_source_remove(s->listen_tag);
+        }
+        if (s->listen_chan) {
+            g_io_channel_unref(s->listen_chan);
+        }
         closesocket(s->listen_fd);
     }
     g_free(s);
@@ -2629,10 +2697,12 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay,
     chr->chr_close = tcp_chr_close;
     chr->get_msgfd = tcp_get_msgfd;
     chr->chr_add_client = tcp_chr_add_client;
+    chr->chr_add_watch = tcp_chr_add_watch;
 
     if (is_listen) {
         s->listen_fd = fd;
-        qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr);
+        s->listen_chan = io_channel_from_socket(s->listen_fd);
+        s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr);
         if (is_telnet) {
             s->do_telnetopt = 1;
         }
@@ -2640,13 +2710,14 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay,
         s->connected = 1;
         s->fd = fd;
         socket_set_nodelay(fd);
+        s->chan = io_channel_from_socket(s->fd);
         tcp_chr_connect(chr);
     }
 
     if (is_listen && is_waitconnect) {
         printf("QEMU waiting for connection on: %s\n",
                chr->filename);
-        tcp_chr_accept(chr);
+        tcp_chr_accept(s->listen_chan, G_IO_IN, chr);
         socket_set_nonblock(s->listen_fd);
     }
     return chr;
@@ -3107,53 +3178,31 @@ static CharDriverState *qemu_chr_open_pp(QemuOpts *opts)
 
 #endif
 
-static const struct {
+typedef struct CharDriver {
     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 = vc_init },
-    { .name = "memory",    .open = qemu_chr_open_ringbuf },
-#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 },
-    { .name = "stdio",     .open = qemu_chr_open_win_stdio },
-#else
-    { .name = "file",      .open = qemu_chr_open_file_out },
-    { .name = "pipe",      .open = qemu_chr_open_pipe },
-    { .name = "stdio",     .open = qemu_chr_open_stdio },
-#endif
-#ifdef CONFIG_BRLAPI
-    { .name = "braille",   .open = chr_baum_init },
-#endif
-#ifdef HAVE_CHARDEV_TTY
-    { .name = "tty",       .open = qemu_chr_open_tty },
-    { .name = "serial",    .open = qemu_chr_open_tty },
-    { .name = "pty",       .open = qemu_chr_open_pty },
-#endif
-#ifdef HAVE_CHARDEV_PARPORT
-    { .name = "parallel",  .open = qemu_chr_open_pp },
-    { .name = "parport",   .open = qemu_chr_open_pp },
-#endif
-#ifdef CONFIG_SPICE
-    { .name = "spicevmc",     .open = qemu_chr_open_spice },
-#if SPICE_SERVER_VERSION >= 0x000c02
-    { .name = "spiceport",    .open = qemu_chr_open_spice_port },
-#endif
-#endif
-};
+} CharDriver;
+
+static GSList *backends;
+
+void register_char_driver(const char *name, CharDriverState *(*open)(QemuOpts *))
+{
+    CharDriver *s;
+
+    s = g_malloc0(sizeof(*s));
+    s->name = g_strdup(name);
+    s->open = open;
+
+    backends = g_slist_append(backends, s);
+}
 
 CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
                                     void (*init)(struct CharDriverState *s),
                                     Error **errp)
 {
+    CharDriver *cd;
     CharDriverState *chr;
-    int i;
+    GSList *i;
 
     if (qemu_opts_id(opts) == NULL) {
         error_setg(errp, "chardev: no id specified");
@@ -3165,17 +3214,20 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,
                    qemu_opts_id(opts));
         goto err;
     }
-    for (i = 0; i < ARRAY_SIZE(backend_table); i++) {
-        if (strcmp(backend_table[i].name, qemu_opt_get(opts, "backend")) == 0)
+    for (i = backends; i; i = i->next) {
+        cd = i->data;
+
+        if (strcmp(cd->name, qemu_opt_get(opts, "backend")) == 0) {
             break;
+        }
     }
-    if (i == ARRAY_SIZE(backend_table)) {
+    if (i == NULL) {
         error_setg(errp, "chardev: backend \"%s\" not found",
                    qemu_opt_get(opts, "backend"));
-        goto err;
+        return NULL;
     }
 
-    chr = backend_table[i].open(opts);
+    chr = cd->open(opts);
     if (!chr) {
         error_setg(errp, "chardev: opening backend \"%s\" failed",
                    qemu_opt_get(opts, "backend"));
@@ -3255,6 +3307,24 @@ void qemu_chr_fe_close(struct CharDriverState *chr)
     }
 }
 
+guint qemu_chr_fe_add_watch(CharDriverState *s, GIOCondition cond,
+                            GIOFunc func, void *user_data)
+{
+    GSource *src;
+    guint tag;
+
+    if (s->chr_add_watch == NULL) {
+        return -ENOSYS;
+    }
+
+    src = s->chr_add_watch(s, cond);
+    g_source_set_callback(src, (GSourceFunc)func, user_data, NULL);
+    tag = g_source_attach(src, NULL);
+    g_source_unref(src);
+
+    return tag;
+}
+
 void qemu_chr_delete(CharDriverState *chr)
 {
     QTAILQ_REMOVE(&chardevs, chr, next);
@@ -3589,3 +3659,33 @@ void qmp_chardev_remove(const char *id, Error **errp)
     }
     qemu_chr_delete(chr);
 }
+
+static void register_types(void)
+{
+    register_char_driver("null", qemu_chr_open_null);
+    register_char_driver("socket", qemu_chr_open_socket);
+    register_char_driver("udp", qemu_chr_open_udp);
+    register_char_driver("memory", qemu_chr_open_ringbuf);
+#ifdef _WIN32
+    register_char_driver("file", qemu_chr_open_win_file_out);
+    register_char_driver("pipe", qemu_chr_open_win_pipe);
+    register_char_driver("console", qemu_chr_open_win_con);
+    register_char_driver("serial", qemu_chr_open_win);
+    register_char_driver("stdio", qemu_chr_open_win_stdio);
+#else
+    register_char_driver("file", qemu_chr_open_file_out);
+    register_char_driver("pipe", qemu_chr_open_pipe);
+    register_char_driver("stdio", qemu_chr_open_stdio);
+#endif
+#ifdef HAVE_CHARDEV_TTY
+    register_char_driver("tty", qemu_chr_open_tty);
+    register_char_driver("serial", qemu_chr_open_tty);
+    register_char_driver("pty", qemu_chr_open_pty);
+#endif
+#ifdef HAVE_CHARDEV_PARPORT
+    register_char_driver("parallel", qemu_chr_open_pp);
+    register_char_driver("parport", qemu_chr_open_pp);
+#endif
+}
+
+type_init(register_types);