All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v1 0/2] Enable passing pre-opened chardev socket FDs
@ 2017-12-21 13:27 Daniel P. Berrange
  2017-12-21 13:27 ` [Qemu-devel] [PATCH v1 1/2] io: move fd_is_socket() into common sockets code Daniel P. Berrange
  2017-12-21 13:27 ` [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup Daniel P. Berrange
  0 siblings, 2 replies; 10+ messages in thread
From: Daniel P. Berrange @ 2017-12-21 13:27 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Marc-André Lureau, Dr. David Alan Gilbert,
	Markus Armbruster, Eric Blake, Daniel P. Berrange

This fixes a long standing problem that libvirt has with starting up QEMU.

We have to busy-wait retrying connect() on the QMP monitor socket until QEMU
finally creates & listens on it, but at same time must be careful to not wait
forever if QEMU exits.

This this patch series, libvirt can simply pass in a pre-opened UNIX domain
socket file descriptor, which it can immediately connect to with no busy-wait.

Daniel P. Berrange (2):
  io: move fd_is_socket() into common sockets code
  char: allow passing pre-opened socket file descriptor at startup

 chardev/char-socket.c  | 66 ++++++++++++++++++++++++++++++++++++++++++++------
 chardev/char.c         |  6 +++++
 include/qemu/sockets.h |  1 +
 io/channel-util.c      | 13 ----------
 monitor.c              |  5 ++++
 qapi/common.json       | 11 +++++++++
 qapi/sockets.json      | 14 ++++++++---
 util/qemu-sockets.c    | 49 +++++++++++++++++++++++++++++++++++++
 8 files changed, 142 insertions(+), 23 deletions(-)

-- 
2.14.3

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v1 1/2] io: move fd_is_socket() into common sockets code
  2017-12-21 13:27 [Qemu-devel] [PATCH v1 0/2] Enable passing pre-opened chardev socket FDs Daniel P. Berrange
@ 2017-12-21 13:27 ` Daniel P. Berrange
  2017-12-21 13:49   ` Marc-André Lureau
  2017-12-21 13:27 ` [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup Daniel P. Berrange
  1 sibling, 1 reply; 10+ messages in thread
From: Daniel P. Berrange @ 2017-12-21 13:27 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Marc-André Lureau, Dr. David Alan Gilbert,
	Markus Armbruster, Eric Blake, Daniel P. Berrange

The fd_is_socket() helper method is useful in a few places, so put it in
the common sockets code.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 include/qemu/sockets.h |  1 +
 io/channel-util.c      | 13 -------------
 util/qemu-sockets.c    | 13 +++++++++++++
 3 files changed, 14 insertions(+), 13 deletions(-)

diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
index 4f7311b52a..5680880f5a 100644
--- a/include/qemu/sockets.h
+++ b/include/qemu/sockets.h
@@ -12,6 +12,7 @@ int inet_aton(const char *cp, struct in_addr *ia);
 #include "qapi-types.h"
 
 /* misc helpers */
+bool fd_is_socket(int fd);
 int qemu_socket(int domain, int type, int protocol);
 int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
 int socket_set_cork(int fd, int v);
diff --git a/io/channel-util.c b/io/channel-util.c
index 0fb4bd0837..423d79845a 100644
--- a/io/channel-util.c
+++ b/io/channel-util.c
@@ -24,19 +24,6 @@
 #include "io/channel-socket.h"
 
 
-static bool fd_is_socket(int fd)
-{
-    int optval;
-    socklen_t optlen;
-    optlen = sizeof(optval);
-    return qemu_getsockopt(fd,
-                           SOL_SOCKET,
-                           SO_TYPE,
-                           (char *)&optval,
-                           &optlen) == 0;
-}
-
-
 QIOChannel *qio_channel_new_fd(int fd,
                                Error **errp)
 {
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index af4f01211a..1d23f0b742 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -91,6 +91,19 @@ NetworkAddressFamily inet_netfamily(int family)
     return NETWORK_ADDRESS_FAMILY_UNKNOWN;
 }
 
+bool fd_is_socket(int fd)
+{
+    int optval;
+    socklen_t optlen;
+    optlen = sizeof(optval);
+    return qemu_getsockopt(fd,
+                           SOL_SOCKET,
+                           SO_TYPE,
+                           (char *)&optval,
+                           &optlen) == 0;
+}
+
+
 /*
  * Matrix we're trying to apply
  *
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup
  2017-12-21 13:27 [Qemu-devel] [PATCH v1 0/2] Enable passing pre-opened chardev socket FDs Daniel P. Berrange
  2017-12-21 13:27 ` [Qemu-devel] [PATCH v1 1/2] io: move fd_is_socket() into common sockets code Daniel P. Berrange
