2
0

spice-app.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  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 "qemu/config-file.h"
  28. #include "qemu/option.h"
  29. #include "qemu/cutils.h"
  30. #include "qemu/module.h"
  31. #include "qapi/error.h"
  32. #include "io/channel-command.h"
  33. #include "chardev/spice.h"
  34. #include "sysemu/sysemu.h"
  35. #include "qom/object.h"
  36. static const char *tmp_dir;
  37. static char *app_dir;
  38. static char *sock_path;
  39. struct VCChardev {
  40. SpiceChardev parent;
  41. };
  42. struct VCChardevClass {
  43. ChardevClass parent;
  44. void (*parent_open)(Chardev *chr, ChardevBackend *backend,
  45. bool *be_opened, Error **errp);
  46. };
  47. #define TYPE_CHARDEV_VC "chardev-vc"
  48. OBJECT_DECLARE_TYPE(VCChardev, VCChardevClass, CHARDEV_VC)
  49. static ChardevBackend *
  50. chr_spice_backend_new(void)
  51. {
  52. ChardevBackend *be = g_new0(ChardevBackend, 1);
  53. be->type = CHARDEV_BACKEND_KIND_SPICEPORT;
  54. be->u.spiceport.data = g_new0(ChardevSpicePort, 1);
  55. return be;
  56. }
  57. static void vc_chr_open(Chardev *chr,
  58. ChardevBackend *backend,
  59. bool *be_opened,
  60. Error **errp)
  61. {
  62. VCChardevClass *vc = CHARDEV_VC_GET_CLASS(chr);
  63. ChardevBackend *be;
  64. const char *fqdn = NULL;
  65. if (strstart(chr->label, "serial", NULL)) {
  66. fqdn = "org.qemu.console.serial.0";
  67. } else if (strstart(chr->label, "parallel", NULL)) {
  68. fqdn = "org.qemu.console.parallel.0";
  69. } else if (strstart(chr->label, "compat_monitor", NULL)) {
  70. fqdn = "org.qemu.monitor.hmp.0";
  71. }
  72. be = chr_spice_backend_new();
  73. be->u.spiceport.data->fqdn = fqdn ?
  74. g_strdup(fqdn) : g_strdup_printf("org.qemu.console.%s", chr->label);
  75. vc->parent_open(chr, be, be_opened, errp);
  76. qapi_free_ChardevBackend(be);
  77. }
  78. static void vc_chr_set_echo(Chardev *chr, bool echo)
  79. {
  80. /* TODO: set echo for frontends QMP and qtest */
  81. }
  82. static void char_vc_class_init(ObjectClass *oc, void *data)
  83. {
  84. VCChardevClass *vc = CHARDEV_VC_CLASS(oc);
  85. ChardevClass *cc = CHARDEV_CLASS(oc);
  86. vc->parent_open = cc->open;
  87. cc->parse = qemu_chr_parse_vc;
  88. cc->open = vc_chr_open;
  89. cc->chr_set_echo = vc_chr_set_echo;
  90. }
  91. static const TypeInfo char_vc_type_info = {
  92. .name = TYPE_CHARDEV_VC,
  93. .parent = TYPE_CHARDEV_SPICEPORT,
  94. .instance_size = sizeof(VCChardev),
  95. .class_init = char_vc_class_init,
  96. .class_size = sizeof(VCChardevClass),
  97. };
  98. static void spice_app_atexit(void)
  99. {
  100. if (sock_path) {
  101. unlink(sock_path);
  102. }
  103. if (tmp_dir) {
  104. rmdir(tmp_dir);
  105. }
  106. g_free(sock_path);
  107. g_free(app_dir);
  108. }
  109. static void spice_app_display_early_init(DisplayOptions *opts)
  110. {
  111. QemuOpts *qopts;
  112. GError *err = NULL;
  113. if (opts->has_full_screen) {
  114. error_report("spice-app full-screen isn't supported yet.");
  115. exit(1);
  116. }
  117. if (opts->has_window_close) {
  118. error_report("spice-app window-close isn't supported yet.");
  119. exit(1);
  120. }
  121. atexit(spice_app_atexit);
  122. if (qemu_name) {
  123. app_dir = g_build_filename(g_get_user_runtime_dir(),
  124. "qemu", qemu_name, NULL);
  125. if (g_mkdir_with_parents(app_dir, S_IRWXU) < -1) {
  126. error_report("Failed to create directory %s: %s",
  127. app_dir, strerror(errno));
  128. exit(1);
  129. }
  130. } else {
  131. app_dir = g_dir_make_tmp(NULL, &err);
  132. tmp_dir = app_dir;
  133. if (err) {
  134. error_report("Failed to create temporary directory: %s",
  135. err->message);
  136. exit(1);
  137. }
  138. }
  139. type_register(&char_vc_type_info);
  140. sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL);
  141. qopts = qemu_opts_create(qemu_find_opts("spice"), NULL, 0, &error_abort);
  142. qemu_opt_set(qopts, "disable-ticketing", "on", &error_abort);
  143. qemu_opt_set(qopts, "unix", "on", &error_abort);
  144. qemu_opt_set(qopts, "addr", sock_path, &error_abort);
  145. qemu_opt_set(qopts, "image-compression", "off", &error_abort);
  146. qemu_opt_set(qopts, "streaming-video", "off", &error_abort);
  147. #ifdef CONFIG_OPENGL
  148. qemu_opt_set(qopts, "gl", opts->has_gl ? "on" : "off", &error_abort);
  149. display_opengl = opts->has_gl;
  150. #endif
  151. }
  152. static void spice_app_display_init(DisplayState *ds, DisplayOptions *opts)
  153. {
  154. ChardevBackend *be = chr_spice_backend_new();
  155. QemuOpts *qopts;
  156. GError *err = NULL;
  157. gchar *uri;
  158. be->u.spiceport.data->fqdn = g_strdup("org.qemu.monitor.qmp.0");
  159. qemu_chardev_new("org.qemu.monitor.qmp", TYPE_CHARDEV_SPICEPORT,
  160. be, NULL, &error_abort);
  161. qopts = qemu_opts_create(qemu_find_opts("mon"),
  162. NULL, 0, &error_fatal);
  163. qemu_opt_set(qopts, "chardev", "org.qemu.monitor.qmp", &error_abort);
  164. qemu_opt_set(qopts, "mode", "control", &error_abort);
  165. qapi_free_ChardevBackend(be);
  166. uri = g_strjoin("", "spice+unix://", app_dir, "/", "spice.sock", NULL);
  167. info_report("Launching display with URI: %s", uri);
  168. g_app_info_launch_default_for_uri(uri, NULL, &err);
  169. if (err) {
  170. error_report("Failed to launch %s URI: %s", uri, err->message);
  171. error_report("You need a capable Spice client, "
  172. "such as virt-viewer 8.0");
  173. exit(1);
  174. }
  175. g_free(uri);
  176. }
  177. static QemuDisplay qemu_display_spice_app = {
  178. .type = DISPLAY_TYPE_SPICE_APP,
  179. .early_init = spice_app_display_early_init,
  180. .init = spice_app_display_init,
  181. };
  182. static void register_spice_app(void)
  183. {
  184. qemu_display_register(&qemu_display_spice_app);
  185. }
  186. type_init(register_spice_app);