diff mbox series

[10/10] display: add -display app launching external application

Message ID 20180803173614.12358-11-marcandre.lureau@redhat.com
State New
Headers show
Series RFC: spice: add -display app to launch external UI | expand

Commit Message

Marc-André Lureau Aug. 3, 2018, 5:36 p.m. UTC
Add a new display backend that will configure Spice to allow a remote
client to control QEMU in a similar fashion as other display backend
like GTK.

For this to work, we set up Spice server with a unix socket, and
register a VC chardev that will be exposed as Spice ports. A QMP
monitor is also exposed as a Spice port, this allows the remote client
fuller qemu control and state handling.

- doesn't handle VC set_echo() - this doesn't seem a strong
  requirement, very few front-end use it
- spice options can be tweaked with other -spice arguments
- Windows support shouldn't be hard to do, but will probably use a TCP
  port instead
- we may want to watch the child process to quit automatically if it
  crashed

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 qapi/ui.json     |   3 +-
 ui/app.c         | 179 +++++++++++++++++++++++++++++++++++++++++++++++
 qemu-options.hx  |   5 ++
 ui/Makefile.objs |   5 ++
 4 files changed, 191 insertions(+), 1 deletion(-)
 create mode 100644 ui/app.c

Comments

Eric Blake Aug. 3, 2018, 6:22 p.m. UTC | #1
On 08/03/2018 12:36 PM, Marc-André Lureau wrote:
> Add a new display backend that will configure Spice to allow a remote
> client to control QEMU in a similar fashion as other display backend
> like GTK.
> 
> For this to work, we set up Spice server with a unix socket, and
> register a VC chardev that will be exposed as Spice ports. A QMP
> monitor is also exposed as a Spice port, this allows the remote client
> fuller qemu control and state handling.
> 
> - doesn't handle VC set_echo() - this doesn't seem a strong
>    requirement, very few front-end use it
> - spice options can be tweaked with other -spice arguments
> - Windows support shouldn't be hard to do, but will probably use a TCP
>    port instead
> - we may want to watch the child process to quit automatically if it
>    crashed
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
> ---
>   qapi/ui.json     |   3 +-
>   ui/app.c         | 179 +++++++++++++++++++++++++++++++++++++++++++++++
>   qemu-options.hx  |   5 ++
>   ui/Makefile.objs |   5 ++
>   4 files changed, 191 insertions(+), 1 deletion(-)
>   create mode 100644 ui/app.c
> 
> diff --git a/qapi/ui.json b/qapi/ui.json
> index 4ca91bb45a..9b96f1f9d7 100644
> --- a/qapi/ui.json
> +++ b/qapi/ui.json
> @@ -1057,7 +1057,8 @@
>   ##
>   { 'enum'    : 'DisplayType',
>     'data'    : [ 'default', 'none', 'gtk', 'sdl',
> -                'egl-headless', 'curses', 'cocoa' ] }
> +                'egl-headless', 'curses', 'cocoa',
> +                'app'] }

Missing documentation for 'app' (at a minimum, it should mention since 3.1)
Daniel P. Berrangé Aug. 7, 2018, 10:15 a.m. UTC | #2
On Fri, Aug 03, 2018 at 07:36:14PM +0200, Marc-André Lureau wrote:
> Add a new display backend that will configure Spice to allow a remote
> client to control QEMU in a similar fashion as other display backend
> like GTK.
> 
> For this to work, we set up Spice server with a unix socket, and
> register a VC chardev that will be exposed as Spice ports. A QMP
> monitor is also exposed as a Spice port, this allows the remote client
> fuller qemu control and state handling.
> 
> - doesn't handle VC set_echo() - this doesn't seem a strong
>   requirement, very few front-end use it
> - spice options can be tweaked with other -spice arguments
> - Windows support shouldn't be hard to do, but will probably use a TCP
>   port instead
> - we may want to watch the child process to quit automatically if it
>   crashed
> 
> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

> +    type_register(&char_vc_type_info);
> +
> +    sock_path = g_strjoin("", tmp_dir, "/", "spice.sock", NULL);
> +    qopts = qemu_opts_create(qemu_find_opts("spice"), NULL, 0, &error_abort);
> +    qemu_opt_set(qopts, "disable-ticketing", "on", &error_abort);
> +    qemu_opt_set(qopts, "unix", "on", &error_abort);
> +    qemu_opt_set(qopts, "addr", sock_path, &error_abort);
> +    qemu_opt_set(qopts, "image-compression", "off", &error_abort);
> +    qemu_opt_set(qopts, "streaming-video", "off", &error_abort);
> +    qemu_opt_set(qopts, "gl", opts->has_gl ? "on" : "off", &error_abort);

