123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246 |
- From 14cd62607c9de232edf0a9b8503bd02783e03411 Mon Sep 17 00:00:00 2001
- From: Alistair Francis <alistair.francis@xilinx.com>
- Date: Thu, 21 Dec 2017 11:35:16 -0800
- Subject: [PATCH 02/12] chardev: connect socket to a spawned command
- The command is started in a shell (sh -c) with stdin connect to QEMU
- via a Unix domain stream socket. QEMU then exchanges data via its own
- end of the socket, just like it normally does.
- "-chardev socket" supports some ways of connecting via protocols like
- telnet, but that is only a subset of the functionality supported by
- tools socat. To use socat instead, for example to connect via a socks
- proxy, use:
- -chardev 'socket,id=socat,cmd=exec socat FD:0 SOCKS4A:socks-proxy.localdomain:example.com:9999,,socksuser=nobody' \
- -device usb-serial,chardev=socat
- Beware that commas in the command must be escaped as double commas.
- Or interactively in the console:
- (qemu) chardev-add socket,id=cat,cmd=cat
- (qemu) device_add usb-serial,chardev=cat
- ^ac
- # cat >/dev/ttyUSB0
- hello
- hello
- Another usage is starting swtpm from inside QEMU. swtpm will
- automatically shut down once it looses the connection to the parent
- QEMU, so there is no risk of lingering processes:
- -chardev 'socket,id=chrtpm0,cmd=exec swtpm socket --terminate --ctrl type=unixio,,clientfd=0 --tpmstate dir=... --log file=swtpm.log' \
- -tpmdev emulator,id=tpm0,chardev=chrtpm0 \
- -device tpm-tis,tpmdev=tpm0
- The patch was discussed upstream, but QEMU developers believe that the
- code calling QEMU should be responsible for managing additional
- processes. In OE-core, that would imply enhancing runqemu and
- oeqa. This patch is a simpler solution.
- Because it is not going upstream, the patch was written so that it is
- as simple as possible.
- Upstream-Status: Inappropriate [embedded specific]
- Signed-off-by: Patrick Ohly <patrick.ohly@intel.com>
- ---
- chardev/char-socket.c | 100 ++++++++++++++++++++++++++++++++++++++++++
- chardev/char.c | 3 ++
- qapi/char.json | 5 +++
- 3 files changed, 108 insertions(+)
- diff --git a/chardev/char-socket.c b/chardev/char-socket.c
- index fab2d791d..c79641f24 100644
- --- a/chardev/char-socket.c
- +++ b/chardev/char-socket.c
- @@ -1315,6 +1315,67 @@ static bool qmp_chardev_validate_socket(ChardevSocket *sock,
- return true;
- }
-
- +#ifndef _WIN32
- +static void chardev_open_socket_cmd(Chardev *chr,
- + const char *cmd,
- + Error **errp)
- +{
- + int fds[2] = { -1, -1 };
- + QIOChannelSocket *sioc = NULL;
- + pid_t pid = -1;
- + const char *argv[] = { "/bin/sh", "-c", cmd, NULL };
- +
- + /*
- + * We need a Unix domain socket for commands like swtpm and a single
- + * connection, therefore we cannot use qio_channel_command_new_spawn()
- + * without patching it first. Duplicating the functionality is easier.
- + */
- + if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC, 0, fds)) {
- + error_setg_errno(errp, errno, "Error creating socketpair(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC)");
- + goto error;
- + }
- +
- + pid = qemu_fork(errp);
- + if (pid < 0) {
- + goto error;
- + }
- +
- + if (!pid) {
- + /* child */
- + dup2(fds[1], STDIN_FILENO);
- + execv(argv[0], (char * const *)argv);
- + _exit(1);
- + }
- +
- + /*
- + * Hand over our end of the socket pair to the qio channel.
- + *
- + * We don't reap the child because it is expected to keep
- + * running. We also don't support the "reconnect" option for the
- + * same reason.
- + */
- + sioc = qio_channel_socket_new_fd(fds[0], errp);
- + if (!sioc) {
- + goto error;
- + }
- + fds[0] = -1;
- +
- + g_free(chr->filename);
- + chr->filename = g_strdup_printf("cmd:%s", cmd);
- + tcp_chr_new_client(chr, sioc);
- +
- + error:
- + if (fds[0] >= 0) {
- + close(fds[0]);
- + }
- + if (fds[1] >= 0) {
- + close(fds[1]);
- + }
- + if (sioc) {
- + object_unref(OBJECT(sioc));
- + }
- +}
- +#endif
-
- static void qmp_chardev_open_socket(Chardev *chr,
- ChardevBackend *backend,
- @@ -1323,6 +1384,9 @@ static void qmp_chardev_open_socket(Chardev *chr,
- {
- SocketChardev *s = SOCKET_CHARDEV(chr);
- ChardevSocket *sock = backend->u.socket.data;
- +#ifndef _WIN32
- + const char *cmd = sock->cmd;
- +#endif
- bool do_nodelay = sock->has_nodelay ? sock->nodelay : false;
- bool is_listen = sock->has_server ? sock->server : true;
- bool is_telnet = sock->has_telnet ? sock->telnet : false;
- @@ -1393,6 +1457,14 @@ static void qmp_chardev_open_socket(Chardev *chr,
-
- update_disconnected_filename(s);
-
- +#ifndef _WIN32
- + if (cmd) {
- + chardev_open_socket_cmd(chr, cmd, errp);
- +
- + /* everything ready (or failed permanently) before we return */
- + *be_opened = true;
- + } else
- +#endif
- if (s->is_listen) {
- if (qmp_chardev_open_socket_server(chr, is_telnet || is_tn3270,
- is_waitconnect, errp) < 0) {
- @@ -1412,6 +1484,9 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
- const char *host = qemu_opt_get(opts, "host");
- const char *port = qemu_opt_get(opts, "port");
- const char *fd = qemu_opt_get(opts, "fd");
- +#ifndef _WIN32
- + const char *cmd = qemu_opt_get(opts, "cmd");
- +#endif
- #ifdef CONFIG_LINUX
- bool tight = qemu_opt_get_bool(opts, "tight", true);
- bool abstract = qemu_opt_get_bool(opts, "abstract", false);
- @@ -1419,6 +1494,20 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
- SocketAddressLegacy *addr;
- ChardevSocket *sock;
-
- +#ifndef _WIN32
- + if (cmd) {
- + /*
- + * Here we have to ensure that no options are set which are incompatible with
- + * spawning a command, otherwise unmodified code that doesn't know about
- + * command spawning (like socket_reconnect_timeout()) might get called.
- + */
- + if (path || sock->server || sock->has_telnet || sock->has_tn3270 || sock->reconnect || host || port || sock->tls_creds) {
- + error_setg(errp, "chardev: socket: cmd does not support any additional options");
- + return;
- + }
- + } else
- +#endif
- +
- if ((!!path + !!fd + !!host) > 1) {
- error_setg(errp,
- "None or one of 'path', 'fd' or 'host' option required.");
- @@ -1469,13 +1558,24 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend,
- sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds"));
- sock->has_tls_authz = qemu_opt_get(opts, "tls-authz");
- sock->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz"));
- +#ifndef _WIN32
- + sock->cmd = g_strdup(cmd);
- +#endif
-
- addr = g_new0(SocketAddressLegacy, 1);
- +#ifndef _WIN32
- + if (path || cmd) {
- +#else
- if (path) {
- +#endif
- UnixSocketAddress *q_unix;
- addr->type = SOCKET_ADDRESS_TYPE_UNIX;
- q_unix = addr->u.q_unix.data = g_new0(UnixSocketAddress, 1);
- +#ifndef _WIN32
- + q_unix->path = cmd ? g_strdup_printf("cmd:%s", cmd) : g_strdup(path);
- +#else
- q_unix->path = g_strdup(path);
- +#endif
- #ifdef CONFIG_LINUX
- q_unix->has_tight = true;
- q_unix->tight = tight;
- diff --git a/chardev/char.c b/chardev/char.c
- index 0169d8dde..ce9a21f41 100644
- --- a/chardev/char.c
- +++ b/chardev/char.c
- @@ -835,6 +835,9 @@ QemuOptsList qemu_chardev_opts = {
- },{
- .name = "path",
- .type = QEMU_OPT_STRING,
- + },{
- + .name = "cmd",
- + .type = QEMU_OPT_STRING,
- },{
- .name = "host",
- .type = QEMU_OPT_STRING,
- diff --git a/qapi/char.json b/qapi/char.json
- index 7b4215157..37feabdac 100644
- --- a/qapi/char.json
- +++ b/qapi/char.json
- @@ -250,6 +250,10 @@
- #
- # @addr: socket address to listen on (server=true)
- # or connect to (server=false)
- +# @cmd: command to run via "sh -c" with stdin as one end of
- +# a AF_UNIX SOCK_DSTREAM socket pair. The other end
- +# is used by the chardev. Either an addr or a cmd can
- +# be specified, but not both.
- # @tls-creds: the ID of the TLS credentials object (since 2.6)
- # @tls-authz: the ID of the QAuthZ authorization object against which
- # the client's x509 distinguished name will be validated. This
- @@ -276,6 +280,7 @@
- ##
- { 'struct': 'ChardevSocket',
- 'data': { 'addr': 'SocketAddressLegacy',
- + '*cmd': 'str',
- '*tls-creds': 'str',
- '*tls-authz' : 'str',
- '*server': 'bool',
- --
- 2.30.2
|