/*************************************************************************/ /* 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 #include #include #include #include #include #include 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 cs; cs.push_back(_executable_path.utf8()); for (int i = 0; i < _arguments.size(); i++) { cs.push_back(_arguments[i].utf8()); } Vector 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