Hmm, so ultimately "-display app" is just syntactic sugar for a hardcoded
set of "-display spice" arguments, plus automatic launching of the client
app.  I'm thinking users may well ask for ability to control some of those
spice arguments over time.  So if we want auto-launching of a remote app,
I think it is preferrable to do it via extra args to the existing
"-display spice" format. eg we could add a "client=yes|no" to control
launching the client

   -display spice,client=yes

would do what your "-display app" proposes, and still let users tweak
all the other spice args as desired.  Another option "autoquit=yes|no"
could control whether QEMU automatically exits when the client quits,
vs whether it stays alive.

Regards,
Daniel
Marc-André Lureau Aug. 7, 2018, 10:33 a.m. UTC | #3
Hi

On Tue, Aug 7, 2018 at 12:15 PM, Daniel P. Berrangé <berrange@redhat.com> wrote:
> On Fri, Aug 03, 2018 at 07:36:14PM +0200, Marc-André Lureau wrote:
>> Add a new display backend that will configure Spice to allow a remote
>> client to control QEMU in a similar fashion as other display backend
>> like GTK.
>>
>> For this to work, we set up Spice server with a unix socket, and
>> register a VC chardev that will be exposed as Spice ports. A QMP
>> monitor is also exposed as a Spice port, this allows the remote client
>> fuller qemu control and state handling.
>>
>> - doesn't handle VC set_echo() - this doesn't seem a strong
>>   requirement, very few front-end use it
>> - spice options can be tweaked with other -spice arguments
>> - Windows support shouldn't be hard to do, but will probably use a TCP
>>   port instead
>> - we may want to watch the child process to quit automatically if it
>>   crashed
>>
>> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
>
>> +    type_register(&char_vc_type_info);
>> +
>> +    sock_path = g_strjoin("", tmp_dir, "/", "spice.sock", NULL);
>> +    qopts = qemu_opts_create(qemu_find_opts("spice"), NULL, 0, &error_abort);
>> +    qemu_opt_set(qopts, "disable-ticketing", "on", &error_abort);
>> +    qemu_opt_set(qopts, "unix", "on", &error_abort);
>> +    qemu_opt_set(qopts, "addr", sock_path, &error_abort);
>> +    qemu_opt_set(qopts, "image-compression", "off", &error_abort);
>> +    qemu_opt_set(qopts, "streaming-video", "off", &error_abort);
>> +    qemu_opt_set(qopts, "gl", opts->has_gl ? "on" : "off", &error_abort);
>
> Hmm, so ultimately "-display app" is just syntactic sugar for a hardcoded
> set of "-display spice" arguments, plus automatic launching of the client
> app.  I'm thinking users may well ask for ability to control some of those

You can do it via additional -spice arguments.

> spice arguments over time.  So if we want auto-launching of a remote app,
> I think it is preferrable to do it via extra args to the existing
> "-display spice" format. eg we could add a "client=yes|no" to control
> launching the client
>
>    -display spice,client=yes

There is no -display spice, atm.

However there is a -display vnc.

It's a bit unclear to me the relation between -display and
-vnc/-spice/-curses etc. In the end, I tend to think of -display foo
as a shortcut for a longer -foo configuration.

So -display spice,client=yes is a reasonable proposal to me, making it
clear that it will run spice. (client=yes is less clear to me but
fine)

I wanted a simple and short replacement for "gtk", "app" was generic
enough, and could be also further tweaked if needed (specify vnc,
spice, auto-quit etc)


>
> would do what your "-display app" proposes, and still let users tweak
> all the other spice args as desired.  Another option "autoquit=yes|no"
> could control whether QEMU automatically exits when the client quits,
> vs whether it stays alive.

Yes, I haven't looked at that in details, but we could either have
extra URI args, use a .vv file with extra config keys, use a dedicated
spice message to tell the client, or have a global client config. Too
many options to chose from :)
Gerd Hoffmann Aug. 7, 2018, 2:30 p.m. UTC | #4
Hi,

