Statistics
| Branch: | Revision:

root / ui / spice-core.c @ a8a00822

History | View | Annotate | Download (21.9 kB)

1
/*
2
 * Copyright (C) 2010 Red Hat, Inc.
3
 *
4
 * This program is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU General Public License as
6
 * published by the Free Software Foundation; either version 2 or
7
 * (at your option) version 3 of the License.
8
 *
9
 * This program is distributed in the hope that it will be useful,
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 * GNU General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU General Public License
15
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
16
 */
17

    
18
#include <spice.h>
19
#include <spice-experimental.h>
20

    
21
#include <netdb.h>
22

    
23
#include "qemu-common.h"
24
#include "qemu-spice.h"
25
#include "qemu-thread.h"
26
#include "qemu-timer.h"
27
#include "qemu-queue.h"
28
#include "qemu-x509.h"
29
#include "qemu_socket.h"
30
#include "qmp-commands.h"
31
#include "qint.h"
32
#include "qbool.h"
33
#include "qstring.h"
34
#include "qjson.h"
35
#include "notify.h"
36
#include "migration.h"
37
#include "monitor.h"
38
#include "hw/hw.h"
39

    
40
/* core bits */
41

    
42
static SpiceServer *spice_server;
43
static Notifier migration_state;
44
static const char *auth = "spice";
45
static char *auth_passwd;
46
static time_t auth_expires = TIME_MAX;
47
int using_spice = 0;
48

    
49
static QemuThread me;
50

    
51
struct SpiceTimer {
52
    QEMUTimer *timer;
53
    QTAILQ_ENTRY(SpiceTimer) next;
54
};
55
static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers);
56

    
57
static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque)
58
{
59
    SpiceTimer *timer;
60

    
61
    timer = g_malloc0(sizeof(*timer));
62
    timer->timer = qemu_new_timer_ms(rt_clock, func, opaque);
63
    QTAILQ_INSERT_TAIL(&timers, timer, next);
64
    return timer;
65
}
66

    
67
static void timer_start(SpiceTimer *timer, uint32_t ms)
68
{
69
    qemu_mod_timer(timer->timer, qemu_get_clock_ms(rt_clock) + ms);
70
}
71

    
72
static void timer_cancel(SpiceTimer *timer)
73
{
74
    qemu_del_timer(timer->timer);
75
}
76

    
77
static void timer_remove(SpiceTimer *timer)
78
{
79
    qemu_del_timer(timer->timer);
80
    qemu_free_timer(timer->timer);
81
    QTAILQ_REMOVE(&timers, timer, next);
82
    g_free(timer);
83
}
84

    
85
struct SpiceWatch {
86
    int fd;
87
    int event_mask;
88
    SpiceWatchFunc func;
89
    void *opaque;
90
    QTAILQ_ENTRY(SpiceWatch) next;
91
};
92
static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches);
93

    
94
static void watch_read(void *opaque)
95
{
96
    SpiceWatch *watch = opaque;
97
    watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque);
98
}
99

    
100
static void watch_write(void *opaque)
101
{
102
    SpiceWatch *watch = opaque;
103
    watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque);
104
}
105

    
106
static void watch_update_mask(SpiceWatch *watch, int event_mask)
107
{
108
    IOHandler *on_read = NULL;
109
    IOHandler *on_write = NULL;
110

    
111
    watch->event_mask = event_mask;
112
    if (watch->event_mask & SPICE_WATCH_EVENT_READ) {
113
        on_read = watch_read;
114
    }
115
    if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) {
116
        on_write = watch_write;
117
    }
118
    qemu_set_fd_handler(watch->fd, on_read, on_write, watch);
