From 9bc3082402cd842e7ce5c729700fe261ae6a6f0b Mon Sep 17 00:00:00 2001 From: derrod Date: Sun, 14 Jan 2024 04:17:53 +0100 Subject: [PATCH] libobs: Add os_process_pipe_create2 This new API uses the os_process_args_t object rather than a string for more safe and sane command line argument handling. --- libobs/util/pipe-posix.c | 28 +++++++++++++---- libobs/util/pipe-windows.c | 64 ++++++++++++++++++++++++++++++++++++++ libobs/util/pipe.h | 2 ++ 3 files changed, 88 insertions(+), 6 deletions(-) diff --git a/libobs/util/pipe-posix.c b/libobs/util/pipe-posix.c index e78c09443..6f06752d8 100644 --- a/libobs/util/pipe-posix.c +++ b/libobs/util/pipe-posix.c @@ -31,14 +31,14 @@ struct os_process_pipe { FILE *err_file; }; -os_process_pipe_t *os_process_pipe_create(const char *cmd_line, - const char *type) +os_process_pipe_t *os_process_pipe_create_internal(const char *bin, char **argv, + const char *type) { struct os_process_pipe process_pipe = {0}; struct os_process_pipe *out; posix_spawn_file_actions_t file_actions; - if (!cmd_line || !type) { + if (!bin || !argv || !type) { return NULL; } @@ -94,9 +94,8 @@ os_process_pipe_t *os_process_pipe_create(const char *cmd_line, STDERR_FILENO); int pid; - char *argv[4] = {"sh", "-c", (char *)cmd_line, NULL}; - - int ret = posix_spawn(&pid, "/bin/sh", &file_actions, NULL, argv, NULL); + int ret = posix_spawn(&pid, bin, &file_actions, NULL, + (char *const *)argv, NULL); posix_spawn_file_actions_destroy(&file_actions); @@ -127,6 +126,23 @@ os_process_pipe_t *os_process_pipe_create(const char *cmd_line, return out; } +os_process_pipe_t *os_process_pipe_create(const char *cmd_line, + const char *type) +{ + if (!cmd_line) + return NULL; + + char *argv[3] = {"-c", (char *)cmd_line, NULL}; + return os_process_pipe_create_internal("/bin/sh", argv, type); +} + +os_process_pipe_t *os_process_pipe_create2(const os_process_args_t *args, + const char *type) +{ + char **argv = os_process_args_get_argv(args); + return os_process_pipe_create_internal(argv[0], argv, type); +} + int os_process_pipe_destroy(os_process_pipe_t *pp) { int ret = 0; diff --git a/libobs/util/pipe-windows.c b/libobs/util/pipe-windows.c index 65d52ba1e..3bceab522 100644 --- a/libobs/util/pipe-windows.c +++ b/libobs/util/pipe-windows.c @@ -19,6 +19,7 @@ #include "platform.h" #include "bmem.h" +#include "dstr.h" #include "pipe.h" struct os_process_pipe { @@ -145,6 +146,69 @@ error: return NULL; } +static inline void add_backslashes(struct dstr *str, size_t count) +{ + while (count--) + dstr_cat_ch(str, '\\'); +} + +os_process_pipe_t *os_process_pipe_create2(const os_process_args_t *args, + const char *type) +{ + struct dstr cmd_line = {0}; + + /* Convert list to command line as Windows does not have any API that + * allows us to just pass argc/argv. */ + char **argv = os_process_args_get_argv(args); + + /* Based on Python subprocess module implementation. */ + while (*argv) { + size_t bs_count = 0; + const char *arg = *argv; + bool needs_quotes = strlen(arg) == 0 || + strstr(arg, " ") != NULL || + strstr(arg, "\t") != NULL; + + if (cmd_line.len) + dstr_cat_ch(&cmd_line, ' '); + if (needs_quotes) + dstr_cat_ch(&cmd_line, '"'); + + while (*arg) { + if (*arg == '\\') { + bs_count++; + } else if (*arg == '"') { + add_backslashes(&cmd_line, bs_count * 2); + dstr_cat(&cmd_line, "\\\""); + bs_count = 0; + } else { + if (bs_count) { + add_backslashes(&cmd_line, bs_count); + bs_count = 0; + } + dstr_cat_ch(&cmd_line, *arg); + } + + arg++; + } + + if (bs_count) + add_backslashes(&cmd_line, bs_count); + + if (needs_quotes) { + add_backslashes(&cmd_line, bs_count); + dstr_cat_ch(&cmd_line, '"'); + } + + argv++; + } + + os_process_pipe_t *ret = os_process_pipe_create(cmd_line.array, type); + + dstr_free(&cmd_line); + return ret; +} + int os_process_pipe_destroy(os_process_pipe_t *pp) { int ret = 0; diff --git a/libobs/util/pipe.h b/libobs/util/pipe.h index bc80b7413..2c39ad754 100644 --- a/libobs/util/pipe.h +++ b/libobs/util/pipe.h @@ -30,6 +30,8 @@ typedef struct os_process_args os_process_args_t; EXPORT os_process_pipe_t *os_process_pipe_create(const char *cmd_line, const char *type); +EXPORT os_process_pipe_t *os_process_pipe_create2(const os_process_args_t *args, + const char *type); EXPORT int os_process_pipe_destroy(os_process_pipe_t *pp); EXPORT size_t os_process_pipe_read(os_process_pipe_t *pp, uint8_t *data,