> > spice arguments over time.  So if we want auto-launching of a remote app,
> > I think it is preferrable to do it via extra args to the existing
> > "-display spice" format. eg we could add a "client=yes|no" to control
> > launching the client
> >
> >    -display spice,client=yes
> 
> There is no -display spice, atm.
> 
> However there is a -display vnc.

That should not be there.  Now that we have a deprecation process
I should probably actually deprecate it in favor of -vnc.

> It's a bit unclear to me the relation between -display and
> -vnc/-spice/-curses etc. In the end, I tend to think of -display foo
> as a shortcut for a longer -foo configuration.

-display is for builtin UIs.  You can have exactly one of these.

-spice and -vnc is for remote protocols.  They can be used together with
builtin UIs (even though that isn't a typical use case).  Configuring
both spice and vnc works too.

-sdl and -curses are shortcuts for -display sdl and -display curses.

> So -display spice,client=yes is a reasonable proposal to me, making it
> clear that it will run spice. (client=yes is less clear to me but
> fine)

Hmm, this will both configure some standard stuff and start an external
application.  Doesn't really fit with "-spice ...".

Adding "-display remote-client" doesn't really fit either.  But still
looks better to me.  Or we just create a new -remote-client top level
switch.  Adding higher-level config options (protocol=spice/vnc,
monitor=on/off, serial=on/off, ...) is less confusing then (especially
vnc support :) ), compared to have a bunch of more -spice options which
only have an effect with client=yes.

Also:  remote-viewer accepts config files.  I'd suggest to write one, so
it is easy to restart remote-viewer.  Also I would not use a temp dir
for the files and sockets, but some fixed location.
/run/user/$uid/qemu/$vmname for example (where $vmname is whatever you
passed to qemu using -name).

cheers,
  Gerd
