Patchwork [RfC,0/9] chardev hotplug

login
register
mail settings
Submitter Gerd Hoffmann
Date Dec. 20, 2012, 1:02 p.m.
Message ID <50D30C7B.3030802@redhat.com>
Download mbox | patch
Permalink /patch/207663/
State New
Headers show

Comments

Gerd Hoffmann - Dec. 20, 2012, 1:02 p.m.
Hi,

> /me wades through the socket code (unix+tcp) right now, which needs some
> refactoring to make it fly.

Sneak preview attached.  Goes on top of the series.
Compile tested only so far.

enjoy,
  Gerd
From 2c5568c20d97d497b43c9af1c1c1388db089c1e9 Mon Sep 17 00:00:00 2001
From: Gerd Hoffmann <kraxel@redhat.com>
Date: Thu, 20 Dec 2012 13:53:12 +0100
Subject: [PATCH] chardev: hotplug, qmp, socket

qemu_chr_open_socket is splitted into two functions.  All initialization
after creating the socket file handler is splitted away into the new
qemu_chr_open_socket_fd function.

chr->filename doesn't get filled from QemuOpts any more.  Qemu gathers
the information using getsockname and getnameinfo instead.  This way it
will also work correctly for file handles passed via file descriptor
passing.

Finally qmp_chardev_open_socket() is the actual qmp hotplug
implementation which basically just calls socket_listen or
socket_connect and the new qemu_chr_open_socket_fd function.

Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
 qapi-schema.json |   13 +++-
 qemu-char.c      |  168 ++++++++++++++++++++++++++++++++++++-----------------
 2 files changed, 124 insertions(+), 57 deletions(-)
Gerd Hoffmann - Dec. 21, 2012, 11:45 a.m.
On 12/20/12 14:02, Gerd Hoffmann wrote:
>   Hi,
> 
>> /me wades through the socket code (unix+tcp) right now, which needs some
>> refactoring to make it fly.
> 
> Sneak preview attached.  Goes on top of the series.
> Compile tested only so far.

Now that it comes to testing: how does the union look (in josn) at the wire?

thanks,
  Gerd
Paolo Bonzini - Dec. 21, 2012, 11:53 a.m.
Il 21/12/2012 12:45, Gerd Hoffmann ha scritto:
> On 12/20/12 14:02, Gerd Hoffmann wrote:
>>   Hi,
>>
>>> /me wades through the socket code (unix+tcp) right now, which needs some
>>> refactoring to make it fly.
>>
>> Sneak preview attached.  Goes on top of the series.
>> Compile tested only so far.
> 
> Now that it comes to testing: how does the union look (in josn) at the wire?

Tests are your friends! (Especially qapi-schema-test.json and
test-qmp-input-visitor.c).  Something like this:

  { 'type': 'UserDefA',
    'data': { 'boolean': 'bool' } }

  { 'type': 'UserDefB',
    'data': { 'integer': 'int' } }

  { 'union': 'UserDefUnion',
    'data': { 'a' : 'UserDefA', 'b' : 'UserDefB' } }

looks like this:

  { 'type': 'b', 'data' : { 'integer': 42 } }

Paolo
Gerd Hoffmann - Dec. 21, 2012, 12:17 p.m.
On 12/21/12 12:53, Paolo Bonzini wrote:
> Il 21/12/2012 12:45, Gerd Hoffmann ha scritto:
>> On 12/20/12 14:02, Gerd Hoffmann wrote:
>>>   Hi,
>>>
>>>> /me wades through the socket code (unix+tcp) right now, which needs some
>>>> refactoring to make it fly.
>>>
>>> Sneak preview attached.  Goes on top of the series.
>>> Compile tested only so far.
>>
>> Now that it comes to testing: how does the union look (in josn) at the wire?
> 
> Tests are your friends! (Especially qapi-schema-test.json and
> test-qmp-input-visitor.c).  Something like this:
> 
>   { 'type': 'UserDefA',
>     'data': { 'boolean': 'bool' } }
> 
>   { 'type': 'UserDefB',
>     'data': { 'integer': 'int' } }
> 
>   { 'union': 'UserDefUnion',
>     'data': { 'a' : 'UserDefA', 'b' : 'UserDefB' } }
> 
> looks like this:
> 
>   { 'type': 'b', 'data' : { 'integer': 42 } }

