xref: /qemu/ui/spice-core.c (revision f1f5f407)
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>
226f8c63fbSGerd Hoffmann 
2329b0040bSGerd Hoffmann #include "qemu-common.h"
2429b0040bSGerd Hoffmann #include "qemu-spice.h"
25f9ab6091SJan Kiszka #include "qemu-thread.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"
30d1f29646SLuiz Capitulino #include "qmp-commands.h"
316f8c63fbSGerd Hoffmann #include "qint.h"
326f8c63fbSGerd Hoffmann #include "qbool.h"
336f8c63fbSGerd Hoffmann #include "qstring.h"
346f8c63fbSGerd Hoffmann #include "qjson.h"
35e866e239SGerd Hoffmann #include "notify.h"
36e866e239SGerd Hoffmann #include "migration.h"
3729b0040bSGerd Hoffmann #include "monitor.h"
38e866e239SGerd Hoffmann #include "hw/hw.h"
3929b0040bSGerd Hoffmann 
4029b0040bSGerd Hoffmann /* core bits */
4129b0040bSGerd Hoffmann 
4229b0040bSGerd Hoffmann static SpiceServer *spice_server;
43e866e239SGerd Hoffmann static Notifier migration_state;
446f8c63fbSGerd Hoffmann static const char *auth = "spice";
457572150cSGerd Hoffmann static char *auth_passwd;
467572150cSGerd Hoffmann static time_t auth_expires = TIME_MAX;
4729b0040bSGerd Hoffmann int using_spice = 0;
4829b0040bSGerd Hoffmann 
49f9ab6091SJan Kiszka static QemuThread me;
5022b626e2SGerd Hoffmann 
5129b0040bSGerd Hoffmann struct SpiceTimer {
5229b0040bSGerd Hoffmann     QEMUTimer *timer;
5329b0040bSGerd Hoffmann     QTAILQ_ENTRY(SpiceTimer) next;
5429b0040bSGerd Hoffmann };
5529b0040bSGerd Hoffmann static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers);
5629b0040bSGerd Hoffmann 
5729b0040bSGerd Hoffmann static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
5829b0040bSGerd Hoffmann {
5929b0040bSGerd Hoffmann     SpiceTimer *timer;
6029b0040bSGerd Hoffmann 
617267c094SAnthony Liguori     timer = g_malloc0(sizeof(*timer));
627bd427d8SPaolo Bonzini     timer->timer = qemu_new_timer_ms(rt_clock, func, opaque);
6329b0040bSGerd Hoffmann     QTAILQ_INSERT_TAIL(&timers, timer, next);
6429b0040bSGerd Hoffmann     return timer;
6529b0040bSGerd Hoffmann }
6629b0040bSGerd Hoffmann 
6729b0040bSGerd Hoffmann static void timer_start(SpiceTimer *timer, uint32_t ms)
6829b0040bSGerd Hoffmann {
697bd427d8SPaolo Bonzini     qemu_mod_timer(timer->timer, qemu_get_clock_ms(rt_clock) + ms);
7029b0040bSGerd Hoffmann }
7129b0040bSGerd Hoffmann 
7229b0040bSGerd Hoffmann static void timer_cancel(SpiceTimer *timer)
7329b0040bSGerd Hoffmann {
7429b0040bSGerd Hoffmann     qemu_del_timer(timer->timer);
7529b0040bSGerd Hoffmann }
7629b0040bSGerd Hoffmann 
7729b0040bSGerd Hoffmann static void timer_remove(SpiceTimer *timer)
7829b0040bSGerd Hoffmann {
7929b0040bSGerd Hoffmann     qemu_del_timer(timer->timer);
8029b0040bSGerd Hoffmann     qemu_free_timer(timer->timer);
8129b0040bSGerd Hoffmann     QTAILQ_REMOVE(&timers, timer, next);
827267c094SAnthony Liguori     g_free(timer);
8329b0040bSGerd Hoffmann }
8429b0040bSGerd Hoffmann 
8529b0040bSGerd Hoffmann struct SpiceWatch {
8629b0040bSGerd Hoffmann     int fd;
8729b0040bSGerd Hoffmann     int event_mask;
8829b0040bSGerd Hoffmann     SpiceWatchFunc func;
8929b0040bSGerd Hoffmann     void *opaque;
9029b0040bSGerd Hoffmann     QTAILQ_ENTRY(SpiceWatch) next;
9129b0040bSGerd Hoffmann };
9229b0040bSGerd Hoffmann static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches);
9329b0040bSGerd Hoffmann 
9429b0040bSGerd Hoffmann static void watch_read(void *opaque)
9529b0040bSGerd Hoffmann {
9629b0040bSGerd Hoffmann     SpiceWatch *watch = opaque;
9729b0040bSGerd Hoffmann     watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque);
9829b0040bSGerd Hoffmann }
9929b0040bSGerd Hoffmann 
10029b0040bSGerd Hoffmann static void watch_write(void *opaque)
10129b0040bSGerd Hoffmann {
10229b0040bSGerd Hoffmann     SpiceWatch *watch = opaque;
10329b0040bSGerd Hoffmann     watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque);
10429b0040bSGerd Hoffmann }
10529b0040bSGerd Hoffmann 
10629b0040bSGerd Hoffmann static void watch_update_mask(SpiceWatch *watch, int event_mask)
10729b0040bSGerd Hoffmann {
10829b0040bSGerd Hoffmann     IOHandler *on_read = NULL;
10929b0040bSGerd Hoffmann     IOHandler *on_write = NULL;
11029b0040bSGerd Hoffmann 
11129b0040bSGerd Hoffmann     watch->event_mask = event_mask;
11229b0040bSGerd Hoffmann     if (watch->event_mask & SPICE_WATCH_EVENT_READ) {
11329b0040bSGerd Hoffmann         on_read = watch_read;
11429b0040bSGerd Hoffmann     }
11529b0040bSGerd Hoffmann     if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
1163d6d306cSHans de Goede         on_write = watch_write;
11729b0040bSGerd Hoffmann     }
11829b0040bSGerd Hoffmann     qemu_set_fd_handler(watch->fd, on_read, on_write, watch);
11929b0040bSGerd Hoffmann }
12029b0040bSGerd Hoffmann 
12129b0040bSGerd Hoffmann static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
12229b0040bSGerd Hoffmann {
12329b0040bSGerd Hoffmann     SpiceWatch *watch;
12429b0040bSGerd Hoffmann 
1257267c094SAnthony Liguori     watch = g_malloc0(sizeof(*watch));
12629b0040bSGerd Hoffmann     watch->fd     = fd;
12729b0040bSGerd Hoffmann     watch->func   = func;
12829b0040bSGerd Hoffmann     watch->opaque = opaque;
12929b0040bSGerd Hoffmann     QTAILQ_INSERT_TAIL(&watches, watch, next);
13029b0040bSGerd Hoffmann 
13129b0040bSGerd Hoffmann     watch_update_mask(watch, event_mask);
13229b0040bSGerd Hoffmann     return watch;
13329b0040bSGerd Hoffmann }
13429b0040bSGerd Hoffmann 
13529b0040bSGerd Hoffmann static void watch_remove(SpiceWatch *watch)
13629b0040bSGerd Hoffmann {
13708cc67f3SGerd Hoffmann     qemu_set_fd_handler(watch->fd, NULL, NULL, NULL);
13829b0040bSGerd Hoffmann     QTAILQ_REMOVE(&watches, watch, next);
1397267c094SAnthony Liguori     g_free(watch);
14029b0040bSGerd Hoffmann }
14129b0040bSGerd Hoffmann 
1426f8c63fbSGerd Hoffmann #if SPICE_INTERFACE_CORE_MINOR >= 3
1436f8c63fbSGerd Hoffmann 
144cb42a870SGerd Hoffmann typedef struct ChannelList ChannelList;
145cb42a870SGerd Hoffmann struct ChannelList {
146cb42a870SGerd Hoffmann     SpiceChannelEventInfo *info;
147cb42a870SGerd Hoffmann     QTAILQ_ENTRY(ChannelList) link;
148cb42a870SGerd Hoffmann };
149cb42a870SGerd Hoffmann static QTAILQ_HEAD(, ChannelList) channel_list = QTAILQ_HEAD_INITIALIZER(channel_list);
150cb42a870SGerd Hoffmann 
151cb42a870SGerd Hoffmann static void channel_list_add(SpiceChannelEventInfo *info)
152cb42a870SGerd Hoffmann {
153cb42a870SGerd Hoffmann     ChannelList *item;
154cb42a870SGerd Hoffmann 
1557267c094SAnthony Liguori     item = g_malloc0(sizeof(*item));
156cb42a870SGerd Hoffmann     item->info = info;
157cb42a870SGerd Hoffmann     QTAILQ_INSERT_TAIL(&channel_list, item, link);
158cb42a870SGerd Hoffmann }
159cb42a870SGerd Hoffmann 
160cb42a870SGerd Hoffmann static void channel_list_del(SpiceChannelEventInfo *info)
161cb42a870SGerd Hoffmann {
162cb42a870SGerd Hoffmann     ChannelList *item;
163cb42a870SGerd Hoffmann 
164cb42a870SGerd Hoffmann     QTAILQ_FOREACH(item, &channel_list, link) {
165cb42a870SGerd Hoffmann         if (item->info != info) {
166cb42a870SGerd Hoffmann             continue;
167cb42a870SGerd Hoffmann         }
168cb42a870SGerd Hoffmann         QTAILQ_REMOVE(&channel_list, item, link);
1697267c094SAnthony Liguori         g_free(item);
170cb42a870SGerd Hoffmann         return;
171cb42a870SGerd Hoffmann     }
172cb42a870SGerd Hoffmann }
173cb42a870SGerd Hoffmann 
1746f8c63fbSGerd Hoffmann static void add_addr_info(QDict *dict, struct sockaddr *addr, int len)
1756f8c63fbSGerd Hoffmann {
1766f8c63fbSGerd Hoffmann     char host[NI_MAXHOST], port[NI_MAXSERV];
1776f8c63fbSGerd Hoffmann     const char *family;
1786f8c63fbSGerd Hoffmann 
1796f8c63fbSGerd Hoffmann     getnameinfo(addr, len, host, sizeof(host), port, sizeof(port),
1806f8c63fbSGerd Hoffmann                 NI_NUMERICHOST | NI_NUMERICSERV);
1816f8c63fbSGerd Hoffmann     family = inet_strfamily(addr->sa_family);
1826f8c63fbSGerd Hoffmann 
1836f8c63fbSGerd Hoffmann     qdict_put(dict, "host", qstring_from_str(host));
1846f8c63fbSGerd Hoffmann     qdict_put(dict, "port", qstring_from_str(port));
1856f8c63fbSGerd Hoffmann     qdict_put(dict, "family", qstring_from_str(family));
1866f8c63fbSGerd Hoffmann }
1876f8c63fbSGerd Hoffmann 
1886f8c63fbSGerd Hoffmann static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info)
1896f8c63fbSGerd Hoffmann {
1906f8c63fbSGerd Hoffmann     int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
1916f8c63fbSGerd Hoffmann 
1926f8c63fbSGerd Hoffmann     qdict_put(dict, "connection-id", qint_from_int(info->connection_id));
1936f8c63fbSGerd Hoffmann     qdict_put(dict, "channel-type", qint_from_int(info->type));
1946f8c63fbSGerd Hoffmann     qdict_put(dict, "channel-id", qint_from_int(info->id));
1956f8c63fbSGerd Hoffmann     qdict_put(dict, "tls", qbool_from_int(tls));
1966f8c63fbSGerd Hoffmann }
1976f8c63fbSGerd Hoffmann 
1986f8c63fbSGerd Hoffmann static void channel_event(int event, SpiceChannelEventInfo *info)
1996f8c63fbSGerd Hoffmann {
2006f8c63fbSGerd Hoffmann     static const int qevent[] = {
2016f8c63fbSGerd Hoffmann         [ SPICE_CHANNEL_EVENT_CONNECTED    ] = QEVENT_SPICE_CONNECTED,
2026f8c63fbSGerd Hoffmann         [ SPICE_CHANNEL_EVENT_INITIALIZED  ] = QEVENT_SPICE_INITIALIZED,
2036f8c63fbSGerd Hoffmann         [ SPICE_CHANNEL_EVENT_DISCONNECTED ] = QEVENT_SPICE_DISCONNECTED,
2046f8c63fbSGerd Hoffmann     };
2056f8c63fbSGerd Hoffmann     QDict *server, *client;
2066f8c63fbSGerd Hoffmann     QObject *data;
2076f8c63fbSGerd Hoffmann 
20822b626e2SGerd Hoffmann     /*
20922b626e2SGerd Hoffmann      * Spice server might have called us from spice worker thread
21022b626e2SGerd Hoffmann      * context (happens on display channel disconnects).  Spice should
21122b626e2SGerd Hoffmann      * not do that.  It isn't that easy to fix it in spice and even
21222b626e2SGerd Hoffmann      * when it is fixed we still should cover the already released
21322b626e2SGerd Hoffmann      * spice versions.  So detect that we've been called from another
21422b626e2SGerd Hoffmann      * thread and grab the iothread lock if so before calling qemu
21522b626e2SGerd Hoffmann      * functions.
21622b626e2SGerd Hoffmann      */
217f9ab6091SJan Kiszka     bool need_lock = !qemu_thread_is_self(&me);
21822b626e2SGerd Hoffmann     if (need_lock) {
21922b626e2SGerd Hoffmann         qemu_mutex_lock_iothread();
22022b626e2SGerd Hoffmann     }
22122b626e2SGerd Hoffmann 
2226f8c63fbSGerd Hoffmann     client = qdict_new();
2236f8c63fbSGerd Hoffmann     add_addr_info(client, &info->paddr, info->plen);
2246f8c63fbSGerd Hoffmann 
2256f8c63fbSGerd Hoffmann     server = qdict_new();
2266f8c63fbSGerd Hoffmann     add_addr_info(server, &info->laddr, info->llen);
2276f8c63fbSGerd Hoffmann 
2286f8c63fbSGerd Hoffmann     if (event == SPICE_CHANNEL_EVENT_INITIALIZED) {
2296f8c63fbSGerd Hoffmann         qdict_put(server, "auth", qstring_from_str(auth));
2306f8c63fbSGerd Hoffmann         add_channel_info(client, info);
231cb42a870SGerd Hoffmann         channel_list_add(info);
232cb42a870SGerd Hoffmann     }
233cb42a870SGerd Hoffmann     if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) {
234cb42a870SGerd Hoffmann         channel_list_del(info);
2356f8c63fbSGerd Hoffmann     }
2366f8c63fbSGerd Hoffmann 
2376f8c63fbSGerd Hoffmann     data = qobject_from_jsonf("{ 'client': %p, 'server': %p }",
2386f8c63fbSGerd Hoffmann                               QOBJECT(client), QOBJECT(server));
2396f8c63fbSGerd Hoffmann     monitor_protocol_event(qevent[event], data);
2406f8c63fbSGerd Hoffmann     qobject_decref(data);
24122b626e2SGerd Hoffmann 
24222b626e2SGerd Hoffmann     if (need_lock) {
24322b626e2SGerd Hoffmann         qemu_mutex_unlock_iothread();
24422b626e2SGerd Hoffmann     }
2456f8c63fbSGerd Hoffmann }
2466f8c63fbSGerd Hoffmann 
2476f8c63fbSGerd Hoffmann #else /* SPICE_INTERFACE_CORE_MINOR >= 3 */
2486f8c63fbSGerd Hoffmann 
2496f8c63fbSGerd Hoffmann static QList *channel_list_get(void)
2506f8c63fbSGerd Hoffmann {
2516f8c63fbSGerd Hoffmann     return NULL;
2526f8c63fbSGerd Hoffmann }
2536f8c63fbSGerd Hoffmann 
2546f8c63fbSGerd Hoffmann #endif /* SPICE_INTERFACE_CORE_MINOR >= 3 */
2556f8c63fbSGerd Hoffmann 
25629b0040bSGerd Hoffmann static SpiceCoreInterface core_interface = {
25729b0040bSGerd Hoffmann     .base.type          = SPICE_INTERFACE_CORE,
25829b0040bSGerd Hoffmann     .base.description   = "qemu core services",
25929b0040bSGerd Hoffmann     .base.major_version = SPICE_INTERFACE_CORE_MAJOR,
26029b0040bSGerd Hoffmann     .base.minor_version = SPICE_INTERFACE_CORE_MINOR,
26129b0040bSGerd Hoffmann 
26229b0040bSGerd Hoffmann     .timer_add          = timer_add,
26329b0040bSGerd Hoffmann     .timer_start        = timer_start,
26429b0040bSGerd Hoffmann     .timer_cancel       = timer_cancel,
26529b0040bSGerd Hoffmann     .timer_remove       = timer_remove,
26629b0040bSGerd Hoffmann 
26729b0040bSGerd Hoffmann     .watch_add          = watch_add,
26829b0040bSGerd Hoffmann     .watch_update_mask  = watch_update_mask,
26929b0040bSGerd Hoffmann     .watch_remove       = watch_remove,
2706f8c63fbSGerd Hoffmann 
2716f8c63fbSGerd Hoffmann #if SPICE_INTERFACE_CORE_MINOR >= 3
2726f8c63fbSGerd Hoffmann     .channel_event      = channel_event,
2736f8c63fbSGerd Hoffmann #endif
27429b0040bSGerd Hoffmann };
27529b0040bSGerd Hoffmann 
276026f773fSYonit Halperin #ifdef SPICE_INTERFACE_MIGRATION
277026f773fSYonit Halperin typedef struct SpiceMigration {
278026f773fSYonit Halperin     SpiceMigrateInstance sin;
279026f773fSYonit Halperin     struct {
280026f773fSYonit Halperin         MonitorCompletion *cb;
281026f773fSYonit Halperin         void *opaque;
282026f773fSYonit Halperin     } connect_complete;
283026f773fSYonit Halperin } SpiceMigration;
284026f773fSYonit Halperin 
285026f773fSYonit Halperin static void migrate_connect_complete_cb(SpiceMigrateInstance *sin);
286026f773fSYonit Halperin 
287026f773fSYonit Halperin static const SpiceMigrateInterface migrate_interface = {
288026f773fSYonit Halperin     .base.type = SPICE_INTERFACE_MIGRATION,
289026f773fSYonit Halperin     .base.description = "migration",
290026f773fSYonit Halperin     .base.major_version = SPICE_INTERFACE_MIGRATION_MAJOR,
291026f773fSYonit Halperin     .base.minor_version = SPICE_INTERFACE_MIGRATION_MINOR,
292026f773fSYonit Halperin     .migrate_connect_complete = migrate_connect_complete_cb,
293026f773fSYonit Halperin     .migrate_end_complete = NULL,
294026f773fSYonit Halperin };
295026f773fSYonit Halperin 
296026f773fSYonit Halperin static SpiceMigration spice_migrate;
297026f773fSYonit Halperin 
298026f773fSYonit Halperin static void migrate_connect_complete_cb(SpiceMigrateInstance *sin)
299026f773fSYonit Halperin {
300026f773fSYonit Halperin     SpiceMigration *sm = container_of(sin, SpiceMigration, sin);
301026f773fSYonit Halperin     if (sm->connect_complete.cb) {
302026f773fSYonit Halperin         sm->connect_complete.cb(sm->connect_complete.opaque, NULL);
303026f773fSYonit Halperin     }
304026f773fSYonit Halperin     sm->connect_complete.cb = NULL;
305026f773fSYonit Halperin }
306026f773fSYonit Halperin #endif
307026f773fSYonit Halperin 
3089f04e09eSYonit Halperin /* config string parsing */
3099f04e09eSYonit Halperin 
3109f04e09eSYonit Halperin static int name2enum(const char *string, const char *table[], int entries)
3119f04e09eSYonit Halperin {
3129f04e09eSYonit Halperin     int i;
3139f04e09eSYonit Halperin 
3149f04e09eSYonit Halperin     if (string) {
3159f04e09eSYonit Halperin         for (i = 0; i < entries; i++) {
3169f04e09eSYonit Halperin             if (!table[i]) {
3179f04e09eSYonit Halperin                 continue;
3189f04e09eSYonit Halperin             }
3199f04e09eSYonit Halperin             if (strcmp(string, table[i]) != 0) {
3209f04e09eSYonit Halperin                 continue;
3219f04e09eSYonit Halperin             }
3229f04e09eSYonit Halperin             return i;
3239f04e09eSYonit Halperin         }
3249f04e09eSYonit Halperin     }
3259f04e09eSYonit Halperin     return -1;
3269f04e09eSYonit Halperin }
3279f04e09eSYonit Halperin 
3289f04e09eSYonit Halperin static int parse_name(const char *string, const char *optname,
3299f04e09eSYonit Halperin                       const char *table[], int entries)
3309f04e09eSYonit Halperin {
3319f04e09eSYonit Halperin     int value = name2enum(string, table, entries);
3329f04e09eSYonit Halperin 
3339f04e09eSYonit Halperin     if (value != -1) {
3349f04e09eSYonit Halperin         return value;
3359f04e09eSYonit Halperin     }
3369f04e09eSYonit Halperin     fprintf(stderr, "spice: invalid %s: %s\n", optname, string);
3379f04e09eSYonit Halperin     exit(1);
3389f04e09eSYonit Halperin }
3399f04e09eSYonit Halperin 
34084a23f25SGerd Hoffmann static const char *stream_video_names[] = {
34184a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_OFF ]    = "off",
34284a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_ALL ]    = "all",
34384a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_FILTER ] = "filter",
34484a23f25SGerd Hoffmann };
34584a23f25SGerd Hoffmann #define parse_stream_video(_name) \
34684a23f25SGerd Hoffmann     name2enum(_name, stream_video_names, ARRAY_SIZE(stream_video_names))
34784a23f25SGerd Hoffmann 
3489f04e09eSYonit Halperin static const char *compression_names[] = {
3499f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_OFF ]      = "off",
3509f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz",
3519f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_AUTO_LZ ]  = "auto_lz",
3529f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_QUIC ]     = "quic",
3539f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_GLZ ]      = "glz",
3549f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_LZ ]       = "lz",
3559f04e09eSYonit Halperin };
3569f04e09eSYonit Halperin #define parse_compression(_name)                                        \
3579f04e09eSYonit Halperin     parse_name(_name, "image compression",                              \
3589f04e09eSYonit Halperin                compression_names, ARRAY_SIZE(compression_names))
3599f04e09eSYonit Halperin 
3609f04e09eSYonit Halperin static const char *wan_compression_names[] = {
3619f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_AUTO   ] = "auto",
3629f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_NEVER  ] = "never",
3639f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always",
3649f04e09eSYonit Halperin };
3659f04e09eSYonit Halperin #define parse_wan_compression(_name)                                    \
3669f04e09eSYonit Halperin     parse_name(_name, "wan compression",                                \
3679f04e09eSYonit Halperin                wan_compression_names, ARRAY_SIZE(wan_compression_names))
3689f04e09eSYonit Halperin 
36929b0040bSGerd Hoffmann /* functions for the rest of qemu */
37029b0040bSGerd Hoffmann 
371d1f29646SLuiz Capitulino static SpiceChannelList *qmp_query_spice_channels(void)
372cb42a870SGerd Hoffmann {
373d1f29646SLuiz Capitulino     SpiceChannelList *cur_item = NULL, *head = NULL;
374d1f29646SLuiz Capitulino     ChannelList *item;
375cb42a870SGerd Hoffmann 
376d1f29646SLuiz Capitulino     QTAILQ_FOREACH(item, &channel_list, link) {
377d1f29646SLuiz Capitulino         SpiceChannelList *chan;
378d1f29646SLuiz Capitulino         char host[NI_MAXHOST], port[NI_MAXSERV];
379cb42a870SGerd Hoffmann 
380d1f29646SLuiz Capitulino         chan = g_malloc0(sizeof(*chan));
381d1f29646SLuiz Capitulino         chan->value = g_malloc0(sizeof(*chan->value));
382cb42a870SGerd Hoffmann 
383d1f29646SLuiz Capitulino         getnameinfo(&item->info->paddr, item->info->plen,
384d1f29646SLuiz Capitulino                     host, sizeof(host), port, sizeof(port),
385d1f29646SLuiz Capitulino                     NI_NUMERICHOST | NI_NUMERICSERV);
386d1f29646SLuiz Capitulino         chan->value->host = g_strdup(host);
387d1f29646SLuiz Capitulino         chan->value->port = g_strdup(port);
388d1f29646SLuiz Capitulino         chan->value->family = g_strdup(inet_strfamily(item->info->paddr.sa_family));
389cb42a870SGerd Hoffmann 
390d1f29646SLuiz Capitulino         chan->value->connection_id = item->info->connection_id;
391d1f29646SLuiz Capitulino         chan->value->channel_type = item->info->type;
392d1f29646SLuiz Capitulino         chan->value->channel_id = item->info->id;
393d1f29646SLuiz Capitulino         chan->value->tls = item->info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
394cb42a870SGerd Hoffmann 
395d1f29646SLuiz Capitulino        /* XXX: waiting for the qapi to support GSList */
396d1f29646SLuiz Capitulino         if (!cur_item) {
397d1f29646SLuiz Capitulino             head = cur_item = chan;
398cb42a870SGerd Hoffmann         } else {
399d1f29646SLuiz Capitulino             cur_item->next = chan;
400d1f29646SLuiz Capitulino             cur_item = chan;
401cb42a870SGerd Hoffmann         }
402cb42a870SGerd Hoffmann     }
403cb42a870SGerd Hoffmann 
404d1f29646SLuiz Capitulino     return head;
405d1f29646SLuiz Capitulino }
406d1f29646SLuiz Capitulino 
407d1f29646SLuiz Capitulino SpiceInfo *qmp_query_spice(Error **errp)
408cb42a870SGerd Hoffmann {
409cb42a870SGerd Hoffmann     QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
410cb42a870SGerd Hoffmann     int port, tls_port;
411d1f29646SLuiz Capitulino     const char *addr;
412d1f29646SLuiz Capitulino     SpiceInfo *info;
4138df0c7e8SAlon Levy     char version_string[20]; /* 12 = |255.255.255\0| is the max */
414cb42a870SGerd Hoffmann 
415d1f29646SLuiz Capitulino     info = g_malloc0(sizeof(*info));
416d1f29646SLuiz Capitulino 
4173bb781f3SAlon Levy     if (!spice_server || !opts) {
418d1f29646SLuiz Capitulino         info->enabled = false;
419d1f29646SLuiz Capitulino         return info;
420cb42a870SGerd Hoffmann     }
421cb42a870SGerd Hoffmann 
422d1f29646SLuiz Capitulino     info->enabled = true;
423d1f29646SLuiz Capitulino 
424cb42a870SGerd Hoffmann     addr = qemu_opt_get(opts, "addr");
425cb42a870SGerd Hoffmann     port = qemu_opt_get_number(opts, "port", 0);
426cb42a870SGerd Hoffmann     tls_port = qemu_opt_get_number(opts, "tls-port", 0);
427cb42a870SGerd Hoffmann 
428d1f29646SLuiz Capitulino     info->has_auth = true;
429d1f29646SLuiz Capitulino     info->auth = g_strdup(auth);
430d1f29646SLuiz Capitulino 
431d1f29646SLuiz Capitulino     info->has_host = true;
432d1f29646SLuiz Capitulino     info->host = g_strdup(addr ? addr : "0.0.0.0");
433d1f29646SLuiz Capitulino 
434d1f29646SLuiz Capitulino     info->has_compiled_version = true;
4358df0c7e8SAlon Levy     snprintf(version_string, sizeof(version_string), "%d.%d.%d",
4368df0c7e8SAlon Levy              (SPICE_SERVER_VERSION & 0xff0000) >> 16,
4378df0c7e8SAlon Levy              (SPICE_SERVER_VERSION & 0xff00) >> 8,
4388df0c7e8SAlon Levy              SPICE_SERVER_VERSION & 0xff);
439d1f29646SLuiz Capitulino     info->compiled_version = g_strdup(version_string);
440d1f29646SLuiz Capitulino 
441cb42a870SGerd Hoffmann     if (port) {
442d1f29646SLuiz Capitulino         info->has_port = true;
443d1f29646SLuiz Capitulino         info->port = port;
444cb42a870SGerd Hoffmann     }
445cb42a870SGerd Hoffmann     if (tls_port) {
446d1f29646SLuiz Capitulino         info->has_tls_port = true;
447d1f29646SLuiz Capitulino         info->tls_port = tls_port;
448cb42a870SGerd Hoffmann     }
449cb42a870SGerd Hoffmann 
450d1f29646SLuiz Capitulino     /* for compatibility with the original command */
451d1f29646SLuiz Capitulino     info->has_channels = true;
452d1f29646SLuiz Capitulino     info->channels = qmp_query_spice_channels();
453d1f29646SLuiz Capitulino 
454d1f29646SLuiz Capitulino     return info;
455cb42a870SGerd Hoffmann }
456cb42a870SGerd Hoffmann 
4579e8dd451SJan Kiszka static void migration_state_notifier(Notifier *notifier, void *data)
458e866e239SGerd Hoffmann {
4597073693bSJuan Quintela     MigrationState *s = data;
460e866e239SGerd Hoffmann 
461026f773fSYonit Halperin     if (migration_is_active(s)) {
462026f773fSYonit Halperin #ifdef SPICE_INTERFACE_MIGRATION
463026f773fSYonit Halperin         spice_server_migrate_start(spice_server);
464026f773fSYonit Halperin #endif
465026f773fSYonit Halperin     } else if (migration_has_finished(s)) {
466e866e239SGerd Hoffmann #if SPICE_SERVER_VERSION >= 0x000701 /* 0.7.1 */
467026f773fSYonit Halperin #ifndef SPICE_INTERFACE_MIGRATION
468e866e239SGerd Hoffmann         spice_server_migrate_switch(spice_server);
469026f773fSYonit Halperin #else
470026f773fSYonit Halperin         spice_server_migrate_end(spice_server, true);
471026f773fSYonit Halperin     } else if (migration_has_failed(s)) {
472026f773fSYonit Halperin         spice_server_migrate_end(spice_server, false);
473026f773fSYonit Halperin #endif
474e866e239SGerd Hoffmann #endif
475e866e239SGerd Hoffmann     }
476e866e239SGerd Hoffmann }
477e866e239SGerd Hoffmann 
478e866e239SGerd Hoffmann int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
479edc5cb1aSYonit Halperin                             const char *subject,
480edc5cb1aSYonit Halperin                             MonitorCompletion *cb, void *opaque)
481e866e239SGerd Hoffmann {
482edc5cb1aSYonit Halperin     int ret;
483026f773fSYonit Halperin #ifdef SPICE_INTERFACE_MIGRATION
484026f773fSYonit Halperin     spice_migrate.connect_complete.cb = cb;
485026f773fSYonit Halperin     spice_migrate.connect_complete.opaque = opaque;
486026f773fSYonit Halperin     ret = spice_server_migrate_connect(spice_server, hostname,
487026f773fSYonit Halperin                                        port, tls_port, subject);
488026f773fSYonit Halperin #else
489edc5cb1aSYonit Halperin     ret = spice_server_migrate_info(spice_server, hostname,
490e866e239SGerd Hoffmann                                     port, tls_port, subject);
491edc5cb1aSYonit Halperin     cb(opaque, NULL);
492026f773fSYonit Halperin #endif
493edc5cb1aSYonit Halperin     return ret;
494e866e239SGerd Hoffmann }
495e866e239SGerd Hoffmann 
49617b6dea0SGerd Hoffmann static int add_channel(const char *name, const char *value, void *opaque)
49717b6dea0SGerd Hoffmann {
49817b6dea0SGerd Hoffmann     int security = 0;
49917b6dea0SGerd Hoffmann     int rc;
50017b6dea0SGerd Hoffmann 
50117b6dea0SGerd Hoffmann     if (strcmp(name, "tls-channel") == 0) {
50217b6dea0SGerd Hoffmann         security = SPICE_CHANNEL_SECURITY_SSL;
50317b6dea0SGerd Hoffmann     }
50417b6dea0SGerd Hoffmann     if (strcmp(name, "plaintext-channel") == 0) {
50517b6dea0SGerd Hoffmann         security = SPICE_CHANNEL_SECURITY_NONE;
50617b6dea0SGerd Hoffmann     }
50717b6dea0SGerd Hoffmann     if (security == 0) {
50817b6dea0SGerd Hoffmann         return 0;
50917b6dea0SGerd Hoffmann     }
51017b6dea0SGerd Hoffmann     if (strcmp(value, "default") == 0) {
51117b6dea0SGerd Hoffmann         rc = spice_server_set_channel_security(spice_server, NULL, security);
51217b6dea0SGerd Hoffmann     } else {
51317b6dea0SGerd Hoffmann         rc = spice_server_set_channel_security(spice_server, value, security);
51417b6dea0SGerd Hoffmann     }
51517b6dea0SGerd Hoffmann     if (rc != 0) {
51617b6dea0SGerd Hoffmann         fprintf(stderr, "spice: failed to set channel security for %s\n", value);
51717b6dea0SGerd Hoffmann         exit(1);
51817b6dea0SGerd Hoffmann     }
51917b6dea0SGerd Hoffmann     return 0;
52017b6dea0SGerd Hoffmann }
52117b6dea0SGerd Hoffmann 
52229b0040bSGerd Hoffmann void qemu_spice_init(void)
52329b0040bSGerd Hoffmann {
52429b0040bSGerd Hoffmann     QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
525333b0eebSGerd Hoffmann     const char *password, *str, *x509_dir, *addr,
526c448e855SGerd Hoffmann         *x509_key_password = NULL,
527c448e855SGerd Hoffmann         *x509_dh_file = NULL,
528c448e855SGerd Hoffmann         *tls_ciphers = NULL;
529c448e855SGerd Hoffmann     char *x509_key_file = NULL,
530c448e855SGerd Hoffmann         *x509_cert_file = NULL,
531c448e855SGerd Hoffmann         *x509_cacert_file = NULL;
532f61d6960SGerd Hoffmann     int port, tls_port, len, addr_flags;
5339f04e09eSYonit Halperin     spice_image_compression_t compression;
5349f04e09eSYonit Halperin     spice_wan_compression_t wan_compr;
53529b0040bSGerd Hoffmann 
536f9ab6091SJan Kiszka     qemu_thread_get_self(&me);
53722b626e2SGerd Hoffmann 
53829b0040bSGerd Hoffmann    if (!opts) {
53929b0040bSGerd Hoffmann         return;
54029b0040bSGerd Hoffmann     }
54129b0040bSGerd Hoffmann     port = qemu_opt_get_number(opts, "port", 0);
542c448e855SGerd Hoffmann     tls_port = qemu_opt_get_number(opts, "tls-port", 0);
543c448e855SGerd Hoffmann     if (!port && !tls_port) {
544df9cb669SGerd Hoffmann         fprintf(stderr, "neither port nor tls-port specified for spice.");
545df9cb669SGerd Hoffmann         exit(1);
546df9cb669SGerd Hoffmann     }
547df9cb669SGerd Hoffmann     if (port < 0 || port > 65535) {
548df9cb669SGerd Hoffmann         fprintf(stderr, "spice port is out of range");
549df9cb669SGerd Hoffmann         exit(1);
550df9cb669SGerd Hoffmann     }
551df9cb669SGerd Hoffmann     if (tls_port < 0 || tls_port > 65535) {
552df9cb669SGerd Hoffmann         fprintf(stderr, "spice tls-port is out of range");
553df9cb669SGerd Hoffmann         exit(1);
55429b0040bSGerd Hoffmann     }
55529b0040bSGerd Hoffmann     password = qemu_opt_get(opts, "password");
55629b0040bSGerd Hoffmann 
557c448e855SGerd Hoffmann     if (tls_port) {
558c448e855SGerd Hoffmann         x509_dir = qemu_opt_get(opts, "x509-dir");
559c448e855SGerd Hoffmann         if (NULL == x509_dir) {
560c448e855SGerd Hoffmann             x509_dir = ".";
561c448e855SGerd Hoffmann         }
562c448e855SGerd Hoffmann         len = strlen(x509_dir) + 32;
563c448e855SGerd Hoffmann 
564c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-key-file");
565c448e855SGerd Hoffmann         if (str) {
5667267c094SAnthony Liguori             x509_key_file = g_strdup(str);
567c448e855SGerd Hoffmann         } else {
5687267c094SAnthony Liguori             x509_key_file = g_malloc(len);
569c448e855SGerd Hoffmann             snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE);
570c448e855SGerd Hoffmann         }
571c448e855SGerd Hoffmann 
572c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-cert-file");
573c448e855SGerd Hoffmann         if (str) {
5747267c094SAnthony Liguori             x509_cert_file = g_strdup(str);
575c448e855SGerd Hoffmann         } else {
5767267c094SAnthony Liguori             x509_cert_file = g_malloc(len);
577c448e855SGerd Hoffmann             snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE);
578c448e855SGerd Hoffmann         }
579c448e855SGerd Hoffmann 
580c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-cacert-file");
581c448e855SGerd Hoffmann         if (str) {
5827267c094SAnthony Liguori             x509_cacert_file = g_strdup(str);
583c448e855SGerd Hoffmann         } else {
5847267c094SAnthony Liguori             x509_cacert_file = g_malloc(len);
585c448e855SGerd Hoffmann             snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE);
586c448e855SGerd Hoffmann         }
587c448e855SGerd Hoffmann 
588c448e855SGerd Hoffmann         x509_key_password = qemu_opt_get(opts, "x509-key-password");
589c448e855SGerd Hoffmann         x509_dh_file = qemu_opt_get(opts, "x509-dh-file");
590c448e855SGerd Hoffmann         tls_ciphers = qemu_opt_get(opts, "tls-ciphers");
591c448e855SGerd Hoffmann     }
592c448e855SGerd Hoffmann 
593333b0eebSGerd Hoffmann     addr = qemu_opt_get(opts, "addr");
594333b0eebSGerd Hoffmann     addr_flags = 0;
595333b0eebSGerd Hoffmann     if (qemu_opt_get_bool(opts, "ipv4", 0)) {
596333b0eebSGerd Hoffmann         addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY;
597333b0eebSGerd Hoffmann     } else if (qemu_opt_get_bool(opts, "ipv6", 0)) {
598333b0eebSGerd Hoffmann         addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY;
599333b0eebSGerd Hoffmann     }
600333b0eebSGerd Hoffmann 
60129b0040bSGerd Hoffmann     spice_server = spice_server_new();
602333b0eebSGerd Hoffmann     spice_server_set_addr(spice_server, addr ? addr : "", addr_flags);
603c448e855SGerd Hoffmann     if (port) {
60429b0040bSGerd Hoffmann         spice_server_set_port(spice_server, port);
605c448e855SGerd Hoffmann     }
606c448e855SGerd Hoffmann     if (tls_port) {
607c448e855SGerd Hoffmann         spice_server_set_tls(spice_server, tls_port,
608c448e855SGerd Hoffmann                              x509_cacert_file,
609c448e855SGerd Hoffmann                              x509_cert_file,
610c448e855SGerd Hoffmann                              x509_key_file,
611c448e855SGerd Hoffmann                              x509_key_password,
612c448e855SGerd Hoffmann                              x509_dh_file,
613c448e855SGerd Hoffmann                              tls_ciphers);
614c448e855SGerd Hoffmann     }
61529b0040bSGerd Hoffmann     if (password) {
61629b0040bSGerd Hoffmann         spice_server_set_ticket(spice_server, password, 0, 0, 0);
61729b0040bSGerd Hoffmann     }
61848b3ed0aSMarc-André Lureau     if (qemu_opt_get_bool(opts, "sasl", 0)) {
61948b3ed0aSMarc-André Lureau #if SPICE_SERVER_VERSION >= 0x000900 /* 0.9.0 */
62048b3ed0aSMarc-André Lureau         if (spice_server_set_sasl_appname(spice_server, "qemu") == -1 ||
62148b3ed0aSMarc-André Lureau             spice_server_set_sasl(spice_server, 1) == -1) {
62248b3ed0aSMarc-André Lureau             fprintf(stderr, "spice: failed to enable sasl\n");
62348b3ed0aSMarc-André Lureau             exit(1);
62448b3ed0aSMarc-André Lureau         }
62548b3ed0aSMarc-André Lureau #else
62648b3ed0aSMarc-André Lureau         fprintf(stderr, "spice: sasl is not available (spice >= 0.9 required)\n");
62748b3ed0aSMarc-André Lureau         exit(1);
62848b3ed0aSMarc-André Lureau #endif
62948b3ed0aSMarc-André Lureau     }
63029b0040bSGerd Hoffmann     if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) {
6316f8c63fbSGerd Hoffmann         auth = "none";
63229b0040bSGerd Hoffmann         spice_server_set_noauth(spice_server);
63329b0040bSGerd Hoffmann     }
63429b0040bSGerd Hoffmann 
635d4970b07SHans de Goede #if SPICE_SERVER_VERSION >= 0x000801
636d4970b07SHans de Goede     if (qemu_opt_get_bool(opts, "disable-copy-paste", 0)) {
637d4970b07SHans de Goede         spice_server_set_agent_copypaste(spice_server, false);
638d4970b07SHans de Goede     }
639d4970b07SHans de Goede #endif
640d4970b07SHans de Goede 
6419f04e09eSYonit Halperin     compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ;
6429f04e09eSYonit Halperin     str = qemu_opt_get(opts, "image-compression");
6439f04e09eSYonit Halperin     if (str) {
6449f04e09eSYonit Halperin         compression = parse_compression(str);
6459f04e09eSYonit Halperin     }
6469f04e09eSYonit Halperin     spice_server_set_image_compression(spice_server, compression);
6479f04e09eSYonit Halperin 
6489f04e09eSYonit Halperin     wan_compr = SPICE_WAN_COMPRESSION_AUTO;
6499f04e09eSYonit Halperin     str = qemu_opt_get(opts, "jpeg-wan-compression");
6509f04e09eSYonit Halperin     if (str) {
6519f04e09eSYonit Halperin         wan_compr = parse_wan_compression(str);
6529f04e09eSYonit Halperin     }
6539f04e09eSYonit Halperin     spice_server_set_jpeg_compression(spice_server, wan_compr);
6549f04e09eSYonit Halperin 
6559f04e09eSYonit Halperin     wan_compr = SPICE_WAN_COMPRESSION_AUTO;
6569f04e09eSYonit Halperin     str = qemu_opt_get(opts, "zlib-glz-wan-compression");
6579f04e09eSYonit Halperin     if (str) {
6589f04e09eSYonit Halperin         wan_compr = parse_wan_compression(str);
6599f04e09eSYonit Halperin     }
6609f04e09eSYonit Halperin     spice_server_set_zlib_glz_compression(spice_server, wan_compr);
66129b0040bSGerd Hoffmann 
66284a23f25SGerd Hoffmann     str = qemu_opt_get(opts, "streaming-video");
66384a23f25SGerd Hoffmann     if (str) {
664f61d6960SGerd Hoffmann         int streaming_video = parse_stream_video(str);
66584a23f25SGerd Hoffmann         spice_server_set_streaming_video(spice_server, streaming_video);
66684a23f25SGerd Hoffmann     }
66784a23f25SGerd Hoffmann 
66884a23f25SGerd Hoffmann     spice_server_set_agent_mouse
66984a23f25SGerd Hoffmann         (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1));
67084a23f25SGerd Hoffmann     spice_server_set_playback_compression
67184a23f25SGerd Hoffmann         (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1));
67284a23f25SGerd Hoffmann 
67317b6dea0SGerd Hoffmann     qemu_opt_foreach(opts, add_channel, NULL, 0);
67417b6dea0SGerd Hoffmann 
675fba810f1SGerd Hoffmann     if (0 != spice_server_init(spice_server, &core_interface)) {
676fba810f1SGerd Hoffmann         fprintf(stderr, "failed to initialize spice server");
677fba810f1SGerd Hoffmann         exit(1);
678fba810f1SGerd Hoffmann     };
67929b0040bSGerd Hoffmann     using_spice = 1;
680864401c2SGerd Hoffmann 
681e866e239SGerd Hoffmann     migration_state.notify = migration_state_notifier;
682e866e239SGerd Hoffmann     add_migration_state_change_notifier(&migration_state);
683026f773fSYonit Halperin #ifdef SPICE_INTERFACE_MIGRATION
684026f773fSYonit Halperin     spice_migrate.sin.base.sif = &migrate_interface.base;
685026f773fSYonit Halperin     spice_migrate.connect_complete.cb = NULL;
686026f773fSYonit Halperin     qemu_spice_add_interface(&spice_migrate.sin.base);
687026f773fSYonit Halperin #endif
688e866e239SGerd Hoffmann 
689864401c2SGerd Hoffmann     qemu_spice_input_init();
6903e313753SGerd Hoffmann     qemu_spice_audio_init();
691c448e855SGerd Hoffmann 
6927267c094SAnthony Liguori     g_free(x509_key_file);
6937267c094SAnthony Liguori     g_free(x509_cert_file);
6947267c094SAnthony Liguori     g_free(x509_cacert_file);
69529b0040bSGerd Hoffmann }
69629b0040bSGerd Hoffmann 
69729b0040bSGerd Hoffmann int qemu_spice_add_interface(SpiceBaseInstance *sin)
69829b0040bSGerd Hoffmann {
699a19cbfb3SGerd Hoffmann     if (!spice_server) {
700a19cbfb3SGerd Hoffmann         if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) {
701a19cbfb3SGerd Hoffmann             fprintf(stderr, "Oops: spice configured but not active\n");
702a19cbfb3SGerd Hoffmann             exit(1);
703a19cbfb3SGerd Hoffmann         }
704a19cbfb3SGerd Hoffmann         /*
705a19cbfb3SGerd Hoffmann          * Create a spice server instance.
706a19cbfb3SGerd Hoffmann          * It does *not* listen on the network.
707a19cbfb3SGerd Hoffmann          * It handles QXL local rendering only.
708a19cbfb3SGerd Hoffmann          *
709a19cbfb3SGerd Hoffmann          * With a command line like '-vnc :0 -vga qxl' you'll end up here.
710a19cbfb3SGerd Hoffmann          */
711a19cbfb3SGerd Hoffmann         spice_server = spice_server_new();
712a19cbfb3SGerd Hoffmann         spice_server_init(spice_server, &core_interface);
713a19cbfb3SGerd Hoffmann     }
71429b0040bSGerd Hoffmann     return spice_server_add_interface(spice_server, sin);
71529b0040bSGerd Hoffmann }
71629b0040bSGerd Hoffmann 
7177572150cSGerd Hoffmann static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn)
7187572150cSGerd Hoffmann {
7197572150cSGerd Hoffmann     time_t lifetime, now = time(NULL);
7207572150cSGerd Hoffmann     char *passwd;
7217572150cSGerd Hoffmann 
7227572150cSGerd Hoffmann     if (now < auth_expires) {
7237572150cSGerd Hoffmann         passwd = auth_passwd;
7247572150cSGerd Hoffmann         lifetime = (auth_expires - now);
7257572150cSGerd Hoffmann         if (lifetime > INT_MAX) {
7267572150cSGerd Hoffmann             lifetime = INT_MAX;
7277572150cSGerd Hoffmann         }
7287572150cSGerd Hoffmann     } else {
7297572150cSGerd Hoffmann         passwd = NULL;
7307572150cSGerd Hoffmann         lifetime = 1;
7317572150cSGerd Hoffmann     }
7327572150cSGerd Hoffmann     return spice_server_set_ticket(spice_server, passwd, lifetime,
7337572150cSGerd Hoffmann                                    fail_if_conn, disconnect_if_conn);
7347572150cSGerd Hoffmann }
7357572150cSGerd Hoffmann 
7367572150cSGerd Hoffmann int qemu_spice_set_passwd(const char *passwd,
7377572150cSGerd Hoffmann                           bool fail_if_conn, bool disconnect_if_conn)
7387572150cSGerd Hoffmann {
7397572150cSGerd Hoffmann     free(auth_passwd);
7407572150cSGerd Hoffmann     auth_passwd = strdup(passwd);
7417572150cSGerd Hoffmann     return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn);
7427572150cSGerd Hoffmann }
7437572150cSGerd Hoffmann 
7447572150cSGerd Hoffmann int qemu_spice_set_pw_expire(time_t expires)
7457572150cSGerd Hoffmann {
7467572150cSGerd Hoffmann     auth_expires = expires;
7477572150cSGerd Hoffmann     return qemu_spice_set_ticket(false, false);
7487572150cSGerd Hoffmann }
7497572150cSGerd Hoffmann 
750*f1f5f407SDaniel P. Berrange int qemu_spice_display_add_client(int csock, int skipauth, int tls)
751*f1f5f407SDaniel P. Berrange {
752*f1f5f407SDaniel P. Berrange #if SPICE_SERVER_VERSION >= 0x000a01
753*f1f5f407SDaniel P. Berrange     if (tls) {
754*f1f5f407SDaniel P. Berrange         return spice_server_add_ssl_client(spice_server, csock, skipauth);
755*f1f5f407SDaniel P. Berrange     } else {
756*f1f5f407SDaniel P. Berrange         return spice_server_add_client(spice_server, csock, skipauth);
757*f1f5f407SDaniel P. Berrange     }
758*f1f5f407SDaniel P. Berrange #else
759*f1f5f407SDaniel P. Berrange     return -1;
760*f1f5f407SDaniel P. Berrange #endif
761*f1f5f407SDaniel P. Berrange }
762*f1f5f407SDaniel P. Berrange 
76329b0040bSGerd Hoffmann static void spice_register_config(void)
76429b0040bSGerd Hoffmann {
76529b0040bSGerd Hoffmann     qemu_add_opts(&qemu_spice_opts);
76629b0040bSGerd Hoffmann }
76729b0040bSGerd Hoffmann machine_init(spice_register_config);
76829b0040bSGerd Hoffmann 
76983f7d43aSAndreas Färber static void spice_register_types(void)
77029b0040bSGerd Hoffmann {
77129b0040bSGerd Hoffmann     qemu_spice_init();
77229b0040bSGerd Hoffmann }
77383f7d43aSAndreas Färber 
77483f7d43aSAndreas Färber type_init(spice_register_types)
775