0006-chardev-connect-socket-to-a-spawned-command.patch 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. From bcc63f775e265df69963a4ad7805b8678ace68f0 Mon Sep 17 00:00:00 2001
  2. From: Alistair Francis <alistair.francis@xilinx.com>
  3. Date: Thu, 21 Dec 2017 11:35:16 -0800
  4. Subject: [PATCH] chardev: connect socket to a spawned command
  5. The command is started in a shell (sh -c) with stdin connect to QEMU
  6. via a Unix domain stream socket. QEMU then exchanges data via its own
  7. end of the socket, just like it normally does.
  8. "-chardev socket" supports some ways of connecting via protocols like
  9. telnet, but that is only a subset of the functionality supported by
  10. tools socat. To use socat instead, for example to connect via a socks
  11. proxy, use:
  12. -chardev 'socket,id=socat,cmd=exec socat FD:0 SOCKS4A:socks-proxy.localdomain:example.com:9999,,socksuser=nobody' \
  13. -device usb-serial,chardev=socat
  14. Beware that commas in the command must be escaped as double commas.
  15. Or interactively in the console:
  16. (qemu) chardev-add socket,id=cat,cmd=cat
  17. (qemu) device_add usb-serial,chardev=cat
  18. ^ac
  19. # cat >/dev/ttyUSB0
  20. hello
  21. hello
  22. Another usage is starting swtpm from inside QEMU. swtpm will
  23. automatically shut down once it looses the connection to the parent
  24. QEMU, so there is no risk of lingering processes:
  25. -chardev 'socket,id=chrtpm0,cmd=exec swtpm socket --terminate --ctrl type=unixio,,clientfd=0 --tpmstate dir=... --log file=swtpm.log' \
  26. -tpmdev emulator,id=tpm0,chardev=chrtpm0 \
  27. -device tpm-tis,tpmdev=tpm0
  28. The patch was discussed upstream, but QEMU developers believe that the
  29. code calling QEMU should be responsible for managing additional
  30. processes. In OE-core, that would imply enhancing runqemu and
  31. oeqa. This patch is a simpler solution.
  32. Because it is not going upstream, the patch was written so that it is
  33. as simple as possible.
  34. Upstream-Status: Inappropriate [embedded specific]
  35. Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
  36. ---
  37. chardev/char-socket.c | 101 ++++++++++++++++++++++++++++++++++++++++++
  38. chardev/char.c | 3 ++
  39. qapi/char.json | 5 +++
  40. 3 files changed, 109 insertions(+)
  41. Index: qemu-6.0.0/chardev/char-socket.c
  42. ===================================================================
  43. --- qemu-6.0.0.orig/chardev/char-socket.c
  44. +++ qemu-6.0.0/chardev/char-socket.c
  45. @@ -1362,6 +1362,67 @@ static bool qmp_chardev_validate_socket(
  46. return true;
  47. }
  48. +#ifndef _WIN32
  49. +static void chardev_open_socket_cmd(Chardev *chr,
  50. + const char *cmd,
  51. + Error **errp)
  52. +{
  53. + int fds[2] = { -1, -1 };
  54. + QIOChannelSocket *sioc = NULL;
  55. + pid_t pid = -1;
  56. + const char *argv[] = { "/bin/sh", "-c", cmd, NULL };
  57. +
  58. + /*
  59. + * We need a Unix domain socket for commands like swtpm and a single
  60. + * connection, therefore we cannot use qio_channel_command_new_spawn()
  61. + * without patching it first. Duplicating the functionality is easier.
  62. + */
  63. + if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds)) {
  64. + error_setg_errno(errp, errno, "Error creating socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC)");
  65. + goto error;
  66. + }
  67. +
  68. + pid = qemu_fork(errp);
  69. + if (pid < 0) {
  70. + goto error;
  71. + }
  72. +
  73. + if (!pid) {
  74. + /* child */
  75. + dup2(fds[1], STDIN_FILENO);
  76. + execv(argv[0], (char * const *)argv);
  77. + _exit(1);
  78. + }
  79. +
  80. + /*
  81. + * Hand over our end of the socket pair to the qio channel.
  82. + *
  83. + * We don't reap the child because it is expected to keep
  84. + * running. We also don't support the "reconnect" option for the
  85. + * same reason.
  86. + */
  87. + sioc = qio_channel_socket_new_fd(fds[0], errp);
  88. + if (!sioc) {
  89. + goto error;
  90. + }
  91. + fds[0] = -1;
  92. +
  93. + g_free(chr->filename);
  94. + chr->filename = g_strdup_printf("cmd:%s", cmd);
  95. + tcp_chr_new_client(chr, sioc);
  96. +
  97. + error:
  98. + if (fds[0] >= 0) {
  99. + close(fds[0]);
  100. + }
  101. + if (fds[1] >= 0) {
  102. + close(fds[1]);
  103. + }
  104. + if (sioc) {
  105. + object_unref(OBJECT(sioc));
  106. + }
  107. +}
  108. +#endif
  109. static void qmp_chardev_open_socket(Chardev *chr,
  110. ChardevBackend *backend,
  111. @@ -1370,6 +1431,9 @@ static void qmp_chardev_open_socket(Char
  112. {
  113. SocketChardev *s = SOCKET_CHARDEV(chr);
  114. ChardevSocket *sock = backend->u.socket.data;
  115. +#ifndef _WIN32
  116. + const char *cmd = sock->cmd;
  117. +#endif
  118. bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
  119. bool is_listen = sock->has_server ? sock->server : true;
  120. bool is_telnet = sock->has_telnet ? sock->telnet : false;
  121. @@ -1446,6 +1510,14 @@ static void qmp_chardev_open_socket(Char
  122. update_disconnected_filename(s);
  123. +#ifndef _WIN32
  124. + if (cmd) {
  125. + chardev_open_socket_cmd(chr, cmd, errp);
  126. +
  127. + /* everything ready (or failed permanently) before we return */
  128. + *be_opened = true;
  129. + } else
  130. +#endif
  131. if (s->is_listen) {
  132. if (qmp_chardev_open_socket_server(chr, is_telnet || is_tn3270,
  133. is_waitconnect, errp) < 0) {
  134. @@ -1465,6 +1537,9 @@ static void qemu_chr_parse_socket(QemuOp
  135. const char *host = qemu_opt_get(opts, "host");
  136. const char *port = qemu_opt_get(opts, "port");
  137. const char *fd = qemu_opt_get(opts, "fd");
  138. +#ifndef _WIN32
  139. + const char *cmd = qemu_opt_get(opts, "cmd");
  140. +#endif
  141. #ifdef CONFIG_LINUX
  142. bool tight = qemu_opt_get_bool(opts, "tight", true);
  143. bool abstract = qemu_opt_get_bool(opts, "abstract", false);
  144. @@ -1472,6 +1547,20 @@ static void qemu_chr_parse_socket(QemuOp
  145. SocketAddressLegacy *addr;
  146. ChardevSocket *sock;
  147. +#ifndef _WIN32
  148. + if (cmd) {
  149. + /*
  150. + * Here we have to ensure that no options are set which are incompatible with
  151. + * spawning a command, otherwise unmodified code that doesn't know about
  152. + * command spawning (like socket_reconnect_timeout()) might get called.
  153. + */
  154. + if (path || sock->server || sock->has_telnet || sock->has_tn3270 || sock->reconnect || host || port || sock->tls_creds) {
  155. + error_setg(errp, "chardev: socket: cmd does not support any additional options");
  156. + return;
  157. + }
  158. + } else
  159. +#endif
  160. +
  161. if ((!!path + !!fd + !!host) != 1) {
  162. error_setg(errp,
  163. "Exactly one of 'path', 'fd' or 'host' required");
  164. @@ -1522,13 +1611,24 @@ static void qemu_chr_parse_socket(QemuOp
  165. sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds"));
  166. sock->has_tls_authz = qemu_opt_get(opts, "tls-authz");
  167. sock->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz"));
  168. +#ifndef _WIN32
  169. + sock->cmd = g_strdup(cmd);
  170. +#endif
  171. addr = g_new0(SocketAddressLegacy, 1);
  172. +#ifndef _WIN32
  173. + if (path || cmd) {
  174. +#else
  175. if (path) {
  176. +#endif
  177. UnixSocketAddress *q_unix;
  178. addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
  179. q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
  180. +#ifndef _WIN32
  181. + q_unix->path = cmd ? g_strdup_printf("cmd:%s", cmd) : g_strdup(path);
  182. +#else
  183. q_unix->path = g_strdup(path);
  184. +#endif
  185. #ifdef CONFIG_LINUX
  186. q_unix->has_tight = true;
  187. q_unix->tight = tight;
  188. Index: qemu-6.0.0/chardev/char.c
  189. ===================================================================
  190. --- qemu-6.0.0.orig/chardev/char.c
  191. +++ qemu-6.0.0/chardev/char.c
  192. @@ -840,6 +840,9 @@ QemuOptsList qemu_chardev_opts = {
  193. .name = "path",
  194. .type = QEMU_OPT_STRING,
  195. },{
  196. + .name = "cmd",
  197. + .type = QEMU_OPT_STRING,
  198. + },{
  199. .name = "host",
  200. .type = QEMU_OPT_STRING,
  201. },{
  202. Index: qemu-6.0.0/qapi/char.json
  203. ===================================================================
  204. --- qemu-6.0.0.orig/qapi/char.json
  205. +++ qemu-6.0.0/qapi/char.json
  206. @@ -250,6 +250,10 @@
  207. #
  208. # @addr: socket address to listen on (server=true)
  209. # or connect to (server=false)
  210. +# @cmd: command to run via "sh -c" with stdin as one end of
  211. +# a AF_UNIX SOCK_DSTREAM socket pair. The other end
  212. +# is used by the chardev. Either an addr or a cmd can
  213. +# be specified, but not both.
  214. # @tls-creds: the ID of the TLS credentials object (since 2.6)
  215. # @tls-authz: the ID of the QAuthZ authorization object against which
  216. # the client's x509 distinguished name will be validated. This
  217. @@ -276,6 +280,7 @@
  218. ##
  219. { 'struct': 'ChardevSocket',
  220. 'data': { 'addr': 'SocketAddressLegacy',
  221. + '*cmd': 'str',
  222. '*tls-creds': 'str',
  223. '*tls-authz' : 'str',
  224. '*server': 'bool',