@ 2017-12-21 13:27 ` Daniel P. Berrange
  2017-12-21 13:48   ` Marc-André Lureau
                     ` (2 more replies)
  1 sibling, 3 replies; 10+ messages in thread
From: Daniel P. Berrange @ 2017-12-21 13:27 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Marc-André Lureau, Dr. David Alan Gilbert,
	Markus Armbruster, Eric Blake, Daniel P. Berrange

When starting QEMU management apps will usually setup a monitor socket, and
then open it immediately after startup. If not using QEMU's own -daemonize
arg, this process can be troublesome to handle correctly. The mgmt app will
need to repeatedly call connect() until it succeeds, because it does not
know when QEMU has created the listener socket. If can't retry connect()
forever though, because an error might have caused QEMU to exit before it
even creates the monitor.

The obvious way to fix this kind of problem is to just pass in a pre-opened
socket file descriptor for the QEMU monitor to listen on. The management app
can now immediately call connect() just once. If connect() fails it knows
that QEMU has exited with an error.

The SocketAddress(Legacy) structs allow for FD passing via the monitor, using
the 'getfd' command, but only when using QMP JSON syntax. The HMP syntax has
no way to initialize the SocketAddress(Legacy) 'fd' variant. So this patch
first wires up the 'fd' parameter to refer to a monitor file descriptor,
allowing HMP to use

   getfd myfd
   chardev-add socket,fd=myfd

The SocketAddress 'fd' variant is currently tied to the use of the monitor
'getfd' command, so we have a chicken & egg problem with reusing that at
startup wher no monitor connection is available. We could define that the
special fd name prefix '/dev/fdset' refers to a FD passed via the CLI, but
magic strings feel unpleasant.

Instead we define a SocketAddress 'fdset' variant that takes an fd set number
that works in combination with the 'add-fd' command line argument. e.g.

  -add-fd fd=3,set=1
  -chardev socket,fdset=1,id=mon
  -mon chardev=mon,mode=control

Note that we do not wire this up in the legacy chardev syntax, so you cannot
use FD passing with '-qmp', you must use the modern '-mon' + '-chardev' pair

An illustrative example of usage is:

  #!/usr/bin/perl

  use IO::Socket::UNIX;
  use Fcntl;

  unlink "/tmp/qmp";
  my $srv = IO::Socket::UNIX->new(
    Type => SOCK_STREAM(),
    Local => "/tmp/qmp",
    Listen => 1,
  );

  my $flags = fcntl $srv, F_GETFD, 0;
  fcntl $srv, F_SETFD, $flags & ~FD_CLOEXEC;

  my $fd = $srv->fileno();

  exec "qemu-system-x86_64", \
      "-add-fd", "fd=$fd,set=1", \
      "-chardev", "socket,fdset=1,server,nowait,id=mon", \
      "-mon", "chardev=mon,mode=control";

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 chardev/char-socket.c | 66 +++++++++++++++++++++++++++++++++++++++++++++------
 chardev/char.c        |  6 +++++
 monitor.c             |  5 ++++
 qapi/common.json      | 11 +++++++++
 qapi/sockets.json     | 14 ++++++++---
 util/qemu-sockets.c   | 36 ++++++++++++++++++++++++++++
 6 files changed, 128 insertions(+), 10 deletions(-)

diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 6013972f72..c85298589f 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -28,6 +28,7 @@
 #include "qemu/error-report.h"
 #include "qapi/error.h"
 #include "qapi/clone-visitor.h"
+#include "qemu/cutils.h"
 
 #include "chardev/char-io.h"
 
@@ -372,6 +373,10 @@ static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr,
         return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.str,
                                is_listen ? ",server" : "");
         break;
+    case SOCKET_ADDRESS_TYPE_FDSET:
+        return g_strdup_printf("%sfdset:%" PRId64 "%s", prefix, addr->u.fdset.i,
+                               is_listen ? ",server" : "");
+        break;
     case SOCKET_ADDRESS_TYPE_VSOCK:
         return g_strdup_printf("%svsock:%s:%s", prefix,
                                addr->u.vsock.cid,
@@ -983,25 +988,62 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
     const char *path = qemu_opt_get(opts, "path");
     const char *host = qemu_opt_get(opts, "host");
     const char *port = qemu_opt_get(opts, "port");
+    const char *fd = qemu_opt_get(opts, "fd");
+    const char *fdset = qemu_opt_get(opts, "fdset");
+    int64_t fdseti;
     const char *tls_creds = qemu_opt_get(opts, "tls-creds");
     SocketAddressLegacy *addr;
     ChardevSocket *sock;
+    int num = 0;
+
+    if (path) {
+        num++;
+    }
+    if (fd) {
+        num++;
+    }
+    if (fdset) {
+        num++;
+    }
+    if (host) {
+        num++;
+    }
+    if (num != 1) {
+        error_setg(errp,
+                   "Exactly one of 'path', 'fd', 'fdset' or 'host' required");
+        return;
+    }
 
     backend->type = CHARDEV_BACKEND_KIND_SOCKET;
-    if (!path) {
-        if (!host) {
-            error_setg(errp, "chardev: socket: no host given");
+    if (path) {
+        if (tls_creds) {
+            error_setg(errp, "TLS can only be used over TCP socket");
             return;
         }
+    } else if (host) {
         if (!port) {
             error_setg(errp, "chardev: socket: no port given");
             return;
         }
-    } else {
-        if (tls_creds) {
-            error_setg(errp, "TLS can only be used over TCP socket");
+    } else if (fd) {
+        /* We don't know what host to validate against when in client mode */
+        if (tls_creds && !is_listen) {
+            error_setg(errp, "TLS can not be used with pre-opened client FD");
+            return;
+        }
+    } else if (fdset) {
+        /* We don't know what host to validate against when in client mode */
+        if (tls_creds && !is_listen) {
+            error_setg(errp, "TLS can not be used with pre-opened client FD");
             return;
         }
+        if (qemu_strtoi64(fdset, NULL, 10, &fdseti) < 0) {
+            error_setg_errno(errp, errno,
+                             "Cannot parse fd set number %s", fdset);
+            return;
+        }
+    } else {
+        g_assert_not_reached();
     }
 
     sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
@@ -1027,7 +1069,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
         addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
         q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
         q_unix->path = g_strdup(path);
-    } else {
+    } else if (host) {
         addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
         addr->u.inet.data = g_new(InetSocketAddress, 1);
         *addr->u.inet.data = (InetSocketAddress) {
@@ -1040,6 +1082,16 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
             .has_ipv6 = qemu_opt_get(opts, "ipv6"),
             .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
         };
+    } else if (fd) {
+        addr->type = SOCKET_ADDRESS_LEGACY_KIND_FD;
+        addr->u.fd.data = g_new(String, 1);
+        addr->u.fd.data->str = g_strdup(fd);
+    } else if (fdset) {
+        addr->type = SOCKET_ADDRESS_LEGACY_KIND_FDSET;
+        addr->u.fdset.data = g_new(Int, 1);
+        addr->u.fdset.data->i = fdseti;
+    } else {
+        g_assert_not_reached();
     }
     sock->addr = addr;
 }
diff --git a/chardev/char.c b/chardev/char.c
index 2ae4f465ec..db940fc40f 100644
--- a/chardev/char.c
+++ b/chardev/char.c
@@ -798,6 +798,12 @@ QemuOptsList qemu_chardev_opts = {
         },{
             .name = "port",
             .type = QEMU_OPT_STRING,
+        },{
+            .name = "fd",
+            .type = QEMU_OPT_STRING,
+        },{
+            .name = "fdset",
+            .type = QEMU_OPT_STRING,
         },{
             .name = "localaddr",
             .type = QEMU_OPT_STRING,
diff --git a/monitor.c b/monitor.c
index d682eee2d8..c7df558535 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1962,6 +1962,11 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
 {
     mon_fd_t *monfd;
 
+    if (mon == NULL) {
+        error_setg(errp, "No monitor is available to acquire FD");
+        return -1;
+    }
+
     QLIST_FOREACH(monfd, &mon->fds, next) {
         int fd;
 
diff --git a/qapi/common.json b/qapi/common.json
index 6eb01821ef..a15cdc36e9 100644
--- a/qapi/common.json
+++ b/qapi/common.json
@@ -74,6 +74,17 @@
 { 'enum': 'OnOffSplit',
   'data': [ 'on', 'off', 'split' ] }
 
+##
+# @Int:
+#
+# A fat type wrapping 'int', to be embedded in lists.
+#
+# Since: 2.12
+##
+{ 'struct': 'Int',
+  'data': {
+    'i': 'int' } }
+
 ##
 # @String:
 #
diff --git a/qapi/sockets.json b/qapi/sockets.json
index ac022c6ad0..f3cac02166 100644
--- a/qapi/sockets.json
+++ b/qapi/sockets.json
@@ -112,7 +112,8 @@
     'inet': 'InetSocketAddress',
     'unix': 'UnixSocketAddress',
     'vsock': 'VsockSocketAddress',
-    'fd': 'String' } }
+    'fd': 'String',
+    'fdset': 'Int' } }
 
 ##
 # @SocketAddressType:
@@ -123,10 +124,16 @@
 #
 # @unix:  Unix domain socket
 #
+# @vsock: VSOCK socket
+#
+# @fd: socket file descriptor passed over monitor
+#
+# @fdset: socket file descriptor passed via CLI (since 2.12)
+#
 # Since: 2.9
 ##
 { 'enum': 'SocketAddressType',
-  'data': [ 'inet', 'unix', 'vsock', 'fd' ] }
+  'data': [ 'inet', 'unix', 'vsock', 'fd', 'fdset' ] }
 
 ##
 # @SocketAddress:
@@ -144,4 +151,5 @@
   'data': { 'inet': 'InetSocketAddress',
             'unix': 'UnixSocketAddress',
             'vsock': 'VsockSocketAddress',
-            'fd': 'String' } }
+            'fd': 'String',
+            'fdset': 'Int' } }
diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
index 1d23f0b742..d623a9840c 100644
--- a/util/qemu-sockets.c
+++ b/util/qemu-sockets.c
@@ -1049,6 +1049,22 @@ int socket_connect(SocketAddress *addr, Error **errp)
         fd = monitor_get_fd(cur_mon, addr->u.fd.str, errp);
         break;
 
+    case SOCKET_ADDRESS_TYPE_FDSET:
+        fd = monitor_fdset_get_fd(addr->u.fdset.i, O_RDWR);
+        if (fd < 0) {
+            error_setg_errno(errp, errno,
+                             "Unable to get FD from fdset %" PRId64,
+                             addr->u.fdset.i);
+            return -1;
+        }
+        if (!fd_is_socket(fd)) {
+            error_setg(errp, "Expected a socket FD from fdset %" PRId64,
+                       addr->u.fdset.i);
+            close(fd);
+            return -1;
+        }
+        break;
+
     case SOCKET_ADDRESS_TYPE_VSOCK:
         fd = vsock_connect_saddr(&addr->u.vsock, errp);
         break;
@@ -1076,6 +1092,22 @@ int socket_listen(SocketAddress *addr, Error **errp)
         fd = monitor_get_fd(cur_mon, addr->u.fd.str, errp);
         break;
 
+    case SOCKET_ADDRESS_TYPE_FDSET:
+        fd = monitor_fdset_get_fd(addr->u.fdset.i, O_RDWR);
+        if (fd < 0) {
+            error_setg_errno(errp, errno,
+                             "Unable to get FD from fdset %" PRId64,
+                             addr->u.fdset.i);
+            return -1;
+        }
+        if (!fd_is_socket(fd)) {
+            error_setg(errp, "Expected a socket FD from fdset %" PRId64,
+                       addr->u.fdset.i);
+            close(fd);
+            return -1;
+        }
+        break;
+
     case SOCKET_ADDRESS_TYPE_VSOCK:
         fd = vsock_listen_saddr(&addr->u.vsock, errp);
         break;
@@ -1293,6 +1325,10 @@ SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy)
         addr->type = SOCKET_ADDRESS_TYPE_FD;
         QAPI_CLONE_MEMBERS(String, &addr->u.fd, addr_legacy->u.fd.data);
         break;
+    case SOCKET_ADDRESS_LEGACY_KIND_FDSET:
+        addr->type = SOCKET_ADDRESS_TYPE_FDSET;
+        QAPI_CLONE_MEMBERS(Int, &addr->u.fdset, addr_legacy->u.fdset.data);
+        break;
     default:
         abort();
     }
-- 
2.14.3

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup
  2017-12-21 13:27 ` [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup Daniel P. Berrange
@ 2017-12-21 13:48   ` Marc-André Lureau
  2017-12-21 14:18     ` Eric Blake
  2017-12-21 14:20   ` Daniel P. Berrange
  2017-12-21 16:49   ` Markus Armbruster
  2 siblings, 1 reply; 10+ messages in thread
From: Marc-André Lureau @ 2017-12-21 13:48 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: QEMU, Dr. David Alan Gilbert, Markus Armbruster, Paolo Bonzini

H

On Thu, Dec 21, 2017 at 2:27 PM, Daniel P. Berrange <berrange@redhat.com> wrote:
> When starting QEMU management apps will usually setup a monitor socket, and
> then open it immediately after startup. If not using QEMU's own -daemonize
> arg, this process can be troublesome to handle correctly. The mgmt app will
> need to repeatedly call connect() until it succeeds, because it does not
> know when QEMU has created the listener socket. If can't retry connect()

If->It

> forever though, because an error might have caused QEMU to exit before it
> even creates the monitor.
>
> The obvious way to fix this kind of problem is to just pass in a pre-opened
> socket file descriptor for the QEMU monitor to listen on. The management app
> can now immediately call connect() just once. If connect() fails it knows
> that QEMU has exited with an error.
>
> The SocketAddress(Legacy) structs allow for FD passing via the monitor, using
> the 'getfd' command, but only when using QMP JSON syntax. The HMP syntax has
> no way to initialize the SocketAddress(Legacy) 'fd' variant. So this patch
> first wires up the 'fd' parameter to refer to a monitor file descriptor,
> allowing HMP to use

Make it a seperate patch? Do we need that feature in HMP?

>
>    getfd myfd
>    chardev-add socket,fd=myfd
>
> The SocketAddress 'fd' variant is currently tied to the use of the monitor
> 'getfd' command, so we have a chicken & egg problem with reusing that at
> startup wher no monitor connection is available. We could define that the
> special fd name prefix '/dev/fdset' refers to a FD passed via the CLI, but
> magic strings feel unpleasant.

Ok

>
> Instead we define a SocketAddress 'fdset' variant that takes an fd set number
> that works in combination with the 'add-fd' command line argument. e.g.
>
>   -add-fd fd=3,set=1
>   -chardev socket,fdset=1,id=mon
>   -mon chardev=mon,mode=control
>
> Note that we do not wire this up in the legacy chardev syntax, so you cannot
> use FD passing with '-qmp', you must use the modern '-mon' + '-chardev' pair
>
> An illustrative example of usage is:
>
>   #!/usr/bin/perl
>
>   use IO::Socket::UNIX;
>   use Fcntl;
>
>   unlink "/tmp/qmp";
>   my $srv = IO::Socket::UNIX->new(
>     Type => SOCK_STREAM(),
>     Local => "/tmp/qmp",
>     Listen => 1,
>   );
>
>   my $flags = fcntl $srv, F_GETFD, 0;
>   fcntl $srv, F_SETFD, $flags & ~FD_CLOEXEC;
>
>   my $fd = $srv->fileno();
>
>   exec "qemu-system-x86_64", \
>       "-add-fd", "fd=$fd,set=1", \
>       "-chardev", "socket,fdset=1,server,nowait,id=mon", \
>       "-mon", "chardev=mon,mode=control";
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  chardev/char-socket.c | 66 +++++++++++++++++++++++++++++++++++++++++++++------
>  chardev/char.c        |  6 +++++
>  monitor.c             |  5 ++++
>  qapi/common.json      | 11 +++++++++
>  qapi/sockets.json     | 14 ++++++++---
>  util/qemu-sockets.c   | 36 ++++++++++++++++++++++++++++
>  6 files changed, 128 insertions(+), 10 deletions(-)
>
> diff --git a/chardev/char-socket.c b/chardev/char-socket.c
> index 6013972f72..c85298589f 100644
> --- a/chardev/char-socket.c
> +++ b/chardev/char-socket.c
> @@ -28,6 +28,7 @@
>  #include "qemu/error-report.h"
>  #include "qapi/error.h"
>  #include "qapi/clone-visitor.h"
> +#include "qemu/cutils.h"
>
>  #include "chardev/char-io.h"
>
> @@ -372,6 +373,10 @@ static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr,
>          return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.str,
>                                 is_listen ? ",server" : "");
>          break;
> +    case SOCKET_ADDRESS_TYPE_FDSET:
> +        return g_strdup_printf("%sfdset:%" PRId64 "%s", prefix, addr->u.fdset.i,
> +                               is_listen ? ",server" : "");
> +        break;
>      case SOCKET_ADDRESS_TYPE_VSOCK:
>          return g_strdup_printf("%svsock:%s:%s", prefix,
>                                 addr->u.vsock.cid,
> @@ -983,25 +988,62 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
>      const char *path = qemu_opt_get(opts, "path");
>      const char *host = qemu_opt_get(opts, "host");
>      const char *port = qemu_opt_get(opts, "port");
> +    const char *fd = qemu_opt_get(opts, "fd");
> +    const char *fdset = qemu_opt_get(opts, "fdset");
> +    int64_t fdseti;
>      const char *tls_creds = qemu_opt_get(opts, "tls-creds");
>      SocketAddressLegacy *addr;
>      ChardevSocket *sock;
> +    int num = 0;
> +
> +    if (path) {
> +        num++;
> +    }
> +    if (fd) {
> +        num++;
> +    }
> +    if (fdset) {
> +        num++;
> +    }
> +    if (host) {
> +        num++;
> +    }
> +    if (num != 1) {
> +        error_setg(errp,
> +                   "Exactly one of 'path', 'fd', 'fdset' or 'host' required");
> +        return;
> +    }
>

That could be shorter ;)

