I am using boost.process to start another process.
I want to capture the stdout and print it myself.
The problem is, that the output is only printed in chunks or when stopping the subprocess.
The test subprocess is a python script which calls echo "test" 20 times per second.
void ExternalAppLauncher::setup()
{
boost::process::pipe stdout_p = boost::process::create_pipe();
boost::process::pipe stderr_p = boost::process::create_pipe();
{
file_descriptor_sink stdout_sink(stdout_p.sink, close_handle);
file_descriptor_sink stderr_sink(stderr_p.sink, close_handle);
file_descriptor_source stdout_source(stdout_p.source, close_handle);
file_descriptor_source stderr_source(stderr_p.source, close_handle);
out_stream.open(stdout_source);
err_stream.open(stderr_source);
childProcess.reset(new child(execute(
set_args(args),
bind_stdout(stdout_sink),
bind_stderr(stderr_sink),
inherit_env(),
// Guarantees that the child process gets killed, even when this process recieves SIGKILL(9) instead of SIGINT(2)
on_exec_setup([](executor&)
{
::prctl(PR_SET_PDEATHSIG, SIGKILL);
})
)));
}
}
// runs in another thread
void ExternalAppLauncher::run()
{
std::string s;
while (std::getline(err_stream, s))
{
std::cout << s;
}
}
This prints the output only every 10 seconds, probably because the buffer needs to be full before it is forwared?
When I dont call bind_stdout() the output appears immediately on the console.
What could solve this problem?
Thanks!
As I found out in the thread How to capture standard out and print to both the console and a file during process runtime (C++/Boost.Process) the python script I ran was the culprit.
Deactivating the buffering for the python script with export PYTHONUNBUFFERED=1 solved it.
The env var can be passed in to the application with set_env:
childProcess.reset(new child(execute(
set_args(args),
start_in_dir(workingDir),
bind_stdout(stdout_sink),
bind_stderr(stderr_sink),
inherit_env(),
set_env(std::vector<std::string> {"PYTHONUNBUFFERED=1"}))));
Related
I have to get the output of a QProcess while it is running. Therefore I have written the following Code:
CommandExecutor_C::CommandExecutor_C():
mProcessStatus(AI_UNKNOWN),
mOnTdiActiveCallback(),
mTdiProcess(new QProcess)
{
connect(mTdiProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(CheckOutput()));
connect(mTdiProcess, SIGNAL(readyReadStandardError()), this, SLOT(CheckOutput()));
}
void CommandExecutor_C::ExecuteCommand(QString &aCommand)
{
mTdiProcess->start(aCommand, QProcess::Unbuffered | QProcess::ReadWrite);
LOGINFO(FB_TDI,"Launch command: " + aCommand.toStdString());
}
void CommandExecutor_C::CheckOutput()
{
QString StdOut = QString(mTdiProcess->readAllStandardOutput());
QString StdErr = QString(mTdiProcess->readAllStandardError());
mProcessStatus = CheckTdiAutomationInterface(StdOut.toStdString(), StdErr.toStdString());
if(mProcessStatus != AI_UNKNOWN)
{
OnTdiActive(mProcessStatus);
}
}
This works fine if QProcess gets finished but in my case the Process starts an automation interface which should run in background permanently. Therefore I have used "readyReadStandardOutput" and connect it to the slot CheckOutput(). CheckOutput() is getting called just if the process has been finished. Otherwise I am waiting endless.
I have googled a lot about the problem but nothing worked. I am very sure that the output is getting buffered and does just return if the Process has finished. Therefore I have started the Process in Unbuffered-Mode. I have also tried to forward the channels of mTdiProcess. Here the Code:
void CommandExecutor_C::ExecuteCommand(QString &aCommand)
{
mTdiProcess->setProcessChannelMode(QProcess::ForwardedChannels);
mTdiProcess->start(aCommand, QProcess::Unbuffered | QProcess::ReadWrite);
LOGINFO(FB_TDI,"Launch command: " + aCommand.toStdString());
}
But nothing worked. I hope you can help me.
I am using Qt 5.4.2 if that's important.
I usually check the output in regular intervals like this:
bool returnBool = false;
while (returnBool == false)
{
/*! Wait one second if the process finishes. Then read all output to
* stdout and stderr and redo. */
returnBool = process.waitForFinished(1000);
QString outputStdOut = process.readAllStandardOutput();
QString outputStdErr = process.readAllStandardError();
}
I currently have this batch file:
#echo off
setlocal enabledelayedexpansion
set Times=0
for /f "skip=1" %%p in ('wmic cpu get loadpercentage') do (
set Cpusage!Times!=%%p
set /A Times=!Times! + 1
)
echo %Cpusage0%
And I would like to run it in a standard C++ Qt windows application and store the percentage in a variable. I can see you can run batch files using QProcess, but I'm not sure how to get the result.
Thanks in advance for any help.
There are multiple solutions to this task (standard C++, Qt, WinAPI, etc.), I will list a few of those. I have tested and verified all the listed ones (in case I did not make a mistake, they should work fine).
However all the solutions listed below requires you to change one line in the batch script you provided.
The reason for that is the last line "echo %Cpusage0%" only prints the result value to the command prompt instead of returning it.
For that reason I changed the last line of the batch file
from echo %Cpusage0%
to exit %Cpusage0%
This returns the value in the variable Cpusage0 from the command prompt.
Solutions:
In general: the batch file needs to be run through the command interpreter (cmd.exe - command prompt on Windows). In addition you need to specify the /C option to the cmd.exe if you wish to run only one command in the interpreter and then terminate it (that's true in your case).
So the command you wish to execute is:
std::string BatchFile = "..."; // Access path to the batch file.
std::string Command = "cmd.exe /C " + BatchFile;
Note: if you change the Command std::string the previously obtained c_str() pointers become invalid.
So either
don't change the std::string while the std::system() call is running,
use a local variable for storing the command (like i did in the examples),
copy the command into a C-string ([const] char*) pointer or
preferably into a smart pointer character array
(std::unique_ptr<char[]>).
In case of using C-string, don't forget to delete it.
Summary:
Standard C++ - std::system(...)
A, Waiting for std::system(...) to finish. This blocks the calling thread.
B, Running std::system(...) in a different thread with std::thread. This does not block the calling thread.
C, Running std::system(...) in a different thread with std::async. This does not block the calling thread.
Qt - QProcess
A, Waiting for QProcess to finish. This blocks the calling thread.
B, Signaling a slot with QProcess::finished(). This does not block the calling thread.
WinAPI - CreateProcess(...)
A, Waiting for CreateProcess(...) to finish. This blocks the calling thread.
B, Starting a new thread (CreateThread(...)) to wait for CreateProcess(...) to finish. This does not block the calling thread.
Alternative: I would like to mention the same thing #wOxxOm advised in a comment on the question - you can get the CPU usage directly in C++. This has been asked on StackOverflow a couple of times before, here are some examples:
How to determine CPU and memory consumption from inside a process?
Retrieving CPU Load Percent total in Windows with C++
Note: I haven't verified the "cpu usage directly from C++" answers myself, but one is heavily upvoted and the other one is accepted as an answer.
In details:
Note: These are minimalistic solutions with minimal error checking. In case of using a "multi-threaded solution" do not forget to add proper protection for the shared resources (for example using std::atomic<int> or std::mutex to protect the shared variable).
1. Standard C++ - std::system(...)
You can execute the batch file in the current thread and wait for the result by calling std::system(...) with the Command and storing the result in an int variable (the percentage value).
A, Plain blocking std::system(...) call.
This blocks the calling thread.
auto runBatchSTDSystemWaited(const std::string& BatchFile) -> int {
auto Command = std::string("cmd.exe /C " + BatchFile);
return std::system(Command.c_str());
}
You can do the same in another thread (and continue to do other things while waiting for the result) by using either std::thread or std::async(...).
B, std::thread. (std::promise, std::future)
This does not block the calling thread.
auto runBatchSTDSystemThread(const std::string& BatchFile, std::shared_ptr<std::promise<int>> Promise) -> std::future<int> {
// Note: the Promise object must exist until the last call to either the promise or the future objects.
auto Command = std::string("cmd.exe /C " + BatchFile);
auto Future = Promise->get_future();
std::thread Thread([](decltype(Command) STDSystemCommand, decltype(Promise) ResultPromise) -> void {
ResultPromise->set_value_at_thread_exit(std::system(STDSystemCommand.c_str()));
}, Command, Promise);
Thread.detach();
return Future;
// Note: You can access the CPU usage value by calling the std::future::get() function of the returned future object.
}
The following basically wraps the 1/B solution into 1 call.
C, std::async(...)
This does not block the calling thread.
auto runBatchSTDSystemAsync(const std::string& BatchFile) -> std::future<int> {
auto Command = std::string("cmd.exe /C " + BatchFile);
// std::async can be forced to create new thread with std::launch::async launch policy.
// Make sure that the Command string exists until the new thread ends (reason for string copy).
// The lambda-function is required to copy the command string to the new thread.
auto Future = std::future<int>(std::async(std::launch::async, [](decltype(Command) STDSystemCommand) -> int {
return std::system(STDSystemCommand.c_str());
}, Command));
return Future;
// Note: You can access the CPU usage value by calling the std::future::get() function of the returned future object.
}
2. Qt - QProcess
Similarly to the standard c++ solutions you can wait for the QProcess thread to finish the execution and obtain the result.
A, QProcess::waitForFinished(-1)
This blocks the calling thread.
auto runBatchQtQProcessWaited(const std::string& BatchFile) -> int {
QProcess Process;
auto Command = QString("cmd.exe");
auto Arguments = QStringList{
QString("/C"),
QString::fromStdString(BatchFile)
};
Process.start(Command, Arguments);
Process.waitForFinished(-1);
return Process.exitCode();
}
With Qt another possibility is to signal an appropriate slot function for receiving the result of the QProcess.
B, QProcess::finished()
This does not block the calling thread.
class SlotClass : public QObject {
Q_OBJECT
public:
SlotClass(std::shared_ptr<QProcess> Process);
auto getResult() const -> int;
public slots:
/*auto*/ void onPostFinishQtQProcess(int ExitCode, QProcess::ExitStatus ExitStatus) /*-> void*/;
// Seems like Qt 5.5 moc compiler fails to correctly recognize auto declared slots. (Throws error when compiling at ":".)
private:
std::shared_ptr<QProcess> Process;
int Result;
};
SlotClass::SlotClass(std::shared_ptr<QProcess> Process) :
Process(Process),
Result(-1) {}
auto SlotClass::getResult() const -> int {
return this->Result;
}
/*auto*/ void SlotClass::onPostFinishQtQProcess(int ExitCode, QProcess::ExitStatus ExitStatus) /*-> void*/ {
if (ExitStatus == QProcess::CrashExit)
throw std::runtime_error("Batch process crashed.");
this->Result = ExitCode;
}
auto runBatchQtQProcessSignaled(const std::string& BatchFile, const SlotClass& SlotObject) -> void {
auto Command = QString("cmd.exe");
auto Arguments = QStringList{
QString("/C"),
QString::fromStdString(BatchFile)
};
QObject::connect(SlotObject.getProcess().get(), SIGNAL(finished(int, QProcess::ExitStatus)),
&SlotObject, SLOT(onPostFinishQtQProcess(int, QProcess::ExitStatus)));
SlotObject.getProcess()->start(Command, Arguments);
}
3. WinAPI - CreateProcess(...)
There is a variation for the blocking wait with WinAPI as well.
A, CreateProcess(...)
This blocks the calling thread.
auto runBatchWinAPICreateProcessWaited(const std::string& BatchFile) -> int {
auto Command = "cmd.exe /C " + BatchFile;
// Creates wide string array from the narrow command string.
auto WideStringConverter = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>();
auto WideCommand = WideStringConverter.from_bytes(Command);
auto WideCommandArray = std::make_unique<wchar_t[]>(WideCommand.length() + 1);
std::wcscpy(WideCommandArray.get(), WideCommand.c_str());
// Initializes necessary structures.
STARTUPINFO BatchStartupInformation;
std::memset(&BatchStartupInformation, 0, sizeof(BatchStartupInformation));
BatchStartupInformation.cb = sizeof(BatchStartupInformation);
PROCESS_INFORMATION BatchProcessInformation;
std::memset(&BatchProcessInformation, 0, sizeof(BatchProcessInformation));
// Creates a new command prompt process with no window and executes the given batch file.
if (!CreateProcess(nullptr, WideCommandArray.get(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW,
nullptr, nullptr, &BatchStartupInformation, &BatchProcessInformation))
throw std::exception(("Could not create process for running the batch file. Error code: " + std::to_string(GetLastError())).c_str());
// Waits until the created process has already finished.
auto WaitResult = WaitForSingleObject(BatchProcessInformation.hProcess, INFINITE);
if (WAIT_FAILED == WaitResult)
throw std::runtime_error(("Waiting for batch process failed. Error code: " + std::to_string(GetLastError())).c_str());
//else if (WAIT_TIMEOUT == WaitResult)
// ; //...
auto ProcessResult = 0ul;
if (!GetExitCodeProcess(BatchProcessInformation.hProcess, &ProcessResult))
throw std::exception(("Could not retrieve process exit code after running batch file. Exit code: " + std::to_string(GetLastError())).c_str());
CloseHandle(BatchProcessInformation.hProcess);
CloseHandle(BatchProcessInformation.hThread);
return ProcessResult;
}
Or you can do the same as 3/A, but create a new thread to wait for the batch file to finish.
B, CreateThread(), CreateProcess()
This does not block the calling thread.
auto runBatchWinAPICreateProcessEvent(const std::string& BatchFile) -> void {
auto Command = "cmd.exe /C " + BatchFile;
// Creates wide string array from the narrow command string.
auto WideStringConverter = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>();
auto WideCommand = WideStringConverter.from_bytes(Command);
auto WideCommandArray = std::make_unique<wchar_t[]>(WideCommand.length() + 1);
std::wcscpy(WideCommandArray.get(), WideCommand.c_str());
// Initializes necessary structures.
STARTUPINFO BatchStartupInformation;
std::memset(&BatchStartupInformation, 0, sizeof(BatchStartupInformation));
BatchStartupInformation.cb = sizeof(BatchStartupInformation);
PROCESS_INFORMATION BatchProcessInformation;
std::memset(&BatchProcessInformation, 0, sizeof(BatchProcessInformation));
// Creates a new command prompt process with no window and executes the given batch file.
if (!CreateProcess(nullptr, WideCommandArray.get(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW,
nullptr, nullptr, &BatchStartupInformation, &BatchProcessInformation))
throw std::exception(("Could not create process for running the batch file. Error code: " + std::to_string(GetLastError())).c_str());
if (!CreateThread(nullptr, 0, &waitForWinAPICreateProcessResult, new PROCESS_INFORMATION(BatchProcessInformation), 0, nullptr))
throw std::exception(("Could not create process for retrieving the result of the batch file. Error code: " + std::to_string(GetLastError())).c_str());
}
auto WINAPI waitForWinAPICreateProcessResult(LPVOID ThreadParameter) -> DWORD {
auto BatchProcessInformation = std::unique_ptr<PROCESS_INFORMATION>(reinterpret_cast<PROCESS_INFORMATION*>(ThreadParameter));
// Waits until the created process has already finished.
auto WaitResult = WaitForSingleObject(BatchProcessInformation->hProcess, INFINITE);
if (WAIT_FAILED == WaitResult)
throw std::runtime_error(("Waiting for batch process failed. Error code: " + std::to_string(GetLastError())).c_str());
//else if (WAIT_TIMEOUT == WaitResult)
// ; //...
auto ProcessResult = 0ul;
if (!GetExitCodeProcess(BatchProcessInformation->hProcess, &ProcessResult))
throw std::exception(("Could not retrieve process exit code after running batch file. Exit code: " + std::to_string(GetLastError())).c_str());
// You have the result in the ProcessResult variable.
CloseHandle(BatchProcessInformation->hProcess);
CloseHandle(BatchProcessInformation->hThread);
return 0;
}
I am launching a command using system api (I am ok with using this api with C/C++). The command I pass may hang at times and hence I would like to kill after certain timeout.
Currently I am using it as:
system("COMMAND");
I want to use it something like this:
Run a command using a system independent API (I don't want to use CreateProcess since it is for Windows only) Kill the process if it does not exit after 'X' Minutes.
Since system() is a platform-specific call, there cannot be a platform-independent way of solving your problem. However, system() is a POSIX call, so if it is supported on any given platform, the rest of the POSIX API should be as well. So, one way to solve your problem is to use fork() and kill().
There is a complication in that system() invokes a shell, which will probably spawn other processes, and I presume you want to kill all of them, so one way to do that is to use a process group. The basic idea is use fork() to create another process, place it in its own process group, and kill that group if it doesn't exit after a certain time.
A simple example - the program forks; the child process sets its own process group to be the same as its process ID, and uses system() to spawn an endless loop. The parent process waits 10 seconds then kills the process group, using the negative value of the child process PID. This will kill the forked process and any children of that process (unless they have changed their process group.)
Since the parent process is in a different group, the kill() has no effect on it.
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <stdio.h>
int main() {
pid_t pid;
pid = fork();
if(pid == 0) { // child process
setpgid(getpid(), getpid());
system("while true ; do echo xx ; sleep 5; done");
} else { // parent process
sleep(10);
printf("Sleep returned\n");
kill(-pid, SIGKILL);
printf("killed process group %d\n", pid);
}
exit(0);
}
There is no standard, cross-platform system API. The hint is that they are system APIs! We're actually "lucky" that we get system, but we don't get anything other than that.
You could try to find some third-party abstraction.
Check below C++ thread based attempt for linux. (not tested)
#include <iostream>
#include <string>
#include <thread>
#include <stdio.h>
using namespace std;
// execute system command and get output
// http://stackoverflow.com/questions/478898/how-to-execute-a-command-and-get-output-of-command-within-c
std::string exec(const char* cmd) {
FILE* pipe = popen(cmd, "r");
if (!pipe) return "ERROR";
char buffer[128];
std::string result = "";
while(!feof(pipe)) {
if(fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
pclose(pipe);
return result;
}
void system_task(string& cmd){
exec(cmd.c_str());
}
int main(){
// system commad that takes time
string command = "find /";
// run the command in a separate thread
std::thread t1(system_task, std::ref(command));
// gives some time for the system task
std::this_thread::sleep_for(chrono::milliseconds(200));
// get the process id of the system task
string query_command = "pgrep -u $LOGNAME " + command;
string process_id = exec(query_command.c_str());
// kill system task
cout << "killing process " << process_id << "..." << endl;
string kill_command = "kill " + process_id;
exec(kill_command.c_str());
if (t1.joinable())
t1.join();
cout << "continue work on main thread" << endl;
return 0;
}
I had a similar problem, in a Qt/QML development: I want to start a bash command, while continuing to process events on the Qt Loop, and killing the bash command if it takes too long.
I came up with the following class that I'm sharing here (see below), in hope it may be of some use to people with a similar problem.
Instead of calling a 'kill' command, I call a cleanupCommand supplied by the developper. Example: if I'm to call myscript.sh and want to check that it won't last run for more than 10 seconds, I'll call it the following way:
SystemWithTimeout systemWithTimeout("myScript.sh", 10, "killall myScript.sh");
systemWithTimeout.start();
Code:
class SystemWithTimeout {
private:
bool m_childFinished = false ;
QString m_childCommand ;
int m_seconds ;
QString m_cleanupCmd ;
int m_period;
void startChild(void) {
int rc = system(m_childCommand.toUtf8().data());
if (rc != 0) SYSLOG(LOG_NOTICE, "Error SystemWithTimeout startChild: system returned %d", rc);
m_childFinished = true ;
}
public:
SystemWithTimeout(QString cmd, int seconds, QString cleanupCmd)
: m_childFinished {false}, m_childCommand {cmd}, m_seconds {seconds}, m_cleanupCmd {cleanupCmd}
{ m_period = 200; }
void setPeriod(int period) {m_period = period;}
void start(void) ;
};
void SystemWithTimeout::start(void)
{
m_childFinished = false ; // re-arm the boolean for 2nd and later calls to 'start'
qDebug()<<"systemWithTimeout"<<m_childCommand<<m_seconds;
QTime dieTime= QTime::currentTime().addSecs(m_seconds);
std::thread child(&SystemWithTimeout::startChild, this);
child.detach();
while (!m_childFinished && QTime::currentTime() < dieTime)
{
QTime then = QTime::currentTime();
QCoreApplication::processEvents(QEventLoop::AllEvents, m_period); // Process events during up to m_period ms (default: 200ms)
QTime now = QTime::currentTime();
int waitTime = m_period-(then.msecsTo(now)) ;
QThread::msleep(waitTime); // wait for the remaning of the 200 ms before looping again.
}
if (!m_childFinished)
{
SYSLOG(LOG_NOTICE, "Killing command <%s> after timeout reached (%d seconds)", m_childCommand.toUtf8().data(), m_seconds);
int rc = system(m_cleanupCmd.toUtf8().data());
if (rc != 0) SYSLOG(LOG_NOTICE, "Error SystemWithTimeout 164: system returned %d", rc);
m_childFinished = true ;
}
}
I do not know any portable way to do that in C nor C++ languages. As you ask for alternatives, I know it is possible in other languages. For example in Python, it is possible using the subprocess module.
import subprocess
cmd = subprocess.Popen("COMMAND", shell = True)
You can then test if COMMAND has ended with
if cmd.poll() is not None:
# cmd has finished
and you can kill it with :
cmd.terminate()
Even if you prefere to use C language, you should read the documentation for subprocess module because it explains that internally it uses CreateProcess on Windows and os.execvp on Posix systems to start the command, and it uses TerminateProcess on Windows and SIG_TERM on Posix to stop it.
I'm trying to create a parent and a child processes that would communicate through a pipe.
I've setup the child to listen to its parent through a pipe, with a read command running in a while loop.
In order to debug my program I print debug messages to the standard output (note that my read command is set to the pipe with a file descriptor different than 0 or 1).
From some reason these debug messages are being received in the read command of my child process. I can't understand why this is happening. What could be causing this? What elegant solution do I have to solve it (apart from writing to the standard error instead of output)?
This code causes an endless loop because of the cout message that just triggers another read. Why? Notice that the child process exists upon receiving a CHILD_EXIT_CODE signal from parent.
int myPipe[2]
pipe(myPipe);
if(fork() == 0)
{
int readPipe = myPipe[0];
while(true)
{
size_t nBytes = read(readPipe, readBuffer, sizeof(readBuffer));
std::cout << readBuffer << "\n";
int newPosition = atoi(readBuffer);
if(newPosition == CHILD_EXIT_CODE)
{
exit(0);
}
}
}
Edit: Code creating the pipe and fork
I do not know what is doing your parent process (you did not post your code), but because of your description it seems like your parent and child processes are sharing the same stdout stream (the child inherits copies of the parent's set of open file descriptors; see man fork)
I guess, what you should do is to attach stdout and stderr streams in your parent process to the write side of your pipes (you need one more pipe for the stderr stream)
This is what I would try if I were in your situation (in my opinion you are missing dup2):
pid_t pid; /*Child or parent PID.*/
int out[2], err[2]; /*Store pipes file descriptors. Write ends attached to the stdout*/
/*and stderr streams.*/
// Init value as error.
out[0] = out[1] = err[0] = err[1] = -1;
/*Creating pipes, they will be attached to the stderr and stdout streams*/
if (pipe(out) < 0 || pipe(err) < 0) {
/* Error: you should log it */
exit (EXIT_FAILURE);
}
if ((pid=fork()) == -1) {
/* Error: you should log it */
exit (EXIT_FAILURE);
}
if (pid != 0) {
/*Parent process*/
/*Attach stderr and stdout streams to your pipes (their write end)*/
if ((dup2(out[1], 1) < 0) || (dup2(err[1], 2) < 0)) {
/* Error: you should log it */
/* The child is going to be an orphan process you should kill it before calling exit.*/
exit (EXIT_FAILURE);
}
/*WHATEVER YOU DO WITH YOUR PARENT PROCESS*/
/* The child is going to be an orphan process you should kill it before calling exit.*/
exit(EXIT_SUCCESS);
}
else {
/*Child process*/
}
You should not forget a couple of things:
wait or waitpid to release associated memory to child process when it dies. wait or waitpid must be called from parent process.
If you use wait or waitpid you might have to think about blocking SIGCHLD before calling fork and in that case you should unblock SIGCHLD in your child process right after fork, at the beginning of your child process code (A child created via fork(2) inherits a copy of its parent's signal mask; see sigprocmask).
.
Something that many times is forgotten. Be aware of EINTR error. dup2, waitpid/wait, read and many others are affected by this error.
If your parent process dies before your child process you should try to kill the child process if you do not want it to become an orphan one.
Take a look at _exit. Perhaps you should use it in your child process instead of exit.
I need to run an external program from within a c++ application. I need the output from that program (i want to see it while the program is still running) and it also needs to get input.
What is the best and most elegant way to redirect the IO? Should it be running in it's own thread? Any examples?
It's running on OSX.
I implemented it like this:
ProgramHandler::ProgramHandler(std::string prog): program(prog){
// Create two pipes
std::cout << "Created Class\n";
pipe(pipe1);
pipe(pipe2);
int id = fork();
std::cout << "id: " << id << std::endl;
if (id == 0)
{
// In child
// Close current `stdin` and `stdout` file handles
close(fileno(stdin));
close(fileno(stdout));
// Duplicate pipes as new `stdin` and `stdout`
dup2(pipe1[0], fileno(stdin));
dup2(pipe2[1], fileno(stdout));
// We don't need the other ends of the pipes, so close them
close(pipe1[1]);
close(pipe2[0]);
// Run the external program
execl("/bin/ls", "bin/ls");
char buffer[30];
while (read(pipe1[0], buffer, 30)) {
std::cout << "Buf: " << buffer << std::endl;
}
}
else
{
// We don't need the read-end of the first pipe (the childs `stdin`)
// or the write-end of the second pipe (the childs `stdout`)
close(pipe1[0]);
close(pipe2[1]);
// Now you can write to `pipe1[1]` and it will end up as `stdin` in the child
// Read from `pipe2[0]` to read from the childs `stdout`
}
}
but as an output i get this:
Created Class
id: 84369
id: 0
I don't understand why it s called twice and why it wont fork the first time. What am I doing/understanding wrong.
If using a POSIX system (like OSX or Linux) then you have to learn the system calls pipe, fork, close, dup2 and exec.
What you do is create two pipes, one for reading from the external application and one for writing. Then you fork to create a new process, and in the child process you set up the pipes as stdin and stdout and then call exec which replaces the child process with an external program using your new stdin and stdout file handles. In the parent process you can not read the output from the child process, and write to its input.
In pseudo-code:
// Create two pipes
pipe(pipe1);
pipe(pipe2);
if (fork() == 0)
{
// In child
// Close current `stdin` and `stdout` file handles
close(FILENO_STDIN);
close(FILENO_STDOUT);
// Duplicate pipes as new `stdin` and `stdout`
dup2(pipe1[0], FILENO_STDIN);
dup2(pipe2[1], FILENO_STDOUT);
// We don't need the other ends of the pipes, so close them
close(pipe1[1]);
close(pipe2[0]);
// Run the external program
exec("/some/program", ...);
}
else
{
// We don't need the read-end of the first pipe (the childs `stdin`)
// or the write-end of the second pipe (the childs `stdout`)
close(pipe1[0]);
close(pipe2[1]);
// Now you can write to `pipe1[1]` and it will end up as `stdin` in the child
// Read from `pipe2[0]` to read from the childs `stdout`
}
Read the manual pages of the system calls for more information about them. You also need to add error checking as all of these system calls may fail.
Well there is a pretty standard way to do this. In general you would like to fork the process and to close the standard I/O (fd 0,1) of the child. Before forking have create two pipes, after forking close the standard input and output in the child and connect them to the pipe, using dup.
Pseudo code, shows only one side of the connection, I'm sure you can figure out the other side.
int main(){
int fd[2]; // file descriptors
pipe(fd);
// Fork child process
if (fork() == 0){
char buffer [80];
close(1);
dup(fd[1]); // this will take the first free discriptor, the one you just closed.
close(fd[1]); // clean up
}else{
close(0);
dup(fd[0]);
close(fd[0]);
}
return 0;
}
After you have the pipe set up and one of the parent threads waiting on a select or something, you can call exec for your external tool and have all the data flowing.
The basic approach to communicate with a different program on POSIX systems is to setup a pipe(), then fork() your program, close() and dup() file descriptors into the correct location, and finally to exec??() the desired executable.
Once this is done, you have your two programs connected with suitable streams. Unfortunately, this doesn't deal with any form of asynchronous processing of the two programs. That is, it is likely that you either want to access the created file descriptor with suitable asynchronous and non-blocking operations (i.e., setup the various file descriptors to be non-blocking and/or access them only when poll() yields results indicating that you can access them). If there is just that one executable it may be easier to control it from a separate thread, though.
A different approach (and if you are also writing the external program) is to use shared memory. Something along the lines of (pseudo code)
// create shared memory
int l_shmid = shmget(key, size ,0600 | IPC_CREAT);
if(l_shmid < 0)
ERROR
// attach to shared memory
dataptr* ptr = (dataptr*)shmat(l_shmid, NULL, 0600);
// run external program
pid_t l_pid = fork();
if(l_pid == (pid_t)-1)
{
ERROR
// detach & delete shared mem
shmdt(ptr);
shmctl(l_shmid,
IPC_RMID,
(shmid_ds *)NULL);
return;
}
else if(l_pid == 0)
{
// child:
execl(path,
args,
NULL);
return;
}
// wait for the external program to finish
int l_stat(0);
waitpid(l_pid, &l_stat, 0);
// read from shmem
memset(mydata, ..,..);
memcpy(mydata, ptr, ...);
// detach & close shared mem
shmdt(ptr);
shmctl(l_shmid,
IPC_RMID,
(shmid_ds *)NULL);
Your external program can write to shared memory in a similar way. No need for pipes & reading/writing/selecting etc.