119
}
120

    
121
static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque)
122
{
123
    SpiceWatch *watch;
124

    
125
    watch = g_malloc0(sizeof(*watch));
126
    watch->fd     = fd;
127
    watch->func   = func;
128
    watch->opaque = opaque;
129
    QTAILQ_INSERT_TAIL(&watches, watch, next);
130

    
131
    watch_update_mask(watch, event_mask);
132
    return watch;
133
}
134

    
135
static void watch_remove(SpiceWatch *watch)
136
{
137
    qemu_set_fd_handler(watch->fd, NULL, NULL, NULL);
138
    QTAILQ_REMOVE(&watches, watch, next);
139
    g_free(watch);
140
}
141

    
142
#if SPICE_INTERFACE_CORE_MINOR >= 3
143

    
144
typedef struct ChannelList ChannelList;
145
struct ChannelList {
146
    SpiceChannelEventInfo *info;
147
    QTAILQ_ENTRY(ChannelList) link;
148
};
149
static QTAILQ_HEAD(, ChannelList) channel_list = QTAILQ_HEAD_INITIALIZER(channel_list);
150

    
151
static void channel_list_add(SpiceChannelEventInfo *info)
152
{
153
    ChannelList *item;
154

    
155
    item = g_malloc0(sizeof(*item));
156
    item->info = info;
157
    QTAILQ_INSERT_TAIL(&channel_list, item, link);
158
}
159

    
160
static void channel_list_del(SpiceChannelEventInfo *info)
161
{
162
    ChannelList *item;
163

    
164
    QTAILQ_FOREACH(item, &channel_list, link) {
165
        if (item->info != info) {
166
            continue;
167
        }
168
        QTAILQ_REMOVE(&channel_list, item, link);
169
        g_free(item);
170
        return;
171
    }
172
}
173

    
174
static void add_addr_info(QDict *dict, struct sockaddr *addr, int len)
175
{
176
    char host[NI_MAXHOST], port[NI_MAXSERV];
177
    const char *family;
178

    
179
    getnameinfo(addr, len, host, sizeof(host), port, sizeof(port),
180
                NI_NUMERICHOST | NI_NUMERICSERV);
181
    family = inet_strfamily(addr->sa_family);
182

    
183
    qdict_put(dict, "host", qstring_from_str(host));
184
    qdict_put(dict, "port", qstring_from_str(port));
185
    qdict_put(dict, "family", qstring_from_str(family));
186
}
187

    
188
static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info)
189
{
190
    int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
191

    
192
    qdict_put(dict, "connection-id", qint_from_int(info->connection_id));
193
    qdict_put(dict, "channel-type", qint_from_int(info->type));
194
    qdict_put(dict, "channel-id", qint_from_int(info->id));
195
    qdict_put(dict, "tls", qbool_from_int(tls));
196
}
197

    
198
static void channel_event(int event, SpiceChannelEventInfo *info)
199
{
200
    static const int qevent[] = {
201
        [ SPICE_CHANNEL_EVENT_CONNECTED    ] = QEVENT_SPICE_CONNECTED,
202
        [ SPICE_CHANNEL_EVENT_INITIALIZED  ] = QEVENT_SPICE_INITIALIZED,
203
        [ SPICE_CHANNEL_EVENT_DISCONNECTED ] = QEVENT_SPICE_DISCONNECTED,
204
    };
205
    QDict *server, *client;
206
    QObject *data;
207

    
208
    /*
209
     * Spice server might have called us from spice worker thread
210
     * context (happens on display channel disconnects).  Spice should
211
     * not do that.  It isn't that easy to fix it in spice and even
212
     * when it is fixed we still should cover the already released
213
     * spice versions.  So detect that we've been called from another
214
     * thread and grab the iothread lock if so before calling qemu
215
     * functions.
216
     */
217
    bool need_lock = !qemu_thread_is_self(&me);
218
    if (need_lock) {
219
        qemu_mutex_lock_iothread();
220
    }
221

    
222
    client = qdict_new();
223
    add_addr_info(client, &info->paddr, info->plen);
224

    
225
    server = qdict_new();
226
    add_addr_info(server, &info->laddr, info->llen);
227

    
228
    if (event == SPICE_CHANNEL_EVENT_INITIALIZED) {
229
        qdict_put(server, "auth", qstring_from_str(auth));
230
        add_channel_info(client, info);
231
        channel_list_add(info);
232
    }
233
    if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) {
234
        channel_list_del(info);
235
    }
