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