summaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/qemu/qemu/chardev-connect-socket-to-a-spawned-command.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-devtools/qemu/qemu/chardev-connect-socket-to-a-spawned-command.patch')
-rw-r--r--meta/recipes-devtools/qemu/qemu/chardev-connect-socket-to-a-spawned-command.patch227
1 files changed, 227 insertions, 0 deletions
diff --git a/meta/recipes-devtools/qemu/qemu/chardev-connect-socket-to-a-spawned-command.patch b/meta/recipes-devtools/qemu/qemu/chardev-connect-socket-to-a-spawned-command.patch
new file mode 100644
index 0000000000..49d4af2e5e
--- /dev/null
+++ b/meta/recipes-devtools/qemu/qemu/chardev-connect-socket-to-a-spawned-command.patch
@@ -0,0 +1,227 @@
1From aa3aef4cf5f4dd98f9133df085e825ff5da7dcbd Mon Sep 17 00:00:00 2001
2From: Patrick Ohly <patrick.ohly@intel.com>
3Date: Fri, 27 Oct 2017 15:23:35 +0200
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
48---
49 chardev/char-socket.c | 86 ++++++++++++++++++++++++++++++++++++++++++++++++---
50 chardev/char.c | 3 ++
51 qapi-schema.json | 5 +++
52 3 files changed, 90 insertions(+), 4 deletions(-)
53
54diff --git a/chardev/char-socket.c b/chardev/char-socket.c
55index 1ae730a4..c366a02a 100644
56--- a/chardev/char-socket.c
57+++ b/chardev/char-socket.c
58@@ -854,6 +854,66 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
59 return false;
60 }
61
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+
122 static void qmp_chardev_open_socket(Chardev *chr,
123 ChardevBackend *backend,
124 bool *be_opened,
125@@ -861,6 +921,7 @@ static void qmp_chardev_open_socket(Chardev *chr,
126 {
127 SocketChardev *s = SOCKET_CHARDEV(chr);
128 ChardevSocket *sock = backend->u.socket.data;
129+ const char *cmd = sock->cmd;
130 bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
131 bool is_listen = sock->has_server ? sock->server : true;
132 bool is_telnet = sock->has_telnet ? sock->telnet : false;
133@@ -928,7 +989,12 @@ static void qmp_chardev_open_socket(Chardev *chr,
134 s->reconnect_time = reconnect;
135 }
136
137- if (s->reconnect_time) {
138+ if (cmd) {
139+ chardev_open_socket_cmd(chr, cmd, errp);
140+
141+ /* everything ready (or failed permanently) before we return */
142+ *be_opened = true;
143+ } else if (s->reconnect_time) {
144 sioc = qio_channel_socket_new();
145 tcp_chr_set_client_ioc_name(chr, sioc);
146 qio_channel_socket_connect_async(sioc, s->addr,
147@@ -987,11 +1053,22 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
148 const char *host = qemu_opt_get(opts, "host");
149 const char *port = qemu_opt_get(opts, "port");
150 const char *tls_creds = qemu_opt_get(opts, "tls-creds");
151+ const char *cmd = qemu_opt_get(opts, "cmd");
152 SocketAddressLegacy *addr;
153 ChardevSocket *sock;
154
155 backend->type = CHARDEV_BACKEND_KIND_SOCKET;
156- if (!path) {
157+ if (cmd) {
158+ /*
159+ * Here we have to ensure that no options are set which are incompatible with
160+ * spawning a command, otherwise unmodified code that doesn't know about
161+ * command spawning (like socket_reconnect_timeout()) might get called.
162+ */
163+ if (path || is_listen || is_telnet || is_tn3270 || reconnect || host || port || tls_creds) {
164+ error_setg(errp, "chardev: socket: cmd does not support any additional options");
165+ return;
166+ }
167+ } else if (!path) {
168 if (!host) {
169 error_setg(errp, "chardev: socket: no host given");
170 return;
171@@ -1023,13 +1100,14 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
172 sock->has_reconnect = true;
173 sock->reconnect = reconnect;
174 sock->tls_creds = g_strdup(tls_creds);
175+ sock->cmd = g_strdup(cmd);
176
177 addr = g_new0(SocketAddressLegacy, 1);
178- if (path) {
179+ if (path || cmd) {
180 UnixSocketAddress *q_unix;
181 addr->type = SOCKET_ADDRESS_LEGACY_KIND_UNIX;
182 q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
183- q_unix->path = g_strdup(path);
184+ q_unix->path = cmd ? g_strdup_printf("cmd:%s", cmd) : g_strdup(path);
185 } else {
186 addr->type = SOCKET_ADDRESS_LEGACY_KIND_INET;
187 addr->u.inet.data = g_new(InetSocketAddress, 1);
188diff --git a/chardev/char.c b/chardev/char.c
189index 5d283b90..ccb329d4 100644
190--- a/chardev/char.c
191+++ b/chardev/char.c
192@@ -782,6 +782,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 },{
202diff --git a/qapi-schema.json b/qapi-schema.json
203index 78a00bc8..790b026d 100644
204--- a/qapi-schema.json
205+++ b/qapi-schema.json
206@@ -5004,6 +5004,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 # @server: create server socket (default: true)
216 # @wait: wait for incoming connection on server
217@@ -5021,6 +5025,7 @@
218 # Since: 1.4
219 ##
220 { 'struct': 'ChardevSocket', 'data': { 'addr' : 'SocketAddressLegacy',
221+ '*cmd' : 'str',
222 '*tls-creds' : 'str',
223 '*server' : 'bool',
224 '*wait' : 'bool',
225--
2262.11.0
227