236

    
237
    data = qobject_from_jsonf("{ 'client': %p, 'server': %p }",
238
                              QOBJECT(client), QOBJECT(server));
239
    monitor_protocol_event(qevent[event], data);
240
    qobject_decref(data);
241

    
242
    if (need_lock) {
243
        qemu_mutex_unlock_iothread();
244
    }
245
}
246

    
247
#else /* SPICE_INTERFACE_CORE_MINOR >= 3 */
248

    
249
static QList *channel_list_get(void)
250
{
251
    return NULL;
252
}
253

    
254
#endif /* SPICE_INTERFACE_CORE_MINOR >= 3 */
255

    
256
static SpiceCoreInterface core_interface = {
257
    .base.type          = SPICE_INTERFACE_CORE,
258
    .base.description   = "qemu core services",
259
    .base.major_version = SPICE_INTERFACE_CORE_MAJOR,
260
    .base.minor_version = SPICE_INTERFACE_CORE_MINOR,
261

    
262
    .timer_add          = timer_add,
263
    .timer_start        = timer_start,
264
    .timer_cancel       = timer_cancel,
265
    .timer_remove       = timer_remove,
266

    
267
    .watch_add          = watch_add,
268
    .watch_update_mask  = watch_update_mask,
269
    .watch_remove       = watch_remove,
270

    
271
#if SPICE_INTERFACE_CORE_MINOR >= 3
272
    .channel_event      = channel_event,
273
#endif
274
};
275

    
276
#ifdef SPICE_INTERFACE_MIGRATION
277
typedef struct SpiceMigration {
278
    SpiceMigrateInstance sin;
279
    struct {
280
        MonitorCompletion *cb;
281
        void *opaque;
282
    } connect_complete;
283
} SpiceMigration;
284

    
285
static void migrate_connect_complete_cb(SpiceMigrateInstance *sin);
286

    
287
static const SpiceMigrateInterface migrate_interface = {
288
    .base.type = SPICE_INTERFACE_MIGRATION,
289
    .base.description = "migration",
290
    .base.major_version = SPICE_INTERFACE_MIGRATION_MAJOR,
291
    .base.minor_version = SPICE_INTERFACE_MIGRATION_MINOR,
292
    .migrate_connect_complete = migrate_connect_complete_cb,
293
    .migrate_end_complete = NULL,
294
};
295

    
296
static SpiceMigration spice_migrate;
297

    
298
static void migrate_connect_complete_cb(SpiceMigrateInstance *sin)
299
{
300
    SpiceMigration *sm = container_of(sin, SpiceMigration, sin);
301
    if (sm->connect_complete.cb) {
302
        sm->connect_complete.cb(sm->connect_complete.opaque, NULL);
303
    }
304
    sm->connect_complete.cb = NULL;
305
}
306
#endif
307

    
308
/* config string parsing */
309

    
310
static int name2enum(const char *string, const char *table[], int entries)
311
{
312
    int i;
313

    
314
    if (string) {
315
        for (i = 0; i < entries; i++) {
316
            if (!table[i]) {
317
                continue;
318
            }
319
            if (strcmp(string, table[i]) != 0) {
320
                continue;
321
            }
322
            return i;
323
        }
324
    }
325
    return -1;
326
}
327

    
328
static int parse_name(const char *string, const char *optname,
329
                      const char *table[], int entries)
330
{
331
    int value = name2enum(string, table, entries);
332

    
333
    if (value != -1) {
334
        return value;
335
    }
336
    fprintf(stderr, "spice: invalid %s: %s\n", optname, string);
337
    exit(1);
338
}
339

    
340
static const char *stream_video_names[] = {
341
    [ SPICE_STREAM_VIDEO_OFF ]    = "off",
342
    [ SPICE_STREAM_VIDEO_ALL ]    = "all",
343
    [ SPICE_STREAM_VIDEO_FILTER ] = "filter",
344
};
345
#define parse_stream_video(_name) \
346
    name2enum(_name, stream_video_names, ARRAY_SIZE(stream_video_names))