>      backend->type = CHARDEV_BACKEND_KIND_SOCKET;
> -    if (!path) {
> -        if (!host) {
> -            error_setg(errp, "chardev: socket: no host given");
> +    if (path) {
> +        if (tls_creds) {
> +            error_setg(errp, "TLS can only be used over TCP socket");
>              return;
>          }
> +    } else if (host) {
>          if (!port) {
>              error_setg(errp, "chardev: socket: no port given");
>              return;
>          }
> -    } else {
> -        if (tls_creds) {
> -            error_setg(errp, "TLS can only be used over TCP socket");
> +    } else if (fd) {
> +        /* We don't know what host to validate against when in client mode */
> +        if (tls_creds && !is_listen) {
> +            error_setg(errp, "TLS can not be used with pre-opened client FD");
> +            return;
> +        }
> +    } else if (fdset) {
> +        /* We don't know what host to validate against when in client mode */
> +        if (tls_creds && !is_listen) {
> +            error_setg(errp, "TLS can not be used with pre-opened client FD");
>              return;
>          }
> +        if (qemu_strtoi64(fdset, NULL, 10, &fdseti) < 0) {
> +            error_setg_errno(errp, errno,
> +                             "Cannot parse fd set number %s", fdset);
> +            return;
> +        }
> +    } else {
> +        g_assert_not_reached();
>      }
>
>      sock = backend->u.socket.data = g_new0(ChardevSocket, 1);
> @@ -1027,7 +1069,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
>          addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
>          q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
>          q_unix->path = g_strdup(path);
> -    } else {
> +    } else if (host) {
>          addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
>          addr->u.inet.data = g_new(InetSocketAddress, 1);
>          *addr->u.inet.data = (InetSocketAddress) {
> @@ -1040,6 +1082,16 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
>              .has_ipv6 = qemu_opt_get(opts, "ipv6"),
>              .ipv6 = qemu_opt_get_bool(opts, "ipv6", 0),
>          };
> +    } else if (fd) {
> +        addr->type = SOCKET_ADDRESS_LEGACY_KIND_FD;
> +        addr->u.fd.data = g_new(String, 1);
> +        addr->u.fd.data->str = g_strdup(fd);
> +    } else if (fdset) {
> +        addr->type = SOCKET_ADDRESS_LEGACY_KIND_FDSET;
> +        addr->u.fdset.data = g_new(Int, 1);
> +        addr->u.fdset.data->i = fdseti;
> +    } else {
> +        g_assert_not_reached();
>      }
>      sock->addr = addr;
>  }
> diff --git a/chardev/char.c b/chardev/char.c
> index 2ae4f465ec..db940fc40f 100644
> --- a/chardev/char.c
> +++ b/chardev/char.c
> @@ -798,6 +798,12 @@ QemuOptsList qemu_chardev_opts = {
>          },{
>              .name = "port",
>              .type = QEMU_OPT_STRING,
> +        },{
> +            .name = "fd",
> +            .type = QEMU_OPT_STRING,
> +        },{
> +            .name = "fdset",
> +            .type = QEMU_OPT_STRING,
>          },{
>              .name = "localaddr",
>              .type = QEMU_OPT_STRING,
> diff --git a/monitor.c b/monitor.c
> index d682eee2d8..c7df558535 100644
> --- a/monitor.c
> +++ b/monitor.c
> @@ -1962,6 +1962,11 @@ int monitor_get_fd(Monitor *mon, const char *fdname, Error **errp)
>  {
>      mon_fd_t *monfd;
>
> +    if (mon == NULL) {
> +        error_setg(errp, "No monitor is available to acquire FD");
> +        return -1;
> +    }
> +
>      QLIST_FOREACH(monfd, &mon->fds, next) {
>          int fd;
>
> diff --git a/qapi/common.json b/qapi/common.json
> index 6eb01821ef..a15cdc36e9 100644
> --- a/qapi/common.json
> +++ b/qapi/common.json
> @@ -74,6 +74,17 @@
>  { 'enum': 'OnOffSplit',
>    'data': [ 'on', 'off', 'split' ] }
>
> +##
> +# @Int:
> +#
> +# A fat type wrapping 'int', to be embedded in lists.
> +#
> +# Since: 2.12
> +##
> +{ 'struct': 'Int',
> +  'data': {
> +    'i': 'int' } }
> +
>  ##
>  # @String:
>  #
> diff --git a/qapi/sockets.json b/qapi/sockets.json
> index ac022c6ad0..f3cac02166 100644
> --- a/qapi/sockets.json
> +++ b/qapi/sockets.json
> @@ -112,7 +112,8 @@
>      'inet': 'InetSocketAddress',
>      'unix': 'UnixSocketAddress',
>      'vsock': 'VsockSocketAddress',
> -    'fd': 'String' } }
> +    'fd': 'String',
> +    'fdset': 'Int' } }
>
>  ##
>  # @SocketAddressType:
> @@ -123,10 +124,16 @@
>  #
>  # @unix:  Unix domain socket
>  #
> +# @vsock: VSOCK socket
> +#
> +# @fd: socket file descriptor passed over monitor
> +#
> +# @fdset: socket file descriptor passed via CLI (since 2.12)
> +#
>  # Since: 2.9
>  ##
>  { 'enum': 'SocketAddressType',
> -  'data': [ 'inet', 'unix', 'vsock', 'fd' ] }
> +  'data': [ 'inet', 'unix', 'vsock', 'fd', 'fdset' ] }
>
>  ##
>  # @SocketAddress:
> @@ -144,4 +151,5 @@
>    'data': { 'inet': 'InetSocketAddress',
>              'unix': 'UnixSocketAddress',
>              'vsock': 'VsockSocketAddress',
> -            'fd': 'String' } }
> +            'fd': 'String',
> +            'fdset': 'Int' } }
> diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
> index 1d23f0b742..d623a9840c 100644
> --- a/util/qemu-sockets.c
> +++ b/util/qemu-sockets.c
> @@ -1049,6 +1049,22 @@ int socket_connect(SocketAddress *addr, Error **errp)
>          fd = monitor_get_fd(cur_mon, addr->u.fd.str, errp);
>          break;
>
> +    case SOCKET_ADDRESS_TYPE_FDSET:
> +        fd = monitor_fdset_get_fd(addr->u.fdset.i, O_RDWR);
> +        if (fd < 0) {
> +            error_setg_errno(errp, errno,
> +                             "Unable to get FD from fdset %" PRId64,
> +                             addr->u.fdset.i);
> +            return -1;
> +        }
> +        if (!fd_is_socket(fd)) {
> +            error_setg(errp, "Expected a socket FD from fdset %" PRId64,
> +                       addr->u.fdset.i);
> +            close(fd);
> +            return -1;
> +        }
> +        break;
> +
>      case SOCKET_ADDRESS_TYPE_VSOCK:
>          fd = vsock_connect_saddr(&addr->u.vsock, errp);
>          break;
> @@ -1076,6 +1092,22 @@ int socket_listen(SocketAddress *addr, Error **errp)
>          fd = monitor_get_fd(cur_mon, addr->u.fd.str, errp);
>          break;
>
> +    case SOCKET_ADDRESS_TYPE_FDSET:
> +        fd = monitor_fdset_get_fd(addr->u.fdset.i, O_RDWR);
> +        if (fd < 0) {
> +            error_setg_errno(errp, errno,
> +                             "Unable to get FD from fdset %" PRId64,
> +                             addr->u.fdset.i);
> +            return -1;
> +        }
> +        if (!fd_is_socket(fd)) {
> +            error_setg(errp, "Expected a socket FD from fdset %" PRId64,
> +                       addr->u.fdset.i);
> +            close(fd);
> +            return -1;
> +        }
> +        break;
> +
>      case SOCKET_ADDRESS_TYPE_VSOCK:
>          fd = vsock_listen_saddr(&addr->u.vsock, errp);
>          break;
> @@ -1293,6 +1325,10 @@ SocketAddress *socket_address_flatten(SocketAddressLegacy *addr_legacy)
>          addr->type = SOCKET_ADDRESS_TYPE_FD;
>          QAPI_CLONE_MEMBERS(String, &addr->u.fd, addr_legacy->u.fd.data);
>          break;
> +    case SOCKET_ADDRESS_LEGACY_KIND_FDSET:
> +        addr->type = SOCKET_ADDRESS_TYPE_FDSET;
> +        QAPI_CLONE_MEMBERS(Int, &addr->u.fdset, addr_legacy->u.fdset.data);
> +        break;
>      default:
>          abort();
>      }
> --
> 2.14.3
>
>

