X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/bd4bd24ed3e33f4f0ffa9dde23b8b85430592dc6..c95f3901b4ead79f3fe2c641fda7d2c70fc84c72:/ui/spice-core.c diff --git a/ui/spice-core.c b/ui/spice-core.c index bcc4199e7a..fe705c1ae2 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -16,7 +16,6 @@ */ #include -#include #include #include "sysemu/sysemu.h" @@ -35,9 +34,9 @@ #include "qapi/qmp/qjson.h" #include "qemu/notify.h" #include "migration/migration.h" -#include "monitor/monitor.h" #include "hw/hw.h" #include "ui/spice-display.h" +#include "qapi-event.h" /* core bits */ @@ -47,6 +46,8 @@ static const char *auth = "spice"; static char *auth_passwd; static time_t auth_expires = TIME_MAX; static int spice_migration_completed; +static int spice_display_is_running; +static int spice_have_target_host; int using_spice = 0; static QemuThread me; @@ -62,25 +63,25 @@ static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque) SpiceTimer *timer; timer = g_malloc0(sizeof(*timer)); - timer->timer = qemu_new_timer_ms(rt_clock, func, opaque); + timer->timer = timer_new_ms(QEMU_CLOCK_REALTIME, func, opaque); QTAILQ_INSERT_TAIL(&timers, timer, next); return timer; } static void timer_start(SpiceTimer *timer, uint32_t ms) { - qemu_mod_timer(timer->timer, qemu_get_clock_ms(rt_clock) + ms); + timer_mod(timer->timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + ms); } static void timer_cancel(SpiceTimer *timer) { - qemu_del_timer(timer->timer); + timer_del(timer->timer); } static void timer_remove(SpiceTimer *timer) { - qemu_del_timer(timer->timer); - qemu_free_timer(timer->timer); + timer_del(timer->timer); + timer_free(timer->timer); QTAILQ_REMOVE(&timers, timer, next); g_free(timer); } @@ -172,39 +173,34 @@ static void channel_list_del(SpiceChannelEventInfo *info) } } -static void add_addr_info(QDict *dict, struct sockaddr *addr, int len) +static void add_addr_info(SpiceBasicInfo *info, struct sockaddr *addr, int len) { char host[NI_MAXHOST], port[NI_MAXSERV]; - const char *family; getnameinfo(addr, len, host, sizeof(host), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); - family = inet_strfamily(addr->sa_family); - qdict_put(dict, "host", qstring_from_str(host)); - qdict_put(dict, "port", qstring_from_str(port)); - qdict_put(dict, "family", qstring_from_str(family)); + info->host = g_strdup(host); + info->port = g_strdup(port); + info->family = inet_netfamily(addr->sa_family); } -static void add_channel_info(QDict *dict, SpiceChannelEventInfo *info) +static void add_channel_info(SpiceChannel *sc, SpiceChannelEventInfo *info) { int tls = info->flags & SPICE_CHANNEL_EVENT_FLAG_TLS; - qdict_put(dict, "connection-id", qint_from_int(info->connection_id)); - qdict_put(dict, "channel-type", qint_from_int(info->type)); - qdict_put(dict, "channel-id", qint_from_int(info->id)); - qdict_put(dict, "tls", qbool_from_int(tls)); + sc->connection_id = info->connection_id; + sc->channel_type = info->type; + sc->channel_id = info->id; + sc->tls = !!tls; } static void channel_event(int event, SpiceChannelEventInfo *info) { - static const int qevent[] = { - [ SPICE_CHANNEL_EVENT_CONNECTED ] = QEVENT_SPICE_CONNECTED, - [ SPICE_CHANNEL_EVENT_INITIALIZED ] = QEVENT_SPICE_INITIALIZED, - [ SPICE_CHANNEL_EVENT_DISCONNECTED ] = QEVENT_SPICE_DISCONNECTED, - }; - QDict *server, *client; - QObject *data; + SpiceServerInfo *server = g_malloc0(sizeof(*server)); + SpiceChannel *client = g_malloc0(sizeof(*client)); + server->base = g_malloc0(sizeof(*server->base)); + client->base = g_malloc0(sizeof(*client->base)); /* * Spice server might have called us from spice worker thread @@ -220,36 +216,43 @@ static void channel_event(int event, SpiceChannelEventInfo *info) qemu_mutex_lock_iothread(); } - client = qdict_new(); - server = qdict_new(); - if (info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) { - add_addr_info(client, (struct sockaddr *)&info->paddr_ext, + add_addr_info(client->base, (struct sockaddr *)&info->paddr_ext, info->plen_ext); - add_addr_info(server, (struct sockaddr *)&info->laddr_ext, + add_addr_info(server->base, (struct sockaddr *)&info->laddr_ext, info->llen_ext); } else { error_report("spice: %s, extended address is expected", __func__); } - if (event == SPICE_CHANNEL_EVENT_INITIALIZED) { - qdict_put(server, "auth", qstring_from_str(auth)); + switch (event) { + case SPICE_CHANNEL_EVENT_CONNECTED: + qapi_event_send_spice_connected(server->base, client->base, &error_abort); + break; + case SPICE_CHANNEL_EVENT_INITIALIZED: + if (auth) { + server->has_auth = true; + server->auth = g_strdup(auth); + } add_channel_info(client, info); channel_list_add(info); - } - if (event == SPICE_CHANNEL_EVENT_DISCONNECTED) { + qapi_event_send_spice_initialized(server, client, &error_abort); + break; + case SPICE_CHANNEL_EVENT_DISCONNECTED: channel_list_del(info); + qapi_event_send_spice_disconnected(server->base, client->base, &error_abort); + break; + default: + break; } - data = qobject_from_jsonf("{ 'client': %p, 'server': %p }", - QOBJECT(client), QOBJECT(server)); - monitor_protocol_event(qevent[event], data); - qobject_decref(data); - if (need_lock) { qemu_mutex_unlock_iothread(); } + + qapi_free_SpiceServerInfo(server); + qapi_free_SpiceChannel(client); } static SpiceCoreInterface core_interface = { @@ -303,7 +306,7 @@ static void migrate_connect_complete_cb(SpiceMigrateInstance *sin) static void migrate_end_complete_cb(SpiceMigrateInstance *sin) { - monitor_protocol_event(QEVENT_SPICE_MIGRATE_COMPLETED, NULL); + qapi_event_send_spice_migrate_completed(&error_abort); spice_migration_completed = true; } @@ -382,23 +385,20 @@ static SpiceChannelList *qmp_query_spice_channels(void) struct sockaddr *paddr; socklen_t plen; + assert(item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT); + chan = g_malloc0(sizeof(*chan)); chan->value = g_malloc0(sizeof(*chan->value)); + chan->value->base = g_malloc0(sizeof(*chan->value->base)); - if (item->info->flags & SPICE_CHANNEL_EVENT_FLAG_ADDR_EXT) { - paddr = (struct sockaddr *)&item->info->paddr_ext; - plen = item->info->plen_ext; - } else { - paddr = &item->info->paddr; - plen = item->info->plen; - } - + paddr = (struct sockaddr *)&item->info->paddr_ext; + plen = item->info->plen_ext; getnameinfo(paddr, plen, host, sizeof(host), port, sizeof(port), NI_NUMERICHOST | NI_NUMERICSERV); - chan->value->host = g_strdup(host); - chan->value->port = g_strdup(port); - chan->value->family = g_strdup(inet_strfamily(paddr->sa_family)); + chan->value->base->host = g_strdup(host); + chan->value->base->port = g_strdup(port); + chan->value->base->family = inet_netfamily(paddr->sa_family); chan->value->connection_id = item->info->connection_id; chan->value->channel_type = item->info->type; @@ -445,6 +445,9 @@ static QemuOptsList qemu_spice_opts = { },{ .name = "disable-copy-paste", .type = QEMU_OPT_BOOL, + },{ + .name = "disable-agent-file-xfer", + .type = QEMU_OPT_BOOL, },{ .name = "sasl", .type = QEMU_OPT_BOOL, @@ -507,7 +510,9 @@ SpiceInfo *qmp_query_spice(Error **errp) int port, tls_port; const char *addr; SpiceInfo *info; - char version_string[20]; /* 12 = |255.255.255\0| is the max */ + unsigned int major; + unsigned int minor; + unsigned int micro; info = g_malloc0(sizeof(*info)); @@ -527,14 +532,13 @@ SpiceInfo *qmp_query_spice(Error **errp) info->auth = g_strdup(auth); info->has_host = true; - info->host = g_strdup(addr ? addr : "0.0.0.0"); + info->host = g_strdup(addr ? addr : "*"); info->has_compiled_version = true; - snprintf(version_string, sizeof(version_string), "%d.%d.%d", - (SPICE_SERVER_VERSION & 0xff0000) >> 16, - (SPICE_SERVER_VERSION & 0xff00) >> 8, - SPICE_SERVER_VERSION & 0xff); - info->compiled_version = g_strdup(version_string); + major = (SPICE_SERVER_VERSION & 0xff0000) >> 16; + minor = (SPICE_SERVER_VERSION & 0xff00) >> 8; + micro = SPICE_SERVER_VERSION & 0xff; + info->compiled_version = g_strdup_printf("%d.%d.%d", major, minor, micro); if (port) { info->has_port = true; @@ -560,12 +564,18 @@ static void migration_state_notifier(Notifier *notifier, void *data) { MigrationState *s = data; - if (migration_is_active(s)) { + if (!spice_have_target_host) { + return; + } + + if (migration_in_setup(s)) { spice_server_migrate_start(spice_server); } else if (migration_has_finished(s)) { spice_server_migrate_end(spice_server, true); + spice_have_target_host = false; } else if (migration_has_failed(s)) { spice_server_migrate_end(spice_server, false); + spice_have_target_host = false; } } @@ -579,6 +589,7 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, spice_migrate.connect_complete.opaque = opaque; ret = spice_server_migrate_connect(spice_server, hostname, port, tls_port, subject); + spice_have_target_host = true; return ret; } @@ -619,9 +630,7 @@ static void vm_change_state_handler(void *opaque, int running, { if (running) { qemu_spice_display_start(); - spice_server_vm_start(spice_server); } else { - spice_server_vm_stop(spice_server); qemu_spice_display_stop(); } } @@ -636,7 +645,7 @@ void qemu_spice_init(void) char *x509_key_file = NULL, *x509_cert_file = NULL, *x509_cacert_file = NULL; - int port, tls_port, len, addr_flags; + int port, tls_port, addr_flags; spice_image_compression_t compression; spice_wan_compression_t wan_compr; bool seamless_migration; @@ -648,10 +657,6 @@ void qemu_spice_init(void) } port = qemu_opt_get_number(opts, "port", 0); tls_port = qemu_opt_get_number(opts, "tls-port", 0); - if (!port && !tls_port) { - error_report("neither port nor tls-port specified for spice"); - exit(1); - } if (port < 0 || port > 65535) { error_report("spice port is out of range"); exit(1); @@ -664,33 +669,32 @@ void qemu_spice_init(void) if (tls_port) { x509_dir = qemu_opt_get(opts, "x509-dir"); - if (NULL == x509_dir) { + if (!x509_dir) { x509_dir = "."; } - len = strlen(x509_dir) + 32; str = qemu_opt_get(opts, "x509-key-file"); if (str) { x509_key_file = g_strdup(str); } else { - x509_key_file = g_malloc(len); - snprintf(x509_key_file, len, "%s/%s", x509_dir, X509_SERVER_KEY_FILE); + x509_key_file = g_strdup_printf("%s/%s", x509_dir, + X509_SERVER_KEY_FILE); } str = qemu_opt_get(opts, "x509-cert-file"); if (str) { x509_cert_file = g_strdup(str); } else { - x509_cert_file = g_malloc(len); - snprintf(x509_cert_file, len, "%s/%s", x509_dir, X509_SERVER_CERT_FILE); + x509_cert_file = g_strdup_printf("%s/%s", x509_dir, + X509_SERVER_CERT_FILE); } str = qemu_opt_get(opts, "x509-cacert-file"); if (str) { x509_cacert_file = g_strdup(str); } else { - x509_cacert_file = g_malloc(len); - snprintf(x509_cacert_file, len, "%s/%s", x509_dir, X509_CA_CERT_FILE); + x509_cacert_file = g_strdup_printf("%s/%s", x509_dir, + X509_CA_CERT_FILE); } x509_key_password = qemu_opt_get(opts, "x509-key-password"); @@ -721,7 +725,7 @@ void qemu_spice_init(void) tls_ciphers); } if (password) { - spice_server_set_ticket(spice_server, password, 0, 0, 0); + qemu_spice_set_passwd(password, false, false); } if (qemu_opt_get_bool(opts, "sasl", 0)) { if (spice_server_set_sasl_appname(spice_server, "qemu") == -1 || @@ -729,6 +733,7 @@ void qemu_spice_init(void) error_report("spice: failed to enable sasl"); exit(1); } + auth = "sasl"; } if (qemu_opt_get_bool(opts, "disable-ticketing", 0)) { auth = "none"; @@ -739,6 +744,16 @@ void qemu_spice_init(void) spice_server_set_agent_copypaste(spice_server, false); } + if (qemu_opt_get_bool(opts, "disable-agent-file-xfer", 0)) { +#if SPICE_SERVER_VERSION >= 0x000c04 + spice_server_set_agent_file_xfer(spice_server, false); +#else + error_report("this qemu build does not support the " + "\"disable-agent-file-xfer\" option"); + exit(1); +#endif + } + compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ; str = qemu_opt_get(opts, "image-compression"); if (str) { @@ -764,6 +779,8 @@ void qemu_spice_init(void) if (str) { int streaming_video = parse_stream_video(str); spice_server_set_streaming_video(spice_server, streaming_video); + } else { + spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF); } spice_server_set_agent_mouse @@ -778,7 +795,7 @@ void qemu_spice_init(void) seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0); spice_server_set_seamless_migration(spice_server, seamless_migration); - if (0 != spice_server_init(spice_server, &core_interface)) { + if (spice_server_init(spice_server, &core_interface) != 0) { error_report("failed to initialize spice server"); exit(1); }; @@ -819,6 +836,7 @@ int qemu_spice_add_interface(SpiceBaseInstance *sin) * With a command line like '-vnc :0 -vga qxl' you'll end up here. */ spice_server = spice_server_new(); + spice_server_set_sasl_appname(spice_server, "qemu"); spice_server_init(spice_server, &core_interface); qemu_add_vm_change_state_handler(vm_change_state_handler, NULL); } @@ -826,6 +844,26 @@ int qemu_spice_add_interface(SpiceBaseInstance *sin) return spice_server_add_interface(spice_server, sin); } +static GSList *spice_consoles; + +bool qemu_spice_have_display_interface(QemuConsole *con) +{ + if (g_slist_find(spice_consoles, con)) { + return true; + } + return false; +} + +int qemu_spice_add_display_interface(QXLInstance *qxlin, QemuConsole *con) +{ + if (g_slist_find(spice_consoles, con)) { + return -1; + } + qxlin->id = qemu_console_get_index(con); + spice_consoles = g_slist_append(spice_consoles, con); + return qemu_spice_add_interface(&qxlin->base); +} + static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn) { time_t lifetime, now = time(NULL); @@ -848,6 +886,10 @@ static int qemu_spice_set_ticket(bool fail_if_conn, bool disconnect_if_conn) int qemu_spice_set_passwd(const char *passwd, bool fail_if_conn, bool disconnect_if_conn) { + if (strcmp(auth, "spice") != 0) { + return -1; + } + g_free(auth_passwd); auth_passwd = g_strdup(passwd); return qemu_spice_set_ticket(fail_if_conn, disconnect_if_conn); @@ -868,6 +910,23 @@ int qemu_spice_display_add_client(int csock, int skipauth, int tls) } } +void qemu_spice_display_start(void) +{ + spice_display_is_running = true; + spice_server_vm_start(spice_server); +} + +void qemu_spice_display_stop(void) +{ + spice_server_vm_stop(spice_server); + spice_display_is_running = false; +} + +int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd) +{ + return spice_display_is_running; +} + static void spice_register_config(void) { qemu_add_opts(&qemu_spice_opts);