blob: 2afe4e9376d5ad5b5c00f73fd00373e086595511 [file] [log] [blame]
Brad Bishop1a4b7ee2018-12-16 17:11:34 -08001From 20a09bb18907e67565c54fc505a741cbbef53f7f Mon Sep 17 00:00:00 2001
Brad Bishop316dfdd2018-06-25 12:45:53 -04002From: Alistair Francis <alistair.francis@xilinx.com>
3Date: Thu, 21 Dec 2017 11:35:16 -0800
4Subject: [PATCH] chardev: connect socket to a spawned command
5
6The command is started in a shell (sh -c) with stdin connect to QEMU
7via a Unix domain stream socket. QEMU then exchanges data via its own
8end of the socket, just like it normally does.
9
10"-chardev socket" supports some ways of connecting via protocols like
11telnet, but that is only a subset of the functionality supported by
12tools socat. To use socat instead, for example to connect via a socks
13proxy, use:
14
15 -chardev 'socket,id=socat,cmd=exec socat FD:0 SOCKS4A:socks-proxy.localdomain:example.com:9999,,socksuser=nobody' \
16 -device usb-serial,chardev=socat
17
18Beware that commas in the command must be escaped as double commas.
19
20Or interactively in the console:
21 (qemu) chardev-add socket,id=cat,cmd=cat
22 (qemu) device_add usb-serial,chardev=cat
23 ^ac
24 # cat >/dev/ttyUSB0
25 hello
26 hello
27
28Another usage is starting swtpm from inside QEMU. swtpm will
29automatically shut down once it looses the connection to the parent
30QEMU, so there is no risk of lingering processes:
31
32 -chardev 'socket,id=chrtpm0,cmd=exec swtpm socket --terminate --ctrl type=unixio,,clientfd=0 --tpmstate dir=... --log file=swtpm.log' \
33 -tpmdev emulator,id=tpm0,chardev=chrtpm0 \
34 -device tpm-tis,tpmdev=tpm0
35
36The patch was discussed upstream, but QEMU developers believe that the
37code calling QEMU should be responsible for managing additional
38processes. In OE-core, that would imply enhancing runqemu and
39oeqa. This patch is a simpler solution.
40
41Because it is not going upstream, the patch was written so that it is
42as simple as possible.
43
44Upstream-Status: Inappropriate [embedded specific]
45
46Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
47---
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080048 chardev/char-socket.c | 102 ++++++++++++++++++++++++++++++++++++++++++
49 chardev/char.c | 3 ++
50 qapi/char.json | 5 +++
51 3 files changed, 110 insertions(+)
Brad Bishop316dfdd2018-06-25 12:45:53 -040052
53diff --git a/chardev/char-socket.c b/chardev/char-socket.c
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080054index 159e69c3b1..84778cf31a 100644
Brad Bishop316dfdd2018-06-25 12:45:53 -040055--- a/chardev/char-socket.c
56+++ b/chardev/char-socket.c
Brad Bishop1a4b7ee2018-12-16 17:11:34 -080057@@ -934,6 +934,68 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
Brad Bishop316dfdd2018-06-25 12:45:53 -040058 return false;
59 }
60
61+#ifndef _WIN32
62+static void chardev_open_socket_cmd(Chardev *chr,
63+ const char *cmd,
64+ Error **errp)
65+{
66+ int fds[2] = { -1, -1 };
67+ QIOChannelSocket *sioc = NULL;
68+ pid_t pid = -1;
69+ const char *argv[] = { "/bin/sh", "-c", cmd, NULL };
70+
71+ /*
72+ * We need a Unix domain socket for commands like swtpm and a single
73+ * connection, therefore we cannot use qio_channel_command_new_spawn()
74+ * without patching it first. Duplicating the functionality is easier.
75+ */
76+ if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds)) {
77+ error_setg_errno(errp, errno, "Error creating socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC)");
78+ goto error;
79+ }
80+
81+ pid = qemu_fork(errp);
82+ if (pid < 0) {
83+ goto error;
84+ }
85+
86+ if (!pid) {
87+ /* child */
88+ dup2(fds[1], STDIN_FILENO);
89+ execv(argv[0], (char * const *)argv);
90+ _exit(1);
91+ }
92+
93+ /*
94+ * Hand over our end of the socket pair to the qio channel.
95+ *
96+ * We don't reap the child because it is expected to keep
97+ * running. We also don't support the "reconnect" option for the
98+ * same reason.
99+ */
100+ sioc = qio_channel_socket_new_fd(fds[0], errp);
101+ if (!sioc) {
102+ goto error;
103+ }
104+ fds[0] = -1;
105+
106+ g_free(chr->filename);
107+ chr->filename = g_strdup_printf("cmd:%s", cmd);
108+ tcp_chr_new_client(chr, sioc);
109+
110+ error:
111+ if (fds[0] >= 0) {
112+ close(fds[0]);
113+ }
114+ if (fds[1] >= 0) {
115+ close(fds[1]);
116+ }
117+ if (sioc) {
118+ object_unref(OBJECT(sioc));
119+ }
120+}
121+#endif
122+
123 static void qmp_chardev_open_socket(Chardev *chr,
124 ChardevBackend *backend,
125 bool *be_opened,
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800126@@ -941,6 +1003,9 @@ static void qmp_chardev_open_socket(Chardev *chr,
Brad Bishop316dfdd2018-06-25 12:45:53 -0400127 {
128 SocketChardev *s = SOCKET_CHARDEV(chr);
129 ChardevSocket *sock = backend->u.socket.data;
130+#ifndef _WIN32
131+ const char *cmd = sock->cmd;
132+#endif
133 bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
134 bool is_listen = sock->has_server ? sock->server : true;
135 bool is_telnet = sock->has_telnet ? sock->telnet : false;
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800136@@ -1008,6 +1073,14 @@ static void qmp_chardev_open_socket(Chardev *chr,
Brad Bishop316dfdd2018-06-25 12:45:53 -0400137 s->reconnect_time = reconnect;
138 }
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800139
Brad Bishop316dfdd2018-06-25 12:45:53 -0400140+#ifndef _WIN32
141+ if (cmd) {
142+ chardev_open_socket_cmd(chr, cmd, errp);
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800143+
Brad Bishop316dfdd2018-06-25 12:45:53 -0400144+ /* everything ready (or failed permanently) before we return */
145+ *be_opened = true;
146+ } else
147+#endif
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800148 /* If reconnect_time is set, will do that in chr_machine_done. */
149 if (!s->reconnect_time) {
150 if (s->is_listen) {
151@@ -1065,9 +1138,26 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
Brad Bishop316dfdd2018-06-25 12:45:53 -0400152 const char *port = qemu_opt_get(opts, "port");
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800153 const char *fd = qemu_opt_get(opts, "fd");
Brad Bishop316dfdd2018-06-25 12:45:53 -0400154 const char *tls_creds = qemu_opt_get(opts, "tls-creds");
155+#ifndef _WIN32
156+ const char *cmd = qemu_opt_get(opts, "cmd");
157+#endif
158 SocketAddressLegacy *addr;
159 ChardevSocket *sock;
160
Brad Bishop316dfdd2018-06-25 12:45:53 -0400161+#ifndef _WIN32
162+ if (cmd) {
163+ /*
164+ * Here we have to ensure that no options are set which are incompatible with
165+ * spawning a command, otherwise unmodified code that doesn't know about
166+ * command spawning (like socket_reconnect_timeout()) might get called.
167+ */
168+ if (path || is_listen || is_telnet || is_tn3270 || reconnect || host || port || tls_creds) {
169+ error_setg(errp, "chardev: socket: cmd does not support any additional options");
170+ return;
171+ }
172+ } else
173+#endif
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800174+
175 if ((!!path + !!fd + !!host) != 1) {
176 error_setg(errp,
177 "Exactly one of 'path', 'fd' or 'host' required");
178@@ -1112,12 +1202,24 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
Brad Bishop316dfdd2018-06-25 12:45:53 -0400179 sock->reconnect = reconnect;
180 sock->tls_creds = g_strdup(tls_creds);
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800181
Brad Bishop316dfdd2018-06-25 12:45:53 -0400182+#ifndef _WIN32
183+ sock->cmd = g_strdup(cmd);
184+#endif
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800185+
Brad Bishop316dfdd2018-06-25 12:45:53 -0400186 addr = g_new0(SocketAddressLegacy, 1);
187+#ifndef _WIN32
188+ if (path || cmd) {
189+#else
190 if (path) {
191+#endif
192 UnixSocketAddress *q_unix;
193 addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
194 q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
195+#ifndef _WIN32
196+ q_unix->path = cmd ? g_strdup_printf("cmd:%s", cmd) : g_strdup(path);
197+#else
198 q_unix->path = g_strdup(path);
199+#endif
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800200 } else if (host) {
Brad Bishop316dfdd2018-06-25 12:45:53 -0400201 addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
202 addr->u.inet.data = g_new(InetSocketAddress, 1);
203diff --git a/chardev/char.c b/chardev/char.c
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800204index 76d866e6fe..9747d51d7c 100644
Brad Bishop316dfdd2018-06-25 12:45:53 -0400205--- a/chardev/char.c
206+++ b/chardev/char.c
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800207@@ -792,6 +792,9 @@ QemuOptsList qemu_chardev_opts = {
208 },{
Brad Bishop316dfdd2018-06-25 12:45:53 -0400209 .name = "path",
210 .type = QEMU_OPT_STRING,
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800211+ },{
Brad Bishop316dfdd2018-06-25 12:45:53 -0400212+ .name = "cmd",
213+ .type = QEMU_OPT_STRING,
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800214 },{
Brad Bishop316dfdd2018-06-25 12:45:53 -0400215 .name = "host",
216 .type = QEMU_OPT_STRING,
217diff --git a/qapi/char.json b/qapi/char.json
Brad Bishop1a4b7ee2018-12-16 17:11:34 -0800218index ae19dcd1ed..6de0f29bcd 100644
Brad Bishop316dfdd2018-06-25 12:45:53 -0400219--- a/qapi/char.json
220+++ b/qapi/char.json
221@@ -241,6 +241,10 @@
222 #
223 # @addr: socket address to listen on (server=true)
224 # or connect to (server=false)
225+# @cmd: command to run via "sh -c" with stdin as one end of
226+# a AF_UNIX SOCK_DSTREAM socket pair. The other end
227+# is used by the chardev. Either an addr or a cmd can
228+# be specified, but not both.
229 # @tls-creds: the ID of the TLS credentials object (since 2.6)
230 # @server: create server socket (default: true)
231 # @wait: wait for incoming connection on server
232@@ -258,6 +262,7 @@
233 # Since: 1.4
234 ##
235 { 'struct': 'ChardevSocket', 'data': { 'addr' : 'SocketAddressLegacy',
236+ '*cmd' : 'str',
237 '*tls-creds' : 'str',
238 '*server' : 'bool',
239 '*wait' : 'bool',