aboutsummaryrefslogtreecommitdiff
path: root/ui/vnc.c
diff options
context:
space:
mode:
authorDaniel P. Berrangé <berrange@redhat.com>2020-12-11 16:08:25 +0000
committerGerd Hoffmann <kraxel@redhat.com>2021-01-15 11:22:42 +0100
commit7b5fa0b583c8d54f4bc3be796c4086de39ea09d3 (patch)
tree798384dccac4e0086a0869bee71bf0a4e959ebb1 /ui/vnc.c
parent521534df57cc0bee7b0da9e69fbbaa7149036ddb (diff)
downloadqemu-7b5fa0b583c8d54f4bc3be796c4086de39ea09d3.zip
qemu-7b5fa0b583c8d54f4bc3be796c4086de39ea09d3.tar.gz
qemu-7b5fa0b583c8d54f4bc3be796c4086de39ea09d3.tar.bz2
ui: add support for remote power control to VNC server
The "XVP" (Xen VNC Proxy) extension defines a mechanism for a VNC client to issue power control requests to trigger graceful shutdown, reboot, or hard reset. This option is not enabled by default, since we cannot assume that users with VNC access implicitly have administrator access to the guest OS. Thus is it enabled with a boolean "power-control" option e.g. -vnc :1,power-control=on While, QEMU can easily support shutdown and reset, there's no easy way to wire up reboot support at this time. In theory it could be done by issuing a shutdown, followed by a reset, but there's no convenient wiring for such a pairing in QEMU. It also isn't possible to have the VNC server directly talk to QEMU guest agent, since the agent chardev is typically owned by an external mgmt app. Signed-off-by: Daniel P. Berrangé <berrange@redhat.com> [ kraxel: rebase to master ] [ kraxel: add missing break ] Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
Diffstat (limited to 'ui/vnc.c')
-rw-r--r--ui/vnc.c59
1 files changed, 59 insertions, 0 deletions
diff --git a/ui/vnc.c b/ui/vnc.c
index 69e92b1..a0bf750 100644
--- a/ui/vnc.c
+++ b/ui/vnc.c
@@ -30,6 +30,7 @@
#include "trace.h"
#include "hw/qdev-core.h"
#include "sysemu/sysemu.h"
+#include "sysemu/runstate.h"
#include "qemu/error-report.h"
#include "qemu/main-loop.h"
#include "qemu/module.h"
@@ -2042,6 +2043,17 @@ static void send_ext_audio_ack(VncState *vs)
vnc_flush(vs);
}
+static void send_xvp_message(VncState *vs, int code)
+{
+ vnc_lock_output(vs);
+ vnc_write_u8(vs, VNC_MSG_SERVER_XVP);
+ vnc_write_u8(vs, 0); /* pad */
+ vnc_write_u8(vs, 1); /* version */
+ vnc_write_u8(vs, code);
+ vnc_unlock_output(vs);
+ vnc_flush(vs);
+}
+
static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
{
int i;
@@ -2121,6 +2133,12 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings)
case VNC_ENCODING_LED_STATE:
vs->features |= VNC_FEATURE_LED_STATE_MASK;
break;
+ case VNC_ENCODING_XVP:
+ if (vs->vd->power_control) {
+ vs->features |= VNC_FEATURE_XVP;
+ send_xvp_message(vs, VNC_XVP_CODE_INIT);
+ }
+ break;
case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9:
vs->tight->compression = (enc & 0x0F);
break;
@@ -2353,6 +2371,42 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len)
client_cut_text(vs, read_u32(data, 4), data + 8);
break;
+ case VNC_MSG_CLIENT_XVP:
+ if (!(vs->features & VNC_FEATURE_XVP)) {
+ error_report("vnc: xvp client message while disabled");
+ vnc_client_error(vs);
+ break;
+ }
+ if (len == 1) {
+ return 4;
+ }
+ if (len == 4) {
+ uint8_t version = read_u8(data, 2);
+ uint8_t action = read_u8(data, 3);
+
+ if (version != 1) {
+ error_report("vnc: xvp client message version %d != 1",
+ version);
+ vnc_client_error(vs);
+ break;
+ }
+
+ switch (action) {
+ case VNC_XVP_ACTION_SHUTDOWN:
+ qemu_system_powerdown_request();
+ break;
+ case VNC_XVP_ACTION_REBOOT:
+ send_xvp_message(vs, VNC_XVP_CODE_FAIL);
+ break;
+ case VNC_XVP_ACTION_RESET:
+ qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET);
+ break;
+ default:
+ send_xvp_message(vs, VNC_XVP_CODE_FAIL);
+ break;
+ }
+ }
+ break;
case VNC_MSG_CLIENT_QEMU:
if (len == 1)
return 2;
@@ -3379,6 +3433,9 @@ static QemuOptsList qemu_vnc_opts = {
},{
.name = "audiodev",
.type = QEMU_OPT_STRING,
+ },{
+ .name = "power-control",
+ .type = QEMU_OPT_BOOL,
},
{ /* end of list */ }
},
@@ -3942,6 +3999,8 @@ void vnc_display_open(const char *id, Error **errp)
vd->non_adaptive = true;
}
+ vd->power_control = qemu_opt_get_bool(opts, "power-control", false);
+
if (tlsauthz) {
vd->tlsauthzid = g_strdup(tlsauthz);
} else if (acl) {