Added a new SubProcess class with a Uniz backend for better process control.

This commit is contained in:
Relintai 2023-09-10 12:19:51 +02:00
parent 3cfe43b5e8
commit 5fd0326745
8 changed files with 913 additions and 0 deletions

View File

@ -2613,6 +2613,147 @@ _Directory::~_Directory() {
}
}
///////////////////////
void _SubProcess::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_executable_path"), &_SubProcess::get_executable_path);
ClassDB::bind_method(D_METHOD("set_executable_path", "executable_path"), &_SubProcess::set_executable_path);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "executable_path"), "set_executable_path", "get_executable_path");
ClassDB::bind_method(D_METHOD("get_arguments"), &_SubProcess::get_arguments);
ClassDB::bind_method(D_METHOD("set_arguments", "arguments"), &_SubProcess::set_arguments);
ADD_PROPERTY(PropertyInfo(Variant::POOL_STRING_ARRAY, "arguments"), "set_arguments", "get_arguments");
ClassDB::bind_method(D_METHOD("get_blocking"), &_SubProcess::get_blocking);
ClassDB::bind_method(D_METHOD("set_blocking", "value"), &_SubProcess::set_blocking);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "blocking"), "set_blocking", "get_blocking");
ClassDB::bind_method(D_METHOD("get_read_output"), &_SubProcess::get_read_output);
ClassDB::bind_method(D_METHOD("set_read_output", "value"), &_SubProcess::set_read_output);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_output"), "set_read_output", "get_read_output");
ClassDB::bind_method(D_METHOD("get_read_std"), &_SubProcess::get_read_std);
ClassDB::bind_method(D_METHOD("set_read_std", "value"), &_SubProcess::set_read_std);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_std"), "set_read_std", "get_read_std");
ClassDB::bind_method(D_METHOD("get_read_std_err"), &_SubProcess::get_read_std_err);
ClassDB::bind_method(D_METHOD("set_read_std_err", "value"), &_SubProcess::set_read_std_err);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "read_std_err"), "set_read_std_err", "get_read_std_err");
ClassDB::bind_method(D_METHOD("get_use_pipe_mutex"), &_SubProcess::get_use_pipe_mutex);
ClassDB::bind_method(D_METHOD("set_use_pipe_mutex", "value"), &_SubProcess::set_use_pipe_mutex);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_pipe_mutex"), "set_use_pipe_mutex", "get_use_pipe_mutex");
ClassDB::bind_method(D_METHOD("get_open_console"), &_SubProcess::get_open_console);
ClassDB::bind_method(D_METHOD("set_open_console", "value"), &_SubProcess::set_open_console);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "open_console"), "set_open_console", "get_open_console");
ClassDB::bind_method(D_METHOD("get_data"), &_SubProcess::get_data);
ClassDB::bind_method(D_METHOD("get_process_id"), &_SubProcess::get_process_id);
ClassDB::bind_method(D_METHOD("get_exitcode"), &_SubProcess::get_exitcode);
ClassDB::bind_method(D_METHOD("start"), &_SubProcess::start);
ClassDB::bind_method(D_METHOD("stop"), &_SubProcess::stop);
ClassDB::bind_method(D_METHOD("poll"), &_SubProcess::poll);
ClassDB::bind_method(D_METHOD("send_signal", "signal"), &_SubProcess::send_signal);
ClassDB::bind_method(D_METHOD("send_data", "data"), &_SubProcess::send_data);
ClassDB::bind_method(D_METHOD("is_process_running"), &_SubProcess::is_process_running);
}
String _SubProcess::get_executable_path() const {
return _sub_process->get_executable_path();
}
void _SubProcess::set_executable_path(const String &p_executable_path) {
_sub_process->set_executable_path(p_executable_path);
}
Vector<String> _SubProcess::get_arguments() const {
return _sub_process->get_arguments();
}
void _SubProcess::set_arguments(const Vector<String> &p_arguments) {
_sub_process->set_arguments(p_arguments);
}
bool _SubProcess::get_blocking() const {
return _sub_process->get_blocking();
}
void _SubProcess::set_blocking(const bool p_value) {
_sub_process->set_blocking(p_value);
}
bool _SubProcess::get_read_output() const {
return _sub_process->get_read_output();
}
void _SubProcess::set_read_output(const bool p_value) {
_sub_process->set_read_output(p_value);
}
bool _SubProcess::get_read_std() const {
return _sub_process->get_read_std();
}
void _SubProcess::set_read_std(const bool p_value) {
_sub_process->set_read_std(p_value);
}
bool _SubProcess::get_read_std_err() const {
return _sub_process->get_read_std_err();
}
void _SubProcess::set_read_std_err(const bool p_value) {
_sub_process->set_read_std_err(p_value);
}
bool _SubProcess::get_use_pipe_mutex() const {
return _sub_process->get_use_pipe_mutex();
}
void _SubProcess::set_use_pipe_mutex(const bool p_value) {
_sub_process->set_use_pipe_mutex(p_value);
}
bool _SubProcess::get_open_console() const {
return _sub_process->get_open_console();
}
void _SubProcess::set_open_console(const bool p_value) {
_sub_process->set_open_console(p_value);
}
String _SubProcess::get_data() const {
return _sub_process->get_data();
}
int _SubProcess::get_process_id() const {
return _sub_process->get_process_id();
}
int _SubProcess::get_exitcode() const {
return _sub_process->get_exitcode();
}
Error _SubProcess::start() {
return _sub_process->start();
}
Error _SubProcess::stop() {
return _sub_process->stop();
}
Error _SubProcess::poll() {
return _sub_process->poll();
}
Error _SubProcess::send_signal(const int p_signal) {
return _sub_process->send_signal(p_signal);
}
Error _SubProcess::send_data(const String &p_data) {
return _sub_process->send_data(p_data);
}
bool _SubProcess::is_process_running() const {
return _sub_process->is_process_running();
}
_SubProcess::_SubProcess() {
_sub_process = SubProcess::create();
}
_SubProcess::~_SubProcess() {
memdelete(_sub_process);
}
///////////////////////
_Marshalls *_Marshalls::singleton = nullptr;
_Marshalls *_Marshalls::get_singleton() {

View File

@ -41,6 +41,7 @@
#include "core/os/rw_lock.h"
#include "core/os/safe_refcount.h"
#include "core/os/semaphore.h"
#include "core/os/sub_process.h"
#include "core/os/thread.h"
class _ResourceLoader : public Object {
@ -667,6 +668,54 @@ private:
bool _list_skip_hidden;
};
class _SubProcess : public Reference {
GDCLASS(_SubProcess, Reference);
SubProcess *_sub_process;
protected:
static void _bind_methods();
public:
String get_executable_path() const;
void set_executable_path(const String &p_executable_path);
Vector<String> get_arguments() const;
void set_arguments(const Vector<String> &p_arguments);
bool get_blocking() const;
void set_blocking(const bool p_value);
bool get_read_output() const;
void set_read_output(const bool p_value);
bool get_read_std() const;
void set_read_std(const bool p_value);
bool get_read_std_err() const;
void set_read_std_err(const bool p_value);
bool get_use_pipe_mutex() const;
void set_use_pipe_mutex(const bool p_value);
bool get_open_console() const;
void set_open_console(const bool p_value);
String get_data() const;
int get_process_id() const;
int get_exitcode() const;
Error start();
Error stop();
Error poll();
Error send_signal(const int p_signal);
Error send_data(const String &p_data);
bool is_process_running() const;
_SubProcess();
virtual ~_SubProcess();
};
class _Marshalls : public Object {
GDCLASS(_Marshalls, Object);

165
core/os/sub_process.cpp Normal file
View File

@ -0,0 +1,165 @@
/*************************************************************************/
/* file_access.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "sub_process.h"
SubProcess::CreateFunc SubProcess::create_func = NULL;
SubProcess *SubProcess::create() {
SubProcess *ret = create_func();
return ret;
}
String SubProcess::get_executable_path() const {
return _executable_path;
}
void SubProcess::set_executable_path(const String &p_executable_path) {
ERR_FAIL_COND(is_process_running());
_executable_path = p_executable_path;
}
Vector<String> SubProcess::get_arguments() const {
return _arguments;
}
void SubProcess::set_arguments(const Vector<String> &p_arguments) {
ERR_FAIL_COND(is_process_running());
_arguments = p_arguments;
}
bool SubProcess::get_blocking() const {
return _blocking;
}
void SubProcess::set_blocking(const bool p_value) {
ERR_FAIL_COND(is_process_running());
_blocking = p_value;
}
bool SubProcess::get_read_output() const {
return _read_output;
}
void SubProcess::set_read_output(const bool p_value) {
ERR_FAIL_COND(is_process_running());
_read_output = p_value;
}
bool SubProcess::get_read_std() const {
return _read_std;
}
void SubProcess::set_read_std(const bool p_value) {
ERR_FAIL_COND(is_process_running());
_read_std = p_value;
}
bool SubProcess::get_read_std_err() const {
return _read_std_err;
}
void SubProcess::set_read_std_err(const bool p_value) {
ERR_FAIL_COND(is_process_running());
_read_std_err = p_value;
}
bool SubProcess::get_use_pipe_mutex() const {
return _use_pipe_mutex;
}
void SubProcess::set_use_pipe_mutex(const bool p_value) {
ERR_FAIL_COND(is_process_running());
_use_pipe_mutex = p_value;
}
bool SubProcess::get_open_console() const {
return _open_console;
}
void SubProcess::set_open_console(const bool p_value) {
ERR_FAIL_COND(is_process_running());
_open_console = p_value;
}
Error SubProcess::run(const String &p_executable_path, const Vector<String> &p_arguments, bool p_output, bool p_blocking, bool p_read_std_err, bool p_use_pipe_mutex, bool p_open_console) {
if (is_process_running()) {
return ERR_ALREADY_IN_USE;
}
String _executable_path = p_executable_path;
Vector<String> _arguments = p_arguments;
_blocking = p_blocking;
_read_output = p_output;
_read_std = true;
_read_std_err = p_read_std_err;
_use_pipe_mutex = p_use_pipe_mutex;
_open_console = p_open_console;
_setup_pipe_mutex();
return start();
}
SubProcess::SubProcess() {
_blocking = false;
_read_output = true;
_read_std = true;
_read_std_err = false;
_use_pipe_mutex = false;
_pipe_mutex = NULL;
_open_console = false;
_process_id = ProcessID();
_exitcode = 0;
};
void SubProcess::_setup_pipe_mutex() {
if (_use_pipe_mutex) {
if (!_pipe_mutex) {
_pipe_mutex = memnew(Mutex);
}
} else {
if (_pipe_mutex) {
memdelete(_pipe_mutex);
_pipe_mutex = NULL;
}
}
}

164
core/os/sub_process.h Normal file
View File

@ -0,0 +1,164 @@
#ifndef SUB_PROCESS_H
#define SUB_PROCESS_H
/*************************************************************************/
/* file_access.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "core/containers/list.h"
#include "core/math/math_defs.h"
#include "core/os/memory.h"
#include "core/os/mutex.h"
#include "core/string/ustring.h"
#include "core/typedefs.h"
/**
* Multi-Platform abstraction for running and communicating with sub processes
*/
class SubProcess {
public:
typedef int64_t ProcessID;
static SubProcess *create();
String get_executable_path() const;
void set_executable_path(const String &p_executable_path);
Vector<String> get_arguments() const;
void set_arguments(const Vector<String> &p_arguments);
bool get_blocking() const;
void set_blocking(const bool p_value);
bool get_read_output() const;
void set_read_output(const bool p_value);
bool get_read_std() const;
void set_read_std(const bool p_value);
bool get_read_std_err() const;
void set_read_std_err(const bool p_value);
bool get_use_pipe_mutex() const;
void set_use_pipe_mutex(const bool p_value);
bool get_open_console() const;
void set_open_console(const bool p_value);
String get_data() const {
return _pipe;
}
int get_process_id() const {
return _process_id;
}
int get_exitcode() const {
return _exitcode;
}
virtual Error start() = 0;
virtual Error stop() = 0;
virtual Error poll() = 0;
virtual Error send_signal(const int p_signal) = 0;
virtual Error send_data(const String &p_data) = 0;
virtual bool is_process_running() const = 0;
Error run(const String &p_executable_path, const Vector<String> &p_arguments, bool p_output = true, bool p_blocking = true, bool p_read_std_err = false, bool p_use_pipe_mutex = false, bool p_open_console = false);
template <class T>
static void make_default() {
create_func = _create_builtin<T>;
}
SubProcess();
virtual ~SubProcess() {}
protected:
void _setup_pipe_mutex();
String _executable_path;
Vector<String> _arguments;
bool _blocking;
bool _read_output;
bool _read_std;
bool _read_std_err;
String _pipe;
bool _use_pipe_mutex;
Mutex *_pipe_mutex;
bool _open_console;
ProcessID _process_id;
int _exitcode;
public:
typedef SubProcess *(*CreateFunc)();
private:
static CreateFunc create_func;
template <class T>
static SubProcess *_create_builtin() {
return memnew(T);
}
};
struct SubProcessRef {
SubProcess *f;
_FORCE_INLINE_ bool is_null() const { return f == nullptr; }
_FORCE_INLINE_ bool is_valid() const { return f != nullptr; }
_FORCE_INLINE_ operator bool() const { return f != nullptr; }
_FORCE_INLINE_ operator SubProcess *() { return f; }
_FORCE_INLINE_ SubProcess *operator->() {
return f;
}
SubProcessRef(SubProcess *fa) { f = fa; }
SubProcessRef(SubProcessRef &&other) {
f = other.f;
other.f = nullptr;
}
~SubProcessRef() {
if (f) {
memdelete(f);
}
}
};
#endif

View File

@ -210,6 +210,7 @@ void register_core_types() {
ClassDB::register_class<_Mutex>();
ClassDB::register_class<_RWLock>();
ClassDB::register_class<_Semaphore>();
ClassDB::register_class<_SubProcess>();
ClassDB::register_class<XMLParser>();

View File

@ -36,6 +36,7 @@
#include "drivers/unix/dir_access_unix.h"
#include "drivers/unix/file_access_unix.h"
#include "drivers/unix/net_socket_posix.h"
#include "drivers/unix/sub_process_unix.h"
#include "drivers/unix/thread_posix.h"
#include "servers/rendering_server.h"
@ -120,6 +121,8 @@ void OS_Unix::initialize_core() {
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_USERDATA);
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
SubProcess::make_default<SubProcessUnix>();
#ifndef NO_NETWORK
NetSocketPosix::make_default();
IP_Unix::make_default();

View File

@ -0,0 +1,332 @@
/*************************************************************************/
/* dir_access_unix.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#include "sub_process_unix.h"
#ifdef UNIX_ENABLED
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
Error SubProcessUnix::start() {
if (is_process_running()) {
return ERR_BUSY;
}
#ifdef __EMSCRIPTEN__
// Don't compile this code at all to avoid undefined references.
// Actual virtual call goes to OS_JavaScript.
ERR_FAIL_V(ERR_BUG);
#else
if (_blocking && _read_output) {
String argss;
argss = "\"" + _executable_path + "\"";
for (int i = 0; i < _arguments.size(); i++) {
argss += String(" \"") + _arguments[i] + "\"";
}
if (_read_std_err) {
argss += " 2>&1"; // Read stderr too
} else {
argss += " 2>/dev/null"; //silence stderr
}
FILE *f = popen(argss.utf8().get_data(), "r");
ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot pipe stream from process running with following arguments '" + argss + "'.");
char buf[65535];
while (fgets(buf, 65535, f)) {
if (_pipe_mutex) {
_pipe_mutex->lock();
}
_pipe += String::utf8(buf);
if (_pipe_mutex) {
_pipe_mutex->unlock();
}
}
int rv = pclose(f);
_exitcode = WEXITSTATUS(rv);
return OK;
}
if (!_blocking && _read_output) {
String argss;
argss = "\"" + _executable_path + "\"";
for (int i = 0; i < _arguments.size(); i++) {
argss += String(" \"") + _arguments[i] + "\"";
}
if (_read_std_err) {
argss += " 2>&1"; // Read stderr too
} else {
argss += " 2>/dev/null"; //silence stderr
}
_process_fp = popen(argss.utf8().get_data(), "r");
ERR_FAIL_COND_V_MSG(!_process_fp, ERR_CANT_OPEN, "Cannot pipe stream from process running with following arguments '" + argss + "'.");
return OK;
}
// We just run it, no need to worry about output
pid_t pid = fork();
ERR_FAIL_COND_V(pid < 0, ERR_CANT_FORK);
if (pid == 0) {
// is child
if (!_blocking) {
// For non blocking calls, create a new session-ID so parent won't wait for it.
// This ensures the process won't go zombie at end.
setsid();
}
Vector<CharString> cs;
cs.push_back(_executable_path.utf8());
for (int i = 0; i < _arguments.size(); i++) {
cs.push_back(_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);
execvp(_executable_path.utf8().get_data(), &args[0]);
// still alive? something failed..
fprintf(stderr, "**ERROR** SubProcessUnix::execute - Could not create child process while executing: %s\n", _executable_path.utf8().get_data());
raise(SIGKILL);
}
if (_blocking) {
int status;
waitpid(pid, &status, 0);
_exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : status;
} else {
_process_id = pid;
}
return OK;
#endif
}
Error SubProcessUnix::stop() {
if (_process_fp) {
int rv = pclose(_process_fp);
_process_fp = NULL;
_exitcode = WEXITSTATUS(rv);
_process_id = 0;
return OK;
}
if (_process_id) {
int ret = ::kill(_process_id, SIGKILL);
if (!ret) {
//avoid zombie process
int st;
::waitpid(_process_id, &st, 0);
}
_process_id = 0;
return ret ? ERR_INVALID_PARAMETER : OK;
}
ERR_FAIL_V(ERR_BUG);
}
Error SubProcessUnix::poll() {
#ifdef __EMSCRIPTEN__
// Don't compile this code at all to avoid undefined references.
// Actual virtual call goes to OS_JavaScript.
ERR_FAIL_V(ERR_BUG);
#else
if (_process_fp) {
if (fgets(_process_buf, 65535, _process_fp)) {
if (_pipe_mutex) {
_pipe_mutex->lock();
}
_pipe = String::utf8(_process_buf);
if (_pipe_mutex) {
_pipe_mutex->unlock();
}
} else {
// The process finished
// Cleanup:
stop();
return ERR_FILE_EOF;
}
}
return OK;
#endif
}
Error SubProcessUnix::send_signal(const int p_signal) {
//Not Yet Impl
ERR_FAIL_V(ERR_BUG);
}
Error SubProcessUnix::send_data(const String &p_data) {
//Not Yet Impl
ERR_FAIL_V(ERR_BUG);
}
bool SubProcessUnix::is_process_running() const {
if (_process_fp) {
return !feof(_process_fp);
}
if (_process_id == 0) {
return false;
}
int status = 0;
if (waitpid(_process_id, &status, WNOHANG) != 0) {
return false;
}
return true;
}
SubProcessUnix::SubProcessUnix() : SubProcess() {
_process_fp = NULL;
}
SubProcessUnix::~SubProcessUnix() {
}
/*
Error SubProcessUnix::execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id, 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.
// Actual virtual call goes to OS_JavaScript.
ERR_FAIL_V(ERR_BUG);
#else
if (p_blocking && r_pipe) {
String argss;
argss = "\"" + p_path + "\"";
for (int i = 0; i < p_arguments.size(); i++) {
argss += String(" \"") + p_arguments[i] + "\"";
}
if (read_stderr) {
argss += " 2>&1"; // Read stderr too
} else {
argss += " 2>/dev/null"; //silence stderr
}
FILE *f = popen(argss.utf8().get_data(), "r");
ERR_FAIL_COND_V_MSG(!f, ERR_CANT_OPEN, "Cannot pipe stream from process running with following arguments '" + argss + "'.");
char buf[65535];
while (fgets(buf, 65535, f)) {
if (p_pipe_mutex) {
p_pipe_mutex->lock();
}
(*r_pipe) += String::utf8(buf);
if (p_pipe_mutex) {
p_pipe_mutex->unlock();
}
}
int rv = pclose(f);
if (r_exitcode) {
*r_exitcode = WEXITSTATUS(rv);
}
return OK;
}
pid_t pid = fork();
ERR_FAIL_COND_V(pid < 0, ERR_CANT_FORK);
if (pid == 0) {
// is child
if (!p_blocking) {
// For non blocking calls, create a new session-ID so parent won't wait for it.
// This ensures the process won't go zombie at end.
setsid();
}
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);
execvp(p_path.utf8().get_data(), &args[0]);
// still alive? something failed..
fprintf(stderr, "**ERROR** SubProcessUnix::execute - Could not create child process while executing: %s\n", p_path.utf8().get_data());
raise(SIGKILL);
}
if (p_blocking) {
int status;
waitpid(pid, &status, 0);
if (r_exitcode) {
*r_exitcode = WIFEXITED(status) ? WEXITSTATUS(status) : status;
}
} else {
if (r_child_id) {
*r_child_id = pid;
}
}
return OK;
#endif
}
*/
#endif //posix_enabled

View File

@ -0,0 +1,58 @@
#ifndef SUB_PROCESS_UNIX_H
#define SUB_PROCESS_UNIX_H
/*************************************************************************/
/* dir_access_unix.h */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/*************************************************************************/
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
#ifdef UNIX_ENABLED
#include "core/os/sub_process.h"
#include <stdio.h>
class SubProcessUnix : public SubProcess {
public:
virtual Error start();
virtual Error stop();
virtual Error poll();
virtual Error send_signal(const int p_signal);
virtual Error send_data(const String &p_data);
virtual bool is_process_running() const;
SubProcessUnix();
~SubProcessUnix();
protected:
FILE *_process_fp;
char _process_buf[65535];
};
#endif //UNIX ENABLED
#endif