]>
Commit | Line | Data |
---|---|---|
f376b2b9 SR |
1 | From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
2 | From: Stefan Reiter <s.reiter@proxmox.com> | |
3 | Date: Wed, 25 Aug 2021 11:14:13 +0200 | |
4 | Subject: [PATCH] monitor: refactor set/expire_password and allow VNC display | |
5 | id | |
6 | MIME-Version: 1.0 | |
7 | Content-Type: text/plain; charset=UTF-8 | |
8 | Content-Transfer-Encoding: 8bit | |
9 | ||
10 | It is possible to specify more than one VNC server on the command line, | |
11 | either with an explicit ID or the auto-generated ones à la "default", | |
12 | "vnc2", "vnc3", ... | |
13 | ||
14 | It is not possible to change the password on one of these extra VNC | |
15 | displays though. Fix this by adding a "display" parameter to the | |
16 | "set_password" and "expire_password" QMP and HMP commands. | |
17 | ||
18 | For HMP, the display is specified using the "-d" value flag. | |
19 | ||
20 | For QMP, the schema is updated to explicitly express the supported | |
21 | variants of the commands with protocol-discriminated unions. | |
22 | ||
23 | Suggested-by: Eric Blake <eblake@redhat.com> | |
24 | Suggested-by: Markus Armbruster <armbru@redhat.com> | |
25 | Signed-off-by: Stefan Reiter <s.reiter@proxmox.com> | |
26 | --- | |
27 | hmp-commands.hx | 29 ++++---- | |
28 | monitor/hmp-cmds.c | 57 +++++++++++++++- | |
29 | monitor/qmp-cmds.c | 62 ++++++----------- | |
30 | qapi/ui.json | 163 ++++++++++++++++++++++++++++++++++++++------- | |
31 | 4 files changed, 232 insertions(+), 79 deletions(-) | |
32 | ||
33 | diff --git a/hmp-commands.hx b/hmp-commands.hx | |
34 | index 8e45bce2cd..d78e4cfc47 100644 | |
35 | --- a/hmp-commands.hx | |
36 | +++ b/hmp-commands.hx | |
37 | @@ -1514,34 +1514,35 @@ ERST | |
38 | ||
39 | { | |
40 | .name = "set_password", | |
41 | - .args_type = "protocol:s,password:s,connected:s?", | |
42 | - .params = "protocol password action-if-connected", | |
43 | + .args_type = "protocol:s,password:s,display:-dS,connected:s?", | |
44 | + .params = "protocol password [-d display] [action-if-connected]", | |
45 | .help = "set spice/vnc password", | |
46 | .cmd = hmp_set_password, | |
47 | }, | |
48 | ||
49 | SRST | |
50 | -``set_password [ vnc | spice ] password [ action-if-connected ]`` | |
51 | - Change spice/vnc password. Use zero to make the password stay valid | |
52 | - forever. *action-if-connected* specifies what should happen in | |
53 | - case a connection is established: *fail* makes the password change | |
54 | - fail. *disconnect* changes the password and disconnects the | |
55 | - client. *keep* changes the password and keeps the connection up. | |
56 | - *keep* is the default. | |
57 | +``set_password [ vnc | spice ] password [ -d display ] [ action-if-connected ]`` | |
58 | + Change spice/vnc password. *display* can be used with 'vnc' to specify | |
59 | + which display to set the password on. *action-if-connected* specifies | |
60 | + what should happen in case a connection is established: *fail* makes | |
61 | + the password change fail. *disconnect* changes the password and | |
62 | + disconnects the client. *keep* changes the password and keeps the | |
63 | + connection up. *keep* is the default. | |
64 | ERST | |
65 | ||
66 | { | |
67 | .name = "expire_password", | |
68 | - .args_type = "protocol:s,time:s", | |
69 | - .params = "protocol time", | |
70 | + .args_type = "protocol:s,time:s,display:-dS", | |
71 | + .params = "protocol time [-d display]", | |
72 | .help = "set spice/vnc password expire-time", | |
73 | .cmd = hmp_expire_password, | |
74 | }, | |
75 | ||
76 | SRST | |
77 | -``expire_password [ vnc | spice ]`` *expire-time* | |
78 | - Specify when a password for spice/vnc becomes | |
79 | - invalid. *expire-time* accepts: | |
80 | +``expire_password [ vnc | spice ] expire-time [ -d display ]`` | |
81 | + Specify when a password for spice/vnc becomes invalid. | |
82 | + *display* behaves the same as in ``set_password``. | |
83 | + *expire-time* accepts: | |
84 | ||
85 | ``now`` | |
86 | Invalidate password instantly. | |
87 | diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c | |
88 | index e00255f7ee..047294e1ed 100644 | |
89 | --- a/monitor/hmp-cmds.c | |
90 | +++ b/monitor/hmp-cmds.c | |
91 | @@ -1451,10 +1451,41 @@ void hmp_set_password(Monitor *mon, const QDict *qdict) | |
92 | { | |
93 | const char *protocol = qdict_get_str(qdict, "protocol"); | |
94 | const char *password = qdict_get_str(qdict, "password"); | |
95 | + const char *display = qdict_get_try_str(qdict, "display"); | |
96 | const char *connected = qdict_get_try_str(qdict, "connected"); | |
97 | Error *err = NULL; | |
98 | + DisplayProtocol proto; | |
99 | ||
100 | - qmp_set_password(protocol, password, !!connected, connected, &err); | |
101 | + SetPasswordOptions opts = { | |
102 | + .password = g_strdup(password), | |
103 | + .u.vnc.display = NULL, | |
104 | + }; | |
105 | + | |
106 | + proto = qapi_enum_parse(&DisplayProtocol_lookup, protocol, | |
107 | + DISPLAY_PROTOCOL_VNC, &err); | |
108 | + if (err) { | |
109 | + hmp_handle_error(mon, err); | |
110 | + return; | |
111 | + } | |
112 | + opts.protocol = proto; | |
113 | + | |
114 | + if (proto == DISPLAY_PROTOCOL_VNC) { | |
115 | + opts.u.vnc.has_display = !!display; | |
116 | + opts.u.vnc.display = g_strdup(display); | |
117 | + } else if (proto == DISPLAY_PROTOCOL_SPICE) { | |
118 | + opts.u.spice.has_connected = !!connected; | |
119 | + opts.u.spice.connected = | |
120 | + qapi_enum_parse(&SetPasswordAction_lookup, connected, | |
121 | + SET_PASSWORD_ACTION_KEEP, &err); | |
122 | + if (err) { | |
123 | + hmp_handle_error(mon, err); | |
124 | + return; | |
125 | + } | |
126 | + } | |
127 | + | |
128 | + qmp_set_password(&opts, &err); | |
129 | + g_free(opts.password); | |
130 | + g_free(opts.u.vnc.display); | |
131 | hmp_handle_error(mon, err); | |
132 | } | |
133 | ||
134 | @@ -1462,9 +1493,31 @@ void hmp_expire_password(Monitor *mon, const QDict *qdict) | |
135 | { | |
136 | const char *protocol = qdict_get_str(qdict, "protocol"); | |
137 | const char *whenstr = qdict_get_str(qdict, "time"); | |
138 | + const char *display = qdict_get_try_str(qdict, "display"); | |
139 | Error *err = NULL; | |
140 | + DisplayProtocol proto; | |
141 | + | |
142 | + ExpirePasswordOptions opts = { | |
143 | + .time = g_strdup(whenstr), | |
144 | + .u.vnc.display = NULL, | |
145 | + }; | |
146 | + | |
147 | + proto = qapi_enum_parse(&DisplayProtocol_lookup, protocol, | |
148 | + DISPLAY_PROTOCOL_VNC, &err); | |
149 | + if (err) { | |
150 | + hmp_handle_error(mon, err); | |
151 | + return; | |
152 | + } | |
153 | + opts.protocol = proto; | |
154 | + | |
155 | + if (proto == DISPLAY_PROTOCOL_VNC) { | |
156 | + opts.u.vnc.has_display = !!display; | |
157 | + opts.u.vnc.display = g_strdup(display); | |
158 | + } | |
159 | ||
160 | - qmp_expire_password(protocol, whenstr, &err); | |
161 | + qmp_expire_password(&opts, &err); | |
162 | + g_free(opts.time); | |
163 | + g_free(opts.u.vnc.display); | |
164 | hmp_handle_error(mon, err); | |
165 | } | |
166 | ||
167 | diff --git a/monitor/qmp-cmds.c b/monitor/qmp-cmds.c | |
168 | index f7d64a6457..65882b5997 100644 | |
169 | --- a/monitor/qmp-cmds.c | |
170 | +++ b/monitor/qmp-cmds.c | |
171 | @@ -164,45 +164,30 @@ void qmp_system_wakeup(Error **errp) | |
172 | qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp); | |
173 | } | |
174 | ||
175 | -void qmp_set_password(const char *protocol, const char *password, | |
176 | - bool has_connected, const char *connected, Error **errp) | |
177 | +void qmp_set_password(SetPasswordOptions *opts, Error **errp) | |
178 | { | |
179 | - int disconnect_if_connected = 0; | |
180 | - int fail_if_connected = 0; | |
181 | - int rc; | |
182 | - | |
183 | - if (has_connected) { | |
184 | - if (strcmp(connected, "fail") == 0) { | |
185 | - fail_if_connected = 1; | |
186 | - } else if (strcmp(connected, "disconnect") == 0) { | |
187 | - disconnect_if_connected = 1; | |
188 | - } else if (strcmp(connected, "keep") == 0) { | |
189 | - /* nothing */ | |
190 | - } else { | |
191 | - error_setg(errp, QERR_INVALID_PARAMETER, "connected"); | |
192 | - return; | |
193 | - } | |
194 | - } | |
195 | + bool disconnect_if_connected = false; | |
196 | + bool fail_if_connected = false; | |
197 | + int rc = 0; | |
198 | ||
199 | - if (strcmp(protocol, "spice") == 0) { | |
200 | + if (opts->protocol == DISPLAY_PROTOCOL_SPICE) { | |
201 | if (!qemu_using_spice(errp)) { | |
202 | return; | |
203 | } | |
204 | - rc = qemu_spice.set_passwd(password, fail_if_connected, | |
205 | - disconnect_if_connected); | |
206 | - } else if (strcmp(protocol, "vnc") == 0) { | |
207 | - if (fail_if_connected || disconnect_if_connected) { | |
208 | - /* vnc supports "connected=keep" only */ | |
209 | - error_setg(errp, QERR_INVALID_PARAMETER, "connected"); | |
210 | - return; | |
211 | + if (opts->u.spice.has_connected) { | |
212 | + fail_if_connected = | |
213 | + opts->u.spice.connected == SET_PASSWORD_ACTION_FAIL; | |
214 | + disconnect_if_connected = | |
215 | + opts->u.spice.connected == SET_PASSWORD_ACTION_DISCONNECT; | |
216 | } | |
217 | + rc = qemu_spice.set_passwd(opts->password, fail_if_connected, | |
218 | + disconnect_if_connected); | |
219 | + } else if (opts->protocol == DISPLAY_PROTOCOL_VNC) { | |
220 | /* Note that setting an empty password will not disable login through | |
221 | * this interface. */ | |
222 | - rc = vnc_display_password(NULL, password); | |
223 | - } else { | |
224 | - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", | |
225 | - "'vnc' or 'spice'"); | |
226 | - return; | |
227 | + rc = vnc_display_password( | |
228 | + opts->u.vnc.has_display ? opts->u.vnc.display : NULL, | |
229 | + opts->password); | |
230 | } | |
231 | ||
232 | if (rc != 0) { | |
233 | @@ -210,11 +195,11 @@ void qmp_set_password(const char *protocol, const char *password, | |
234 | } | |
235 | } | |
236 | ||
237 | -void qmp_expire_password(const char *protocol, const char *whenstr, | |
238 | - Error **errp) | |
239 | +void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp) | |
240 | { | |
241 | time_t when; | |
242 | int rc; | |
243 | + const char* whenstr = opts->time; | |
244 | ||
245 | if (strcmp(whenstr, "now") == 0) { | |
246 | when = 0; | |
247 | @@ -226,17 +211,14 @@ void qmp_expire_password(const char *protocol, const char *whenstr, | |
248 | when = strtoull(whenstr, NULL, 10); | |
249 | } | |
250 | ||
251 | - if (strcmp(protocol, "spice") == 0) { | |
252 | + if (opts->protocol == DISPLAY_PROTOCOL_SPICE) { | |
253 | if (!qemu_using_spice(errp)) { | |
254 | return; | |
255 | } | |
256 | rc = qemu_spice.set_pw_expire(when); | |
257 | - } else if (strcmp(protocol, "vnc") == 0) { | |
258 | - rc = vnc_display_pw_expire(NULL, when); | |
259 | - } else { | |
260 | - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", | |
261 | - "'vnc' or 'spice'"); | |
262 | - return; | |
263 | + } else if (opts->protocol == DISPLAY_PROTOCOL_VNC) { | |
264 | + rc = vnc_display_pw_expire( | |
265 | + opts->u.vnc.has_display ? opts->u.vnc.display : NULL, when); | |
266 | } | |
267 | ||
268 | if (rc != 0) { | |
269 | diff --git a/qapi/ui.json b/qapi/ui.json | |
270 | index fd9677d48e..0177cf4ee9 100644 | |
271 | --- a/qapi/ui.json | |
272 | +++ b/qapi/ui.json | |
273 | @@ -10,20 +10,21 @@ | |
274 | { 'include': 'sockets.json' } | |
275 | ||
276 | ## | |
277 | -# @set_password: | |
278 | +# @DisplayProtocol: | |
279 | # | |
280 | -# Sets the password of a remote display session. | |
281 | +# Display protocols which support changing password options. | |
282 | # | |
283 | -# @protocol: - 'vnc' to modify the VNC server password | |
284 | -# - 'spice' to modify the Spice server password | |
285 | +# Since: 6.2 | |
286 | # | |
287 | -# @password: the new password | |
288 | +## | |
289 | +{ 'enum': 'DisplayProtocol', | |
290 | + 'data': [ { 'name': 'vnc', 'if': 'defined(CONFIG_VNC)' }, | |
291 | + { 'name': 'spice', 'if': 'defined(CONFIG_SPICE)' } ] } | |
292 | + | |
293 | +## | |
294 | +# @set_password: | |
295 | # | |
296 | -# @connected: how to handle existing clients when changing the | |
297 | -# password. If nothing is specified, defaults to 'keep' | |
298 | -# 'fail' to fail the command if clients are connected | |
299 | -# 'disconnect' to disconnect existing clients | |
300 | -# 'keep' to maintain existing clients | |
301 | +# Sets the password of a remote display session. | |
302 | # | |
303 | # Returns: - Nothing on success | |
304 | # - If Spice is not enabled, DeviceNotFound | |
305 | @@ -37,16 +38,123 @@ | |
306 | # <- { "return": {} } | |
307 | # | |
308 | ## | |
309 | -{ 'command': 'set_password', | |
310 | - 'data': {'protocol': 'str', 'password': 'str', '*connected': 'str'} } | |
311 | +{ 'command': 'set_password', 'boxed': true, 'data': 'SetPasswordOptions' } | |
312 | + | |
313 | +## | |
314 | +# @SetPasswordOptions: | |
315 | +# | |
316 | +# Data required to set a new password on a display server protocol. | |
317 | +# | |
318 | +# @protocol: - 'vnc' to modify the VNC server password | |
319 | +# - 'spice' to modify the Spice server password | |
320 | +# | |
321 | +# @password: the new password | |
322 | +# | |
323 | +# Since: 6.2 | |
324 | +# | |
325 | +## | |
326 | +{ 'union': 'SetPasswordOptions', | |
327 | + 'base': { 'protocol': 'DisplayProtocol', | |
328 | + 'password': 'str' }, | |
329 | + 'discriminator': 'protocol', | |
330 | + 'data': { 'vnc': 'SetPasswordOptionsVnc', | |
331 | + 'spice': 'SetPasswordOptionsSpice' } } | |
332 | + | |
333 | +## | |
334 | +# @SetPasswordAction: | |
335 | +# | |
336 | +# An action to take on changing a password on a connection with active clients. | |
337 | +# | |
338 | +# @fail: fail the command if clients are connected | |
339 | +# | |
340 | +# @disconnect: disconnect existing clients | |
341 | +# | |
342 | +# @keep: maintain existing clients | |
343 | +# | |
344 | +# Since: 6.2 | |
345 | +# | |
346 | +## | |
347 | +{ 'enum': 'SetPasswordAction', | |
348 | + 'data': [ 'fail', 'disconnect', 'keep' ] } | |
349 | + | |
350 | +## | |
351 | +# @SetPasswordActionVnc: | |
352 | +# | |
353 | +# See @SetPasswordAction. VNC only supports the keep action. 'connection' | |
354 | +# should just be omitted for VNC, this is kept for backwards compatibility. | |
355 | +# | |
356 | +# @keep: maintain existing clients | |
357 | +# | |
358 | +# Since: 6.2 | |
359 | +# | |
360 | +## | |
361 | +{ 'enum': 'SetPasswordActionVnc', | |
362 | + 'data': [ 'keep' ] } | |
363 | + | |
364 | +## | |
365 | +# @SetPasswordOptionsSpice: | |
366 | +# | |
367 | +# Options for set_password specific to the VNC procotol. | |
368 | +# | |
369 | +# @connected: How to handle existing clients when changing the | |
370 | +# password. If nothing is specified, defaults to 'keep'. | |
371 | +# | |
372 | +# Since: 6.2 | |
373 | +# | |
374 | +## | |
375 | +{ 'struct': 'SetPasswordOptionsSpice', | |
376 | + 'data': { '*connected': 'SetPasswordAction' } } | |
377 | + | |
378 | +## | |
379 | +# @SetPasswordOptionsVnc: | |
380 | +# | |
381 | +# Options for set_password specific to the VNC procotol. | |
382 | +# | |
383 | +# @display: The id of the display where the password should be changed. | |
384 | +# Defaults to the first. | |
385 | +# | |
386 | +# @connected: How to handle existing clients when changing the | |
387 | +# password. | |
388 | +# | |
389 | +# Features: | |
390 | +# @deprecated: For VNC, @connected will always be 'keep', parameter should be | |
391 | +# omitted. | |
392 | +# | |
393 | +# Since: 6.2 | |
394 | +# | |
395 | +## | |
396 | +{ 'struct': 'SetPasswordOptionsVnc', | |
397 | + 'data': { '*display': 'str', | |
398 | + '*connected': { 'type': 'SetPasswordActionVnc', | |
399 | + 'features': ['deprecated'] } } } | |
400 | ||
401 | ## | |
402 | # @expire_password: | |
403 | # | |
404 | # Expire the password of a remote display server. | |
405 | # | |
406 | -# @protocol: the name of the remote display protocol 'vnc' or 'spice' | |
407 | +# Returns: - Nothing on success | |
408 | +# - If @protocol is 'spice' and Spice is not active, DeviceNotFound | |
409 | +# | |
410 | +# Since: 0.14 | |
411 | +# | |
412 | +# Example: | |
413 | +# | |
414 | +# -> { "execute": "expire_password", "arguments": { "protocol": "vnc", | |
415 | +# "time": "+60" } } | |
416 | +# <- { "return": {} } | |
417 | +# | |
418 | +## | |
419 | +{ 'command': 'expire_password', 'boxed': true, 'data': 'ExpirePasswordOptions' } | |
420 | + | |
421 | +## | |
422 | +# @ExpirePasswordOptions: | |
423 | +# | |
424 | +# Data required to set password expiration on a display server protocol. | |
425 | # | |
426 | +# @protocol: - 'vnc' to modify the VNC server expiration | |
427 | +# - 'spice' to modify the Spice server expiration | |
428 | + | |
429 | # @time: when to expire the password. | |
430 | # | |
431 | # - 'now' to expire the password immediately | |
432 | @@ -54,24 +162,33 @@ | |
433 | # - '+INT' where INT is the number of seconds from now (integer) | |
434 | # - 'INT' where INT is the absolute time in seconds | |
435 | # | |
436 | -# Returns: - Nothing on success | |
437 | -# - If @protocol is 'spice' and Spice is not active, DeviceNotFound | |
438 | -# | |
439 | -# Since: 0.14 | |
440 | -# | |
441 | # Notes: Time is relative to the server and currently there is no way to | |
442 | # coordinate server time with client time. It is not recommended to | |
443 | # use the absolute time version of the @time parameter unless you're | |
444 | # sure you are on the same machine as the QEMU instance. | |
445 | # | |
446 | -# Example: | |
447 | +# Since: 6.2 | |
448 | # | |
449 | -# -> { "execute": "expire_password", "arguments": { "protocol": "vnc", | |
450 | -# "time": "+60" } } | |
451 | -# <- { "return": {} } | |
452 | +## | |
453 | +{ 'union': 'ExpirePasswordOptions', | |
454 | + 'base': { 'protocol': 'DisplayProtocol', | |
455 | + 'time': 'str' }, | |
456 | + 'discriminator': 'protocol', | |
457 | + 'data': { 'vnc': 'ExpirePasswordOptionsVnc' } } | |
458 | + | |
459 | +## | |
460 | +# @ExpirePasswordOptionsVnc: | |
461 | +# | |
462 | +# Options for expire_password specific to the VNC procotol. | |
463 | +# | |
464 | +# @display: The id of the display where the expiration should be changed. | |
465 | +# Defaults to the first. | |
466 | +# | |
467 | +# Since: 6.2 | |
468 | # | |
469 | ## | |
470 | -{ 'command': 'expire_password', 'data': {'protocol': 'str', 'time': 'str'} } | |
471 | +{ 'struct': 'ExpirePasswordOptionsVnc', | |
472 | + 'data': { '*display': 'str' } } | |
473 | ||
474 | ## | |
475 | # @screendump: |