1d514b0f3Smrg/*
2d514b0f3Smrg * Copyright 2011 Red Hat, Inc.
3d514b0f3Smrg *
4d514b0f3Smrg * Permission is hereby granted, free of charge, to any person obtaining a
5d514b0f3Smrg * copy of this software and associated documentation files (the "Software"),
6d514b0f3Smrg * to deal in the Software without restriction, including without limitation
7d514b0f3Smrg * on the rights to use, copy, modify, merge, publish, distribute, sub
8d514b0f3Smrg * license, and/or sell copies of the Software, and to permit persons to whom
9d514b0f3Smrg * the Software is furnished to do so, subject to the following conditions:
10d514b0f3Smrg *
11d514b0f3Smrg * The above copyright notice and this permission notice (including the next
12d514b0f3Smrg * paragraph) shall be included in all copies or substantial portions of the
13d514b0f3Smrg * Software.
14d514b0f3Smrg *
15d514b0f3Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16d514b0f3Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17d514b0f3Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
18d514b0f3Smrg * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19d514b0f3Smrg * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20d514b0f3Smrg * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21d514b0f3Smrg */
22d514b0f3Smrg
23d514b0f3Smrg/** \file spiceqxl_spice_server.c
24d514b0f3Smrg * \author Alon Levy <alevy@redhat.com>
25d514b0f3Smrg *
26d514b0f3Smrg * spice server helpers for spiceqxl.
27d514b0f3Smrg */
28d514b0f3Smrg#ifdef HAVE_CONFIG_H
29d514b0f3Smrg#include "config.h"
30d514b0f3Smrg#endif
31d514b0f3Smrg
32d514b0f3Smrg#include "qxl.h"
33d514b0f3Smrg#include "qxl_option_helpers.h"
34d514b0f3Smrg#include "spiceqxl_spice_server.h"
35d514b0f3Smrg
36d514b0f3Smrg/* Single instance of spice server per Xorg executable.
37d514b0f3Smrg */
38d514b0f3SmrgSpiceServer *xspice_get_spice_server(void)
39d514b0f3Smrg{
40d514b0f3Smrg    static SpiceServer *spice_server;
41d514b0f3Smrg    if (!spice_server) {
42d514b0f3Smrg        spice_server = spice_server_new();
43d514b0f3Smrg    }
44d514b0f3Smrg    return spice_server;
45d514b0f3Smrg}
46d514b0f3Smrg
47d514b0f3Smrg#define X509_CA_CERT_FILE "ca-cert.pem"
48d514b0f3Smrg#define X509_SERVER_KEY_FILE "server-key.pem"
49d514b0f3Smrg#define X509_SERVER_CERT_FILE "server-cert.pem"
50d514b0f3Smrg
51d514b0f3Smrg/* config string parsing */
52d514b0f3Smrg
53d514b0f3Smrg#define SPICE_ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
54d514b0f3Smrg
55d514b0f3Smrgstatic int name2enum(const char *string, const char *table[], int entries)
56d514b0f3Smrg{
57d514b0f3Smrg    int i;
58d514b0f3Smrg
59d514b0f3Smrg    if (string) {
60d514b0f3Smrg        for (i = 0; i < entries; i++) {
61d514b0f3Smrg            if (!table[i]) {
62d514b0f3Smrg                continue;
63d514b0f3Smrg            }
64d514b0f3Smrg            if (strcmp(string, table[i]) != 0) {
65d514b0f3Smrg                continue;
66d514b0f3Smrg            }
67d514b0f3Smrg            return i;
68d514b0f3Smrg        }
69d514b0f3Smrg    }
70d514b0f3Smrg    return -1;
71d514b0f3Smrg}
72d514b0f3Smrg
73d514b0f3Smrgstatic int parse_name(const char *string, const char *optname,
74d514b0f3Smrg                      const char *table[], int entries)
75d514b0f3Smrg{
76d514b0f3Smrg    int value = name2enum(string, table, entries);
77d514b0f3Smrg
78d514b0f3Smrg    if (value != -1) {
79d514b0f3Smrg        return value;
80d514b0f3Smrg    }
81d514b0f3Smrg    fprintf(stderr, "spice: invalid %s: %s\n", optname, string);
82d514b0f3Smrg    exit(1);
83d514b0f3Smrg}
84d514b0f3Smrg
85d514b0f3Smrgstatic const char *stream_video_names[] = {
86d514b0f3Smrg    [ SPICE_STREAM_VIDEO_OFF ]    = "off",
87d514b0f3Smrg    [ SPICE_STREAM_VIDEO_ALL ]    = "all",
88d514b0f3Smrg    [ SPICE_STREAM_VIDEO_FILTER ] = "filter",
89d514b0f3Smrg};
90d514b0f3Smrg#define parse_stream_video(_name) \
91d514b0f3Smrg    name2enum(_name, stream_video_names, SPICE_ARRAY_SIZE(stream_video_names))
92d514b0f3Smrg
93d514b0f3Smrgstatic const char *compression_names[] = {
94d514b0f3Smrg    [ SPICE_IMAGE_COMPRESS_OFF ]      = "off",
95d514b0f3Smrg    [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz",
96d514b0f3Smrg    [ SPICE_IMAGE_COMPRESS_AUTO_LZ ]  = "auto_lz",
97d514b0f3Smrg    [ SPICE_IMAGE_COMPRESS_QUIC ]     = "quic",
98d514b0f3Smrg    [ SPICE_IMAGE_COMPRESS_GLZ ]      = "glz",
99d514b0f3Smrg    [ SPICE_IMAGE_COMPRESS_LZ ]       = "lz",
100d514b0f3Smrg};
101d514b0f3Smrg#define parse_compression(_name)                                        \
102d514b0f3Smrg    parse_name(_name, "image compression",                              \
103d514b0f3Smrg               compression_names, SPICE_ARRAY_SIZE(compression_names))
104d514b0f3Smrg
105d514b0f3Smrgstatic const char *wan_compression_names[] = {
106d514b0f3Smrg    [ SPICE_WAN_COMPRESSION_AUTO   ] = "auto",
107d514b0f3Smrg    [ SPICE_WAN_COMPRESSION_NEVER  ] = "never",
108d514b0f3Smrg    [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always",
109d514b0f3Smrg};
110d514b0f3Smrg#define parse_wan_compression(_name)                                    \
111d514b0f3Smrg    parse_name(_name, "wan compression",                                \
112d514b0f3Smrg               wan_compression_names, SPICE_ARRAY_SIZE(wan_compression_names))
113d514b0f3Smrg
114d514b0f3Smrgvoid xspice_set_spice_server_options(OptionInfoPtr options)
115d514b0f3Smrg{
116d514b0f3Smrg    /* environment variables take precedence. If not then take
117d514b0f3Smrg     * parameters from the config file. */
118d514b0f3Smrg    int addr_flags;
119d514b0f3Smrg    int len;
120d514b0f3Smrg    spice_image_compression_t compression;
121d514b0f3Smrg    spice_wan_compression_t wan_compr;
122d514b0f3Smrg    int port = get_int_option(options, OPTION_SPICE_PORT, "XSPICE_PORT");
123d514b0f3Smrg    int tls_port =
124d514b0f3Smrg        get_int_option(options, OPTION_SPICE_TLS_PORT, "XSPICE_TLS_PORT");
125d514b0f3Smrg    const char *password =
126d514b0f3Smrg        get_str_option(options, OPTION_SPICE_PASSWORD, "XSPICE_PASSWORD");
127d514b0f3Smrg    int disable_ticketing =
128d514b0f3Smrg        get_bool_option(options, OPTION_SPICE_DISABLE_TICKETING, "XSPICE_DISABLE_TICKETING");
129d514b0f3Smrg    const char *x509_dir =
130d514b0f3Smrg        get_str_option(options, OPTION_SPICE_X509_DIR, "XSPICE_X509_DIR");
131d514b0f3Smrg    int sasl = get_bool_option(options, OPTION_SPICE_SASL, "XSPICE_SASL");
132d514b0f3Smrg    const char *x509_key_file_base =
133d514b0f3Smrg        get_str_option(options, OPTION_SPICE_X509_KEY_FILE,
134d514b0f3Smrg                       "XSPICE_X509_KEY_FILE");
135d514b0f3Smrg    char *x509_key_file = NULL;
136d514b0f3Smrg    const char *x509_cert_file_base =
137d514b0f3Smrg        get_str_option(options, OPTION_SPICE_X509_CERT_FILE,
138d514b0f3Smrg                       "XSPICE_X509_CERT_FILE");
139d514b0f3Smrg    char *x509_cert_file = NULL;
140d514b0f3Smrg    const char *x509_key_password =
141d514b0f3Smrg        get_str_option(options, OPTION_SPICE_X509_KEY_PASSWORD,
142d514b0f3Smrg                    "XSPICE_X509_KEY_PASSWORD");
143d514b0f3Smrg    const char *tls_ciphers =
144d514b0f3Smrg        get_str_option(options, OPTION_SPICE_TLS_CIPHERS,
145d514b0f3Smrg                    "XSPICE_TLS_CIPHERS");
146d514b0f3Smrg    const char *x509_cacert_file_base =
147d514b0f3Smrg        get_str_option(options, OPTION_SPICE_CACERT_FILE,
148d514b0f3Smrg                    "XSPICE_CACERT_FILE");
149d514b0f3Smrg    char *x509_cacert_file = NULL;
150d514b0f3Smrg    const char * addr =
151d514b0f3Smrg        get_str_option(options, OPTION_SPICE_ADDR, "XSPICE_ADDR");
152d514b0f3Smrg    int ipv4 =
153d514b0f3Smrg        get_bool_option(options, OPTION_SPICE_IPV4_ONLY, "XSPICE_IPV4_ONLY");
154d514b0f3Smrg    int ipv6 =
155d514b0f3Smrg        get_bool_option(options, OPTION_SPICE_IPV6_ONLY, "XSPICE_IPV6_ONLY");
156d514b0f3Smrg    const char *x509_dh_file =
157d514b0f3Smrg        get_str_option(options, OPTION_SPICE_DH_FILE, "XSPICE_DH_FILE");
158d514b0f3Smrg    int disable_copy_paste =
159d514b0f3Smrg        get_bool_option(options, OPTION_SPICE_DISABLE_COPY_PASTE,
160d514b0f3Smrg                        "XSPICE_DISABLE_COPY_PASTE");
161d514b0f3Smrg    int exit_on_disconnect =
162d514b0f3Smrg        get_bool_option(options, OPTION_SPICE_EXIT_ON_DISCONNECT,
163d514b0f3Smrg                        "XSPICE_EXIT_ON_DISCONNECT");
164d514b0f3Smrg    const char *image_compression =
165d514b0f3Smrg        get_str_option(options, OPTION_SPICE_IMAGE_COMPRESSION,
166d514b0f3Smrg                       "XSPICE_IMAGE_COMPRESSION");
167d514b0f3Smrg    const char *jpeg_wan_compression =
168d514b0f3Smrg        get_str_option(options, OPTION_SPICE_JPEG_WAN_COMPRESSION,
169d514b0f3Smrg                       "XSPICE_JPEG_WAN_COMPRESSION");
170d514b0f3Smrg    const char *zlib_glz_wan_compression =
171d514b0f3Smrg        get_str_option(options, OPTION_SPICE_ZLIB_GLZ_WAN_COMPRESSION,
172d514b0f3Smrg                       "XSPICE_ZLIB_GLZ_WAN_COMPRESSION");
173d514b0f3Smrg    const char *streaming_video =
174d514b0f3Smrg        get_str_option(options, OPTION_SPICE_STREAMING_VIDEO,
175d514b0f3Smrg                       "XSPICE_STREAMING_VIDEO");
176d514b0f3Smrg    const char *video_codecs =
177d514b0f3Smrg        get_str_option(options, OPTION_SPICE_VIDEO_CODECS,
178d514b0f3Smrg                       "XSPICE_VIDEO_CODECS");
179d514b0f3Smrg    int agent_mouse =
180d514b0f3Smrg        get_bool_option(options, OPTION_SPICE_AGENT_MOUSE,
181d514b0f3Smrg                        "XSPICE_AGENT_MOUSE");
182d514b0f3Smrg    int playback_compression =
183d514b0f3Smrg        get_bool_option(options, OPTION_SPICE_PLAYBACK_COMPRESSION,
184d514b0f3Smrg                        "XSPICE_PLAYBACK_COMPRESSION");
185d514b0f3Smrg
186d514b0f3Smrg    SpiceServer *spice_server = xspice_get_spice_server();
187d514b0f3Smrg
188d514b0f3Smrg    if (!port && !tls_port) {
189d514b0f3Smrg        printf("one of tls-port or port must be set\n");
190d514b0f3Smrg        exit(1);
191d514b0f3Smrg    }
192d514b0f3Smrg    printf("xspice: port = %d, tls_port = %d\n", port, tls_port);
193d514b0f3Smrg    if (disable_ticketing) {
194d514b0f3Smrg        spice_server_set_noauth(spice_server);
195d514b0f3Smrg    }
196d514b0f3Smrg    if (tls_port) {
197d514b0f3Smrg        if (NULL == x509_dir) {
198d514b0f3Smrg            x509_dir = ".";
199d514b0f3Smrg        }
200d514b0f3Smrg        len = strlen(x509_dir) + 32;
201d514b0f3Smrg
202d514b0f3Smrg        if (x509_key_file_base) {
203d514b0f3Smrg            x509_key_file = xnfstrdup(x509_key_file_base);
204d514b0f3Smrg        } else {
205d514b0f3Smrg            x509_key_file = xnfalloc(len);
206d514b0f3Smrg            snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE);
207d514b0f3Smrg        }
208d514b0f3Smrg
209d514b0f3Smrg        if (x509_cert_file_base) {
210d514b0f3Smrg            x509_cert_file = xnfstrdup(x509_cert_file_base);
211d514b0f3Smrg        } else {
212d514b0f3Smrg            x509_cert_file = xnfalloc(len);
213d514b0f3Smrg            snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE);
214d514b0f3Smrg        }
215d514b0f3Smrg
216d514b0f3Smrg        if (x509_cacert_file_base) {
217d514b0f3Smrg            x509_cacert_file = xnfstrdup(x509_cert_file_base);
218d514b0f3Smrg        } else {
219d514b0f3Smrg            x509_cacert_file = xnfalloc(len);
220d514b0f3Smrg            snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE);
221d514b0f3Smrg        }
222d514b0f3Smrg    }
223d514b0f3Smrg
224d514b0f3Smrg    addr_flags = 0;
225d514b0f3Smrg    if (ipv4) {
226d514b0f3Smrg        addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY;
227d514b0f3Smrg    } else if (ipv6) {
228d514b0f3Smrg        addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY;
229d514b0f3Smrg    }
230d514b0f3Smrg
231d514b0f3Smrg    spice_server_set_addr(spice_server, addr ? addr : "", addr_flags);
232d514b0f3Smrg    if (port) {
233d514b0f3Smrg        spice_server_set_port(spice_server, port);
234d514b0f3Smrg    }
235d514b0f3Smrg    if (tls_port) {
236d514b0f3Smrg        spice_server_set_tls(spice_server, tls_port,
237d514b0f3Smrg                             x509_cacert_file,
238d514b0f3Smrg                             x509_cert_file,
239d514b0f3Smrg                             x509_key_file,
240d514b0f3Smrg                             x509_key_password,
241d514b0f3Smrg                             x509_dh_file,
242d514b0f3Smrg                             tls_ciphers);
243d514b0f3Smrg    }
244d514b0f3Smrg    if (password) {
245d514b0f3Smrg        spice_server_set_ticket(spice_server, password, 0, 0, 0);
246d514b0f3Smrg    }
247d514b0f3Smrg    if (sasl) {
248d514b0f3Smrg#if SPICE_SERVER_VERSION >= 0x000802 /* 0.8.2 */
249d514b0f3Smrg        if (spice_server_set_sasl_appname(spice_server, "xspice") == -1 ||
250d514b0f3Smrg            spice_server_set_sasl(spice_server, 1) == -1) {
251d514b0f3Smrg            fprintf(stderr, "spice: failed to enable sasl\n");
252d514b0f3Smrg            exit(1);
253d514b0f3Smrg        }
254d514b0f3Smrg#else
255d514b0f3Smrg        fprintf(stderr, "spice: sasl is not available (spice >= 0.8.2 required)\n");
256d514b0f3Smrg        exit(1);
257d514b0f3Smrg#endif
258d514b0f3Smrg    }
259d514b0f3Smrg
260d514b0f3Smrg#if SPICE_SERVER_VERSION >= 0x000801
261d514b0f3Smrg    /* we still don't actually support agent in xspice, but this
262d514b0f3Smrg     * can't hurt for later, just copied from qemn/ui/spice-core.c */
263d514b0f3Smrg    if (disable_copy_paste) {
264d514b0f3Smrg        spice_server_set_agent_copypaste(spice_server, 0);
265d514b0f3Smrg    }
266d514b0f3Smrg#endif
267d514b0f3Smrg
268d514b0f3Smrg    if (exit_on_disconnect) {
269d514b0f3Smrg#if SPICE_SERVER_VERSION >= 0x000b04 /* 0.11.4 */
270d514b0f3Smrg        spice_server_set_exit_on_disconnect(spice_server, exit_on_disconnect);
271d514b0f3Smrg#else
272d514b0f3Smrg        fprintf(stderr, "spice: cannot set exit_on_disconnect (spice >= 0.11.4 required)\n");
273d514b0f3Smrg        exit(1);
274d514b0f3Smrg#endif
275d514b0f3Smrg    }
276d514b0f3Smrg
277d514b0f3Smrg    compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ;
278d514b0f3Smrg    if (image_compression) {
279d514b0f3Smrg        compression = parse_compression(image_compression);
280d514b0f3Smrg    }
281d514b0f3Smrg    spice_server_set_image_compression(spice_server, compression);
282d514b0f3Smrg
283d514b0f3Smrg    wan_compr = SPICE_WAN_COMPRESSION_AUTO;
284d514b0f3Smrg    if (jpeg_wan_compression) {
285d514b0f3Smrg        wan_compr = parse_wan_compression(jpeg_wan_compression);
286d514b0f3Smrg    }
287d514b0f3Smrg    spice_server_set_jpeg_compression(spice_server, wan_compr);
288d514b0f3Smrg
289d514b0f3Smrg    wan_compr = SPICE_WAN_COMPRESSION_AUTO;
290d514b0f3Smrg    if (zlib_glz_wan_compression) {
291d514b0f3Smrg        wan_compr = parse_wan_compression(zlib_glz_wan_compression);
292d514b0f3Smrg    }
293d514b0f3Smrg    spice_server_set_zlib_glz_compression(spice_server, wan_compr);
294d514b0f3Smrg
295d514b0f3Smrg    if (streaming_video) {
296d514b0f3Smrg        int streaming_video_opt = parse_stream_video(streaming_video);
297d514b0f3Smrg        spice_server_set_streaming_video(spice_server, streaming_video_opt);
298d514b0f3Smrg    }
299d514b0f3Smrg
300d514b0f3Smrg    if (video_codecs) {
301d514b0f3Smrg#if SPICE_SERVER_VERSION >= 0x000d02 /* 0.13.2 */
302d514b0f3Smrg        if (spice_server_set_video_codecs(spice_server, video_codecs)) {
303d514b0f3Smrg            fprintf(stderr, "spice: invalid video encoder %s\n", video_codecs);
304d514b0f3Smrg            exit(1);
305d514b0f3Smrg        }
306d514b0f3Smrg#else
307d514b0f3Smrg        fprintf(stderr, "spice: video_codecs are not available (spice >= 0.13.2 required)\n");
308d514b0f3Smrg        exit(1);
309d514b0f3Smrg#endif
310d514b0f3Smrg    }
311d514b0f3Smrg
312d514b0f3Smrg    spice_server_set_agent_mouse(spice_server, agent_mouse);
313d514b0f3Smrg    spice_server_set_playback_compression(spice_server, playback_compression);
314d514b0f3Smrg
315d514b0f3Smrg    free(x509_key_file);
316d514b0f3Smrg    free(x509_cert_file);
317d514b0f3Smrg    free(x509_cacert_file);
318d514b0f3Smrg}
319