spice-app.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. /*
  2. * QEMU external Spice client display driver
  3. *
  4. * Copyright (c) 2018 Marc-André Lureau <marcandre.lureau@redhat.com>
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. #include "qemu/osdep.h"
  25. #include <gio/gio.h>
  26. #include "ui/console.h"
  27. #include "ui/spice-display.h"
  28. #include "qemu/config-file.h"
  29. #include "qemu/error-report.h"
  30. #include "qemu/option.h"
  31. #include "qemu/cutils.h"
  32. #include "qemu/module.h"
  33. #include "qapi/error.h"
  34. #include "io/channel-command.h"
  35. #include "chardev/spice.h"
  36. #include "system/system.h"
  37. #include "qom/object.h"
  38. static const char *tmp_dir;
  39. static char *app_dir;
  40. static char *sock_path;
  41. struct VCChardev {
  42. SpiceChardev parent;
  43. };
  44. struct VCChardevClass {
  45. ChardevClass parent;
  46. void (*parent_open)(Chardev *chr, ChardevBackend *backend,
  47. bool *be_opened, Error **errp);
  48. };
  49. #define TYPE_CHARDEV_VC "chardev-vc"
  50. OBJECT_DECLARE_TYPE(VCChardev, VCChardevClass, CHARDEV_VC)
  51. static ChardevBackend *
  52. chr_spice_backend_new(void)
  53. {
  54. ChardevBackend *be = g_new0(ChardevBackend, 1);
  55. be->type = CHARDEV_BACKEND_KIND_SPICEPORT;
  56. be->u.spiceport.data = g_new0(ChardevSpicePort, 1);
  57. return be;
  58. }
  59. static void vc_chr_open(Chardev *chr,
  60. ChardevBackend *backend,
  61. bool *be_opened,
  62. Error **errp)
  63. {
  64. VCChardevClass *vc = CHARDEV_VC_GET_CLASS(chr);
  65. ChardevBackend *be;
  66. const char *fqdn = NULL;
  67. if (strstart(chr->label, "serial", NULL)) {
  68. fqdn = "org.qemu.console.serial.0";
  69. } else if (strstart(chr->label, "parallel", NULL)) {
  70. fqdn = "org.qemu.console.parallel.0";
  71. } else if (strstart(chr->label, "compat_monitor", NULL)) {
  72. fqdn = "org.qemu.monitor.hmp.0";
  73. }
  74. be = chr_spice_backend_new();
  75. be->u.spiceport.data->fqdn = fqdn ?
  76. g_strdup(fqdn) : g_strdup_printf("org.qemu.console.%s", chr->label);
  77. vc->parent_open(chr, be, be_opened, errp);
  78. qapi_free_ChardevBackend(be);
  79. }
  80. static void vc_chr_set_echo(Chardev *chr, bool echo)
  81. {
  82. /* TODO: set echo for frontends QMP and qtest */
  83. }
  84. static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp)
  85. {
  86. /* fqdn is dealt with in vc_chr_open() */
  87. }
  88. static void char_vc_class_init(ObjectClass *oc, void *data)
  89. {
  90. VCChardevClass *vc = CHARDEV_VC_CLASS(oc);
  91. ChardevClass *cc = CHARDEV_CLASS(oc);
  92. vc->parent_open = cc->open;
  93. cc->parse = vc_chr_parse;
  94. cc->open = vc_chr_open;
  95. cc->chr_set_echo = vc_chr_set_echo;
  96. }
  97. static const TypeInfo char_vc_type_info = {
  98. .name = TYPE_CHARDEV_VC,
  99. .parent = TYPE_CHARDEV_SPICEPORT,
  100. .instance_size = sizeof(VCChardev),
  101. .class_init = char_vc_class_init,
  102. .class_size = sizeof(VCChardevClass),
  103. };
  104. static void spice_app_atexit(void)
  105. {
  106. if (sock_path) {
  107. unlink(sock_path);
  108. }
  109. if (tmp_dir) {
  110. rmdir(tmp_dir);
  111. }
  112. g_free(sock_path);
  113. g_free(app_dir);
  114. }
  115. static void spice_app_display_early_init(DisplayOptions *opts)
  116. {
  117. QemuOpts *qopts;
  118. QemuOptsList *list;
  119. GError *err = NULL;
  120. if (opts->has_full_screen) {
  121. error_report("spice-app full-screen isn't supported yet.");
  122. exit(1);
  123. }
  124. if (opts->has_window_close) {
  125. error_report("spice-app window-close isn't supported yet.");
  126. exit(1);
  127. }
  128. atexit(spice_app_atexit);
  129. if (qemu_name) {
  130. app_dir = g_build_filename(g_get_user_runtime_dir(),
  131. "qemu", qemu_name, NULL);
  132. if (g_mkdir_with_parents(app_dir, S_IRWXU) < -1) {
  133. error_report("Failed to create directory %s: %s",
  134. app_dir, strerror(errno));
  135. exit(1);
  136. }
  137. } else {
  138. app_dir = g_dir_make_tmp(NULL, &err);
  139. tmp_dir = app_dir;
  140. if (err) {
  141. error_report("Failed to create temporary directory: %s",
  142. err->message);
  143. exit(1);
  144. }
  145. }
  146. list = qemu_find_opts("spice");
  147. if (list == NULL) {
  148. error_report("spice-app missing spice support");
  149. exit(1);
  150. }
  151. type_register_static(&char_vc_type_info);
  152. sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL);
  153. qopts = qemu_opts_create(list, NULL, 0, &error_abort);
  154. qemu_opt_set(qopts, "disable-ticketing", "on", &error_abort);
  155. qemu_opt_set(qopts, "unix", "on", &error_abort);
  156. qemu_opt_set(qopts, "addr", sock_path, &error_abort);
  157. qemu_opt_set(qopts, "image-compression", "off", &error_abort);
  158. qemu_opt_set(qopts, "streaming-video", "off", &error_abort);
  159. #ifdef HAVE_SPICE_GL
  160. qemu_opt_set(qopts, "gl", opts->has_gl ? "on" : "off", &error_abort);
  161. display_opengl = opts->has_gl;
  162. #endif
  163. }
  164. static void spice_app_display_init(DisplayState *ds, DisplayOptions *opts)
  165. {
  166. ChardevBackend *be = chr_spice_backend_new();
  167. QemuOpts *qopts;
  168. GError *err = NULL;
  169. gchar *uri;
  170. be->u.spiceport.data->fqdn = g_strdup("org.qemu.monitor.qmp.0");
  171. qemu_chardev_new("org.qemu.monitor.qmp", TYPE_CHARDEV_SPICEPORT,
  172. be, NULL, &error_abort);
  173. qopts = qemu_opts_create(qemu_find_opts("mon"),
  174. NULL, 0, &error_fatal);
  175. qemu_opt_set(qopts, "chardev", "org.qemu.monitor.qmp", &error_abort);
  176. qemu_opt_set(qopts, "mode", "control", &error_abort);
  177. qapi_free_ChardevBackend(be);
  178. uri = g_strjoin("", "spice+unix://", app_dir, "/", "spice.sock", NULL);
  179. info_report("Launching display with URI: %s", uri);
  180. g_app_info_launch_default_for_uri(uri, NULL, &err);
  181. if (err) {
  182. error_report("Failed to launch %s URI: %s", uri, err->message);
  183. error_report("You need a capable Spice client, "
  184. "such as virt-viewer 8.0");
  185. exit(1);
  186. }
  187. g_free(uri);
  188. }
  189. static QemuDisplay qemu_display_spice_app = {
  190. .type = DISPLAY_TYPE_SPICE_APP,
  191. .early_init = spice_app_display_early_init,
  192. .init = spice_app_display_init,
  193. .vc = "vc",
  194. };
  195. static void register_spice_app(void)
  196. {
  197. qemu_display_register(&qemu_display_spice_app);
  198. }
  199. type_init(register_spice_app);
  200. module_dep("ui-spice-core");
  201. module_dep("chardev-spice");