fix a bug of defunct process by adding a SIGCHLD handler.

This commit is contained in:
yytdfc 2017-08-04 11:14:34 +08:00
parent a23c1033df
commit 814be9e635

View File

@ -42,6 +42,7 @@ Documentation for C++ subprocessing libraray.
#include <cassert>
#include <cstring>
#include <cstdio>
#include <csignal>
#include <future>
#include <vector>
#include <sstream>
@ -53,7 +54,7 @@ extern "C" {
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/wait.h>
#include <signal.h>
}
@ -67,7 +68,7 @@ extern "C" {
* library: OSError and CalledProcessError
*
* 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.
*
* 3. Util namespace
@ -89,11 +90,11 @@ static const size_t SP_MAX_ERR_BUF_SIZ = 1024;
// Default buffer capcity for OutBuffer and ErrBuffer.
// If the data exceeds this capacity, the buffer size is grown
// 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
* 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.
* [in] deleims : Delimiter characters based upon which the string needs
* to be split. Default constructed to ' '(space) and '\t'(tab)
@ -258,7 +259,7 @@ namespace util
/*!
* 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.
* Parameters:
* [in] fd : The file descriptor from which it needs to read.
@ -355,7 +356,7 @@ namespace util
int status = 0;
int ret = -1;
while (1) {
ret = waitpid(pid, &status, WNOHANG);
ret = waitpid(pid, &status, WNOHANG);
if (ret == -1) break;
if (ret == 0) continue;
return std::make_pair(ret, status);
@ -379,7 +380,7 @@ namespace util
* streams of the child process.
* Default value is 0.
*/
struct bufsize {
struct bufsize {
bufsize(int siz): bufsiz(siz) {}
int bufsiz = 0;
};
@ -389,7 +390,7 @@ struct bufsize {
* till `Popen::start_process` API is called.
* Default value is false.
*/
struct defer_spawn {
struct defer_spawn {
defer_spawn(bool d): defer(d) {}
bool defer = false;
};
@ -403,7 +404,7 @@ struct defer_spawn {
*
* Default value is false.
*/
struct close_fds {
struct close_fds {
close_fds(bool c): close_all(c) {}
bool close_all = false;
};
@ -438,7 +439,7 @@ struct string_arg
/*!
* Option to specify the executable name seperately
* 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.
*
* Eg: executable{"ls"}
@ -480,7 +481,7 @@ struct environment
/*!
* Used for redirecting input/output/error
*/
enum IOTYPE {
enum IOTYPE {
STDOUT = 1,
STDERR,
PIPE,
@ -568,7 +569,7 @@ struct error
error(FILE* fp):error(fileno(fp)) { assert(fp); }
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);
wr_ch_ = fd;
}
@ -593,7 +594,7 @@ struct error
// ATTN: Can be used only to execute functions with no
// arguments and returning void.
// 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.
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
* or in check_ouput arguments.
* 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.
*/
struct ArgumentDeducer
@ -755,7 +756,7 @@ public:
void execute_child();
private:
// Lets call it parent even though
// Lets call it parent even though
// technically a bit incorrect
Popen* parent_ = nullptr;
int err_wr_pipe_ = -1;
@ -803,7 +804,7 @@ private:
* This is a helper class to Popen.
* It takes care of management of all the file descriptors
* and file pointers.
* It dispatches of the communication aspects to the
* It dispatches of the communication aspects to the
* Communication class.
* Read through the data members to understand about the
* various file descriptors used.
@ -935,7 +936,7 @@ public:
friend class detail::Child;
template <typename... Args>
Popen(const std::string& cmd_args, Args&& ...args):
Popen(const std::string& cmd_args, Args&& ...args):
args_(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); }
int send(const char* msg, size_t length)
int send(const char* msg, size_t length)
{ return stream_.send(msg, length); }
int send(const std::vector<char>& msg)
@ -1150,6 +1151,11 @@ void Popen::execute_process() throw (CalledProcessError, OSError)
}
exe_name_ = vargs_[0];
std::signal(SIGCHLD, [](int sig){
int status;
waitpid(-1, &status, WNOHANG);
});
child_pid_ = fork();
if (child_pid_ < 0) {
@ -1170,8 +1176,8 @@ void Popen::execute_process() throw (CalledProcessError, OSError)
detail::Child chld(this, err_wr_pipe);
chld.execute_child();
}
else
}
else
{
int sys_ret = -1;
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,};
int read_bytes = util::read_atmost_n(
err_rd_pipe,
err_buf,
err_rd_pipe,
err_buf,
SP_MAX_ERR_BUF_SIZ);
close(err_rd_pipe);
@ -1301,13 +1307,13 @@ namespace detail {
_dup2_(stream.err_write_, 2); // Error stream
// 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_);
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_);
if (stream.err_write_ != -1 && stream.err_write_ > 2)
if (stream.err_write_ != -1 && stream.err_write_ > 2)
close(stream.err_write_);
// Close all the inherited fd's except the error write pipe
@ -1444,7 +1450,7 @@ namespace detail {
int rbytes = util::read_atmost_n(
fileno(stream_->error()),
ebuf.buf.data(),
ebuf.buf.data(),
ebuf.buf.size());
if (rbytes == -1) {
@ -1538,12 +1544,12 @@ namespace detail
{
return Popen(std::forward<F>(farg), std::forward<Args>(args)...).wait();
}
void pipeline_impl(std::vector<Popen>& cmds) { /* EMPTY IMPL */ }
template<typename... Args>
void pipeline_impl(std::vector<Popen>& cmds,
const std::string& cmd,
void pipeline_impl(std::vector<Popen>& cmds,
const std::string& cmd,
Args&&... args)
{
if (cmds.size() == 0) {
@ -1565,7 +1571,7 @@ namespace detail
/*!
* 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.
*/
template<typename... Args>