1
0
mirror of https://github.com/godotengine/godot.git synced 2025-11-04 12:00:25 +00:00

Implement OS.execute_with_pipe method to run process with redirected stdio.

Implement `pipe://*` path handling for creation of named pipes.
This commit is contained in:
bruvzg
2023-10-19 14:35:10 +03:00
parent 7d151c8381
commit 082b420c0a
16 changed files with 790 additions and 11 deletions

View File

@@ -37,6 +37,7 @@
#include "core/debugger/script_debugger.h"
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
#include "drivers/unix/file_access_unix_pipe.h"
#include "drivers/unix/net_socket_posix.h"
#include "drivers/unix/thread_posix.h"
#include "servers/rendering_server.h"
@@ -160,6 +161,7 @@ void OS_Unix::initialize_core() {
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
FileAccess::make_default<FileAccessUnixPipe>(FileAccess::ACCESS_PIPE);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_RESOURCES);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
@@ -489,6 +491,106 @@ Dictionary OS_Unix::get_memory_info() const {
return meminfo;
}
Dictionary OS_Unix::execute_with_pipe(const String &p_path, const List<String> &p_arguments) {
#define CLEAN_PIPES \
if (pipe_in[0] >= 0) { \
::close(pipe_in[0]); \
} \
if (pipe_in[1] >= 0) { \
::close(pipe_in[1]); \
} \
if (pipe_out[0] >= 0) { \
::close(pipe_out[0]); \
} \
if (pipe_out[1] >= 0) { \
::close(pipe_out[1]); \
} \
if (pipe_err[0] >= 0) { \
::close(pipe_err[0]); \
} \
if (pipe_err[1] >= 0) { \
::close(pipe_err[1]); \
}
Dictionary ret;
#ifdef __EMSCRIPTEN__
// Don't compile this code at all to avoid undefined references.
// Actual virtual call goes to OS_Web.
ERR_FAIL_V(ret);
#else
// Create pipes.
int pipe_in[2] = { -1, -1 };
int pipe_out[2] = { -1, -1 };
int pipe_err[2] = { -1, -1 };
ERR_FAIL_COND_V(pipe(pipe_in) != 0, ret);
if (pipe(pipe_out) != 0) {
CLEAN_PIPES
ERR_FAIL_V(ret);
}
if (pipe(pipe_err) != 0) {
CLEAN_PIPES
ERR_FAIL_V(ret);
}
// Create process.
pid_t pid = fork();
if (pid < 0) {
CLEAN_PIPES
ERR_FAIL_V(ret);
}
if (pid == 0) {
// The child process.
Vector<CharString> cs;
cs.push_back(p_path.utf8());
for (int i = 0; i < p_arguments.size(); i++) {
cs.push_back(p_arguments[i].utf8());
}
Vector<char *> args;
for (int i = 0; i < cs.size(); i++) {
args.push_back((char *)cs[i].get_data());
}
args.push_back(0);
::close(STDIN_FILENO);
::dup2(pipe_in[0], STDIN_FILENO);
::close(STDOUT_FILENO);
::dup2(pipe_out[1], STDOUT_FILENO);
::close(STDERR_FILENO);
::dup2(pipe_err[1], STDERR_FILENO);
CLEAN_PIPES
execvp(p_path.utf8().get_data(), &args[0]);
// The execvp() function only returns if an error occurs.
ERR_PRINT("Could not create child process: " + p_path);
raise(SIGKILL);
}
::close(pipe_in[0]);
::close(pipe_out[1]);
::close(pipe_err[1]);
Ref<FileAccessUnixPipe> main_pipe;
main_pipe.instantiate();
main_pipe->open_existing(pipe_out[0], pipe_in[1]);
Ref<FileAccessUnixPipe> err_pipe;
err_pipe.instantiate();
err_pipe->open_existing(pipe_err[0], 0);
ret["stdio"] = main_pipe;
ret["stderr"] = err_pipe;
ret["pid"] = pid;
#undef CLEAN_PIPES
return ret;
#endif
}
Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, String *r_pipe, int *r_exitcode, bool read_stderr, Mutex *p_pipe_mutex, bool p_open_console) {
#ifdef __EMSCRIPTEN__
// Don't compile this code at all to avoid undefined references.