<commit_before>#include <x0/process.hpp>
#include <boost/asio.hpp>
#include <string>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>

namespace x0 {

/** invokes \p cmd until its not early aborted with EINTR. */
#define EINTR_LOOP(rv, cmd) 				\
	do {									\
		rv = cmd;							\
	} while (rv == -1 && errno == EINTR)

process::process(boost::asio::io_service& io) :
	input_(io), output_(io), error_(io), pid_(-1), status_(0)
{
}

process::process(boost::asio::io_service& io, const std::string& exe, const params& args, const environment& env, const std::string& workdir) :
	input_(io), output_(io), error_(io), pid_(-1), status_(0)
{
	start(exe, args, env, workdir);
}

process::~process()
{
	// XXX kill child?
	fetch_status();
}

void process::start(const std::string& exe, const params& args, const environment& env, const std::string& workdir)
{
	//::fprintf(stderr, "proc[%d] start(exe=%s, args=[...], workdir=%s)\n", getpid(), exe.c_str(), workdir.c_str());
	switch (pid_ = fork())
	{
		case 0: // child
			setup_child(exe, args, env, workdir);
			break;
		case -1: // error
			throw boost::system::system_error(boost::system::error_code(errno, boost::system::system_category));
		default: // parent
			setup_parent();
			break;
	}
}

void process::terminate()
{
	::kill(pid_, SIGTERM);
}

bool process::expired()
{
	if (pid_ <= 0)
		return true;

	switch (fetch_status())
	{
		case 0:
			// we could fetch a status, meaning, program is still running
			return false;
		case -1:
			// oops? waitpid error?
			return false;
		default:
			// waidpid returned value > 0, meaning, program has returned already(?)
			return true;
	}
}

int process::fetch_status()
{
	int rv;

	do rv = ::waitpid(pid_, &status_, WNOHANG);
	while (rv == -1 && (errno == EINTR || errno == ECHILD));

	return rv;
}

void process::setup_parent()
{
	// setup I/O
	input_.remote().close();
	output_.remote().close();
	error_.remote().close();
}

void process::setup_child(const std::string& _exe, const params& _args, const environment& _env, const std::string& _workdir)
{
	// restore signal handler(s)
	::signal(SIGPIPE, SIG_DFL);

	// setup environment
	int k = 0;
	std::vector<char *> env(_env.size() + 1);

	for (environment::const_iterator i = _env.cbegin(), e = _env.cend(); i != e; ++i)<commit_msg>core: improved process's child process exit handling (fixes defunct childs)<commit_after>#include <x0/process.hpp>
#include <boost/asio.hpp>
#include <string>
#include <cstdlib>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>

namespace x0 {

/** invokes \p cmd until its not early aborted with EINTR. */
#define EINTR_LOOP(rv, cmd) 				\
	do {									\
		rv = cmd;							\
	} while (rv == -1 && errno == EINTR)

process::process(boost::asio::io_service& io) :
	input_(io), output_(io), error_(io), pid_(-1), status_(0)
{
}

process::process(boost::asio::io_service& io, const std::string& exe, const params& args, const environment& env, const std::string& workdir) :
	input_(io), output_(io), error_(io), pid_(-1), status_(0)
{
	start(exe, args, env, workdir);
}

process::~process()
{
	int rv;
	EINTR_LOOP(rv, ::waitpid(pid_, &status_, 0));
	//printf("~process(): rv=%d, errno=%s\n", rv, strerror(errno));
}

void process::start(const std::string& exe, const params& args, const environment& env, const std::string& workdir)
{
	//::fprintf(stderr, "proc[%d] start(exe=%s, args=[...], workdir=%s)\n", getpid(), exe.c_str(), workdir.c_str());
	switch (pid_ = fork())
	{
		case 0: // child
			setup_child(exe, args, env, workdir);
			break;
		case -1: // error
			throw boost::system::system_error(boost::system::error_code(errno, boost::system::system_category));
		default: // parent
			setup_parent();
			break;
	}
}

void process::terminate()
{
	::kill(pid_, SIGTERM);
}

bool process::expired()
{
	if (pid_ <= 0)
		return true;

	if ((fetch_status() == -1 && errno == ECHILD)
			|| (WIFEXITED(status_) || WIFSIGNALED(status_)))
		return true;

	return false;
}

int process::fetch_status()
{
	int rv;

	do rv = ::waitpid(pid_, &status_, WNOHANG);
	while (rv == -1 && errno == EINTR);

	return rv;
}

void process::setup_parent()
{
	// setup I/O
	input_.remote().close();
	output_.remote().close();
	error_.remote().close();
}

void process::setup_child(const std::string& _exe, const params& _args, const environment& _env, const std::string& _workdir)
{
	// restore signal handler(s)
	::signal(SIGPIPE, SIG_DFL);

	// setup environment
	int k = 0;
	std::vector<char *> env(_env.size() + 1);

	for (environment::const_iterator i = _env.cbegin(), e = _env.cend(); i != e; ++i)
{'fraction_non_alphanumeric': 0.18815704784381035, 'fraction_numerical': 0.006650933276121004, 'mean_word_length': 4.334096109839817, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 22, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '873790', 'n_tokens_mistral': 1839, 'n_tokens_neox': 1710, 'n_words': 433}