From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Stefan Reiter Date: Wed, 25 Aug 2021 11:14:13 +0200 Subject: [PATCH] monitor: refactor set/expire_password and allow VNC display id MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible to specify more than one VNC server on the command line, either with an explicit ID or the auto-generated ones à la "default", "vnc2", "vnc3", ... It is not possible to change the password on one of these extra VNC displays though. Fix this by adding a "display" parameter to the "set_password" and "expire_password" QMP and HMP commands. For HMP, the display is specified using the "-d" value flag. For QMP, the schema is updated to explicitly express the supported variants of the commands with protocol-discriminated unions. Suggested-by: Eric Blake Suggested-by: Markus Armbruster Signed-off-by: Stefan Reiter Signed-off-by: Thomas Lamprecht --- hmp-commands.hx | 29 ++++---- monitor/hmp-cmds.c | 57 +++++++++++++++- monitor/qmp-cmds.c | 62 ++++++----------- qapi/ui.json | 165 ++++++++++++++++++++++++++++++++++++++------- 4 files changed, 233 insertions(+), 80 deletions(-) diff --git a/hmp-commands.hx b/hmp-commands.hx index 8e45bce2cd..d78e4cfc47 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1514,34 +1514,35 @@ ERST { .name = "set_password", - .args_type = "protocol:s,password:s,connected:s?", - .params = "protocol password action-if-connected", + .args_type = "protocol:s,password:s,display:-dS,connected:s?", + .params = "protocol password [-d display] [action-if-connected]", .help = "set spice/vnc password", .cmd = hmp_set_password, }, SRST -``set_password [ vnc | spice ] password [ action-if-connected ]`` - Change spice/vnc password. Use zero to make the password stay valid - forever. *action-if-connected* specifies what should happen in - case a connection is established: *fail* makes the password change - fail. *disconnect* changes the password and disconnects the - client. *keep* changes the password and keeps the connection up. - *keep* is the default. +``set_password [ vnc | spice ] password [ -d display ] [ action-if-connected ]`` + Change spice/vnc password. *display* can be used with 'vnc' to specify + which display to set the password on. *action-if-connected* specifies + what should happen in case a connection is established: *fail* makes + the password change fail. *disconnect* changes the password and + disconnects the client. *keep* changes the password and keeps the + connection up. *keep* is the default. ERST { .name = "expire_password", - .args_type = "protocol:s,time:s", - .params = "protocol time", + .args_type = "protocol:s,time:s,display:-dS", + .params = "protocol time [-d display]", .help = "set spice/vnc password expire-time", .cmd = hmp_expire_password, }, SRST -``expire_password [ vnc | spice ]`` *expire-time* - Specify when a password for spice/vnc becomes - invalid. *expire-time* accepts: +``expire_password [ vnc | spice ] expire-time [ -d display ]`` + Specify when a password for spice/vnc becomes invalid. + *display* behaves the same as in ``set_password``. + *expire-time* accepts: ``now`` Invalidate password instantly. diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index a7e197a90b..f4ef58d257 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -1451,10 +1451,41 @@ void hmp_set_password(Monitor *mon, const QDict *qdict) { const char *protocol = qdict_get_str(qdict, "protocol"); const char *password = qdict_get_str(qdict, "password"); + const char *display = qdict_get_try_str(qdict, "display"); const char *connected = qdict_get_try_str(qdict, "connected"); Error *err = NULL; + DisplayProtocol proto; - qmp_set_password(protocol, password, !!connected, connected, &err); + SetPasswordOptions opts = { + .password = g_strdup(password), + .u.vnc.display = NULL, + }; + + proto = qapi_enum_parse(&DisplayProtocol_lookup, protocol, + DISPLAY_PROTOCOL_VNC, &err); + if (err) { + hmp_handle_error(mon, err); + return; + } + opts.protocol = proto; + + if (proto == DISPLAY_PROTOCOL_VNC) { + opts.u.vnc.has_display = !!display; + opts.u.vnc.display = g_strdup(display); + } else if (proto == DISPLAY_PROTOCOL_SPICE) { + opts.u.spice.has_connected = !!connected; + opts.u.spice.connected = + qapi_enum_parse(&SetPasswordAction_lookup, connected, + SET_PASSWORD_ACTION_KEEP, &err); + if (err) { + hmp_handle_error(mon, err); + return; + } + } + + qmp_set_password(&opts, &err); + g_free(opts.password); + g_free(opts.u.vnc.display); hmp_handle_error(mon, err); } @@ -1462,9 +1493,31 @@ void hmp_expire_password(Monitor *mon, const QDict *qdict) { const char *protocol = qdict_get_str(qdict, "protocol"); const char *whenstr = qdict_get_str(qdict, "time"); + const char *display = qdict_get_try_str(qdict, "display"); Error *err = NULL; + DisplayProtocol proto; - qmp_expire_password(protocol, whenstr, &err); + ExpirePasswordOptions opts = { + .time = g_strdup(whenstr), + .u.vnc.display = NULL, + }; + + proto = qapi_enum_parse(&DisplayProtocol_lookup, protocol, + DISPLAY_PROTOCOL_VNC, &err); + if (err) { + hmp_handle_error(mon, err); + return; + } + opts.protocol = proto; + + if (proto == DISPLAY_PROTOCOL_VNC) { + opts.u.vnc.has_display = !!display; + opts.u.vnc.display = g_strdup(display); + } + + qmp_expire_password(&opts, &err); + g_free(opts.time); + g_free(opts.u.vnc.display); hmp_handle_error(mon, err); } diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c index f7d64a6457..65882b5997 100644 --- a/monitor/qmp-cmds.c +++ b/monitor/qmp-cmds.c @@ -164,45 +164,30 @@ void qmp_system_wakeup(Error **errp) qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp); } -void qmp_set_password(const char *protocol, const char *password, - bool has_connected, const char *connected, Error **errp) +void qmp_set_password(SetPasswordOptions *opts, Error **errp) { - int disconnect_if_connected = 0; - int fail_if_connected = 0; - int rc; + bool disconnect_if_connected = false; + bool fail_if_connected = false; + int rc = 0; - if (has_connected) { - if (strcmp(connected, "fail") == 0) { - fail_if_connected = 1; - } else if (strcmp(connected, "disconnect") == 0) { - disconnect_if_connected = 1; - } else if (strcmp(connected, "keep") == 0) { - /* nothing */ - } else { - error_setg(errp, QERR_INVALID_PARAMETER, "connected"); - return; - } - } - - if (strcmp(protocol, "spice") == 0) { + if (opts->protocol == DISPLAY_PROTOCOL_SPICE) { if (!qemu_using_spice(errp)) { return; } - rc = qemu_spice.set_passwd(password, fail_if_connected, + if (opts->u.spice.has_connected) { + fail_if_connected = + opts->u.spice.connected == SET_PASSWORD_ACTION_FAIL; + disconnect_if_connected = + opts->u.spice.connected == SET_PASSWORD_ACTION_DISCONNECT; + } + rc = qemu_spice.set_passwd(opts->password, fail_if_connected, disconnect_if_connected); - } else if (strcmp(protocol, "vnc") == 0) { - if (fail_if_connected || disconnect_if_connected) { - /* vnc supports "connected=keep" only */ - error_setg(errp, QERR_INVALID_PARAMETER, "connected"); - return; - } + } else if (opts->protocol == DISPLAY_PROTOCOL_VNC) { /* Note that setting an empty password will not disable login through * this interface. */ - rc = vnc_display_password(NULL, password); - } else { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", - "'vnc' or 'spice'"); - return; + rc = vnc_display_password( + opts->u.vnc.has_display ? opts->u.vnc.display : NULL, + opts->password); } if (rc != 0) { @@ -210,11 +195,11 @@ void qmp_set_password(const char *protocol, const char *password, } } -void qmp_expire_password(const char *protocol, const char *whenstr, - Error **errp) +void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp) { time_t when; int rc; + const char* whenstr = opts->time; if (strcmp(whenstr, "now") == 0) { when = 0; @@ -226,17 +211,14 @@ void qmp_expire_password(const char *protocol, const char *whenstr, when = strtoull(whenstr, NULL, 10); } - if (strcmp(protocol, "spice") == 0) { + if (opts->protocol == DISPLAY_PROTOCOL_SPICE) { if (!qemu_using_spice(errp)) { return; } rc = qemu_spice.set_pw_expire(when); - } else if (strcmp(protocol, "vnc") == 0) { - rc = vnc_display_pw_expire(NULL, when); - } else { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", - "'vnc' or 'spice'"); - return; + } else if (opts->protocol == DISPLAY_PROTOCOL_VNC) { + rc = vnc_display_pw_expire( + opts->u.vnc.has_display ? opts->u.vnc.display : NULL, when); } if (rc != 0) { diff --git a/qapi/ui.json b/qapi/ui.json index fd9677d48e..cba8665b73 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -9,22 +9,23 @@ { 'include': 'common.json' } { 'include': 'sockets.json' } +## +# @DisplayProtocol: +# +# Display protocols which support changing password options. +# +# Since: 6.2 +# +## +{ 'enum': 'DisplayProtocol', + 'data': [ { 'name': 'vnc', 'if': 'defined(CONFIG_VNC)' }, + { 'name': 'spice', 'if': 'defined(CONFIG_SPICE)' } ] } + ## # @set_password: # # Sets the password of a remote display session. # -# @protocol: - 'vnc' to modify the VNC server password -# - 'spice' to modify the Spice server password -# -# @password: the new password -# -# @connected: how to handle existing clients when changing the -# password. If nothing is specified, defaults to 'keep' -# 'fail' to fail the command if clients are connected -# 'disconnect' to disconnect existing clients -# 'keep' to maintain existing clients -# # Returns: - Nothing on success # - If Spice is not enabled, DeviceNotFound # @@ -37,16 +38,123 @@ # <- { "return": {} } # ## -{ 'command': 'set_password', - 'data': {'protocol': 'str', 'password': 'str', '*connected': 'str'} } +{ 'command': 'set_password', 'boxed': true, 'data': 'SetPasswordOptions' } + +## +# @SetPasswordOptions: +# +# Data required to set a new password on a display server protocol. +# +# @protocol: - 'vnc' to modify the VNC server password +# - 'spice' to modify the Spice server password +# +# @password: the new password +# +# Since: 6.2 +# +## +{ 'union': 'SetPasswordOptions', + 'base': { 'protocol': 'DisplayProtocol', + 'password': 'str' }, + 'discriminator': 'protocol', + 'data': { 'vnc': 'SetPasswordOptionsVnc', + 'spice': 'SetPasswordOptionsSpice' } } + +## +# @SetPasswordAction: +# +# An action to take on changing a password on a connection with active clients. +# +# @fail: fail the command if clients are connected +# +# @disconnect: disconnect existing clients +# +# @keep: maintain existing clients +# +# Since: 6.2 +# +## +{ 'enum': 'SetPasswordAction', + 'data': [ 'fail', 'disconnect', 'keep' ] } + +## +# @SetPasswordActionVnc: +# +# See @SetPasswordAction. VNC only supports the keep action. 'connection' +# should just be omitted for VNC, this is kept for backwards compatibility. +# +# @keep: maintain existing clients +# +# Since: 6.2 +# +## +{ 'enum': 'SetPasswordActionVnc', + 'data': [ 'keep' ] } + +## +# @SetPasswordOptionsSpice: +# +# Options for set_password specific to the VNC procotol. +# +# @connected: How to handle existing clients when changing the +# password. If nothing is specified, defaults to 'keep'. +# +# Since: 6.2 +# +## +{ 'struct': 'SetPasswordOptionsSpice', + 'data': { '*connected': 'SetPasswordAction' } } + +## +# @SetPasswordOptionsVnc: +# +# Options for set_password specific to the VNC procotol. +# +# @display: The id of the display where the password should be changed. +# Defaults to the first. +# +# @connected: How to handle existing clients when changing the +# password. +# +# Features: +# @deprecated: For VNC, @connected will always be 'keep', parameter should be +# omitted. +# +# Since: 6.2 +# +## +{ 'struct': 'SetPasswordOptionsVnc', + 'data': { '*display': 'str', + '*connected': { 'type': 'SetPasswordActionVnc', + 'features': ['deprecated'] } } } ## # @expire_password: # # Expire the password of a remote display server. # -# @protocol: the name of the remote display protocol 'vnc' or 'spice' +# Returns: - Nothing on success +# - If @protocol is 'spice' and Spice is not active, DeviceNotFound # +# Since: 0.14 +# +# Example: +# +# -> { "execute": "expire_password", "arguments": { "protocol": "vnc", +# "time": "+60" } } +# <- { "return": {} } +# +## +{ 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' } + +## +# @ExpirePasswordOptions: +# +# Data required to set password expiration on a display server protocol. +# +# @protocol: - 'vnc' to modify the VNC server expiration +# - 'spice' to modify the Spice server expiration + # @time: when to expire the password. # # - 'now' to expire the password immediately @@ -54,24 +162,33 @@ # - '+INT' where INT is the number of seconds from now (integer) # - 'INT' where INT is the absolute time in seconds # -# Returns: - Nothing on success -# - If @protocol is 'spice' and Spice is not active, DeviceNotFound -# -# Since: 0.14 -# # Notes: Time is relative to the server and currently there is no way to # coordinate server time with client time. It is not recommended to # use the absolute time version of the @time parameter unless you're # sure you are on the same machine as the QEMU instance. # -# Example: +# Since: 6.2 # -# -> { "execute": "expire_password", "arguments": { "protocol": "vnc", -# "time": "+60" } } -# <- { "return": {} } +## +{ 'union': 'ExpirePasswordOptions', + 'base': { 'protocol': 'DisplayProtocol', + 'time': 'str' }, + 'discriminator': 'protocol', + 'data': { 'vnc': 'ExpirePasswordOptionsVnc' } } + +## +# @ExpirePasswordOptionsVnc: +# +# Options for expire_password specific to the VNC procotol. +# +# @display: The id of the display where the expiration should be changed. +# Defaults to the first. +# +# Since: 6.2 # ## -{ 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} } +{ 'struct': 'ExpirePasswordOptionsVnc', + 'data': { '*display': 'str' } } ## # @screendump: