xref: /qemu/ui/spice-core.c (revision 5663dd3f)
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 
18e16f4c87SPeter Maydell #include "qemu/osdep.h"
1929b0040bSGerd Hoffmann #include <spice.h>
2029b0040bSGerd Hoffmann 
219c17d615SPaolo Bonzini #include "sysemu/sysemu.h"
2254d31236SMarkus Armbruster #include "sysemu/runstate.h"
2328ecbaeeSPaolo Bonzini #include "ui/qemu-spice.h"
24d49b6836SMarkus Armbruster #include "qemu/error-report.h"
25db725815SMarkus Armbruster #include "qemu/main-loop.h"
260b8fa32fSMarkus Armbruster #include "qemu/module.h"
271de7afc9SPaolo Bonzini #include "qemu/thread.h"
281de7afc9SPaolo Bonzini #include "qemu/timer.h"
291de7afc9SPaolo Bonzini #include "qemu/queue.h"
30c448e855SGerd Hoffmann #include "qemu-x509.h"
311de7afc9SPaolo Bonzini #include "qemu/sockets.h"
32e688df6bSMarkus Armbruster #include "qapi/error.h"
339af23989SMarkus Armbruster #include "qapi/qapi-commands-ui.h"
349af23989SMarkus Armbruster #include "qapi/qapi-events-ui.h"
351de7afc9SPaolo Bonzini #include "qemu/notify.h"
36922a01a0SMarkus Armbruster #include "qemu/option.h"
3799522f69SDaniel P. Berrangé #include "crypto/secret_common.h"
38c4b63b7cSJuan Quintela #include "migration/misc.h"
39be812c0aSLukáš Hrázký #include "hw/pci/pci_bus.h"
4028ecbaeeSPaolo Bonzini #include "ui/spice-display.h"
4129b0040bSGerd Hoffmann 
4229b0040bSGerd Hoffmann /* core bits */
4329b0040bSGerd Hoffmann 
4429b0040bSGerd Hoffmann static SpiceServer *spice_server;
453e775730SSteve Sistare static NotifierWithReturn 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;
507cc6a25fSGerd Hoffmann static int spice_display_is_running;
51a76a2f72SGerd Hoffmann static int spice_have_target_host;
5229b0040bSGerd Hoffmann 
53f9ab6091SJan Kiszka static QemuThread me;
5422b626e2SGerd Hoffmann 
5529b0040bSGerd Hoffmann struct SpiceTimer {
5629b0040bSGerd Hoffmann     QEMUTimer *timer;
5729b0040bSGerd Hoffmann };
5829b0040bSGerd Hoffmann 
timer_add(SpiceTimerFunc func,void * opaque)5929b0040bSGerd Hoffmann static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
6029b0040bSGerd Hoffmann {
6129b0040bSGerd Hoffmann     SpiceTimer *timer;
6229b0040bSGerd Hoffmann 
637267c094SAnthony Liguori     timer = g_malloc0(sizeof(*timer));
64bc72ad67SAlex Bligh     timer->timer = timer_new_ms(QEMU_CLOCK_REALTIME, func, opaque);
6529b0040bSGerd Hoffmann     return timer;
6629b0040bSGerd Hoffmann }
6729b0040bSGerd Hoffmann 
timer_start(SpiceTimer * timer,uint32_t ms)6829b0040bSGerd Hoffmann static void timer_start(SpiceTimer *timer, uint32_t ms)
6929b0040bSGerd Hoffmann {
70bc72ad67SAlex Bligh     timer_mod(timer->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + ms);
7129b0040bSGerd Hoffmann }
7229b0040bSGerd Hoffmann 
timer_cancel(SpiceTimer * timer)7329b0040bSGerd Hoffmann static void timer_cancel(SpiceTimer *timer)
7429b0040bSGerd Hoffmann {
75bc72ad67SAlex Bligh     timer_del(timer->timer);
7629b0040bSGerd Hoffmann }
7729b0040bSGerd Hoffmann 
timer_remove(SpiceTimer * timer)7829b0040bSGerd Hoffmann static void timer_remove(SpiceTimer *timer)
7929b0040bSGerd Hoffmann {
80bc72ad67SAlex Bligh     timer_free(timer->timer);
817267c094SAnthony Liguori     g_free(timer);
8229b0040bSGerd Hoffmann }
8329b0040bSGerd Hoffmann 
8429b0040bSGerd Hoffmann struct SpiceWatch {
8529b0040bSGerd Hoffmann     int fd;
8629b0040bSGerd Hoffmann     SpiceWatchFunc func;
8729b0040bSGerd Hoffmann     void *opaque;
8829b0040bSGerd Hoffmann };
8929b0040bSGerd Hoffmann 
watch_read(void * opaque)9029b0040bSGerd Hoffmann static void watch_read(void *opaque)
9129b0040bSGerd Hoffmann {
9229b0040bSGerd Hoffmann     SpiceWatch *watch = opaque;
93e40283d9SMarc-André Lureau     int fd = watch->fd;
94e40283d9SMarc-André Lureau 
95e40283d9SMarc-André Lureau #ifdef WIN32
96e40283d9SMarc-André Lureau     fd = _get_osfhandle(fd);
97e40283d9SMarc-André Lureau #endif
98e40283d9SMarc-André Lureau     watch->func(fd, SPICE_WATCH_EVENT_READ, watch->opaque);
9929b0040bSGerd Hoffmann }
10029b0040bSGerd Hoffmann 
watch_write(void * opaque)10129b0040bSGerd Hoffmann static void watch_write(void *opaque)
10229b0040bSGerd Hoffmann {
10329b0040bSGerd Hoffmann     SpiceWatch *watch = opaque;
104e40283d9SMarc-André Lureau     int fd = watch->fd;
105e40283d9SMarc-André Lureau 
106e40283d9SMarc-André Lureau #ifdef WIN32
107e40283d9SMarc-André Lureau     fd = _get_osfhandle(fd);
108e40283d9SMarc-André Lureau #endif
109e40283d9SMarc-André Lureau     watch->func(fd, SPICE_WATCH_EVENT_WRITE, watch->opaque);
11029b0040bSGerd Hoffmann }
11129b0040bSGerd Hoffmann 
watch_update_mask(SpiceWatch * watch,int event_mask)11229b0040bSGerd Hoffmann static void watch_update_mask(SpiceWatch *watch, int event_mask)
11329b0040bSGerd Hoffmann {
11429b0040bSGerd Hoffmann     IOHandler *on_read = NULL;
11529b0040bSGerd Hoffmann     IOHandler *on_write = NULL;
11629b0040bSGerd Hoffmann 
11758a5d33aSFrediano Ziglio     if (event_mask & SPICE_WATCH_EVENT_READ) {
11829b0040bSGerd Hoffmann         on_read = watch_read;
11929b0040bSGerd Hoffmann     }
12058a5d33aSFrediano Ziglio     if (event_mask & SPICE_WATCH_EVENT_WRITE) {
1213d6d306cSHans de Goede         on_write = watch_write;
12229b0040bSGerd Hoffmann     }
12329b0040bSGerd Hoffmann     qemu_set_fd_handler(watch->fd, on_read, on_write, watch);
12429b0040bSGerd Hoffmann }
12529b0040bSGerd Hoffmann 
watch_add(int fd,int event_mask,SpiceWatchFunc func,void * opaque)12629b0040bSGerd Hoffmann static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
12729b0040bSGerd Hoffmann {
12829b0040bSGerd Hoffmann     SpiceWatch *watch;
12929b0040bSGerd Hoffmann 
130e40283d9SMarc-André Lureau #ifdef WIN32
131e40283d9SMarc-André Lureau     fd = _open_osfhandle(fd, _O_BINARY);
132e40283d9SMarc-André Lureau     if (fd < 0) {
133e40283d9SMarc-André Lureau         error_setg_win32(&error_warn, WSAGetLastError(), "Couldn't associate a FD with the SOCKET");
134e40283d9SMarc-André Lureau         return NULL;
135e40283d9SMarc-André Lureau     }
136e40283d9SMarc-André Lureau #endif
137e40283d9SMarc-André Lureau 
1387267c094SAnthony Liguori     watch = g_malloc0(sizeof(*watch));
13929b0040bSGerd Hoffmann     watch->fd     = fd;
14029b0040bSGerd Hoffmann     watch->func   = func;
14129b0040bSGerd Hoffmann     watch->opaque = opaque;
14229b0040bSGerd Hoffmann 
14329b0040bSGerd Hoffmann     watch_update_mask(watch, event_mask);
14429b0040bSGerd Hoffmann     return watch;
14529b0040bSGerd Hoffmann }
14629b0040bSGerd Hoffmann 
watch_remove(SpiceWatch * watch)14729b0040bSGerd Hoffmann static void watch_remove(SpiceWatch *watch)
14829b0040bSGerd Hoffmann {
14908cc67f3SGerd Hoffmann     qemu_set_fd_handler(watch->fd, NULL, NULL, NULL);
150e40283d9SMarc-André Lureau #ifdef WIN32
151e40283d9SMarc-André Lureau     /* SOCKET is owned by spice */
1527b1bde95SMarc-André Lureau     qemu_close_socket_osfhandle(watch->fd);
153e40283d9SMarc-André Lureau #endif
1547267c094SAnthony Liguori     g_free(watch);
15529b0040bSGerd Hoffmann }
15629b0040bSGerd Hoffmann 
157cb42a870SGerd Hoffmann typedef struct ChannelList ChannelList;
158cb42a870SGerd Hoffmann struct ChannelList {
159cb42a870SGerd Hoffmann     SpiceChannelEventInfo *info;
160cb42a870SGerd Hoffmann     QTAILQ_ENTRY(ChannelList) link;
161cb42a870SGerd Hoffmann };
162cb42a870SGerd Hoffmann static QTAILQ_HEAD(, ChannelList) channel_list = QTAILQ_HEAD_INITIALIZER(channel_list);
163cb42a870SGerd Hoffmann 
channel_list_add(SpiceChannelEventInfo * info)164cb42a870SGerd Hoffmann static void channel_list_add(SpiceChannelEventInfo *info)
165cb42a870SGerd Hoffmann {
166cb42a870SGerd Hoffmann     ChannelList *item;
167cb42a870SGerd Hoffmann 
1687267c094SAnthony Liguori     item = g_malloc0(sizeof(*item));
169cb42a870SGerd Hoffmann     item->info = info;
170cb42a870SGerd Hoffmann     QTAILQ_INSERT_TAIL(&channel_list, item, link);
171cb42a870SGerd Hoffmann }
172cb42a870SGerd Hoffmann 
channel_list_del(SpiceChannelEventInfo * info)173cb42a870SGerd Hoffmann static void channel_list_del(SpiceChannelEventInfo *info)
174cb42a870SGerd Hoffmann {
175cb42a870SGerd Hoffmann     ChannelList *item;
176cb42a870SGerd Hoffmann 
177cb42a870SGerd Hoffmann     QTAILQ_FOREACH(item, &channel_list, link) {
178cb42a870SGerd Hoffmann         if (item->info != info) {
179cb42a870SGerd Hoffmann             continue;
180cb42a870SGerd Hoffmann         }
181cb42a870SGerd Hoffmann         QTAILQ_REMOVE(&channel_list, item, link);
1827267c094SAnthony Liguori         g_free(item);
183cb42a870SGerd Hoffmann         return;
184cb42a870SGerd Hoffmann     }
185cb42a870SGerd Hoffmann }
186cb42a870SGerd Hoffmann 
add_addr_info(SpiceBasicInfo * info,struct sockaddr * addr,int len)1877cfadb6bSWenchao Xia static void add_addr_info(SpiceBasicInfo *info, struct sockaddr *addr, int len)
1886f8c63fbSGerd Hoffmann {
1896f8c63fbSGerd Hoffmann     char host[NI_MAXHOST], port[NI_MAXSERV];
1906f8c63fbSGerd Hoffmann 
1916f8c63fbSGerd Hoffmann     getnameinfo(addr, len, host, sizeof(host), port, sizeof(port),
1926f8c63fbSGerd Hoffmann                 NI_NUMERICHOST | NI_NUMERICSERV);
1936f8c63fbSGerd Hoffmann 
1947cfadb6bSWenchao Xia     info->host = g_strdup(host);
1957cfadb6bSWenchao Xia     info->port = g_strdup(port);
1967cfadb6bSWenchao Xia     info->family = inet_netfamily(addr->sa_family);
1976f8c63fbSGerd Hoffmann }
1986f8c63fbSGerd Hoffmann 
add_channel_info(SpiceChannel * sc,SpiceChannelEventInfo * info)1997cfadb6bSWenchao Xia static void add_channel_info(SpiceChannel *sc, SpiceChannelEventInfo *info)
2006f8c63fbSGerd Hoffmann {
2016f8c63fbSGerd Hoffmann     int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
2026f8c63fbSGerd Hoffmann 
2037cfadb6bSWenchao Xia     sc->connection_id = info->connection_id;
2047cfadb6bSWenchao Xia     sc->channel_type = info->type;
2057cfadb6bSWenchao Xia     sc->channel_id = info->id;
2067cfadb6bSWenchao Xia     sc->tls = !!tls;
2076f8c63fbSGerd Hoffmann }
2086f8c63fbSGerd Hoffmann 
channel_event(int event,SpiceChannelEventInfo * info)2096f8c63fbSGerd Hoffmann static void channel_event(int event, SpiceChannelEventInfo *info)
2106f8c63fbSGerd Hoffmann {
2117cfadb6bSWenchao Xia     SpiceServerInfo *server = g_malloc0(sizeof(*server));
2127cfadb6bSWenchao Xia     SpiceChannel *client = g_malloc0(sizeof(*client));
2136f8c63fbSGerd Hoffmann 
21422b626e2SGerd Hoffmann     /*
21522b626e2SGerd Hoffmann      * Spice server might have called us from spice worker thread
21622b626e2SGerd Hoffmann      * context (happens on display channel disconnects).  Spice should
21722b626e2SGerd Hoffmann      * not do that.  It isn't that easy to fix it in spice and even
21822b626e2SGerd Hoffmann      * when it is fixed we still should cover the already released
21922b626e2SGerd Hoffmann      * spice versions.  So detect that we've been called from another
220a4a411fbSStefan Hajnoczi      * thread and grab the BQL if so before calling qemu
22122b626e2SGerd Hoffmann      * functions.
22222b626e2SGerd Hoffmann      */
223f9ab6091SJan Kiszka     bool need_lock = !qemu_thread_is_self(&me);
22422b626e2SGerd Hoffmann     if (need_lock) {
225195801d7SStefan Hajnoczi         bql_lock();
22622b626e2SGerd Hoffmann     }
22722b626e2SGerd Hoffmann 
228faa98223SYonit Halperin     if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) {
229ddf21908SEric Blake         add_addr_info(qapi_SpiceChannel_base(client),
230ddf21908SEric Blake                       (struct sockaddr *)&info->paddr_ext,
231faa98223SYonit Halperin                       info->plen_ext);
232ddf21908SEric Blake         add_addr_info(qapi_SpiceServerInfo_base(server),
233ddf21908SEric Blake                       (struct sockaddr *)&info->laddr_ext,
234faa98223SYonit Halperin                       info->llen_ext);
235faa98223SYonit Halperin     } else {
236339a475fSChristophe Fergeau         error_report("spice: %s, extended address is expected",
237faa98223SYonit Halperin                      __func__);
238faa98223SYonit Halperin     }
2396f8c63fbSGerd Hoffmann 
2407cfadb6bSWenchao Xia     switch (event) {
2417cfadb6bSWenchao Xia     case SPICE_CHANNEL_EVENT_CONNECTED:
242ddf21908SEric Blake         qapi_event_send_spice_connected(qapi_SpiceServerInfo_base(server),
2433ab72385SPeter Xu                                         qapi_SpiceChannel_base(client));
2447cfadb6bSWenchao Xia         break;
2457cfadb6bSWenchao Xia     case SPICE_CHANNEL_EVENT_INITIALIZED:
2467cfadb6bSWenchao Xia         if (auth) {
2477cfadb6bSWenchao Xia             server->auth = g_strdup(auth);
2487cfadb6bSWenchao Xia         }
2496f8c63fbSGerd Hoffmann         add_channel_info(client, info);
250cb42a870SGerd Hoffmann         channel_list_add(info);
2513ab72385SPeter Xu         qapi_event_send_spice_initialized(server, client);
2527cfadb6bSWenchao Xia         break;
2537cfadb6bSWenchao Xia     case SPICE_CHANNEL_EVENT_DISCONNECTED:
254cb42a870SGerd Hoffmann         channel_list_del(info);
255ddf21908SEric Blake         qapi_event_send_spice_disconnected(qapi_SpiceServerInfo_base(server),
2563ab72385SPeter Xu                                            qapi_SpiceChannel_base(client));
2577cfadb6bSWenchao Xia         break;
2587cfadb6bSWenchao Xia     default:
2597cfadb6bSWenchao Xia         break;
2606f8c63fbSGerd Hoffmann     }
2616f8c63fbSGerd Hoffmann 
26222b626e2SGerd Hoffmann     if (need_lock) {
263195801d7SStefan Hajnoczi         bql_unlock();
26422b626e2SGerd Hoffmann     }
2657cfadb6bSWenchao Xia 
2667cfadb6bSWenchao Xia     qapi_free_SpiceServerInfo(server);
2677cfadb6bSWenchao Xia     qapi_free_SpiceChannel(client);
2686f8c63fbSGerd Hoffmann }
2696f8c63fbSGerd Hoffmann 
27029b0040bSGerd Hoffmann static SpiceCoreInterface core_interface = {
27129b0040bSGerd Hoffmann     .base.type          = SPICE_INTERFACE_CORE,
27229b0040bSGerd Hoffmann     .base.description   = "qemu core services",
27329b0040bSGerd Hoffmann     .base.major_version = SPICE_INTERFACE_CORE_MAJOR,
27429b0040bSGerd Hoffmann     .base.minor_version = SPICE_INTERFACE_CORE_MINOR,
27529b0040bSGerd Hoffmann 
27629b0040bSGerd Hoffmann     .timer_add          = timer_add,
27729b0040bSGerd Hoffmann     .timer_start        = timer_start,
27829b0040bSGerd Hoffmann     .timer_cancel       = timer_cancel,
27929b0040bSGerd Hoffmann     .timer_remove       = timer_remove,
28029b0040bSGerd Hoffmann 
28129b0040bSGerd Hoffmann     .watch_add          = watch_add,
28229b0040bSGerd Hoffmann     .watch_update_mask  = watch_update_mask,
28329b0040bSGerd Hoffmann     .watch_remove       = watch_remove,
2846f8c63fbSGerd Hoffmann 
2856f8c63fbSGerd Hoffmann     .channel_event      = channel_event,
28629b0040bSGerd Hoffmann };
28729b0040bSGerd Hoffmann 
288026f773fSYonit Halperin static void migrate_connect_complete_cb(SpiceMigrateInstance *sin);
2892fdd16e2SYonit Halperin static void migrate_end_complete_cb(SpiceMigrateInstance *sin);
290026f773fSYonit Halperin 
291026f773fSYonit Halperin static const SpiceMigrateInterface migrate_interface = {
292026f773fSYonit Halperin     .base.type = SPICE_INTERFACE_MIGRATION,
293026f773fSYonit Halperin     .base.description = "migration",
294026f773fSYonit Halperin     .base.major_version = SPICE_INTERFACE_MIGRATION_MAJOR,
295026f773fSYonit Halperin     .base.minor_version = SPICE_INTERFACE_MIGRATION_MINOR,
296026f773fSYonit Halperin     .migrate_connect_complete = migrate_connect_complete_cb,
2972fdd16e2SYonit Halperin     .migrate_end_complete = migrate_end_complete_cb,
298026f773fSYonit Halperin };
299026f773fSYonit Halperin 
3003b5704b2SMarkus Armbruster static SpiceMigrateInstance spice_migrate;
301026f773fSYonit Halperin 
migrate_connect_complete_cb(SpiceMigrateInstance * sin)302026f773fSYonit Halperin static void migrate_connect_complete_cb(SpiceMigrateInstance *sin)
303026f773fSYonit Halperin {
3043b5704b2SMarkus Armbruster     /* nothing, but libspice-server expects this cb being present. */
305026f773fSYonit Halperin }
3062fdd16e2SYonit Halperin 
migrate_end_complete_cb(SpiceMigrateInstance * sin)3072fdd16e2SYonit Halperin static void migrate_end_complete_cb(SpiceMigrateInstance *sin)
3082fdd16e2SYonit Halperin {
3093ab72385SPeter Xu     qapi_event_send_spice_migrate_completed();
31061c4efe2SYonit Halperin     spice_migration_completed = true;
3112fdd16e2SYonit Halperin }
312026f773fSYonit Halperin 
3139f04e09eSYonit Halperin /* config string parsing */
3149f04e09eSYonit Halperin 
name2enum(const char * string,const char * table[],int entries)3159f04e09eSYonit Halperin static int name2enum(const char *string, const char *table[], int entries)
3169f04e09eSYonit Halperin {
3179f04e09eSYonit Halperin     int i;
3189f04e09eSYonit Halperin 
3199f04e09eSYonit Halperin     if (string) {
3209f04e09eSYonit Halperin         for (i = 0; i < entries; i++) {
3219f04e09eSYonit Halperin             if (!table[i]) {
3229f04e09eSYonit Halperin                 continue;
3239f04e09eSYonit Halperin             }
3249f04e09eSYonit Halperin             if (strcmp(string, table[i]) != 0) {
3259f04e09eSYonit Halperin                 continue;
3269f04e09eSYonit Halperin             }
3279f04e09eSYonit Halperin             return i;
3289f04e09eSYonit Halperin         }
3299f04e09eSYonit Halperin     }
3309f04e09eSYonit Halperin     return -1;
3319f04e09eSYonit Halperin }
3329f04e09eSYonit Halperin 
parse_name(const char * string,const char * optname,const char * table[],int entries)3339f04e09eSYonit Halperin static int parse_name(const char *string, const char *optname,
3349f04e09eSYonit Halperin                       const char *table[], int entries)
3359f04e09eSYonit Halperin {
3369f04e09eSYonit Halperin     int value = name2enum(string, table, entries);
3379f04e09eSYonit Halperin 
3389f04e09eSYonit Halperin     if (value != -1) {
3399f04e09eSYonit Halperin         return value;
3409f04e09eSYonit Halperin     }
341339a475fSChristophe Fergeau     error_report("spice: invalid %s: %s", optname, string);
3429f04e09eSYonit Halperin     exit(1);
3439f04e09eSYonit Halperin }
3449f04e09eSYonit Halperin 
34584a23f25SGerd Hoffmann static const char *stream_video_names[] = {
34684a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_OFF ]    = "off",
34784a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_ALL ]    = "all",
34884a23f25SGerd Hoffmann     [ SPICE_STREAM_VIDEO_FILTER ] = "filter",
34984a23f25SGerd Hoffmann };
35084a23f25SGerd Hoffmann #define parse_stream_video(_name) \
351835cab85SChristophe Fergeau     parse_name(_name, "stream video control", \
352835cab85SChristophe Fergeau                stream_video_names, ARRAY_SIZE(stream_video_names))
35384a23f25SGerd Hoffmann 
3549f04e09eSYonit Halperin static const char *compression_names[] = {
3559f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_OFF ]      = "off",
3569f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz",
3579f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_AUTO_LZ ]  = "auto_lz",
3589f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_QUIC ]     = "quic",
3599f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_GLZ ]      = "glz",
3609f04e09eSYonit Halperin     [ SPICE_IMAGE_COMPRESS_LZ ]       = "lz",
3619f04e09eSYonit Halperin };
3629f04e09eSYonit Halperin #define parse_compression(_name)                                        \
3639f04e09eSYonit Halperin     parse_name(_name, "image compression",                              \
3649f04e09eSYonit Halperin                compression_names, ARRAY_SIZE(compression_names))
3659f04e09eSYonit Halperin 
3669f04e09eSYonit Halperin static const char *wan_compression_names[] = {
3679f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_AUTO   ] = "auto",
3689f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_NEVER  ] = "never",
3699f04e09eSYonit Halperin     [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always",
3709f04e09eSYonit Halperin };
3719f04e09eSYonit Halperin #define parse_wan_compression(_name)                                    \
3729f04e09eSYonit Halperin     parse_name(_name, "wan compression",                                \
3739f04e09eSYonit Halperin                wan_compression_names, ARRAY_SIZE(wan_compression_names))
3749f04e09eSYonit Halperin 
37529b0040bSGerd Hoffmann /* functions for the rest of qemu */
37629b0040bSGerd Hoffmann 
qmp_query_spice_channels(void)377d1f29646SLuiz Capitulino static SpiceChannelList *qmp_query_spice_channels(void)
378cb42a870SGerd Hoffmann {
37995b3a8c8SEric Blake     SpiceChannelList *head = NULL, **tail = &head;
380d1f29646SLuiz Capitulino     ChannelList *item;
381cb42a870SGerd Hoffmann 
382d1f29646SLuiz Capitulino     QTAILQ_FOREACH(item, &channel_list, link) {
38395b3a8c8SEric Blake         SpiceChannel *chan;
384d1f29646SLuiz Capitulino         char host[NI_MAXHOST], port[NI_MAXSERV];
385faa98223SYonit Halperin         struct sockaddr *paddr;
386faa98223SYonit Halperin         socklen_t plen;
387cb42a870SGerd Hoffmann 
388a4164270SGonglei         assert(item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT);
38926defe81SMarc-André Lureau 
390d1f29646SLuiz Capitulino         chan = g_malloc0(sizeof(*chan));
391cb42a870SGerd Hoffmann 
392faa98223SYonit Halperin         paddr = (struct sockaddr *)&item->info->paddr_ext;
393faa98223SYonit Halperin         plen = item->info->plen_ext;
394faa98223SYonit Halperin         getnameinfo(paddr, plen,
395d1f29646SLuiz Capitulino                     host, sizeof(host), port, sizeof(port),
396d1f29646SLuiz Capitulino                     NI_NUMERICHOST | NI_NUMERICSERV);
39795b3a8c8SEric Blake         chan->host = g_strdup(host);
39895b3a8c8SEric Blake         chan->port = g_strdup(port);
39995b3a8c8SEric Blake         chan->family = inet_netfamily(paddr->sa_family);
400cb42a870SGerd Hoffmann 
40195b3a8c8SEric Blake         chan->connection_id = item->info->connection_id;
40295b3a8c8SEric Blake         chan->channel_type = item->info->type;
40395b3a8c8SEric Blake         chan->channel_id = item->info->id;
40495b3a8c8SEric Blake         chan->tls = item->info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
405cb42a870SGerd Hoffmann 
40695b3a8c8SEric Blake         QAPI_LIST_APPEND(tail, chan);
407cb42a870SGerd Hoffmann     }
408cb42a870SGerd Hoffmann 
409d1f29646SLuiz Capitulino     return head;
410d1f29646SLuiz Capitulino }
411d1f29646SLuiz Capitulino 
4124d454574SPaolo Bonzini static QemuOptsList qemu_spice_opts = {
4134d454574SPaolo Bonzini     .name = "spice",
4144d454574SPaolo Bonzini     .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head),
41579216718SMarc-André Lureau     .merge_lists = true,
4164d454574SPaolo Bonzini     .desc = {
4174d454574SPaolo Bonzini         {
4184d454574SPaolo Bonzini             .name = "port",
4194d454574SPaolo Bonzini             .type = QEMU_OPT_NUMBER,
4204d454574SPaolo Bonzini         },{
4214d454574SPaolo Bonzini             .name = "tls-port",
4224d454574SPaolo Bonzini             .type = QEMU_OPT_NUMBER,
4234d454574SPaolo Bonzini         },{
4244d454574SPaolo Bonzini             .name = "addr",
4254d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4264d454574SPaolo Bonzini         },{
4274d454574SPaolo Bonzini             .name = "ipv4",
4284d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4294d454574SPaolo Bonzini         },{
4304d454574SPaolo Bonzini             .name = "ipv6",
4314d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
432fe4831b1SMarc-André Lureau #ifdef SPICE_ADDR_FLAG_UNIX_ONLY
433fe4831b1SMarc-André Lureau         },{
434fe4831b1SMarc-André Lureau             .name = "unix",
435fe4831b1SMarc-André Lureau             .type = QEMU_OPT_BOOL,
436fe4831b1SMarc-André Lureau #endif
4374d454574SPaolo Bonzini         },{
43899522f69SDaniel P. Berrangé             .name = "password-secret",
43999522f69SDaniel P. Berrangé             .type = QEMU_OPT_STRING,
44099522f69SDaniel P. Berrangé         },{
4414d454574SPaolo Bonzini             .name = "disable-ticketing",
4424d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4434d454574SPaolo Bonzini         },{
4444d454574SPaolo Bonzini             .name = "disable-copy-paste",
4454d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4464d454574SPaolo Bonzini         },{
4475ad24e5fSHans de Goede             .name = "disable-agent-file-xfer",
4485ad24e5fSHans de Goede             .type = QEMU_OPT_BOOL,
4495ad24e5fSHans de Goede         },{
4504d454574SPaolo Bonzini             .name = "sasl",
4514d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4524d454574SPaolo Bonzini         },{
4534d454574SPaolo Bonzini             .name = "x509-dir",
4544d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4554d454574SPaolo Bonzini         },{
4564d454574SPaolo Bonzini             .name = "x509-key-file",
4574d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4584d454574SPaolo Bonzini         },{
4594d454574SPaolo Bonzini             .name = "x509-key-password",
4604d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4614d454574SPaolo Bonzini         },{
4624d454574SPaolo Bonzini             .name = "x509-cert-file",
4634d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4644d454574SPaolo Bonzini         },{
4654d454574SPaolo Bonzini             .name = "x509-cacert-file",
4664d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4674d454574SPaolo Bonzini         },{
4684d454574SPaolo Bonzini             .name = "x509-dh-key-file",
4694d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4704d454574SPaolo Bonzini         },{
4714d454574SPaolo Bonzini             .name = "tls-ciphers",
4724d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4734d454574SPaolo Bonzini         },{
4744d454574SPaolo Bonzini             .name = "tls-channel",
4754d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4764d454574SPaolo Bonzini         },{
4774d454574SPaolo Bonzini             .name = "plaintext-channel",
4784d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4794d454574SPaolo Bonzini         },{
4804d454574SPaolo Bonzini             .name = "image-compression",
4814d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4824d454574SPaolo Bonzini         },{
4834d454574SPaolo Bonzini             .name = "jpeg-wan-compression",
4844d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4854d454574SPaolo Bonzini         },{
4864d454574SPaolo Bonzini             .name = "zlib-glz-wan-compression",
4874d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4884d454574SPaolo Bonzini         },{
4894d454574SPaolo Bonzini             .name = "streaming-video",
4904d454574SPaolo Bonzini             .type = QEMU_OPT_STRING,
4914d454574SPaolo Bonzini         },{
4924d454574SPaolo Bonzini             .name = "agent-mouse",
4934d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4944d454574SPaolo Bonzini         },{
4954d454574SPaolo Bonzini             .name = "playback-compression",
4964d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
4974d454574SPaolo Bonzini         },{
4984d454574SPaolo Bonzini             .name = "seamless-migration",
4994d454574SPaolo Bonzini             .type = QEMU_OPT_BOOL,
5008bf69b49SGerd Hoffmann         },{
5018bf69b49SGerd Hoffmann             .name = "display",
5028bf69b49SGerd Hoffmann             .type = QEMU_OPT_STRING,
5038bf69b49SGerd Hoffmann         },{
5048bf69b49SGerd Hoffmann             .name = "head",
5058bf69b49SGerd Hoffmann             .type = QEMU_OPT_NUMBER,
506474114b7SGerd Hoffmann #ifdef HAVE_SPICE_GL
507474114b7SGerd Hoffmann         },{
508474114b7SGerd Hoffmann             .name = "gl",
509474114b7SGerd Hoffmann             .type = QEMU_OPT_BOOL,
5107b525508SMarc-André Lureau         },{
5117b525508SMarc-André Lureau             .name = "rendernode",
5127b525508SMarc-André Lureau             .type = QEMU_OPT_STRING,
513474114b7SGerd Hoffmann #endif
5144d454574SPaolo Bonzini         },
5154d454574SPaolo Bonzini         { /* end of list */ }
5164d454574SPaolo Bonzini     },
5174d454574SPaolo Bonzini };
5184d454574SPaolo Bonzini 
qmp_query_spice_real(Error ** errp)519db5732c9SGerd Hoffmann static SpiceInfo *qmp_query_spice_real(Error **errp)
520cb42a870SGerd Hoffmann {
521cb42a870SGerd Hoffmann     QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
522cb42a870SGerd Hoffmann     int port, tls_port;
523d1f29646SLuiz Capitulino     const char *addr;
524d1f29646SLuiz Capitulino     SpiceInfo *info;
5256735aa99SChristophe Fergeau     unsigned int major;
5266735aa99SChristophe Fergeau     unsigned int minor;
5276735aa99SChristophe Fergeau     unsigned int micro;
528cb42a870SGerd Hoffmann 
529d1f29646SLuiz Capitulino     info = g_malloc0(sizeof(*info));
530d1f29646SLuiz Capitulino 
5313bb781f3SAlon Levy     if (!spice_server || !opts) {
532d1f29646SLuiz Capitulino         info->enabled = false;
533d1f29646SLuiz Capitulino         return info;
534cb42a870SGerd Hoffmann     }
535cb42a870SGerd Hoffmann 
536d1f29646SLuiz Capitulino     info->enabled = true;
53761c4efe2SYonit Halperin     info->migrated = spice_migration_completed;
538d1f29646SLuiz Capitulino 
539cb42a870SGerd Hoffmann     addr = qemu_opt_get(opts, "addr");
540cb42a870SGerd Hoffmann     port = qemu_opt_get_number(opts, "port", 0);
541cb42a870SGerd Hoffmann     tls_port = qemu_opt_get_number(opts, "tls-port", 0);
542cb42a870SGerd Hoffmann 
543d1f29646SLuiz Capitulino     info->auth = g_strdup(auth);
5444f60af9aSGerd Hoffmann     info->host = g_strdup(addr ? addr : "*");
545d1f29646SLuiz Capitulino 
5466735aa99SChristophe Fergeau     major = (SPICE_SERVER_VERSION & 0xff0000) >> 16;
5476735aa99SChristophe Fergeau     minor = (SPICE_SERVER_VERSION & 0xff00) >> 8;
5486735aa99SChristophe Fergeau     micro = SPICE_SERVER_VERSION & 0xff;
5496735aa99SChristophe Fergeau     info->compiled_version = g_strdup_printf("%d.%d.%d", major, minor, micro);
550d1f29646SLuiz Capitulino 
551cb42a870SGerd Hoffmann     if (port) {
552d1f29646SLuiz Capitulino         info->has_port = true;
553d1f29646SLuiz Capitulino         info->port = port;
554cb42a870SGerd Hoffmann     }
555cb42a870SGerd Hoffmann     if (tls_port) {
556d1f29646SLuiz Capitulino         info->has_tls_port = true;
557d1f29646SLuiz Capitulino         info->tls_port = tls_port;
558cb42a870SGerd Hoffmann     }
559cb42a870SGerd Hoffmann 
5604efee029SAlon Levy     info->mouse_mode = spice_server_is_server_mouse(spice_server) ?
5614efee029SAlon Levy                        SPICE_QUERY_MOUSE_MODE_SERVER :
5624efee029SAlon Levy                        SPICE_QUERY_MOUSE_MODE_CLIENT;
56367be6726SGerd Hoffmann 
564d1f29646SLuiz Capitulino     /* for compatibility with the original command */
565d1f29646SLuiz Capitulino     info->has_channels = true;
566d1f29646SLuiz Capitulino     info->channels = qmp_query_spice_channels();
567d1f29646SLuiz Capitulino 
568d1f29646SLuiz Capitulino     return info;
569cb42a870SGerd Hoffmann }
570cb42a870SGerd Hoffmann 
migration_state_notifier(NotifierWithReturn * notifier,MigrationEvent * e,Error ** errp)5713e775730SSteve Sistare static int migration_state_notifier(NotifierWithReturn *notifier,
572*5663dd3fSSteve Sistare                                     MigrationEvent *e, Error **errp)
573e866e239SGerd Hoffmann {
574a76a2f72SGerd Hoffmann     if (!spice_have_target_host) {
5753e775730SSteve Sistare         return 0;
576a76a2f72SGerd Hoffmann     }
577a76a2f72SGerd Hoffmann 
5789d9babf7SSteve Sistare     if (e->type == MIG_EVENT_PRECOPY_SETUP) {
579026f773fSYonit Halperin         spice_server_migrate_start(spice_server);
5809d9babf7SSteve Sistare     } else if (e->type == MIG_EVENT_PRECOPY_DONE) {
581026f773fSYonit Halperin         spice_server_migrate_end(spice_server, true);
582a76a2f72SGerd Hoffmann         spice_have_target_host = false;
5839d9babf7SSteve Sistare     } else if (e->type == MIG_EVENT_PRECOPY_FAILED) {
584026f773fSYonit Halperin         spice_server_migrate_end(spice_server, false);
585a76a2f72SGerd Hoffmann         spice_have_target_host = false;
586e866e239SGerd Hoffmann     }
5873e775730SSteve Sistare     return 0;
588e866e239SGerd Hoffmann }
589e866e239SGerd Hoffmann 
qemu_spice_migrate_info(const char * hostname,int port,int tls_port,const char * subject)590e866e239SGerd Hoffmann int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
5913b5704b2SMarkus Armbruster                             const char *subject)
592e866e239SGerd Hoffmann {
593edc5cb1aSYonit Halperin     int ret;
59467be6726SGerd Hoffmann 
595026f773fSYonit Halperin     ret = spice_server_migrate_connect(spice_server, hostname,
596026f773fSYonit Halperin                                        port, tls_port, subject);
597a76a2f72SGerd Hoffmann     spice_have_target_host = true;
598edc5cb1aSYonit Halperin     return ret;
599e866e239SGerd Hoffmann }
600e866e239SGerd Hoffmann 
add_channel(void * opaque,const char * name,const char * value,Error ** errp)60171df1d83SMarkus Armbruster static int add_channel(void *opaque, const char *name, const char *value,
60271df1d83SMarkus Armbruster                        Error **errp)
60317b6dea0SGerd Hoffmann {
60417b6dea0SGerd Hoffmann     int security = 0;
60517b6dea0SGerd Hoffmann     int rc;
60617b6dea0SGerd Hoffmann 
60717b6dea0SGerd Hoffmann     if (strcmp(name, "tls-channel") == 0) {
60835c63329SChristophe Fergeau         int *tls_port = opaque;
60935c63329SChristophe Fergeau         if (!*tls_port) {
6109338570bSMarkus Armbruster             error_setg(errp, "spice: tried to setup tls-channel"
61135c63329SChristophe Fergeau                        " without specifying a TLS port");
6129338570bSMarkus Armbruster             return -1;
61335c63329SChristophe Fergeau         }
61417b6dea0SGerd Hoffmann         security = SPICE_CHANNEL_SECURITY_SSL;
61517b6dea0SGerd Hoffmann     }
61617b6dea0SGerd Hoffmann     if (strcmp(name, "plaintext-channel") == 0) {
61717b6dea0SGerd Hoffmann         security = SPICE_CHANNEL_SECURITY_NONE;
61817b6dea0SGerd Hoffmann     }
61917b6dea0SGerd Hoffmann     if (security == 0) {
62017b6dea0SGerd Hoffmann         return 0;
62117b6dea0SGerd Hoffmann     }
62217b6dea0SGerd Hoffmann     if (strcmp(value, "default") == 0) {
62317b6dea0SGerd Hoffmann         rc = spice_server_set_channel_security(spice_server, NULL, security);
62417b6dea0SGerd Hoffmann     } else {
62517b6dea0SGerd Hoffmann         rc = spice_server_set_channel_security(spice_server, value, security);
62617b6dea0SGerd Hoffmann     }
62717b6dea0SGerd Hoffmann     if (rc != 0) {
6289338570bSMarkus Armbruster         error_setg(errp, "spice: failed to set channel security for %s",
6299338570bSMarkus Armbruster                    value);
6309338570bSMarkus Armbruster         return -1;
63117b6dea0SGerd Hoffmann     }
63217b6dea0SGerd Hoffmann     return 0;
63317b6dea0SGerd Hoffmann }
63417b6dea0SGerd Hoffmann 
vm_change_state_handler(void * opaque,bool running,RunState state)635538f0497SPhilippe Mathieu-Daudé static void vm_change_state_handler(void *opaque, bool running,
636f5bb039cSYonit Halperin                                     RunState state)
637f5bb039cSYonit Halperin {
638f5bb039cSYonit Halperin     if (running) {
63971d388d4SYonit Halperin         qemu_spice_display_start();
6405b1638bcSMarc-André Lureau     } else if (state != RUN_STATE_PAUSED) {
64171d388d4SYonit Halperin         qemu_spice_display_stop();
642f5bb039cSYonit Halperin     }
643f5bb039cSYonit Halperin }
644f5bb039cSYonit Halperin 
qemu_spice_display_init_done(void)645a652b120SMarc-André Lureau void qemu_spice_display_init_done(void)
646a652b120SMarc-André Lureau {
647a652b120SMarc-André Lureau     if (runstate_is_running()) {
648a652b120SMarc-André Lureau         qemu_spice_display_start();
649a652b120SMarc-André Lureau     }
650a652b120SMarc-André Lureau     qemu_add_vm_change_state_handler(vm_change_state_handler, NULL);
651a652b120SMarc-André Lureau }
652a652b120SMarc-André Lureau 
qemu_spice_init(void)65363be30e6SGerd Hoffmann static void qemu_spice_init(void)
65429b0040bSGerd Hoffmann {
65529b0040bSGerd Hoffmann     QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
65699522f69SDaniel P. Berrangé     char *password = NULL;
65799522f69SDaniel P. Berrangé     const char *passwordSecret;
65899522f69SDaniel P. Berrangé     const char *str, *x509_dir, *addr,
659c448e855SGerd Hoffmann         *x509_key_password = NULL,
660c448e855SGerd Hoffmann         *x509_dh_file = NULL,
661c448e855SGerd Hoffmann         *tls_ciphers = NULL;
662c448e855SGerd Hoffmann     char *x509_key_file = NULL,
663c448e855SGerd Hoffmann         *x509_cert_file = NULL,
664c448e855SGerd Hoffmann         *x509_cacert_file = NULL;
6656735aa99SChristophe Fergeau     int port, tls_port, addr_flags;
6669f04e09eSYonit Halperin     spice_image_compression_t compression;
6679f04e09eSYonit Halperin     spice_wan_compression_t wan_compr;
6688c957053SYonit Halperin     bool seamless_migration;
66929b0040bSGerd Hoffmann 
670f9ab6091SJan Kiszka     qemu_thread_get_self(&me);
67122b626e2SGerd Hoffmann 
67229b0040bSGerd Hoffmann     if (!opts) {
67329b0040bSGerd Hoffmann         return;
67429b0040bSGerd Hoffmann     }
67529b0040bSGerd Hoffmann     port = qemu_opt_get_number(opts, "port", 0);
676c448e855SGerd Hoffmann     tls_port = qemu_opt_get_number(opts, "tls-port", 0);
677df9cb669SGerd Hoffmann     if (port < 0 || port > 65535) {
678339a475fSChristophe Fergeau         error_report("spice port is out of range");
679df9cb669SGerd Hoffmann         exit(1);
680df9cb669SGerd Hoffmann     }
681df9cb669SGerd Hoffmann     if (tls_port < 0 || tls_port > 65535) {
682339a475fSChristophe Fergeau         error_report("spice tls-port is out of range");
683df9cb669SGerd Hoffmann         exit(1);
68429b0040bSGerd Hoffmann     }
68599522f69SDaniel P. Berrangé     passwordSecret = qemu_opt_get(opts, "password-secret");
68699522f69SDaniel P. Berrangé     if (passwordSecret) {
68799522f69SDaniel P. Berrangé         password = qcrypto_secret_lookup_as_utf8(passwordSecret,
688f9734d5dSMarkus Armbruster                                                  &error_fatal);
68999522f69SDaniel P. Berrangé     }
69029b0040bSGerd Hoffmann 
691c448e855SGerd Hoffmann     if (tls_port) {
692c448e855SGerd Hoffmann         x509_dir = qemu_opt_get(opts, "x509-dir");
693fe8e8327SGonglei         if (!x509_dir) {
694c448e855SGerd Hoffmann             x509_dir = ".";
695c448e855SGerd Hoffmann         }
696c448e855SGerd Hoffmann 
697c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-key-file");
698c448e855SGerd Hoffmann         if (str) {
6997267c094SAnthony Liguori             x509_key_file = g_strdup(str);
700c448e855SGerd Hoffmann         } else {
7016735aa99SChristophe Fergeau             x509_key_file = g_strdup_printf("%s/%s", x509_dir,
7026735aa99SChristophe Fergeau                                             X509_SERVER_KEY_FILE);
703c448e855SGerd Hoffmann         }
704c448e855SGerd Hoffmann 
705c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-cert-file");
706c448e855SGerd Hoffmann         if (str) {
7077267c094SAnthony Liguori             x509_cert_file = g_strdup(str);
708c448e855SGerd Hoffmann         } else {
7096735aa99SChristophe Fergeau             x509_cert_file = g_strdup_printf("%s/%s", x509_dir,
7106735aa99SChristophe Fergeau                                              X509_SERVER_CERT_FILE);
711c448e855SGerd Hoffmann         }
712c448e855SGerd Hoffmann 
713c448e855SGerd Hoffmann         str = qemu_opt_get(opts, "x509-cacert-file");
714c448e855SGerd Hoffmann         if (str) {
7157267c094SAnthony Liguori             x509_cacert_file = g_strdup(str);
716c448e855SGerd Hoffmann         } else {
7176735aa99SChristophe Fergeau             x509_cacert_file = g_strdup_printf("%s/%s", x509_dir,
7186735aa99SChristophe Fergeau                                                X509_CA_CERT_FILE);
719c448e855SGerd Hoffmann         }
720c448e855SGerd Hoffmann 
721c448e855SGerd Hoffmann         x509_key_password = qemu_opt_get(opts, "x509-key-password");
7229995c0b7SLei Li         x509_dh_file = qemu_opt_get(opts, "x509-dh-key-file");
723c448e855SGerd Hoffmann         tls_ciphers = qemu_opt_get(opts, "tls-ciphers");
724c448e855SGerd Hoffmann     }
725c448e855SGerd Hoffmann 
726333b0eebSGerd Hoffmann     addr = qemu_opt_get(opts, "addr");
727333b0eebSGerd Hoffmann     addr_flags = 0;
728333b0eebSGerd Hoffmann     if (qemu_opt_get_bool(opts, "ipv4", 0)) {
729333b0eebSGerd Hoffmann         addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY;
730333b0eebSGerd Hoffmann     } else if (qemu_opt_get_bool(opts, "ipv6", 0)) {
731333b0eebSGerd Hoffmann         addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY;
732fe4831b1SMarc-André Lureau #ifdef SPICE_ADDR_FLAG_UNIX_ONLY
733fe4831b1SMarc-André Lureau     } else if (qemu_opt_get_bool(opts, "unix", 0)) {
734fe4831b1SMarc-André Lureau         addr_flags |= SPICE_ADDR_FLAG_UNIX_ONLY;
735fe4831b1SMarc-André Lureau #endif
736333b0eebSGerd Hoffmann     }
737333b0eebSGerd Hoffmann 
73829b0040bSGerd Hoffmann     spice_server = spice_server_new();
739333b0eebSGerd Hoffmann     spice_server_set_addr(spice_server, addr ? addr : "", addr_flags);
740c448e855SGerd Hoffmann     if (port) {
74129b0040bSGerd Hoffmann         spice_server_set_port(spice_server, port);
742c448e855SGerd Hoffmann     }
743c448e855SGerd Hoffmann     if (tls_port) {
744c448e855SGerd Hoffmann         spice_server_set_tls(spice_server, tls_port,
745c448e855SGerd Hoffmann                              x509_cacert_file,
746c448e855SGerd Hoffmann                              x509_cert_file,
747c448e855SGerd Hoffmann                              x509_key_file,
748c448e855SGerd Hoffmann                              x509_key_password,
749c448e855SGerd Hoffmann                              x509_dh_file,
750c448e855SGerd Hoffmann                              tls_ciphers);
751c448e855SGerd Hoffmann     }
75229b0040bSGerd Hoffmann     if (password) {
75308ad2626SGerd Hoffmann         qemu_spice.set_passwd(password, false, false);
75429b0040bSGerd Hoffmann     }
75548b3ed0aSMarc-André Lureau     if (qemu_opt_get_bool(opts, "sasl", 0)) {
75606bb8814SChristophe Fergeau         if (spice_server_set_sasl(spice_server, 1) == -1) {
757339a475fSChristophe Fergeau             error_report("spice: failed to enable sasl");
75848b3ed0aSMarc-André Lureau             exit(1);
75948b3ed0aSMarc-André Lureau         }
760b1ea7b79SGerd Hoffmann         auth = "sasl";
76148b3ed0aSMarc-André Lureau     }
76229b0040bSGerd Hoffmann     if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) {
7636f8c63fbSGerd Hoffmann         auth = "none";
76429b0040bSGerd Hoffmann         spice_server_set_noauth(spice_server);
76529b0040bSGerd Hoffmann     }
76629b0040bSGerd Hoffmann 
767d4970b07SHans de Goede     if (qemu_opt_get_bool(opts, "disable-copy-paste", 0)) {
768d4970b07SHans de Goede         spice_server_set_agent_copypaste(spice_server, false);
769d4970b07SHans de Goede     }
770d4970b07SHans de Goede 
7715ad24e5fSHans de Goede     if (qemu_opt_get_bool(opts, "disable-agent-file-xfer", 0)) {
7725ad24e5fSHans de Goede         spice_server_set_agent_file_xfer(spice_server, false);
7735ad24e5fSHans de Goede     }
7745ad24e5fSHans de Goede 
7759f04e09eSYonit Halperin     compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ;
7769f04e09eSYonit Halperin     str = qemu_opt_get(opts, "image-compression");
7779f04e09eSYonit Halperin     if (str) {
7789f04e09eSYonit Halperin         compression = parse_compression(str);
7799f04e09eSYonit Halperin     }
7809f04e09eSYonit Halperin     spice_server_set_image_compression(spice_server, compression);
7819f04e09eSYonit Halperin 
7829f04e09eSYonit Halperin     wan_compr = SPICE_WAN_COMPRESSION_AUTO;
7839f04e09eSYonit Halperin     str = qemu_opt_get(opts, "jpeg-wan-compression");
7849f04e09eSYonit Halperin     if (str) {
7859f04e09eSYonit Halperin         wan_compr = parse_wan_compression(str);
7869f04e09eSYonit Halperin     }
7879f04e09eSYonit Halperin     spice_server_set_jpeg_compression(spice_server, wan_compr);
7889f04e09eSYonit Halperin 
7899f04e09eSYonit Halperin     wan_compr = SPICE_WAN_COMPRESSION_AUTO;
7909f04e09eSYonit Halperin     str = qemu_opt_get(opts, "zlib-glz-wan-compression");
7919f04e09eSYonit Halperin     if (str) {
7929f04e09eSYonit Halperin         wan_compr = parse_wan_compression(str);
7939f04e09eSYonit Halperin     }
7949f04e09eSYonit Halperin     spice_server_set_zlib_glz_compression(spice_server, wan_compr);
79529b0040bSGerd Hoffmann 
79684a23f25SGerd Hoffmann     str = qemu_opt_get(opts, "streaming-video");
79784a23f25SGerd Hoffmann     if (str) {
798f61d6960SGerd Hoffmann         int streaming_video = parse_stream_video(str);
79984a23f25SGerd Hoffmann         spice_server_set_streaming_video(spice_server, streaming_video);
800f1d3e586SGerd Hoffmann     } else {
801f1d3e586SGerd Hoffmann         spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF);
80284a23f25SGerd Hoffmann     }
80384a23f25SGerd Hoffmann 
80484a23f25SGerd Hoffmann     spice_server_set_agent_mouse
80584a23f25SGerd Hoffmann         (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1));
80684a23f25SGerd Hoffmann     spice_server_set_playback_compression
80784a23f25SGerd Hoffmann         (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1));
80884a23f25SGerd Hoffmann 
8099338570bSMarkus Armbruster     qemu_opt_foreach(opts, add_channel, &tls_port, &error_fatal);
81017b6dea0SGerd Hoffmann 
8114c77ee12SMarc-André Lureau     spice_server_set_name(spice_server, qemu_name ?: "QEMU " QEMU_VERSION);
8129c5ce8dbSFam Zheng     spice_server_set_uuid(spice_server, (unsigned char *)&qemu_uuid);
813d0638b18SMarc-André Lureau 
8148c957053SYonit Halperin     seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0);
8158c957053SYonit Halperin     spice_server_set_seamless_migration(spice_server, seamless_migration);
81606bb8814SChristophe Fergeau     spice_server_set_sasl_appname(spice_server, "qemu");
817fe8e8327SGonglei     if (spice_server_init(spice_server, &core_interface) != 0) {
818339a475fSChristophe Fergeau         error_report("failed to initialize spice server");
819fba810f1SGerd Hoffmann         exit(1);
820fba810f1SGerd Hoffmann     };
82129b0040bSGerd Hoffmann     using_spice = 1;
822864401c2SGerd Hoffmann 
823d9cda213SSteve Sistare     migration_add_notifier(&migration_state, migration_state_notifier);
8243b5704b2SMarkus Armbruster     spice_migrate.base.sif = &migrate_interface.base;
82505b53636SGerd Hoffmann     qemu_spice.add_interface(&spice_migrate.base);
826e866e239SGerd Hoffmann 
827864401c2SGerd Hoffmann     qemu_spice_input_init();
828c448e855SGerd Hoffmann 
829641381c1SGerd Hoffmann     qemu_spice_display_stop();
830f5bb039cSYonit Halperin 
8317267c094SAnthony Liguori     g_free(x509_key_file);
8327267c094SAnthony Liguori     g_free(x509_cert_file);
8337267c094SAnthony Liguori     g_free(x509_cacert_file);
83499522f69SDaniel P. Berrangé     g_free(password);
835afd0b409SMarc-André Lureau 
836474114b7SGerd Hoffmann #ifdef HAVE_SPICE_GL
837474114b7SGerd Hoffmann     if (qemu_opt_get_bool(opts, "gl", 0)) {
838569a93cbSChristophe Fergeau         if ((port != 0) || (tls_port != 0)) {
839569a93cbSChristophe Fergeau             error_report("SPICE GL support is local-only for now and "
840569a93cbSChristophe Fergeau                          "incompatible with -spice port/tls-port");
841569a93cbSChristophe Fergeau             exit(1);
842569a93cbSChristophe Fergeau         }
8430e1be59eSMarc-André Lureau         egl_init(qemu_opt_get(opts, "rendernode"), DISPLAYGL_MODE_ON, &error_fatal);
844fe5c44f9SGerd Hoffmann         spice_opengl = 1;
845474114b7SGerd Hoffmann     }
846474114b7SGerd Hoffmann #endif
84729b0040bSGerd Hoffmann }
84829b0040bSGerd Hoffmann 
qemu_spice_add_interface(SpiceBaseInstance * sin)84905b53636SGerd Hoffmann static int qemu_spice_add_interface(SpiceBaseInstance *sin)
85029b0040bSGerd Hoffmann {
851a19cbfb3SGerd Hoffmann     if (!spice_server) {
852a19cbfb3SGerd Hoffmann         if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) {
853339a475fSChristophe Fergeau             error_report("Oops: spice configured but not active");
854a19cbfb3SGerd Hoffmann             exit(1);
855a19cbfb3SGerd Hoffmann         }
856a19cbfb3SGerd Hoffmann         /*
857a19cbfb3SGerd Hoffmann          * Create a spice server instance.
858a19cbfb3SGerd Hoffmann          * It does *not* listen on the network.
859a19cbfb3SGerd Hoffmann          * It handles QXL local rendering only.
860a19cbfb3SGerd Hoffmann          *
861a19cbfb3SGerd Hoffmann          * With a command line like '-vnc :0 -vga qxl' you'll end up here.
862a19cbfb3SGerd Hoffmann          */
863a19cbfb3SGerd Hoffmann         spice_server = spice_server_new();
864764eb39dSChristophe Fergeau         spice_server_set_sasl_appname(spice_server, "qemu");
865a19cbfb3SGerd Hoffmann         spice_server_init(spice_server, &core_interface);
866bfb82a28SStefan Hajnoczi         qemu_add_vm_change_state_handler(vm_change_state_handler, NULL);
867a19cbfb3SGerd Hoffmann     }
86871d388d4SYonit Halperin 
8699fa03286SGerd Hoffmann     return spice_server_add_interface(spice_server, sin);
87058ae52a8SGerd Hoffmann }
87158ae52a8SGerd Hoffmann 
8729fa03286SGerd Hoffmann static GSList *spice_consoles;
8739fa03286SGerd Hoffmann 
qemu_spice_have_display_interface(QemuConsole * con)8749fa03286SGerd Hoffmann bool qemu_spice_have_display_interface(QemuConsole *con)
8759fa03286SGerd Hoffmann {
8769fa03286SGerd Hoffmann     if (g_slist_find(spice_consoles, con)) {
8779fa03286SGerd Hoffmann         return true;
8789fa03286SGerd Hoffmann     }
8799fa03286SGerd Hoffmann     return false;
8809fa03286SGerd Hoffmann }
8819fa03286SGerd Hoffmann 
qemu_spice_add_display_interface(QXLInstance * qxlin,QemuConsole * con)8829fa03286SGerd Hoffmann int qemu_spice_add_display_interface(QXLInstance *qxlin, QemuConsole *con)
8839fa03286SGerd Hoffmann {
8849fa03286SGerd Hoffmann     if (g_slist_find(spice_consoles, con)) {
8859fa03286SGerd Hoffmann         return -1;
8869fa03286SGerd Hoffmann     }
887cd56cc6bSGerd Hoffmann     qxlin->id = qemu_console_get_index(con);
8889fa03286SGerd Hoffmann     spice_consoles = g_slist_append(spice_consoles, con);
8899fa03286SGerd Hoffmann     return qemu_spice_add_interface(&qxlin->base);
89029b0040bSGerd Hoffmann }
89129b0040bSGerd Hoffmann 
qemu_spice_set_ticket(bool fail_if_conn,bool disconnect_if_conn)8927572150cSGerd Hoffmann static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn)
8937572150cSGerd Hoffmann {
8947572150cSGerd Hoffmann     time_t lifetime, now = time(NULL);
8957572150cSGerd Hoffmann     char *passwd;
8967572150cSGerd Hoffmann 
8977572150cSGerd Hoffmann     if (now < auth_expires) {
8987572150cSGerd Hoffmann         passwd = auth_passwd;
8997572150cSGerd Hoffmann         lifetime = (auth_expires - now);
9007572150cSGerd Hoffmann         if (lifetime > INT_MAX) {
9017572150cSGerd Hoffmann             lifetime = INT_MAX;
9027572150cSGerd Hoffmann         }
9037572150cSGerd Hoffmann     } else {
9047572150cSGerd Hoffmann         passwd = NULL;
9057572150cSGerd Hoffmann         lifetime = 1;
9067572150cSGerd Hoffmann     }
9077572150cSGerd Hoffmann     return spice_server_set_ticket(spice_server, passwd, lifetime,
9087572150cSGerd Hoffmann                                    fail_if_conn, disconnect_if_conn);
9097572150cSGerd Hoffmann }
9107572150cSGerd Hoffmann 
qemu_spice_set_passwd(const char * passwd,bool fail_if_conn,bool disconnect_if_conn)91108ad2626SGerd Hoffmann static int qemu_spice_set_passwd(const char *passwd,
9127572150cSGerd Hoffmann                                  bool fail_if_conn, bool disconnect_if_conn)
9137572150cSGerd Hoffmann {
914b1ea7b79SGerd Hoffmann     if (strcmp(auth, "spice") != 0) {
915b1ea7b79SGerd Hoffmann         return -1;
916b1ea7b79SGerd Hoffmann     }
917b1ea7b79SGerd Hoffmann 
918fd3bea3fSMarkus Armbruster     g_free(auth_passwd);
919fd3bea3fSMarkus Armbruster     auth_passwd = g_strdup(passwd);
9207572150cSGerd Hoffmann     return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn);
9217572150cSGerd Hoffmann }
9227572150cSGerd Hoffmann 
qemu_spice_set_pw_expire(time_t expires)92308ad2626SGerd Hoffmann static int qemu_spice_set_pw_expire(time_t expires)
9247572150cSGerd Hoffmann {
9257572150cSGerd Hoffmann     auth_expires = expires;
9267572150cSGerd Hoffmann     return qemu_spice_set_ticket(false, false);
9277572150cSGerd Hoffmann }
9287572150cSGerd Hoffmann 
qemu_spice_display_add_client(int csock,int skipauth,int tls)929864a024cSGerd Hoffmann static int qemu_spice_display_add_client(int csock, int skipauth, int tls)
930f1f5f407SDaniel P. Berrange {
931e40283d9SMarc-André Lureau #ifdef WIN32
932e40283d9SMarc-André Lureau     csock = qemu_close_socket_osfhandle(csock);
933e40283d9SMarc-André Lureau #endif
934f1f5f407SDaniel P. Berrange     if (tls) {
935f1f5f407SDaniel P. Berrange         return spice_server_add_ssl_client(spice_server, csock, skipauth);
936f1f5f407SDaniel P. Berrange     } else {
937f1f5f407SDaniel P. Berrange         return spice_server_add_client(spice_server, csock, skipauth);
938f1f5f407SDaniel P. Berrange     }
939f1f5f407SDaniel P. Berrange }
940f1f5f407SDaniel P. Berrange 
qemu_spice_display_start(void)9417cc6a25fSGerd Hoffmann void qemu_spice_display_start(void)
9427cc6a25fSGerd Hoffmann {
94383f71802SMarc-André Lureau     if (spice_display_is_running) {
94483f71802SMarc-André Lureau         return;
94583f71802SMarc-André Lureau     }
94683f71802SMarc-André Lureau 
9477cc6a25fSGerd Hoffmann     spice_display_is_running = true;
948b50f3e42SGerd Hoffmann     spice_server_vm_start(spice_server);
9497cc6a25fSGerd Hoffmann }
9507cc6a25fSGerd Hoffmann 
qemu_spice_display_stop(void)9517cc6a25fSGerd Hoffmann void qemu_spice_display_stop(void)
9527cc6a25fSGerd Hoffmann {
95383f71802SMarc-André Lureau     if (!spice_display_is_running) {
95483f71802SMarc-André Lureau         return;
95583f71802SMarc-André Lureau     }
95683f71802SMarc-André Lureau 
957b50f3e42SGerd Hoffmann     spice_server_vm_stop(spice_server);
9587cc6a25fSGerd Hoffmann     spice_display_is_running = false;
9597cc6a25fSGerd Hoffmann }
9607cc6a25fSGerd Hoffmann 
qemu_spice_display_is_running(SimpleSpiceDisplay * ssd)9617cc6a25fSGerd Hoffmann int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd)
9627cc6a25fSGerd Hoffmann {
9637cc6a25fSGerd Hoffmann     return spice_display_is_running;
9647cc6a25fSGerd Hoffmann }
9657cc6a25fSGerd Hoffmann 
9667477477cSGerd Hoffmann static struct QemuSpiceOps real_spice_ops = {
96763be30e6SGerd Hoffmann     .init         = qemu_spice_init,
968b192cd1eSGerd Hoffmann     .display_init = qemu_spice_display_init,
9697477477cSGerd Hoffmann     .migrate_info = qemu_spice_migrate_info,
97008ad2626SGerd Hoffmann     .set_passwd   = qemu_spice_set_passwd,
97108ad2626SGerd Hoffmann     .set_pw_expire = qemu_spice_set_pw_expire,
972864a024cSGerd Hoffmann     .display_add_client = qemu_spice_display_add_client,
97305b53636SGerd Hoffmann     .add_interface = qemu_spice_add_interface,
974db5732c9SGerd Hoffmann     .qmp_query = qmp_query_spice_real,
9757477477cSGerd Hoffmann };
9767477477cSGerd Hoffmann 
spice_register_config(void)97729b0040bSGerd Hoffmann static void spice_register_config(void)
97829b0040bSGerd Hoffmann {
9797477477cSGerd Hoffmann     qemu_spice = real_spice_ops;
98029b0040bSGerd Hoffmann     qemu_add_opts(&qemu_spice_opts);
98129b0040bSGerd Hoffmann }
98234294e2fSEduardo Habkost opts_init(spice_register_config);
983b36ae1c1SGerd Hoffmann module_opts("spice");
984b36ae1c1SGerd Hoffmann 
9859a6c69d3SAkihiko Odaki #ifdef HAVE_SPICE_GL
986b36ae1c1SGerd Hoffmann module_dep("ui-opengl");
987b36ae1c1SGerd Hoffmann #endif
988