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 "qemu-common.h" 22 #include "qemu-spice.h" 23 #include "qemu-timer.h" 24 #include "qemu-queue.h" 25 #include "qemu-x509.h" 26 #include "monitor.h" 27 28 /* core bits */ 29 30 static SpiceServer *spice_server; 31 int using_spice = 0; 32 33 struct SpiceTimer { 34 QEMUTimer *timer; 35 QTAILQ_ENTRY(SpiceTimer) next; 36 }; 37 static QTAILQ_HEAD(, SpiceTimer) timers = QTAILQ_HEAD_INITIALIZER(timers); 38 39 static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque) 40 { 41 SpiceTimer *timer; 42 43 timer = qemu_mallocz(sizeof(*timer)); 44 timer->timer = qemu_new_timer(rt_clock, func, opaque); 45 QTAILQ_INSERT_TAIL(&timers, timer, next); 46 return timer; 47 } 48 49 static void timer_start(SpiceTimer *timer, uint32_t ms) 50 { 51 qemu_mod_timer(timer->timer, qemu_get_clock(rt_clock) + ms); 52 } 53 54 static void timer_cancel(SpiceTimer *timer) 55 { 56 qemu_del_timer(timer->timer); 57 } 58 59 static void timer_remove(SpiceTimer *timer) 60 { 61 qemu_del_timer(timer->timer); 62 qemu_free_timer(timer->timer); 63 QTAILQ_REMOVE(&timers, timer, next); 64 qemu_free(timer); 65 } 66 67 struct SpiceWatch { 68 int fd; 69 int event_mask; 70 SpiceWatchFunc func; 71 void *opaque; 72 QTAILQ_ENTRY(SpiceWatch) next; 73 }; 74 static QTAILQ_HEAD(, SpiceWatch) watches = QTAILQ_HEAD_INITIALIZER(watches); 75 76 static void watch_read(void *opaque) 77 { 78 SpiceWatch *watch = opaque; 79 watch->func(watch->fd, SPICE_WATCH_EVENT_READ, watch->opaque); 80 } 81 82 static void watch_write(void *opaque) 83 { 84 SpiceWatch *watch = opaque; 85 watch->func(watch->fd, SPICE_WATCH_EVENT_WRITE, watch->opaque); 86 } 87 88 static void watch_update_mask(SpiceWatch *watch, int event_mask) 89 { 90 IOHandler *on_read = NULL; 91 IOHandler *on_write = NULL; 92 93 watch->event_mask = event_mask; 94 if (watch->event_mask & SPICE_WATCH_EVENT_READ) { 95 on_read = watch_read; 96 } 97 if (watch->event_mask & SPICE_WATCH_EVENT_WRITE) { 98 on_write = watch_write; 99 } 100 qemu_set_fd_handler(watch->fd, on_read, on_write, watch); 101 } 102 103 static SpiceWatch *watch_add(int fd, int event_mask, SpiceWatchFunc func, void *opaque) 104 { 105 SpiceWatch *watch; 106 107 watch = qemu_mallocz(sizeof(*watch)); 108 watch->fd = fd; 109 watch->func = func; 110 watch->opaque = opaque; 111 QTAILQ_INSERT_TAIL(&watches, watch, next); 112 113 watch_update_mask(watch, event_mask); 114 return watch; 115 } 116 117 static void watch_remove(SpiceWatch *watch) 118 { 119 watch_update_mask(watch, 0); 120 QTAILQ_REMOVE(&watches, watch, next); 121 qemu_free(watch); 122 } 123 124 static SpiceCoreInterface core_interface = { 125 .base.type = SPICE_INTERFACE_CORE, 126 .base.description = "qemu core services", 127 .base.major_version = SPICE_INTERFACE_CORE_MAJOR, 128 .base.minor_version = SPICE_INTERFACE_CORE_MINOR, 129 130 .timer_add = timer_add, 131 .timer_start = timer_start, 132 .timer_cancel = timer_cancel, 133 .timer_remove = timer_remove, 134 135 .watch_add = watch_add, 136 .watch_update_mask = watch_update_mask, 137 .watch_remove = watch_remove, 138 }; 139 140 /* config string parsing */ 141 142 static int name2enum(const char *string, const char *table[], int entries) 143 { 144 int i; 145 146 if (string) { 147 for (i = 0; i < entries; i++) { 148 if (!table[i]) { 149 continue; 150 } 151 if (strcmp(string, table[i]) != 0) { 152 continue; 153 } 154 return i; 155 } 156 } 157 return -1; 158 } 159 160 static int parse_name(const char *string, const char *optname, 161 const char *table[], int entries) 162 { 163 int value = name2enum(string, table, entries); 164 165 if (value != -1) { 166 return value; 167 } 168 fprintf(stderr, "spice: invalid %s: %s\n", optname, string); 169 exit(1); 170 } 171 172 #if SPICE_SERVER_VERSION >= 0x000600 /* 0.6.0 */ 173 174 static const char *stream_video_names[] = { 175 [ SPICE_STREAM_VIDEO_OFF ] = "off", 176 [ SPICE_STREAM_VIDEO_ALL ] = "all", 177 [ SPICE_STREAM_VIDEO_FILTER ] = "filter", 178 }; 179 #define parse_stream_video(_name) \ 180 name2enum(_name, stream_video_names, ARRAY_SIZE(stream_video_names)) 181 182 #endif /* >= 0.6.0 */ 183 184 static const char *compression_names[] = { 185 [ SPICE_IMAGE_COMPRESS_OFF ] = "off", 186 [ SPICE_IMAGE_COMPRESS_AUTO_GLZ ] = "auto_glz", 187 [ SPICE_IMAGE_COMPRESS_AUTO_LZ ] = "auto_lz", 188 [ SPICE_IMAGE_COMPRESS_QUIC ] = "quic", 189 [ SPICE_IMAGE_COMPRESS_GLZ ] = "glz", 190 [ SPICE_IMAGE_COMPRESS_LZ ] = "lz", 191 }; 192 #define parse_compression(_name) \ 193 parse_name(_name, "image compression", \ 194 compression_names, ARRAY_SIZE(compression_names)) 195 196 static const char *wan_compression_names[] = { 197 [ SPICE_WAN_COMPRESSION_AUTO ] = "auto", 198 [ SPICE_WAN_COMPRESSION_NEVER ] = "never", 199 [ SPICE_WAN_COMPRESSION_ALWAYS ] = "always", 200 }; 201 #define parse_wan_compression(_name) \ 202 parse_name(_name, "wan compression", \ 203 wan_compression_names, ARRAY_SIZE(wan_compression_names)) 204 205 /* functions for the rest of qemu */ 206 207 static int add_channel(const char *name, const char *value, void *opaque) 208 { 209 int security = 0; 210 int rc; 211 212 if (strcmp(name, "tls-channel") == 0) { 213 security = SPICE_CHANNEL_SECURITY_SSL; 214 } 215 if (strcmp(name, "plaintext-channel") == 0) { 216 security = SPICE_CHANNEL_SECURITY_NONE; 217 } 218 if (security == 0) { 219 return 0; 220 } 221 if (strcmp(value, "default") == 0) { 222 rc = spice_server_set_channel_security(spice_server, NULL, security); 223 } else { 224 rc = spice_server_set_channel_security(spice_server, value, security); 225 } 226 if (rc != 0) { 227 fprintf(stderr, "spice: failed to set channel security for %s\n", value); 228 exit(1); 229 } 230 return 0; 231 } 232 233 void qemu_spice_init(void) 234 { 235 QemuOpts *opts = QTAILQ_FIRST(&qemu_spice_opts.head); 236 const char *password, *str, *x509_dir, *addr, 237 *x509_key_password = NULL, 238 *x509_dh_file = NULL, 239 *tls_ciphers = NULL; 240 char *x509_key_file = NULL, 241 *x509_cert_file = NULL, 242 *x509_cacert_file = NULL; 243 int port, tls_port, len, addr_flags; 244 spice_image_compression_t compression; 245 spice_wan_compression_t wan_compr; 246 247 if (!opts) { 248 return; 249 } 250 port = qemu_opt_get_number(opts, "port", 0); 251 tls_port = qemu_opt_get_number(opts, "tls-port", 0); 252 if (!port && !tls_port) { 253 return; 254 } 255 password = qemu_opt_get(opts, "password"); 256 257 if (tls_port) { 258 x509_dir = qemu_opt_get(opts, "x509-dir"); 259 if (NULL == x509_dir) { 260 x509_dir = "."; 261 } 262 len = strlen(x509_dir) + 32; 263 264 str = qemu_opt_get(opts, "x509-key-file"); 265 if (str) { 266 x509_key_file = qemu_strdup(str); 267 } else { 268 x509_key_file = qemu_malloc(len); 269 snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE); 270 } 271 272 str = qemu_opt_get(opts, "x509-cert-file"); 273 if (str) { 274 x509_cert_file = qemu_strdup(str); 275 } else { 276 x509_cert_file = qemu_malloc(len); 277 snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE); 278 } 279 280 str = qemu_opt_get(opts, "x509-cacert-file"); 281 if (str) { 282 x509_cacert_file = qemu_strdup(str); 283 } else { 284 x509_cacert_file = qemu_malloc(len); 285 snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE); 286 } 287 288 x509_key_password = qemu_opt_get(opts, "x509-key-password"); 289 x509_dh_file = qemu_opt_get(opts, "x509-dh-file"); 290 tls_ciphers = qemu_opt_get(opts, "tls-ciphers"); 291 } 292 293 addr = qemu_opt_get(opts, "addr"); 294 addr_flags = 0; 295 if (qemu_opt_get_bool(opts, "ipv4", 0)) { 296 addr_flags |= SPICE_ADDR_FLAG_IPV4_ONLY; 297 } else if (qemu_opt_get_bool(opts, "ipv6", 0)) { 298 addr_flags |= SPICE_ADDR_FLAG_IPV6_ONLY; 299 } 300 301 spice_server = spice_server_new(); 302 spice_server_set_addr(spice_server, addr ? addr : "", addr_flags); 303 if (port) { 304 spice_server_set_port(spice_server, port); 305 } 306 if (tls_port) { 307 spice_server_set_tls(spice_server, tls_port, 308 x509_cacert_file, 309 x509_cert_file, 310 x509_key_file, 311 x509_key_password, 312 x509_dh_file, 313 tls_ciphers); 314 } 315 if (password) { 316 spice_server_set_ticket(spice_server, password, 0, 0, 0); 317 } 318 if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) { 319 spice_server_set_noauth(spice_server); 320 } 321 322 compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ; 323 str = qemu_opt_get(opts, "image-compression"); 324 if (str) { 325 compression = parse_compression(str); 326 } 327 spice_server_set_image_compression(spice_server, compression); 328 329 wan_compr = SPICE_WAN_COMPRESSION_AUTO; 330 str = qemu_opt_get(opts, "jpeg-wan-compression"); 331 if (str) { 332 wan_compr = parse_wan_compression(str); 333 } 334 spice_server_set_jpeg_compression(spice_server, wan_compr); 335 336 wan_compr = SPICE_WAN_COMPRESSION_AUTO; 337 str = qemu_opt_get(opts, "zlib-glz-wan-compression"); 338 if (str) { 339 wan_compr = parse_wan_compression(str); 340 } 341 spice_server_set_zlib_glz_compression(spice_server, wan_compr); 342 343 #if SPICE_SERVER_VERSION >= 0x000600 /* 0.6.0 */ 344 345 str = qemu_opt_get(opts, "streaming-video"); 346 if (str) { 347 int streaming_video = parse_stream_video(str); 348 spice_server_set_streaming_video(spice_server, streaming_video); 349 } 350 351 spice_server_set_agent_mouse 352 (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1)); 353 spice_server_set_playback_compression 354 (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1)); 355 356 #endif /* >= 0.6.0 */ 357 358 qemu_opt_foreach(opts, add_channel, NULL, 0); 359 360 spice_server_init(spice_server, &core_interface); 361 using_spice = 1; 362 363 qemu_spice_input_init(); 364 qemu_spice_audio_init(); 365 366 qemu_free(x509_key_file); 367 qemu_free(x509_cert_file); 368 qemu_free(x509_cacert_file); 369 } 370 371 int qemu_spice_add_interface(SpiceBaseInstance *sin) 372 { 373 return spice_server_add_interface(spice_server, sin); 374 } 375 376 static void spice_register_config(void) 377 { 378 qemu_add_opts(&qemu_spice_opts); 379 } 380 machine_init(spice_register_config); 381 382 static void spice_initialize(void) 383 { 384 qemu_spice_init(); 385 } 386 device_init(spice_initialize); 387