Home | History | Annotate | Line # | Download | only in src
      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 (at) 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  */
     38 SpiceServer *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 
     55 static 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 
     73 static 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 
     85 static 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 
     93 static 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 
    105 static 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 
    114 void 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