So ...

{ 'type': 'ChardevDummy', 'data': { } }

{ 'union': 'ChardevBackend', 'data': { 'file'   : 'ChardevFile',
                                       'port'   : 'ChardevPort',
                                       'socket' : 'ChardevSocket',
                                       'pty'    : 'ChardevDummy',
                                       'null'   : 'ChardevDummy' } }

{ 'command': 'chardev-add', 'data': {'id'      : 'str',
                                     'backend' : 'ChardevBackend' },

... should accept ...

{'id': 'test-null', 'backend': {'data': {}, 'type': 'null'}}

... as parameters for chardev-add, no?  But I get back ...

 {u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter
'backend'"}}

... and can't see what is wrong there ...

cheers,
  Gerd

Patch

diff --git a/qapi-schema.json b/qapi-schema.json
index 0922823..39e0ab4 100644
--- a/qapi-schema.json
+++ b/qapi-schema.json
@@ -3043,11 +3043,18 @@ 
 { 'type': 'ChardevPort', 'data': { 'device' : 'ChardevFileSource',
                                    'type'   : 'ChardevPortKind'} }
 
+{ 'type': 'ChardevSocket', 'data': { 'addr'    : 'SocketAddress',
+                                     '*server' : 'bool',
+                                     '*wait'   : 'bool',
+                                     '*delay'  : 'bool',
+                                     '*telnet' : 'bool' } }
+
 { 'type': 'ChardevDummy', 'data': { } }
 
-{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile',
-                                       'port' : 'ChardevPort',
-                                       'null' : 'ChardevDummy' } }
+{ 'union': 'ChardevBackend', 'data': { 'file'   : 'ChardevFile',
+                                       'port'   : 'ChardevPort',
+                                       'socket' : 'ChardevSocket',
+                                       'null'   : 'ChardevDummy' } }
 
 { 'command': 'chardev-add', 'data': {'id'      : 'str',
                                      'backend' : 'ChardevBackend' } }
diff --git a/qemu-char.c b/qemu-char.c
index 5a6d3c5..15f2ae2 100644
--- a/qemu-char.c
+++ b/qemu-char.c
@@ -2427,10 +2427,88 @@  static void tcp_chr_close(CharDriverState *chr)
     qemu_chr_be_event(chr, CHR_EVENT_CLOSED);
 }
 