347

    
348
static const char *compression_names[] = {
349
    [ SPICE_IMAGE_COMPRESS_OFF ]      = "off",
350
    [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz",
351
    [ SPICE_IMAGE_COMPRESS_AUTO_LZ ]  = "auto_lz",
352
    [ SPICE_IMAGE_COMPRESS_QUIC ]     = "quic",
353
    [ SPICE_IMAGE_COMPRESS_GLZ ]      = "glz",
354
    [ SPICE_IMAGE_COMPRESS_LZ ]       = "lz",
355
};
356
#define parse_compression(_name)                                        \
357
    parse_name(_name, "image compression",                              \
358
               compression_names, ARRAY_SIZE(compression_names))
359

    
360
static const char *wan_compression_names[] = {
361
    [ SPICE_WAN_COMPRESSION_AUTO   ] = "auto",
362
    [ SPICE_WAN_COMPRESSION_NEVER  ] = "never",
363
    [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always",
364
};
365
#define parse_wan_compression(_name)                                    \
366
    parse_name(_name, "wan compression",                                \
367
               wan_compression_names, ARRAY_SIZE(wan_compression_names))
368

    
369
/* functions for the rest of qemu */
370

    
371
static SpiceChannelList *qmp_query_spice_channels(void)
372
{
373
    SpiceChannelList *cur_item = NULL, *head = NULL;
374
    ChannelList *item;
375

    
376
    QTAILQ_FOREACH(item, &channel_list, link) {
377
        SpiceChannelList *chan;
378
        char host[NI_MAXHOST], port[NI_MAXSERV];
379

    
380
        chan = g_malloc0(sizeof(*chan));
381
        chan->value = g_malloc0(sizeof(*chan->value));
382

    
383
        getnameinfo(&item->info->paddr, item->info->plen,
384
                    host, sizeof(host), port, sizeof(port),
385
                    NI_NUMERICHOST | NI_NUMERICSERV);
386
        chan->value->host = g_strdup(host);
387
        chan->value->port = g_strdup(port);
388
        chan->value->family = g_strdup(inet_strfamily(item->info->paddr.sa_family));
389

    
390
        chan->value->connection_id = item->info->connection_id;
391
        chan->value->channel_type = item->info->type;
392
        chan->value->channel_id = item->info->id;
393
        chan->value->tls = item->info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS;
394

    
395
       /* XXX: waiting for the qapi to support GSList */
396
        if (!cur_item) {
397
            head = cur_item = chan;
398
        } else {
399
            cur_item->next = chan;
400
            cur_item = chan;
401
        }
402
    }
403

    
404
    return head;
405
}
406

    
407
SpiceInfo *qmp_query_spice(Error **errp)
408
{
409
    QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
410
    int port, tls_port;
411
    const char *addr;
412
    SpiceInfo *info;
413
    char version_string[20]; /* 12 = |255.255.255\0| is the max */
414

    
415
    info = g_malloc0(sizeof(*info));
416

    
417
    if (!spice_server || !opts) {
418
        info->enabled = false;
419
        return info;
420
    }
421

    
422
    info->enabled = true;
423

    
424
    addr = qemu_opt_get(opts, "addr");
425
    port = qemu_opt_get_number(opts, "port", 0);
426
    tls_port = qemu_opt_get_number(opts, "tls-port", 0);
427

    
428
    info->has_auth = true;
429
    info->auth = g_strdup(auth);
430

    
431
    info->has_host = true;
432
    info->host = g_strdup(addr ? addr : "0.0.0.0");
433

    
434
    info->has_compiled_version = true;
435
    snprintf(version_string, sizeof(version_string), "%d.%d.%d",
436
             (SPICE_SERVER_VERSION & 0xff0000) >> 16,
437
             (SPICE_SERVER_VERSION & 0xff00) >> 8,
438
             SPICE_SERVER_VERSION & 0xff);
439
    info->compiled_version = g_strdup(version_string);
440

    
441
    if (port) {
442
        info->has_port = true;
443
        info->port = port;
444
    }
445
    if (tls_port) {
446
        info->has_tls_port = true;
447
        info->tls_port = tls_port;
448
    }
449

    
450
    /* for compatibility with the original command */
451
    info->has_channels = true;
452
    info->channels = qmp_query_spice_channels();
453

    
454
    return info;
455
}
456

    
457
static void migration_state_notifier(Notifier *notifier, void *data)
458
{
459
    MigrationState *s = data;
460

    
461
    if (migration_is_active(s)) {
462
#ifdef SPICE_INTERFACE_MIGRATION
463
        spice_server_migrate_start(spice_server);
464
#endif
465
    } else if (migration_has_finished(s)) {
466
#if SPICE_SERVER_VERSION >= 0x000701 /* 0.7.1 */
467
#ifndef SPICE_INTERFACE_MIGRATION
468
        spice_server_migrate_switch(spice_server);
469
#else
470
        spice_server_migrate_end(spice_server, true);
471
    } else if (migration_has_failed(s)) {
472
        spice_server_migrate_end(spice_server, false);
473
#endif
474
#endif
475
    }
476
}
477

    
478
int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,
479
                            const char *subject,
480
                            MonitorCompletion *cb, void *opaque)
481
{
482
    int ret;
483
#ifdef SPICE_INTERFACE_MIGRATION
484
    spice_migrate.connect_complete.cb = cb;
485
    spice_migrate.connect_complete.opaque = opaque;
486
    ret = spice_server_migrate_connect(spice_server, hostname,
487
                                       port, tls_port, subject);
488
#else
489
    ret = spice_server_migrate_info(spice_server, hostname,
490
                                    port, tls_port, subject);
491
    cb(opaque, NULL);
492
#endif
493
    return ret;
494
}
495

    
496
static int add_channel(const char *name, const char *value, void *opaque)
497
{
498
    int security = 0;
499
    int rc;
500

    
501
    if (strcmp(name, "tls-channel") == 0) {
502
        security = SPICE_CHANNEL_SECURITY_SSL;
503
    }
504
    if (strcmp(name, "plaintext-channel") == 0) {
505
        security = SPICE_CHANNEL_SECURITY_NONE;
506
    }
507
    if (security == 0) {
508
        return 0;
509
    }
510
    if (strcmp(value, "default") == 0) {
511
        rc = spice_server_set_channel_security(spice_server, NULL, security);
512
    } else {
513
        rc = spice_server_set_channel_security(spice_server, value, security);
514
    }
515
    if (rc != 0) {
516
        fprintf(stderr, "spice: failed to set channel security for %s\n", value);
517
        exit(1);
518
    }
519
    return 0;
520
}
521

    
522
void qemu_spice_init(void)
523
{
524
    QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head);
525
    const char *password, *str, *x509_dir, *addr,
526
        *x509_key_password = NULL,
527
        *x509_dh_file = NULL,
528
        *tls_ciphers = NULL;
529
    char *x509_key_file = NULL,
530
        *x509_cert_file = NULL,
531
        *x509_cacert_file = NULL;
532
    int port, tls_port, len, addr_flags;
533
    spice_image_compression_t compression;
534
    spice_wan_compression_t wan_compr;
535

    
536
    qemu_thread_get_self(&me);
537

    
538
   if (!opts) {
539
        return;
540
    }
541
    port = qemu_opt_get_number(opts, "port", 0);
542
    tls_port = qemu_opt_get_number(opts, "tls-port", 0);
543
    if (!port && !tls_port) {
544
        fprintf(stderr, "neither port nor tls-port specified for spice.");
545
        exit(1);
546
    }
547
    if (port < 0 || port > 65535) {
548
        fprintf(stderr, "spice port is out of range");
549
        exit(1);
550
    }
551
    if (tls_port < 0 || tls_port > 65535) {
552
        fprintf(stderr, "spice tls-port is out of range");
553
        exit(1);
554
    }
555
    password = qemu_opt_get(opts, "password");
556

    
557
    if (tls_port) {
558
        x509_dir = qemu_opt_get(opts, "x509-dir");
559
        if (NULL == x509_dir) {
560
            x509_dir = ".";
561
        }
562
        len = strlen(x509_dir) + 32;
563

    
564
        str = qemu_opt_get(opts, "x509-key-file");
565
        if (str) {
566
            x509_key_file = g_strdup(str);
567
        } else {
568
            x509_key_file = g_malloc(len);
569
            snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE);
570
        }
571

    
572
        str = qemu_opt_get(opts, "x509-cert-file");
573
        if (str) {
574
            x509_cert_file = g_strdup(str);
575
        } else {
576
            x509_cert_file = g_malloc(len);
577
            snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE);
578
        }
