blob: 63a99c962058dbf04ac0771c331be6e70140612f [file] [log] [blame]
Andrew Geisslerd5838332022-05-27 11:33:10 -05001From 14cd62607c9de232edf0a9b8503bd02783e03411 Mon Sep 17 00:00:00 2001
Brad Bishopc342db32019-05-15 21:57:59 -04002From: Alistair Francis <alistair.francis@xilinx.com>
3Date: Thu, 21 Dec 2017 11:35:16 -0800
Andrew Geisslerd5838332022-05-27 11:33:10 -05004Subject: [PATCH 02/12] chardev: connect socket to a spawned command
Brad Bishopc342db32019-05-15 21:57:59 -04005
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---
Andrew Geisslerd5838332022-05-27 11:33:10 -050049 chardev/char-socket.c | 100 ++++++++++++++++++++++++++++++++++++++++++
Brad Bishopc342db32019-05-15 21:57:59 -040050 chardev/char.c | 3 ++
51 qapi/char.json | 5 +++
Andrew Geisslerd5838332022-05-27 11:33:10 -050052 3 files changed, 108 insertions(+)
Brad Bishopc342db32019-05-15 21:57:59 -040053
Andrew Geisslerd5838332022-05-27 11:33:10 -050054diff --git a/chardev/char-socket.c b/chardev/char-socket.c
55index fab2d791d..c79641f24 100644
56--- a/chardev/char-socket.c
57+++ b/chardev/char-socket.c
58@@ -1315,6 +1315,67 @@ static bool qmp_chardev_validate_socket(ChardevSocket *sock,
Brad Bishopc342db32019-05-15 21:57:59 -040059 return true;
60 }
61
62+#ifndef _WIN32
63+static void chardev_open_socket_cmd(Chardev *chr,
64+ const char *cmd,
65+ Error **errp)
66+{
67+ int fds[2] = { -1, -1 };
68+ QIOChannelSocket *sioc = NULL;
69+ pid_t pid = -1;
70+ const char *argv[] = { "/bin/sh", "-c", cmd, NULL };
71+
72+ /*
73+ * We need a Unix domain socket for commands like swtpm and a single
74+ * connection, therefore we cannot use qio_channel_command_new_spawn()
75+ * without patching it first. Duplicating the functionality is easier.
76+ */
77+ if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds)) {
78+ error_setg_errno(errp, errno, "Error creating socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC)");
79+ goto error;
80+ }
81+
82+ pid = qemu_fork(errp);
83+ if (pid < 0) {
84+ goto error;
85+ }
86+
87+ if (!pid) {
88+ /* child */
89+ dup2(fds[1], STDIN_FILENO);
90+ execv(argv[0], (char * const *)argv);
91+ _exit(1);
92+ }
93+
94+ /*
95+ * Hand over our end of the socket pair to the qio channel.
96+ *
97+ * We don't reap the child because it is expected to keep
98+ * running. We also don't support the "reconnect" option for the
99+ * same reason.
100+ */
101+ sioc = qio_channel_socket_new_fd(fds[0], errp);
102+ if (!sioc) {
103+ goto error;
104+ }
105+ fds[0] = -1;
106+
107+ g_free(chr->filename);
108+ chr->filename = g_strdup_printf("cmd:%s", cmd);
109+ tcp_chr_new_client(chr, sioc);
110+
111+ error:
112+ if (fds[0] >= 0) {
113+ close(fds[0]);
114+ }
115+ if (fds[1] >= 0) {
116+ close(fds[1]);
117+ }
118+ if (sioc) {
119+ object_unref(OBJECT(sioc));
120+ }
121+}
122+#endif
123
124 static void qmp_chardev_open_socket(Chardev *chr,
125 ChardevBackend *backend,
Andrew Geisslerd5838332022-05-27 11:33:10 -0500126@@ -1323,6 +1384,9 @@ static void qmp_chardev_open_socket(Chardev *chr,
Brad Bishopc342db32019-05-15 21:57:59 -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;
Andrew Geisslerd5838332022-05-27 11:33:10 -0500136@@ -1393,6 +1457,14 @@ static void qmp_chardev_open_socket(Chardev *chr,
Brad Bishopc342db32019-05-15 21:57:59 -0400137
138 update_disconnected_filename(s);
139
140+#ifndef _WIN32
141+ if (cmd) {
142+ chardev_open_socket_cmd(chr, cmd, errp);
143+
144+ /* everything ready (or failed permanently) before we return */
145+ *be_opened = true;
146+ } else
147+#endif
148 if (s->is_listen) {
149 if (qmp_chardev_open_socket_server(chr, is_telnet || is_tn3270,
150 is_waitconnect, errp) < 0) {
Andrew Geisslerd5838332022-05-27 11:33:10 -0500151@@ -1412,6 +1484,9 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
Brad Bishopc342db32019-05-15 21:57:59 -0400152 const char *host = qemu_opt_get(opts, "host");
153 const char *port = qemu_opt_get(opts, "port");
154 const char *fd = qemu_opt_get(opts, "fd");
155+#ifndef _WIN32
156+ const char *cmd = qemu_opt_get(opts, "cmd");
157+#endif
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600158 #ifdef CONFIG_LINUX
Andrew Geissler635e0e42020-08-21 15:58:33 -0500159 bool tight = qemu_opt_get_bool(opts, "tight", true);
160 bool abstract = qemu_opt_get_bool(opts, "abstract", false);
Andrew Geisslerd5838332022-05-27 11:33:10 -0500161@@ -1419,6 +1494,20 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
Brad Bishopc342db32019-05-15 21:57:59 -0400162 SocketAddressLegacy *addr;
163 ChardevSocket *sock;
164
165+#ifndef _WIN32
166+ if (cmd) {
167+ /*
168+ * Here we have to ensure that no options are set which are incompatible with
169+ * spawning a command, otherwise unmodified code that doesn't know about
170+ * command spawning (like socket_reconnect_timeout()) might get called.
171+ */
172+ if (path || sock->server || sock->has_telnet || sock->has_tn3270 || sock->reconnect || host || port || sock->tls_creds) {
173+ error_setg(errp, "chardev: socket: cmd does not support any additional options");
174+ return;
175+ }
176+ } else
177+#endif
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600178+
Andrew Geisslerd5838332022-05-27 11:33:10 -0500179 if ((!!path + !!fd + !!host) > 1) {
Brad Bishopc342db32019-05-15 21:57:59 -0400180 error_setg(errp,
Andrew Geisslerd5838332022-05-27 11:33:10 -0500181 "None or one of 'path', 'fd' or 'host' option required.");
182@@ -1469,13 +1558,24 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600183 sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds"));
Brad Bishopc342db32019-05-15 21:57:59 -0400184 sock->has_tls_authz = qemu_opt_get(opts, "tls-authz");
185 sock->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz"));
Brad Bishopc342db32019-05-15 21:57:59 -0400186+#ifndef _WIN32
187+ sock->cmd = g_strdup(cmd);
188+#endif
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600189
190 addr = g_new0(SocketAddressLegacy, 1);
Brad Bishopc342db32019-05-15 21:57:59 -0400191+#ifndef _WIN32
192+ if (path || cmd) {
193+#else
194 if (path) {
195+#endif
196 UnixSocketAddress *q_unix;
Andrew Geissler595f6302022-01-24 19:11:47 +0000197 addr->type = SOCKET_ADDRESS_TYPE_UNIX;
Brad Bishopc342db32019-05-15 21:57:59 -0400198 q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
199+#ifndef _WIN32
200+ q_unix->path = cmd ? g_strdup_printf("cmd:%s", cmd) : g_strdup(path);
201+#else
202 q_unix->path = g_strdup(path);
203+#endif
Andrew Geisslerd1e89492021-02-12 15:35:20 -0600204 #ifdef CONFIG_LINUX
205 q_unix->has_tight = true;
Andrew Geissler635e0e42020-08-21 15:58:33 -0500206 q_unix->tight = tight;
Andrew Geisslerd5838332022-05-27 11:33:10 -0500207diff --git a/chardev/char.c b/chardev/char.c
208index 0169d8dde..ce9a21f41 100644
209--- a/chardev/char.c
210+++ b/chardev/char.c
211@@ -835,6 +835,9 @@ QemuOptsList qemu_chardev_opts = {
212 },{
Brad Bishopc342db32019-05-15 21:57:59 -0400213 .name = "path",
214 .type = QEMU_OPT_STRING,
Andrew Geisslerd5838332022-05-27 11:33:10 -0500215+ },{
Brad Bishopc342db32019-05-15 21:57:59 -0400216+ .name = "cmd",
217+ .type = QEMU_OPT_STRING,
Andrew Geisslerd5838332022-05-27 11:33:10 -0500218 },{
Brad Bishopc342db32019-05-15 21:57:59 -0400219 .name = "host",
220 .type = QEMU_OPT_STRING,
Andrew Geisslerd5838332022-05-27 11:33:10 -0500221diff --git a/qapi/char.json b/qapi/char.json
222index 7b4215157..37feabdac 100644
223--- a/qapi/char.json
224+++ b/qapi/char.json
Andrew Geissler635e0e42020-08-21 15:58:33 -0500225@@ -250,6 +250,10 @@
Brad Bishopc342db32019-05-15 21:57:59 -0400226 #
227 # @addr: socket address to listen on (server=true)
228 # or connect to (server=false)
229+# @cmd: command to run via "sh -c" with stdin as one end of
230+# a AF_UNIX SOCK_DSTREAM socket pair. The other end
231+# is used by the chardev. Either an addr or a cmd can
232+# be specified, but not both.
233 # @tls-creds: the ID of the TLS credentials object (since 2.6)
234 # @tls-authz: the ID of the QAuthZ authorization object against which
235 # the client's x509 distinguished name will be validated. This
Andrew Geissler635e0e42020-08-21 15:58:33 -0500236@@ -276,6 +280,7 @@
Brad Bishopc342db32019-05-15 21:57:59 -0400237 ##
238 { 'struct': 'ChardevSocket',
239 'data': { 'addr': 'SocketAddressLegacy',
240+ '*cmd': 'str',
241 '*tls-creds': 'str',
242 '*tls-authz' : 'str',
243 '*server': 'bool',
Andrew Geisslerd5838332022-05-27 11:33:10 -0500244--
2452.30.2
246