From e27c523a10708b7265c33a4bae631663df4bb297 Mon Sep 17 00:00:00 2001 From: wm4 Date: Sun, 16 Aug 2020 02:54:44 +0200 Subject: command: extend subprocess command stdin, change behavior Make it possible to feed a string to stdin of a subprocess. Out of laziness, it can't be an arbitrary byte string. (Would require adding an option type that takes in a Lua byte string.) Do not set stdin of a subprocess to fd 0 (i.e. mpv's stdin) anymore, because it makes things more consistent. Enabling stdin didn't make too much sense in the first place, so this behavior change seems justifiable. win32 support missing. Fixes: #8003 --- osdep/subprocess-posix.c | 41 +++++++++++++++++++++++++++++++++++++---- osdep/subprocess.h | 12 ++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) (limited to 'osdep') diff --git a/osdep/subprocess-posix.c b/osdep/subprocess-posix.c index e7af4663ea..bae0ca2c78 100644 --- a/osdep/subprocess-posix.c +++ b/osdep/subprocess-posix.c @@ -165,8 +165,21 @@ void mp_subprocess2(struct mp_subprocess_opts *opts, } for (int n = 0; n < opts->num_fds; n++) { + assert(!(opts->fds[n].on_read && opts->fds[n].on_write)); + if (opts->fds[n].on_read && mp_make_cloexec_pipe(comm_pipe[n]) < 0) goto done; + + if (opts->fds[n].on_write || opts->fds[n].write_buf) { + assert(opts->fds[n].on_write && opts->fds[n].write_buf); + if (mp_make_cloexec_pipe(comm_pipe[n]) < 0) + goto done; + MPSWAP(int, comm_pipe[n][0], comm_pipe[n][1]); + + struct sigaction sa = {.sa_handler = SIG_IGN, .sa_flags = SA_RESTART}; + sigfillset(&sa.sa_mask); + sigaction(SIGPIPE, &sa, NULL); + } } devnull = open("/dev/null", O_RDONLY | O_CLOEXEC); @@ -225,7 +238,7 @@ void mp_subprocess2(struct mp_subprocess_opts *opts, if (comm_pipe[n][0] >= 0) { map_fds[num_fds] = n; fds[num_fds++] = (struct pollfd){ - .events = POLLIN, + .events = opts->fds[n].on_read ? POLLIN : POLLOUT, .fd = comm_pipe[n][0], }; } @@ -249,15 +262,35 @@ void mp_subprocess2(struct mp_subprocess_opts *opts, kill(pid, SIGKILL); killed_by_us = true; break; - } else { + } + struct mp_subprocess_fd *fd = &opts->fds[n]; + if (fd->on_read) { char buf[4096]; ssize_t r = read(comm_pipe[n][0], buf, sizeof(buf)); if (r < 0 && errno == EINTR) continue; - if (r > 0 && opts->fds[n].on_read) - opts->fds[n].on_read(opts->fds[n].on_read_ctx, buf, r); + fd->on_read(fd->on_read_ctx, buf, MPMAX(r, 0)); if (r <= 0) SAFE_CLOSE(comm_pipe[n][0]); + } else if (fd->on_write) { + if (!fd->write_buf->len) { + fd->on_write(fd->on_write_ctx); + if (!fd->write_buf->len) { + SAFE_CLOSE(comm_pipe[n][0]); + continue; + } + } + ssize_t r = write(comm_pipe[n][0], fd->write_buf->start, + fd->write_buf->len); + if (r < 0 && errno == EINTR) + continue; + if (r < 0) { + // Let's not signal an error for now - caller can check + // whether all buffer was written. + SAFE_CLOSE(comm_pipe[n][0]); + continue; + } + *fd->write_buf = bstr_cut(*fd->write_buf, r); } } } diff --git a/osdep/subprocess.h b/osdep/subprocess.h index 14d4896c58..4bf2dc32dd 100644 --- a/osdep/subprocess.h +++ b/osdep/subprocess.h @@ -22,9 +22,18 @@ #include #include +#include "misc/bstr.h" + struct mp_cancel; +// Incrementally called with data that was read. Buffer valid only during call. +// size==0 means EOF. typedef void (*subprocess_read_cb)(void *ctx, char *data, size_t size); +// Incrementally called to refill *mp_subprocess_fd.write_buf, whenever write_buf +// has length 0 and the pipe is writable. While writing, *write_buf is adjusted +// to contain only the not yet written data. +// Not filling the buffer means EOF. +typedef void (*subprocess_write_cb)(void *ctx); void mp_devnull(void *ctx, char *data, size_t size); @@ -37,6 +46,9 @@ struct mp_subprocess_fd { // Note: "neutral" initialization requires setting src_fd=-1. subprocess_read_cb on_read; // if not NULL, serve reads void *on_read_ctx; // for on_read(on_read_ctx, ...) + subprocess_write_cb on_write; // if not NULL, serve writes + void *on_write_ctx; // for on_write(on_write_ctx, ...) + bstr *write_buf; // must be !=NULL if on_write is set int src_fd; // if >=0, dup this FD to target FD }; -- cgit v1.2.3