579

    
580
        str = qemu_opt_get(opts, "x509-cacert-file");
581
        if (str) {
582
            x509_cacert_file = g_strdup(str);
583
        } else {
584
            x509_cacert_file = g_malloc(len);
585
            snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE);
586
        }
587

    
588
        x509_key_password = qemu_opt_get(opts, "x509-key-password");
589
        x509_dh_file = qemu_opt_get(opts, "x509-dh-file");
590
        tls_ciphers = qemu_opt_get(opts, "tls-ciphers");
591
    }
592

    
593
    addr = qemu_opt_get(opts, "addr");
594
    addr_flags = 0;
595
    if (qemu_opt_get_bool(opts, "ipv4", 0)) {
596
        addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY;
597
    } else if (qemu_opt_get_bool(opts, "ipv6", 0)) {
598
        addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY;
599
    }
600

    
601
    spice_server = spice_server_new();
602
    spice_server_set_addr(spice_server, addr ? addr : "", addr_flags);
603
    if (port) {
604
        spice_server_set_port(spice_server, port);
605
    }
606
    if (tls_port) {
607
        spice_server_set_tls(spice_server, tls_port,
608
                             x509_cacert_file,
609
                             x509_cert_file,
610
                             x509_key_file,
611
                             x509_key_password,
612
                             x509_dh_file,
613
                             tls_ciphers);
614
    }
