master
c 390 lines 10.8 KB
Raw
1 /*
2 * QMP commands related to UI
3 *
4 * Copyright IBM, Corp. 2011
5 *
6 * Authors:
7 * Anthony Liguori <aliguori@us.ibm.com>
8 *
9 * This work is licensed under the terms of the GNU GPL, version 2. See
10 * the COPYING file in the top-level directory.
11 *
12 * Contributions after 2012-01-13 are licensed under the terms of the
13 * GNU GPL, version 2 or (at your option) any later version.
14 */
15
16 #include "qemu/osdep.h"
17
18 #include "io/channel-file.h"
19 #include "monitor/qmp-helpers.h"
20 #include "qapi/qapi-commands-ui.h"
21 #include "qapi/qmp/qerror.h"
22 #include "qemu/coroutine.h"
23 #include "qemu/cutils.h"
24 #include "trace.h"
25 #include "ui/console.h"
26 #include "ui/dbus-display.h"
27 #include "ui/qemu-spice.h"
28 #ifdef CONFIG_PNG
29 #include <png.h>
30 #endif
31
32 void qmp_set_password(SetPasswordOptions *opts, Error **errp)
33 {
34 if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
35 if (!qemu_using_spice(errp)) {
36 return;
37 }
38 qemu_spice.set_passwd(opts->password,
39 opts->connected == SET_PASSWORD_ACTION_FAIL,
40 opts->connected == SET_PASSWORD_ACTION_DISCONNECT,
41 errp);
42 } else {
43 assert(opts->protocol == DISPLAY_PROTOCOL_VNC);
44 if (opts->connected != SET_PASSWORD_ACTION_KEEP) {
45 /* vnc supports "connected=keep" only */
46 error_setg(errp, "parameter 'connected' must be 'keep'"
47 " when 'protocol' is 'vnc'");
48 return;
49 }
50 /*
51 * Note that setting an empty password will not disable login
52 * through this interface.
53 */
54 vnc_display_password(opts->u.vnc.display, opts->password, errp);
55 }
56 }
57
58 void qmp_expire_password(ExpirePasswordOptions *opts, Error **errp)
59 {
60 time_t when;
61 int rc;
62 const char *whenstr = opts->time;
63 const char *numstr = NULL;
64 uint64_t num;
65
66 if (strcmp(whenstr, "now") == 0) {
67 when = 0;
68 } else if (strcmp(whenstr, "never") == 0) {
69 when = TIME_MAX;
70 } else if (whenstr[0] == '+') {
71 when = time(NULL);
72 numstr = whenstr + 1;
73 } else {
74 when = 0;
75 numstr = whenstr;
76 }
77
78 if (numstr) {
79 if (qemu_strtou64(numstr, NULL, 10, &num) < 0) {
80 error_setg(errp, "Parameter 'time' doesn't take value '%s'",
81 whenstr);
82 return;
83 }
84 when += num;
85 }
86
87 if (opts->protocol == DISPLAY_PROTOCOL_SPICE) {
88 if (!qemu_using_spice(errp)) {
89 return;
90 }
91 rc = qemu_spice.set_pw_expire(when);
92 } else {
93 assert(opts->protocol == DISPLAY_PROTOCOL_VNC);
94 rc = vnc_display_pw_expire(opts->u.vnc.display, when);
95 }
96
97 if (rc != 0) {
98 error_setg(errp, "Could not set password expire time");
99 }
100 }
101
102 #ifdef CONFIG_VNC
103 void qmp_change_vnc_password(const char *password, Error **errp)
104 {
105 vnc_display_password(NULL, password, errp);
106 }
107 #endif
108
109 bool qmp_add_client_spice(int fd, bool has_skipauth, bool skipauth,
110 bool has_tls, bool tls, Error **errp)
111 {
112 if (!qemu_using_spice(errp)) {
113 return false;
114 }
115 skipauth = has_skipauth ? skipauth : false;
116 tls = has_tls ? tls : false;
117 if (qemu_spice.display_add_client(fd, skipauth, tls) < 0) {
118 error_setg(errp, "spice failed to add client");
119 return false;
120 }
121 return true;
122 }
123
124 #ifdef CONFIG_VNC
125 bool qmp_add_client_vnc(int fd, bool has_skipauth, bool skipauth,
126 bool has_tls, bool tls, Error **errp)
127 {
128 skipauth = has_skipauth ? skipauth : false;
129 vnc_display_add_client(NULL, fd, skipauth);
130 return true;
131 }
132 #endif
133
134 #ifdef CONFIG_DBUS_DISPLAY
135 bool qmp_add_client_dbus_display(int fd, bool has_skipauth, bool skipauth,
136 bool has_tls, bool tls, Error **errp)
137 {
138 if (!qemu_using_dbus_display(errp)) {
139 return false;
140 }
141 if (!qemu_dbus_display.add_client(fd, errp)) {
142 return false;
143 }
144 return true;
145 }
146 #endif
147
148 void qmp_display_reload(DisplayReloadOptions *arg, Error **errp)
149 {
150 switch (arg->type) {
151 case DISPLAY_RELOAD_TYPE_VNC:
152 #ifdef CONFIG_VNC
153 if (arg->u.vnc.has_tls_certs && arg->u.vnc.tls_certs) {
154 vnc_display_reload_certs(NULL, errp);
155 }
156 #else
157 error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'");
158 #endif
159 break;
160 default:
161 abort();
162 }
163 }
164
165 void qmp_display_update(DisplayUpdateOptions *arg, Error **errp)
166 {
167 switch (arg->type) {
168 case DISPLAY_UPDATE_TYPE_VNC:
169 #ifdef CONFIG_VNC
170 vnc_display_update(&arg->u.vnc, errp);
171 #else
172 error_setg(errp, "vnc is invalid, missing 'CONFIG_VNC'");
173 #endif
174 break;
175 default:
176 abort();
177 }
178 }
179
180 void qmp_client_migrate_info(const char *protocol, const char *hostname,
181 bool has_port, int64_t port,
182 bool has_tls_port, int64_t tls_port,
183 const char *cert_subject,
184 Error **errp)
185 {
186 if (strcmp(protocol, "spice") == 0) {
187 if (!qemu_using_spice(errp)) {
188 return;
189 }
190
191 if (!has_port && !has_tls_port) {
192 error_setg(errp, "parameter 'port' or 'tls-port' is required");
193 return;
194 }
195
196 if (qemu_spice.migrate_info(hostname,
197 has_port ? port : -1,
198 has_tls_port ? tls_port : -1,
199 cert_subject)) {
200 error_setg(errp, "Could not set up display for migration");
201 return;
202 }
203 return;
204 }
205
206 error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "protocol", "'spice'");
207 }
208
209 #ifdef CONFIG_PIXMAN
210 #ifdef CONFIG_PNG
211 /**
212 * png_save: Take a screenshot as PNG
213 *
214 * Saves screendump as a PNG file
215 *
216 * Returns true for success or false for error.
217 *
218 * @fd: File descriptor for PNG file.
219 * @image: Image data in pixman format.
220 * @errp: Pointer to an error.
221 */
222 static bool png_save(int fd, pixman_image_t *image, Error **errp)
223 {
224 int width = pixman_image_get_width(image);
225 int height = pixman_image_get_height(image);
226 png_struct *png_ptr;
227 png_info *info_ptr;
228 g_autoptr(pixman_image_t) linebuf =
229 qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
230 uint8_t *buf = (uint8_t *)pixman_image_get_data(linebuf);
231 FILE *f = fdopen(fd, "wb");
232 int y;
233 if (!f) {
234 error_setg_errno(errp, errno,
235 "Failed to create file from file descriptor");
236 return false;
237 }
238
239 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
240 NULL, NULL);
241 if (!png_ptr) {
242 error_setg(errp, "PNG creation failed. Unable to write struct");
243 fclose(f);
244 return false;
245 }
246
247 info_ptr = png_create_info_struct(png_ptr);
248
249 if (!info_ptr) {
250 error_setg(errp, "PNG creation failed. Unable to write info");
251 fclose(f);
252 png_destroy_write_struct(&png_ptr, &info_ptr);
253 return false;
254 }
255
256 png_init_io(png_ptr, f);
257
258 png_set_IHDR(png_ptr, info_ptr, width, height, 8,
259 PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
260 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
261
262 png_write_info(png_ptr, info_ptr);
263
264 for (y = 0; y < height; ++y) {
265 qemu_pixman_linebuf_fill(linebuf, image, width, 0, y);
266 png_write_row(png_ptr, buf);
267 }
268
269 png_write_end(png_ptr, NULL);
270
271 png_destroy_write_struct(&png_ptr, &info_ptr);
272
273 if (fclose(f) != 0) {
274 error_setg_errno(errp, errno,
275 "PNG creation failed. Unable to close file");
276 return false;
277 }
278
279 return true;
280 }
281
282 #else /* no png support */
283
284 static bool png_save(int fd, pixman_image_t *image, Error **errp)
285 {
286 error_setg(errp, "Enable PNG support with libpng for screendump");
287 return false;
288 }
289
290 #endif /* CONFIG_PNG */
291
292 static bool ppm_save(int fd, pixman_image_t *image, Error **errp)
293 {
294 int width = pixman_image_get_width(image);
295 int height = pixman_image_get_height(image);
296 g_autoptr(Object) ioc = OBJECT(qio_channel_file_new_fd(fd));
297 g_autofree char *header = NULL;
298 g_autoptr(pixman_image_t) linebuf = NULL;
299 int y;
300
301 trace_ppm_save(fd, image);
302
303 header = g_strdup_printf("P6\n%d %d\n%d\n", width, height, 255);
304 if (qio_channel_write_all(QIO_CHANNEL(ioc),
305 header, strlen(header), errp) < 0) {
306 return false;
307 }
308
309 linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, width);
310 for (y = 0; y < height; y++) {
311 qemu_pixman_linebuf_fill(linebuf, image, width, 0, y);
312 if (qio_channel_write_all(QIO_CHANNEL(ioc),
313 (char *)pixman_image_get_data(linebuf),
314 pixman_image_get_stride(linebuf), errp) < 0) {
315 return false;
316 }
317 }
318
319 return true;
320 }
321
322 /* Safety: coroutine-only, concurrent-coroutine safe, main thread only */
323 void coroutine_fn
324 qmp_screendump(const char *filename, const char *device,
325 bool has_head, int64_t head,
326 bool has_format, ImageFormat format, Error **errp)
327 {
328 g_autoptr(pixman_image_t) image = NULL;
329 QemuConsole *con;
330 DisplaySurface *surface;
331 int fd;
332
333 if (device) {
334 con = qemu_console_lookup_by_device_name(device, has_head ? head : 0,
335 errp);
336 if (!con) {
337 return;
338 }
339 } else {
340 if (has_head) {
341 error_setg(errp, "'head' must be specified together with 'device'");
342 return;
343 }
344 con = qemu_console_lookup_by_index(0);
345 if (!con) {
346 error_setg(errp, "There is no console to take a screendump from");
347 return;
348 }
349 }
350
351 object_ref(con);
352 qemu_console_co_wait_update(con);
353
354 /*
355 * All pending coroutines are woken up, while the BQL is held. No
356 * further graphic update are possible until it is released. Take
357 * an image ref before that.
358 */
359 surface = qemu_console_surface(con);
360 if (!surface) {
361 error_setg(errp, "no surface");
362 object_unref(con);
363 return;
364 }
365 image = pixman_image_ref(surface->image);
366 object_unref(con);
367
368 fd = qemu_create(filename, O_WRONLY | O_TRUNC | O_BINARY, 0666, errp);
369 if (fd == -1) {
370 return;
371 }
372
373 /*
374 * The image content could potentially be updated as the coroutine
375 * yields and releases the BQL. It could produce corrupted dump, but
376 * it should be otherwise safe.
377 */
378 if (has_format && format == IMAGE_FORMAT_PNG) {
379 /* PNG format specified for screendump */
380 if (!png_save(fd, image, errp)) {
381 qemu_unlink(filename);
382 }
383 } else {
384 /* PPM format specified/default for screendump */
385 if (!ppm_save(fd, image, errp)) {
386 qemu_unlink(filename);
387 }
388 }
389 }
390 #endif /* CONFIG_PIXMAN */