pandemonium_engine/drivers/unix/sub_process_unix.cpp

262 lines
6.8 KiB
C++

/*************************************************************************/
/* sub_process_unix.cpp */
/*************************************************************************/
/* This file is part of: */
/* PANDEMONIUM ENGINE */
/* https://github.com/Relintai/pandemonium_engine */
/*************************************************************************/
/* Copyright (c) 2022-present Péter Magyar. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
/* Copyright (c) 2007-2022 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 "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>
#include <unistd.h>
Error SubProcessUnix::start() {
#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 (_executable_path.empty()) {
return ERR_FILE_BAD_PATH;
}
if (is_process_running()) {
return ERR_BUSY;
}
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() {
#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) {
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;
}
return OK;
#endif
}
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 {
#ifdef __EMSCRIPTEN__
// Don't compile this code at all to avoid undefined references.
// Actual virtual call goes to OS_JavaScript.
ERR_FAIL_V(false);
#else
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;
#endif
}
SubProcessUnix::SubProcessUnix() :
SubProcess() {
_process_fp = NULL;
}
SubProcessUnix::~SubProcessUnix() {
stop();
}
#endif //posix_enabled