615
    if (password) {
616
        spice_server_set_ticket(spice_server, password, 0, 0, 0);
617
    }
618
    if (qemu_opt_get_bool(opts, "sasl", 0)) {
619
#if SPICE_SERVER_VERSION >= 0x000900 /* 0.9.0 */
620
        if (spice_server_set_sasl_appname(spice_server, "qemu") == -1 ||
621
            spice_server_set_sasl(spice_server, 1) == -1) {
622
            fprintf(stderr, "spice: failed to enable sasl\n");
623
            exit(1);
624
        }
625
#else
626
        fprintf(stderr, "spice: sasl is not available (spice >= 0.9 required)\n");
627
        exit(1);
628
#endif
629
    }
630
    if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) {
631
        auth = "none";
632
        spice_server_set_noauth(spice_server);
633
    }
634

    
635
#if SPICE_SERVER_VERSION >= 0x000801
636
    if (qemu_opt_get_bool(opts, "disable-copy-paste", 0)) {
637
        spice_server_set_agent_copypaste(spice_server, false);
638
    }
639
#endif
640

    
641
    compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ;
642
    str = qemu_opt_get(opts, "image-compression");
643
    if (str) {
644
        compression = parse_compression(str);
645
    }
646
    spice_server_set_image_compression(spice_server, compression);
647

    
648
    wan_compr = SPICE_WAN_COMPRESSION_AUTO;