Looks fine, I would prefer the patch to be split, and some tests
added, please :)

thanks

-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v1 1/2] io: move fd_is_socket() into common sockets code
  2017-12-21 13:27 ` [Qemu-devel] [PATCH v1 1/2] io: move fd_is_socket() into common sockets code Daniel P. Berrange
@ 2017-12-21 13:49   ` Marc-André Lureau
  0 siblings, 0 replies; 10+ messages in thread
From: Marc-André Lureau @ 2017-12-21 13:49 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: QEMU, Dr. David Alan Gilbert, Markus Armbruster, Paolo Bonzini

Hi

On Thu, Dec 21, 2017 at 2:27 PM, Daniel P. Berrange <berrange@redhat.com> wrote:
> The fd_is_socket() helper method is useful in a few places, so put it in
> the common sockets code.
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>

Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>


> ---
>  include/qemu/sockets.h |  1 +
>  io/channel-util.c      | 13 -------------
>  util/qemu-sockets.c    | 13 +++++++++++++
>  3 files changed, 14 insertions(+), 13 deletions(-)
>
> diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h
> index 4f7311b52a..5680880f5a 100644
> --- a/include/qemu/sockets.h
> +++ b/include/qemu/sockets.h
> @@ -12,6 +12,7 @@ int inet_aton(const char *cp, struct in_addr *ia);
>  #include "qapi-types.h"
>
>  /* misc helpers */
> +bool fd_is_socket(int fd);
>  int qemu_socket(int domain, int type, int protocol);
>  int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
>  int socket_set_cork(int fd, int v);
> diff --git a/io/channel-util.c b/io/channel-util.c
> index 0fb4bd0837..423d79845a 100644
> --- a/io/channel-util.c
> +++ b/io/channel-util.c
> @@ -24,19 +24,6 @@
>  #include "io/channel-socket.h"
>
>
> -static bool fd_is_socket(int fd)
> -{
> -    int optval;
> -    socklen_t optlen;
> -    optlen = sizeof(optval);
> -    return qemu_getsockopt(fd,
> -                           SOL_SOCKET,
> -                           SO_TYPE,
> -                           (char *)&optval,
> -                           &optlen) == 0;
> -}
> -
> -
>  QIOChannel *qio_channel_new_fd(int fd,
>                                 Error **errp)
>  {
> diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c
> index af4f01211a..1d23f0b742 100644
> --- a/util/qemu-sockets.c
> +++ b/util/qemu-sockets.c
> @@ -91,6 +91,19 @@ NetworkAddressFamily inet_netfamily(int family)
>      return NETWORK_ADDRESS_FAMILY_UNKNOWN;
>  }
>
> +bool fd_is_socket(int fd)
> +{
> +    int optval;
> +    socklen_t optlen;
> +    optlen = sizeof(optval);
> +    return qemu_getsockopt(fd,
> +                           SOL_SOCKET,
> +                           SO_TYPE,
> +                           (char *)&optval,
> +                           &optlen) == 0;
> +}
> +
> +
>  /*
>   * Matrix we're trying to apply
>   *
> --
> 2.14.3
>
>



-- 
Marc-André Lureau

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup
  2017-12-21 13:48   ` Marc-André Lureau
@ 2017-12-21 14:18     ` Eric Blake
  0 siblings, 0 replies; 10+ messages in thread
From: Eric Blake @ 2017-12-21 14:18 UTC (permalink / raw)
  To: Marc-André Lureau, Daniel P. Berrange
  Cc: Markus Armbruster, Paolo Bonzini, QEMU, Dr. David Alan Gilbert

On 12/21/2017 07:48 AM, Marc-André Lureau wrote:

>>       ChardevSocket *sock;
>> +    int num = 0;
>> +
>> +    if (path) {
>> +        num++;
>> +    }
>> +    if (fd) {
>> +        num++;
>> +    }
>> +    if (fdset) {
>> +        num++;
>> +    }
>> +    if (host) {
>> +        num++;
>> +    }
>> +    if (num != 1) {
>> +        error_setg(errp,
>> +                   "Exactly one of 'path', 'fd', 'fdset' or 'host' required");
>> +        return;
>> +    }
>>
> 
> That could be shorter ;)

Here's one way:

if (!!path + !!fd + !!fdset ++ !!host) {
     error_setg...

>> +++ b/qapi/common.json
>> @@ -74,6 +74,17 @@
>>   { 'enum': 'OnOffSplit',
>>     'data': [ 'on', 'off', 'split' ] }
>>
>> +##
>> +# @Int:
>> +#
>> +# A fat type wrapping 'int', to be embedded in lists.
>> +#
>> +# Since: 2.12
>> +##
>> +{ 'struct': 'Int',
>> +  'data': {
>> +    'i': 'int' } }
>> +

The documentation is odd - you need this type because flat unions 
require a struct rather than a native type for each branch, and not 
because you are embedding 'int' in lists (that is, we support ['int'] 
just fine; the documentation for other fat types pre-dates when we 
supported lists of native types).

>>   ##
>>   # @String:
>>   #
>> diff --git a/qapi/sockets.json b/qapi/sockets.json
>> index ac022c6ad0..f3cac02166 100644
>> --- a/qapi/sockets.json
>> +++ b/qapi/sockets.json
>> @@ -112,7 +112,8 @@
>>       'inet': 'InetSocketAddress',
>>       'unix': 'UnixSocketAddress',
>>       'vsock': 'VsockSocketAddress',
>> -    'fd': 'String' } }
>> +    'fd': 'String',
>> +    'fdset': 'Int' } }

This is SocketAddressLegacy, which is an old-style union.  Do we really 
want to be expanding it at this point in time?  Old-style unions support:

'fd':'str',
'fdset':'int'

except that we already used the fat 'String', so using the fat 'Int' is 
done for consistency more than anything else, if we really do want it 
added to this type.

Missing documentation of when the new branch types were added.

>>
>>   ##
>>   # @SocketAddressType:
>> @@ -123,10 +124,16 @@
>>   #
>>   # @unix:  Unix domain socket
>>   #
>> +# @vsock: VSOCK socket
>> +#
>> +# @fd: socket file descriptor passed over monitor
>> +#
>> +# @fdset: socket file descriptor passed via CLI (since 2.12)
>> +#
>>   # Since: 2.9
>>   ##
>>   { 'enum': 'SocketAddressType',
>> -  'data': [ 'inet', 'unix', 'vsock', 'fd' ] }
>> +  'data': [ 'inet', 'unix', 'vsock', 'fd', 'fdset' ] }

Hmm, good catch on the missing vsock documentation (SocketAddressType 
was named SocketAddressFlatType back in 2.9, but 'vsock' did exist back 
then).

>>
>>   ##
>>   # @SocketAddress:
>> @@ -144,4 +151,5 @@
>>     'data': { 'inet': 'InetSocketAddress',
>>               'unix': 'UnixSocketAddress',
>>               'vsock': 'VsockSocketAddress',
>> -            'fd': 'String' } }
>> +            'fd': 'String',
>> +            'fdset': 'Int' } }

Again, missing documentation on when the branch was added.

But here, you are absolutely correct that we need the fat 'Int' here, as 
this is a flat union, which requires a struct for each branch (not 
native scalar types).

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup
  2017-12-21 13:27 ` [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup Daniel P. Berrange
  2017-12-21 13:48   ` Marc-André Lureau
@ 2017-12-21 14:20   ` Daniel P. Berrange
  2017-12-21 16:49   ` Markus Armbruster
  2 siblings, 0 replies; 10+ messages in thread
From: Daniel P. Berrange @ 2017-12-21 14:20 UTC (permalink / raw)
  To: qemu-devel
  Cc: Paolo Bonzini, Marc-André Lureau, Dr. David Alan Gilbert,
	Markus Armbruster, Eric Blake

On Thu, Dec 21, 2017 at 01:27:17PM +0000, Daniel P. Berrange wrote:
> When starting QEMU management apps will usually setup a monitor socket, and
> then open it immediately after startup. If not using QEMU's own -daemonize
> arg, this process can be troublesome to handle correctly. The mgmt app will
> need to repeatedly call connect() until it succeeds, because it does not
> know when QEMU has created the listener socket. If can't retry connect()
> forever though, because an error might have caused QEMU to exit before it
> even creates the monitor.
> 
> The obvious way to fix this kind of problem is to just pass in a pre-opened
> socket file descriptor for the QEMU monitor to listen on. The management app
> can now immediately call connect() just once. If connect() fails it knows
> that QEMU has exited with an error.
> 
> The SocketAddress(Legacy) structs allow for FD passing via the monitor, using
> the 'getfd' command, but only when using QMP JSON syntax. The HMP syntax has
> no way to initialize the SocketAddress(Legacy) 'fd' variant. So this patch
> first wires up the 'fd' parameter to refer to a monitor file descriptor,
> allowing HMP to use
> 
>    getfd myfd
>    chardev-add socket,fd=myfd
> 
> The SocketAddress 'fd' variant is currently tied to the use of the monitor
> 'getfd' command, so we have a chicken & egg problem with reusing that at
> startup wher no monitor connection is available. We could define that the
> special fd name prefix '/dev/fdset' refers to a FD passed via the CLI, but
> magic strings feel unpleasant.
> 
> Instead we define a SocketAddress 'fdset' variant that takes an fd set number
> that works in combination with the 'add-fd' command line argument. e.g.
> 
>   -add-fd fd=3,set=1
>   -chardev socket,fdset=1,id=mon
>   -mon chardev=mon,mode=control

Having written this, it occurs to me that using fdset for this is really
just adding uncessary complication.

The 'fd' parameter in SocketAddress is required to be a string that refers
to a named FD  passed over the monitor. I now see, however, that these
strings are forbidden to start with a digit. This means we could easily
re-use this facility to just directly reference a passed-in file descriptor
number, without clashing with named FD strings. eg we could do

   -chardev socket,fd=3,id=mon
   -mon chardev=mon,mode=control

This would simplify this patch significantly, and give close alignement
between the HMP & cli usage.



Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup
  2017-12-21 13:27 ` [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup Daniel P. Berrange
  2017-12-21 13:48   ` Marc-André Lureau
  2017-12-21 14:20   ` Daniel P. Berrange
@ 2017-12-21 16:49   ` Markus Armbruster
  2017-12-21 16:53     ` Daniel P. Berrange
  2017-12-21 19:05     ` Eric Blake
  2 siblings, 2 replies; 10+ messages in thread
From: Markus Armbruster @ 2017-12-21 16:49 UTC (permalink / raw)
  To: Daniel P. Berrange
  Cc: qemu-devel, Dr. David Alan Gilbert, Marc-André Lureau,
	Paolo Bonzini

QAPI schema review only.

"Daniel P. Berrange" <berrange@redhat.com> writes:

> When starting QEMU management apps will usually setup a monitor socket, and
> then open it immediately after startup. If not using QEMU's own -daemonize
> arg, this process can be troublesome to handle correctly. The mgmt app will
> need to repeatedly call connect() until it succeeds, because it does not
> know when QEMU has created the listener socket. If can't retry connect()
> forever though, because an error might have caused QEMU to exit before it
> even creates the monitor.
>
> The obvious way to fix this kind of problem is to just pass in a pre-opened
> socket file descriptor for the QEMU monitor to listen on. The management app
> can now immediately call connect() just once. If connect() fails it knows
> that QEMU has exited with an error.
>
> The SocketAddress(Legacy) structs allow for FD passing via the monitor, using
> the 'getfd' command, but only when using QMP JSON syntax. The HMP syntax has
> no way to initialize the SocketAddress(Legacy) 'fd' variant. So this patch
> first wires up the 'fd' parameter to refer to a monitor file descriptor,
> allowing HMP to use
>
>    getfd myfd
>    chardev-add socket,fd=myfd
>
> The SocketAddress 'fd' variant is currently tied to the use of the monitor
> 'getfd' command, so we have a chicken & egg problem with reusing that at
> startup wher no monitor connection is available. We could define that the

s/wher/where/

> special fd name prefix '/dev/fdset' refers to a FD passed via the CLI, but
> magic strings feel unpleasant.
>
> Instead we define a SocketAddress 'fdset' variant that takes an fd set number
> that works in combination with the 'add-fd' command line argument. e.g.
>
>   -add-fd fd=3,set=1
>   -chardev socket,fdset=1,id=mon
>   -mon chardev=mon,mode=control
>
> Note that we do not wire this up in the legacy chardev syntax, so you cannot
> use FD passing with '-qmp', you must use the modern '-mon' + '-chardev' pair
>
> An illustrative example of usage is:
>
>   #!/usr/bin/perl
>
>   use IO::Socket::UNIX;
>   use Fcntl;
>
>   unlink "/tmp/qmp";
>   my $srv = IO::Socket::UNIX->new(
>     Type => SOCK_STREAM(),
>     Local => "/tmp/qmp",
>     Listen => 1,
>   );
>
>   my $flags = fcntl $srv, F_GETFD, 0;
>   fcntl $srv, F_SETFD, $flags & ~FD_CLOEXEC;
>
>   my $fd = $srv->fileno();
>
>   exec "qemu-system-x86_64", \
>       "-add-fd", "fd=$fd,set=1", \
>       "-chardev", "socket,fdset=1,server,nowait,id=mon", \
>       "-mon", "chardev=mon,mode=control";
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
[...]
> diff --git a/qapi/common.json b/qapi/common.json
> index 6eb01821ef..a15cdc36e9 100644
> --- a/qapi/common.json
> +++ b/qapi/common.json
> @@ -74,6 +74,17 @@
>  { 'enum': 'OnOffSplit',
>    'data': [ 'on', 'off', 'split' ] }
>  
> +##
> +# @Int:
> +#
> +# A fat type wrapping 'int', to be embedded in lists.

I figure you got the "to be embedded in lists" part from @String.  That
one's occasionally used as list element type, but there are other uses.
@Int has only such other uses so far.  Let's drop this line from both types.

> +#
> +# Since: 2.12
> +##
> +{ 'struct': 'Int',
> +  'data': {
> +    'i': 'int' } }
> +
>  ##
>  # @String:
>  #
> diff --git a/qapi/sockets.json b/qapi/sockets.json
> index ac022c6ad0..f3cac02166 100644
> --- a/qapi/sockets.json
> +++ b/qapi/sockets.json
> @@ -112,7 +112,8 @@
>      'inet': 'InetSocketAddress',
>      'unix': 'UnixSocketAddress',
>      'vsock': 'VsockSocketAddress',
> -    'fd': 'String' } }
> +    'fd': 'String',
> +    'fdset': 'Int' } }
>  
>  ##
>  # @SocketAddressType:
> @@ -123,10 +124,16 @@
>  #
>  # @unix:  Unix domain socket
>  #
> +# @vsock: VSOCK socket
> +#
> +# @fd: socket file descriptor passed over monitor
> +#

Indepedent doc fix.  I'd put it in a separate patch.

One inaccuracy: @fd is *not* a file descriptor, it's the *name* of a
file descriptor.  Please fix.

> +# @fdset: socket file descriptor passed via CLI (since 2.12)
> +#

I gather we have to ways to pass file descriptors.  One way identifies
them by name (member @fd), the other by numeric ID (member @fdset).

0. This is disgusting.  Is there any way to unify the two, and deprecate
the loser (hopefully the numeric one)?

1. What makes the second one a *set*?

2. What ties the second one to the CLI?  Accidents of implementation or
something deeper?

>  # Since: 2.9
>  ##
>  { 'enum': 'SocketAddressType',
> -  'data': [ 'inet', 'unix', 'vsock', 'fd' ] }
> +  'data': [ 'inet', 'unix', 'vsock', 'fd', 'fdset' ] }
>  
>  ##
>  # @SocketAddress:
> @@ -144,4 +151,5 @@
>    'data': { 'inet': 'InetSocketAddress',
>              'unix': 'UnixSocketAddress',
>              'vsock': 'VsockSocketAddress',
> -            'fd': 'String' } }
> +            'fd': 'String',
> +            'fdset': 'Int' } }
[...]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup
  2017-12-21 16:49   ` Markus Armbruster
@ 2017-12-21 16:53     ` Daniel P. Berrange
  2017-12-21 19:05     ` Eric Blake
  1 sibling, 0 replies; 10+ messages in thread
From: Daniel P. Berrange @ 2017-12-21 16:53 UTC (permalink / raw)
  To: Markus Armbruster
  Cc: qemu-devel, Dr. David Alan Gilbert, Marc-André Lureau,
	Paolo Bonzini

On Thu, Dec 21, 2017 at 05:49:14PM +0100, Markus Armbruster wrote:
> QAPI schema review only.
> 
> "Daniel P. Berrange" <berrange@redhat.com> writes:
> 
> > When starting QEMU management apps will usually setup a monitor socket, and
> > then open it immediately after startup. If not using QEMU's own -daemonize
> > arg, this process can be troublesome to handle correctly. The mgmt app will
> > need to repeatedly call connect() until it succeeds, because it does not
> > know when QEMU has created the listener socket. If can't retry connect()
> > forever though, because an error might have caused QEMU to exit before it
> > even creates the monitor.
> >
> > The obvious way to fix this kind of problem is to just pass in a pre-opened
> > socket file descriptor for the QEMU monitor to listen on. The management app
> > can now immediately call connect() just once. If connect() fails it knows
> > that QEMU has exited with an error.
> >
> > The SocketAddress(Legacy) structs allow for FD passing via the monitor, using
> > the 'getfd' command, but only when using QMP JSON syntax. The HMP syntax has
> > no way to initialize the SocketAddress(Legacy) 'fd' variant. So this patch
> > first wires up the 'fd' parameter to refer to a monitor file descriptor,
> > allowing HMP to use
> >
> >    getfd myfd
> >    chardev-add socket,fd=myfd
> >
> > The SocketAddress 'fd' variant is currently tied to the use of the monitor
> > 'getfd' command, so we have a chicken & egg problem with reusing that at
> > startup wher no monitor connection is available. We could define that the
> 
> s/wher/where/
> 
> > special fd name prefix '/dev/fdset' refers to a FD passed via the CLI, but
> > magic strings feel unpleasant.
> >
> > Instead we define a SocketAddress 'fdset' variant that takes an fd set number
> > that works in combination with the 'add-fd' command line argument. e.g.
> >
> >   -add-fd fd=3,set=1
> >   -chardev socket,fdset=1,id=mon
> >   -mon chardev=mon,mode=control
> >
> > Note that we do not wire this up in the legacy chardev syntax, so you cannot
> > use FD passing with '-qmp', you must use the modern '-mon' + '-chardev' pair
> >
> > An illustrative example of usage is:
> >
> >   #!/usr/bin/perl
> >
> >   use IO::Socket::UNIX;
> >   use Fcntl;
> >
> >   unlink "/tmp/qmp";
> >   my $srv = IO::Socket::UNIX->new(
> >     Type => SOCK_STREAM(),
> >     Local => "/tmp/qmp",
> >     Listen => 1,
> >   );
> >
> >   my $flags = fcntl $srv, F_GETFD, 0;
> >   fcntl $srv, F_SETFD, $flags & ~FD_CLOEXEC;
> >
> >   my $fd = $srv->fileno();
> >
> >   exec "qemu-system-x86_64", \
> >       "-add-fd", "fd=$fd,set=1", \
> >       "-chardev", "socket,fdset=1,server,nowait,id=mon", \
> >       "-mon", "chardev=mon,mode=control";
> >
> > Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> [...]
> > diff --git a/qapi/common.json b/qapi/common.json
> > index 6eb01821ef..a15cdc36e9 100644
> > --- a/qapi/common.json
> > +++ b/qapi/common.json
> > @@ -74,6 +74,17 @@
> >  { 'enum': 'OnOffSplit',
> >    'data': [ 'on', 'off', 'split' ] }
> >  
> > +##
> > +# @Int:
> > +#
> > +# A fat type wrapping 'int', to be embedded in lists.
> 
> I figure you got the "to be embedded in lists" part from @String.  That
> one's occasionally used as list element type, but there are other uses.
> @Int has only such other uses so far.  Let's drop this line from both types.
> 
> > +#
> > +# Since: 2.12
> > +##
> > +{ 'struct': 'Int',
> > +  'data': {
> > +    'i': 'int' } }
> > +
> >  ##
> >  # @String:
> >  #
> > diff --git a/qapi/sockets.json b/qapi/sockets.json
> > index ac022c6ad0..f3cac02166 100644
> > --- a/qapi/sockets.json
> > +++ b/qapi/sockets.json
> > @@ -112,7 +112,8 @@
> >      'inet': 'InetSocketAddress',
> >      'unix': 'UnixSocketAddress',
> >      'vsock': 'VsockSocketAddress',
> > -    'fd': 'String' } }
> > +    'fd': 'String',
> > +    'fdset': 'Int' } }
> >  
> >  ##
> >  # @SocketAddressType:
> > @@ -123,10 +124,16 @@
> >  #
> >  # @unix:  Unix domain socket
> >  #
> > +# @vsock: VSOCK socket
> > +#
> > +# @fd: socket file descriptor passed over monitor
> > +#
> 
> Indepedent doc fix.  I'd put it in a separate patch.
> 
> One inaccuracy: @fd is *not* a file descriptor, it's the *name* of a
> file descriptor.  Please fix.
> 
> > +# @fdset: socket file descriptor passed via CLI (since 2.12)
> > +#
> 
> I gather we have to ways to pass file descriptors.  One way identifies
> them by name (member @fd), the other by numeric ID (member @fdset).
> 
> 0. This is disgusting.  Is there any way to unify the two, and deprecate
> the loser (hopefully the numeric one)?

Checkout my v2 which takes a different, less disgusting approach.

> 1. What makes the second one a *set*?

Just that the API i used was called fdset.

> 2. What ties the second one to the CLI?  Accidents of implementation or
> something deeper?

Nothing strict - just convention of usage. You could technically (ab)use
it from the monitor too.  My v2 approach enforces the distinct usage
more strictly.

> 
> >  # Since: 2.9
> >  ##
> >  { 'enum': 'SocketAddressType',
> > -  'data': [ 'inet', 'unix', 'vsock', 'fd' ] }
> > +  'data': [ 'inet', 'unix', 'vsock', 'fd', 'fdset' ] }
> >  
> >  ##
> >  # @SocketAddress:
> > @@ -144,4 +151,5 @@
> >    'data': { 'inet': 'InetSocketAddress',
> >              'unix': 'UnixSocketAddress',
> >              'vsock': 'VsockSocketAddress',
> > -            'fd': 'String' } }
> > +            'fd': 'String',
> > +            'fdset': 'Int' } }
> [...]

Regards,
Daniel
-- 
|: https://berrange.com      -o-    https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org         -o-            https://fstop138.berrange.com :|
|: https://entangle-photo.org    -o-    https://www.instagram.com/dberrange :|

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup
  2017-12-21 16:49   ` Markus Armbruster
  2017-12-21 16:53     ` Daniel P. Berrange
@ 2017-12-21 19:05     ` Eric Blake
  1 sibling, 0 replies; 10+ messages in thread
From: Eric Blake @ 2017-12-21 19:05 UTC (permalink / raw)
  To: Markus Armbruster, Daniel P. Berrange
  Cc: Marc-André Lureau, Paolo Bonzini, qemu-devel,
	Dr. David Alan Gilbert

On 12/21/2017 10:49 AM, Markus Armbruster wrote:
> QAPI schema review only.
> 

>> @@ -123,10 +124,16 @@
>>   #
>>   # @unix:  Unix domain socket
>>   #
>> +# @vsock: VSOCK socket
>> +#
>> +# @fd: socket file descriptor passed over monitor
>> +#
> 
> Indepedent doc fix.  I'd put it in a separate patch.

Missed in v2, so we still need that.

> 
> One inaccuracy: @fd is *not* a file descriptor, it's the *name* of a
> file descriptor.  Please fix.
> 
>> +# @fdset: socket file descriptor passed via CLI (since 2.12)
>> +#
> 
> I gather we have to ways to pass file descriptors.  One way identifies
> them by name (member @fd), the other by numeric ID (member @fdset).

Yes, sort of.

> 
> 0. This is disgusting.  Is there any way to unify the two, and deprecate
> the loser (hopefully the numeric one)?

v2 went with the numeric one for the CLI, as it is less verbose.

> 
> 1. What makes the second one a *set*?

We have two existing mechanisms for fd passing:

a) HMP and QMP have the older 'getfd' command since 0.14, which 
associates a single fd (via SCM_RIGHTS) with a name (which cannot start 
with a digit).  This does not scale - every command that wants to use an 
fd like this has to be updated to take an explicit fd argument; and you 
can't alter permissions on the fd after the fact.

b) QMP added the newer 'add-fd' in 1.2, which allows the association of 
multiple fds (via SCM_RIGHTS) with a set number (the set is then 
accessed via the magic prefix /dev/fdset/NNN.  This was also exposed via 
the command line, via '--add-fd', at the same time.  It also scales 
better, in that any command that already takes a filename now operates 
on fdsets as needed; also, you can associate multiple fds to the set 
(one that is read-only, another that is read-write), and qemu will 
alternate between fds according to permissions needed at the time.

New code targetting QMP should use only 'add-fd'; particularly since 
-addfd is the only version that works via CLI, even if there is only one 
fd in the set.  But it is more verbose, so having CLI shorthand that 
takes just an fd number is sometimes acceptable.

> 
> 2. What ties the second one to the CLI?  Accidents of implementation or
> something deeper?
> 

At this point, more history than anything else.

-- 
Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization:  qemu.org | libvirt.org

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2017-12-21 19:06 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-12-21 13:27 [Qemu-devel] [PATCH v1 0/2] Enable passing pre-opened chardev socket FDs Daniel P. Berrange
2017-12-21 13:27 ` [Qemu-devel] [PATCH v1 1/2] io: move fd_is_socket() into common sockets code Daniel P. Berrange
2017-12-21 13:49   ` Marc-André Lureau
2017-12-21 13:27 ` [Qemu-devel] [PATCH v1 2/2] char: allow passing pre-opened socket file descriptor at startup Daniel P. Berrange
2017-12-21 13:48   ` Marc-André Lureau
2017-12-21 14:18     ` Eric Blake
2017-12-21 14:20   ` Daniel P. Berrange
2017-12-21 16:49   ` Markus Armbruster
2017-12-21 16:53     ` Daniel P. Berrange
2017-12-21 19:05     ` Eric Blake

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.