1
0
mirror of https://github.com/mpv-player/mpv synced 2024-12-27 17:42:17 +00:00
mpv/osdep/subprocess.h
wm4 f2c7c641b3 subprocess: implement proper detached processes on POSIX
The previous method for this sucked: for every launched detached
process, it started a thread, which then would leak if the launched
process didn't end before the player uninitialized. This was very racy
(although I bet the race condition wouldn't trigger in a 100 years), and
wasteful (threads aren't a cheap resource).

Implement it for POSIX directly. posix_spawn() has no direct support for
this, so we need to do it ourselves with fork(). We could probably do it
without fork(), and attempt to collect the PID in another thread. But
then we'd either have a waiting thread again, or we'd need to do an
unsafe waitpid(-1, ...) call. (POSIX process management sucks so badly,
how did they even manage this. Hopefully I'm just missing something, but
I'm not.) So now we depend on both posix_spawn() _and_ fork(), isn't it
fun?

Also call setsid(), to essentially detach the child process from the
terminal. (Otherwise it can receive various signals from the terminal,
which is probably not what you want.) posix_spawn() adds
POSIX_SPAWN_SETSID in newer POSIX releases, but we don't want to rely on
this yet.

The posix_spawnp() call is duplicated, but this is better than somehow
trying to unify the code paths.

Only somewhat tested, so enjoy the bugs.
2020-02-16 22:08:48 +01:00

86 lines
3.2 KiB
C

/*
* This file is part of mpv.
*
* mpv is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* mpv is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef MP_SUBPROCESS_H_
#define MP_SUBPROCESS_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
struct mp_cancel;
typedef void (*subprocess_read_cb)(void *ctx, char *data, size_t size);
void mp_devnull(void *ctx, char *data, size_t size);
#define MP_SUBPROCESS_MAX_FDS 10
struct mp_subprocess_fd {
int fd; // target FD
// Only one of on_read or src_fd can be set. If none are set, use /dev/null.
// 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, ...)
int src_fd; // if >=0, dup this FD to target FD
};
struct mp_subprocess_opts {
char *exe; // binary to execute (never non-NULL)
char **args; // argument list (NULL for none, otherwise NULL-terminated)
char **env; // if !NULL, set this as environment variable block
// Complete set of FDs passed down. All others are supposed to be closed.
struct mp_subprocess_fd fds[MP_SUBPROCESS_MAX_FDS];
int num_fds;
struct mp_cancel *cancel; // if !NULL, asynchronous process abort (kills it)
bool detach; // if true, do not wait for process to end
};
struct mp_subprocess_result {
int error; // one of MP_SUBPROCESS_* (>0 on error)
// NB: if WIFEXITED applies, error==0, and this is WEXITSTATUS
// on win32, this can use the full 32 bit
// if started with detach==true, this is always 0
uint32_t exit_status; // if error==0==MP_SUBPROCESS_OK, 0 otherwise
};
// Subprocess error values.
#define MP_SUBPROCESS_OK 0 // no error
#define MP_SUBPROCESS_EGENERIC -1 // unknown error
#define MP_SUBPROCESS_EKILLED_BY_US -2 // mp_cancel was triggered
#define MP_SUBPROCESS_EINIT -3 // error during initialization
#define MP_SUBPROCESS_EUNSUPPORTED -4 // API not supported
// Turn MP_SUBPROCESS_* values into a static string. Never returns NULL.
const char *mp_subprocess_err_str(int num);
// Caller must set *opts.
void mp_subprocess2(struct mp_subprocess_opts *opts,
struct mp_subprocess_result *res);
// Start a subprocess. Uses callbacks to read from stdout and stderr.
// Returns any of MP_SUBPROCESS_*, or a value >=0 for the process exir
int mp_subprocess(char **args, struct mp_cancel *cancel, void *ctx,
subprocess_read_cb on_stdout, subprocess_read_cb on_stderr,
char **error);
struct mp_log;
void mp_subprocess_detached(struct mp_log *log, char **args);
#endif