summaryrefslogtreecommitdiffstats
path: root/player/scripting.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2020-02-19 22:18:15 +0100
committerwm4 <wm4@nowhere>2020-02-19 22:18:15 +0100
commite2ab6b7f3567542a2a1b5aab053d513737e72878 (patch)
tree36f3f24792e4974cf1e6c5601a61e799b098e69a /player/scripting.c
parentd06ebe2251b2ac75ebc6b63b1580ed15adecd3cc (diff)
downloadmpv-e2ab6b7f3567542a2a1b5aab053d513737e72878.tar.bz2
mpv-e2ab6b7f3567542a2a1b5aab053d513737e72878.tar.xz
scripting: add a way to run sub processes as "scripts"
This is just a more convenient way to start IPC client scripts per mpv instance. Does not work on Windows, although it could if the subprocess and IPC parts are implemented (and I guess .exe/.bat suffixes are required). Also untested whether it builds on Windows. A lot of other things are untested too, so don't complain.
Diffstat (limited to 'player/scripting.c')
-rw-r--r--player/scripting.c89
1 files changed, 79 insertions, 10 deletions
diff --git a/player/scripting.c b/player/scripting.c
index d83c4d241f..c42706a0b9 100644
--- a/player/scripting.c
+++ b/player/scripting.c
@@ -22,14 +22,17 @@
#include <math.h>
#include <pthread.h>
#include <assert.h>
+#include <unistd.h>
#include "config.h"
#include "osdep/io.h"
+#include "osdep/subprocess.h"
#include "osdep/threads.h"
#include "common/common.h"
#include "common/msg.h"
+#include "input/input.h"
#include "options/m_config.h"
#include "options/parse_configfile.h"
#include "options/path.h"
@@ -41,6 +44,7 @@
extern const struct mp_scripting mp_scripting_lua;
extern const struct mp_scripting mp_scripting_cplugin;
extern const struct mp_scripting mp_scripting_js;
+extern const struct mp_scripting mp_scripting_run;
static const struct mp_scripting *const scripting_backends[] = {
#if HAVE_LUA
@@ -52,6 +56,7 @@ static const struct mp_scripting *const scripting_backends[] = {
#if HAVE_JAVASCRIPT
&mp_scripting_js,
#endif
+ &mp_scripting_run,
NULL
};
@@ -76,12 +81,8 @@ static char *script_name_from_filename(void *talloc_ctx, const char *fname)
return talloc_asprintf(talloc_ctx, "%s", name);
}
-static void *script_thread(void *p)
+static void run_script(struct mp_script_args *arg)
{
- pthread_detach(pthread_self());
-
- struct mp_script_args *arg = p;
-
char name[90];
snprintf(name, sizeof(name), "%s (%s)", arg->backend->name,
mpv_client_name(arg->client));
@@ -92,6 +93,15 @@ static void *script_thread(void *p)
mpv_destroy(arg->client);
talloc_free(arg);
+}
+
+static void *script_thread(void *p)
+{
+ pthread_detach(pthread_self());
+
+ struct mp_script_args *arg = p;
+ run_script(arg);
+
return NULL;
}
@@ -176,11 +186,15 @@ static int mp_load_script(struct MPContext *mpctx, const char *fname)
MP_DBG(arg, "Loading %s %s...\n", backend->name, fname);
- pthread_t thread;
- if (pthread_create(&thread, NULL, script_thread, arg)) {
- mpv_destroy(arg->client);
- talloc_free(arg);
- return -1;
+ if (backend->no_thread) {
+ run_script(arg);
+ } else {
+ pthread_t thread;
+ if (pthread_create(&thread, NULL, script_thread, arg)) {
+ mpv_destroy(arg->client);
+ talloc_free(arg);
+ return -1;
+ }
}
return 0;
@@ -311,3 +325,58 @@ const struct mp_scripting mp_scripting_cplugin = {
};
#endif
+
+static int load_run(struct mp_script_args *args)
+{
+ int fds[2];
+ if (!mp_ipc_start_anon_client(args->mpctx->ipc_ctx, args->client, fds))
+ return -1;
+ args->client = NULL; // ownership lost
+
+ // Hardcode them (according to opts.fds[]), because we want to allow clients
+ // to hardcode them if they want. Sue me.
+ char *fdopt = fds[1] >= 0 ? "--mpv-ipc-fd=3:4"
+ : "--mpv-ipc-fd=3";
+
+ struct mp_subprocess_opts opts = {
+ .exe = (char *)args->filename,
+ .args = (char *[]){(char *)args->filename, fdopt, NULL},
+ .fds = {
+ // Keep terminal stuff
+ {.fd = 0, .src_fd = 0,},
+ {.fd = 1, .src_fd = 1,},
+ {.fd = 2, .src_fd = 2,},
+ // Just hope these don't step over each other (e.g. fds[1] is not
+ // below 4, if the std FDs are missing).
+ {.fd = 3, .src_fd = fds[0], },
+ {.fd = 4, .src_fd = fds[1], },
+ },
+ .num_fds = fds[1] >= 0 ? 4 : 5,
+ .detach = true,
+ };
+ struct mp_subprocess_result res;
+ mp_subprocess2(&opts, &res);
+
+ // Closing these will (probably) make the client exit, if it really died.
+ // They _should_ be CLOEXEC, but are not, because
+ // posix_spawn_file_actions_adddup2() may not clear the CLOEXEC flag
+ // properly if by coincidence fd==src_fd.
+ close(fds[0]);
+ if (fds[1] >= 0)
+ close(fds[1]);
+
+ if (res.error < 0) {
+ MP_ERR(args, "Starting '%s' failed: %s\n", args->filename,
+ mp_subprocess_err_str(res.error));
+ return -1;
+ }
+
+ return 0;
+}
+
+const struct mp_scripting mp_scripting_run = {
+ .name = "spawned IPC process",
+ .file_ext = "run",
+ .no_thread = true,
+ .load = load_run,
+};