Files
pip/libs/main/system/piprocess.cpp
peri4 1c7fc39b6c version 4.0.0_alpha
in almost all methods removed timeouts in milliseconds, replaced to PISystemTime
PITimer rewrite, remove internal impl, now only thread implementation, API similar to PIThread
PITimer API no longer pass void*
PIPeer, PIConnection improved stability on reinit and exit
PISystemTime new methods
pisd now exit without hanging
2024-07-30 14:18:02 +03:00

323 lines
8.2 KiB
C++

/*
PIP - Platform Independent Primitives
Process
Ivan Pelipenko peri4ko@yandex.ru
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "piliterals_time.h"
#include "pitime.h"
#ifndef MICRO_PIP
# include "piincludes_p.h"
# include "piprocess.h"
# ifndef WINDOWS
# include <csignal>
# include <sys/wait.h>
# endif
# ifdef MAC_OS
# include <crt_externs.h>
# endif
//! \class PIProcess piprocess.h
//! \~\details
//! \~english
//! This class able to start external executables, watch for them,
//! grab output and exit code.
//!
//! \~russian
//!
//!
//! \~english \section PIProcess_sec0 Synopsis
//! \~russian \section PIProcess_sec0 Краткий обзор
//! \~english
//! External executable can be started with control or fully independent
//! from application.\n
//! Start with control allow you to wait for finish, grab output
//! and terminate it at any time.\n
//! You can change working directory and environment of executable.\n
//!
//! \~russian
//!
//!
//! \~english \section PIProcess_sec1 Usage
//! \~russian \section PIProcess_sec1 Использование
//! \~english
//!
//!
//! \~russian
//!
PRIVATE_DEFINITION_START(PIProcess)
# ifdef WINDOWS
STARTUPINFOA si;
PROCESS_INFORMATION pi;
# else
pid_t pid;
# endif
FILE *tf_in, *tf_out, *tf_err;
PRIVATE_DEFINITION_END(PIProcess)
PIProcess::PIProcess(): PIThread() {
exit_code = -1;
# ifdef WINDOWS
PRIVATE->pi.dwProcessId = 0;
# else
PRIVATE->pid = 0;
# endif
is_exec = false;
g_in = g_out = g_err = false;
t_in = t_out = t_err = false;
PRIVATE->tf_in = PRIVATE->tf_out = PRIVATE->tf_err = 0;
env = PIProcess::currentEnvironment();
}
PIProcess::~PIProcess() {
PIThread::stopAndWait();
if (t_in) f_in.remove();
if (t_out) f_out.remove();
if (t_err) f_err.remove();
}
void PIProcess::exec_() {
is_exec = false;
startOnce();
// cout << "exec wait" << endl;
while (!is_exec)
piMinSleep();
// cout << "exec end" << endl;
}
void PIProcess::startProc(bool detached) {
// cout << "run" << endl;
PIString str;
/// arguments convertion
int as = 0;
const char * argscc[args.size() + 1];
int argsl[args.size()];
for (int i = 0; i < args.size_s(); ++i) {
argscc[i] = args[i].data();
argsl[i] = strlen(argscc[i]);
as += argsl[i] + 3;
}
argscc[args.size()] = 0;
# ifdef WINDOWS
char * a = new char[as];
memset(a, ' ', as - 1);
as = 0;
for (int i = 0; i < args.size_s(); ++i) {
str = args[i];
a[as] = '"';
memcpy(&(a[as + 1]), argscc[i], argsl[i]);
a[as + argsl[i] + 1] = '"';
as += argsl[i] + 3;
}
a[as - 1] = 0;
// piCout << a;
# endif
# ifndef WINDOWS
/// environment convertion
const char * envcc[env.size() + 1];
envcc[env.size_s()] = 0;
for (int i = 0; i < env.size_s(); ++i) {
envcc[i] = env[i].data();
}
# endif
/// files for stdin/out/err
t_in = t_out = t_err = false;
if (f_in.path().isEmpty()) {
f_in.openTemporary(PIIODevice::ReadWrite);
t_in = true;
}
if (f_out.path().isEmpty()) {
f_out.openTemporary(PIIODevice::ReadWrite);
t_out = true;
}
if (f_err.path().isEmpty()) {
f_err.openTemporary(PIIODevice::ReadWrite);
t_err = true;
}
str = args.front();
is_exec = true;
if (!detached) execStarted(str);
# ifndef WINDOWS
int pid_ = fork();
if (!detached) PRIVATE->pid = pid_;
if (pid_ == 0) {
# endif
PRIVATE->tf_in = PRIVATE->tf_out = PRIVATE->tf_err = 0;
// cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl;
// cout << f_out.path() << endl;
if (g_in) PRIVATE->tf_in = freopen(f_in.path().data(), "r", stdin);
if (g_out) PRIVATE->tf_out = freopen(f_out.path().data(), "w", stdout);
if (g_err) PRIVATE->tf_err = freopen(f_err.path().data(), "w", stderr);
# ifndef WINDOWS
if (!wd.isEmpty())
if (!chdir(wd.data())) piCoutObj << "Error while set working directory";
# endif
# ifdef WINDOWS
GetStartupInfoA(&(PRIVATE->si));
memset(&(PRIVATE->pi), 0, sizeof(PRIVATE->pi));
if (CreateProcessA(0, // No module name (use command line)
a, // Command line
0, // Process handle not inheritable
0, // Thread handle not inheritable
false, // Set handle inheritance to FALSE
detached ? DETACHED_PROCESS /*CREATE_NEW_CONSOLE*/ : 0, // Creation flags
0, // envcc, // Use environment
wd.isEmpty() ? 0 : wd.data(), // Use working directory
&(PRIVATE->si), // Pointer to STARTUPINFO structure
&(PRIVATE->pi))) // Pointer to PROCESS_INFORMATION structure
{
if (!detached) {
WaitForSingleObject(PRIVATE->pi.hProcess, INFINITE);
DWORD code = -1;
if (GetExitCodeProcess(PRIVATE->pi.hProcess, &code) != 0) exit_code = code;
}
CloseHandle(PRIVATE->pi.hThread);
CloseHandle(PRIVATE->pi.hProcess);
} else
piCoutObj << "\"CreateProcess\" error, " << errorString();
# else
// cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl;
if (execve(str.data(), (char * const *)argscc, (char * const *)envcc) < 0)
piCoutObj << "\"execve" << str << args << "\" error :" << errorString();
}
else {
piMinSleep();
// cout << "wait" << endl;
if (!detached) {
wait(&exit_code);
pid_ = 0;
if (!detached) PRIVATE->pid = pid_;
// cout << "wait done" << endl;
}
}
# endif
if (!detached) execFinished(str, exit_code);
is_exec = false;
# ifdef WINDOWS
delete[] a;
# endif
}
void PIProcess::terminate() {
# ifdef WINDOWS
if (is_exec)
if (!TerminateProcess(PRIVATE->pi.hProcess, 0)) return;
PRIVATE->pi.dwProcessId = 0;
# else
if (is_exec) kill(PRIVATE->pid, SIGKILL);
PRIVATE->pid = 0;
# endif
}
bool PIProcess::waitForFinish() {
return PIThread::waitForFinish(1_m);
}
void PIProcess::execIndependent(const PIString & program, const PIStringList & args_) {
PIProcess p;
p.args << program << args_;
p.startProc(true);
}
int PIProcess::pID() const {
# ifdef WINDOWS
return PRIVATE->pi.dwProcessId;
# else
return PRIVATE->pid;
# endif
}
int PIProcess::currentPID() {
# ifdef WINDOWS
return GetCurrentProcessId();
# else
return getpid();
# endif
}
PIStringList PIProcess::currentEnvironment() {
PIStringList l;
int i = 0;
while (environ[i] != 0) {
l << environ[i];
++i;
}
return l;
}
void PIProcess::run() {
startProc(false);
}
void PIProcess::removeEnvironmentVariable(const PIString & variable) {
PIString s;
for (int i = 0; i < env.size_s(); ++i) {
s = env[i];
if (s.left(s.find("=")).trimmed() == variable) {
env.remove(i);
--i;
}
}
}
void PIProcess::setEnvironmentVariable(const PIString & variable, const PIString & value) {
PIString s, v;
for (int i = 0; i < env.size_s(); ++i) {
s = env[i];
v = s.left(s.find("=")).trimmed();
if (v == variable) {
env[i] = v + "=" + value;
return;
}
}
env << variable + "=" + value;
}
PIString PIProcess::getEnvironmentVariable(const PIString & variable) {
PIStringList env_ = currentEnvironment();
PIString s, v;
for (int i = 0; i < env_.size_s(); ++i) {
s = env_[i];
v = s.left(s.find("=")).trimmed();
if (v == variable) {
return s.right(s.size() - 1 - s.find("=")).trimmed();
}
}
return PIString();
}
#endif // MICRO_PIP