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> 22d0638b18SMarc-André Lureau #include "sysemu.h" 236f8c63fbSGerd Hoffmann 2429b0040bSGerd Hoffmann #include "qemu-common.h" 2528ecbaeeSPaolo Bonzini #include "ui/qemu-spice.h" 26f9ab6091SJan Kiszka #include "qemu-thread.h" 2729b0040bSGerd Hoffmann #include "qemu-timer.h" 2829b0040bSGerd Hoffmann #include "qemu-queue.h" 29c448e855SGerd Hoffmann #include "qemu-x509.h" 306f8c63fbSGerd Hoffmann #include "qemu_socket.h" 31d1f29646SLuiz Capitulino #include "qmp-commands.h" 327b1b5d19SPaolo Bonzini #include "qapi/qmp/qint.h" 337b1b5d19SPaolo Bonzini #include "qapi/qmp/qbool.h" 347b1b5d19SPaolo Bonzini #include "qapi/qmp/qstring.h" 357b1b5d19SPaolo Bonzini #include "qapi/qmp/qjson.h" 36e866e239SGerd Hoffmann #include "notify.h" 37*caf71f86SPaolo Bonzini #include "migration/migration.h" 3883c9089eSPaolo Bonzini #include "monitor/monitor.h" 39e866e239SGerd Hoffmann #include "hw/hw.h" 4028ecbaeeSPaolo Bonzini #include "ui/spice-display.h" 4129b0040bSGerd Hoffmann 4229b0040bSGerd Hoffmann /* core bits */ 4329b0040bSGerd Hoffmann 4429b0040bSGerd Hoffmann static SpiceServer *spice_server; 45e866e239SGerd Hoffmann static Notifier migration_state; 466f8c63fbSGerd Hoffmann static const char *auth = "spice"; 477572150cSGerd Hoffmann static char *auth_passwd; 487572150cSGerd Hoffmann static time_t auth_expires = TIME_MAX; 4961c4efe2SYonit Halperin static int spice_migration_completed; 5029b0040bSGerd Hoffmann int using_spice = 0; 5129b0040bSGerd Hoffmann 52f9ab6091SJan Kiszka static QemuThread me; 5322b626e2SGerd Hoffmann 5429b0040bSGerd Hoffmann struct SpiceTimer { 5529b0040bSGerd Hoffmann QEMUTimer *timer; 5629b0040bSGerd Hoffmann QTAILQ_ENTRY(SpiceTimer) next; 5729b0040bSGerd Hoffmann }; 5829b0040bSGerd Hoffmann static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers); 5929b0040bSGerd Hoffmann 6029b0040bSGerd Hoffmann static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque) 6129b0040bSGerd Hoffmann { 6229b0040bSGerd Hoffmann SpiceTimer *timer; 6329b0040bSGerd Hoffmann 647267c094SAnthony Liguori timer = g_malloc0(sizeof(*timer)); 657bd427d8SPaolo Bonzini timer->timer = qemu_new_timer_ms(rt_clock, func, opaque); 6629b0040bSGerd Hoffmann QTAILQ_INSERT_TAIL(&timers, timer, next); 6729b0040bSGerd Hoffmann return timer; 6829b0040bSGerd Hoffmann } 6929b0040bSGerd Hoffmann 7029b0040bSGerd Hoffmann static void timer_start(SpiceTimer *timer, uint32_t ms) 7129b0040bSGerd Hoffmann { 727bd427d8SPaolo Bonzini qemu_mod_timer(timer->timer, qemu_get_clock_ms(rt_clock) + ms); 7329b0040bSGerd Hoffmann } 7429b0040bSGerd Hoffmann 7529b0040bSGerd Hoffmann static void timer_cancel(SpiceTimer *timer) 7629b0040bSGerd Hoffmann { 7729b0040bSGerd Hoffmann qemu_del_timer(timer->timer); 7829b0040bSGerd Hoffmann } 7929b0040bSGerd Hoffmann 8029b0040bSGerd Hoffmann static void timer_remove(SpiceTimer *timer) 8129b0040bSGerd Hoffmann { 8229b0040bSGerd Hoffmann qemu_del_timer(timer->timer); 8329b0040bSGerd Hoffmann qemu_free_timer(timer->timer); 8429b0040bSGerd Hoffmann QTAILQ_REMOVE(&timers, timer, next); 857267c094SAnthony Liguori g_free(timer); 8629b0040bSGerd Hoffmann } 8729b0040bSGerd Hoffmann 8829b0040bSGerd Hoffmann struct SpiceWatch { 8929b0040bSGerd Hoffmann int fd; 9029b0040bSGerd Hoffmann int event_mask; 9129b0040bSGerd Hoffmann SpiceWatchFunc func; 9229b0040bSGerd Hoffmann void *opaque; 9329b0040bSGerd Hoffmann QTAILQ_ENTRY(SpiceWatch) next; 9429b0040bSGerd Hoffmann }; 9529b0040bSGerd Hoffmann static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches); 9629b0040bSGerd Hoffmann 9729b0040bSGerd Hoffmann static void watch_read(void *opaque) 9829b0040bSGerd Hoffmann { 9929b0040bSGerd Hoffmann SpiceWatch *watch = opaque; 10029b0040bSGerd Hoffmann watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque); 10129b0040bSGerd Hoffmann } 10229b0040bSGerd Hoffmann 10329b0040bSGerd Hoffmann static void watch_write(void *opaque) 10429b0040bSGerd Hoffmann { 10529b0040bSGerd Hoffmann SpiceWatch *watch = opaque; 10629b0040bSGerd Hoffmann watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque); 10729b0040bSGerd Hoffmann } 10829b0040bSGerd Hoffmann 10929b0040bSGerd Hoffmann static void watch_update_mask(SpiceWatch *watch, int event_mask) 11029b0040bSGerd Hoffmann { 11129b0040bSGerd Hoffmann IOHandler *on_read = NULL; 11229b0040bSGerd Hoffmann IOHandler *on_write = NULL; 11329b0040bSGerd Hoffmann 11429b0040bSGerd Hoffmann watch->event_mask = event_mask; 11529b0040bSGerd Hoffmann if (watch->event_mask & SPICE_WATCH_EVENT_READ) { 11629b0040bSGerd Hoffmann on_read = watch_read; 11729b0040bSGerd Hoffmann } 11829b0040bSGerd Hoffmann if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) { 1193d6d306cSHans de Goede on_write = watch_write; 12029b0040bSGerd Hoffmann } 12129b0040bSGerd Hoffmann qemu_set_fd_handler(watch->fd, on_read, on_write, watch); 12229b0040bSGerd Hoffmann } 12329b0040bSGerd Hoffmann 12429b0040bSGerd Hoffmann static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) 12529b0040bSGerd Hoffmann { 12629b0040bSGerd Hoffmann SpiceWatch *watch; 12729b0040bSGerd Hoffmann 1287267c094SAnthony Liguori watch = g_malloc0(sizeof(*watch)); 12929b0040bSGerd Hoffmann watch->fd = fd; 13029b0040bSGerd Hoffmann watch->func = func; 13129b0040bSGerd Hoffmann watch->opaque = opaque; 13229b0040bSGerd Hoffmann QTAILQ_INSERT_TAIL(&watches, watch, next); 13329b0040bSGerd Hoffmann 13429b0040bSGerd Hoffmann watch_update_mask(watch, event_mask); 13529b0040bSGerd Hoffmann return watch; 13629b0040bSGerd Hoffmann } 13729b0040bSGerd Hoffmann 13829b0040bSGerd Hoffmann static void watch_remove(SpiceWatch *watch) 13929b0040bSGerd Hoffmann { 14008cc67f3SGerd Hoffmann qemu_set_fd_handler(watch->fd, NULL, NULL, NULL); 14129b0040bSGerd Hoffmann QTAILQ_REMOVE(&watches, watch, next); 1427267c094SAnthony Liguori g_free(watch); 14329b0040bSGerd Hoffmann } 14429b0040bSGerd Hoffmann 145cb42a870SGerd Hoffmann typedef struct ChannelList ChannelList; 146cb42a870SGerd Hoffmann struct ChannelList { 147cb42a870SGerd Hoffmann SpiceChannelEventInfo *info; 148cb42a870SGerd Hoffmann QTAILQ_ENTRY(ChannelList) link; 149cb42a870SGerd Hoffmann }; 150cb42a870SGerd Hoffmann static QTAILQ_HEAD(, ChannelList) channel_list = QTAILQ_HEAD_INITIALIZER(channel_list); 151cb42a870SGerd Hoffmann 152cb42a870SGerd Hoffmann static void channel_list_add(SpiceChannelEventInfo *info) 153cb42a870SGerd Hoffmann { 154cb42a870SGerd Hoffmann ChannelList *item; 155cb42a870SGerd Hoffmann 1567267c094SAnthony Liguori item = g_malloc0(sizeof(*item)); 157cb42a870SGerd Hoffmann item->info = info; 158cb42a870SGerd Hoffmann QTAILQ_INSERT_TAIL(&channel_list, item, link); 159cb42a870SGerd Hoffmann } 160cb42a870SGerd Hoffmann 161cb42a870SGerd Hoffmann static void channel_list_del(SpiceChannelEventInfo *info) 162cb42a870SGerd Hoffmann { 163cb42a870SGerd Hoffmann ChannelList *item; 164cb42a870SGerd Hoffmann 165cb42a870SGerd Hoffmann QTAILQ_FOREACH(item, &channel_list, link) { 166cb42a870SGerd Hoffmann if (item->info != info) { 167cb42a870SGerd Hoffmann continue; 168cb42a870SGerd Hoffmann } 169cb42a870SGerd Hoffmann QTAILQ_REMOVE(&channel_list, item, link); 1707267c094SAnthony Liguori g_free(item); 171cb42a870SGerd Hoffmann return; 172cb42a870SGerd Hoffmann } 173cb42a870SGerd Hoffmann } 174cb42a870SGerd Hoffmann 1756f8c63fbSGerd Hoffmann static void add_addr_info(QDict *dict, struct sockaddr *addr, int len) 1766f8c63fbSGerd Hoffmann { 1776f8c63fbSGerd Hoffmann char host[NI_MAXHOST], port[NI_MAXSERV]; 1786f8c63fbSGerd Hoffmann const char *family; 1796f8c63fbSGerd Hoffmann 1806f8c63fbSGerd Hoffmann getnameinfo(addr, len, host, sizeof(host), port, sizeof(port), 1816f8c63fbSGerd Hoffmann NI_NUMERICHOST | NI_NUMERICSERV); 1826f8c63fbSGerd Hoffmann family = inet_strfamily(addr->sa_family); 1836f8c63fbSGerd Hoffmann 1846f8c63fbSGerd Hoffmann qdict_put(dict, "host", qstring_from_str(host)); 1856f8c63fbSGerd Hoffmann qdict_put(dict, "port", qstring_from_str(port)); 1866f8c63fbSGerd Hoffmann qdict_put(dict, "family", qstring_from_str(family)); 1876f8c63fbSGerd Hoffmann } 1886f8c63fbSGerd Hoffmann 1896f8c63fbSGerd Hoffmann static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info) 1906f8c63fbSGerd Hoffmann { 1916f8c63fbSGerd Hoffmann int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS; 1926f8c63fbSGerd Hoffmann 1936f8c63fbSGerd Hoffmann qdict_put(dict, "connection-id", qint_from_int(info->connection_id)); 1946f8c63fbSGerd Hoffmann qdict_put(dict, "channel-type", qint_from_int(info->type)); 1956f8c63fbSGerd Hoffmann qdict_put(dict, "channel-id", qint_from_int(info->id)); 1966f8c63fbSGerd Hoffmann qdict_put(dict, "tls", qbool_from_int(tls)); 1976f8c63fbSGerd Hoffmann } 1986f8c63fbSGerd Hoffmann 1996f8c63fbSGerd Hoffmann static void channel_event(int event, SpiceChannelEventInfo *info) 2006f8c63fbSGerd Hoffmann { 2016f8c63fbSGerd Hoffmann static const int qevent[] = { 2026f8c63fbSGerd Hoffmann [ SPICE_CHANNEL_EVENT_CONNECTED ] = QEVENT_SPICE_CONNECTED, 2036f8c63fbSGerd Hoffmann [ SPICE_CHANNEL_EVENT_INITIALIZED ] = QEVENT_SPICE_INITIALIZED, 2046f8c63fbSGerd Hoffmann [ SPICE_CHANNEL_EVENT_DISCONNECTED ] = QEVENT_SPICE_DISCONNECTED, 2056f8c63fbSGerd Hoffmann }; 2066f8c63fbSGerd Hoffmann QDict *server, *client; 2076f8c63fbSGerd Hoffmann QObject *data; 2086f8c63fbSGerd Hoffmann 20922b626e2SGerd Hoffmann /* 21022b626e2SGerd Hoffmann * Spice server might have called us from spice worker thread 21122b626e2SGerd Hoffmann * context (happens on display channel disconnects). Spice should 21222b626e2SGerd Hoffmann * not do that. It isn't that easy to fix it in spice and even 21322b626e2SGerd Hoffmann * when it is fixed we still should cover the already released 21422b626e2SGerd Hoffmann * spice versions. So detect that we've been called from another 21522b626e2SGerd Hoffmann * thread and grab the iothread lock if so before calling qemu 21622b626e2SGerd Hoffmann * functions. 21722b626e2SGerd Hoffmann */ 218f9ab6091SJan Kiszka bool need_lock = !qemu_thread_is_self(&me); 21922b626e2SGerd Hoffmann if (need_lock) { 22022b626e2SGerd Hoffmann qemu_mutex_lock_iothread(); 22122b626e2SGerd Hoffmann } 22222b626e2SGerd Hoffmann 2236f8c63fbSGerd Hoffmann client = qdict_new(); 2246f8c63fbSGerd Hoffmann server = qdict_new(); 225faa98223SYonit Halperin 226faa98223SYonit Halperin if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) { 227faa98223SYonit Halperin add_addr_info(client, (struct sockaddr *)&info->paddr_ext, 228faa98223SYonit Halperin info->plen_ext); 229faa98223SYonit Halperin add_addr_info(server, (struct sockaddr *)&info->laddr_ext, 230faa98223SYonit Halperin info->llen_ext); 231faa98223SYonit Halperin } else { 232339a475fSChristophe Fergeau error_report("spice: %s, extended address is expected", 233faa98223SYonit Halperin __func__); 234faa98223SYonit Halperin } 2356f8c63fbSGerd Hoffmann 2366f8c63fbSGerd Hoffmann if (event == SPICE_CHANNEL_EVENT_INITIALIZED) { 2376f8c63fbSGerd Hoffmann qdict_put(server, "auth", qstring_from_str(auth)); 2386f8c63fbSGerd Hoffmann add_channel_info(client, info); 239cb42a870SGerd Hoffmann channel_list_add(info); 240cb42a870SGerd Hoffmann } 241cb42a870SGerd Hoffmann if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) { 242cb42a870SGerd Hoffmann channel_list_del(info); 2436f8c63fbSGerd Hoffmann } 2446f8c63fbSGerd Hoffmann 2456f8c63fbSGerd Hoffmann data = qobject_from_jsonf("{ 'client': %p, 'server': %p }", 2466f8c63fbSGerd Hoffmann QOBJECT(client), QOBJECT(server)); 2476f8c63fbSGerd Hoffmann monitor_protocol_event(qevent[event], data); 2486f8c63fbSGerd Hoffmann qobject_decref(data); 24922b626e2SGerd Hoffmann 25022b626e2SGerd Hoffmann if (need_lock) { 25122b626e2SGerd Hoffmann qemu_mutex_unlock_iothread(); 25222b626e2SGerd Hoffmann } 2536f8c63fbSGerd Hoffmann } 2546f8c63fbSGerd Hoffmann 25529b0040bSGerd Hoffmann static SpiceCoreInterface core_interface = { 25629b0040bSGerd Hoffmann .base.type = SPICE_INTERFACE_CORE, 25729b0040bSGerd Hoffmann .base.description = "qemu core services", 25829b0040bSGerd Hoffmann .base.major_version = SPICE_INTERFACE_CORE_MAJOR, 25929b0040bSGerd Hoffmann .base.minor_version = SPICE_INTERFACE_CORE_MINOR, 26029b0040bSGerd Hoffmann 26129b0040bSGerd Hoffmann .timer_add = timer_add, 26229b0040bSGerd Hoffmann .timer_start = timer_start, 26329b0040bSGerd Hoffmann .timer_cancel = timer_cancel, 26429b0040bSGerd Hoffmann .timer_remove = timer_remove, 26529b0040bSGerd Hoffmann 26629b0040bSGerd Hoffmann .watch_add = watch_add, 26729b0040bSGerd Hoffmann .watch_update_mask = watch_update_mask, 26829b0040bSGerd Hoffmann .watch_remove = watch_remove, 2696f8c63fbSGerd Hoffmann 2706f8c63fbSGerd Hoffmann .channel_event = channel_event, 27129b0040bSGerd Hoffmann }; 27229b0040bSGerd Hoffmann 273026f773fSYonit Halperin typedef struct SpiceMigration { 274026f773fSYonit Halperin SpiceMigrateInstance sin; 275026f773fSYonit Halperin struct { 276026f773fSYonit Halperin MonitorCompletion *cb; 277026f773fSYonit Halperin void *opaque; 278026f773fSYonit Halperin } connect_complete; 279026f773fSYonit Halperin } SpiceMigration; 280026f773fSYonit Halperin 281026f773fSYonit Halperin static void migrate_connect_complete_cb(SpiceMigrateInstance *sin); 2822fdd16e2SYonit Halperin static void migrate_end_complete_cb(SpiceMigrateInstance *sin); 283026f773fSYonit Halperin 284026f773fSYonit Halperin static const SpiceMigrateInterface migrate_interface = { 285026f773fSYonit Halperin .base.type = SPICE_INTERFACE_MIGRATION, 286026f773fSYonit Halperin .base.description = "migration", 287026f773fSYonit Halperin .base.major_version = SPICE_INTERFACE_MIGRATION_MAJOR, 288026f773fSYonit Halperin .base.minor_version = SPICE_INTERFACE_MIGRATION_MINOR, 289026f773fSYonit Halperin .migrate_connect_complete = migrate_connect_complete_cb, 2902fdd16e2SYonit Halperin .migrate_end_complete = migrate_end_complete_cb, 291026f773fSYonit Halperin }; 292026f773fSYonit Halperin 293026f773fSYonit Halperin static SpiceMigration spice_migrate; 294026f773fSYonit Halperin 295026f773fSYonit Halperin static void migrate_connect_complete_cb(SpiceMigrateInstance *sin) 296026f773fSYonit Halperin { 297026f773fSYonit Halperin SpiceMigration *sm = container_of(sin, SpiceMigration, sin); 298026f773fSYonit Halperin if (sm->connect_complete.cb) { 299026f773fSYonit Halperin sm->connect_complete.cb(sm->connect_complete.opaque, NULL); 300026f773fSYonit Halperin } 301026f773fSYonit Halperin sm->connect_complete.cb = NULL; 302026f773fSYonit Halperin } 3032fdd16e2SYonit Halperin 3042fdd16e2SYonit Halperin static void migrate_end_complete_cb(SpiceMigrateInstance *sin) 3052fdd16e2SYonit Halperin { 3062fdd16e2SYonit Halperin monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL); 30761c4efe2SYonit Halperin spice_migration_completed = true; 3082fdd16e2SYonit Halperin } 309026f773fSYonit Halperin 3109f04e09eSYonit Halperin /* config string parsing */ 3119f04e09eSYonit Halperin 3129f04e09eSYonit Halperin static int name2enum(const char *string, const char *table[], int entries) 3139f04e09eSYonit Halperin { 3149f04e09eSYonit Halperin int i; 3159f04e09eSYonit Halperin 3169f04e09eSYonit Halperin if (string) { 3179f04e09eSYonit Halperin for (i = 0; i < entries; i++) { 3189f04e09eSYonit Halperin if (!table[i]) { 3199f04e09eSYonit Halperin continue; 3209f04e09eSYonit Halperin } 3219f04e09eSYonit Halperin if (strcmp(string, table[i]) != 0) { 3229f04e09eSYonit Halperin continue; 3239f04e09eSYonit Halperin } 3249f04e09eSYonit Halperin return i; 3259f04e09eSYonit Halperin } 3269f04e09eSYonit Halperin } 3279f04e09eSYonit Halperin return -1; 3289f04e09eSYonit Halperin } 3299f04e09eSYonit Halperin 3309f04e09eSYonit Halperin static int parse_name(const char *string, const char *optname, 3319f04e09eSYonit Halperin const char *table[], int entries) 3329f04e09eSYonit Halperin { 3339f04e09eSYonit Halperin int value = name2enum(string, table, entries); 3349f04e09eSYonit Halperin 3359f04e09eSYonit Halperin if (value != -1) { 3369f04e09eSYonit Halperin return value; 3379f04e09eSYonit Halperin } 338339a475fSChristophe Fergeau error_report("spice: invalid %s: %s", optname, string); 3399f04e09eSYonit Halperin exit(1); 3409f04e09eSYonit Halperin } 3419f04e09eSYonit Halperin 34284a23f25SGerd Hoffmann static const char *stream_video_names[] = { 34384a23f25SGerd Hoffmann [ SPICE_STREAM_VIDEO_OFF ] = "off", 34484a23f25SGerd Hoffmann [ SPICE_STREAM_VIDEO_ALL ] = "all", 34584a23f25SGerd Hoffmann [ SPICE_STREAM_VIDEO_FILTER ] = "filter", 34684a23f25SGerd Hoffmann }; 34784a23f25SGerd Hoffmann #define parse_stream_video(_name) \ 348835cab85SChristophe Fergeau parse_name(_name, "stream video control", \ 349835cab85SChristophe Fergeau stream_video_names, ARRAY_SIZE(stream_video_names)) 35084a23f25SGerd Hoffmann 3519f04e09eSYonit Halperin static const char *compression_names[] = { 3529f04e09eSYonit Halperin [ SPICE_IMAGE_COMPRESS_OFF ] = "off", 3539f04e09eSYonit Halperin [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz", 3549f04e09eSYonit Halperin [ SPICE_IMAGE_COMPRESS_AUTO_LZ ] = "auto_lz", 3559f04e09eSYonit Halperin [ SPICE_IMAGE_COMPRESS_QUIC ] = "quic", 3569f04e09eSYonit Halperin [ SPICE_IMAGE_COMPRESS_GLZ ] = "glz", 3579f04e09eSYonit Halperin [ SPICE_IMAGE_COMPRESS_LZ ] = "lz", 3589f04e09eSYonit Halperin }; 3599f04e09eSYonit Halperin #define parse_compression(_name) \ 3609f04e09eSYonit Halperin parse_name(_name, "image compression", \ 3619f04e09eSYonit Halperin compression_names, ARRAY_SIZE(compression_names)) 3629f04e09eSYonit Halperin 3639f04e09eSYonit Halperin static const char *wan_compression_names[] = { 3649f04e09eSYonit Halperin [ SPICE_WAN_COMPRESSION_AUTO ] = "auto", 3659f04e09eSYonit Halperin [ SPICE_WAN_COMPRESSION_NEVER ] = "never", 3669f04e09eSYonit Halperin [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always", 3679f04e09eSYonit Halperin }; 3689f04e09eSYonit Halperin #define parse_wan_compression(_name) \ 3699f04e09eSYonit Halperin parse_name(_name, "wan compression", \ 3709f04e09eSYonit Halperin wan_compression_names, ARRAY_SIZE(wan_compression_names)) 3719f04e09eSYonit Halperin 37229b0040bSGerd Hoffmann /* functions for the rest of qemu */ 37329b0040bSGerd Hoffmann 374d1f29646SLuiz Capitulino static SpiceChannelList *qmp_query_spice_channels(void) 375cb42a870SGerd Hoffmann { 376d1f29646SLuiz Capitulino SpiceChannelList *cur_item = NULL, *head = NULL; 377d1f29646SLuiz Capitulino ChannelList *item; 378cb42a870SGerd Hoffmann 379d1f29646SLuiz Capitulino QTAILQ_FOREACH(item, &channel_list, link) { 380d1f29646SLuiz Capitulino SpiceChannelList *chan; 381d1f29646SLuiz Capitulino char host[NI_MAXHOST], port[NI_MAXSERV]; 382faa98223SYonit Halperin struct sockaddr *paddr; 383faa98223SYonit Halperin socklen_t plen; 384cb42a870SGerd Hoffmann 385d1f29646SLuiz Capitulino chan = g_malloc0(sizeof(*chan)); 386d1f29646SLuiz Capitulino chan->value = g_malloc0(sizeof(*chan->value)); 387cb42a870SGerd Hoffmann 388faa98223SYonit Halperin if (item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) { 389faa98223SYonit Halperin paddr = (struct sockaddr *)&item->info->paddr_ext; 390faa98223SYonit Halperin plen = item->info->plen_ext; 391faa98223SYonit Halperin } else { 392faa98223SYonit Halperin paddr = &item->info->paddr; 393faa98223SYonit Halperin plen = item->info->plen; 394faa98223SYonit Halperin } 395faa98223SYonit Halperin 396faa98223SYonit Halperin getnameinfo(paddr, plen, 397d1f29646SLuiz Capitulino host, sizeof(host), port, sizeof(port), 398d1f29646SLuiz Capitulino NI_NUMERICHOST | NI_NUMERICSERV); 399d1f29646SLuiz Capitulino chan->value->host = g_strdup(host); 400d1f29646SLuiz Capitulino chan->value->port = g_strdup(port); 401faa98223SYonit Halperin chan->value->family = g_strdup(inet_strfamily(paddr->sa_family)); 402cb42a870SGerd Hoffmann 403d1f29646SLuiz Capitulino chan->value->connection_id = item->info->connection_id; 404d1f29646SLuiz Capitulino chan->value->channel_type = item->info->type; 405d1f29646SLuiz Capitulino chan->value->channel_id = item->info->id; 406d1f29646SLuiz Capitulino chan->value->tls = item->info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS; 407cb42a870SGerd Hoffmann 408d1f29646SLuiz Capitulino /* XXX: waiting for the qapi to support GSList */ 409d1f29646SLuiz Capitulino if (!cur_item) { 410d1f29646SLuiz Capitulino head = cur_item = chan; 411cb42a870SGerd Hoffmann } else { 412d1f29646SLuiz Capitulino cur_item->next = chan; 413d1f29646SLuiz Capitulino cur_item = chan; 414cb42a870SGerd Hoffmann } 415cb42a870SGerd Hoffmann } 416cb42a870SGerd Hoffmann 417d1f29646SLuiz Capitulino return head; 418d1f29646SLuiz Capitulino } 419d1f29646SLuiz Capitulino 420d1f29646SLuiz Capitulino SpiceInfo *qmp_query_spice(Error **errp) 421cb42a870SGerd Hoffmann { 422cb42a870SGerd Hoffmann QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); 423cb42a870SGerd Hoffmann int port, tls_port; 424d1f29646SLuiz Capitulino const char *addr; 425d1f29646SLuiz Capitulino SpiceInfo *info; 4268df0c7e8SAlon Levy char version_string[20]; /* 12 = |255.255.255\0| is the max */ 427cb42a870SGerd Hoffmann 428d1f29646SLuiz Capitulino info = g_malloc0(sizeof(*info)); 429d1f29646SLuiz Capitulino 4303bb781f3SAlon Levy if (!spice_server || !opts) { 431d1f29646SLuiz Capitulino info->enabled = false; 432d1f29646SLuiz Capitulino return info; 433cb42a870SGerd Hoffmann } 434cb42a870SGerd Hoffmann 435d1f29646SLuiz Capitulino info->enabled = true; 43661c4efe2SYonit Halperin info->migrated = spice_migration_completed; 437d1f29646SLuiz Capitulino 438cb42a870SGerd Hoffmann addr = qemu_opt_get(opts, "addr"); 439cb42a870SGerd Hoffmann port = qemu_opt_get_number(opts, "port", 0); 440cb42a870SGerd Hoffmann tls_port = qemu_opt_get_number(opts, "tls-port", 0); 441cb42a870SGerd Hoffmann 442d1f29646SLuiz Capitulino info->has_auth = true; 443d1f29646SLuiz Capitulino info->auth = g_strdup(auth); 444d1f29646SLuiz Capitulino 445d1f29646SLuiz Capitulino info->has_host = true; 446d1f29646SLuiz Capitulino info->host = g_strdup(addr ? addr : "0.0.0.0"); 447d1f29646SLuiz Capitulino 448d1f29646SLuiz Capitulino info->has_compiled_version = true; 4498df0c7e8SAlon Levy snprintf(version_string, sizeof(version_string), "%d.%d.%d", 4508df0c7e8SAlon Levy (SPICE_SERVER_VERSION & 0xff0000) >> 16, 4518df0c7e8SAlon Levy (SPICE_SERVER_VERSION & 0xff00) >> 8, 4528df0c7e8SAlon Levy SPICE_SERVER_VERSION & 0xff); 453d1f29646SLuiz Capitulino info->compiled_version = g_strdup(version_string); 454d1f29646SLuiz Capitulino 455cb42a870SGerd Hoffmann if (port) { 456d1f29646SLuiz Capitulino info->has_port = true; 457d1f29646SLuiz Capitulino info->port = port; 458cb42a870SGerd Hoffmann } 459cb42a870SGerd Hoffmann if (tls_port) { 460d1f29646SLuiz Capitulino info->has_tls_port = true; 461d1f29646SLuiz Capitulino info->tls_port = tls_port; 462cb42a870SGerd Hoffmann } 463cb42a870SGerd Hoffmann 4644efee029SAlon Levy info->mouse_mode = spice_server_is_server_mouse(spice_server) ? 4654efee029SAlon Levy SPICE_QUERY_MOUSE_MODE_SERVER : 4664efee029SAlon Levy SPICE_QUERY_MOUSE_MODE_CLIENT; 46767be6726SGerd Hoffmann 468d1f29646SLuiz Capitulino /* for compatibility with the original command */ 469d1f29646SLuiz Capitulino info->has_channels = true; 470d1f29646SLuiz Capitulino info->channels = qmp_query_spice_channels(); 471d1f29646SLuiz Capitulino 472d1f29646SLuiz Capitulino return info; 473cb42a870SGerd Hoffmann } 474cb42a870SGerd Hoffmann 4759e8dd451SJan Kiszka static void migration_state_notifier(Notifier *notifier, void *data) 476e866e239SGerd Hoffmann { 4777073693bSJuan Quintela MigrationState *s = data; 478e866e239SGerd Hoffmann 479026f773fSYonit Halperin if (migration_is_active(s)) { 480026f773fSYonit Halperin spice_server_migrate_start(spice_server); 481026f773fSYonit Halperin } else if (migration_has_finished(s)) { 482026f773fSYonit Halperin spice_server_migrate_end(spice_server, true); 483026f773fSYonit Halperin } else if (migration_has_failed(s)) { 484026f773fSYonit Halperin spice_server_migrate_end(spice_server, false); 485e866e239SGerd Hoffmann } 486e866e239SGerd Hoffmann } 487e866e239SGerd Hoffmann 488e866e239SGerd Hoffmann int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, 489edc5cb1aSYonit Halperin const char *subject, 490edc5cb1aSYonit Halperin MonitorCompletion *cb, void *opaque) 491e866e239SGerd Hoffmann { 492edc5cb1aSYonit Halperin int ret; 49367be6726SGerd Hoffmann 494026f773fSYonit Halperin spice_migrate.connect_complete.cb = cb; 495026f773fSYonit Halperin spice_migrate.connect_complete.opaque = opaque; 496026f773fSYonit Halperin ret = spice_server_migrate_connect(spice_server, hostname, 497026f773fSYonit Halperin port, tls_port, subject); 498edc5cb1aSYonit Halperin return ret; 499e866e239SGerd Hoffmann } 500e866e239SGerd Hoffmann 50117b6dea0SGerd Hoffmann static int add_channel(const char *name, const char *value, void *opaque) 50217b6dea0SGerd Hoffmann { 50317b6dea0SGerd Hoffmann int security = 0; 50417b6dea0SGerd Hoffmann int rc; 50517b6dea0SGerd Hoffmann 50617b6dea0SGerd Hoffmann if (strcmp(name, "tls-channel") == 0) { 50735c63329SChristophe Fergeau int *tls_port = opaque; 50835c63329SChristophe Fergeau if (!*tls_port) { 50935c63329SChristophe Fergeau error_report("spice: tried to setup tls-channel" 51035c63329SChristophe Fergeau " without specifying a TLS port"); 51135c63329SChristophe Fergeau exit(1); 51235c63329SChristophe Fergeau } 51317b6dea0SGerd Hoffmann security = SPICE_CHANNEL_SECURITY_SSL; 51417b6dea0SGerd Hoffmann } 51517b6dea0SGerd Hoffmann if (strcmp(name, "plaintext-channel") == 0) { 51617b6dea0SGerd Hoffmann security = SPICE_CHANNEL_SECURITY_NONE; 51717b6dea0SGerd Hoffmann } 51817b6dea0SGerd Hoffmann if (security == 0) { 51917b6dea0SGerd Hoffmann return 0; 52017b6dea0SGerd Hoffmann } 52117b6dea0SGerd Hoffmann if (strcmp(value, "default") == 0) { 52217b6dea0SGerd Hoffmann rc = spice_server_set_channel_security(spice_server, NULL, security); 52317b6dea0SGerd Hoffmann } else { 52417b6dea0SGerd Hoffmann rc = spice_server_set_channel_security(spice_server, value, security); 52517b6dea0SGerd Hoffmann } 52617b6dea0SGerd Hoffmann if (rc != 0) { 527339a475fSChristophe Fergeau error_report("spice: failed to set channel security for %s", value); 52817b6dea0SGerd Hoffmann exit(1); 52917b6dea0SGerd Hoffmann } 53017b6dea0SGerd Hoffmann return 0; 53117b6dea0SGerd Hoffmann } 53217b6dea0SGerd Hoffmann 533f5bb039cSYonit Halperin static void vm_change_state_handler(void *opaque, int running, 534f5bb039cSYonit Halperin RunState state) 535f5bb039cSYonit Halperin { 536f5bb039cSYonit Halperin if (running) { 53771d388d4SYonit Halperin qemu_spice_display_start(); 538f5bb039cSYonit Halperin spice_server_vm_start(spice_server); 539f5bb039cSYonit Halperin } else { 540f5bb039cSYonit Halperin spice_server_vm_stop(spice_server); 54171d388d4SYonit Halperin qemu_spice_display_stop(); 542f5bb039cSYonit Halperin } 543f5bb039cSYonit Halperin } 544f5bb039cSYonit Halperin 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; 5588c957053SYonit Halperin bool seamless_migration; 55929b0040bSGerd Hoffmann 560f9ab6091SJan Kiszka qemu_thread_get_self(&me); 56122b626e2SGerd Hoffmann 56229b0040bSGerd Hoffmann if (!opts) { 56329b0040bSGerd Hoffmann return; 56429b0040bSGerd Hoffmann } 56529b0040bSGerd Hoffmann port = qemu_opt_get_number(opts, "port", 0); 566c448e855SGerd Hoffmann tls_port = qemu_opt_get_number(opts, "tls-port", 0); 567c448e855SGerd Hoffmann if (!port && !tls_port) { 568339a475fSChristophe Fergeau error_report("neither port nor tls-port specified for spice"); 569df9cb669SGerd Hoffmann exit(1); 570df9cb669SGerd Hoffmann } 571df9cb669SGerd Hoffmann if (port < 0 || port > 65535) { 572339a475fSChristophe Fergeau error_report("spice port is out of range"); 573df9cb669SGerd Hoffmann exit(1); 574df9cb669SGerd Hoffmann } 575df9cb669SGerd Hoffmann if (tls_port < 0 || tls_port > 65535) { 576339a475fSChristophe Fergeau error_report("spice tls-port is out of range"); 577df9cb669SGerd Hoffmann exit(1); 57829b0040bSGerd Hoffmann } 57929b0040bSGerd Hoffmann password = qemu_opt_get(opts, "password"); 58029b0040bSGerd Hoffmann 581c448e855SGerd Hoffmann if (tls_port) { 582c448e855SGerd Hoffmann x509_dir = qemu_opt_get(opts, "x509-dir"); 583c448e855SGerd Hoffmann if (NULL == x509_dir) { 584c448e855SGerd Hoffmann x509_dir = "."; 585c448e855SGerd Hoffmann } 586c448e855SGerd Hoffmann len = strlen(x509_dir) + 32; 587c448e855SGerd Hoffmann 588c448e855SGerd Hoffmann str = qemu_opt_get(opts, "x509-key-file"); 589c448e855SGerd Hoffmann if (str) { 5907267c094SAnthony Liguori x509_key_file = g_strdup(str); 591c448e855SGerd Hoffmann } else { 5927267c094SAnthony Liguori x509_key_file = g_malloc(len); 593c448e855SGerd Hoffmann snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE); 594c448e855SGerd Hoffmann } 595c448e855SGerd Hoffmann 596c448e855SGerd Hoffmann str = qemu_opt_get(opts, "x509-cert-file"); 597c448e855SGerd Hoffmann if (str) { 5987267c094SAnthony Liguori x509_cert_file = g_strdup(str); 599c448e855SGerd Hoffmann } else { 6007267c094SAnthony Liguori x509_cert_file = g_malloc(len); 601c448e855SGerd Hoffmann snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE); 602c448e855SGerd Hoffmann } 603c448e855SGerd Hoffmann 604c448e855SGerd Hoffmann str = qemu_opt_get(opts, "x509-cacert-file"); 605c448e855SGerd Hoffmann if (str) { 6067267c094SAnthony Liguori x509_cacert_file = g_strdup(str); 607c448e855SGerd Hoffmann } else { 6087267c094SAnthony Liguori x509_cacert_file = g_malloc(len); 609c448e855SGerd Hoffmann snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE); 610c448e855SGerd Hoffmann } 611c448e855SGerd Hoffmann 612c448e855SGerd Hoffmann x509_key_password = qemu_opt_get(opts, "x509-key-password"); 6139995c0b7SLei Li x509_dh_file = qemu_opt_get(opts, "x509-dh-key-file"); 614c448e855SGerd Hoffmann tls_ciphers = qemu_opt_get(opts, "tls-ciphers"); 615c448e855SGerd Hoffmann } 616c448e855SGerd Hoffmann 617333b0eebSGerd Hoffmann addr = qemu_opt_get(opts, "addr"); 618333b0eebSGerd Hoffmann addr_flags = 0; 619333b0eebSGerd Hoffmann if (qemu_opt_get_bool(opts, "ipv4", 0)) { 620333b0eebSGerd Hoffmann addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY; 621333b0eebSGerd Hoffmann } else if (qemu_opt_get_bool(opts, "ipv6", 0)) { 622333b0eebSGerd Hoffmann addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY; 623333b0eebSGerd Hoffmann } 624333b0eebSGerd Hoffmann 62529b0040bSGerd Hoffmann spice_server = spice_server_new(); 626333b0eebSGerd Hoffmann spice_server_set_addr(spice_server, addr ? addr : "", addr_flags); 627c448e855SGerd Hoffmann if (port) { 62829b0040bSGerd Hoffmann spice_server_set_port(spice_server, port); 629c448e855SGerd Hoffmann } 630c448e855SGerd Hoffmann if (tls_port) { 631c448e855SGerd Hoffmann spice_server_set_tls(spice_server, tls_port, 632c448e855SGerd Hoffmann x509_cacert_file, 633c448e855SGerd Hoffmann x509_cert_file, 634c448e855SGerd Hoffmann x509_key_file, 635c448e855SGerd Hoffmann x509_key_password, 636c448e855SGerd Hoffmann x509_dh_file, 637c448e855SGerd Hoffmann tls_ciphers); 638c448e855SGerd Hoffmann } 63929b0040bSGerd Hoffmann if (password) { 64029b0040bSGerd Hoffmann spice_server_set_ticket(spice_server, password, 0, 0, 0); 64129b0040bSGerd Hoffmann } 64248b3ed0aSMarc-André Lureau if (qemu_opt_get_bool(opts, "sasl", 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) { 645339a475fSChristophe Fergeau error_report("spice: failed to enable sasl"); 64648b3ed0aSMarc-André Lureau exit(1); 64748b3ed0aSMarc-André Lureau } 64848b3ed0aSMarc-André Lureau } 64929b0040bSGerd Hoffmann if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) { 6506f8c63fbSGerd Hoffmann auth = "none"; 65129b0040bSGerd Hoffmann spice_server_set_noauth(spice_server); 65229b0040bSGerd Hoffmann } 65329b0040bSGerd Hoffmann 654d4970b07SHans de Goede if (qemu_opt_get_bool(opts, "disable-copy-paste", 0)) { 655d4970b07SHans de Goede spice_server_set_agent_copypaste(spice_server, false); 656d4970b07SHans de Goede } 657d4970b07SHans de Goede 6589f04e09eSYonit Halperin compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ; 6599f04e09eSYonit Halperin str = qemu_opt_get(opts, "image-compression"); 6609f04e09eSYonit Halperin if (str) { 6619f04e09eSYonit Halperin compression = parse_compression(str); 6629f04e09eSYonit Halperin } 6639f04e09eSYonit Halperin spice_server_set_image_compression(spice_server, compression); 6649f04e09eSYonit Halperin 6659f04e09eSYonit Halperin wan_compr = SPICE_WAN_COMPRESSION_AUTO; 6669f04e09eSYonit Halperin str = qemu_opt_get(opts, "jpeg-wan-compression"); 6679f04e09eSYonit Halperin if (str) { 6689f04e09eSYonit Halperin wan_compr = parse_wan_compression(str); 6699f04e09eSYonit Halperin } 6709f04e09eSYonit Halperin spice_server_set_jpeg_compression(spice_server, wan_compr); 6719f04e09eSYonit Halperin 6729f04e09eSYonit Halperin wan_compr = SPICE_WAN_COMPRESSION_AUTO; 6739f04e09eSYonit Halperin str = qemu_opt_get(opts, "zlib-glz-wan-compression"); 6749f04e09eSYonit Halperin if (str) { 6759f04e09eSYonit Halperin wan_compr = parse_wan_compression(str); 6769f04e09eSYonit Halperin } 6779f04e09eSYonit Halperin spice_server_set_zlib_glz_compression(spice_server, wan_compr); 67829b0040bSGerd Hoffmann 67984a23f25SGerd Hoffmann str = qemu_opt_get(opts, "streaming-video"); 68084a23f25SGerd Hoffmann if (str) { 681f61d6960SGerd Hoffmann int streaming_video = parse_stream_video(str); 68284a23f25SGerd Hoffmann spice_server_set_streaming_video(spice_server, streaming_video); 68384a23f25SGerd Hoffmann } 68484a23f25SGerd Hoffmann 68584a23f25SGerd Hoffmann spice_server_set_agent_mouse 68684a23f25SGerd Hoffmann (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1)); 68784a23f25SGerd Hoffmann spice_server_set_playback_compression 68884a23f25SGerd Hoffmann (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1)); 68984a23f25SGerd Hoffmann 69035c63329SChristophe Fergeau qemu_opt_foreach(opts, add_channel, &tls_port, 0); 69117b6dea0SGerd Hoffmann 692d0638b18SMarc-André Lureau spice_server_set_name(spice_server, qemu_name); 693d0638b18SMarc-André Lureau spice_server_set_uuid(spice_server, qemu_uuid); 694d0638b18SMarc-André Lureau 6958c957053SYonit Halperin seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0); 6968c957053SYonit Halperin spice_server_set_seamless_migration(spice_server, seamless_migration); 697fba810f1SGerd Hoffmann if (0 != spice_server_init(spice_server, &core_interface)) { 698339a475fSChristophe Fergeau error_report("failed to initialize spice server"); 699fba810f1SGerd Hoffmann exit(1); 700fba810f1SGerd Hoffmann }; 70129b0040bSGerd Hoffmann using_spice = 1; 702864401c2SGerd Hoffmann 703e866e239SGerd Hoffmann migration_state.notify = migration_state_notifier; 704e866e239SGerd Hoffmann add_migration_state_change_notifier(&migration_state); 705026f773fSYonit Halperin spice_migrate.sin.base.sif = &migrate_interface.base; 706026f773fSYonit Halperin spice_migrate.connect_complete.cb = NULL; 707026f773fSYonit Halperin qemu_spice_add_interface(&spice_migrate.sin.base); 708e866e239SGerd Hoffmann 709864401c2SGerd Hoffmann qemu_spice_input_init(); 7103e313753SGerd Hoffmann qemu_spice_audio_init(); 711c448e855SGerd Hoffmann 712f5bb039cSYonit Halperin qemu_add_vm_change_state_handler(vm_change_state_handler, &spice_server); 713f5bb039cSYonit Halperin 7147267c094SAnthony Liguori g_free(x509_key_file); 7157267c094SAnthony Liguori g_free(x509_cert_file); 7167267c094SAnthony Liguori g_free(x509_cacert_file); 71729b0040bSGerd Hoffmann } 71829b0040bSGerd Hoffmann 71929b0040bSGerd Hoffmann int qemu_spice_add_interface(SpiceBaseInstance *sin) 72029b0040bSGerd Hoffmann { 721a19cbfb3SGerd Hoffmann if (!spice_server) { 722a19cbfb3SGerd Hoffmann if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) { 723339a475fSChristophe Fergeau error_report("Oops: spice configured but not active"); 724a19cbfb3SGerd Hoffmann exit(1); 725a19cbfb3SGerd Hoffmann } 726a19cbfb3SGerd Hoffmann /* 727a19cbfb3SGerd Hoffmann * Create a spice server instance. 728a19cbfb3SGerd Hoffmann * It does *not* listen on the network. 729a19cbfb3SGerd Hoffmann * It handles QXL local rendering only. 730a19cbfb3SGerd Hoffmann * 731a19cbfb3SGerd Hoffmann * With a command line like '-vnc :0 -vga qxl' you'll end up here. 732a19cbfb3SGerd Hoffmann */ 733a19cbfb3SGerd Hoffmann spice_server = spice_server_new(); 734a19cbfb3SGerd Hoffmann spice_server_init(spice_server, &core_interface); 735a19cbfb3SGerd Hoffmann } 73671d388d4SYonit Halperin 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 773f1f5f407SDaniel P. Berrange int qemu_spice_display_add_client(int csock, int skipauth, int tls) 774f1f5f407SDaniel P. Berrange { 775f1f5f407SDaniel P. Berrange if (tls) { 776f1f5f407SDaniel P. Berrange return spice_server_add_ssl_client(spice_server, csock, skipauth); 777f1f5f407SDaniel P. Berrange } else { 778f1f5f407SDaniel P. Berrange return spice_server_add_client(spice_server, csock, skipauth); 779f1f5f407SDaniel P. Berrange } 780f1f5f407SDaniel P. Berrange } 781f1f5f407SDaniel P. Berrange 78229b0040bSGerd Hoffmann static void spice_register_config(void) 78329b0040bSGerd Hoffmann { 78429b0040bSGerd Hoffmann qemu_add_opts(&qemu_spice_opts); 78529b0040bSGerd Hoffmann } 78629b0040bSGerd Hoffmann machine_init(spice_register_config); 787