649
    str = qemu_opt_get(opts, "jpeg-wan-compression");
650
    if (str) {
651
        wan_compr = parse_wan_compression(str);
652
    }
653
    spice_server_set_jpeg_compression(spice_server, wan_compr);
654

    
655
    wan_compr = SPICE_WAN_COMPRESSION_AUTO;
656
    str = qemu_opt_get(opts, "zlib-glz-wan-compression");
657
    if (str) {
658
        wan_compr = parse_wan_compression(str);
659
    }
660
    spice_server_set_zlib_glz_compression(spice_server, wan_compr);
661

    
662
    str = qemu_opt_get(opts, "streaming-video");
663
    if (str) {
664
        int streaming_video = parse_stream_video(str);
665
        spice_server_set_streaming_video(spice_server, streaming_video);
666
    }
667

    
668
    spice_server_set_agent_mouse
669
        (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1));
670
    spice_server_set_playback_compression
671
        (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1));
672

    
673
    qemu_opt_foreach(opts, add_channel, NULL, 0);
674

    
675
    if (0 != spice_server_init(spice_server, &core_interface)) {
676
        fprintf(stderr, "failed to initialize spice server");
677
        exit(1);
678
    };
679
    using_spice = 1;
680

    
681
    migration_state.notify = migration_state_notifier;
682
    add_migration_state_change_notifier(&migration_state);
683
#ifdef SPICE_INTERFACE_MIGRATION
684
    spice_migrate.sin.base.sif = &migrate_interface.base;
685
    spice_migrate.connect_complete.cb = NULL;
686
    qemu_spice_add_interface(&spice_migrate.sin.base);
687
#endif
688

    
689
    qemu_spice_input_init();
690
    qemu_spice_audio_init();
691

    
692
    g_free(x509_key_file);
693
    g_free(x509_cert_file);
694
    g_free(x509_cacert_file);
695
}
696

    
697
int qemu_spice_add_interface(SpiceBaseInstance *sin)
698
{
699
    if (!spice_server) {
700
        if (QTAILQ_FIRST(&qemu_spice_opts.head) != NULL) {
701
            fprintf(stderr, "Oops: spice configured but not active\n");
702
            exit(1);
703
        }
704
        /*
705
         * Create a spice server instance.
706
         * It does *not* listen on the network.
707
         * It handles QXL local rendering only.
708
         *
709
         * With a command line like '-vnc :0 -vga qxl' you'll end up here.
710
         */
711
        spice_server = spice_server_new();
712
        spice_server_init(spice_server, &core_interface);
713
    }
714
    return spice_server_add_interface(spice_server, sin);
715
}
716

    
717
static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn)
718
{
719
    time_t lifetime, now = time(NULL);
720
    char *passwd;
721

    
722
    if (now < auth_expires) {
723
        passwd = auth_passwd;
724
        lifetime = (auth_expires - now);
725
        if (lifetime > INT_MAX) {
726
            lifetime = INT_MAX;
727
        }
728
    } else {
729
        passwd = NULL;
730
        lifetime = 1;
731
    }
732
    return spice_server_set_ticket(spice_server, passwd, lifetime,
733
                                   fail_if_conn, disconnect_if_conn);
734
}
735

    
736
int qemu_spice_set_passwd(const char *passwd,
737
                          bool fail_if_conn, bool disconnect_if_conn)
738
{
739
    free(auth_passwd);
740
    auth_passwd = strdup(passwd);
741
    return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn);
742
}
743

    
744
int qemu_spice_set_pw_expire(time_t expires)
745
{
746
    auth_expires = expires;
747
    return qemu_spice_set_ticket(false, false);
748
}
749

    
750
static void spice_register_config(void)
751
{
752
    qemu_add_opts(&qemu_spice_opts);
753
}
754
machine_init(spice_register_config);
755

    
756
static void spice_initialize(void)
757
{
758
    qemu_spice_init();
759
}
760
device_init(spice_initialize);