@samitouri / QOSamiQemu / commits / 4a33bdd9e0

ui/vnc: clean up VNC displays on exit

Previously, VNC displays were never torn down on QEMU exit, leaking resources and leaving connected clients with unclean disconnects. Add vnc_cleanup() to free all VNC displays during qemu_cleanup(). Make vnc_display_close() initiate disconnection of active clients, and have vnc_display_free() drain the main loop until all clients have completed their teardown, instead of asserting the client list is empty. Reviewed-by: Daniel P. Berrangé <berrange@redhat.com> Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>

Marc-André Lureau committed Apr 8, 2026 at 17:15 UTC 4a33bdd9e0412971b5dec5e435024f29dc0cd5fe
3 files changed +24 -2
include/ui/console.h
+1
@@ -445,6 +445,7 @@ void vnc_parse(const char *str);
445 int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp);
446 bool vnc_display_reload_certs(const char *id, Error **errp);
447 bool vnc_display_update(DisplayUpdateOptionsVNC *arg, Error **errp);
448 +void vnc_cleanup(void);
449
450 /* input.c */
451 int index_from_key(const char *key, size_t key_length);
system/runstate.c
+5
@@ -61,6 +61,8 @@
61 #include "system/confidential-guest-support.h"
62 #include "system/system.h"
63 #include "system/tpm.h"
64 +#include "ui/console.h"
65 +
66 #include "trace.h"
67
68 static NotifierList exit_notifiers =
@@ -1044,5 +1046,8 @@ void qemu_cleanup(int status)
1046 monitor_cleanup();
1047 qemu_chr_cleanup();
1048 user_creatable_cleanup();
1049 +#ifdef CONFIG_VNC
1050 + vnc_cleanup();
1051 +#endif
1052 /* TODO: unref root container, check all devices are ok */
1053 }
ui/vnc.c
+18 -2
@@ -3473,8 +3473,13 @@ VncDisplay *vnc_display_new(const char *id, Error **errp)
3473
3474 static void vnc_display_close(VncDisplay *vd)
3475 {
3476 + VncState *vs;
3477 +
3478 assert(vd);
3479
3480 + QTAILQ_FOREACH(vs, &vd->clients, next) {
3481 + vnc_disconnect_start(vs);
3482 + }
3483 if (vd->listener) {
3484 qio_net_listener_disconnect(vd->listener);
3485 object_unref(OBJECT(vd->listener));
@@ -3515,10 +3520,12 @@ void vnc_display_free(VncDisplay *vd)
3520 return;
3521 }
3522
3518 - assert(QTAILQ_EMPTY(&vd->clients));
3523 + vnc_display_close(vd);
3524 + while (!QTAILQ_EMPTY(&vd->clients)) {
3525 + main_loop_wait(false);
3526 + }
3527
3528 vnc_stop_worker_thread(vd);
3521 - vnc_display_close(vd);
3529 unregister_displaychangelistener(&vd->dcl);
3530 qkbd_state_free(vd->kbd);
3531 qemu_del_vm_change_state_handler(vd->vmstate_handler_entry);
@@ -4348,6 +4355,15 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp)
4355 return vnc_display_new(id, errp) != NULL ? 0 : -1;
4356 }
4357
4358 +void vnc_cleanup(void)
4359 +{
4360 + VncDisplay *vd, *vd_next;
4361 +
4362 + QTAILQ_FOREACH_SAFE(vd, &vnc_displays, next, vd_next) {
4363 + vnc_display_free(vd);
4364 + }
4365 +}
4366 +
4367 static void vnc_register_config(void)
4368 {
4369 qemu_add_opts(&qemu_vnc_opts);