Paolo Bonzini Aug. 7, 2018, 2:43 p.m. UTC | #5
On 07/08/2018 16:30, Gerd Hoffmann wrote:
>>>
>>>    -display spice,client=yes
>> There is no -display spice, atm.
>>
>> However there is a -display vnc.
> That should not be there.  Now that we have a deprecation process
> I should probably actually deprecate it in favor of -vnc.
> 
>> It's a bit unclear to me the relation between -display and
>> -vnc/-spice/-curses etc. In the end, I tend to think of -display foo
>> as a shortcut for a longer -foo configuration.
> -display is for builtin UIs.  You can have exactly one of these.
> 
> -spice and -vnc is for remote protocols.  They can be used together with
> builtin UIs (even though that isn't a typical use case).  Configuring
> both spice and vnc works too.
> 
> -sdl and -curses are shortcuts for -display sdl and -display curses.
> 

Why not allow multiple -display options, but give an error if more than
one builtin UI is specified?

Paolo
Marc-André Lureau Dec. 18, 2018, 2:04 p.m. UTC | #6
Hi

On Tue, Aug 7, 2018 at 6:30 PM Gerd Hoffmann <kraxel@redhat.com> wrote:
>
>   Hi,
>
> > > spice arguments over time.  So if we want auto-launching of a remote app,
> > > I think it is preferrable to do it via extra args to the existing
> > > "-display spice" format. eg we could add a "client=yes|no" to control
> > > launching the client
> > >
> > >    -display spice,client=yes
> >
> > There is no -display spice, atm.
> >
> > However there is a -display vnc.
>
> That should not be there.  Now that we have a deprecation process
> I should probably actually deprecate it in favor of -vnc.
>
> > It's a bit unclear to me the relation between -display and
> > -vnc/-spice/-curses etc. In the end, I tend to think of -display foo
> > as a shortcut for a longer -foo configuration.
>
> -display is for builtin UIs.  You can have exactly one of these.
>
> -spice and -vnc is for remote protocols.  They can be used together with
> builtin UIs (even though that isn't a typical use case).  Configuring
> both spice and vnc works too.
>
> -sdl and -curses are shortcuts for -display sdl and -display curses.
>
> > So -display spice,client=yes is a reasonable proposal to me, making it
> > clear that it will run spice. (client=yes is less clear to me but
> > fine)
>
> Hmm, this will both configure some standard stuff and start an external
> application.  Doesn't really fit with "-spice ...".
>
> Adding "-display remote-client" doesn't really fit either.  But still

I would rather keep the shorter "-display app" version. If you feel
strongly about "remote-client", I'll rename it.

> looks better to me.  Or we just create a new -remote-client top level
> switch.  Adding higher-level config options (protocol=spice/vnc,
> monitor=on/off, serial=on/off, ...) is less confusing then (especially
> vnc support :) ), compared to have a bunch of more -spice options which
> only have an effect with client=yes.
>
> Also:  remote-viewer accepts config files.  I'd suggest to write one, so
> it is easy to restart remote-viewer.  Also I would not use a temp dir

Where should it be written? Should the location be printed on stdout,
or given by the user, on command line? What should be the content of
the .vv file? Should it use a template to avoid proxying all the
options?

I think this can be considered for a later improvement.

> for the files and sockets, but some fixed location.
> /run/user/$uid/qemu/$vmname for example (where $vmname is whatever you
> passed to qemu using -name).

That's easy enough, although if no -name is given, I suppose a
temporary location is still better than a fixed location. I'll update
the patch.

thanks
Gerd Hoffmann Dec. 19, 2018, 7:13 a.m. UTC | #7
Hi,

> > Also:  remote-viewer accepts config files.  I'd suggest to write one, so
> > it is easy to restart remote-viewer.  Also I would not use a temp dir
> 
> Where should it be written?

/run/user/$uid/qemu/$vmname/remote-viewer.vv ?

> What should be the content of
> the .vv file?

Everything needed, so launching remote-viewer is
just "remote-viewer /path/to/config.vv".

We might consider writing such a config file
unconditionally, even without -display app.

> Should it use a template to avoid proxying all the
> options?

Is this needed?  virt-viewer has a user settings file,
shouldn't user preferences go there instead?

> > for the files and sockets, but some fixed location.
> > /run/user/$uid/qemu/$vmname for example (where $vmname is whatever you
> > passed to qemu using -name).
> 
> That's easy enough, although if no -name is given, I suppose a
> temporary location is still better than a fixed location.

Yes, just replace $vmname with `mktemp --directory` then.

cheers,
  Gerd
Marc-André Lureau Dec. 19, 2018, 7:44 a.m. UTC | #8
On Wed, Dec 19, 2018 at 11:13 AM Gerd Hoffmann <kraxel@redhat.com> wrote:
>
>   Hi,
>
> > > Also:  remote-viewer accepts config files.  I'd suggest to write one, so
> > > it is easy to restart remote-viewer.  Also I would not use a temp dir
> >
> > Where should it be written?
>
> /run/user/$uid/qemu/$vmname/remote-viewer.vv ?
>
> > What should be the content of
> > the .vv file?
>
> Everything needed, so launching remote-viewer is
> just "remote-viewer /path/to/config.vv".

If it's just what is needed to launch the viewer, why not stick to the
url syntax?

.vv files are quite specific to virt-viewer/remote-viewer.

>
> We might consider writing such a config file
> unconditionally, even without -display app.
>
> > Should it use a template to avoid proxying all the
> > options?
>
> Is this needed?  virt-viewer has a user settings file,
> shouldn't user preferences go there instead?

User settings files are quite limited at this point. Afaik, it's only
used for the "ask-quit" preference, and "monitor-mapping". Other
options are either from command line or .vv files.

>
> > > for the files and sockets, but some fixed location.
> > > /run/user/$uid/qemu/$vmname for example (where $vmname is whatever you
> > > passed to qemu using -name).
> >
> > That's easy enough, although if no -name is given, I suppose a
> > temporary location is still better than a fixed location.
>
> Yes, just replace $vmname with `mktemp --directory` then.
>
> cheers,
>   Gerd
>

thanks
Gerd Hoffmann Dec. 19, 2018, 9:54 a.m. UTC | #9
On Wed, Dec 19, 2018 at 11:44:07AM +0400, Marc-André Lureau wrote:
> On Wed, Dec 19, 2018 at 11:13 AM Gerd Hoffmann <kraxel@redhat.com> wrote:
> >
> >   Hi,
> >
> > > > Also:  remote-viewer accepts config files.  I'd suggest to write one, so
> > > > it is easy to restart remote-viewer.  Also I would not use a temp dir
> > >
> > > Where should it be written?
> >
> > /run/user/$uid/qemu/$vmname/remote-viewer.vv ?
> >
> > > What should be the content of
> > > the .vv file?
> >
> > Everything needed, so launching remote-viewer is
> > just "remote-viewer /path/to/config.vv".
> 
> If it's just what is needed to launch the viewer, why not stick to the
> url syntax?

First, the info needed to launch is located on a fixed & well known
place on disk then, so it is easy to restart remote-viewer.

Second, with a vv file you can do a bit more, for example setting "ca"
in case TLS is configured, or set enable-{smartcard,usbredir} depending
on guest configuration.

> .vv files are quite specific to virt-viewer/remote-viewer.

The same is true for the URL syntax.  Trying to pass a URL to vncviewer
doesn't work ...

> > > Should it use a template to avoid proxying all the
> > > options?
> >
> > Is this needed?  virt-viewer has a user settings file,
> > shouldn't user preferences go there instead?
> 
> User settings files are quite limited at this point. Afaik, it's only
> used for the "ask-quit" preference, and "monitor-mapping". Other
> options are either from command line or .vv files.

Ok.

I think it is more useful to extend that instead of passing through
options from the qemu command line to virt-viewer.

cheers,
  Gerd
Marc-André Lureau Dec. 19, 2018, 12:34 p.m. UTC | #10
Hi

On Wed, Dec 19, 2018 at 1:54 PM Gerd Hoffmann <kraxel@redhat.com> wrote:
>
> On Wed, Dec 19, 2018 at 11:44:07AM +0400, Marc-André Lureau wrote:
> > On Wed, Dec 19, 2018 at 11:13 AM Gerd Hoffmann <kraxel@redhat.com> wrote:
> > >
> > >   Hi,
> > >
> > > > > Also:  remote-viewer accepts config files.  I'd suggest to write one, so
> > > > > it is easy to restart remote-viewer.  Also I would not use a temp dir
> > > >
> > > > Where should it be written?
> > >
> > > /run/user/$uid/qemu/$vmname/remote-viewer.vv ?
> > >
> > > > What should be the content of
> > > > the .vv file?
> > >
> > > Everything needed, so launching remote-viewer is
> > > just "remote-viewer /path/to/config.vv".
> >
> > If it's just what is needed to launch the viewer, why not stick to the
> > url syntax?
>
> First, the info needed to launch is located on a fixed & well known
> place on disk then, so it is easy to restart remote-viewer.

It would also be a fixed URI, if we use the spice+unix:// syntax.

Fwiw, yes it's not registered in IANA/IETF, or with a RFC, but it is
considered "standard" withing the spice project. So we could formally
document it, as part of the protocol perhaps?, if it helps.

>
> Second, with a vv file you can do a bit more, for example setting "ca"
> in case TLS is configured, or set enable-{smartcard,usbredir} depending
> on guest configuration.
>
> > .vv files are quite specific to virt-viewer/remote-viewer.
>
> The same is true for the URL syntax.  Trying to pass a URL to vncviewer
> doesn't work ...

For vnc://
https://tools.ietf.org/html/rfc7869

(I recall some docs abourt rdp:// as well)

>
> > > > Should it use a template to avoid proxying all the
> > > > options?
> > >
> > > Is this needed?  virt-viewer has a user settings file,
> > > shouldn't user preferences go there instead?
> >
> > User settings files are quite limited at this point. Afaik, it's only
> > used for the "ask-quit" preference, and "monitor-mapping". Other
> > options are either from command line or .vv files.
>
> Ok.
>
> I think it is more useful to extend that instead of passing through
> options from the qemu command line to virt-viewer.

Hmm, although it limits what you can easily configure per instance as well..
Gerd Hoffmann Dec. 19, 2018, 12:55 p.m. UTC | #11
> > > > Everything needed, so launching remote-viewer is
> > > > just "remote-viewer /path/to/config.vv".
> > >
> > > If it's just what is needed to launch the viewer, why not stick to the
> > > url syntax?
> >
> > First, the info needed to launch is located on a fixed & well known
> > place on disk then, so it is easy to restart remote-viewer.
> 
> It would also be a fixed URI, if we use the spice+unix:// syntax.

Ah, good.  Fine with me then.

> > I think it is more useful to extend that instead of passing through
> > options from the qemu command line to virt-viewer.
> 
> Hmm, although it limits what you can easily configure per instance as well..

We might decide on a case-by-vase basis, which make sense as
per-instance and which not.  For example: Hotkeys is more a user
preference and I don't think we need them be configurable per-vm.
For fullscreen it is probably more useful to have that as vm-specific
option.

cheers,
  Gerd
diff mbox series

Patch

diff --git a/qapi/ui.json b/qapi/ui.json
index 4ca91bb45a..9b96f1f9d7 100644
--- a/qapi/ui.json
+++ b/qapi/ui.json
@@ -1057,7 +1057,8 @@ 
 ##
 { 'enum'    : 'DisplayType',
   'data'    : [ 'default', 'none', 'gtk', 'sdl',
-                'egl-headless', 'curses', 'cocoa' ] }
+                'egl-headless', 'curses', 'cocoa',
+                'app'] }
 
 ##
 # @DisplayOptions:
