2009-04-20 15:00:56 +02:00
# include "cache.h"
# include "run-command.h"
# include "exec_cmd.h"
2014-08-14 02:22:36 +00:00
# include "debug.h"
2009-04-20 15:00:56 +02:00
static inline void close_pair ( int fd [ 2 ] )
{
close ( fd [ 0 ] ) ;
close ( fd [ 1 ] ) ;
}
static inline void dup_devnull ( int to )
{
int fd = open ( " /dev/null " , O_RDWR ) ;
dup2 ( fd , to ) ;
close ( fd ) ;
}
int start_command ( struct child_process * cmd )
{
int need_in , need_out , need_err ;
int fdin [ 2 ] , fdout [ 2 ] , fderr [ 2 ] ;
2014-08-14 02:22:36 +00:00
char sbuf [ STRERR_BUFSIZE ] ;
2009-04-20 15:00:56 +02:00
/*
* In case of errors we must keep the promise to close FDs
* that have been passed in via - > in and - > out .
*/
need_in = ! cmd - > no_stdin & & cmd - > in < 0 ;
if ( need_in ) {
if ( pipe ( fdin ) < 0 ) {
if ( cmd - > out > 0 )
close ( cmd - > out ) ;
return - ERR_RUN_COMMAND_PIPE ;
}
cmd - > in = fdin [ 1 ] ;
}
need_out = ! cmd - > no_stdout
& & ! cmd - > stdout_to_stderr
& & cmd - > out < 0 ;
if ( need_out ) {
if ( pipe ( fdout ) < 0 ) {
if ( need_in )
close_pair ( fdin ) ;
else if ( cmd - > in )
close ( cmd - > in ) ;
return - ERR_RUN_COMMAND_PIPE ;
}
cmd - > out = fdout [ 0 ] ;
}
need_err = ! cmd - > no_stderr & & cmd - > err < 0 ;
if ( need_err ) {
if ( pipe ( fderr ) < 0 ) {
if ( need_in )
close_pair ( fdin ) ;
else if ( cmd - > in )
close ( cmd - > in ) ;
if ( need_out )
close_pair ( fdout ) ;
else if ( cmd - > out )
close ( cmd - > out ) ;
return - ERR_RUN_COMMAND_PIPE ;
}
cmd - > err = fderr [ 0 ] ;
}
fflush ( NULL ) ;
cmd - > pid = fork ( ) ;
if ( ! cmd - > pid ) {
if ( cmd - > no_stdin )
dup_devnull ( 0 ) ;
else if ( need_in ) {
dup2 ( fdin [ 0 ] , 0 ) ;
close_pair ( fdin ) ;
} else if ( cmd - > in ) {
dup2 ( cmd - > in , 0 ) ;
close ( cmd - > in ) ;
}
if ( cmd - > no_stderr )
dup_devnull ( 2 ) ;
else if ( need_err ) {
dup2 ( fderr [ 1 ] , 2 ) ;
close_pair ( fderr ) ;
}
if ( cmd - > no_stdout )
dup_devnull ( 1 ) ;
else if ( cmd - > stdout_to_stderr )
dup2 ( 2 , 1 ) ;
else if ( need_out ) {
dup2 ( fdout [ 1 ] , 1 ) ;
close_pair ( fdout ) ;
} else if ( cmd - > out > 1 ) {
dup2 ( cmd - > out , 1 ) ;
close ( cmd - > out ) ;
}
if ( cmd - > dir & & chdir ( cmd - > dir ) )
die ( " exec %s: cd to %s failed (%s) " , cmd - > argv [ 0 ] ,
2014-08-14 02:22:36 +00:00
cmd - > dir , strerror_r ( errno , sbuf , sizeof ( sbuf ) ) ) ;
2009-04-20 15:00:56 +02:00
if ( cmd - > env ) {
for ( ; * cmd - > env ; cmd - > env + + ) {
if ( strchr ( * cmd - > env , ' = ' ) )
putenv ( ( char * ) * cmd - > env ) ;
else
unsetenv ( * cmd - > env ) ;
}
}
if ( cmd - > preexec_cb )
cmd - > preexec_cb ( ) ;
if ( cmd - > perf_cmd ) {
execv_perf_cmd ( cmd - > argv ) ;
} else {
execvp ( cmd - > argv [ 0 ] , ( char * const * ) cmd - > argv ) ;
}
exit ( 127 ) ;
}
if ( cmd - > pid < 0 ) {
int err = errno ;
if ( need_in )
close_pair ( fdin ) ;
else if ( cmd - > in )
close ( cmd - > in ) ;
if ( need_out )
close_pair ( fdout ) ;
else if ( cmd - > out )
close ( cmd - > out ) ;
if ( need_err )
close_pair ( fderr ) ;
return err = = ENOENT ?
- ERR_RUN_COMMAND_EXEC :
- ERR_RUN_COMMAND_FORK ;
}
if ( need_in )
close ( fdin [ 0 ] ) ;
else if ( cmd - > in )
close ( cmd - > in ) ;
if ( need_out )
close ( fdout [ 1 ] ) ;
else if ( cmd - > out )
close ( cmd - > out ) ;
if ( need_err )
close ( fderr [ 1 ] ) ;
return 0 ;
}
static int wait_or_whine ( pid_t pid )
{
2014-08-14 02:22:36 +00:00
char sbuf [ STRERR_BUFSIZE ] ;
2009-04-20 15:00:56 +02:00
for ( ; ; ) {
int status , code ;
pid_t waiting = waitpid ( pid , & status , 0 ) ;
if ( waiting < 0 ) {
if ( errno = = EINTR )
continue ;
2014-08-14 02:22:36 +00:00
error ( " waitpid failed (%s) " ,
strerror_r ( errno , sbuf , sizeof ( sbuf ) ) ) ;
2009-04-20 15:00:56 +02:00
return - ERR_RUN_COMMAND_WAITPID ;
}
if ( waiting ! = pid )
return - ERR_RUN_COMMAND_WAITPID_WRONG_PID ;
if ( WIFSIGNALED ( status ) )
return - ERR_RUN_COMMAND_WAITPID_SIGNAL ;
if ( ! WIFEXITED ( status ) )
return - ERR_RUN_COMMAND_WAITPID_NOEXIT ;
code = WEXITSTATUS ( status ) ;
switch ( code ) {
case 127 :
return - ERR_RUN_COMMAND_EXEC ;
case 0 :
return 0 ;
default :
return - code ;
}
}
}
int finish_command ( struct child_process * cmd )
{
return wait_or_whine ( cmd - > pid ) ;
}
int run_command ( struct child_process * cmd )
{
int code = start_command ( cmd ) ;
if ( code )
return code ;
return finish_command ( cmd ) ;
}
static void prepare_run_command_v_opt ( struct child_process * cmd ,
const char * * argv ,
int opt )
{
memset ( cmd , 0 , sizeof ( * cmd ) ) ;
cmd - > argv = argv ;
cmd - > no_stdin = opt & RUN_COMMAND_NO_STDIN ? 1 : 0 ;
cmd - > perf_cmd = opt & RUN_PERF_CMD ? 1 : 0 ;
cmd - > stdout_to_stderr = opt & RUN_COMMAND_STDOUT_TO_STDERR ? 1 : 0 ;
}
int run_command_v_opt ( const char * * argv , int opt )
{
struct child_process cmd ;
prepare_run_command_v_opt ( & cmd , argv , opt ) ;
return run_command ( & cmd ) ;
}