Reputation: 57
I have a simple C++ program which use boost::process:child
to receive a terminal command and execute it, and then print out the result. But then if I execute a command like ps aux
, the whole program will just no responding. I'm not sure how to fix. I've tried to debug it, and the debugger will just stop at cp.wait()
.
try{
child cp(cmd+" "+param, (std_out & std_err) > pipe);
cp.wait();
return_code = cp.exit_code();
}catch (process_error e){
return_code = -1;
}
Upvotes: 1
Views: 895
Reputation: 393507
You need to consume the pipe. What is the pipe connected to (in other words, who is consuming it?).
If, as seems to be the case, you're not interested in the output at all, simply redirect to the null device:
As a side note, I'd strongly discourage
using namespace boost::process
as it looks like you're doing. There are many names in that namespace that are very prone to clash with C/standard library names. For example, ispipe
a local variable? Is it the typeboost::process::pipe
? Is it the POSIX system library call::pipe
?
#include <boost/process.hpp>
#include <iostream>
int main()
{
namespace bp = boost::process;
std::string cmd = "cat", param = "/etc/dictionaries-common/words";
//bp::pipe pipe;
int return_code = -1;
try {
bp::child cp(cmd + " " + param, (bp::std_out & bp::std_err) > bp::null);
cp.wait();
return_code = cp.exit_code();
} catch (bp::process_error const& e) {
return_code = -1;
}
std::cout << "return_code: " << return_code << "\n";
return return_code;
}
Note that it returns 1
on Coliru because that dictionary doesn't exist. On my system it correctly returns 0
(without blocking.
Note that piecing together commands as strings can be very error-prone and invites security vulnerability (what if the filename passed in is "foot.txt; rm -rf *"?).
I'd usggest using the argument vector style:
std::string cmd = "cat";
std::vector<std::string> params{"-n", "--",
"/etc/dictionaries-common/words"};
bp::child cp(bp::search_path(cmd), params,
(bp::std_out & bp::std_err) > bp::null);
Even safer is not using
search_path
but it requires the exe to be a fully specified path (like/bin/cat
or./bin/myexe
).
Note that now it's safe enough to accept file names from external input:
std::vector<std::string> params{"-n", "--"};
params.insert(end(params), argv + 1, argv + argc);
As a finishing touch, let's close stdin for the child process (so we get no privacy leak there and also no hang if there are no files listed on the command line...):
#include <boost/process.hpp>
#include <iostream>
int main(int argc, char** argv)
{
namespace bp = boost::process;
int return_code = -1;
try {
std::string cmd = "/bin/cat";
std::vector<std::string> params{"-n", "--"};
params.insert(end(params), argv + 1, argv + argc);
bp::child cp(cmd, params, //
bp::std_in.close(), //
(bp::std_out & bp::std_err) > bp::null);
cp.wait();
return_code = cp.exit_code();
} catch (bp::process_error const& e) {
return_code = -1;
}
std::cout << "return_code: " << return_code << "\n";
return return_code;
}
Now it prints return_code: 0
because the files actually exist on Coliru.
Upvotes: 2