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