xref: /qemu/ui/spice-core.c (revision 026f773f)
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>
2222b626e2SGerd Hoffmann #include <pthread.h>
236f8c63fbSGerd Hoffmann 
2429b0040bSGerd Hoffmann #include "qemu-common.h"
2529b0040bSGerd Hoffmann #include "qemu-spice.h"
2629b0040bSGerd Hoffmann #include "qemu-timer.h"
2729b0040bSGerd Hoffmann #include "qemu-queue.h"
28c448e855SGerd Hoffmann #include "qemu-x509.h"
296f8c63fbSGerd Hoffmann #include "qemu_socket.h"
306f8c63fbSGerd Hoffmann #include "qint.h"
316f8c63fbSGerd Hoffmann #include "qbool.h"
326f8c63fbSGerd Hoffmann #include "qstring.h"
336f8c63fbSGerd Hoffmann #include "qjson.h"
34e866e239SGerd Hoffmann #include "notify.h"
35e866e239SGerd Hoffmann #include "migration.h"
3629b0040bSGerd Hoffmann #include "monitor.h"
37e866e239SGerd Hoffmann #include "hw/hw.h"
3829b0040bSGerd Hoffmann 
3929b0040bSGerd Hoffmann /* core bits */
4029b0040bSGerd Hoffmann 
4129b0040bSGerd Hoffmann static SpiceServer *spice_server;
42e866e239SGerd Hoffmann static Notifier migration_state;
436f8c63fbSGerd Hoffmann static const char *auth = "spice";
447572150cSGerd Hoffmann static char *auth_passwd;
457572150cSGerd Hoffmann static time_t auth_expires = TIME_MAX;
4629b0040bSGerd Hoffmann int using_spice = 0;
4729b0040bSGerd Hoffmann 
4822b626e2SGerd Hoffmann static pthread_t me;
4922b626e2SGerd Hoffmann 
5029b0040bSGerd Hoffmann struct SpiceTimer {
5129b0040bSGerd Hoffmann     QEMUTimer *timer;
5229b0040bSGerd Hoffmann     QTAILQ_ENTRY(SpiceTimer) next;
5329b0040bSGerd Hoffmann };
5429b0040bSGerd Hoffmann static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers);
5529b0040bSGerd Hoffmann 
5629b0040bSGerd Hoffmann static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
5729b0040bSGerd Hoffmann {
5829b0040bSGerd Hoffmann     SpiceTimer *timer;
5929b0040bSGerd Hoffmann 
607267c094SAnthony Liguori     timer = g_malloc0(sizeof(*timer));
617bd427d8SPaolo Bonzini     timer->timer = qemu_new_timer_ms(rt_clock, func, opaque);
6229b0040bSGerd Hoffmann     QTAILQ_INSERT_TAIL(&timers, timer, next);
6329b0040bSGerd Hoffmann     return timer;
6429b0040bSGerd Hoffmann }
6529b0040bSGerd Hoffmann 
6629b0040bSGerd Hoffmann static void timer_start(SpiceTimer *timer, uint32_t ms)
6729b0040bSGerd Hoffmann {
687bd427d8SPaolo Bonzini     qemu_mod_timer(timer->timer, qemu_get_clock_ms(rt_clock) + ms);
6929b0040bSGerd Hoffmann }
7029b0040bSGerd Hoffmann 
7129b0040bSGerd Hoffmann static void timer_cancel(SpiceTimer *timer)
7229b0040bSGerd Hoffmann {
7329b0040bSGerd Hoffmann     qemu_del_timer(timer->timer);
7429b0040bSGerd Hoffmann }
7529b0040bSGerd Hoffmann 
7629b0040bSGerd Hoffmann static void timer_remove(SpiceTimer *timer)
7729b0040bSGerd Hoffmann {
7829b0040bSGerd Hoffmann     qemu_del_timer(timer->timer);
7929b0040bSGerd Hoffmann     qemu_free_timer(timer->timer);
8029b0040bSGerd Hoffmann     QTAILQ_REMOVE(&timers, timer, next);
817267c094SAnthony Liguori     g_free(timer);
8229b0040bSGerd Hoffmann }
8329b0040bSGerd Hoffmann 
8429b0040bSGerd Hoffmann struct SpiceWatch {
8529b0040bSGerd Hoffmann     int fd;
8629b0040bSGerd Hoffmann     int event_mask;
8729b0040bSGerd Hoffmann     SpiceWatchFunc func;
8829b0040bSGerd Hoffmann     void *opaque;
8929b0040bSGerd Hoffmann     QTAILQ_ENTRY(SpiceWatch) next;
9029b0040bSGerd Hoffmann };
9129b0040bSGerd Hoffmann static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches);
9229b0040bSGerd Hoffmann 
9329b0040bSGerd Hoffmann static void watch_read(void *opaque)
9429b0040bSGerd Hoffmann {
9529b0040bSGerd Hoffmann     SpiceWatch *watch = opaque;
9629b0040bSGerd Hoffmann     watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque);
9729b0040bSGerd Hoffmann }
9829b0040bSGerd Hoffmann 
9929b0040bSGerd Hoffmann static void watch_write(void *opaque)
10029b0040bSGerd Hoffmann {
10129b0040bSGerd Hoffmann     SpiceWatch *watch = opaque;
10229b0040bSGerd Hoffmann     watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque);
10329b0040bSGerd Hoffmann }
10429b0040bSGerd Hoffmann 
10529b0040bSGerd Hoffmann static void watch_update_mask(SpiceWatch *watch, int event_mask)
10629b0040bSGerd Hoffmann {
10729b0040bSGerd Hoffmann     IOHandler *on_read = NULL;
10829b0040bSGerd Hoffmann     IOHandler *on_write = NULL;
10929b0040bSGerd Hoffmann 
11029b0040bSGerd Hoffmann     watch->event_mask = event_mask;
11129b0040bSGerd Hoffmann     if (watch->event_mask & SPICE_WATCH_EVENT_READ) {
11229b0040bSGerd Hoffmann         on_read = watch_read;
11329b0040bSGerd Hoffmann     }
11429b0040bSGerd Hoffmann     if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
1153d6d306cSHans de Goede         on_write = watch_write;
11629b0040bSGerd Hoffmann     }
11729b0040bSGerd Hoffmann     qemu_set_fd_handler(watch->fd, on_read, on_write, watch);
11829b0040bSGerd Hoffmann }
11929b0040bSGerd Hoffmann 
12029b0040bSGerd Hoffmann static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
12129b0040bSGerd Hoffmann {
12229b0040bSGerd Hoffmann     SpiceWatch *watch;
12329b0040bSGerd Hoffmann 
1247267c094SAnthony Liguori     watch = g_malloc0(sizeof(*watch));
12529b0040bSGerd Hoffmann     watch->fd     = fd;
12629b0040bSGerd Hoffmann     watch->func   = func;
12729b0040bSGerd Hoffmann     watch->opaque = opaque;
12829b0040bSGerd Hoffmann     QTAILQ_INSERT_TAIL(&watches, watch, next);
12929b0040bSGerd Hoffmann 
13029b0040bSGerd Hoffmann     watch_update_mask(watch, event_mask);
13129b0040bSGerd Hoffmann     return watch;
13229b0040bSGerd Hoffmann }
13329b0040bSGerd Hoffmann 
13429b0040bSGerd Hoffmann static void watch_remove(SpiceWatch *watch)
13529b0040bSGerd Hoffmann {
13629b0040bSGerd Hoffmann     watch_update_mask(watch, 0);
13729b0040bSGerd Hoffmann     QTAILQ_REMOVE(&watches, watch, next);
1387267c094SAnthony Liguori     g_free(watch);
13929b0040bSGerd Hoffmann }
14029b0040bSGerd Hoffmann 
1416f8c63fbSGerd Hoffmann #if SPICE_INTERFACE_CORE_MINOR >= 3
1426f8c63fbSGerd Hoffmann 
143cb42a870SGerd Hoffmann typedef struct ChannelList ChannelList;
144cb42a870SGerd Hoffmann struct ChannelList {
145cb42a870SGerd Hoffmann     SpiceChannelEventInfo *info;
146cb42a870SGerd Hoffmann     QTAILQ_ENTRY(ChannelList) link;
147cb42a870SGerd Hoffmann };
148cb42a870SGerd Hoffmann static QTAILQ_HEAD(, ChannelList) channel_list = QTAILQ_HEAD_INITIALIZER(channel_list);
149cb42a870SGerd Hoffmann 
150cb42a870SGerd Hoffmann static void channel_list_add(SpiceChannelEventInfo *info)
151cb42a870SGerd Hoffmann {
152cb42a870SGerd Hoffmann     ChannelList *item;
153cb42a870SGerd Hoffmann 
1547267c094SAnthony Liguori     item = g_malloc0(sizeof(*item));
155cb42a870SGerd Hoffmann     item->info = info;
156cb42a870SGerd Hoffmann     QTAILQ_INSERT_TAIL(&channel_list, item, link);
157cb42a870SGerd Hoffmann }
158cb42a870SGerd Hoffmann 
159cb42a870SGerd Hoffmann static void channel_list_del(SpiceChannelEventInfo *info)
160cb42a870SGerd Hoffmann {
161cb42a870SGerd Hoffmann     ChannelList *item;
162cb42a870SGerd Hoffmann 
163cb42a870SGerd Hoffmann     QTAILQ_FOREACH(item, &channel_list, link) {
164cb42a870SGerd Hoffmann         if (item->info != info) {
165cb42a870SGerd Hoffmann             continue;
166cb42a870SGerd Hoffmann         }
167cb42a870SGerd Hoffmann         QTAILQ_REMOVE(&channel_list, item, link);
1687267c094SAnthony Liguori         g_free(item);
169cb42a870SGerd Hoffmann         return;
170cb42a870SGerd Hoffmann     }
171cb42a870SGerd Hoffmann }
172cb42a870SGerd Hoffmann 
1736f8c63fbSGerd Hoffmann static void add_addr_info(QDict *dict, struct sockaddr *addr, int len)
1746f8c63fbSGerd Hoffmann {
1756f8c63fbSGerd Hoffmann     char host[NI_MAXHOST], port[NI_MAXSERV];
1766f8c63fbSGerd Hoffmann     const char *family;
1776f8c63fbSGerd Hoffmann 
1786f8c63fbSGerd Hoffmann     getnameinfo(addr, len, host, sizeof(host), port, sizeof(port),
1796f8c63fbSGerd Hoffmann                 NI_NUMERICHOST | NI_NUMERICSERV);
1806f8c63fbSGerd Hoffmann     family = inet_strfamily(addr->sa_family);
1816f8c63fbSGerd Hoffmann 
1826f8c63fbSGerd Hoffmann     qdict_put(dict, "host", qstring_from_str(host));
1836f8c63fbSGerd Hoffmann     qdict_put(dict, "port", qstring_from_str(port));
1846f8c63fbSGerd Hoffmann     qdict_put(dict, "family", qstring_from_str(family));
1856f8c63fbSGerd Hoffmann }
1866f8c63fbSGerd Hoffmann 
1876f8c63fbSGerd Hoffmann static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info)
1886f8c63fbSGerd Hoffmann {
1896f8c63fbSGerd Hoffmann     int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
1906f8c63fbSGerd Hoffmann 
1916f8c63fbSGerd Hoffmann     qdict_put(dict, "connection-id", qint_from_int(info->connection_id));
1926f8c63fbSGerd Hoffmann     qdict_put(dict, "channel-type", qint_from_int(info->type));
1936f8c63fbSGerd Hoffmann     qdict_put(dict, "channel-id", qint_from_int(info->id));
1946f8c63fbSGerd Hoffmann     qdict_put(dict, "tls", qbool_from_int(tls));
1956f8c63fbSGerd Hoffmann }
1966f8c63fbSGerd Hoffmann 
197cb42a870SGerd Hoffmann static QList *channel_list_get(void)
198cb42a870SGerd Hoffmann {
199cb42a870SGerd Hoffmann     ChannelList *item;
200cb42a870SGerd Hoffmann     QList *list;
201cb42a870SGerd Hoffmann     QDict *dict;
202cb42a870SGerd Hoffmann 
203cb42a870SGerd Hoffmann     list = qlist_new();
204cb42a870SGerd Hoffmann     QTAILQ_FOREACH(item, &channel_list, link) {
205cb42a870SGerd Hoffmann         dict = qdict_new();
206cb42a870SGerd Hoffmann         add_addr_info(dict, &item->info->paddr, item->info->plen);
207cb42a870SGerd Hoffmann         add_channel_info(dict, item->info);
208cb42a870SGerd Hoffmann         qlist_append(list, dict);
209cb42a870SGerd Hoffmann     }
210cb42a870SGerd Hoffmann     return list;
211cb42a870SGerd Hoffmann }
212cb42a870SGerd Hoffmann 
2136f8c63fbSGerd Hoffmann static void channel_event(int event, SpiceChannelEventInfo *info)
2146f8c63fbSGerd Hoffmann {
2156f8c63fbSGerd Hoffmann     static const int qevent[] = {
2166f8c63fbSGerd Hoffmann         [ SPICE_CHANNEL_EVENT_CONNECTED    ] = QEVENT_SPICE_CONNECTED,
2176f8c63fbSGerd Hoffmann         [ SPICE_CHANNEL_EVENT_INITIALIZED  ] = QEVENT_SPICE_INITIALIZED,
2186f8c63fbSGerd Hoffmann         [ SPICE_CHANNEL_EVENT_DISCONNECTED ] = QEVENT_SPICE_DISCONNECTED,
2196f8c63fbSGerd Hoffmann     };
2206f8c63fbSGerd Hoffmann     QDict *server, *client;
2216f8c63fbSGerd Hoffmann     QObject *data;
2226f8c63fbSGerd Hoffmann 
22322b626e2SGerd Hoffmann     /*
22422b626e2SGerd Hoffmann      * Spice server might have called us from spice worker thread
22522b626e2SGerd Hoffmann      * context (happens on display channel disconnects).  Spice should
22622b626e2SGerd Hoffmann      * not do that.  It isn't that easy to fix it in spice and even
22722b626e2SGerd Hoffmann      * when it is fixed we still should cover the already released
22822b626e2SGerd Hoffmann      * spice versions.  So detect that we've been called from another
22922b626e2SGerd Hoffmann      * thread and grab the iothread lock if so before calling qemu
23022b626e2SGerd Hoffmann      * functions.
23122b626e2SGerd Hoffmann      */
23222b626e2SGerd Hoffmann     bool need_lock = !pthread_equal(me, pthread_self());
23322b626e2SGerd Hoffmann     if (need_lock) {
23422b626e2SGerd Hoffmann         qemu_mutex_lock_iothread();
23522b626e2SGerd Hoffmann     }
23622b626e2SGerd Hoffmann 
2376f8c63fbSGerd Hoffmann     client = qdict_new();
2386f8c63fbSGerd Hoffmann     add_addr_info(client, &info->paddr, info->plen);
2396f8c63fbSGerd Hoffmann 
2406f8c63fbSGerd Hoffmann     server = qdict_new();
2416f8c63fbSGerd Hoffmann     add_addr_info(server, &info->laddr, info->llen);
2426f8c63fbSGerd Hoffmann 
2436f8c63fbSGerd Hoffmann     if (event == SPICE_CHANNEL_EVENT_INITIALIZED) {
2446f8c63fbSGerd Hoffmann         qdict_put(server, "auth", qstring_from_str(auth));
2456f8c63fbSGerd Hoffmann         add_channel_info(client, info);
246cb42a870SGerd Hoffmann         channel_list_add(info);
247cb42a870SGerd Hoffmann     }
248cb42a870SGerd Hoffmann     if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) {
249cb42a870SGerd Hoffmann         channel_list_del(info);
2506f8c63fbSGerd Hoffmann     }
2516f8c63fbSGerd Hoffmann 
2526f8c63fbSGerd Hoffmann     data = qobject_from_jsonf("{ 'client': %p, 'server': %p }",
2536f8c63fbSGerd Hoffmann                               QOBJECT(client), QOBJECT(server));
2546f8c63fbSGerd Hoffmann     monitor_protocol_event(qevent[event], data);
2556f8c63fbSGerd Hoffmann     qobject_decref(data);
25622b626e2SGerd Hoffmann 
25722b626e2SGerd Hoffmann     if (need_lock) {
25822b626e2SGerd Hoffmann         qemu_mutex_unlock_iothread();
25922b626e2SGerd Hoffmann     }
2606f8c63fbSGerd Hoffmann }
2616f8c63fbSGerd Hoffmann 
2626f8c63fbSGerd Hoffmann #else /* SPICE_INTERFACE_CORE_MINOR >= 3 */
2636f8c63fbSGerd Hoffmann 
2646f8c63fbSGerd Hoffmann static QList *channel_list_get(void)
2656f8c63fbSGerd Hoffmann {
2666f8c63fbSGerd Hoffmann     return NULL;
2676f8c63fbSGerd Hoffmann }
2686f8c63fbSGerd Hoffmann 
2696f8c63fbSGerd Hoffmann #endif /* SPICE_INTERFACE_CORE_MINOR >= 3 */
2706f8c63fbSGerd Hoffmann 
27129b0040bSGerd Hoffmann static SpiceCoreInterface core_interface = {
27229b0040bSGerd Hoffmann     .base.type          = SPICE_INTERFACE_CORE,
27329b0040bSGerd Hoffmann     .base.description   = "qemu core services",
27429b0040bSGerd Hoffmann     .base.major_version = SPICE_INTERFACE_CORE_MAJOR,
27529b0040bSGerd Hoffmann     .base.minor_version = SPICE_INTERFACE_CORE_MINOR,
27629b0040bSGerd Hoffmann 
27729b0040bSGerd Hoffmann     .timer_add          = timer_add,
27829b0040bSGerd Hoffmann     .timer_start        = timer_start,
27929b0040bSGerd Hoffmann     .timer_cancel       = timer_cancel,
28029b0040bSGerd Hoffmann     .timer_remove       = timer_remove,
28129b0040bSGerd Hoffmann 
28229b0040bSGerd Hoffmann     .watch_add          = watch_add,
28329b0040bSGerd Hoffmann     .watch_update_mask  = watch_update_mask,
28429b0040bSGerd Hoffmann     .watch_remove       = watch_remove,
2856f8c63fbSGerd Hoffmann 
2866f8c63fbSGerd Hoffmann #if SPICE_INTERFACE_CORE_MINOR >= 3
2876f8c63fbSGerd Hoffmann     .channel_event      = channel_event,
2886f8c63fbSGerd Hoffmann #endif
28929b0040bSGerd Hoffmann };
29029b0040bSGerd Hoffmann 
291*026f773fSYonit Halperin #ifdef SPICE_INTERFACE_MIGRATION
292*026f773fSYonit Halperin typedef struct SpiceMigration {
293*026f773fSYonit Halperin     SpiceMigrateInstance sin;
294*026f773fSYonit Halperin     struct {
295*026f773fSYonit Halperin         MonitorCompletion *cb;
296*026f773fSYonit Halperin         void *opaque;
297*026f773fSYonit Halperin     } connect_complete;
298*026f773fSYonit Halperin } SpiceMigration;
299*026f773fSYonit Halperin 
300*026f773fSYonit Halperin static void migrate_connect_complete_cb(SpiceMigrateInstance *sin);
301*026f773fSYonit Halperin 
302*026f773fSYonit Halperin static const SpiceMigrateInterface migrate_interface = {
303*026f773fSYonit Halperin     .base.type = SPICE_INTERFACE_MIGRATION,
304*026f773fSYonit Halperin     .base.description = "migration",
305*026f773fSYonit Halperin     .base.major_version = SPICE_INTERFACE_MIGRATION_MAJOR,
306*026f773fSYonit Halperin     .base.minor_version = SPICE_INTERFACE_MIGRATION_MINOR,
307*026f773fSYonit Halperin     .migrate_connect_complete = migrate_connect_complete_cb,
308*026f773fSYonit Halperin     .migrate_end_complete = NULL,
309*026f773fSYonit Halperin };
310*026f773fSYonit Halperin 
311*026f773fSYonit Halperin static SpiceMigration spice_migrate;
312*026f773fSYonit Halperin 
313*026f773fSYonit Halperin static void migrate_connect_complete_cb(SpiceMigrateInstance *sin)
314*026f773fSYonit Halperin {
315*026f773fSYonit Halperin     SpiceMigration *sm = container_of(sin, SpiceMigration, sin);
316*026f773fSYonit Halperin     if (sm->connect_complete.cb) {
317*026f773fSYonit Halperin         sm->connect_complete.cb(sm->connect_complete.opaque, NULL);
318*026f773fSYonit Halperin     }
319*026f773fSYonit Halperin     sm->connect_complete.cb = NULL;
320*026f773fSYonit Halperin }
321*026f773fSYonit Halperin #endif
322*026f773fSYonit Halperin 
3239f04e09eSYonit Halperin /* config string parsing */
3249f04e09eSYonit Halperin 
3259f04e09eSYonit Halperin static int name2enum(const char *string, const char *table[], int entries)
3269f04e09eSYonit Halperin {
3279f04e09eSYonit Halperin     int i;
3289f04e09eSYonit Halperin 
3299f04e09eSYonit Halperin     if (string) {
3309f04e09eSYonit Halperin         for (i = 0; i < entries; i++) {
3319f04e09eSYonit Halperin             if (!table[i]) {
3329f04e09eSYonit Halperin                 continue;
3339f04e09eSYonit Halperin             }
3349f04e09eSYonit Halperin             if (strcmp(string, table[i]) != 0) {
3359f04e09eSYonit Halperin                 continue;
3369f04e09eSYonit Halperin             }
3379f04e09eSYonit Halperin             return i;
3389f04e09eSYonit Halperin         }
3399f04e09eSYonit Halperin     }
3409f04e09eSYonit Halperin     return -1;
3419f04e09eSYonit Halperin }
3429f04e09eSYonit Halperin 
3439f04e09eSYonit Halperin static int parse_name(const char *string, const char *optname,
3449f04e09eSYonit Halperin                       const char *table[], int entries)
3459f04e09eSYonit Halperin {
3469f04e09eSYonit Halperin     int value = name2enum(string, table, entries);
3479f04e09eSYonit Halperin 
3489f04e09eSYonit Halperin     if (value != -1) {
3499f04e09eSYonit Halperin         return value;
3509f04e09eSYonit Halperin     }
3519f04e09eSYonit Halperin     fprintf(stderr, "spice: invalid %s: %s\n", optname, string);
3529f04e09eSYonit Halperin     exit(1);
3539f04e09eSYonit Halperin }
3549f04e09eSYonit Halperin 
35584a23f25SGerd Hoffmann static const char *stream_video_names[] = {
35684a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_OFF ]    = "off",
35784a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_ALL ]    = "all",
35884a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_FILTER ] = "filter",
35984a23f25SGerd Hoffmann };
36084a23f25SGerd Hoffmann #define parse_stream_video(_name) \
36184a23f25SGerd Hoffmann     name2enum(_name, stream_video_names, ARRAY_SIZE(stream_video_names))
36284a23f25SGerd Hoffmann 
3639f04e09eSYonit Halperin static const char *compression_names[] = {
3649f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_OFF ]      = "off",
3659f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz",
3669f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_AUTO_LZ ]  = "auto_lz",
3679f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_QUIC ]     = "quic",
3689f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_GLZ ]      = "glz",
3699f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_LZ ]       = "lz",
3709f04e09eSYonit Halperin };
3719f04e09eSYonit Halperin #define parse_compression(_name)                                        \
3729f04e09eSYonit Halperin     parse_name(_name, "image compression",                              \
3739f04e09eSYonit Halperin                compression_names, ARRAY_SIZE(compression_names))
3749f04e09eSYonit Halperin 
3759f04e09eSYonit Halperin static const char *wan_compression_names[] = {
3769f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_AUTO   ] = "auto",
3779f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_NEVER  ] = "never",
3789f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always",
3799f04e09eSYonit Halperin };
3809f04e09eSYonit Halperin #define parse_wan_compression(_name)                                    \
3819f04e09eSYonit Halperin     parse_name(_name, "wan compression",                                \
3829f04e09eSYonit Halperin                wan_compression_names, ARRAY_SIZE(wan_compression_names))
3839f04e09eSYonit Halperin 
38429b0040bSGerd Hoffmann /* functions for the rest of qemu */
38529b0040bSGerd Hoffmann 
386cb42a870SGerd Hoffmann static void info_spice_iter(QObject *obj, void *opaque)
387cb42a870SGerd Hoffmann {
388cb42a870SGerd Hoffmann     QDict *client;
389cb42a870SGerd Hoffmann     Monitor *mon = opaque;
390cb42a870SGerd Hoffmann 
391cb42a870SGerd Hoffmann     client = qobject_to_qdict(obj);
392cb42a870SGerd Hoffmann     monitor_printf(mon, "Channel:\n");
393cb42a870SGerd Hoffmann     monitor_printf(mon, "     address: %s:%s%s\n",
394cb42a870SGerd Hoffmann                    qdict_get_str(client, "host"),
395cb42a870SGerd Hoffmann                    qdict_get_str(client, "port"),
396cb42a870SGerd Hoffmann                    qdict_get_bool(client, "tls") ? " [tls]" : "");
397cb42a870SGerd Hoffmann     monitor_printf(mon, "     session: %" PRId64 "\n",
398cb42a870SGerd Hoffmann                    qdict_get_int(client, "connection-id"));
399cb42a870SGerd Hoffmann     monitor_printf(mon, "     channel: %d:%d\n",
400cb42a870SGerd Hoffmann                    (int)qdict_get_int(client, "channel-type"),
401cb42a870SGerd Hoffmann                    (int)qdict_get_int(client, "channel-id"));
402cb42a870SGerd Hoffmann }
403cb42a870SGerd Hoffmann 
404cb42a870SGerd Hoffmann void do_info_spice_print(Monitor *mon, const QObject *data)
405cb42a870SGerd Hoffmann {
406cb42a870SGerd Hoffmann     QDict *server;
407cb42a870SGerd Hoffmann     QList *channels;
408cb42a870SGerd Hoffmann     const char *host;
409cb42a870SGerd Hoffmann     int port;
410cb42a870SGerd Hoffmann 
411cb42a870SGerd Hoffmann     server = qobject_to_qdict(data);
412cb42a870SGerd Hoffmann     if (qdict_get_bool(server, "enabled") == 0) {
413cb42a870SGerd Hoffmann         monitor_printf(mon, "Server: disabled\n");
414cb42a870SGerd Hoffmann         return;
415cb42a870SGerd Hoffmann     }
416cb42a870SGerd Hoffmann 
417cb42a870SGerd Hoffmann     monitor_printf(mon, "Server:\n");
418cb42a870SGerd Hoffmann     host = qdict_get_str(server, "host");
419cb42a870SGerd Hoffmann     port = qdict_get_try_int(server, "port", -1);
420cb42a870SGerd Hoffmann     if (port != -1) {
421cb42a870SGerd Hoffmann         monitor_printf(mon, "     address: %s:%d\n", host, port);
422cb42a870SGerd Hoffmann     }
423cb42a870SGerd Hoffmann     port = qdict_get_try_int(server, "tls-port", -1);
424cb42a870SGerd Hoffmann     if (port != -1) {
425cb42a870SGerd Hoffmann         monitor_printf(mon, "     address: %s:%d [tls]\n", host, port);
426cb42a870SGerd Hoffmann     }
427cb42a870SGerd Hoffmann     monitor_printf(mon, "        auth: %s\n", qdict_get_str(server, "auth"));
4288df0c7e8SAlon Levy     monitor_printf(mon, "    compiled: %s\n",
4298df0c7e8SAlon Levy                    qdict_get_str(server, "compiled-version"));
430cb42a870SGerd Hoffmann 
431cb42a870SGerd Hoffmann     channels = qdict_get_qlist(server, "channels");
432cb42a870SGerd Hoffmann     if (qlist_empty(channels)) {
433cb42a870SGerd Hoffmann         monitor_printf(mon, "Channels: none\n");
434cb42a870SGerd Hoffmann     } else {
435cb42a870SGerd Hoffmann         qlist_iter(channels, info_spice_iter, mon);
436cb42a870SGerd Hoffmann     }
437cb42a870SGerd Hoffmann }
438cb42a870SGerd Hoffmann 
439cb42a870SGerd Hoffmann void do_info_spice(Monitor *mon, QObject **ret_data)
440cb42a870SGerd Hoffmann {
441cb42a870SGerd Hoffmann     QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
442cb42a870SGerd Hoffmann     QDict *server;
443cb42a870SGerd Hoffmann     QList *clist;
444cb42a870SGerd Hoffmann     const char *addr;
445cb42a870SGerd Hoffmann     int port, tls_port;
4468df0c7e8SAlon Levy     char version_string[20]; /* 12 = |255.255.255\0| is the max */
447cb42a870SGerd Hoffmann 
448cb42a870SGerd Hoffmann     if (!spice_server) {
449cb42a870SGerd Hoffmann         *ret_data = qobject_from_jsonf("{ 'enabled': false }");
450cb42a870SGerd Hoffmann         return;
451cb42a870SGerd Hoffmann     }
452cb42a870SGerd Hoffmann 
453cb42a870SGerd Hoffmann     addr = qemu_opt_get(opts, "addr");
454cb42a870SGerd Hoffmann     port = qemu_opt_get_number(opts, "port", 0);
455cb42a870SGerd Hoffmann     tls_port = qemu_opt_get_number(opts, "tls-port", 0);
456cb42a870SGerd Hoffmann     clist = channel_list_get();
457cb42a870SGerd Hoffmann 
458cb42a870SGerd Hoffmann     server = qdict_new();
459cb42a870SGerd Hoffmann     qdict_put(server, "enabled", qbool_from_int(true));
460cb42a870SGerd Hoffmann     qdict_put(server, "auth", qstring_from_str(auth));
461cb42a870SGerd Hoffmann     qdict_put(server, "host", qstring_from_str(addr ? addr : "0.0.0.0"));
4628df0c7e8SAlon Levy     snprintf(version_string, sizeof(version_string), "%d.%d.%d",
4638df0c7e8SAlon Levy              (SPICE_SERVER_VERSION & 0xff0000) >> 16,
4648df0c7e8SAlon Levy              (SPICE_SERVER_VERSION & 0xff00) >> 8,
4658df0c7e8SAlon Levy              SPICE_SERVER_VERSION & 0xff);
4668df0c7e8SAlon Levy     qdict_put(server, "compiled-version", qstring_from_str(version_string));
467cb42a870SGerd Hoffmann     if (port) {
468cb42a870SGerd Hoffmann         qdict_put(server, "port", qint_from_int(port));
469cb42a870SGerd Hoffmann     }
470cb42a870SGerd Hoffmann     if (tls_port) {
471cb42a870SGerd Hoffmann         qdict_put(server, "tls-port", qint_from_int(tls_port));
472cb42a870SGerd Hoffmann     }
473cb42a870SGerd Hoffmann     if (clist) {
474cb42a870SGerd Hoffmann         qdict_put(server, "channels", clist);
475cb42a870SGerd Hoffmann     }
476cb42a870SGerd Hoffmann 
477cb42a870SGerd Hoffmann     *ret_data = QOBJECT(server);
478cb42a870SGerd Hoffmann }
479cb42a870SGerd Hoffmann 
4809e8dd451SJan Kiszka static void migration_state_notifier(Notifier *notifier, void *data)
481e866e239SGerd Hoffmann {
4827073693bSJuan Quintela     MigrationState *s = data;
483e866e239SGerd Hoffmann 
484*026f773fSYonit Halperin     if (migration_is_active(s)) {
485*026f773fSYonit Halperin #ifdef SPICE_INTERFACE_MIGRATION
486*026f773fSYonit Halperin         spice_server_migrate_start(spice_server);
487*026f773fSYonit Halperin #endif
488*026f773fSYonit Halperin     } else if (migration_has_finished(s)) {
489e866e239SGerd Hoffmann #if SPICE_SERVER_VERSION >= 0x000701 /* 0.7.1 */
490*026f773fSYonit Halperin #ifndef SPICE_INTERFACE_MIGRATION
491e866e239SGerd Hoffmann         spice_server_migrate_switch(spice_server);
492*026f773fSYonit Halperin #else
493*026f773fSYonit Halperin         spice_server_migrate_end(spice_server, true);
494*026f773fSYonit Halperin     } else if (migration_has_failed(s)) {
495*026f773fSYonit Halperin         spice_server_migrate_end(spice_server, false);
496*026f773fSYonit Halperin #endif
497e866e239SGerd Hoffmann #endif
498e866e239SGerd Hoffmann     }
499e866e239SGerd Hoffmann }
500e866e239SGerd Hoffmann 
501e866e239SGerd Hoffmann int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
502edc5cb1aSYonit Halperin                             const char *subject,
503edc5cb1aSYonit Halperin                             MonitorCompletion *cb, void *opaque)
504e866e239SGerd Hoffmann {
505edc5cb1aSYonit Halperin     int ret;
506*026f773fSYonit Halperin #ifdef SPICE_INTERFACE_MIGRATION
507*026f773fSYonit Halperin     spice_migrate.connect_complete.cb = cb;
508*026f773fSYonit Halperin     spice_migrate.connect_complete.opaque = opaque;
509*026f773fSYonit Halperin     ret = spice_server_migrate_connect(spice_server, hostname,
510*026f773fSYonit Halperin                                        port, tls_port, subject);
511*026f773fSYonit Halperin #else
512edc5cb1aSYonit Halperin     ret = spice_server_migrate_info(spice_server, hostname,
513e866e239SGerd Hoffmann                                     port, tls_port, subject);
514edc5cb1aSYonit Halperin     cb(opaque, NULL);
515*026f773fSYonit Halperin #endif
516edc5cb1aSYonit Halperin     return ret;
517e866e239SGerd Hoffmann }
518e866e239SGerd Hoffmann 
51917b6dea0SGerd Hoffmann static int add_channel(const char *name, const char *value, void *opaque)
52017b6dea0SGerd Hoffmann {
52117b6dea0SGerd Hoffmann     int security = 0;
52217b6dea0SGerd Hoffmann     int rc;
52317b6dea0SGerd Hoffmann 
52417b6dea0SGerd Hoffmann     if (strcmp(name, "tls-channel") == 0) {
52517b6dea0SGerd Hoffmann         security = SPICE_CHANNEL_SECURITY_SSL;
52617b6dea0SGerd Hoffmann     }
52717b6dea0SGerd Hoffmann     if (strcmp(name, "plaintext-channel") == 0) {
52817b6dea0SGerd Hoffmann         security = SPICE_CHANNEL_SECURITY_NONE;
52917b6dea0SGerd Hoffmann     }
53017b6dea0SGerd Hoffmann     if (security == 0) {
53117b6dea0SGerd Hoffmann         return 0;
53217b6dea0SGerd Hoffmann     }
53317b6dea0SGerd Hoffmann     if (strcmp(value, "default") == 0) {
53417b6dea0SGerd Hoffmann         rc = spice_server_set_channel_security(spice_server, NULL, security);
53517b6dea0SGerd Hoffmann     } else {
53617b6dea0SGerd Hoffmann         rc = spice_server_set_channel_security(spice_server, value, security);
53717b6dea0SGerd Hoffmann     }
53817b6dea0SGerd Hoffmann     if (rc != 0) {
53917b6dea0SGerd Hoffmann         fprintf(stderr, "spice: failed to set channel security for %s\n", value);
54017b6dea0SGerd Hoffmann         exit(1);
54117b6dea0SGerd Hoffmann     }
54217b6dea0SGerd Hoffmann     return 0;
54317b6dea0SGerd Hoffmann }
54417b6dea0SGerd Hoffmann 
54529b0040bSGerd Hoffmann void qemu_spice_init(void)
54629b0040bSGerd Hoffmann {
54729b0040bSGerd Hoffmann     QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
548333b0eebSGerd Hoffmann     const char *password, *str, *x509_dir, *addr,
549c448e855SGerd Hoffmann         *x509_key_password = NULL,
550c448e855SGerd Hoffmann         *x509_dh_file = NULL,
551c448e855SGerd Hoffmann         *tls_ciphers = NULL;
552c448e855SGerd Hoffmann     char *x509_key_file = NULL,
553c448e855SGerd Hoffmann         *x509_cert_file = NULL,
554c448e855SGerd Hoffmann         *x509_cacert_file = NULL;
555f61d6960SGerd Hoffmann     int port, tls_port, len, addr_flags;
5569f04e09eSYonit Halperin     spice_image_compression_t compression;
5579f04e09eSYonit Halperin     spice_wan_compression_t wan_compr;
55829b0040bSGerd Hoffmann 
55922b626e2SGerd Hoffmann     me = pthread_self();
56022b626e2SGerd Hoffmann 
56129b0040bSGerd Hoffmann    if (!opts) {
56229b0040bSGerd Hoffmann         return;
56329b0040bSGerd Hoffmann     }
56429b0040bSGerd Hoffmann     port = qemu_opt_get_number(opts, "port", 0);
565c448e855SGerd Hoffmann     tls_port = qemu_opt_get_number(opts, "tls-port", 0);
566c448e855SGerd Hoffmann     if (!port && !tls_port) {
567df9cb669SGerd Hoffmann         fprintf(stderr, "neither port nor tls-port specified for spice.");
568df9cb669SGerd Hoffmann         exit(1);
569df9cb669SGerd Hoffmann     }
570df9cb669SGerd Hoffmann     if (port < 0 || port > 65535) {
571df9cb669SGerd Hoffmann         fprintf(stderr, "spice port is out of range");
572df9cb669SGerd Hoffmann         exit(1);
573df9cb669SGerd Hoffmann     }
574df9cb669SGerd Hoffmann     if (tls_port < 0 || tls_port > 65535) {
575df9cb669SGerd Hoffmann         fprintf(stderr, "spice tls-port is out of range");
576df9cb669SGerd Hoffmann         exit(1);
57729b0040bSGerd Hoffmann     }
57829b0040bSGerd Hoffmann     password = qemu_opt_get(opts, "password");
57929b0040bSGerd Hoffmann 
580c448e855SGerd Hoffmann     if (tls_port) {
581c448e855SGerd Hoffmann         x509_dir = qemu_opt_get(opts, "x509-dir");
582c448e855SGerd Hoffmann         if (NULL == x509_dir) {
583c448e855SGerd Hoffmann             x509_dir = ".";
584c448e855SGerd Hoffmann         }
585c448e855SGerd Hoffmann         len = strlen(x509_dir) + 32;
586c448e855SGerd Hoffmann 
587c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-key-file");
588c448e855SGerd Hoffmann         if (str) {
5897267c094SAnthony Liguori             x509_key_file = g_strdup(str);
590c448e855SGerd Hoffmann         } else {
5917267c094SAnthony Liguori             x509_key_file = g_malloc(len);
592c448e855SGerd Hoffmann             snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE);
593c448e855SGerd Hoffmann         }
594c448e855SGerd Hoffmann 
595c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-cert-file");
596c448e855SGerd Hoffmann         if (str) {
5977267c094SAnthony Liguori             x509_cert_file = g_strdup(str);
598c448e855SGerd Hoffmann         } else {
5997267c094SAnthony Liguori             x509_cert_file = g_malloc(len);
600c448e855SGerd Hoffmann             snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE);
601c448e855SGerd Hoffmann         }
602c448e855SGerd Hoffmann 
603c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-cacert-file");
604c448e855SGerd Hoffmann         if (str) {
6057267c094SAnthony Liguori             x509_cacert_file = g_strdup(str);
606c448e855SGerd Hoffmann         } else {
6077267c094SAnthony Liguori             x509_cacert_file = g_malloc(len);
608c448e855SGerd Hoffmann             snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE);
609c448e855SGerd Hoffmann         }
610c448e855SGerd Hoffmann 
611c448e855SGerd Hoffmann         x509_key_password = qemu_opt_get(opts, "x509-key-password");
612c448e855SGerd Hoffmann         x509_dh_file = qemu_opt_get(opts, "x509-dh-file");
613c448e855SGerd Hoffmann         tls_ciphers = qemu_opt_get(opts, "tls-ciphers");
614c448e855SGerd Hoffmann     }
615c448e855SGerd Hoffmann 
616333b0eebSGerd Hoffmann     addr = qemu_opt_get(opts, "addr");
617333b0eebSGerd Hoffmann     addr_flags = 0;
618333b0eebSGerd Hoffmann     if (qemu_opt_get_bool(opts, "ipv4", 0)) {
619333b0eebSGerd Hoffmann         addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY;
620333b0eebSGerd Hoffmann     } else if (qemu_opt_get_bool(opts, "ipv6", 0)) {
621333b0eebSGerd Hoffmann         addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY;
622333b0eebSGerd Hoffmann     }
623333b0eebSGerd Hoffmann 
62429b0040bSGerd Hoffmann     spice_server = spice_server_new();
625333b0eebSGerd Hoffmann     spice_server_set_addr(spice_server, addr ? addr : "", addr_flags);
626c448e855SGerd Hoffmann     if (port) {
62729b0040bSGerd Hoffmann         spice_server_set_port(spice_server, port);
628c448e855SGerd Hoffmann     }
629c448e855SGerd Hoffmann     if (tls_port) {
630c448e855SGerd Hoffmann         spice_server_set_tls(spice_server, tls_port,
631c448e855SGerd Hoffmann                              x509_cacert_file,
632c448e855SGerd Hoffmann                              x509_cert_file,
633c448e855SGerd Hoffmann                              x509_key_file,
634c448e855SGerd Hoffmann                              x509_key_password,
635c448e855SGerd Hoffmann                              x509_dh_file,
636c448e855SGerd Hoffmann                              tls_ciphers);
637c448e855SGerd Hoffmann     }
63829b0040bSGerd Hoffmann     if (password) {
63929b0040bSGerd Hoffmann         spice_server_set_ticket(spice_server, password, 0, 0, 0);
64029b0040bSGerd Hoffmann     }
64148b3ed0aSMarc-André Lureau     if (qemu_opt_get_bool(opts, "sasl", 0)) {
64248b3ed0aSMarc-André Lureau #if SPICE_SERVER_VERSION >= 0x000900 /* 0.9.0 */
64348b3ed0aSMarc-André Lureau         if (spice_server_set_sasl_appname(spice_server, "qemu") == -1 ||
64448b3ed0aSMarc-André Lureau             spice_server_set_sasl(spice_server, 1) == -1) {
64548b3ed0aSMarc-André Lureau             fprintf(stderr, "spice: failed to enable sasl\n");
64648b3ed0aSMarc-André Lureau             exit(1);
64748b3ed0aSMarc-André Lureau         }
64848b3ed0aSMarc-André Lureau #else
64948b3ed0aSMarc-André Lureau         fprintf(stderr, "spice: sasl is not available (spice >= 0.9 required)\n");
65048b3ed0aSMarc-André Lureau         exit(1);
65148b3ed0aSMarc-André Lureau #endif
65248b3ed0aSMarc-André Lureau     }
65329b0040bSGerd Hoffmann     if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) {
6546f8c63fbSGerd Hoffmann         auth = "none";
65529b0040bSGerd Hoffmann         spice_server_set_noauth(spice_server);
65629b0040bSGerd Hoffmann     }
65729b0040bSGerd Hoffmann 
658d4970b07SHans de Goede #if SPICE_SERVER_VERSION >= 0x000801
659d4970b07SHans de Goede     if (qemu_opt_get_bool(opts, "disable-copy-paste", 0)) {
660d4970b07SHans de Goede         spice_server_set_agent_copypaste(spice_server, false);
661d4970b07SHans de Goede     }
662d4970b07SHans de Goede #endif
663d4970b07SHans de Goede 
6649f04e09eSYonit Halperin     compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ;
6659f04e09eSYonit Halperin     str = qemu_opt_get(opts, "image-compression");
6669f04e09eSYonit Halperin     if (str) {
6679f04e09eSYonit Halperin         compression = parse_compression(str);
6689f04e09eSYonit Halperin     }
6699f04e09eSYonit Halperin     spice_server_set_image_compression(spice_server, compression);
6709f04e09eSYonit Halperin 
6719f04e09eSYonit Halperin     wan_compr = SPICE_WAN_COMPRESSION_AUTO;
6729f04e09eSYonit Halperin     str = qemu_opt_get(opts, "jpeg-wan-compression");
6739f04e09eSYonit Halperin     if (str) {
6749f04e09eSYonit Halperin         wan_compr = parse_wan_compression(str);
6759f04e09eSYonit Halperin     }
6769f04e09eSYonit Halperin     spice_server_set_jpeg_compression(spice_server, wan_compr);
6779f04e09eSYonit Halperin 
6789f04e09eSYonit Halperin     wan_compr = SPICE_WAN_COMPRESSION_AUTO;
6799f04e09eSYonit Halperin     str = qemu_opt_get(opts, "zlib-glz-wan-compression");
6809f04e09eSYonit Halperin     if (str) {
6819f04e09eSYonit Halperin         wan_compr = parse_wan_compression(str);
6829f04e09eSYonit Halperin     }
6839f04e09eSYonit Halperin     spice_server_set_zlib_glz_compression(spice_server, wan_compr);
68429b0040bSGerd Hoffmann 
68584a23f25SGerd Hoffmann     str = qemu_opt_get(opts, "streaming-video");
68684a23f25SGerd Hoffmann     if (str) {
687f61d6960SGerd Hoffmann         int streaming_video = parse_stream_video(str);
68884a23f25SGerd Hoffmann         spice_server_set_streaming_video(spice_server, streaming_video);
68984a23f25SGerd Hoffmann     }
69084a23f25SGerd Hoffmann 
69184a23f25SGerd Hoffmann     spice_server_set_agent_mouse
69284a23f25SGerd Hoffmann         (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1));
69384a23f25SGerd Hoffmann     spice_server_set_playback_compression
69484a23f25SGerd Hoffmann         (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1));
69584a23f25SGerd Hoffmann 
69617b6dea0SGerd Hoffmann     qemu_opt_foreach(opts, add_channel, NULL, 0);
69717b6dea0SGerd Hoffmann 
698fba810f1SGerd Hoffmann     if (0 != spice_server_init(spice_server, &core_interface)) {
699fba810f1SGerd Hoffmann         fprintf(stderr, "failed to initialize spice server");
700fba810f1SGerd Hoffmann         exit(1);
701fba810f1SGerd Hoffmann     };
70229b0040bSGerd Hoffmann     using_spice = 1;
703864401c2SGerd Hoffmann 
704e866e239SGerd Hoffmann     migration_state.notify = migration_state_notifier;
705e866e239SGerd Hoffmann     add_migration_state_change_notifier(&migration_state);
706*026f773fSYonit Halperin #ifdef SPICE_INTERFACE_MIGRATION
707*026f773fSYonit Halperin     spice_migrate.sin.base.sif = &migrate_interface.base;
708*026f773fSYonit Halperin     spice_migrate.connect_complete.cb = NULL;
709*026f773fSYonit Halperin     qemu_spice_add_interface(&spice_migrate.sin.base);
710*026f773fSYonit Halperin #endif
711e866e239SGerd Hoffmann 
712864401c2SGerd Hoffmann     qemu_spice_input_init();
7133e313753SGerd Hoffmann     qemu_spice_audio_init();
714c448e855SGerd Hoffmann 
7157267c094SAnthony Liguori     g_free(x509_key_file);
7167267c094SAnthony Liguori     g_free(x509_cert_file);
7177267c094SAnthony Liguori     g_free(x509_cacert_file);
71829b0040bSGerd Hoffmann }
71929b0040bSGerd Hoffmann 
72029b0040bSGerd Hoffmann int qemu_spice_add_interface(SpiceBaseInstance *sin)
72129b0040bSGerd Hoffmann {
722a19cbfb3SGerd Hoffmann     if (!spice_server) {
723a19cbfb3SGerd Hoffmann         if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) {
724a19cbfb3SGerd Hoffmann             fprintf(stderr, "Oops: spice configured but not active\n");
725a19cbfb3SGerd Hoffmann             exit(1);
726a19cbfb3SGerd Hoffmann         }
727a19cbfb3SGerd Hoffmann         /*
728a19cbfb3SGerd Hoffmann          * Create a spice server instance.
729a19cbfb3SGerd Hoffmann          * It does *not* listen on the network.
730a19cbfb3SGerd Hoffmann          * It handles QXL local rendering only.
731a19cbfb3SGerd Hoffmann          *
732a19cbfb3SGerd Hoffmann          * With a command line like '-vnc :0 -vga qxl' you'll end up here.
733a19cbfb3SGerd Hoffmann          */
734a19cbfb3SGerd Hoffmann         spice_server = spice_server_new();
735a19cbfb3SGerd Hoffmann         spice_server_init(spice_server, &core_interface);
736a19cbfb3SGerd Hoffmann     }
73729b0040bSGerd Hoffmann     return spice_server_add_interface(spice_server, sin);
73829b0040bSGerd Hoffmann }
73929b0040bSGerd Hoffmann 
7407572150cSGerd Hoffmann static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn)
7417572150cSGerd Hoffmann {
7427572150cSGerd Hoffmann     time_t lifetime, now = time(NULL);
7437572150cSGerd Hoffmann     char *passwd;
7447572150cSGerd Hoffmann 
7457572150cSGerd Hoffmann     if (now < auth_expires) {
7467572150cSGerd Hoffmann         passwd = auth_passwd;
7477572150cSGerd Hoffmann         lifetime = (auth_expires - now);
7487572150cSGerd Hoffmann         if (lifetime > INT_MAX) {
7497572150cSGerd Hoffmann             lifetime = INT_MAX;
7507572150cSGerd Hoffmann         }
7517572150cSGerd Hoffmann     } else {
7527572150cSGerd Hoffmann         passwd = NULL;
7537572150cSGerd Hoffmann         lifetime = 1;
7547572150cSGerd Hoffmann     }
7557572150cSGerd Hoffmann     return spice_server_set_ticket(spice_server, passwd, lifetime,
7567572150cSGerd Hoffmann                                    fail_if_conn, disconnect_if_conn);
7577572150cSGerd Hoffmann }
7587572150cSGerd Hoffmann 
7597572150cSGerd Hoffmann int qemu_spice_set_passwd(const char *passwd,
7607572150cSGerd Hoffmann                           bool fail_if_conn, bool disconnect_if_conn)
7617572150cSGerd Hoffmann {
7627572150cSGerd Hoffmann     free(auth_passwd);
7637572150cSGerd Hoffmann     auth_passwd = strdup(passwd);
7647572150cSGerd Hoffmann     return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn);
7657572150cSGerd Hoffmann }
7667572150cSGerd Hoffmann 
7677572150cSGerd Hoffmann int qemu_spice_set_pw_expire(time_t expires)
7687572150cSGerd Hoffmann {
7697572150cSGerd Hoffmann     auth_expires = expires;
7707572150cSGerd Hoffmann     return qemu_spice_set_ticket(false, false);
7717572150cSGerd Hoffmann }
7727572150cSGerd Hoffmann 
77329b0040bSGerd Hoffmann static void spice_register_config(void)
77429b0040bSGerd Hoffmann {
77529b0040bSGerd Hoffmann     qemu_add_opts(&qemu_spice_opts);
77629b0040bSGerd Hoffmann }
77729b0040bSGerd Hoffmann machine_init(spice_register_config);
77829b0040bSGerd Hoffmann 
77929b0040bSGerd Hoffmann static void spice_initialize(void)
78029b0040bSGerd Hoffmann {
78129b0040bSGerd Hoffmann     qemu_spice_init();
78229b0040bSGerd Hoffmann }
78329b0040bSGerd Hoffmann device_init(spice_initialize);
784