diff --git a/ui/app.c b/ui/app.c
new file mode 100644
index 0000000000..d29f630a70
--- /dev/null
+++ b/ui/app.c
@@ -0,0 +1,179 @@ 
+/*
+ * QEMU external app display driver
+ *
+ * Copyright (c) 2018 Marc-André Lureau <marcandre.lureau@redhat.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include "qemu/osdep.h"
+
+#include <gio/gio.h>
+
+#include "qemu-common.h"
+#include "ui/console.h"
+#include "qemu/config-file.h"
+#include "qemu/option.h"
+#include "qemu/cutils.h"
+#include "qapi/error.h"
+#include "io/channel-command.h"
+#include "chardev/spice.h"
+#include "sysemu/sysemu.h"
+
+static char *tmp_dir;
+static char *sock_path;
+
+typedef struct VCChardev {
+    SpiceChardev parent;
+} VCChardev;
+
+#define TYPE_CHARDEV_VC "chardev-vc"
+#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC)
+
+static ChardevBackend *
+chr_spice_backend_new(void)
+{
+    ChardevBackend *be = g_new0(ChardevBackend, 1);
+
+    be->type = CHARDEV_BACKEND_KIND_SPICEPORT;
+    be->u.spiceport.data = g_new0(ChardevSpicePort, 1);
+
+    return be;
+}
+
+static void vc_chr_open(Chardev *chr,
+                        ChardevBackend *backend,
+                        bool *be_opened,
+                        Error **errp)
+{
+    ChardevBackend *be;
+    const char *fqdn = NULL;
+
+    if (strstart(chr->label, "serial", NULL)) {
+        fqdn = "org.qemu.console.serial.0";
+    } else if (strstart(chr->label, "parallel", NULL)) {
+        fqdn = "org.qemu.console.parallel.0";
+    } else if (strstart(chr->label, "compat_monitor", NULL)) {
+        fqdn = "org.qemu.monitor.hmp.0";
+    }
+
+    be = chr_spice_backend_new();
+    be->u.spiceport.data->fqdn = fqdn ?
+        g_strdup(fqdn) : g_strdup_printf("org.qemu.console.%s", chr->label);
+    qemu_chr_open_spice_port(chr, be, be_opened, errp);
+    qapi_free_ChardevBackend(be);
+}
+
+static void vc_chr_set_echo(Chardev *chr, bool echo)
+{
+    /* TODO: set echo for frontends QMP and qtest */
+}
+
+static void char_vc_class_init(ObjectClass *oc, void *data)
+{
+    ChardevClass *cc = CHARDEV_CLASS(oc);
+
+    cc->parse = qemu_chr_parse_vc;
+    cc->open = vc_chr_open;
+    cc->chr_set_echo = vc_chr_set_echo;
+}
+
+static const TypeInfo char_vc_type_info = {
+    .name = TYPE_CHARDEV_VC,
+    .parent = TYPE_CHARDEV_SPICEPORT,
+    .instance_size = sizeof(VCChardev),
+    .class_init = char_vc_class_init,
+};
+
+static void app_atexit(void)
+{
+    if (sock_path) {
+        unlink(sock_path);
+    }
+    if (tmp_dir) {
+        rmdir(tmp_dir);
+    }
+    g_free(sock_path);
+    g_free(tmp_dir);
+}
+
+static void app_display_early_init(DisplayOptions *opts)
+{
+    QemuOpts *qopts;
+    ChardevBackend *be = chr_spice_backend_new();
+    GError *err = NULL;
+
+    atexit(app_atexit);
+    tmp_dir = g_dir_make_tmp(NULL, &err);
+    if (err) {
+        error_report("Failed to create temporary directory: %s", err->message);
+        abort();
+    }
+
+    type_register(&char_vc_type_info);
+
+    sock_path = g_strjoin("", tmp_dir, "/", "spice.sock", NULL);
+    qopts = qemu_opts_create(qemu_find_opts("spice"), NULL, 0, &error_abort);
+    qemu_opt_set(qopts, "disable-ticketing", "on", &error_abort);
+    qemu_opt_set(qopts, "unix", "on", &error_abort);
+    qemu_opt_set(qopts, "addr", sock_path, &error_abort);
+    qemu_opt_set(qopts, "image-compression", "off", &error_abort);
+    qemu_opt_set(qopts, "streaming-video", "off", &error_abort);
+    qemu_opt_set(qopts, "gl", opts->has_gl ? "on" : "off", &error_abort);
+    display_opengl = opts->has_gl;
+
+    be->u.spiceport.data->fqdn = g_strdup("org.qemu.monitor.qmp.0");
+    qemu_chardev_new("org.qemu.monitor.qmp", TYPE_CHARDEV_SPICEPORT,
+                     be, &error_abort);
+    qopts = qemu_opts_create(qemu_find_opts("mon"),
+                             NULL, 0, &error_fatal);
+    qemu_opt_set(qopts, "chardev", "org.qemu.monitor.qmp", &error_abort);
+    qemu_opt_set(qopts, "mode", "control", &error_abort);
+
+    if (!qemu_name) {
+        qemu_name = "QEMU";
+    }
+    qapi_free_ChardevBackend(be);
+}
+
+static void app_display_init(DisplayState *ds, DisplayOptions *opts)
+{
+    GError *err = NULL;
+    gchar *uri;
+
+    uri = g_strjoin("", "spice+unix://", tmp_dir, "/", "spice.sock", NULL);
+    g_app_info_launch_default_for_uri(uri, NULL, &err);
+    if (err) {
+        error_report("Failed to launch %s URI: %s", uri, err->message);
+        exit(1);
+    }
+    g_free(uri);
+}
+
+static QemuDisplay qemu_display_app = {
+    .type       = DISPLAY_TYPE_APP,
+    .early_init = app_display_early_init,
+    .init       = app_display_init,
+};
+
+static void register_app(void)
+{
+    qemu_display_register(&qemu_display_app);
+}
+
+type_init(register_app);
diff --git a/qemu-options.hx b/qemu-options.hx
index b1bf0f485f..846e0b5c65 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -1239,6 +1239,7 @@  STEXI
 ETEXI
 
 DEF("display", HAS_ARG, QEMU_OPTION_display,
+    "-display app[,gl=on|off]\n"
     "-display sdl[,frame=on|off][,alt_grab=on|off][,ctrl_grab=on|off]\n"
     "            [,window_close=on|off][,gl=on|core|es|off]\n"
     "-display gtk[,grab_on_hover=on|off][,gl=on|off]|\n"
@@ -1286,6 +1287,10 @@  menus and other UI elements to configure and control the VM during
 runtime.
 @item vnc
 Start a VNC server on display <arg>
+@item app
+Start QEMU as a Spice server and launch the default Spice client
+application. The Spice server will redirect the serial consoles and
+QEMU monitors.
 @end table
 ETEXI
 
diff --git a/ui/Makefile.objs b/ui/Makefile.objs
index 00f6976c30..11178fbe24 100644
--- a/ui/Makefile.objs
+++ b/ui/Makefile.objs
@@ -54,6 +54,11 @@  curses.mo-objs := curses.o
 curses.mo-cflags := $(CURSES_CFLAGS)
 curses.mo-libs := $(CURSES_LIBS)
 
+common-obj-$(call land,$(CONFIG_SPICE),$(CONFIG_GIO)) += app.mo
+app.mo-objs := app.o
+app.mo-cflags := $(GIO_CFLAGS)
+app.mo-libs := $(GIO_LIBS)
+
 common-obj-$(CONFIG_OPENGL) += shader.o
 common-obj-$(CONFIG_OPENGL) += console-gl.o
 common-obj-$(CONFIG_OPENGL) += egl-helpers.o