-static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
+static CharDriverState *qemu_chr_open_socket_fd(int fd, int do_nodelay,
+                                                int is_listen, int is_telnet,
+                                                int is_waitconnect,
+                                                Error **errp)
 {
     CharDriverState *chr = NULL;
     TCPCharDriver *s = NULL;
+    char host[NI_MAXHOST], serv[NI_MAXSERV];
+    const char *left = "", *right = "";
+    struct sockaddr_storage ss;
+    socklen_t ss_len = sizeof(ss);
+
+    memset(&ss, 0, ss_len);
+    if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) != 0) {
+        error_setg(errp, "getsockname: %s", strerror(errno));
+        return NULL;
+    }
+
+    chr = g_malloc0(sizeof(CharDriverState));
+    s = g_malloc0(sizeof(TCPCharDriver));
+
+    s->connected = 0;
+    s->fd = -1;
+    s->listen_fd = -1;
+    s->msgfd = -1;
+
+    chr->filename = g_malloc(256);
+    switch (ss.ss_family) {
+#ifndef _WIN32
+    case AF_UNIX:
+        s->is_unix = 1;
+        snprintf(chr->filename, 256, "unix:%s%s",
+                 ((struct sockaddr_un *)(&ss))->sun_path,
+                 is_listen ? ",server" : "");
+        break;
+#endif
+    case AF_INET6:
+        left  = "[";
+        right = "]";
+        /* fall through */
+    case AF_INET:
+        s->do_nodelay = do_nodelay;
+        getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host),
+                    serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV);
+        snprintf(chr->filename, 256, "%s:%s:%s%s%s%s",
+                 is_telnet ? "telnet" : "tcp",
+                 left, host, right, serv,
+                 is_listen ? ",server" : "");
+        break;
+    }
+
+    chr->opaque = s;
+    chr->chr_write = tcp_chr_write;
+    chr->chr_close = tcp_chr_close;
+    chr->get_msgfd = tcp_get_msgfd;
+    chr->chr_add_client = tcp_chr_add_client;
+
+    if (is_listen) {
+        s->listen_fd = fd;
+        qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr);
+        if (is_telnet) {
+            s->do_telnetopt = 1;
+        }
+    } else {
+        s->connected = 1;
+        s->fd = fd;
+        socket_set_nodelay(fd);
+        tcp_chr_connect(chr);
+    }
+
+    if (is_listen && is_waitconnect) {
+        printf("QEMU waiting for connection on: %s\n",
+               chr->filename);
+        tcp_chr_accept(chr);
+        socket_set_nonblock(s->listen_fd);
+    }
+    return chr;
+}
+
+static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
+{
+    CharDriverState *chr = NULL;
     Error *local_err = NULL;
     int fd = -1;
     int is_listen;
@@ -2447,10 +2525,7 @@  static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
     if (!is_listen)
         is_waitconnect = 0;
 
-    chr = g_malloc0(sizeof(CharDriverState));
-    s = g_malloc0(sizeof(TCPCharDriver));
-
-    if (is_unix) {
+     if (is_unix) {
         if (is_listen) {
             fd = unix_listen_opts(opts, &local_err);
         } else {
@@ -2470,56 +2545,14 @@  static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
     if (!is_waitconnect)
         socket_set_nonblock(fd);
 
-    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;
-    chr->chr_add_client = tcp_chr_add_client;
-
-    if (is_listen) {
-        s->listen_fd = fd;
-        qemu_set_fd_handler2(s->listen_fd, NULL, tcp_chr_accept, NULL, chr);
-        if (is_telnet)
-            s->do_telnetopt = 1;
-
-    } else {
-        s->connected = 1;
-        s->fd = fd;
-        socket_set_nodelay(fd);
-        tcp_chr_connect(chr);
-    }
-
-    /* for "info chardev" monitor command */
-    chr->filename = g_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);
-        tcp_chr_accept(chr);
-        socket_set_nonblock(s->listen_fd);
+    chr = qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, is_telnet,
+                                  is_waitconnect, &local_err);
+    if (error_is_set(&local_err)) {
+        goto fail;
     }
     return chr;
 
+
  fail:
     if (local_err) {
         qerror_report_err(local_err);
@@ -2528,8 +2561,10 @@  static CharDriverState *qemu_chr_open_socket(QemuOpts *opts)
     if (fd >= 0) {
         closesocket(fd);
     }
-    g_free(s);
-    g_free(chr);
+    if (chr) {
+        g_free(chr->opaque);
+        g_free(chr);
+    }
     return NULL;
 }
 
@@ -3057,6 +3092,28 @@  static CharDriverState *qmp_chardev_open_port(ChardevPort *port, Error **errp)
 
 #endif /* WIN32 */
 
+static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock,
+                                                Error **errp)
+{
+    SocketAddress *addr = sock->addr;
+    bool do_nodelay     = sock->has_delay  ? !sock->delay : true;
+    bool is_listen      = sock->has_server ? sock->server : true;
+    bool is_telnet      = sock->has_telnet ? sock->telnet : false;
+    bool is_waitconnect = sock->has_wait   ? sock->wait   : false;
+    int fd;
+
+    if (is_listen) {
+        fd = socket_listen(addr, errp);
+    } else {
+        fd = socket_connect(addr, errp, NULL, NULL);
+    }
+    if (error_is_set(errp)) {
+        return NULL;
+    }
+    return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen,
+                                   is_telnet, is_waitconnect, errp);
+}
+
 void qmp_chardev_add(const char *id, ChardevBackend *backend, Error **errp)
 {
     CharDriverState *chr;
@@ -3068,6 +3125,9 @@  void qmp_chardev_add(const char *id, ChardevBackend *backend, Error **errp)
     case CHARDEV_BACKEND_KIND_PORT:
         chr = qmp_chardev_open_port(backend->port, errp);
         break;
+    case CHARDEV_BACKEND_KIND_SOCKET:
+        chr = qmp_chardev_open_socket(backend->socket, errp);
+        break;
     case CHARDEV_BACKEND_KIND_NULL:
         chr = qemu_chr_open_null(NULL);
         break;