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