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:
parent
7d151c8381
commit
082b420c0a
|
@ -283,8 +283,8 @@ String OS::read_string_from_stdin() {
|
||||||
|
|
||||||
int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr, bool p_open_console) {
|
int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r_output, bool p_read_stderr, bool p_open_console) {
|
||||||
List<String> args;
|
List<String> args;
|
||||||
for (int i = 0; i < p_arguments.size(); i++) {
|
for (const String &arg : p_arguments) {
|
||||||
args.push_back(p_arguments[i]);
|
args.push_back(arg);
|
||||||
}
|
}
|
||||||
String pipe;
|
String pipe;
|
||||||
int exitcode = 0;
|
int exitcode = 0;
|
||||||
|
@ -296,10 +296,18 @@ int OS::execute(const String &p_path, const Vector<String> &p_arguments, Array r
|
||||||
return exitcode;
|
return exitcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dictionary OS::execute_with_pipe(const String &p_path, const Vector<String> &p_arguments) {
|
||||||
|
List<String> args;
|
||||||
|
for (const String &arg : p_arguments) {
|
||||||
|
args.push_back(arg);
|
||||||
|
}
|
||||||
|
return ::OS::get_singleton()->execute_with_pipe(p_path, args);
|
||||||
|
}
|
||||||
|
|
||||||
int OS::create_instance(const Vector<String> &p_arguments) {
|
int OS::create_instance(const Vector<String> &p_arguments) {
|
||||||
List<String> args;
|
List<String> args;
|
||||||
for (int i = 0; i < p_arguments.size(); i++) {
|
for (const String &arg : p_arguments) {
|
||||||
args.push_back(p_arguments[i]);
|
args.push_back(arg);
|
||||||
}
|
}
|
||||||
::OS::ProcessID pid = 0;
|
::OS::ProcessID pid = 0;
|
||||||
Error err = ::OS::get_singleton()->create_instance(args, &pid);
|
Error err = ::OS::get_singleton()->create_instance(args, &pid);
|
||||||
|
@ -311,8 +319,8 @@ int OS::create_instance(const Vector<String> &p_arguments) {
|
||||||
|
|
||||||
int OS::create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console) {
|
int OS::create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console) {
|
||||||
List<String> args;
|
List<String> args;
|
||||||
for (int i = 0; i < p_arguments.size(); i++) {
|
for (const String &arg : p_arguments) {
|
||||||
args.push_back(p_arguments[i]);
|
args.push_back(arg);
|
||||||
}
|
}
|
||||||
::OS::ProcessID pid = 0;
|
::OS::ProcessID pid = 0;
|
||||||
Error err = ::OS::get_singleton()->create_process(p_path, args, &pid, p_open_console);
|
Error err = ::OS::get_singleton()->create_process(p_path, args, &pid, p_open_console);
|
||||||
|
@ -587,6 +595,7 @@ void OS::_bind_methods() {
|
||||||
ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
|
ClassDB::bind_method(D_METHOD("get_executable_path"), &OS::get_executable_path);
|
||||||
ClassDB::bind_method(D_METHOD("read_string_from_stdin"), &OS::read_string_from_stdin);
|
ClassDB::bind_method(D_METHOD("read_string_from_stdin"), &OS::read_string_from_stdin);
|
||||||
ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
|
ClassDB::bind_method(D_METHOD("execute", "path", "arguments", "output", "read_stderr", "open_console"), &OS::execute, DEFVAL(Array()), DEFVAL(false), DEFVAL(false));
|
||||||
|
ClassDB::bind_method(D_METHOD("execute_with_pipe", "path", "arguments"), &OS::execute_with_pipe);
|
||||||
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
|
ClassDB::bind_method(D_METHOD("create_process", "path", "arguments", "open_console"), &OS::create_process, DEFVAL(false));
|
||||||
ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
|
ClassDB::bind_method(D_METHOD("create_instance", "arguments"), &OS::create_instance);
|
||||||
ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);
|
ClassDB::bind_method(D_METHOD("kill", "pid"), &OS::kill);
|
||||||
|
|
|
@ -156,6 +156,7 @@ public:
|
||||||
String get_executable_path() const;
|
String get_executable_path() const;
|
||||||
String read_string_from_stdin();
|
String read_string_from_stdin();
|
||||||
int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
|
int execute(const String &p_path, const Vector<String> &p_arguments, Array r_output = Array(), bool p_read_stderr = false, bool p_open_console = false);
|
||||||
|
Dictionary execute_with_pipe(const String &p_path, const Vector<String> &p_arguments);
|
||||||
int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
|
int create_process(const String &p_path, const Vector<String> &p_arguments, bool p_open_console = false);
|
||||||
int create_instance(const Vector<String> &p_arguments);
|
int create_instance(const Vector<String> &p_arguments);
|
||||||
Error kill(int p_pid);
|
Error kill(int p_pid);
|
||||||
|
|
|
@ -47,6 +47,7 @@ thread_local Error FileAccess::last_file_open_error = OK;
|
||||||
|
|
||||||
Ref<FileAccess> FileAccess::create(AccessType p_access) {
|
Ref<FileAccess> FileAccess::create(AccessType p_access) {
|
||||||
ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr);
|
ERR_FAIL_INDEX_V(p_access, ACCESS_MAX, nullptr);
|
||||||
|
ERR_FAIL_NULL_V(create_func[p_access], nullptr);
|
||||||
|
|
||||||
Ref<FileAccess> ret = create_func[p_access]();
|
Ref<FileAccess> ret = create_func[p_access]();
|
||||||
ret->_set_access_type(p_access);
|
ret->_set_access_type(p_access);
|
||||||
|
@ -75,7 +76,8 @@ Ref<FileAccess> FileAccess::create_for_path(const String &p_path) {
|
||||||
ret = create(ACCESS_RESOURCES);
|
ret = create(ACCESS_RESOURCES);
|
||||||
} else if (p_path.begins_with("user://")) {
|
} else if (p_path.begins_with("user://")) {
|
||||||
ret = create(ACCESS_USERDATA);
|
ret = create(ACCESS_USERDATA);
|
||||||
|
} else if (p_path.begins_with("pipe://")) {
|
||||||
|
ret = create(ACCESS_PIPE);
|
||||||
} else {
|
} else {
|
||||||
ret = create(ACCESS_FILESYSTEM);
|
ret = create(ACCESS_FILESYSTEM);
|
||||||
}
|
}
|
||||||
|
@ -209,6 +211,9 @@ String FileAccess::fix_path(const String &p_path) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
|
case ACCESS_PIPE: {
|
||||||
|
return r_path;
|
||||||
|
} break;
|
||||||
case ACCESS_FILESYSTEM: {
|
case ACCESS_FILESYSTEM: {
|
||||||
return r_path;
|
return r_path;
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -50,6 +50,7 @@ public:
|
||||||
ACCESS_RESOURCES,
|
ACCESS_RESOURCES,
|
||||||
ACCESS_USERDATA,
|
ACCESS_USERDATA,
|
||||||
ACCESS_FILESYSTEM,
|
ACCESS_FILESYSTEM,
|
||||||
|
ACCESS_PIPE,
|
||||||
ACCESS_MAX
|
ACCESS_MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -170,6 +170,7 @@ public:
|
||||||
virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return Vector<String>(); };
|
virtual Vector<String> get_system_font_path_for_text(const String &p_font_name, const String &p_text, const String &p_locale = String(), const String &p_script = String(), int p_weight = 400, int p_stretch = 100, bool p_italic = false) const { return Vector<String>(); };
|
||||||
virtual String get_executable_path() const;
|
virtual String get_executable_path() const;
|
||||||
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0;
|
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) = 0;
|
||||||
|
virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) { return Dictionary(); }
|
||||||
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0;
|
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) = 0;
|
||||||
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) { return create_process(get_executable_path(), p_arguments, r_child_id); };
|
virtual Error create_instance(const List<String> &p_arguments, ProcessID *r_child_id = nullptr) { return create_process(get_executable_path(), p_arguments, r_child_id); };
|
||||||
virtual Error kill(const ProcessID &p_pid) = 0;
|
virtual Error kill(const ProcessID &p_pid) = 0;
|
||||||
|
|
|
@ -50,9 +50,9 @@
|
||||||
<param index="1" name="arguments" type="PackedStringArray" />
|
<param index="1" name="arguments" type="PackedStringArray" />
|
||||||
<param index="2" name="open_console" type="bool" default="false" />
|
<param index="2" name="open_console" type="bool" default="false" />
|
||||||
<description>
|
<description>
|
||||||
Creates a new process that runs independently of Godot. It will not terminate when Godot terminates. The path specified in [param path] must exist and be executable file or macOS .app bundle. Platform path resolution will be used. The [param arguments] are used in the given order and separated by a space.
|
Creates a new process that runs independently of Godot. It will not terminate when Godot terminates. The path specified in [param path] must exist and be an executable file or macOS [code].app[/code] bundle. The path is resolved based on the current platform. The [param arguments] are used in the given order and separated by a space.
|
||||||
On Windows, if [param open_console] is [code]true[/code] and the process is a console app, a new terminal window will be opened.
|
On Windows, if [param open_console] is [code]true[/code] and the process is a console app, a new terminal window will be opened.
|
||||||
If the process is successfully created, this method returns its process ID, which you can use to monitor the process (and potentially terminate it with [method kill]). Otherwise this method returns [code]-1[/code].
|
If the process is successfully created, this method returns its process ID, which you can use to monitor the process (and potentially terminate it with [method kill]). Otherwise, this method returns [code]-1[/code].
|
||||||
For example, running another instance of the project:
|
For example, running another instance of the project:
|
||||||
[codeblocks]
|
[codeblocks]
|
||||||
[gdscript]
|
[gdscript]
|
||||||
|
@ -63,7 +63,7 @@
|
||||||
[/csharp]
|
[/csharp]
|
||||||
[/codeblocks]
|
[/codeblocks]
|
||||||
See [method execute] if you wish to run an external command and retrieve the results.
|
See [method execute] if you wish to run an external command and retrieve the results.
|
||||||
[b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
|
[b]Note:[/b] This method is implemented on Android, Linux, macOS, and Windows.
|
||||||
[b]Note:[/b] On macOS, sandboxed applications are limited to run only embedded helper executables, specified during export or system .app bundle, system .app bundles will ignore arguments.
|
[b]Note:[/b] On macOS, sandboxed applications are limited to run only embedded helper executables, specified during export or system .app bundle, system .app bundles will ignore arguments.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
@ -120,7 +120,7 @@
|
||||||
OS.Execute("CMD.exe", new string[] {"/C", "cd %TEMP% && dir"}, output);
|
OS.Execute("CMD.exe", new string[] {"/C", "cd %TEMP% && dir"}, output);
|
||||||
[/csharp]
|
[/csharp]
|
||||||
[/codeblocks]
|
[/codeblocks]
|
||||||
[b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows.
|
[b]Note:[/b] This method is implemented on Android, Linux, macOS, and Windows.
|
||||||
[b]Note:[/b] To execute a Windows command interpreter built-in command, specify [code]cmd.exe[/code] in [param path], [code]/c[/code] as the first argument, and the desired command as the second argument.
|
[b]Note:[/b] To execute a Windows command interpreter built-in command, specify [code]cmd.exe[/code] in [param path], [code]/c[/code] as the first argument, and the desired command as the second argument.
|
||||||
[b]Note:[/b] To execute a PowerShell built-in command, specify [code]powershell.exe[/code] in [param path], [code]-Command[/code] as the first argument, and the desired command as the second argument.
|
[b]Note:[/b] To execute a PowerShell built-in command, specify [code]powershell.exe[/code] in [param path], [code]-Command[/code] as the first argument, and the desired command as the second argument.
|
||||||
[b]Note:[/b] To execute a Unix shell built-in command, specify shell executable name in [param path], [code]-c[/code] as the first argument, and the desired command as the second argument.
|
[b]Note:[/b] To execute a Unix shell built-in command, specify shell executable name in [param path], [code]-c[/code] as the first argument, and the desired command as the second argument.
|
||||||
|
@ -128,6 +128,23 @@
|
||||||
[b]Note:[/b] On Android, system commands such as [code]dumpsys[/code] can only be run on a rooted device.
|
[b]Note:[/b] On Android, system commands such as [code]dumpsys[/code] can only be run on a rooted device.
|
||||||
</description>
|
</description>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="execute_with_pipe">
|
||||||
|
<return type="Dictionary" />
|
||||||
|
<param index="0" name="path" type="String" />
|
||||||
|
<param index="1" name="arguments" type="PackedStringArray" />
|
||||||
|
<description>
|
||||||
|
Creates a new process that runs independently of Godot with redirected IO. It will not terminate when Godot terminates. The path specified in [param path] must exist and be an executable file or macOS [code].app[/code] bundle. The path is resolved based on the current platform. The [param arguments] are used in the given order and separated by a space.
|
||||||
|
If the process cannot be created, this method returns an empty [Dictionary]. Otherwise, this method returns a [Dictionary] with the following keys:
|
||||||
|
- [code]"stdio"[/code] - [FileAccess] to access the process stdin and stdout pipes (read/write).
|
||||||
|
- [code]"stderr"[/code] - [FileAccess] to access the process stderr pipe (read only).
|
||||||
|
- [code]"pid"[/code] - Process ID as an [int], which you can use to monitor the process (and potentially terminate it with [method kill]).
|
||||||
|
[b]Note:[/b] This method is implemented on Android, Linux, macOS, and Windows.
|
||||||
|
[b]Note:[/b] To execute a Windows command interpreter built-in command, specify [code]cmd.exe[/code] in [param path], [code]/c[/code] as the first argument, and the desired command as the second argument.
|
||||||
|
[b]Note:[/b] To execute a PowerShell built-in command, specify [code]powershell.exe[/code] in [param path], [code]-Command[/code] as the first argument, and the desired command as the second argument.
|
||||||
|
[b]Note:[/b] To execute a Unix shell built-in command, specify shell executable name in [param path], [code]-c[/code] as the first argument, and the desired command as the second argument.
|
||||||
|
[b]Note:[/b] On macOS, sandboxed applications are limited to run only embedded helper executables, specified during export or system .app bundle, system .app bundles will ignore arguments.
|
||||||
|
</description>
|
||||||
|
</method>
|
||||||
<method name="find_keycode_from_string" qualifiers="const">
|
<method name="find_keycode_from_string" qualifiers="const">
|
||||||
<return type="int" enum="Key" />
|
<return type="int" enum="Key" />
|
||||||
<param index="0" name="string" type="String" />
|
<param index="0" name="string" type="String" />
|
||||||
|
|
|
@ -0,0 +1,185 @@
|
||||||
|
/**************************************************************************/
|
||||||
|
/* file_access_unix_pipe.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* 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 "file_access_unix_pipe.h"
|
||||||
|
|
||||||
|
#if defined(UNIX_ENABLED)
|
||||||
|
|
||||||
|
#include "core/os/os.h"
|
||||||
|
#include "core/string/print_string.h"
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
Error FileAccessUnixPipe::open_existing(int p_rfd, int p_wfd) {
|
||||||
|
// Open pipe using handles created by pipe(fd) call in the OS.execute_with_pipe.
|
||||||
|
_close();
|
||||||
|
|
||||||
|
path_src = String();
|
||||||
|
unlink_on_close = false;
|
||||||
|
ERR_FAIL_COND_V_MSG(fd[0] >= 0 || fd[1] >= 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
|
||||||
|
fd[0] = p_rfd;
|
||||||
|
fd[1] = p_wfd;
|
||||||
|
|
||||||
|
last_error = OK;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error FileAccessUnixPipe::open_internal(const String &p_path, int p_mode_flags) {
|
||||||
|
_close();
|
||||||
|
|
||||||
|
path_src = p_path;
|
||||||
|
ERR_FAIL_COND_V_MSG(fd[0] >= 0 || fd[1] >= 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
|
||||||
|
|
||||||
|
path = String("/tmp/") + p_path.replace("pipe://", "").replace("/", "_");
|
||||||
|
struct stat st = {};
|
||||||
|
int err = stat(path.utf8().get_data(), &st);
|
||||||
|
if (err) {
|
||||||
|
if (mkfifo(path.utf8().get_data(), 0666) != 0) {
|
||||||
|
last_error = ERR_FILE_CANT_OPEN;
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
unlink_on_close = true;
|
||||||
|
} else {
|
||||||
|
ERR_FAIL_COND_V_MSG(!S_ISFIFO(st.st_mode), ERR_ALREADY_IN_USE, "Pipe name is already used by file.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int f = ::open(path.utf8().get_data(), O_RDWR | O_CLOEXEC);
|
||||||
|
if (f < 0) {
|
||||||
|
switch (errno) {
|
||||||
|
case ENOENT: {
|
||||||
|
last_error = ERR_FILE_NOT_FOUND;
|
||||||
|
} break;
|
||||||
|
default: {
|
||||||
|
last_error = ERR_FILE_CANT_OPEN;
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set close on exec to avoid leaking it to subprocesses.
|
||||||
|
fd[0] = f;
|
||||||
|
fd[1] = f;
|
||||||
|
|
||||||
|
last_error = OK;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessUnixPipe::_close() {
|
||||||
|
if (fd[0] < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd[1] != fd[0]) {
|
||||||
|
::close(fd[1]);
|
||||||
|
}
|
||||||
|
::close(fd[0]);
|
||||||
|
fd[0] = -1;
|
||||||
|
fd[1] = -1;
|
||||||
|
|
||||||
|
if (unlink_on_close) {
|
||||||
|
::unlink(path.utf8().ptr());
|
||||||
|
}
|
||||||
|
unlink_on_close = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileAccessUnixPipe::is_open() const {
|
||||||
|
return (fd[0] >= 0 || fd[1] >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
String FileAccessUnixPipe::get_path() const {
|
||||||
|
return path_src;
|
||||||
|
}
|
||||||
|
|
||||||
|
String FileAccessUnixPipe::get_path_absolute() const {
|
||||||
|
return path_src;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t FileAccessUnixPipe::get_8() const {
|
||||||
|
ERR_FAIL_COND_V_MSG(fd[0] < 0, 0, "Pipe must be opened before use.");
|
||||||
|
|
||||||
|
uint8_t b;
|
||||||
|
if (::read(fd[0], &b, 1) == 0) {
|
||||||
|
last_error = ERR_FILE_CANT_READ;
|
||||||
|
b = '\0';
|
||||||
|
} else {
|
||||||
|
last_error = OK;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t FileAccessUnixPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||||
|
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||||
|
ERR_FAIL_COND_V_MSG(fd[0] < 0, -1, "Pipe must be opened before use.");
|
||||||
|
|
||||||
|
uint64_t read = ::read(fd[0], p_dst, p_length);
|
||||||
|
if (read == p_length) {
|
||||||
|
last_error = ERR_FILE_CANT_READ;
|
||||||
|
} else {
|
||||||
|
last_error = OK;
|
||||||
|
}
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error FileAccessUnixPipe::get_error() const {
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessUnixPipe::store_8(uint8_t p_src) {
|
||||||
|
ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use.");
|
||||||
|
if (::write(fd[1], &p_src, 1) != 1) {
|
||||||
|
last_error = ERR_FILE_CANT_WRITE;
|
||||||
|
} else {
|
||||||
|
last_error = OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessUnixPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||||
|
ERR_FAIL_COND_MSG(fd[1] < 0, "Pipe must be opened before use.");
|
||||||
|
ERR_FAIL_COND(!p_src && p_length > 0);
|
||||||
|
if (::write(fd[1], p_src, p_length) != (ssize_t)p_length) {
|
||||||
|
last_error = ERR_FILE_CANT_WRITE;
|
||||||
|
} else {
|
||||||
|
last_error = OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessUnixPipe::close() {
|
||||||
|
_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileAccessUnixPipe::~FileAccessUnixPipe() {
|
||||||
|
_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // UNIX_ENABLED
|
|
@ -0,0 +1,96 @@
|
||||||
|
/**************************************************************************/
|
||||||
|
/* file_access_unix_pipe.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* 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. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef FILE_ACCESS_UNIX_PIPE_H
|
||||||
|
#define FILE_ACCESS_UNIX_PIPE_H
|
||||||
|
|
||||||
|
#include "core/io/file_access.h"
|
||||||
|
#include "core/os/memory.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#if defined(UNIX_ENABLED)
|
||||||
|
|
||||||
|
class FileAccessUnixPipe : public FileAccess {
|
||||||
|
bool unlink_on_close = false;
|
||||||
|
|
||||||
|
int fd[2] = { -1, -1 };
|
||||||
|
|
||||||
|
mutable Error last_error = OK;
|
||||||
|
String path;
|
||||||
|
String path_src;
|
||||||
|
|
||||||
|
void _close();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Error open_existing(int p_rfd, int p_wfd);
|
||||||
|
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
|
||||||
|
|
||||||
|
virtual bool is_open() const override; ///< true when file is open
|
||||||
|
|
||||||
|
virtual String get_path() const override; /// returns the path for the current open file
|
||||||
|
virtual String get_path_absolute() const override; /// returns the absolute path for the current open file
|
||||||
|
|
||||||
|
virtual void seek(uint64_t p_position) override {}
|
||||||
|
virtual void seek_end(int64_t p_position = 0) override {}
|
||||||
|
virtual uint64_t get_position() const override { return 0; }
|
||||||
|
virtual uint64_t get_length() const override { return 0; }
|
||||||
|
|
||||||
|
virtual bool eof_reached() const override { return false; }
|
||||||
|
|
||||||
|
virtual uint8_t get_8() const override; ///< get a byte
|
||||||
|
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||||
|
|
||||||
|
virtual Error get_error() const override; ///< get last error
|
||||||
|
|
||||||
|
virtual void flush() override {}
|
||||||
|
virtual void store_8(uint8_t p_src) override; ///< store a byte
|
||||||
|
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
|
||||||
|
|
||||||
|
virtual bool file_exists(const String &p_path) override { return false; }
|
||||||
|
|
||||||
|
virtual uint64_t _get_modified_time(const String &p_file) override { return 0; }
|
||||||
|
virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
|
||||||
|
virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return ERR_UNAVAILABLE; }
|
||||||
|
|
||||||
|
virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
|
||||||
|
virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
|
||||||
|
virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
|
||||||
|
virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
|
||||||
|
|
||||||
|
virtual void close() override;
|
||||||
|
|
||||||
|
FileAccessUnixPipe() {}
|
||||||
|
virtual ~FileAccessUnixPipe();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // UNIX_ENABLED
|
||||||
|
|
||||||
|
#endif // FILE_ACCESS_UNIX_PIPE_H
|
|
@ -37,6 +37,7 @@
|
||||||
#include "core/debugger/script_debugger.h"
|
#include "core/debugger/script_debugger.h"
|
||||||
#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/file_access_unix_pipe.h"
|
||||||
#include "drivers/unix/net_socket_posix.h"
|
#include "drivers/unix/net_socket_posix.h"
|
||||||
#include "drivers/unix/thread_posix.h"
|
#include "drivers/unix/thread_posix.h"
|
||||||
#include "servers/rendering_server.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_RESOURCES);
|
||||||
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
|
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
|
||||||
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
|
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_RESOURCES);
|
||||||
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);
|
||||||
|
@ -489,6 +491,106 @@ Dictionary OS_Unix::get_memory_info() const {
|
||||||
return meminfo;
|
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) {
|
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__
|
#ifdef __EMSCRIPTEN__
|
||||||
// Don't compile this code at all to avoid undefined references.
|
// Don't compile this code at all to avoid undefined references.
|
||||||
|
|
|
@ -76,6 +76,7 @@ public:
|
||||||
virtual Dictionary get_memory_info() const override;
|
virtual Dictionary get_memory_info() const override;
|
||||||
|
|
||||||
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
|
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
|
||||||
|
virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) override;
|
||||||
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
|
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
|
||||||
virtual Error kill(const ProcessID &p_pid) override;
|
virtual Error kill(const ProcessID &p_pid) override;
|
||||||
virtual int get_process_id() const override;
|
virtual int get_process_id() const override;
|
||||||
|
|
|
@ -0,0 +1,159 @@
|
||||||
|
/**************************************************************************/
|
||||||
|
/* file_access_windows_pipe.cpp */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* 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 WINDOWS_ENABLED
|
||||||
|
|
||||||
|
#include "file_access_windows_pipe.h"
|
||||||
|
|
||||||
|
#include "core/os/os.h"
|
||||||
|
#include "core/string/print_string.h"
|
||||||
|
|
||||||
|
Error FileAccessWindowsPipe::open_existing(HANDLE p_rfd, HANDLE p_wfd) {
|
||||||
|
// Open pipe using handles created by CreatePipe(rfd, wfd, NULL, 4096) call in the OS.execute_with_pipe.
|
||||||
|
_close();
|
||||||
|
|
||||||
|
path_src = String();
|
||||||
|
ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
|
||||||
|
fd[0] = p_rfd;
|
||||||
|
fd[1] = p_wfd;
|
||||||
|
|
||||||
|
last_error = OK;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error FileAccessWindowsPipe::open_internal(const String &p_path, int p_mode_flags) {
|
||||||
|
_close();
|
||||||
|
|
||||||
|
path_src = p_path;
|
||||||
|
ERR_FAIL_COND_V_MSG(fd[0] != 0 || fd[1] != 0, ERR_ALREADY_IN_USE, "Pipe is already in use.");
|
||||||
|
|
||||||
|
path = String("\\\\.\\pipe\\LOCAL\\") + p_path.replace("pipe://", "").replace("/", "_");
|
||||||
|
|
||||||
|
HANDLE h = CreateFileW((LPCWSTR)path.utf16().get_data(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
|
if (h == INVALID_HANDLE_VALUE) {
|
||||||
|
h = CreateNamedPipeW((LPCWSTR)path.utf16().get_data(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, 1, 4096, 4096, 0, nullptr);
|
||||||
|
if (h == INVALID_HANDLE_VALUE) {
|
||||||
|
last_error = ERR_FILE_CANT_OPEN;
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
ConnectNamedPipe(h, NULL);
|
||||||
|
}
|
||||||
|
fd[0] = h;
|
||||||
|
fd[1] = h;
|
||||||
|
|
||||||
|
last_error = OK;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessWindowsPipe::_close() {
|
||||||
|
if (fd[0] == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fd[1] != fd[0]) {
|
||||||
|
CloseHandle(fd[1]);
|
||||||
|
}
|
||||||
|
CloseHandle(fd[0]);
|
||||||
|
fd[0] = 0;
|
||||||
|
fd[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FileAccessWindowsPipe::is_open() const {
|
||||||
|
return (fd[0] != 0 || fd[1] != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
String FileAccessWindowsPipe::get_path() const {
|
||||||
|
return path_src;
|
||||||
|
}
|
||||||
|
|
||||||
|
String FileAccessWindowsPipe::get_path_absolute() const {
|
||||||
|
return path_src;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t FileAccessWindowsPipe::get_8() const {
|
||||||
|
ERR_FAIL_COND_V_MSG(fd[0] == 0, 0, "Pipe must be opened before use.");
|
||||||
|
|
||||||
|
uint8_t b;
|
||||||
|
if (!ReadFile(fd[0], &b, 1, nullptr, nullptr)) {
|
||||||
|
last_error = ERR_FILE_CANT_READ;
|
||||||
|
b = '\0';
|
||||||
|
} else {
|
||||||
|
last_error = OK;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t FileAccessWindowsPipe::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
|
||||||
|
ERR_FAIL_COND_V(!p_dst && p_length > 0, -1);
|
||||||
|
ERR_FAIL_COND_V_MSG(fd[0] == 0, -1, "Pipe must be opened before use.");
|
||||||
|
|
||||||
|
DWORD read = -1;
|
||||||
|
if (!ReadFile(fd[0], p_dst, p_length, &read, nullptr) || read != p_length) {
|
||||||
|
last_error = ERR_FILE_CANT_READ;
|
||||||
|
} else {
|
||||||
|
last_error = OK;
|
||||||
|
}
|
||||||
|
return read;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error FileAccessWindowsPipe::get_error() const {
|
||||||
|
return last_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessWindowsPipe::store_8(uint8_t p_src) {
|
||||||
|
ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use.");
|
||||||
|
if (!WriteFile(fd[1], &p_src, 1, nullptr, nullptr)) {
|
||||||
|
last_error = ERR_FILE_CANT_WRITE;
|
||||||
|
} else {
|
||||||
|
last_error = OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessWindowsPipe::store_buffer(const uint8_t *p_src, uint64_t p_length) {
|
||||||
|
ERR_FAIL_COND_MSG(fd[1] == 0, "Pipe must be opened before use.");
|
||||||
|
ERR_FAIL_COND(!p_src && p_length > 0);
|
||||||
|
|
||||||
|
DWORD read = -1;
|
||||||
|
bool ok = WriteFile(fd[1], p_src, p_length, &read, nullptr);
|
||||||
|
if (!ok || read != p_length) {
|
||||||
|
last_error = ERR_FILE_CANT_WRITE;
|
||||||
|
} else {
|
||||||
|
last_error = OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FileAccessWindowsPipe::close() {
|
||||||
|
_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
FileAccessWindowsPipe::~FileAccessWindowsPipe() {
|
||||||
|
_close();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // WINDOWS_ENABLED
|
|
@ -0,0 +1,95 @@
|
||||||
|
/**************************************************************************/
|
||||||
|
/* file_access_windows_pipe.h */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/**************************************************************************/
|
||||||
|
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
|
||||||
|
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* */
|
||||||
|
/* 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. */
|
||||||
|
/**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef FILE_ACCESS_WINDOWS_PIPE_H
|
||||||
|
#define FILE_ACCESS_WINDOWS_PIPE_H
|
||||||
|
|
||||||
|
#ifdef WINDOWS_ENABLED
|
||||||
|
|
||||||
|
#include "core/io/file_access.h"
|
||||||
|
#include "core/os/memory.h"
|
||||||
|
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#include <windows.h>
|
||||||
|
class FileAccessWindowsPipe : public FileAccess {
|
||||||
|
HANDLE fd[2] = { 0, 0 };
|
||||||
|
|
||||||
|
mutable Error last_error = OK;
|
||||||
|
|
||||||
|
String path;
|
||||||
|
String path_src;
|
||||||
|
|
||||||
|
void _close();
|
||||||
|
|
||||||
|
public:
|
||||||
|
Error open_existing(HANDLE p_rfd, HANDLE p_wfd);
|
||||||
|
|
||||||
|
virtual Error open_internal(const String &p_path, int p_mode_flags) override; ///< open a file
|
||||||
|
virtual bool is_open() const override; ///< true when file is open
|
||||||
|
|
||||||
|
virtual String get_path() const override; /// returns the path for the current open file
|
||||||
|
virtual String get_path_absolute() const override; /// returns the absolute path for the current open file
|
||||||
|
|
||||||
|
virtual void seek(uint64_t p_position) override {}
|
||||||
|
virtual void seek_end(int64_t p_position = 0) override {}
|
||||||
|
virtual uint64_t get_position() const override { return 0; }
|
||||||
|
virtual uint64_t get_length() const override { return 0; }
|
||||||
|
|
||||||
|
virtual bool eof_reached() const override { return false; }
|
||||||
|
|
||||||
|
virtual uint8_t get_8() const override; ///< get a byte
|
||||||
|
virtual uint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const override;
|
||||||
|
|
||||||
|
virtual Error get_error() const override; ///< get last error
|
||||||
|
|
||||||
|
virtual void flush() override {}
|
||||||
|
virtual void store_8(uint8_t p_src) override; ///< store a byte
|
||||||
|
virtual void store_buffer(const uint8_t *p_src, uint64_t p_length) override; ///< store an array of bytes
|
||||||
|
|
||||||
|
virtual bool file_exists(const String &p_name) override { return false; }
|
||||||
|
|
||||||
|
uint64_t _get_modified_time(const String &p_file) override { return 0; }
|
||||||
|
virtual BitField<FileAccess::UnixPermissionFlags> _get_unix_permissions(const String &p_file) override { return 0; }
|
||||||
|
virtual Error _set_unix_permissions(const String &p_file, BitField<FileAccess::UnixPermissionFlags> p_permissions) override { return ERR_UNAVAILABLE; }
|
||||||
|
|
||||||
|
virtual bool _get_hidden_attribute(const String &p_file) override { return false; }
|
||||||
|
virtual Error _set_hidden_attribute(const String &p_file, bool p_hidden) override { return ERR_UNAVAILABLE; }
|
||||||
|
virtual bool _get_read_only_attribute(const String &p_file) override { return false; }
|
||||||
|
virtual Error _set_read_only_attribute(const String &p_file, bool p_ro) override { return ERR_UNAVAILABLE; }
|
||||||
|
|
||||||
|
virtual void close() override;
|
||||||
|
|
||||||
|
FileAccessWindowsPipe() {}
|
||||||
|
virtual ~FileAccessWindowsPipe();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // WINDOWS_ENABLED
|
||||||
|
|
||||||
|
#endif // FILE_ACCESS_WINDOWS_PIPE_H
|
|
@ -105,6 +105,10 @@ Error OS_Web::execute(const String &p_path, const List<String> &p_arguments, Str
|
||||||
return create_process(p_path, p_arguments);
|
return create_process(p_path, p_arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dictionary OS_Web::execute_with_pipe(const String &p_path, const List<String> &p_arguments) {
|
||||||
|
ERR_FAIL_V_MSG(Dictionary(), "OS::execute_with_pipe is not available on the Web platform.");
|
||||||
|
}
|
||||||
|
|
||||||
Error OS_Web::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
|
Error OS_Web::create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id, bool p_open_console) {
|
||||||
Array args;
|
Array args;
|
||||||
for (const String &E : p_arguments) {
|
for (const String &E : p_arguments) {
|
||||||
|
|
|
@ -80,6 +80,7 @@ public:
|
||||||
bool main_loop_iterate();
|
bool main_loop_iterate();
|
||||||
|
|
||||||
Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
|
Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
|
||||||
|
Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) override;
|
||||||
Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
|
Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
|
||||||
Error kill(const ProcessID &p_pid) override;
|
Error kill(const ProcessID &p_pid) override;
|
||||||
int get_process_id() const override;
|
int get_process_id() const override;
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
#include "drivers/unix/net_socket_posix.h"
|
#include "drivers/unix/net_socket_posix.h"
|
||||||
#include "drivers/windows/dir_access_windows.h"
|
#include "drivers/windows/dir_access_windows.h"
|
||||||
#include "drivers/windows/file_access_windows.h"
|
#include "drivers/windows/file_access_windows.h"
|
||||||
|
#include "drivers/windows/file_access_windows_pipe.h"
|
||||||
#include "main/main.h"
|
#include "main/main.h"
|
||||||
#include "servers/audio_server.h"
|
#include "servers/audio_server.h"
|
||||||
#include "servers/rendering/rendering_server_default.h"
|
#include "servers/rendering/rendering_server_default.h"
|
||||||
|
@ -178,6 +179,7 @@ void OS_Windows::initialize() {
|
||||||
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
|
||||||
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
|
||||||
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
|
||||||
|
FileAccess::make_default<FileAccessWindowsPipe>(FileAccess::ACCESS_PIPE);
|
||||||
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_RESOURCES);
|
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_RESOURCES);
|
||||||
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA);
|
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_USERDATA);
|
||||||
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM);
|
DirAccess::make_default<DirAccessWindows>(DirAccess::ACCESS_FILESYSTEM);
|
||||||
|
@ -727,6 +729,105 @@ Dictionary OS_Windows::get_memory_info() const {
|
||||||
return meminfo;
|
return meminfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dictionary OS_Windows::execute_with_pipe(const String &p_path, const List<String> &p_arguments) {
|
||||||
|
#define CLEAN_PIPES \
|
||||||
|
if (pipe_in[0] != 0) { \
|
||||||
|
CloseHandle(pipe_in[0]); \
|
||||||
|
} \
|
||||||
|
if (pipe_in[1] != 0) { \
|
||||||
|
CloseHandle(pipe_in[1]); \
|
||||||
|
} \
|
||||||
|
if (pipe_out[0] != 0) { \
|
||||||
|
CloseHandle(pipe_out[0]); \
|
||||||
|
} \
|
||||||
|
if (pipe_out[1] != 0) { \
|
||||||
|
CloseHandle(pipe_out[1]); \
|
||||||
|
} \
|
||||||
|
if (pipe_err[0] != 0) { \
|
||||||
|
CloseHandle(pipe_err[0]); \
|
||||||
|
} \
|
||||||
|
if (pipe_err[1] != 0) { \
|
||||||
|
CloseHandle(pipe_err[1]); \
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary ret;
|
||||||
|
|
||||||
|
String path = p_path.replace("/", "\\");
|
||||||
|
String command = _quote_command_line_argument(path);
|
||||||
|
for (const String &E : p_arguments) {
|
||||||
|
command += " " + _quote_command_line_argument(E);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create pipes.
|
||||||
|
HANDLE pipe_in[2] = { 0, 0 };
|
||||||
|
HANDLE pipe_out[2] = { 0, 0 };
|
||||||
|
HANDLE pipe_err[2] = { 0, 0 };
|
||||||
|
|
||||||
|
SECURITY_ATTRIBUTES sa;
|
||||||
|
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
|
sa.bInheritHandle = true;
|
||||||
|
sa.lpSecurityDescriptor = nullptr;
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(!CreatePipe(&pipe_in[0], &pipe_in[1], &sa, 0), ret);
|
||||||
|
if (!SetHandleInformation(pipe_in[1], HANDLE_FLAG_INHERIT, 0)) {
|
||||||
|
CLEAN_PIPES
|
||||||
|
ERR_FAIL_V(ret);
|
||||||
|
}
|
||||||
|
if (!CreatePipe(&pipe_out[0], &pipe_out[1], &sa, 0)) {
|
||||||
|
CLEAN_PIPES
|
||||||
|
ERR_FAIL_V(ret);
|
||||||
|
}
|
||||||
|
if (!SetHandleInformation(pipe_out[0], HANDLE_FLAG_INHERIT, 0)) {
|
||||||
|
CLEAN_PIPES
|
||||||
|
ERR_FAIL_V(ret);
|
||||||
|
}
|
||||||
|
if (!CreatePipe(&pipe_err[0], &pipe_err[1], &sa, 0)) {
|
||||||
|
CLEAN_PIPES
|
||||||
|
ERR_FAIL_V(ret);
|
||||||
|
}
|
||||||
|
ERR_FAIL_COND_V(!SetHandleInformation(pipe_err[0], HANDLE_FLAG_INHERIT, 0), ret);
|
||||||
|
|
||||||
|
// Create process.
|
||||||
|
ProcessInfo pi;
|
||||||
|
ZeroMemory(&pi.si, sizeof(pi.si));
|
||||||
|
pi.si.cb = sizeof(pi.si);
|
||||||
|
ZeroMemory(&pi.pi, sizeof(pi.pi));
|
||||||
|
LPSTARTUPINFOW si_w = (LPSTARTUPINFOW)&pi.si;
|
||||||
|
|
||||||
|
pi.si.dwFlags |= STARTF_USESTDHANDLES;
|
||||||
|
pi.si.hStdInput = pipe_in[0];
|
||||||
|
pi.si.hStdOutput = pipe_out[1];
|
||||||
|
pi.si.hStdError = pipe_err[1];
|
||||||
|
|
||||||
|
DWORD creation_flags = NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW;
|
||||||
|
|
||||||
|
if (!CreateProcessW(nullptr, (LPWSTR)(command.utf16().ptrw()), nullptr, nullptr, true, creation_flags, nullptr, nullptr, si_w, &pi.pi)) {
|
||||||
|
CLEAN_PIPES
|
||||||
|
ERR_FAIL_V_MSG(ret, "Could not create child process: " + command);
|
||||||
|
}
|
||||||
|
CloseHandle(pipe_in[0]);
|
||||||
|
CloseHandle(pipe_out[1]);
|
||||||
|
CloseHandle(pipe_err[1]);
|
||||||
|
|
||||||
|
ProcessID pid = pi.pi.dwProcessId;
|
||||||
|
process_map->insert(pid, pi);
|
||||||
|
|
||||||
|
Ref<FileAccessWindowsPipe> main_pipe;
|
||||||
|
main_pipe.instantiate();
|
||||||
|
main_pipe->open_existing(pipe_out[0], pipe_in[1]);
|
||||||
|
|
||||||
|
Ref<FileAccessWindowsPipe> 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;
|
||||||
|
}
|
||||||
|
|
||||||
Error OS_Windows::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) {
|
Error OS_Windows::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) {
|
||||||
String path = p_path.replace("/", "\\");
|
String path = p_path.replace("/", "\\");
|
||||||
String command = _quote_command_line_argument(path);
|
String command = _quote_command_line_argument(path);
|
||||||
|
|
|
@ -181,6 +181,7 @@ public:
|
||||||
virtual Dictionary get_memory_info() const override;
|
virtual Dictionary get_memory_info() const override;
|
||||||
|
|
||||||
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
|
virtual Error execute(const String &p_path, const List<String> &p_arguments, String *r_pipe = nullptr, int *r_exitcode = nullptr, bool read_stderr = false, Mutex *p_pipe_mutex = nullptr, bool p_open_console = false) override;
|
||||||
|
virtual Dictionary execute_with_pipe(const String &p_path, const List<String> &p_arguments) override;
|
||||||
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
|
virtual Error create_process(const String &p_path, const List<String> &p_arguments, ProcessID *r_child_id = nullptr, bool p_open_console = false) override;
|
||||||
virtual Error kill(const ProcessID &p_pid) override;
|
virtual Error kill(const ProcessID &p_pid) override;
|
||||||
virtual int get_process_id() const override;
|
virtual int get_process_id() const override;
|
||||||
|
|
Loading…
Reference in New Issue