30 #include <sys/types.h>
52 void dupTo(
int stdxxx ) ;
53 void write(
const std::string & ) ;
67 int stdxxx ,
bool clean ,
bool strict_path ,
68 Identity run_as_id ,
bool strict_id ,
69 int exec_error_exit ,
const std::string & exec_error_format ,
70 std::string (*exec_error_format_fn)(std::string,
int) ) ;
72 static std::pair<bool,pid_t> fork() ;
76 static void printError(
int ,
const std::string & s ) ;
77 std::string execErrorFormat(
const std::string & format ,
int errno_ ) ;
92 int stdxxx ,
bool clean ,
bool strict_path ,
93 Identity run_as_id ,
bool strict_id ,
94 int exec_error_exit ,
const std::string & exec_error_format ,
95 std::string (*exec_error_format_fn)(std::string,
int) ) :
97 run_as_id,strict_id,exec_error_exit,exec_error_format,exec_error_format_fn) )
108 return m_imp->wait() ;
113 return NewProcessImp::fork() ;
123 G::NewProcessImp::NewProcessImp(
const Path & exe ,
const StringArray & args ,
124 int stdxxx ,
bool clean ,
bool strict_path ,
125 Identity run_as_id ,
bool strict_id ,
126 int exec_error_exit ,
const std::string & exec_error_format ,
127 std::string (*exec_error_format_fn)(std::string,
int) ) :
132 G_ASSERT( stdxxx == -1 || stdxxx == 1 || stdxxx == 2 ) ;
133 if( stdxxx == 1 ) stdxxx = STDOUT_FILENO ;
134 else if( stdxxx == 2 ) stdxxx = STDERR_FILENO ;
138 throw NewProcess::InvalidPath( exe.
str() ) ;
141 throw NewProcess::Insecure() ;
144 std::pair<bool,pid_t> f =
fork() ;
145 bool in_child = f.first ;
146 m_child_pid = f.second ;
154 Process::beOrdinaryForExec( run_as_id ) ;
158 Process::closeFilesExcept( m_pipe.fd() ) ;
159 m_pipe.dupTo( stdxxx ) ;
163 ::signal( SIGPIPE , SIG_DFL ) ;
166 int e = run( exe , args , clean , strict_path ) ;
169 if( exec_error_format_fn != 0 )
170 printError( stdxxx , (*exec_error_format_fn)(exec_error_format,e) ) ;
171 else if( !exec_error_format.empty() )
172 printError( stdxxx , execErrorFormat(exec_error_format,e) ) ;
177 ::_exit( exec_error_exit ) ;
182 m_wait_future = NewProcessWaitFuture( m_child_pid , m_pipe.fd() ) ;
186 std::pair<bool,pid_t> G::NewProcessImp::fork()
188 std::cout << std::flush ;
189 std::cerr << std::flush ;
190 pid_t rc = ::fork() ;
191 const bool ok = rc != -1 ;
192 if( !ok )
throw NewProcess::CannotFork() ;
193 bool in_child = rc == 0 ;
194 pid_t child_pid =
static_cast<pid_t
>(rc) ;
195 return std::make_pair( in_child , child_pid ) ;
198 void G::NewProcessImp::printError(
int stdxxx ,
const std::string & s )
201 G_IGNORE_RETURN(
int , ::write( stdxxx , s.c_str() , s.length() ) ) ;
204 int G::NewProcessImp::run(
const G::Path & exe ,
const StringArray & args ,
bool clean ,
bool strict )
207 std::string path(
"PATH=/usr/bin:/bin" ) ;
208 std::string ifs(
"IFS= \t\n" ) ;
209 env[0U] =
const_cast<char*
>( path.c_str() ) ;
210 env[1U] =
const_cast<char*
>( ifs.c_str() ) ;
213 char ** argv =
new char* [ args.size() + 2U ] ;
214 std::string str_exe = exe.
str() ;
215 argv[0U] =
const_cast<char*
>( str_exe.c_str() ) ;
216 unsigned int argc = 1U ;
217 for( StringArray::const_iterator arg_p = args.begin() ; arg_p != args.end() ; ++arg_p , argc++ )
218 argv[argc] = const_cast<char*>(arg_p->c_str()) ;
219 argv[argc] = nullptr ;
221 if( clean && strict )
222 ::execve( exe.
str().c_str() , argv , env ) ;
224 ::execvpe( exe.
str().c_str() , argv , env ) ;
226 ::execv( exe.
str().c_str() , argv ) ;
228 ::execvp( exe.
str().c_str() , argv ) ;
229 int e = Process::errno_() ;
232 G_DEBUG(
"G::NewProcess::run: execve() returned: errno=" << e <<
": " << exe ) ;
238 return m_wait_future ;
241 void G::NewProcessImp::kill()
243 if( m_child_pid != -1 )
245 ::kill( m_child_pid , SIGTERM ) ;
250 std::string G::NewProcessImp::execErrorFormat(
const std::string & format ,
int errno_ )
252 std::string result = format ;
263 m_fds[0] = m_fds[1] = -1 ;
264 if( ::socketpair( AF_UNIX , SOCK_STREAM , 0 , m_fds ) < 0 )
265 throw NewProcess::PipeError() ;
266 G_DEBUG(
"G::Pipe::ctor: " << m_fds[0] <<
" " << m_fds[1] ) ;
271 if( m_fds[0] >= 0 ) ::close( m_fds[0] ) ;
272 if( m_fds[1] >= 0 ) ::close( m_fds[1] ) ;
275 void G::Pipe::inChild()
277 ::close( m_fds[0] ) ;
282 void G::Pipe::inParent()
284 ::close( m_fds[1] ) ;
289 int G::Pipe::fd()
const
294 void G::Pipe::dupTo(
int stdxxx )
296 if( m_fd != -1 && stdxxx != -1 && m_fd != stdxxx )
298 if( ::dup2(m_fd,stdxxx) != stdxxx )
299 throw NewProcess::PipeError() ;
304 int flags = ::fcntl( stdxxx , F_GETFD ) ;
305 flags &= ~FD_CLOEXEC ;
306 ::fcntl( stdxxx , F_SETFD , flags ) ;
340 char * p = &m_buffer[0] ;
341 size_t space = m_buffer.size() ;
345 ssize_t n = ::read( m_fd , p?p:more , p?space:
sizeof(more) ) ;
346 m_read_error = errno ;
347 if( n < 0 && m_error == EINTR )
358 m_buffer.resize( size ) ;
375 m_rc = ::waitpid( m_pid , &m_status , 0 ) ;
377 if( m_rc == -1 && m_error == EINTR )
390 if( m_error || m_read_error )
392 std::ostringstream ss ;
393 ss <<
"errno=" << (m_read_error?m_read_error:m_error) ;
394 throw NewProcess::WaitError( ss.str() ) ;
396 if( ! WIFEXITED(m_status) )
399 std::ostringstream ss ;
400 ss <<
"status=" << m_status ;
401 throw NewProcess::ChildError( ss.str() ) ;
403 result = WEXITSTATUS(m_status) ;
410 if( m_fd < 0 || m_read_error != 0 )
411 return std::string() ;
413 return std::string( &m_buffer[0] , m_buffer.size() ) ;
int get()
Returns the result of the run() as either the process exit code or as a thrown exception.
void kill()
Tries to kill the spawned process.
std::string str() const
Returns the path string.
bool isRoot() const
Returns true if the userid is zero.
static Identity invalid()
Returns an invalid identity.
A class that holds the parameters and the results of a waitpid() system call.
A combination of user-id and group-id, with a very low-level interface to the get/set/e/uid/gid funct...
std::vector< std::string > StringArray
A std::vector of std::strings.
static Identity effective()
Returns the current effective identity.
NewProcess(const Path &exe, const StringArray &args, int capture_stdxxx=1, bool clean=true, bool strict_path=true, Identity run_as_id=Identity::invalid(), bool strict_id=true, int exec_error_exit=127, const std::string &exec_error_format=std::string(), std::string(*exec_error_format_fn)(std::string, int)=0)
Constructor.
static std::string fromInt(int i)
Converts int 'i' to a string.
bool isRelative() const
Returns true if the path is a relative path.
static std::pair< bool, pid_t > fork()
A utility function that forks the calling process and returns twice; once in the parent and once in t...
std::string output()
Returns the first bit of child-process output.
NewProcessWaitFuture & run()
Waits for the process identified by the constructor parameter to exit.
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurances of sub-string 'from' with 'to'...
NewProcessWaitFuture & wait()
Returns a reference to the WaitFuture sub-object so that the caller can wait for the child process to...
NewProcessWaitFuture()
Default constructor for an object where run() does nothing and get() returns zero.
A pimple-pattern implementation class used by G::NewProcess.
A Path object represents a file system path.
A private implementation class used by G::NewProcess that wraps a unix pipe.