piprocess #189
@@ -612,7 +612,7 @@ if(NOT PIP_FREERTOS)
|
|||||||
add_subdirectory("utils/translator")
|
add_subdirectory("utils/translator")
|
||||||
add_subdirectory("utils/value_tree_translator")
|
add_subdirectory("utils/value_tree_translator")
|
||||||
if(PIP_UTILS AND (NOT CROSSTOOLS))
|
if(PIP_UTILS AND (NOT CROSSTOOLS))
|
||||||
add_subdirectory("utils/system_test")
|
add_subdirectory("utils/system_calib")
|
||||||
add_subdirectory("utils/udp_file_transfer")
|
add_subdirectory("utils/udp_file_transfer")
|
||||||
if(sodium_FOUND)
|
if(sodium_FOUND)
|
||||||
add_subdirectory("utils/system_daemon")
|
add_subdirectory("utils/system_daemon")
|
||||||
|
|||||||
@@ -17,16 +17,17 @@
|
|||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "piliterals_time.h"
|
|
||||||
#include "pitime.h"
|
#include "pitime.h"
|
||||||
#include "pitranslator.h"
|
|
||||||
#ifndef MICRO_PIP
|
#ifndef MICRO_PIP
|
||||||
|
|
||||||
# include "piincludes_p.h"
|
# include "piincludes_p.h"
|
||||||
|
# include "piliterals_bytes.h"
|
||||||
# include "piprocess.h"
|
# include "piprocess.h"
|
||||||
|
# include "pitranslator.h"
|
||||||
# ifndef WINDOWS
|
# ifndef WINDOWS
|
||||||
# include <csignal>
|
# include <csignal>
|
||||||
# include <sys/wait.h>
|
# include <sys/wait.h>
|
||||||
|
# include <fcntl.h>
|
||||||
# endif
|
# endif
|
||||||
# ifdef MAC_OS
|
# ifdef MAC_OS
|
||||||
# include <crt_externs.h>
|
# include <crt_externs.h>
|
||||||
@@ -63,14 +64,189 @@
|
|||||||
//!
|
//!
|
||||||
|
|
||||||
|
|
||||||
PRIVATE_DEFINITION_START(PIProcess)
|
namespace {
|
||||||
|
enum PipeDirection {
|
||||||
|
PipeRead,
|
||||||
|
PipeWrite,
|
||||||
|
PipeLast = PipeWrite
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StdFile {
|
||||||
|
StdIn,
|
||||||
|
StdOut,
|
||||||
|
StdErr,
|
||||||
|
StdLast = StdErr
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr int PipesDirections = PipeLast + 1;
|
||||||
|
constexpr int StdFileCount = StdLast + 1;
|
||||||
|
|
||||||
|
# ifdef WINDOWS
|
||||||
|
using PipeHandleType = HANDLE;
|
||||||
|
using SizeType = DWORD;
|
||||||
|
# else
|
||||||
|
using SizeType = ssize_t;
|
||||||
|
using PipeHandleType = int;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifdef WINDOWS
|
||||||
|
PIString convertWindowsCmd(PIStringList sl) {
|
||||||
|
if (sl.isNotEmpty()) {
|
||||||
|
sl[0].replaceAll('/', '\\');
|
||||||
|
sl[0].quote();
|
||||||
|
}
|
||||||
|
return sl.join(' ');
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
char * const * convertToCharArrays(const PIStringList & sl) {
|
||||||
|
char ** cc = new char *[sl.size() + 1];
|
||||||
|
for (int i = 0; i < sl.size_s(); ++i) {
|
||||||
|
cc[i] = const_cast<char *>(sl[i].data());
|
||||||
|
}
|
||||||
|
cc[sl.size()] = 0;
|
||||||
|
return cc;
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
PRIVATE_DEFINITION_START(PIProcess)
|
||||||
|
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
STARTUPINFOA si;
|
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
# else
|
# else
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
# endif
|
# endif
|
||||||
FILE *tf_in, *tf_out, *tf_err;
|
PipeHandleType pipes[StdFileCount][PipesDirections];
|
||||||
|
bool grab[StdFileCount];
|
||||||
|
|
||||||
|
|
||||||
|
void forEachPipe(std::function<void(PipeHandleType &)> func) {
|
||||||
|
for (int i = 0; i < StdFileCount; ++i) {
|
||||||
|
for (int j = 0; j < PipesDirections; ++j) {
|
||||||
|
func(pipes[i][j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void initGrab() {
|
||||||
|
for (int i = 0; i < StdFileCount; ++i) {
|
||||||
|
grab[i] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool createPipe(StdFile pipe_type) {
|
||||||
|
const int pt = pipe_type;
|
||||||
|
# ifdef WINDOWS
|
||||||
|
SECURITY_ATTRIBUTES saAttr;
|
||||||
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||||
|
saAttr.bInheritHandle = TRUE;
|
||||||
|
saAttr.lpSecurityDescriptor = NULL;
|
||||||
|
if (!CreatePipe(&(pipes[pt][PipeRead]), &(pipes[pt][PipeWrite]), &saAttr, 0)) return false;
|
||||||
|
return true;
|
||||||
|
# else
|
||||||
|
int ret = pipe(pipes[pt]);
|
||||||
|
return ret != -1;
|
||||||
|
# endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool createPipes() {
|
||||||
|
for (int i = 0; i < StdFileCount; ++i) {
|
||||||
|
if (grab[i]) {
|
||||||
|
if (!createPipe(static_cast<StdFile>(i))) {
|
||||||
|
piCout << "CreatePipe failed";
|
||||||
|
# ifdef WINDOWS
|
||||||
|
piCout << GetLastError();
|
||||||
|
# endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
# ifdef WINDOWS
|
||||||
|
if (grab[StdIn]) SetHandleInformation(pipes[StdIn][PipeWrite], HANDLE_FLAG_INHERIT, 0);
|
||||||
|
if (grab[StdOut]) SetHandleInformation(pipes[StdOut][PipeRead], HANDLE_FLAG_INHERIT, 0);
|
||||||
|
if (grab[StdErr]) SetHandleInformation(pipes[StdErr][PipeRead], HANDLE_FLAG_INHERIT, 0);
|
||||||
|
# endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void closePipe(StdFile pipe_type, PipeDirection direction) {
|
||||||
|
closePipe(pipes[pipe_type][direction]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void closePipe(PipeHandleType & hpipe) {
|
||||||
|
# ifdef WINDOWS
|
||||||
|
if (hpipe != 0) {
|
||||||
|
CloseHandle(hpipe);
|
||||||
|
hpipe = 0;
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
if (hpipe != -1) {
|
||||||
|
::close(hpipe);
|
||||||
|
hpipe = -1;
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void closeAllPipes() {
|
||||||
|
forEachPipe([this](PipeHandleType & hpipe) { closePipe(hpipe); });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PIByteArray readPipe(StdFile pipe_type) {
|
||||||
|
if (!grab[pipe_type]) return {};
|
||||||
|
constexpr size_t read_buffer_size = 4_KiB;
|
||||||
|
PIByteArray read_buffer;
|
||||||
|
read_buffer.resize(read_buffer_size);
|
||||||
|
SizeType bytes_read = 0;
|
||||||
|
size_t offset = 0;
|
||||||
|
while (1) {
|
||||||
|
# ifdef WINDOWS
|
||||||
|
DWORD available = 0;
|
||||||
|
bytes_read = 0;
|
||||||
|
PeekNamedPipe(pipes[pipe_type][PipeRead], nullptr, 0, nullptr, &available, nullptr);
|
||||||
|
if (available > 0) {
|
||||||
|
BOOL ok = ReadFile(pipes[pipe_type][PipeRead],
|
||||||
|
read_buffer.data(offset),
|
||||||
|
piMini(available, read_buffer.size() - offset),
|
||||||
|
&bytes_read,
|
||||||
|
nullptr);
|
||||||
|
if (!ok) bytes_read = 0;
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
bytes_read = ::read(pipes[pipe_type][PipeRead], read_buffer.data(offset), read_buffer.size() - offset);
|
||||||
|
# endif
|
||||||
|
if (bytes_read > 0) {
|
||||||
|
offset += bytes_read;
|
||||||
|
read_buffer.resize(offset + read_buffer_size);
|
||||||
|
} else {
|
||||||
|
read_buffer.resize(offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return read_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool writePipe(const PIByteArray & data) {
|
||||||
|
SizeType sz = 0;
|
||||||
|
# ifdef WINDOWS
|
||||||
|
BOOL ok = WriteFile(pipes[StdIn][PipeWrite], data.data(), data.size(), &sz, NULL);
|
||||||
|
if (!ok) sz = 0;
|
||||||
|
# else
|
||||||
|
sz = ::write(pipes[StdIn][PipeWrite], data.data(), data.size());
|
||||||
|
# endif
|
||||||
|
return sz == data.size_s();
|
||||||
|
}
|
||||||
|
|
||||||
PRIVATE_DEFINITION_END(PIProcess)
|
PRIVATE_DEFINITION_END(PIProcess)
|
||||||
|
|
||||||
|
|
||||||
@@ -80,177 +256,129 @@ PIProcess::PIProcess(): PIThread() {
|
|||||||
PRIVATE->pi.dwProcessId = 0;
|
PRIVATE->pi.dwProcessId = 0;
|
||||||
# else
|
# else
|
||||||
PRIVATE->pid = 0;
|
PRIVATE->pid = 0;
|
||||||
|
PRIVATE->forEachPipe([](PipeHandleType & pipe) { pipe = -1; });
|
||||||
# endif
|
# endif
|
||||||
is_exec = false;
|
exec_start = false;
|
||||||
g_in = g_out = g_err = false;
|
exec_finished = false;
|
||||||
t_in = t_out = t_err = false;
|
PRIVATE->initGrab();
|
||||||
PRIVATE->tf_in = PRIVATE->tf_out = PRIVATE->tf_err = 0;
|
env = PIProcess::currentEnvironment();
|
||||||
env = PIProcess::currentEnvironment();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PIProcess::~PIProcess() {
|
PIProcess::~PIProcess() {
|
||||||
PIThread::stopAndWait();
|
PIThread::stopAndWait();
|
||||||
if (t_in) f_in.remove();
|
PRIVATE->closeAllPipes();
|
||||||
if (t_out) f_out.remove();
|
|
||||||
if (t_err) f_err.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIProcess::exec_() {
|
void PIProcess::exec_() {
|
||||||
is_exec = false;
|
exec_finished = false;
|
||||||
|
exec_start = false;
|
||||||
|
PRIVATE->closeAllPipes();
|
||||||
startOnce();
|
startOnce();
|
||||||
// cout << "exec wait" << endl;
|
|
||||||
while (!is_exec)
|
|
||||||
piMinSleep();
|
|
||||||
// cout << "exec end" << endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIProcess::startProc(bool detached) {
|
void PIProcess::startProc(bool detached) {
|
||||||
// cout << "run" << endl;
|
const PIString & str = args.front();
|
||||||
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);
|
if (!detached) execStarted(str);
|
||||||
# ifndef WINDOWS
|
if (!PRIVATE->createPipes()) return;
|
||||||
int pid_ = fork();
|
# ifdef WINDOWS
|
||||||
|
STARTUPINFOA si;
|
||||||
|
piZeroMemory(si);
|
||||||
|
si.cb = sizeof(STARTUPINFOA);
|
||||||
|
if (PRIVATE->grab[StdIn]) si.hStdInput = PRIVATE->pipes[StdIn][PipeRead];
|
||||||
|
if (PRIVATE->grab[StdOut]) si.hStdOutput = PRIVATE->pipes[StdOut][PipeWrite];
|
||||||
|
if (PRIVATE->grab[StdErr]) si.hStdError = PRIVATE->pipes[StdErr][PipeWrite];
|
||||||
|
si.dwFlags |= STARTF_USESTDHANDLES;
|
||||||
|
const auto cmd = convertWindowsCmd(args);
|
||||||
|
if (CreateProcessA(0, // No module name (use command line)
|
||||||
|
(LPSTR)cmd.data(), // Command line
|
||||||
|
0, // Process handle not inheritable
|
||||||
|
0, // Thread handle not inheritable
|
||||||
|
true, // Set handle inheritance to FALSE
|
||||||
|
detached ? DETACHED_PROCESS /*CREATE_NEW_CONSOLE*/ : 0, // Creation flags
|
||||||
|
0, // Use environment
|
||||||
|
wd.isEmpty() ? 0 : wd.data(), // Use working directory
|
||||||
|
&si, // Pointer to STARTUPINFO structure
|
||||||
|
&(PRIVATE->pi))) // Pointer to PROCESS_INFORMATION structure
|
||||||
|
{
|
||||||
|
exec_start = true;
|
||||||
|
if (!detached) {
|
||||||
|
WaitForSingleObject(PRIVATE->pi.hProcess, INFINITE);
|
||||||
|
DWORD code = -1;
|
||||||
|
if (GetExitCodeProcess(PRIVATE->pi.hProcess, &code) != 0) exit_code = code;
|
||||||
|
exec_finished = true;
|
||||||
|
}
|
||||||
|
CloseHandle(PRIVATE->pi.hThread);
|
||||||
|
CloseHandle(PRIVATE->pi.hProcess);
|
||||||
|
} else {
|
||||||
|
piCoutObj << "\"CreateProcess\" error: %1"_tr("PIProcess").arg(errorString());
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
auto largs = convertToCharArrays(args);
|
||||||
|
auto lenv = convertToCharArrays(env);
|
||||||
|
int pid_ = fork();
|
||||||
if (!detached) PRIVATE->pid = pid_;
|
if (!detached) PRIVATE->pid = pid_;
|
||||||
if (pid_ == 0) {
|
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);
|
|
||||||
|
|
||||||
# ifdef WINDOWS
|
|
||||||
GetStartupInfoA(&(PRIVATE->si));
|
|
||||||
piZeroMemory(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: %1"_tr("PIProcess").arg(errorString());
|
|
||||||
}
|
|
||||||
# endif
|
|
||||||
# ifndef WINDOWS
|
|
||||||
if (!wd.isEmpty()) {
|
if (!wd.isEmpty()) {
|
||||||
if (!chdir(wd.data())) piCoutObj << "Error while set working directory";
|
if (!chdir(wd.data())) piCoutObj << "Error while set working directory";
|
||||||
}
|
}
|
||||||
// cout << "exec " << tf_in << ", " << tf_out << ", " << tf_err << endl;
|
PRIVATE->closePipe(StdIn, PipeWrite);
|
||||||
if (execve(str.data(), (char * const *)argscc, (char * const *)envcc) < 0) {
|
PRIVATE->closePipe(StdOut, PipeRead);
|
||||||
piCoutObj << "\"execve" << str << args << "\" error :" << errorString();
|
PRIVATE->closePipe(StdErr, PipeRead);
|
||||||
}
|
|
||||||
|
if (PRIVATE->grab[StdIn]) dup2(PRIVATE->pipes[StdIn][PipeRead], STDIN_FILENO);
|
||||||
|
if (PRIVATE->grab[StdOut]) dup2(PRIVATE->pipes[StdOut][PipeWrite], STDOUT_FILENO);
|
||||||
|
if (PRIVATE->grab[StdErr]) dup2(PRIVATE->pipes[StdErr][PipeWrite], STDERR_FILENO);
|
||||||
|
|
||||||
|
PRIVATE->closePipe(StdIn, PipeRead);
|
||||||
|
PRIVATE->closePipe(StdOut, PipeWrite);
|
||||||
|
PRIVATE->closePipe(StdErr, PipeWrite);
|
||||||
|
|
||||||
|
execve(str.data(), largs, lenv);
|
||||||
|
// normaly execve can't return, if it returns - error occured
|
||||||
|
piCoutObj << "\"execve" << str << args << "\" error :" << errorString();
|
||||||
|
exit(127);
|
||||||
} else {
|
} else {
|
||||||
piMinSleep();
|
PRIVATE->closePipe(StdIn, PipeRead);
|
||||||
// cout << "wait" << endl;
|
PRIVATE->closePipe(StdOut, PipeWrite);
|
||||||
|
PRIVATE->closePipe(StdErr, PipeWrite);
|
||||||
|
|
||||||
|
if (PRIVATE->grab[StdOut]) fcntl(PRIVATE->pipes[StdOut][PipeRead], F_SETFL, O_NONBLOCK);
|
||||||
|
if (PRIVATE->grab[StdErr]) fcntl(PRIVATE->pipes[StdErr][PipeRead], F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
|
exec_start = true;
|
||||||
if (!detached) {
|
if (!detached) {
|
||||||
waitpid(pid_, &exit_code, 0);
|
waitpid(pid_, &exit_code, 0);
|
||||||
pid_ = 0;
|
if (WIFEXITED(exit_code)) {
|
||||||
|
exec_finished = WEXITSTATUS(exit_code) != 127;
|
||||||
|
}
|
||||||
|
pid_ = 0;
|
||||||
if (!detached) PRIVATE->pid = pid_;
|
if (!detached) PRIVATE->pid = pid_;
|
||||||
// cout << "wait done" << endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
delete[] largs;
|
||||||
|
delete[] lenv;
|
||||||
# endif
|
# endif
|
||||||
if (!detached) execFinished(str, exit_code);
|
if (!detached) execFinished(str, exit_code);
|
||||||
is_exec = false;
|
exec_start = false;
|
||||||
# ifdef WINDOWS
|
|
||||||
delete[] a;
|
|
||||||
# endif
|
|
||||||
}
|
|
||||||
|
|
||||||
PIByteArray PIProcess::readFile(PIFile & f, bool clear)
|
|
||||||
{
|
|
||||||
f.open(PIIODevice::ReadOnly);
|
|
||||||
const auto ret = f.readAll();
|
|
||||||
if (clear) {
|
|
||||||
f.clear();
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void PIProcess::terminate() {
|
void PIProcess::terminate() {
|
||||||
# ifdef WINDOWS
|
# ifdef WINDOWS
|
||||||
if (is_exec)
|
if (exec_start) {
|
||||||
if (!TerminateProcess(PRIVATE->pi.hProcess, 0)) return;
|
if (!TerminateProcess(PRIVATE->pi.hProcess, 0)) return;
|
||||||
|
}
|
||||||
PRIVATE->pi.dwProcessId = 0;
|
PRIVATE->pi.dwProcessId = 0;
|
||||||
# else
|
# else
|
||||||
if (is_exec) kill(PRIVATE->pid, SIGKILL);
|
if (exec_start) kill(PRIVATE->pid, SIGKILL);
|
||||||
PRIVATE->pid = 0;
|
PRIVATE->pid = 0;
|
||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool PIProcess::waitForFinish() {
|
|
||||||
return PIThread::waitForFinish(1_m);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void PIProcess::execIndependent(const PIString & program, const PIStringList & args_) {
|
void PIProcess::execIndependent(const PIString & program, const PIStringList & args_) {
|
||||||
PIProcess p;
|
PIProcess p;
|
||||||
p.args << program << args_;
|
p.args << program << args_;
|
||||||
@@ -266,12 +394,45 @@ int PIProcess::pID() const {
|
|||||||
# endif
|
# endif
|
||||||
}
|
}
|
||||||
|
|
||||||
PIByteArray PIProcess::readOutput(bool clear) {
|
|
||||||
return readFile(f_out, clear);
|
PIByteArray PIProcess::readOutput() {
|
||||||
|
return PRIVATE->readPipe(StdOut);
|
||||||
}
|
}
|
||||||
|
|
||||||
PIByteArray PIProcess::readError(bool clear) {
|
|
||||||
return readFile(f_err, clear);
|
PIByteArray PIProcess::readError() {
|
||||||
|
return PRIVATE->readPipe(StdErr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool PIProcess::writeInput(const PIByteArray & data) {
|
||||||
|
if (PRIVATE->grab[StdIn]) return PRIVATE->writePipe(data);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIProcess::closeInput() {
|
||||||
|
if (PRIVATE->grab[StdIn]) {
|
||||||
|
# ifdef WINDOWS
|
||||||
|
PRIVATE->writePipe({0x1A});
|
||||||
|
# endif
|
||||||
|
PRIVATE->closePipe(StdIn, PipeWrite);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIProcess::enableWriteStdIn(bool on) {
|
||||||
|
PRIVATE->grab[StdIn] = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIProcess::enableReadStdOut(bool on) {
|
||||||
|
PRIVATE->grab[StdOut] = on;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void PIProcess::enableReadStdErr(bool on) {
|
||||||
|
PRIVATE->grab[StdErr] = on;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,18 +9,18 @@
|
|||||||
Process
|
Process
|
||||||
Ivan Pelipenko peri4ko@yandex.ru
|
Ivan Pelipenko peri4ko@yandex.ru
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
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
|
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
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
(at your option) any later version.
|
(at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
GNU Lesser General Public License for more details.
|
GNU Lesser General Public License for more details.
|
||||||
|
|
||||||
You should have received a copy of the GNU Lesser General Public License
|
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/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef PIPROCESS_H
|
#ifndef PIPROCESS_H
|
||||||
@@ -28,14 +28,45 @@
|
|||||||
|
|
||||||
#ifndef MICRO_PIP
|
#ifndef MICRO_PIP
|
||||||
|
|
||||||
# include "pifile.h"
|
|
||||||
# include "pithread.h"
|
# include "pithread.h"
|
||||||
|
|
||||||
|
//! \class PIProcess
|
||||||
//! \ingroup System
|
//! \ingroup System
|
||||||
//! \~\brief
|
//! \~english
|
||||||
//! \~english External process.
|
//! \brief Class for managing external processes
|
||||||
//! \~russian Внешний процесс.
|
//! \details
|
||||||
|
//! The PIProcess class provides functionality to create, control and interact with external processes.
|
||||||
|
//! It allows both attached execution (with full control over input/output streams) and detached execution.
|
||||||
|
//!
|
||||||
|
//! Key features:
|
||||||
|
//! - Start processes with arguments and environment variables
|
||||||
|
//! - Monitor process state (running/finished)
|
||||||
|
//! - Read from stdout/stderr streams
|
||||||
|
//! - Write to stdin stream
|
||||||
|
//! - Set custom working directory
|
||||||
|
//! - Modify environment variables
|
||||||
|
//! - Wait for process completion
|
||||||
|
//! - Terminate processes
|
||||||
|
//! - Retrieve exit codes and process IDs
|
||||||
|
//!
|
||||||
|
//! This class inherits from PIThread and provides event-based notifications for process lifecycle events.
|
||||||
|
//! \~russian
|
||||||
|
//! \brief Класс для управления внешними процессами
|
||||||
|
//! \details
|
||||||
|
//! Класс PIProcess предоставляет функциональность для создания, управления и взаимодействия с внешними процессами.
|
||||||
|
//! Поддерживает как присоединенное выполнение (с полным контролем потоков ввода/вывода), так и независимое выполнение.
|
||||||
|
//!
|
||||||
|
//! Основные возможности:
|
||||||
|
//! - Запуск процессов с аргументами и переменными окружения
|
||||||
|
//! - Мониторинг состояния процесса (запущен/завершен)
|
||||||
|
//! - Чтение из потоков stdout/stderr
|
||||||
|
//! - Запись в поток stdin
|
||||||
|
//! - Установка рабочей директории
|
||||||
|
//! - Изменение переменных окружения
|
||||||
|
//! - Ожидание завершения процесса
|
||||||
|
//! - Завершение процессов
|
||||||
|
//! - Получение кодов завершения и идентификаторов процессов
|
||||||
|
//!
|
||||||
class PIP_EXPORT PIProcess: public PIThread {
|
class PIP_EXPORT PIProcess: public PIThread {
|
||||||
PIOBJECT_SUBCLASS(PIProcess, PIThread);
|
PIOBJECT_SUBCLASS(PIProcess, PIThread);
|
||||||
|
|
||||||
@@ -55,74 +86,64 @@ public:
|
|||||||
//! \~russian Возвращает ID процесса текущего выполнения
|
//! \~russian Возвращает ID процесса текущего выполнения
|
||||||
int pID() const;
|
int pID() const;
|
||||||
|
|
||||||
void setGrabInput(bool yes) { g_in = yes; }
|
|
||||||
|
|
||||||
//! \~english Set attached execution grab output stream enabled
|
|
||||||
//! \~russian
|
|
||||||
void setGrabOutput(bool yes) { g_out = yes; }
|
|
||||||
|
|
||||||
//! \~english Set attached execution grab error stream enabled
|
|
||||||
//! \~russian
|
|
||||||
void setGrabError(bool yes) { g_err = yes; }
|
|
||||||
|
|
||||||
void setInputFile(const PIString & path) { f_in.setPath(path); }
|
|
||||||
|
|
||||||
//! \~english Set attached execution grab output stream file
|
|
||||||
//! \~russian
|
|
||||||
void setOutputFile(const PIString & path) { f_out.setPath(path); }
|
|
||||||
|
|
||||||
//! \~english Set attached execution grab error stream file
|
|
||||||
//! \~russian
|
|
||||||
void setErrorFile(const PIString & path) { f_err.setPath(path); }
|
|
||||||
|
|
||||||
void unsetInputFile() { f_in.setPath(""); }
|
|
||||||
|
|
||||||
//! \~english Reset attached execution grab output stream file
|
|
||||||
//! \~russian
|
|
||||||
void unsetOutputFile() { f_out.setPath(""); }
|
|
||||||
|
|
||||||
//! \~english Reset attached execution grab error stream file
|
|
||||||
//! \~russian
|
|
||||||
void unsetErrorFile() { f_err.setPath(""); }
|
|
||||||
|
|
||||||
//! \~english Returns current attached execution working directory or empty string if it wasn`t set
|
//! \~english Returns current attached execution working directory or empty string if it wasn`t set
|
||||||
//! \~russian
|
//! \~russian Возвращает рабочую директорию выполнения или пустую строку, если не установлена
|
||||||
PIString workingDirectory() const { return wd; }
|
PIString workingDirectory() const { return wd; }
|
||||||
|
|
||||||
//! \~english Set attached execution working directory
|
//! \~english Set attached execution working directory
|
||||||
//! \~russian
|
//! \~russian Устанавливает рабочую директорию для выполнения
|
||||||
void setWorkingDirectory(const PIString & path) { wd = path; }
|
void setWorkingDirectory(const PIString & path) { wd = path; }
|
||||||
|
|
||||||
//! \~english Rseet attached execution working directory, application working dir will be used
|
//! \~english Rseet attached execution working directory, application working dir will be used
|
||||||
//! \~russian
|
//! \~russian Сбрасывает рабочую директорию, будет использоваться директория приложения
|
||||||
void resetWorkingDirectory() { wd.clear(); }
|
void resetWorkingDirectory() { wd.clear(); }
|
||||||
|
|
||||||
//! \~english Returns all attached execution output stream
|
//! \~english Returns all attached execution output stream
|
||||||
//! \~russian
|
//! \~russian Возвращает весь вывод из стандартного потока вывода (stdout)
|
||||||
PIByteArray readOutput(bool clear = false);
|
PIByteArray readOutput();
|
||||||
|
|
||||||
//! \~english Returns all attached execution error stream
|
//! \~english Returns all attached execution error stream
|
||||||
//! \~russian
|
//! \~russian Возвращает весь вывод из потока ошибок (stderr)
|
||||||
PIByteArray readError(bool clear = false);
|
PIByteArray readError();
|
||||||
|
|
||||||
|
//! \~english Write data to attached execution input stream
|
||||||
|
//! \~russian Записывает данные в стандартный поток ввода (stdin)
|
||||||
|
bool writeInput(const PIByteArray & data);
|
||||||
|
|
||||||
|
//! \~english Close attached execution input stream and send EOF
|
||||||
|
//! \~russian Закрывает поток ввода (stdin) и отправляет EOF
|
||||||
|
void closeInput();
|
||||||
|
|
||||||
|
//! \~english Enable or disable writing to process stdin
|
||||||
|
//! \~russian Включает или отключает запись в стандартный поток ввода (stdin) процесса
|
||||||
|
void enableWriteStdIn(bool on = true);
|
||||||
|
|
||||||
|
//! \~english Enable or disable reading from process stdout
|
||||||
|
//! \~russian Включает или отключает чтение из стандартного потока вывода (stdout) процесса
|
||||||
|
void enableReadStdOut(bool on = true);
|
||||||
|
|
||||||
|
//! \~english Enable or disable reading from process stderr
|
||||||
|
//! \~russian Включает или отключает чтение из потока ошибок (stderr) процесса
|
||||||
|
void enableReadStdErr(bool on = true);
|
||||||
|
|
||||||
//! \~english Returns current attached execution environment
|
//! \~english Returns current attached execution environment
|
||||||
//! \~russian
|
//! \~russian Возвращает текущее окружение выполнения
|
||||||
PIStringList environment() { return env; }
|
PIStringList environment() { return env; }
|
||||||
|
|
||||||
//! \~english Clear current attached execution environment. Call before \a exec()
|
//! \~english Clear current attached execution environment. Call before \a exec()
|
||||||
//! \~russian
|
//! \~russian Очищает окружение выполнения. Вызывать перед \a exec()
|
||||||
void clearEnvironment() { env.clear(); }
|
void clearEnvironment() { env.clear(); }
|
||||||
|
|
||||||
//! \~english Remove variable "variable" from current attached execution environment. Call before \a exec()
|
//! \~english Remove variable "variable" from current attached execution environment. Call before \a exec()
|
||||||
//! \~russian
|
//! \~russian Удаляет переменную "variable" из окружения выполнения. Вызывать перед \a exec()
|
||||||
void removeEnvironmentVariable(const PIString & variable);
|
void removeEnvironmentVariable(const PIString & variable);
|
||||||
|
|
||||||
//! \~english Set variable "variable" to "value" in current attached execution environment. Call before \a exec()
|
//! \~english Set variable "variable" to "value" in current attached execution environment. Call before \a exec()
|
||||||
//! \~russian
|
//! \~russian Устанавливает значение "value" для переменной "variable" в окружении выполнения. Вызывать перед \a exec()
|
||||||
void setEnvironmentVariable(const PIString & variable, const PIString & value);
|
void setEnvironmentVariable(const PIString & variable, const PIString & value);
|
||||||
|
|
||||||
//! \~english Start attached execution "program" with one argument "arg"
|
//! \~english Start attached execution "program" with one argument "arg"
|
||||||
//! \~russian
|
//! \~russian Запускает выполнение "program" с одним аргументом "arg"
|
||||||
void exec(const PIString & program, const PIString & arg) {
|
void exec(const PIString & program, const PIString & arg) {
|
||||||
args.clear();
|
args.clear();
|
||||||
args << program << arg;
|
args << program << arg;
|
||||||
@@ -140,36 +161,43 @@ public:
|
|||||||
exec_();
|
exec_();
|
||||||
}
|
}
|
||||||
EVENT_HANDLER(void, terminate);
|
EVENT_HANDLER(void, terminate);
|
||||||
EVENT_HANDLER(bool, waitForFinish);
|
EVENT_HANDLER(bool, waitForFinish) { return PIThread::waitForFinish(); }
|
||||||
EVENT_HANDLER1(bool, waitForFinish, PISystemTime, timeout) { return PIThread::waitForFinish(timeout); }
|
EVENT_HANDLER1(bool, waitForFinish, PISystemTime, timeout) { return PIThread::waitForFinish(timeout); }
|
||||||
|
|
||||||
EVENT1(execStarted, PIString, program);
|
EVENT1(execStarted, PIString, program);
|
||||||
EVENT2(execFinished, PIString, program, int, exit_code);
|
EVENT2(execFinished, PIString, program, int, exit_code);
|
||||||
|
|
||||||
|
//! \~english Check if attached execution has finished
|
||||||
|
//! \~russian Проверяет, завершилось ли выполнение процесса
|
||||||
|
bool isExecFinished() const { return exec_finished; }
|
||||||
|
|
||||||
|
//! \~english Check if attached execution has started
|
||||||
|
//! \~russian Проверяет, запущен ли процесс выполнения
|
||||||
|
bool isExecStarted() const { return exec_start; }
|
||||||
|
|
||||||
//! \~english Start detached execution "program" without arguments
|
//! \~english Start detached execution "program" without arguments
|
||||||
//! \~russian
|
//! \~russian Запускает независимое выполнение "program" без аргументов
|
||||||
static void execIndependent(const PIString & program) { execIndependent(program, PIStringList()); }
|
static void execIndependent(const PIString & program) { execIndependent(program, PIStringList()); }
|
||||||
|
|
||||||
//! \~english Start detached execution "program" with one argument "arg"
|
//! \~english Start detached execution "program" with one argument "arg"
|
||||||
//! \~russian
|
//! \~russian Запускает независимое выполнение "program" с одним аргументом "arg"
|
||||||
static void execIndependent(const PIString & program, const PIString & arg) { execIndependent(program, PIStringList() << arg); }
|
static void execIndependent(const PIString & program, const PIString & arg) { execIndependent(program, PIStringList() << arg); }
|
||||||
|
|
||||||
//! \~english Start detached execution "program" with arguments "args"
|
//! \~english Start detached execution "program" with arguments "args"
|
||||||
//! \~russian
|
//! \~russian Запускает независимое выполнение "program" с аргументами "args"
|
||||||
static void execIndependent(const PIString & program, const PIStringList & args);
|
static void execIndependent(const PIString & program, const PIStringList & args);
|
||||||
|
|
||||||
|
|
||||||
//! \~english Returns application environment
|
//! \~english Returns application environment
|
||||||
//! \~russian
|
//! \~russian Возвращает окружение текущего приложения
|
||||||
static PIStringList currentEnvironment();
|
static PIStringList currentEnvironment();
|
||||||
|
|
||||||
//! \~english Returns application process ID
|
//! \~english Returns application process ID
|
||||||
//! \~russian
|
//! \~russian Возвращает ID процесса текущего приложения
|
||||||
static int currentPID();
|
static int currentPID();
|
||||||
|
|
||||||
//! \~english Returns variable "variable" value from application environment
|
//! \~english Returns variable "variable" value from application environment
|
||||||
//! \~russian
|
//! \~russian Возвращает значение переменной "variable" из окружения приложения
|
||||||
static PIString getEnvironmentVariable(const PIString & variable);
|
static PIString getEnvironmentVariable(const PIString & variable);
|
||||||
|
|
||||||
//! \handlers
|
//! \handlers
|
||||||
@@ -178,27 +206,27 @@ public:
|
|||||||
//! \fn void exec(const PIString & program)
|
//! \fn void exec(const PIString & program)
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Start attached execution "program" without arguments
|
//! \~english Start attached execution "program" without arguments
|
||||||
//! \~russian
|
//! \~russian Запускает выполнение "program" без аргументов
|
||||||
|
|
||||||
//! \fn void exec(const PIString & program, const PIStringList & args)
|
//! \fn void exec(const PIString & program, const PIStringList & args)
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Start attached execution "program" with arguments "args"
|
//! \~english Start attached execution "program" with arguments "args"
|
||||||
//! \~russian
|
//! \~russian Запускает выполнение "program" с аргументами "args"
|
||||||
|
|
||||||
//! \fn void terminate()
|
//! \fn void terminate()
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Immediately terminate attached execution
|
//! \~english Immediately terminate attached execution
|
||||||
//! \~russian
|
//! \~russian Немедленно завершает выполнение
|
||||||
|
|
||||||
//! \fn bool waitForFinish()
|
//! \fn bool waitForFinish()
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Wait for attached execution finish maximum for 60 seconds
|
//! \~english Wait for attached execution finish maximum for 60 seconds
|
||||||
//! \~russian
|
//! \~russian Ожидает завершения выполнения (максимум 60 секунд)
|
||||||
|
|
||||||
//! \fn bool waitForFinish(PISystemTime timeout)
|
//! \fn bool waitForFinish(PISystemTime timeout)
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Wait for attached execution finish maximum for "timeout_"
|
//! \~english Wait for attached execution finish maximum for "timeout_"
|
||||||
//! \~russian
|
//! \~russian Ожидает завершения выполнения в течение "timeout_"
|
||||||
|
|
||||||
//! \}
|
//! \}
|
||||||
//! \events
|
//! \events
|
||||||
@@ -207,12 +235,12 @@ public:
|
|||||||
//! \fn void execStarted(PIString program)
|
//! \fn void execStarted(PIString program)
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Raise on attached execution start
|
//! \~english Raise on attached execution start
|
||||||
//! \~russian
|
//! \~russian Генерируется при запуске выполнения
|
||||||
|
|
||||||
//! \fn void execFinished(PIString program)
|
//! \fn void execFinished(PIString program)
|
||||||
//! \brief
|
//! \brief
|
||||||
//! \~english Raise on attached execution finish
|
//! \~english Raise on attached execution finish
|
||||||
//! \~russian
|
//! \~russian Генерируется при завершении выполнения
|
||||||
|
|
||||||
//! \}
|
//! \}
|
||||||
|
|
||||||
@@ -220,16 +248,14 @@ private:
|
|||||||
void run() override;
|
void run() override;
|
||||||
void exec_();
|
void exec_();
|
||||||
void startProc(bool detached);
|
void startProc(bool detached);
|
||||||
PIByteArray readFile(PIFile & f, bool clear);
|
|
||||||
|
|
||||||
|
private:
|
||||||
PRIVATE_DECLARATION(PIP_EXPORT)
|
PRIVATE_DECLARATION(PIP_EXPORT)
|
||||||
PIStringList args, env;
|
PIStringList args, env;
|
||||||
PIString wd;
|
PIString wd;
|
||||||
PIByteArray out;
|
|
||||||
PIFile f_in, f_out, f_err;
|
|
||||||
bool g_in, g_out, g_err, t_in, t_out, t_err;
|
|
||||||
int exit_code;
|
int exit_code;
|
||||||
bool is_exec;
|
std::atomic_bool exec_start;
|
||||||
|
std::atomic_bool exec_finished;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MICRO_PIP
|
#endif // MICRO_PIP
|
||||||
|
|||||||
@@ -36,3 +36,4 @@ pip_test(core)
|
|||||||
pip_test(piobject)
|
pip_test(piobject)
|
||||||
pip_test(client_server pip_client_server)
|
pip_test(client_server pip_client_server)
|
||||||
pip_test(io)
|
pip_test(io)
|
||||||
|
pip_test(system)
|
||||||
|
|||||||
98
tests/system/process_test.cpp
Normal file
98
tests/system/process_test.cpp
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
#include "piprocess.h"
|
||||||
|
#include "pitime.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
|
||||||
|
class ProcessTest: public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
PIProcess launcher;
|
||||||
|
const PIString command =
|
||||||
|
#ifdef _WIN32
|
||||||
|
"C:/Windows/System32/cmd.exe";
|
||||||
|
#else
|
||||||
|
"/bin/sh";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
launcher.enableWriteStdIn(false);
|
||||||
|
launcher.enableReadStdOut();
|
||||||
|
launcher.enableReadStdErr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
if (launcher.isRunning()) {
|
||||||
|
launcher.waitForFinish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(ProcessTest, Output) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
const PIStringList args = {"/c", "echo Hello from stdout && echo Hello from stderr 1>&2"};
|
||||||
|
#else
|
||||||
|
const PIStringList args = {"-c", "echo Hello from stdout; echo Hello from stderr 1>&2"};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
launcher.exec(command, args);
|
||||||
|
ASSERT_TRUE(launcher.isRunning());
|
||||||
|
|
||||||
|
ASSERT_TRUE(launcher.waitForFinish());
|
||||||
|
ASSERT_TRUE(launcher.isExecFinished());
|
||||||
|
|
||||||
|
const auto out = PIString::fromAscii(launcher.readOutput());
|
||||||
|
const auto err = PIString::fromAscii(launcher.readError());
|
||||||
|
|
||||||
|
EXPECT_TRUE(out.contains("Hello from stdout"));
|
||||||
|
EXPECT_TRUE(err.contains("Hello from stderr"));
|
||||||
|
|
||||||
|
const int exit_code = launcher.exitCode();
|
||||||
|
EXPECT_FALSE(launcher.isRunning());
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
EXPECT_EQ(exit_code, 0);
|
||||||
|
#else
|
||||||
|
EXPECT_TRUE(WIFEXITED(exit_code));
|
||||||
|
EXPECT_EQ(WEXITSTATUS(exit_code), 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(ProcessTest, Input) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
const PIStringList args = {"/c", "more"};
|
||||||
|
#else
|
||||||
|
const PIStringList args = {"-c", "read input; echo $input"};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
launcher.enableWriteStdIn();
|
||||||
|
launcher.exec(command, args);
|
||||||
|
ASSERT_TRUE(launcher.isRunning());
|
||||||
|
piMSleep(100);
|
||||||
|
EXPECT_TRUE(launcher.isExecStarted());
|
||||||
|
EXPECT_TRUE(!launcher.isExecFinished());
|
||||||
|
|
||||||
|
PIString test_input = "Test input string\n";
|
||||||
|
EXPECT_TRUE(launcher.writeInput(test_input.toAscii()));
|
||||||
|
launcher.closeInput();
|
||||||
|
|
||||||
|
ASSERT_TRUE(launcher.waitForFinish());
|
||||||
|
EXPECT_TRUE(launcher.isExecFinished());
|
||||||
|
|
||||||
|
const auto out = PIString::fromAscii(launcher.readOutput());
|
||||||
|
EXPECT_TRUE(out.contains("Test input string"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_F(ProcessTest, NonexistentCommand) {
|
||||||
|
const PIString command = {"nonexistent_command_12345"};
|
||||||
|
|
||||||
|
launcher.enableWriteStdIn(false);
|
||||||
|
launcher.enableReadStdOut(false);
|
||||||
|
launcher.enableReadStdErr(false);
|
||||||
|
launcher.exec(command);
|
||||||
|
ASSERT_TRUE(launcher.isRunning());
|
||||||
|
ASSERT_TRUE(launcher.waitForFinish());
|
||||||
|
EXPECT_FALSE(launcher.isExecFinished());
|
||||||
|
}
|
||||||
7
utils/system_calib/CMakeLists.txt
Executable file
7
utils/system_calib/CMakeLists.txt
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
list(APPEND PIP_UTILS_LIST "pip_system_calib")
|
||||||
|
set(PIP_UTILS_LIST ${PIP_UTILS_LIST} PARENT_SCOPE)
|
||||||
|
add_executable(pip_system_calib "main.cpp")
|
||||||
|
target_link_libraries(pip_system_calib pip)
|
||||||
|
if (DEFINED LIB)
|
||||||
|
install(TARGETS pip_system_calib DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||||
|
endif ()
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
list(APPEND PIP_UTILS_LIST "pip_system_test")
|
|
||||||
set(PIP_UTILS_LIST ${PIP_UTILS_LIST} PARENT_SCOPE)
|
|
||||||
add_executable(pip_system_test "main.cpp")
|
|
||||||
target_link_libraries(pip_system_test pip)
|
|
||||||
if (DEFINED LIB)
|
|
||||||
install(TARGETS pip_system_test DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
|
||||||
endif ()
|
|
||||||
Reference in New Issue
Block a user