xref: /qemu/ui/spice-core.c (revision fd3bea3f)
129b0040bSGerd Hoffmann /*
229b0040bSGerd Hoffmann  * Copyright (C) 2010 Red Hat, Inc.
329b0040bSGerd Hoffmann  *
429b0040bSGerd Hoffmann  * This program is free software; you can redistribute it and/or
529b0040bSGerd Hoffmann  * modify it under the terms of the GNU General Public License as
629b0040bSGerd Hoffmann  * published by the Free Software Foundation; either version 2 or
729b0040bSGerd Hoffmann  * (at your option) version 3 of the License.
829b0040bSGerd Hoffmann  *
929b0040bSGerd Hoffmann  * This program is distributed in the hope that it will be useful,
1029b0040bSGerd Hoffmann  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1129b0040bSGerd Hoffmann  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1229b0040bSGerd Hoffmann  * GNU General Public License for more details.
1329b0040bSGerd Hoffmann  *
1429b0040bSGerd Hoffmann  * You should have received a copy of the GNU General Public License
1529b0040bSGerd Hoffmann  * along with this program; if not, see <http://www.gnu.org/licenses/>.
1629b0040bSGerd Hoffmann  */
1729b0040bSGerd Hoffmann 
1829b0040bSGerd Hoffmann #include <spice.h>
1929b0040bSGerd Hoffmann #include <spice-experimental.h>
2029b0040bSGerd Hoffmann 
216f8c63fbSGerd Hoffmann #include <netdb.h>
229c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
236f8c63fbSGerd Hoffmann 
2429b0040bSGerd Hoffmann #include "qemu-common.h"
2528ecbaeeSPaolo Bonzini #include "ui/qemu-spice.h"
261de7afc9SPaolo Bonzini #include "qemu/thread.h"
271de7afc9SPaolo Bonzini #include "qemu/timer.h"
281de7afc9SPaolo Bonzini #include "qemu/queue.h"
29c448e855SGerd Hoffmann #include "qemu-x509.h"
301de7afc9SPaolo Bonzini #include "qemu/sockets.h"
31d1f29646SLuiz Capitulino #include "qmp-commands.h"
327b1b5d19SPaolo Bonzini #include "qapi/qmp/qint.h"
337b1b5d19SPaolo Bonzini #include "qapi/qmp/qbool.h"
347b1b5d19SPaolo Bonzini #include "qapi/qmp/qstring.h"
357b1b5d19SPaolo Bonzini #include "qapi/qmp/qjson.h"
361de7afc9SPaolo Bonzini #include "qemu/notify.h"
37caf71f86SPaolo Bonzini #include "migration/migration.h"
3883c9089eSPaolo Bonzini #include "monitor/monitor.h"
39e866e239SGerd Hoffmann #include "hw/hw.h"
4028ecbaeeSPaolo Bonzini #include "ui/spice-display.h"
4129b0040bSGerd Hoffmann 
4229b0040bSGerd Hoffmann /* core bits */
4329b0040bSGerd Hoffmann 
4429b0040bSGerd Hoffmann static SpiceServer *spice_server;
45e866e239SGerd Hoffmann static Notifier migration_state;
466f8c63fbSGerd Hoffmann static const char *auth = "spice";
477572150cSGerd Hoffmann static char *auth_passwd;
487572150cSGerd Hoffmann static time_t auth_expires = TIME_MAX;
4961c4efe2SYonit Halperin static int spice_migration_completed;
5029b0040bSGerd Hoffmann int using_spice = 0;
5129b0040bSGerd Hoffmann 
52f9ab6091SJan Kiszka static QemuThread me;
5322b626e2SGerd Hoffmann 
5429b0040bSGerd Hoffmann struct SpiceTimer {
5529b0040bSGerd Hoffmann     QEMUTimer *timer;
5629b0040bSGerd Hoffmann     QTAILQ_ENTRY(SpiceTimer) next;
5729b0040bSGerd Hoffmann };
5829b0040bSGerd Hoffmann static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers);
5929b0040bSGerd Hoffmann 
6029b0040bSGerd Hoffmann static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
6129b0040bSGerd Hoffmann {
6229b0040bSGerd Hoffmann     SpiceTimer *timer;
6329b0040bSGerd Hoffmann 
647267c094SAnthony Liguori     timer = g_malloc0(sizeof(*timer));
657bd427d8SPaolo Bonzini     timer->timer = qemu_new_timer_ms(rt_clock, func, opaque);
6629b0040bSGerd Hoffmann     QTAILQ_INSERT_TAIL(&timers, timer, next);
6729b0040bSGerd Hoffmann     return timer;
6829b0040bSGerd Hoffmann }
6929b0040bSGerd Hoffmann 
7029b0040bSGerd Hoffmann static void timer_start(SpiceTimer *timer, uint32_t ms)
7129b0040bSGerd Hoffmann {
727bd427d8SPaolo Bonzini     qemu_mod_timer(timer->timer, qemu_get_clock_ms(rt_clock) + ms);
7329b0040bSGerd Hoffmann }
7429b0040bSGerd Hoffmann 
7529b0040bSGerd Hoffmann static void timer_cancel(SpiceTimer *timer)
7629b0040bSGerd Hoffmann {
7729b0040bSGerd Hoffmann     qemu_del_timer(timer->timer);
7829b0040bSGerd Hoffmann }
7929b0040bSGerd Hoffmann 
8029b0040bSGerd Hoffmann static void timer_remove(SpiceTimer *timer)
8129b0040bSGerd Hoffmann {
8229b0040bSGerd Hoffmann     qemu_del_timer(timer->timer);
8329b0040bSGerd Hoffmann     qemu_free_timer(timer->timer);
8429b0040bSGerd Hoffmann     QTAILQ_REMOVE(&timers, timer, next);
857267c094SAnthony Liguori     g_free(timer);
8629b0040bSGerd Hoffmann }
8729b0040bSGerd Hoffmann 
8829b0040bSGerd Hoffmann struct SpiceWatch {
8929b0040bSGerd Hoffmann     int fd;
9029b0040bSGerd Hoffmann     int event_mask;
9129b0040bSGerd Hoffmann     SpiceWatchFunc func;
9229b0040bSGerd Hoffmann     void *opaque;
9329b0040bSGerd Hoffmann     QTAILQ_ENTRY(SpiceWatch) next;
9429b0040bSGerd Hoffmann };
9529b0040bSGerd Hoffmann static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches);
9629b0040bSGerd Hoffmann 
9729b0040bSGerd Hoffmann static void watch_read(void *opaque)
9829b0040bSGerd Hoffmann {
9929b0040bSGerd Hoffmann     SpiceWatch *watch = opaque;
10029b0040bSGerd Hoffmann     watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque);
10129b0040bSGerd Hoffmann }
10229b0040bSGerd Hoffmann 
10329b0040bSGerd Hoffmann static void watch_write(void *opaque)
10429b0040bSGerd Hoffmann {
10529b0040bSGerd Hoffmann     SpiceWatch *watch = opaque;
10629b0040bSGerd Hoffmann     watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque);
10729b0040bSGerd Hoffmann }
10829b0040bSGerd Hoffmann 
10929b0040bSGerd Hoffmann static void watch_update_mask(SpiceWatch *watch, int event_mask)
11029b0040bSGerd Hoffmann {
11129b0040bSGerd Hoffmann     IOHandler *on_read = NULL;
11229b0040bSGerd Hoffmann     IOHandler *on_write = NULL;
11329b0040bSGerd Hoffmann 
11429b0040bSGerd Hoffmann     watch->event_mask = event_mask;
11529b0040bSGerd Hoffmann     if (watch->event_mask & SPICE_WATCH_EVENT_READ) {
11629b0040bSGerd Hoffmann         on_read = watch_read;
11729b0040bSGerd Hoffmann     }
11829b0040bSGerd Hoffmann     if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
1193d6d306cSHans de Goede         on_write = watch_write;
12029b0040bSGerd Hoffmann     }
12129b0040bSGerd Hoffmann     qemu_set_fd_handler(watch->fd, on_read, on_write, watch);
12229b0040bSGerd Hoffmann }
12329b0040bSGerd Hoffmann 
12429b0040bSGerd Hoffmann static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
12529b0040bSGerd Hoffmann {
12629b0040bSGerd Hoffmann     SpiceWatch *watch;
12729b0040bSGerd Hoffmann 
1287267c094SAnthony Liguori     watch = g_malloc0(sizeof(*watch));
12929b0040bSGerd Hoffmann     watch->fd     = fd;
13029b0040bSGerd Hoffmann     watch->func   = func;
13129b0040bSGerd Hoffmann     watch->opaque = opaque;
13229b0040bSGerd Hoffmann     QTAILQ_INSERT_TAIL(&watches, watch, next);
13329b0040bSGerd Hoffmann 
13429b0040bSGerd Hoffmann     watch_update_mask(watch, event_mask);
13529b0040bSGerd Hoffmann     return watch;
13629b0040bSGerd Hoffmann }
13729b0040bSGerd Hoffmann 
13829b0040bSGerd Hoffmann static void watch_remove(SpiceWatch *watch)
13929b0040bSGerd Hoffmann {
14008cc67f3SGerd Hoffmann     qemu_set_fd_handler(watch->fd, NULL, NULL, NULL);
14129b0040bSGerd Hoffmann     QTAILQ_REMOVE(&watches, watch, next);
1427267c094SAnthony Liguori     g_free(watch);
14329b0040bSGerd Hoffmann }
14429b0040bSGerd Hoffmann 
145cb42a870SGerd Hoffmann typedef struct ChannelList ChannelList;
146cb42a870SGerd Hoffmann struct ChannelList {
147cb42a870SGerd Hoffmann     SpiceChannelEventInfo *info;
148cb42a870SGerd Hoffmann     QTAILQ_ENTRY(ChannelList) link;
149cb42a870SGerd Hoffmann };
150cb42a870SGerd Hoffmann static QTAILQ_HEAD(, ChannelList) channel_list = QTAILQ_HEAD_INITIALIZER(channel_list);
151cb42a870SGerd Hoffmann 
152cb42a870SGerd Hoffmann static void channel_list_add(SpiceChannelEventInfo *info)
153cb42a870SGerd Hoffmann {
154cb42a870SGerd Hoffmann     ChannelList *item;
155cb42a870SGerd Hoffmann 
1567267c094SAnthony Liguori     item = g_malloc0(sizeof(*item));
157cb42a870SGerd Hoffmann     item->info = info;
158cb42a870SGerd Hoffmann     QTAILQ_INSERT_TAIL(&channel_list, item, link);
159cb42a870SGerd Hoffmann }
160cb42a870SGerd Hoffmann 
161cb42a870SGerd Hoffmann static void channel_list_del(SpiceChannelEventInfo *info)
162cb42a870SGerd Hoffmann {
163cb42a870SGerd Hoffmann     ChannelList *item;
164cb42a870SGerd Hoffmann 
165cb42a870SGerd Hoffmann     QTAILQ_FOREACH(item, &channel_list, link) {
166cb42a870SGerd Hoffmann         if (item->info != info) {
167cb42a870SGerd Hoffmann             continue;
168cb42a870SGerd Hoffmann         }
169cb42a870SGerd Hoffmann         QTAILQ_REMOVE(&channel_list, item, link);
1707267c094SAnthony Liguori         g_free(item);
171cb42a870SGerd Hoffmann         return;
172cb42a870SGerd Hoffmann     }
173cb42a870SGerd Hoffmann }
174cb42a870SGerd Hoffmann 
1756f8c63fbSGerd Hoffmann static void add_addr_info(QDict *dict, struct sockaddr *addr, int len)
1766f8c63fbSGerd Hoffmann {
1776f8c63fbSGerd Hoffmann     char host[NI_MAXHOST], port[NI_MAXSERV];
1786f8c63fbSGerd Hoffmann     const char *family;
1796f8c63fbSGerd Hoffmann 
1806f8c63fbSGerd Hoffmann     getnameinfo(addr, len, host, sizeof(host), port, sizeof(port),
1816f8c63fbSGerd Hoffmann                 NI_NUMERICHOST | NI_NUMERICSERV);
1826f8c63fbSGerd Hoffmann     family = inet_strfamily(addr->sa_family);
1836f8c63fbSGerd Hoffmann 
1846f8c63fbSGerd Hoffmann     qdict_put(dict, "host", qstring_from_str(host));
1856f8c63fbSGerd Hoffmann     qdict_put(dict, "port", qstring_from_str(port));
1866f8c63fbSGerd Hoffmann     qdict_put(dict, "family", qstring_from_str(family));
1876f8c63fbSGerd Hoffmann }
1886f8c63fbSGerd Hoffmann 
1896f8c63fbSGerd Hoffmann static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info)
1906f8c63fbSGerd Hoffmann {
1916f8c63fbSGerd Hoffmann     int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
1926f8c63fbSGerd Hoffmann 
1936f8c63fbSGerd Hoffmann     qdict_put(dict, "connection-id", qint_from_int(info->connection_id));
1946f8c63fbSGerd Hoffmann     qdict_put(dict, "channel-type", qint_from_int(info->type));
1956f8c63fbSGerd Hoffmann     qdict_put(dict, "channel-id", qint_from_int(info->id));
1966f8c63fbSGerd Hoffmann     qdict_put(dict, "tls", qbool_from_int(tls));
1976f8c63fbSGerd Hoffmann }
1986f8c63fbSGerd Hoffmann 
1996f8c63fbSGerd Hoffmann static void channel_event(int event, SpiceChannelEventInfo *info)
2006f8c63fbSGerd Hoffmann {
2016f8c63fbSGerd Hoffmann     static const int qevent[] = {
2026f8c63fbSGerd Hoffmann         [ SPICE_CHANNEL_EVENT_CONNECTED    ] = QEVENT_SPICE_CONNECTED,
2036f8c63fbSGerd Hoffmann         [ SPICE_CHANNEL_EVENT_INITIALIZED  ] = QEVENT_SPICE_INITIALIZED,
2046f8c63fbSGerd Hoffmann         [ SPICE_CHANNEL_EVENT_DISCONNECTED ] = QEVENT_SPICE_DISCONNECTED,
2056f8c63fbSGerd Hoffmann     };
2066f8c63fbSGerd Hoffmann     QDict *server, *client;
2076f8c63fbSGerd Hoffmann     QObject *data;
2086f8c63fbSGerd Hoffmann 
20922b626e2SGerd Hoffmann     /*
21022b626e2SGerd Hoffmann      * Spice server might have called us from spice worker thread
21122b626e2SGerd Hoffmann      * context (happens on display channel disconnects).  Spice should
21222b626e2SGerd Hoffmann      * not do that.  It isn't that easy to fix it in spice and even
21322b626e2SGerd Hoffmann      * when it is fixed we still should cover the already released
21422b626e2SGerd Hoffmann      * spice versions.  So detect that we've been called from another
21522b626e2SGerd Hoffmann      * thread and grab the iothread lock if so before calling qemu
21622b626e2SGerd Hoffmann      * functions.
21722b626e2SGerd Hoffmann      */
218f9ab6091SJan Kiszka     bool need_lock = !qemu_thread_is_self(&me);
21922b626e2SGerd Hoffmann     if (need_lock) {
22022b626e2SGerd Hoffmann         qemu_mutex_lock_iothread();
22122b626e2SGerd Hoffmann     }
22222b626e2SGerd Hoffmann 
2236f8c63fbSGerd Hoffmann     client = qdict_new();
2246f8c63fbSGerd Hoffmann     server = qdict_new();
225faa98223SYonit Halperin 
226faa98223SYonit Halperin     if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
227faa98223SYonit Halperin         add_addr_info(client, (struct sockaddr *)&info->paddr_ext,
228faa98223SYonit Halperin                       info->plen_ext);
229faa98223SYonit Halperin         add_addr_info(server, (struct sockaddr *)&info->laddr_ext,
230faa98223SYonit Halperin                       info->llen_ext);
231faa98223SYonit Halperin     } else {
232339a475fSChristophe Fergeau         error_report("spice: %s, extended address is expected",
233faa98223SYonit Halperin                      __func__);
234faa98223SYonit Halperin     }
2356f8c63fbSGerd Hoffmann 
2366f8c63fbSGerd Hoffmann     if (event == SPICE_CHANNEL_EVENT_INITIALIZED) {
2376f8c63fbSGerd Hoffmann         qdict_put(server, "auth", qstring_from_str(auth));
2386f8c63fbSGerd Hoffmann         add_channel_info(client, info);
239cb42a870SGerd Hoffmann         channel_list_add(info);
240cb42a870SGerd Hoffmann     }
241cb42a870SGerd Hoffmann     if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) {
242cb42a870SGerd Hoffmann         channel_list_del(info);
2436f8c63fbSGerd Hoffmann     }
2446f8c63fbSGerd Hoffmann 
2456f8c63fbSGerd Hoffmann     data = qobject_from_jsonf("{ 'client': %p, 'server': %p }",
2466f8c63fbSGerd Hoffmann                               QOBJECT(client), QOBJECT(server));
2476f8c63fbSGerd Hoffmann     monitor_protocol_event(qevent[event], data);
2486f8c63fbSGerd Hoffmann     qobject_decref(data);
24922b626e2SGerd Hoffmann 
25022b626e2SGerd Hoffmann     if (need_lock) {
25122b626e2SGerd Hoffmann         qemu_mutex_unlock_iothread();
25222b626e2SGerd Hoffmann     }
2536f8c63fbSGerd Hoffmann }
2546f8c63fbSGerd Hoffmann 
25529b0040bSGerd Hoffmann static SpiceCoreInterface core_interface = {
25629b0040bSGerd Hoffmann     .base.type          = SPICE_INTERFACE_CORE,
25729b0040bSGerd Hoffmann     .base.description   = "qemu core services",
25829b0040bSGerd Hoffmann     .base.major_version = SPICE_INTERFACE_CORE_MAJOR,
25929b0040bSGerd Hoffmann     .base.minor_version = SPICE_INTERFACE_CORE_MINOR,
26029b0040bSGerd Hoffmann 
26129b0040bSGerd Hoffmann     .timer_add          = timer_add,
26229b0040bSGerd Hoffmann     .timer_start        = timer_start,
26329b0040bSGerd Hoffmann     .timer_cancel       = timer_cancel,
26429b0040bSGerd Hoffmann     .timer_remove       = timer_remove,
26529b0040bSGerd Hoffmann 
26629b0040bSGerd Hoffmann     .watch_add          = watch_add,
26729b0040bSGerd Hoffmann     .watch_update_mask  = watch_update_mask,
26829b0040bSGerd Hoffmann     .watch_remove       = watch_remove,
2696f8c63fbSGerd Hoffmann 
2706f8c63fbSGerd Hoffmann     .channel_event      = channel_event,
27129b0040bSGerd Hoffmann };
27229b0040bSGerd Hoffmann 
273026f773fSYonit Halperin typedef struct SpiceMigration {
274026f773fSYonit Halperin     SpiceMigrateInstance sin;
275026f773fSYonit Halperin     struct {
276026f773fSYonit Halperin         MonitorCompletion *cb;
277026f773fSYonit Halperin         void *opaque;
278026f773fSYonit Halperin     } connect_complete;
279026f773fSYonit Halperin } SpiceMigration;
280026f773fSYonit Halperin 
281026f773fSYonit Halperin static void migrate_connect_complete_cb(SpiceMigrateInstance *sin);
2822fdd16e2SYonit Halperin static void migrate_end_complete_cb(SpiceMigrateInstance *sin);
283026f773fSYonit Halperin 
284026f773fSYonit Halperin static const SpiceMigrateInterface migrate_interface = {
285026f773fSYonit Halperin     .base.type = SPICE_INTERFACE_MIGRATION,
286026f773fSYonit Halperin     .base.description = "migration",
287026f773fSYonit Halperin     .base.major_version = SPICE_INTERFACE_MIGRATION_MAJOR,
288026f773fSYonit Halperin     .base.minor_version = SPICE_INTERFACE_MIGRATION_MINOR,
289026f773fSYonit Halperin     .migrate_connect_complete = migrate_connect_complete_cb,
2902fdd16e2SYonit Halperin     .migrate_end_complete = migrate_end_complete_cb,
291026f773fSYonit Halperin };
292026f773fSYonit Halperin 
293026f773fSYonit Halperin static SpiceMigration spice_migrate;
294026f773fSYonit Halperin 
295026f773fSYonit Halperin static void migrate_connect_complete_cb(SpiceMigrateInstance *sin)
296026f773fSYonit Halperin {
297026f773fSYonit Halperin     SpiceMigration *sm = container_of(sin, SpiceMigration, sin);
298026f773fSYonit Halperin     if (sm->connect_complete.cb) {
299026f773fSYonit Halperin         sm->connect_complete.cb(sm->connect_complete.opaque, NULL);
300026f773fSYonit Halperin     }
301026f773fSYonit Halperin     sm->connect_complete.cb = NULL;
302026f773fSYonit Halperin }
3032fdd16e2SYonit Halperin 
3042fdd16e2SYonit Halperin static void migrate_end_complete_cb(SpiceMigrateInstance *sin)
3052fdd16e2SYonit Halperin {
3062fdd16e2SYonit Halperin     monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL);
30761c4efe2SYonit Halperin     spice_migration_completed = true;
3082fdd16e2SYonit Halperin }
309026f773fSYonit Halperin 
3109f04e09eSYonit Halperin /* config string parsing */
3119f04e09eSYonit Halperin 
3129f04e09eSYonit Halperin static int name2enum(const char *string, const char *table[], int entries)
3139f04e09eSYonit Halperin {
3149f04e09eSYonit Halperin     int i;
3159f04e09eSYonit Halperin 
3169f04e09eSYonit Halperin     if (string) {
3179f04e09eSYonit Halperin         for (i = 0; i < entries; i++) {
3189f04e09eSYonit Halperin             if (!table[i]) {
3199f04e09eSYonit Halperin                 continue;
3209f04e09eSYonit Halperin             }
3219f04e09eSYonit Halperin             if (strcmp(string, table[i]) != 0) {
3229f04e09eSYonit Halperin                 continue;
3239f04e09eSYonit Halperin             }
3249f04e09eSYonit Halperin             return i;
3259f04e09eSYonit Halperin         }
3269f04e09eSYonit Halperin     }
3279f04e09eSYonit Halperin     return -1;
3289f04e09eSYonit Halperin }
3299f04e09eSYonit Halperin 
3309f04e09eSYonit Halperin static int parse_name(const char *string, const char *optname,
3319f04e09eSYonit Halperin                       const char *table[], int entries)
3329f04e09eSYonit Halperin {
3339f04e09eSYonit Halperin     int value = name2enum(string, table, entries);
3349f04e09eSYonit Halperin 
3359f04e09eSYonit Halperin     if (value != -1) {
3369f04e09eSYonit Halperin         return value;
3379f04e09eSYonit Halperin     }
338339a475fSChristophe Fergeau     error_report("spice: invalid %s: %s", optname, string);
3399f04e09eSYonit Halperin     exit(1);
3409f04e09eSYonit Halperin }
3419f04e09eSYonit Halperin 
34284a23f25SGerd Hoffmann static const char *stream_video_names[] = {
34384a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_OFF ]    = "off",
34484a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_ALL ]    = "all",
34584a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_FILTER ] = "filter",
34684a23f25SGerd Hoffmann };
34784a23f25SGerd Hoffmann #define parse_stream_video(_name) \
348835cab85SChristophe Fergeau     parse_name(_name, "stream video control", \
349835cab85SChristophe Fergeau                stream_video_names, ARRAY_SIZE(stream_video_names))
35084a23f25SGerd Hoffmann 
3519f04e09eSYonit Halperin static const char *compression_names[] = {
3529f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_OFF ]      = "off",
3539f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz",
3549f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_AUTO_LZ ]  = "auto_lz",
3559f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_QUIC ]     = "quic",
3569f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_GLZ ]      = "glz",
3579f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_LZ ]       = "lz",
3589f04e09eSYonit Halperin };
3599f04e09eSYonit Halperin #define parse_compression(_name)                                        \
3609f04e09eSYonit Halperin     parse_name(_name, "image compression",                              \
3619f04e09eSYonit Halperin                compression_names, ARRAY_SIZE(compression_names))
3629f04e09eSYonit Halperin 
3639f04e09eSYonit Halperin static const char *wan_compression_names[] = {
3649f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_AUTO   ] = "auto",
3659f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_NEVER  ] = "never",
3669f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always",
3679f04e09eSYonit Halperin };
3689f04e09eSYonit Halperin #define parse_wan_compression(_name)                                    \
3699f04e09eSYonit Halperin     parse_name(_name, "wan compression",                                \
3709f04e09eSYonit Halperin                wan_compression_names, ARRAY_SIZE(wan_compression_names))
3719f04e09eSYonit Halperin 
37229b0040bSGerd Hoffmann /* functions for the rest of qemu */
37329b0040bSGerd Hoffmann 
374d1f29646SLuiz Capitulino static SpiceChannelList *qmp_query_spice_channels(void)
375cb42a870SGerd Hoffmann {
376d1f29646SLuiz Capitulino     SpiceChannelList *cur_item = NULL, *head = NULL;
377d1f29646SLuiz Capitulino     ChannelList *item;
378cb42a870SGerd Hoffmann 
379d1f29646SLuiz Capitulino     QTAILQ_FOREACH(item, &channel_list, link) {
380d1f29646SLuiz Capitulino         SpiceChannelList *chan;
381d1f29646SLuiz Capitulino         char host[NI_MAXHOST], port[NI_MAXSERV];
382faa98223SYonit Halperin         struct sockaddr *paddr;
383faa98223SYonit Halperin         socklen_t plen;
384cb42a870SGerd Hoffmann 
385d1f29646SLuiz Capitulino         chan = g_malloc0(sizeof(*chan));
386d1f29646SLuiz Capitulino         chan->value = g_malloc0(sizeof(*chan->value));
387cb42a870SGerd Hoffmann 
388faa98223SYonit Halperin         if (item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
389faa98223SYonit Halperin             paddr = (struct sockaddr *)&item->info->paddr_ext;
390faa98223SYonit Halperin             plen = item->info->plen_ext;
391faa98223SYonit Halperin         } else {
392faa98223SYonit Halperin             paddr = &item->info->paddr;
393faa98223SYonit Halperin             plen = item->info->plen;
394faa98223SYonit Halperin         }
395faa98223SYonit Halperin 
396faa98223SYonit Halperin         getnameinfo(paddr, plen,
397d1f29646SLuiz Capitulino                     host, sizeof(host), port, sizeof(port),
398d1f29646SLuiz Capitulino                     NI_NUMERICHOST | NI_NUMERICSERV);
399d1f29646SLuiz Capitulino         chan->value->host = g_strdup(host);
400d1f29646SLuiz Capitulino         chan->value->port = g_strdup(port);
401faa98223SYonit Halperin         chan->value->family = g_strdup(inet_strfamily(paddr->sa_family));
402cb42a870SGerd Hoffmann 
403d1f29646SLuiz Capitulino         chan->value->connection_id = item->info->connection_id;
404d1f29646SLuiz Capitulino         chan->value->channel_type = item->info->type;
405d1f29646SLuiz Capitulino         chan->value->channel_id = item->info->id;
406d1f29646SLuiz Capitulino         chan->value->tls = item->info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
407cb42a870SGerd Hoffmann 
408d1f29646SLuiz Capitulino        /* XXX: waiting for the qapi to support GSList */
409d1f29646SLuiz Capitulino         if (!cur_item) {
410d1f29646SLuiz Capitulino             head = cur_item = chan;
411cb42a870SGerd Hoffmann         } else {
412d1f29646SLuiz Capitulino             cur_item->next = chan;
413d1f29646SLuiz Capitulino             cur_item = chan;
414cb42a870SGerd Hoffmann         }
415cb42a870SGerd Hoffmann     }
416cb42a870SGerd Hoffmann 
417d1f29646SLuiz Capitulino     return head;
418d1f29646SLuiz Capitulino }
419d1f29646SLuiz Capitulino 
4204d454574SPaolo Bonzini static QemuOptsList qemu_spice_opts = {
4214d454574SPaolo Bonzini     .name = "spice",
4224d454574SPaolo Bonzini     .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head),
4234d454574SPaolo Bonzini     .desc = {
4244d454574SPaolo Bonzini         {
4254d454574SPaolo Bonzini             .name = "port",
4264d454574SPaolo Bonzini             .type = QEMU_OPT_NUMBER,
4274d454574SPaolo Bonzini         },{
4284d454574SPaolo Bonzini             .name = "tls-port",
4294d454574SPaolo Bonzini             .type = QEMU_OPT_NUMBER,
4304d454574SPaolo Bonzini         },{
4314d454574SPaolo Bonzini             .name = "addr",
4324d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4334d454574SPaolo Bonzini         },{
4344d454574SPaolo Bonzini             .name = "ipv4",
4354d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4364d454574SPaolo Bonzini         },{
4374d454574SPaolo Bonzini             .name = "ipv6",
4384d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4394d454574SPaolo Bonzini         },{
4404d454574SPaolo Bonzini             .name = "password",
4414d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4424d454574SPaolo Bonzini         },{
4434d454574SPaolo Bonzini             .name = "disable-ticketing",
4444d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4454d454574SPaolo Bonzini         },{
4464d454574SPaolo Bonzini             .name = "disable-copy-paste",
4474d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4484d454574SPaolo Bonzini         },{
4494d454574SPaolo Bonzini             .name = "sasl",
4504d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4514d454574SPaolo Bonzini         },{
4524d454574SPaolo Bonzini             .name = "x509-dir",
4534d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4544d454574SPaolo Bonzini         },{
4554d454574SPaolo Bonzini             .name = "x509-key-file",
4564d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4574d454574SPaolo Bonzini         },{
4584d454574SPaolo Bonzini             .name = "x509-key-password",
4594d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4604d454574SPaolo Bonzini         },{
4614d454574SPaolo Bonzini             .name = "x509-cert-file",
4624d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4634d454574SPaolo Bonzini         },{
4644d454574SPaolo Bonzini             .name = "x509-cacert-file",
4654d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4664d454574SPaolo Bonzini         },{
4674d454574SPaolo Bonzini             .name = "x509-dh-key-file",
4684d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4694d454574SPaolo Bonzini         },{
4704d454574SPaolo Bonzini             .name = "tls-ciphers",
4714d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4724d454574SPaolo Bonzini         },{
4734d454574SPaolo Bonzini             .name = "tls-channel",
4744d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4754d454574SPaolo Bonzini         },{
4764d454574SPaolo Bonzini             .name = "plaintext-channel",
4774d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4784d454574SPaolo Bonzini         },{
4794d454574SPaolo Bonzini             .name = "image-compression",
4804d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4814d454574SPaolo Bonzini         },{
4824d454574SPaolo Bonzini             .name = "jpeg-wan-compression",
4834d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4844d454574SPaolo Bonzini         },{
4854d454574SPaolo Bonzini             .name = "zlib-glz-wan-compression",
4864d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4874d454574SPaolo Bonzini         },{
4884d454574SPaolo Bonzini             .name = "streaming-video",
4894d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4904d454574SPaolo Bonzini         },{
4914d454574SPaolo Bonzini             .name = "agent-mouse",
4924d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4934d454574SPaolo Bonzini         },{
4944d454574SPaolo Bonzini             .name = "playback-compression",
4954d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4964d454574SPaolo Bonzini         }, {
4974d454574SPaolo Bonzini             .name = "seamless-migration",
4984d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4994d454574SPaolo Bonzini         },
5004d454574SPaolo Bonzini         { /* end of list */ }
5014d454574SPaolo Bonzini     },
5024d454574SPaolo Bonzini };
5034d454574SPaolo Bonzini 
504d1f29646SLuiz Capitulino SpiceInfo *qmp_query_spice(Error **errp)
505cb42a870SGerd Hoffmann {
506cb42a870SGerd Hoffmann     QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
507cb42a870SGerd Hoffmann     int port, tls_port;
508d1f29646SLuiz Capitulino     const char *addr;
509d1f29646SLuiz Capitulino     SpiceInfo *info;
5108df0c7e8SAlon Levy     char version_string[20]; /* 12 = |255.255.255\0| is the max */
511cb42a870SGerd Hoffmann 
512d1f29646SLuiz Capitulino     info = g_malloc0(sizeof(*info));
513d1f29646SLuiz Capitulino 
5143bb781f3SAlon Levy     if (!spice_server || !opts) {
515d1f29646SLuiz Capitulino         info->enabled = false;
516d1f29646SLuiz Capitulino         return info;
517cb42a870SGerd Hoffmann     }
518cb42a870SGerd Hoffmann 
519d1f29646SLuiz Capitulino     info->enabled = true;
52061c4efe2SYonit Halperin     info->migrated = spice_migration_completed;
521d1f29646SLuiz Capitulino 
522cb42a870SGerd Hoffmann     addr = qemu_opt_get(opts, "addr");
523cb42a870SGerd Hoffmann     port = qemu_opt_get_number(opts, "port", 0);
524cb42a870SGerd Hoffmann     tls_port = qemu_opt_get_number(opts, "tls-port", 0);
525cb42a870SGerd Hoffmann 
526d1f29646SLuiz Capitulino     info->has_auth = true;
527d1f29646SLuiz Capitulino     info->auth = g_strdup(auth);
528d1f29646SLuiz Capitulino 
529d1f29646SLuiz Capitulino     info->has_host = true;
530d1f29646SLuiz Capitulino     info->host = g_strdup(addr ? addr : "0.0.0.0");
531d1f29646SLuiz Capitulino 
532d1f29646SLuiz Capitulino     info->has_compiled_version = true;
5338df0c7e8SAlon Levy     snprintf(version_string, sizeof(version_string), "%d.%d.%d",
5348df0c7e8SAlon Levy              (SPICE_SERVER_VERSION & 0xff0000) >> 16,
5358df0c7e8SAlon Levy              (SPICE_SERVER_VERSION & 0xff00) >> 8,
5368df0c7e8SAlon Levy              SPICE_SERVER_VERSION & 0xff);
537d1f29646SLuiz Capitulino     info->compiled_version = g_strdup(version_string);
538d1f29646SLuiz Capitulino 
539cb42a870SGerd Hoffmann     if (port) {
540d1f29646SLuiz Capitulino         info->has_port = true;
541d1f29646SLuiz Capitulino         info->port = port;
542cb42a870SGerd Hoffmann     }
543cb42a870SGerd Hoffmann     if (tls_port) {
544d1f29646SLuiz Capitulino         info->has_tls_port = true;
545d1f29646SLuiz Capitulino         info->tls_port = tls_port;
546cb42a870SGerd Hoffmann     }
547cb42a870SGerd Hoffmann 
5484efee029SAlon Levy     info->mouse_mode = spice_server_is_server_mouse(spice_server) ?
5494efee029SAlon Levy                        SPICE_QUERY_MOUSE_MODE_SERVER :
5504efee029SAlon Levy                        SPICE_QUERY_MOUSE_MODE_CLIENT;
55167be6726SGerd Hoffmann 
552d1f29646SLuiz Capitulino     /* for compatibility with the original command */
553d1f29646SLuiz Capitulino     info->has_channels = true;
554d1f29646SLuiz Capitulino     info->channels = qmp_query_spice_channels();
555d1f29646SLuiz Capitulino 
556d1f29646SLuiz Capitulino     return info;
557cb42a870SGerd Hoffmann }
558cb42a870SGerd Hoffmann 
5599e8dd451SJan Kiszka static void migration_state_notifier(Notifier *notifier, void *data)
560e866e239SGerd Hoffmann {
5617073693bSJuan Quintela     MigrationState *s = data;
562e866e239SGerd Hoffmann 
563026f773fSYonit Halperin     if (migration_is_active(s)) {
564026f773fSYonit Halperin         spice_server_migrate_start(spice_server);
565026f773fSYonit Halperin     } else if (migration_has_finished(s)) {
566026f773fSYonit Halperin         spice_server_migrate_end(spice_server, true);
567026f773fSYonit Halperin     } else if (migration_has_failed(s)) {
568026f773fSYonit Halperin         spice_server_migrate_end(spice_server, false);
569e866e239SGerd Hoffmann     }
570e866e239SGerd Hoffmann }
571e866e239SGerd Hoffmann 
572e866e239SGerd Hoffmann int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
573edc5cb1aSYonit Halperin                             const char *subject,
574edc5cb1aSYonit Halperin                             MonitorCompletion *cb, void *opaque)
575e866e239SGerd Hoffmann {
576edc5cb1aSYonit Halperin     int ret;
57767be6726SGerd Hoffmann 
578026f773fSYonit Halperin     spice_migrate.connect_complete.cb = cb;
579026f773fSYonit Halperin     spice_migrate.connect_complete.opaque = opaque;
580026f773fSYonit Halperin     ret = spice_server_migrate_connect(spice_server, hostname,
581026f773fSYonit Halperin                                        port, tls_port, subject);
582edc5cb1aSYonit Halperin     return ret;
583e866e239SGerd Hoffmann }
584e866e239SGerd Hoffmann 
58517b6dea0SGerd Hoffmann static int add_channel(const char *name, const char *value, void *opaque)
58617b6dea0SGerd Hoffmann {
58717b6dea0SGerd Hoffmann     int security = 0;
58817b6dea0SGerd Hoffmann     int rc;
58917b6dea0SGerd Hoffmann 
59017b6dea0SGerd Hoffmann     if (strcmp(name, "tls-channel") == 0) {
59135c63329SChristophe Fergeau         int *tls_port = opaque;
59235c63329SChristophe Fergeau         if (!*tls_port) {
59335c63329SChristophe Fergeau             error_report("spice: tried to setup tls-channel"
59435c63329SChristophe Fergeau                          " without specifying a TLS port");
59535c63329SChristophe Fergeau             exit(1);
59635c63329SChristophe Fergeau         }
59717b6dea0SGerd Hoffmann         security = SPICE_CHANNEL_SECURITY_SSL;
59817b6dea0SGerd Hoffmann     }
59917b6dea0SGerd Hoffmann     if (strcmp(name, "plaintext-channel") == 0) {
60017b6dea0SGerd Hoffmann         security = SPICE_CHANNEL_SECURITY_NONE;
60117b6dea0SGerd Hoffmann     }
60217b6dea0SGerd Hoffmann     if (security == 0) {
60317b6dea0SGerd Hoffmann         return 0;
60417b6dea0SGerd Hoffmann     }
60517b6dea0SGerd Hoffmann     if (strcmp(value, "default") == 0) {
60617b6dea0SGerd Hoffmann         rc = spice_server_set_channel_security(spice_server, NULL, security);
60717b6dea0SGerd Hoffmann     } else {
60817b6dea0SGerd Hoffmann         rc = spice_server_set_channel_security(spice_server, value, security);
60917b6dea0SGerd Hoffmann     }
61017b6dea0SGerd Hoffmann     if (rc != 0) {
611339a475fSChristophe Fergeau         error_report("spice: failed to set channel security for %s", value);
61217b6dea0SGerd Hoffmann         exit(1);
61317b6dea0SGerd Hoffmann     }
61417b6dea0SGerd Hoffmann     return 0;
61517b6dea0SGerd Hoffmann }
61617b6dea0SGerd Hoffmann 
617f5bb039cSYonit Halperin static void vm_change_state_handler(void *opaque, int running,
618f5bb039cSYonit Halperin                                     RunState state)
619f5bb039cSYonit Halperin {
620f5bb039cSYonit Halperin     if (running) {
62171d388d4SYonit Halperin         qemu_spice_display_start();
622f5bb039cSYonit Halperin         spice_server_vm_start(spice_server);
623f5bb039cSYonit Halperin     } else {
624f5bb039cSYonit Halperin         spice_server_vm_stop(spice_server);
62571d388d4SYonit Halperin         qemu_spice_display_stop();
626f5bb039cSYonit Halperin     }
627f5bb039cSYonit Halperin }
628f5bb039cSYonit Halperin 
62929b0040bSGerd Hoffmann void qemu_spice_init(void)
63029b0040bSGerd Hoffmann {
63129b0040bSGerd Hoffmann     QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
632333b0eebSGerd Hoffmann     const char *password, *str, *x509_dir, *addr,
633c448e855SGerd Hoffmann         *x509_key_password = NULL,
634c448e855SGerd Hoffmann         *x509_dh_file = NULL,
635c448e855SGerd Hoffmann         *tls_ciphers = NULL;
636c448e855SGerd Hoffmann     char *x509_key_file = NULL,
637c448e855SGerd Hoffmann         *x509_cert_file = NULL,
638c448e855SGerd Hoffmann         *x509_cacert_file = NULL;
639f61d6960SGerd Hoffmann     int port, tls_port, len, addr_flags;
6409f04e09eSYonit Halperin     spice_image_compression_t compression;
6419f04e09eSYonit Halperin     spice_wan_compression_t wan_compr;
6428c957053SYonit Halperin     bool seamless_migration;
64329b0040bSGerd Hoffmann 
644f9ab6091SJan Kiszka     qemu_thread_get_self(&me);
64522b626e2SGerd Hoffmann 
64629b0040bSGerd Hoffmann     if (!opts) {
64729b0040bSGerd Hoffmann         return;
64829b0040bSGerd Hoffmann     }
64929b0040bSGerd Hoffmann     port = qemu_opt_get_number(opts, "port", 0);
650c448e855SGerd Hoffmann     tls_port = qemu_opt_get_number(opts, "tls-port", 0);
651c448e855SGerd Hoffmann     if (!port && !tls_port) {
652339a475fSChristophe Fergeau         error_report("neither port nor tls-port specified for spice");
653df9cb669SGerd Hoffmann         exit(1);
654df9cb669SGerd Hoffmann     }
655df9cb669SGerd Hoffmann     if (port < 0 || port > 65535) {
656339a475fSChristophe Fergeau         error_report("spice port is out of range");
657df9cb669SGerd Hoffmann         exit(1);
658df9cb669SGerd Hoffmann     }
659df9cb669SGerd Hoffmann     if (tls_port < 0 || tls_port > 65535) {
660339a475fSChristophe Fergeau         error_report("spice tls-port is out of range");
661df9cb669SGerd Hoffmann         exit(1);
66229b0040bSGerd Hoffmann     }
66329b0040bSGerd Hoffmann     password = qemu_opt_get(opts, "password");
66429b0040bSGerd Hoffmann 
665c448e855SGerd Hoffmann     if (tls_port) {
666c448e855SGerd Hoffmann         x509_dir = qemu_opt_get(opts, "x509-dir");
667c448e855SGerd Hoffmann         if (NULL == x509_dir) {
668c448e855SGerd Hoffmann             x509_dir = ".";
669c448e855SGerd Hoffmann         }
670c448e855SGerd Hoffmann         len = strlen(x509_dir) + 32;
671c448e855SGerd Hoffmann 
672c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-key-file");
673c448e855SGerd Hoffmann         if (str) {
6747267c094SAnthony Liguori             x509_key_file = g_strdup(str);
675c448e855SGerd Hoffmann         } else {
6767267c094SAnthony Liguori             x509_key_file = g_malloc(len);
677c448e855SGerd Hoffmann             snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE);
678c448e855SGerd Hoffmann         }
679c448e855SGerd Hoffmann 
680c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-cert-file");
681c448e855SGerd Hoffmann         if (str) {
6827267c094SAnthony Liguori             x509_cert_file = g_strdup(str);
683c448e855SGerd Hoffmann         } else {
6847267c094SAnthony Liguori             x509_cert_file = g_malloc(len);
685c448e855SGerd Hoffmann             snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE);
686c448e855SGerd Hoffmann         }
687c448e855SGerd Hoffmann 
688c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-cacert-file");
689c448e855SGerd Hoffmann         if (str) {
6907267c094SAnthony Liguori             x509_cacert_file = g_strdup(str);
691c448e855SGerd Hoffmann         } else {
6927267c094SAnthony Liguori             x509_cacert_file = g_malloc(len);
693c448e855SGerd Hoffmann             snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE);
694c448e855SGerd Hoffmann         }
695c448e855SGerd Hoffmann 
696c448e855SGerd Hoffmann         x509_key_password = qemu_opt_get(opts, "x509-key-password");
6979995c0b7SLei Li         x509_dh_file = qemu_opt_get(opts, "x509-dh-key-file");
698c448e855SGerd Hoffmann         tls_ciphers = qemu_opt_get(opts, "tls-ciphers");
699c448e855SGerd Hoffmann     }
700c448e855SGerd Hoffmann 
701333b0eebSGerd Hoffmann     addr = qemu_opt_get(opts, "addr");
702333b0eebSGerd Hoffmann     addr_flags = 0;
703333b0eebSGerd Hoffmann     if (qemu_opt_get_bool(opts, "ipv4", 0)) {
704333b0eebSGerd Hoffmann         addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY;
705333b0eebSGerd Hoffmann     } else if (qemu_opt_get_bool(opts, "ipv6", 0)) {
706333b0eebSGerd Hoffmann         addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY;
707333b0eebSGerd Hoffmann     }
708333b0eebSGerd Hoffmann 
70929b0040bSGerd Hoffmann     spice_server = spice_server_new();
710333b0eebSGerd Hoffmann     spice_server_set_addr(spice_server, addr ? addr : "", addr_flags);
711c448e855SGerd Hoffmann     if (port) {
71229b0040bSGerd Hoffmann         spice_server_set_port(spice_server, port);
713c448e855SGerd Hoffmann     }
714c448e855SGerd Hoffmann     if (tls_port) {
715c448e855SGerd Hoffmann         spice_server_set_tls(spice_server, tls_port,
716c448e855SGerd Hoffmann                              x509_cacert_file,
717c448e855SGerd Hoffmann                              x509_cert_file,
718c448e855SGerd Hoffmann                              x509_key_file,
719c448e855SGerd Hoffmann                              x509_key_password,
720c448e855SGerd Hoffmann                              x509_dh_file,
721c448e855SGerd Hoffmann                              tls_ciphers);
722c448e855SGerd Hoffmann     }
72329b0040bSGerd Hoffmann     if (password) {
72429b0040bSGerd Hoffmann         spice_server_set_ticket(spice_server, password, 0, 0, 0);
72529b0040bSGerd Hoffmann     }
72648b3ed0aSMarc-André Lureau     if (qemu_opt_get_bool(opts, "sasl", 0)) {
72748b3ed0aSMarc-André Lureau         if (spice_server_set_sasl_appname(spice_server, "qemu") == -1 ||
72848b3ed0aSMarc-André Lureau             spice_server_set_sasl(spice_server, 1) == -1) {
729339a475fSChristophe Fergeau             error_report("spice: failed to enable sasl");
73048b3ed0aSMarc-André Lureau             exit(1);
73148b3ed0aSMarc-André Lureau         }
73248b3ed0aSMarc-André Lureau     }
73329b0040bSGerd Hoffmann     if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) {
7346f8c63fbSGerd Hoffmann         auth = "none";
73529b0040bSGerd Hoffmann         spice_server_set_noauth(spice_server);
73629b0040bSGerd Hoffmann     }
73729b0040bSGerd Hoffmann 
738d4970b07SHans de Goede     if (qemu_opt_get_bool(opts, "disable-copy-paste", 0)) {
739d4970b07SHans de Goede         spice_server_set_agent_copypaste(spice_server, false);
740d4970b07SHans de Goede     }
741d4970b07SHans de Goede 
7429f04e09eSYonit Halperin     compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ;
7439f04e09eSYonit Halperin     str = qemu_opt_get(opts, "image-compression");
7449f04e09eSYonit Halperin     if (str) {
7459f04e09eSYonit Halperin         compression = parse_compression(str);
7469f04e09eSYonit Halperin     }
7479f04e09eSYonit Halperin     spice_server_set_image_compression(spice_server, compression);
7489f04e09eSYonit Halperin 
7499f04e09eSYonit Halperin     wan_compr = SPICE_WAN_COMPRESSION_AUTO;
7509f04e09eSYonit Halperin     str = qemu_opt_get(opts, "jpeg-wan-compression");
7519f04e09eSYonit Halperin     if (str) {
7529f04e09eSYonit Halperin         wan_compr = parse_wan_compression(str);
7539f04e09eSYonit Halperin     }
7549f04e09eSYonit Halperin     spice_server_set_jpeg_compression(spice_server, wan_compr);
7559f04e09eSYonit Halperin 
7569f04e09eSYonit Halperin     wan_compr = SPICE_WAN_COMPRESSION_AUTO;
7579f04e09eSYonit Halperin     str = qemu_opt_get(opts, "zlib-glz-wan-compression");
7589f04e09eSYonit Halperin     if (str) {
7599f04e09eSYonit Halperin         wan_compr = parse_wan_compression(str);
7609f04e09eSYonit Halperin     }
7619f04e09eSYonit Halperin     spice_server_set_zlib_glz_compression(spice_server, wan_compr);
76229b0040bSGerd Hoffmann 
76384a23f25SGerd Hoffmann     str = qemu_opt_get(opts, "streaming-video");
76484a23f25SGerd Hoffmann     if (str) {
765f61d6960SGerd Hoffmann         int streaming_video = parse_stream_video(str);
76684a23f25SGerd Hoffmann         spice_server_set_streaming_video(spice_server, streaming_video);
76784a23f25SGerd Hoffmann     }
76884a23f25SGerd Hoffmann 
76984a23f25SGerd Hoffmann     spice_server_set_agent_mouse
77084a23f25SGerd Hoffmann         (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1));
77184a23f25SGerd Hoffmann     spice_server_set_playback_compression
77284a23f25SGerd Hoffmann         (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1));
77384a23f25SGerd Hoffmann 
77435c63329SChristophe Fergeau     qemu_opt_foreach(opts, add_channel, &tls_port, 0);
77517b6dea0SGerd Hoffmann 
776d0638b18SMarc-André Lureau     spice_server_set_name(spice_server, qemu_name);
777d0638b18SMarc-André Lureau     spice_server_set_uuid(spice_server, qemu_uuid);
778d0638b18SMarc-André Lureau 
7798c957053SYonit Halperin     seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0);
7808c957053SYonit Halperin     spice_server_set_seamless_migration(spice_server, seamless_migration);
781fba810f1SGerd Hoffmann     if (0 != spice_server_init(spice_server, &core_interface)) {
782339a475fSChristophe Fergeau         error_report("failed to initialize spice server");
783fba810f1SGerd Hoffmann         exit(1);
784fba810f1SGerd Hoffmann     };
78529b0040bSGerd Hoffmann     using_spice = 1;
786864401c2SGerd Hoffmann 
787e866e239SGerd Hoffmann     migration_state.notify = migration_state_notifier;
788e866e239SGerd Hoffmann     add_migration_state_change_notifier(&migration_state);
789026f773fSYonit Halperin     spice_migrate.sin.base.sif = &migrate_interface.base;
790026f773fSYonit Halperin     spice_migrate.connect_complete.cb = NULL;
791026f773fSYonit Halperin     qemu_spice_add_interface(&spice_migrate.sin.base);
792e866e239SGerd Hoffmann 
793864401c2SGerd Hoffmann     qemu_spice_input_init();
7943e313753SGerd Hoffmann     qemu_spice_audio_init();
795c448e855SGerd Hoffmann 
796bfb82a28SStefan Hajnoczi     qemu_add_vm_change_state_handler(vm_change_state_handler, NULL);
797f5bb039cSYonit Halperin 
7987267c094SAnthony Liguori     g_free(x509_key_file);
7997267c094SAnthony Liguori     g_free(x509_cert_file);
8007267c094SAnthony Liguori     g_free(x509_cacert_file);
801afd0b409SMarc-André Lureau 
802afd0b409SMarc-André Lureau #if SPICE_SERVER_VERSION >= 0x000c02
803afd0b409SMarc-André Lureau     qemu_spice_register_ports();
804afd0b409SMarc-André Lureau #endif
80529b0040bSGerd Hoffmann }
80629b0040bSGerd Hoffmann 
80729b0040bSGerd Hoffmann int qemu_spice_add_interface(SpiceBaseInstance *sin)
80829b0040bSGerd Hoffmann {
809a19cbfb3SGerd Hoffmann     if (!spice_server) {
810a19cbfb3SGerd Hoffmann         if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) {
811339a475fSChristophe Fergeau             error_report("Oops: spice configured but not active");
812a19cbfb3SGerd Hoffmann             exit(1);
813a19cbfb3SGerd Hoffmann         }
814a19cbfb3SGerd Hoffmann         /*
815a19cbfb3SGerd Hoffmann          * Create a spice server instance.
816a19cbfb3SGerd Hoffmann          * It does *not* listen on the network.
817a19cbfb3SGerd Hoffmann          * It handles QXL local rendering only.
818a19cbfb3SGerd Hoffmann          *
819a19cbfb3SGerd Hoffmann          * With a command line like '-vnc :0 -vga qxl' you'll end up here.
820a19cbfb3SGerd Hoffmann          */
821a19cbfb3SGerd Hoffmann         spice_server = spice_server_new();
822a19cbfb3SGerd Hoffmann         spice_server_init(spice_server, &core_interface);
823bfb82a28SStefan Hajnoczi         qemu_add_vm_change_state_handler(vm_change_state_handler, NULL);
824a19cbfb3SGerd Hoffmann     }
82571d388d4SYonit Halperin 
82629b0040bSGerd Hoffmann     return spice_server_add_interface(spice_server, sin);
82729b0040bSGerd Hoffmann }
82829b0040bSGerd Hoffmann 
8297572150cSGerd Hoffmann static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn)
8307572150cSGerd Hoffmann {
8317572150cSGerd Hoffmann     time_t lifetime, now = time(NULL);
8327572150cSGerd Hoffmann     char *passwd;
8337572150cSGerd Hoffmann 
8347572150cSGerd Hoffmann     if (now < auth_expires) {
8357572150cSGerd Hoffmann         passwd = auth_passwd;
8367572150cSGerd Hoffmann         lifetime = (auth_expires - now);
8377572150cSGerd Hoffmann         if (lifetime > INT_MAX) {
8387572150cSGerd Hoffmann             lifetime = INT_MAX;
8397572150cSGerd Hoffmann         }
8407572150cSGerd Hoffmann     } else {
8417572150cSGerd Hoffmann         passwd = NULL;
8427572150cSGerd Hoffmann         lifetime = 1;
8437572150cSGerd Hoffmann     }
8447572150cSGerd Hoffmann     return spice_server_set_ticket(spice_server, passwd, lifetime,
8457572150cSGerd Hoffmann                                    fail_if_conn, disconnect_if_conn);
8467572150cSGerd Hoffmann }
8477572150cSGerd Hoffmann 
8487572150cSGerd Hoffmann int qemu_spice_set_passwd(const char *passwd,
8497572150cSGerd Hoffmann                           bool fail_if_conn, bool disconnect_if_conn)
8507572150cSGerd Hoffmann {
851*fd3bea3fSMarkus Armbruster     g_free(auth_passwd);
852*fd3bea3fSMarkus Armbruster     auth_passwd = g_strdup(passwd);
8537572150cSGerd Hoffmann     return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn);
8547572150cSGerd Hoffmann }
8557572150cSGerd Hoffmann 
8567572150cSGerd Hoffmann int qemu_spice_set_pw_expire(time_t expires)
8577572150cSGerd Hoffmann {
8587572150cSGerd Hoffmann     auth_expires = expires;
8597572150cSGerd Hoffmann     return qemu_spice_set_ticket(false, false);
8607572150cSGerd Hoffmann }
8617572150cSGerd Hoffmann 
862f1f5f407SDaniel P. Berrange int qemu_spice_display_add_client(int csock, int skipauth, int tls)
863f1f5f407SDaniel P. Berrange {
864f1f5f407SDaniel P. Berrange     if (tls) {
865f1f5f407SDaniel P. Berrange         return spice_server_add_ssl_client(spice_server, csock, skipauth);
866f1f5f407SDaniel P. Berrange     } else {
867f1f5f407SDaniel P. Berrange         return spice_server_add_client(spice_server, csock, skipauth);
868f1f5f407SDaniel P. Berrange     }
869f1f5f407SDaniel P. Berrange }
870f1f5f407SDaniel P. Berrange 
87129b0040bSGerd Hoffmann static void spice_register_config(void)
87229b0040bSGerd Hoffmann {
87329b0040bSGerd Hoffmann     qemu_add_opts(&qemu_spice_opts);
87429b0040bSGerd Hoffmann }
87529b0040bSGerd Hoffmann machine_init(spice_register_config);
876