38#include "blocxx/BLOCXX_config.h"
40#if !defined(BLOCXX_WIN32)
53#ifdef BLOCXX_HAVE_SYS_RESOURCE_H
54#include <sys/resource.h>
56#ifdef BLOCXX_HAVE_SYS_TYPES_H
59#ifdef BLOCXX_HAVE_UNISTD_H
75#if defined(sigemptyset)
85#define SIG_DFL (void(*)())0
94 void throw_child_error(Exec::PreExec::Error
const & err,
const String& process_path)
96 Format msg(
"Exec::spawn(%1): child startup failed: %2", process_path, err.message);
97 if (err.error_num != 0)
100 ExecErrorException, msg.c_str(), err.error_num);
108 void check(
bool b,
char const * message,
bool use_errno =
true)
112 Exec::PreExec::Error x;
114 x.error_num = use_errno ? errno : 0;
119 void parent_check(
bool b,
char const * msg)
127 void close_on_exec(
Descriptor descr,
bool may_be_bad)
129 int e = ::fcntl(descr, F_SETFD, FD_CLOEXEC);
130 check(e == 0 || may_be_bad && errno == EBADF,
"fcntl");
133 void handle_child_error(
int rc, Exec::PreExec::Error
const & ce, Process & proc,
const String& process_path)
143 Format(
"Exec::spawn(%1): timed out waiting for child to exec()",process_path).c_str());
146 Format(
"Exec::spawn(%1): error reading init status from child",process_path).c_str(), errnum);
150 throw_child_error(ce, process_path);
155 long getMaxOpenFiles()
157 long sysconfValue = sysconf(_SC_OPEN_MAX);
158 long maxOpen = sysconfValue;
160 rl.rlim_cur = rlim_t(0);
161 if( getrlimit(RLIMIT_NOFILE, &rl) != -1 )
163 if( sysconfValue < 0 )
165 maxOpen = rl.rlim_cur;
169 maxOpen = std::min<rlim_t>(rl.rlim_cur, sysconfValue);
175 BLOCXX_ASSERT( (maxOpen > 2) && (maxOpen <=
long(std::numeric_limits<int>::max())) );
179 void init_child(
char const * exec_path,
180 char const *
const argv[],
char const *
const envp[],
187 int exec_err_desc = -1;
188 Exec::PreExec::Error err;
190 err.message[0] =
'\0';
195 pre_exec.call(ppipe);
198 char *
const * cc_argv =
const_cast<char *
const *
>(argv);
199 char *
const * cc_envp =
const_cast<char *
const *
>(envp);
202 check(::execve(exec_path, cc_argv, cc_envp) != -1,
"execve");
206 check(::execv(exec_path, cc_argv) != -1,
"execv");
209 catch (Exec::PreExec::Error & e)
213 catch (std::exception & e)
218 catch (Exec::PreExec::DontCatch & e)
227 ssize_t rv = ::write(exec_err_desc, &err,
sizeof(err));
265 ::sigset_t emptymask;
266 check(::sigemptyset(&emptymask) == 0,
"sigemptyset");
267 check(::sigprocmask(SIG_SETMASK, &emptymask, 0) == 0,
"sigprocmask");
271 if (
sig == SIGKILL ||
sig == SIGSTOP)
275 struct sigaction temp;
276 int e = ::sigaction(
sig, 0, &temp);
277 check(e == 0 || errno == EINVAL,
"sigaction [1]");
278 if (e == 0 && temp.sa_handler != SIG_DFL)
280 temp.sa_handler = SIG_DFL;
283 ::sigaction(
sig, &temp, 0);
291 for (
int d = 3; d < int(numd); ++d)
293 if (
size_t(d) >= keep.size() || !keep[d])
295 close_on_exec(d,
true);
303 if (!(ppipe[0] && ppipe[1] && ppipe[2]))
306 check(nulld >= 0,
"open");
307 close_on_exec(nulld,
false);
309 for (
unsigned d = 0; d < 3; ++d)
314 check(::dup2(ddup, d) != -1,
"dup2");
333 int pgidrv = setpgid(0, 0);
338 : m_max_descriptors(precompute_max_descriptors ? getMaxOpenFiles() : 0)
356StandardPreExec::StandardPreExec() : PreExec(true)
367 std::vector<bool> empty;
368 PreExec::resetSignals();
369 PreExec::setNewProcessGroup();
370 PreExec::setupStandardDescriptors(pparr);
371 PreExec::closeDescriptorsOnExec(empty);
387 std::vector<bool> empty;
388 PreExec::resetSignals();
389 PreExec::setNewProcessGroup();
390 PreExec::closeDescriptorsOnExec(empty);
402 parent_check(exec_path,
"Exec::spawn: null exec_path");
403 char const * default_argv[2] = { exec_path, 0 };
427 ::pid_t child_pid = ::fork();
430 init_child(exec_path, argv, envp, pre_exec, ppipe);
433 parent_check(child_pid >= 0,
Format(
"Exec::spawn(%1): fork() failed", exec_path).c_str());
446 handle_child_error(nread, child_error, *retval, exec_path);
#define BLOCXX_ASSERT(CON)
BLOCXX_ASSERT works similar to the assert() macro, but instead of calling abort(),...
#define BLOCXX_THROW(exType, msg)
Throw an exception using FILE and LINE.
#define BLOCXX_THROW_ERRNO_MSG1(exType, msg, errnum)
Throw an exception using FILE, LINE, errnum and strerror(errnum)
This class is used to specify what spawn() should do between fork and exec.
static void setupStandardDescriptors(pipe_pointer_t const pparr[])
For calling from PreExec::call.
static void resetSignals()
For calling from PreExec::call.
void closeDescriptorsOnExec(std::vector< bool > const &keep)
For calling from PreExec::call.
PreExec(bool precompute_max_descriptors=false)
static void setNewProcessGroup()
For calling from PreExec::call().
static void closePipesOnExec(pipe_pointer_t const pparr[])
For calling from PreExec::call.
virtual bool keepStd(int d) const =0
virtual int read(void *dataIn, int dataInLen, ErrorAction errorAsException=E_RETURN_ON_ERROR)=0
Read a specified number of bytes from the device that is exposing the IOIFC interface.
virtual bool keepStd(int d) const
virtual void call(pipe_pointer_t const pparr[])
Resets all signals to their default actions and sets to close-on-exec all descriptors except the stan...
virtual bool keepStd(int d) const
virtual void call(pipe_pointer_t const pparr[])
Resets all signals to their default actions and sets to close-on-exec all descriptors except the stan...
Descriptor getInputHandle() const
Descriptor getOutputHandle() const
Class for communicating with and managing a child process.
A timeout can be absolute, which means that it will happen at the specified DateTime.
static Timeout relative(float seconds)
Abstract interface for an UnnamedPipe.
virtual Descriptor getInputDescriptor() const =0
Get the underlying input descriptor.
void setReadTimeout(const Timeout &timeout)
Sets the read timeout value.
static UnnamedPipeRef createUnnamedPipe(EOpen doOpen=E_OPEN)
Create an instance of the concrete class that implements the UnnamedPipe interface.
virtual Descriptor getOutputDescriptor() const =0
Get the underlying output descriptor.
unsigned const BLOCXX_NPIPE
void close_child_ends(UnnamedPipeRef ppipe[BLOCXX_NPIPE])
unsigned const BLOCXX_EXEC_ERR
ProcessRef spawnImpl(char const *exec_path, char const *const argv[], char const *const envp[], Exec::PreExec &pre_exec)
char * strcpy_trunc(char *dst, std::size_t dstsize, char const *src)
PROMISE: copies the first n = min(strlen(src), dstsize - 1) characters of C-string src to dst,...