mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2024-12-22 11:56:49 +01:00
Added a new SubProcess class with a Uniz backend for better process control.
This commit is contained in:
parent
3cfe43b5e8
commit
5fd0326745
@ -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::singleton = nullptr;
|
||||||
|
|
||||||
_Marshalls *_Marshalls::get_singleton() {
|
_Marshalls *_Marshalls::get_singleton() {
|
||||||
|
@ -41,6 +41,7 @@
|
|||||||
#include "core/os/rw_lock.h"
|
#include "core/os/rw_lock.h"
|
||||||
#include "core/os/safe_refcount.h"
|
#include "core/os/safe_refcount.h"
|
||||||
#include "core/os/semaphore.h"
|
#include "core/os/semaphore.h"
|
||||||
|
#include "core/os/sub_process.h"
|
||||||
#include "core/os/thread.h"
|
#include "core/os/thread.h"
|
||||||
|
|
||||||
class _ResourceLoader : public Object {
|
class _ResourceLoader : public Object {
|
||||||
@ -667,6 +668,54 @@ private:
|
|||||||
bool _list_skip_hidden;
|
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 {
|
class _Marshalls : public Object {
|
||||||
GDCLASS(_Marshalls, Object);
|
GDCLASS(_Marshalls, Object);
|
||||||
|
|
||||||
|
165
core/os/sub_process.cpp
Normal file
165
core/os/sub_process.cpp
Normal 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
164
core/os/sub_process.h
Normal 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
|
@ -210,6 +210,7 @@ void register_core_types() {
|
|||||||
ClassDB::register_class<_Mutex>();
|
ClassDB::register_class<_Mutex>();
|
||||||
ClassDB::register_class<_RWLock>();
|
ClassDB::register_class<_RWLock>();
|
||||||
ClassDB::register_class<_Semaphore>();
|
ClassDB::register_class<_Semaphore>();
|
||||||
|
ClassDB::register_class<_SubProcess>();
|
||||||
|
|
||||||
ClassDB::register_class<XMLParser>();
|
ClassDB::register_class<XMLParser>();
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@
|
|||||||
#include "drivers/unix/dir_access_unix.h"
|
#include "drivers/unix/dir_access_unix.h"
|
||||||
#include "drivers/unix/file_access_unix.h"
|
#include "drivers/unix/file_access_unix.h"
|
||||||
#include "drivers/unix/net_socket_posix.h"
|
#include "drivers/unix/net_socket_posix.h"
|
||||||
|
#include "drivers/unix/sub_process_unix.h"
|
||||||
#include "drivers/unix/thread_posix.h"
|
#include "drivers/unix/thread_posix.h"
|
||||||
#include "servers/rendering_server.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_USERDATA);
|
||||||
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
|
DirAccess::make_default<DirAccessUnix>(DirAccess::ACCESS_FILESYSTEM);
|
||||||
|
|
||||||
|
SubProcess::make_default<SubProcessUnix>();
|
||||||
|
|
||||||
#ifndef NO_NETWORK
|
#ifndef NO_NETWORK
|
||||||
NetSocketPosix::make_default();
|
NetSocketPosix::make_default();
|
||||||
IP_Unix::make_default();
|
IP_Unix::make_default();
|
||||||
|
332
drivers/unix/sub_process_unix.cpp
Normal file
332
drivers/unix/sub_process_unix.cpp
Normal 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
|
58
drivers/unix/sub_process_unix.h
Normal file
58
drivers/unix/sub_process_unix.h
Normal 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
|
Loading…
Reference in New Issue
Block a user