mirror of
https://github.com/arun11299/cpp-subprocess.git
synced 2025-08-06 05:16:24 -04:00
fix a bug of defunct process by adding a SIGCHLD handler.
This commit is contained in:
parent
a23c1033df
commit
814be9e635
@ -42,6 +42,7 @@ Documentation for C++ subprocessing libraray.
|
|||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <csignal>
|
||||||
#include <future>
|
#include <future>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -53,7 +54,7 @@ extern "C" {
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ extern "C" {
|
|||||||
* library: OSError and CalledProcessError
|
* library: OSError and CalledProcessError
|
||||||
*
|
*
|
||||||
* 2. Popen Class
|
* 2. Popen Class
|
||||||
* This is the main class the users will deal with. It
|
* This is the main class the users will deal with. It
|
||||||
* provides with all the API's to deal with processes.
|
* provides with all the API's to deal with processes.
|
||||||
*
|
*
|
||||||
* 3. Util namespace
|
* 3. Util namespace
|
||||||
@ -89,11 +90,11 @@ static const size_t SP_MAX_ERR_BUF_SIZ = 1024;
|
|||||||
// Default buffer capcity for OutBuffer and ErrBuffer.
|
// Default buffer capcity for OutBuffer and ErrBuffer.
|
||||||
// If the data exceeds this capacity, the buffer size is grown
|
// If the data exceeds this capacity, the buffer size is grown
|
||||||
// by 1.5 times its previous capacity
|
// by 1.5 times its previous capacity
|
||||||
static const size_t DEFAULT_BUF_CAP_BYTES = 8192;
|
static const size_t DEFAULT_BUF_CAP_BYTES = 8192;
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------
|
/*-----------------------------------------------
|
||||||
* EXCEPTION CLASSES
|
* EXCEPTION CLASSES
|
||||||
*-----------------------------------------------
|
*-----------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -139,7 +140,7 @@ namespace util
|
|||||||
/*!
|
/*!
|
||||||
* Function: split
|
* Function: split
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* [in] str : Input string which needs to be split based upon the
|
* [in] str : Input string which needs to be split based upon the
|
||||||
* delimiters provided.
|
* delimiters provided.
|
||||||
* [in] deleims : Delimiter characters based upon which the string needs
|
* [in] deleims : Delimiter characters based upon which the string needs
|
||||||
* to be split. Default constructed to ' '(space) and '\t'(tab)
|
* to be split. Default constructed to ' '(space) and '\t'(tab)
|
||||||
@ -258,7 +259,7 @@ namespace util
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Function: read_atmost_n
|
* Function: read_atmost_n
|
||||||
* Reads at the most `read_upto` bytes from the
|
* Reads at the most `read_upto` bytes from the
|
||||||
* file descriptor `fd` before returning.
|
* file descriptor `fd` before returning.
|
||||||
* Parameters:
|
* Parameters:
|
||||||
* [in] fd : The file descriptor from which it needs to read.
|
* [in] fd : The file descriptor from which it needs to read.
|
||||||
@ -355,7 +356,7 @@ namespace util
|
|||||||
int status = 0;
|
int status = 0;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
while (1) {
|
while (1) {
|
||||||
ret = waitpid(pid, &status, WNOHANG);
|
ret = waitpid(pid, &status, WNOHANG);
|
||||||
if (ret == -1) break;
|
if (ret == -1) break;
|
||||||
if (ret == 0) continue;
|
if (ret == 0) continue;
|
||||||
return std::make_pair(ret, status);
|
return std::make_pair(ret, status);
|
||||||
@ -379,7 +380,7 @@ namespace util
|
|||||||
* streams of the child process.
|
* streams of the child process.
|
||||||
* Default value is 0.
|
* Default value is 0.
|
||||||
*/
|
*/
|
||||||
struct bufsize {
|
struct bufsize {
|
||||||
bufsize(int siz): bufsiz(siz) {}
|
bufsize(int siz): bufsiz(siz) {}
|
||||||
int bufsiz = 0;
|
int bufsiz = 0;
|
||||||
};
|
};
|
||||||
@ -389,7 +390,7 @@ struct bufsize {
|
|||||||
* till `Popen::start_process` API is called.
|
* till `Popen::start_process` API is called.
|
||||||
* Default value is false.
|
* Default value is false.
|
||||||
*/
|
*/
|
||||||
struct defer_spawn {
|
struct defer_spawn {
|
||||||
defer_spawn(bool d): defer(d) {}
|
defer_spawn(bool d): defer(d) {}
|
||||||
bool defer = false;
|
bool defer = false;
|
||||||
};
|
};
|
||||||
@ -403,7 +404,7 @@ struct defer_spawn {
|
|||||||
*
|
*
|
||||||
* Default value is false.
|
* Default value is false.
|
||||||
*/
|
*/
|
||||||
struct close_fds {
|
struct close_fds {
|
||||||
close_fds(bool c): close_all(c) {}
|
close_fds(bool c): close_all(c) {}
|
||||||
bool close_all = false;
|
bool close_all = false;
|
||||||
};
|
};
|
||||||
@ -438,7 +439,7 @@ struct string_arg
|
|||||||
/*!
|
/*!
|
||||||
* Option to specify the executable name seperately
|
* Option to specify the executable name seperately
|
||||||
* from the args sequence.
|
* from the args sequence.
|
||||||
* In this case the cmd args must only contain the
|
* In this case the cmd args must only contain the
|
||||||
* options required for this executable.
|
* options required for this executable.
|
||||||
*
|
*
|
||||||
* Eg: executable{"ls"}
|
* Eg: executable{"ls"}
|
||||||
@ -480,7 +481,7 @@ struct environment
|
|||||||
/*!
|
/*!
|
||||||
* Used for redirecting input/output/error
|
* Used for redirecting input/output/error
|
||||||
*/
|
*/
|
||||||
enum IOTYPE {
|
enum IOTYPE {
|
||||||
STDOUT = 1,
|
STDOUT = 1,
|
||||||
STDERR,
|
STDERR,
|
||||||
PIPE,
|
PIPE,
|
||||||
@ -568,7 +569,7 @@ struct error
|
|||||||
error(FILE* fp):error(fileno(fp)) { assert(fp); }
|
error(FILE* fp):error(fileno(fp)) { assert(fp); }
|
||||||
|
|
||||||
error(const char* filename) {
|
error(const char* filename) {
|
||||||
int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
|
int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
|
||||||
if (fd == -1) throw OSError("File not found: ", errno);
|
if (fd == -1) throw OSError("File not found: ", errno);
|
||||||
wr_ch_ = fd;
|
wr_ch_ = fd;
|
||||||
}
|
}
|
||||||
@ -593,7 +594,7 @@ struct error
|
|||||||
// ATTN: Can be used only to execute functions with no
|
// ATTN: Can be used only to execute functions with no
|
||||||
// arguments and returning void.
|
// arguments and returning void.
|
||||||
// Could have used more efficient methods, ofcourse, but
|
// Could have used more efficient methods, ofcourse, but
|
||||||
// that wont yield me the consistent syntax which I am
|
// that wont yield me the consistent syntax which I am
|
||||||
// aiming for. If you know, then please do let me know.
|
// aiming for. If you know, then please do let me know.
|
||||||
|
|
||||||
class preexec_func
|
class preexec_func
|
||||||
@ -711,11 +712,11 @@ struct has_type<F, param_pack<H,T...>> {
|
|||||||
//----
|
//----
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* A helper class to Popen class for setting
|
* A helper class to Popen class for setting
|
||||||
* options as provided in the Popen constructor
|
* options as provided in the Popen constructor
|
||||||
* or in check_ouput arguments.
|
* or in check_ouput arguments.
|
||||||
* This design allows us to _not_ have any fixed position
|
* This design allows us to _not_ have any fixed position
|
||||||
* to any arguments and specify them in a way similar to what
|
* to any arguments and specify them in a way similar to what
|
||||||
* can be done in python.
|
* can be done in python.
|
||||||
*/
|
*/
|
||||||
struct ArgumentDeducer
|
struct ArgumentDeducer
|
||||||
@ -755,7 +756,7 @@ public:
|
|||||||
void execute_child();
|
void execute_child();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Lets call it parent even though
|
// Lets call it parent even though
|
||||||
// technically a bit incorrect
|
// technically a bit incorrect
|
||||||
Popen* parent_ = nullptr;
|
Popen* parent_ = nullptr;
|
||||||
int err_wr_pipe_ = -1;
|
int err_wr_pipe_ = -1;
|
||||||
@ -803,7 +804,7 @@ private:
|
|||||||
* This is a helper class to Popen.
|
* This is a helper class to Popen.
|
||||||
* It takes care of management of all the file descriptors
|
* It takes care of management of all the file descriptors
|
||||||
* and file pointers.
|
* and file pointers.
|
||||||
* It dispatches of the communication aspects to the
|
* It dispatches of the communication aspects to the
|
||||||
* Communication class.
|
* Communication class.
|
||||||
* Read through the data members to understand about the
|
* Read through the data members to understand about the
|
||||||
* various file descriptors used.
|
* various file descriptors used.
|
||||||
@ -935,7 +936,7 @@ public:
|
|||||||
friend class detail::Child;
|
friend class detail::Child;
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
Popen(const std::string& cmd_args, Args&& ...args):
|
Popen(const std::string& cmd_args, Args&& ...args):
|
||||||
args_(cmd_args)
|
args_(cmd_args)
|
||||||
{
|
{
|
||||||
vargs_ = util::split(cmd_args);
|
vargs_ = util::split(cmd_args);
|
||||||
@ -977,7 +978,7 @@ public:
|
|||||||
|
|
||||||
void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
|
void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
|
||||||
|
|
||||||
int send(const char* msg, size_t length)
|
int send(const char* msg, size_t length)
|
||||||
{ return stream_.send(msg, length); }
|
{ return stream_.send(msg, length); }
|
||||||
|
|
||||||
int send(const std::vector<char>& msg)
|
int send(const std::vector<char>& msg)
|
||||||
@ -1150,6 +1151,11 @@ void Popen::execute_process() throw (CalledProcessError, OSError)
|
|||||||
}
|
}
|
||||||
exe_name_ = vargs_[0];
|
exe_name_ = vargs_[0];
|
||||||
|
|
||||||
|
std::signal(SIGCHLD, [](int sig){
|
||||||
|
int status;
|
||||||
|
waitpid(-1, &status, WNOHANG);
|
||||||
|
});
|
||||||
|
|
||||||
child_pid_ = fork();
|
child_pid_ = fork();
|
||||||
|
|
||||||
if (child_pid_ < 0) {
|
if (child_pid_ < 0) {
|
||||||
@ -1170,8 +1176,8 @@ void Popen::execute_process() throw (CalledProcessError, OSError)
|
|||||||
|
|
||||||
detail::Child chld(this, err_wr_pipe);
|
detail::Child chld(this, err_wr_pipe);
|
||||||
chld.execute_child();
|
chld.execute_child();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int sys_ret = -1;
|
int sys_ret = -1;
|
||||||
close (err_wr_pipe);// close child side of pipe, else get stuck in read below
|
close (err_wr_pipe);// close child side of pipe, else get stuck in read below
|
||||||
@ -1182,8 +1188,8 @@ void Popen::execute_process() throw (CalledProcessError, OSError)
|
|||||||
char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
|
char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
|
||||||
|
|
||||||
int read_bytes = util::read_atmost_n(
|
int read_bytes = util::read_atmost_n(
|
||||||
err_rd_pipe,
|
err_rd_pipe,
|
||||||
err_buf,
|
err_buf,
|
||||||
SP_MAX_ERR_BUF_SIZ);
|
SP_MAX_ERR_BUF_SIZ);
|
||||||
close(err_rd_pipe);
|
close(err_rd_pipe);
|
||||||
|
|
||||||
@ -1301,13 +1307,13 @@ namespace detail {
|
|||||||
_dup2_(stream.err_write_, 2); // Error stream
|
_dup2_(stream.err_write_, 2); // Error stream
|
||||||
|
|
||||||
// Close the duped descriptors
|
// Close the duped descriptors
|
||||||
if (stream.read_from_parent_ != -1 && stream.read_from_parent_ > 2)
|
if (stream.read_from_parent_ != -1 && stream.read_from_parent_ > 2)
|
||||||
close(stream.read_from_parent_);
|
close(stream.read_from_parent_);
|
||||||
|
|
||||||
if (stream.write_to_parent_ != -1 && stream.write_to_parent_ > 2)
|
if (stream.write_to_parent_ != -1 && stream.write_to_parent_ > 2)
|
||||||
close(stream.write_to_parent_);
|
close(stream.write_to_parent_);
|
||||||
|
|
||||||
if (stream.err_write_ != -1 && stream.err_write_ > 2)
|
if (stream.err_write_ != -1 && stream.err_write_ > 2)
|
||||||
close(stream.err_write_);
|
close(stream.err_write_);
|
||||||
|
|
||||||
// Close all the inherited fd's except the error write pipe
|
// Close all the inherited fd's except the error write pipe
|
||||||
@ -1444,7 +1450,7 @@ namespace detail {
|
|||||||
|
|
||||||
int rbytes = util::read_atmost_n(
|
int rbytes = util::read_atmost_n(
|
||||||
fileno(stream_->error()),
|
fileno(stream_->error()),
|
||||||
ebuf.buf.data(),
|
ebuf.buf.data(),
|
||||||
ebuf.buf.size());
|
ebuf.buf.size());
|
||||||
|
|
||||||
if (rbytes == -1) {
|
if (rbytes == -1) {
|
||||||
@ -1538,12 +1544,12 @@ namespace detail
|
|||||||
{
|
{
|
||||||
return Popen(std::forward<F>(farg), std::forward<Args>(args)...).wait();
|
return Popen(std::forward<F>(farg), std::forward<Args>(args)...).wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
void pipeline_impl(std::vector<Popen>& cmds) { /* EMPTY IMPL */ }
|
void pipeline_impl(std::vector<Popen>& cmds) { /* EMPTY IMPL */ }
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void pipeline_impl(std::vector<Popen>& cmds,
|
void pipeline_impl(std::vector<Popen>& cmds,
|
||||||
const std::string& cmd,
|
const std::string& cmd,
|
||||||
Args&&... args)
|
Args&&... args)
|
||||||
{
|
{
|
||||||
if (cmds.size() == 0) {
|
if (cmds.size() == 0) {
|
||||||
@ -1565,7 +1571,7 @@ namespace detail
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Run the command with arguments and wait for it to complete.
|
* Run the command with arguments and wait for it to complete.
|
||||||
* The parameters passed to the argument are exactly the same
|
* The parameters passed to the argument are exactly the same
|
||||||
* one would use for Popen constructors.
|
* one would use for Popen constructors.
|
||||||
*/
|
*/
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user