|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "jam.h" |
|
#include "execcmd.h" |
|
|
|
#include "lists.h" |
|
#include "output.h" |
|
#include "strings.h" |
|
|
|
#include <errno.h> |
|
#include <signal.h> |
|
#include <stdio.h> |
|
#include <time.h> |
|
#include <unistd.h> |
|
#include <sys/resource.h> |
|
#include <sys/times.h> |
|
#include <sys/wait.h> |
|
|
|
#if defined(sun) || defined(__sun) |
|
#include <wait.h> |
|
#endif |
|
|
|
#ifdef USE_EXECUNIX |
|
|
|
#include <sys/times.h> |
|
|
|
#if defined(__APPLE__) |
|
#define NO_VFORK |
|
#endif |
|
|
|
#ifdef NO_VFORK |
|
#define vfork() fork() |
|
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int get_free_cmdtab_slot(); |
|
|
|
|
|
|
|
static clock_t tps; |
|
static int old_time_initialized; |
|
static struct tms old_time; |
|
|
|
|
|
|
|
|
|
#define OUT 0 |
|
#define ERR 1 |
|
|
|
static struct |
|
{ |
|
int pid; |
|
int fd[ 2 ]; |
|
FILE * stream[ 2 ]; |
|
clock_t start_time; |
|
int exit_reason; |
|
char * buffer[ 2 ]; |
|
int buf_size[ 2 ]; |
|
timestamp start_dt; |
|
|
|
|
|
ExecCmdCallback func; |
|
|
|
|
|
void * closure; |
|
} cmdtab[ MAXJOBS ] = { { 0 } }; |
|
|
|
|
|
|
|
|
|
|
|
|
|
int exec_check |
|
( |
|
string const * command, |
|
LIST * * pShell, |
|
int * error_length, |
|
int * error_max_length |
|
) |
|
{ |
|
int const is_raw_cmd = is_raw_command_request( *pShell ); |
|
|
|
|
|
|
|
|
|
if ( !command->size && ( is_raw_cmd || list_empty( *pShell ) ) ) |
|
return EXEC_CHECK_NOOP; |
|
|
|
return is_raw_cmd |
|
? EXEC_CHECK_OK |
|
: check_cmd_for_too_long_lines( command->value, MAXLINE, error_length, |
|
error_max_length ); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define EXECCMD_PIPE_READ 0 |
|
#define EXECCMD_PIPE_WRITE 1 |
|
|
|
void exec_cmd |
|
( |
|
string const * command, |
|
ExecCmdCallback func, |
|
void * closure, |
|
LIST * shell |
|
) |
|
{ |
|
int const slot = get_free_cmdtab_slot(); |
|
int out[ 2 ]; |
|
int err[ 2 ]; |
|
int len; |
|
char const * argv[ MAXARGC + 1 ]; |
|
|
|
|
|
static LIST * default_shell; |
|
if ( !default_shell ) |
|
default_shell = list_push_back( list_new( |
|
object_new( "/bin/sh" ) ), |
|
object_new( "-c" ) ); |
|
|
|
if ( list_empty( shell ) ) |
|
shell = default_shell; |
|
|
|
|
|
|
|
|
|
argv_from_shell( argv, shell, command->value, slot ); |
|
|
|
if ( DEBUG_EXECCMD ) |
|
{ |
|
int i; |
|
printf( "Using shell: " ); |
|
list_print( shell ); |
|
printf( "\n" ); |
|
for ( i = 0; argv[ i ]; ++i ) |
|
printf( " argv[%d] = '%s'\n", i, argv[ i ] ); |
|
} |
|
|
|
|
|
if ( pipe( out ) < 0 || ( globs.pipe_action && pipe( err ) < 0 ) ) |
|
{ |
|
perror( "pipe" ); |
|
exit( EXITBAD ); |
|
} |
|
|
|
|
|
if ( !old_time_initialized ) |
|
{ |
|
times( &old_time ); |
|
old_time_initialized = 1; |
|
} |
|
|
|
|
|
|
|
timestamp_current( &cmdtab[ slot ].start_dt ); |
|
|
|
if ( 0 < globs.timeout ) |
|
{ |
|
|
|
|
|
|
|
struct tms buf; |
|
cmdtab[ slot ].start_time = times( &buf ); |
|
|
|
|
|
if ( !tps ) tps = sysconf( _SC_CLK_TCK ); |
|
} |
|
|
|
|
|
fcntl( out[ EXECCMD_PIPE_READ ], F_SETFD, FD_CLOEXEC ); |
|
if ( globs.pipe_action ) |
|
fcntl( err[ EXECCMD_PIPE_READ ], F_SETFD, FD_CLOEXEC ); |
|
|
|
if ( ( cmdtab[ slot ].pid = vfork() ) == -1 ) |
|
{ |
|
perror( "vfork" ); |
|
exit( EXITBAD ); |
|
} |
|
|
|
if ( cmdtab[ slot ].pid == 0 ) |
|
{ |
|
|
|
|
|
|
|
int const pid = getpid(); |
|
|
|
|
|
dup2( out[ EXECCMD_PIPE_WRITE ], STDOUT_FILENO ); |
|
dup2( globs.pipe_action ? err[ EXECCMD_PIPE_WRITE ] : |
|
out[ EXECCMD_PIPE_WRITE ], STDERR_FILENO ); |
|
close( out[ EXECCMD_PIPE_WRITE ] ); |
|
if ( globs.pipe_action ) |
|
close( err[ EXECCMD_PIPE_WRITE ] ); |
|
|
|
|
|
|
|
|
|
|
|
|
|
if ( 0 < globs.timeout ) |
|
{ |
|
struct rlimit r_limit; |
|
r_limit.rlim_cur = globs.timeout; |
|
r_limit.rlim_max = globs.timeout; |
|
setrlimit( RLIMIT_CPU, &r_limit ); |
|
} |
|
setpgid( pid, pid ); |
|
execvp( argv[ 0 ], (char * *)argv ); |
|
perror( "execvp" ); |
|
_exit( 127 ); |
|
} |
|
|
|
|
|
|
|
|
|
setpgid( cmdtab[ slot ].pid, cmdtab[ slot ].pid ); |
|
|
|
|
|
close( out[ EXECCMD_PIPE_WRITE ] ); |
|
if ( globs.pipe_action ) |
|
close( err[ EXECCMD_PIPE_WRITE ] ); |
|
|
|
|
|
fcntl( out[ EXECCMD_PIPE_READ ], F_SETFL, O_NONBLOCK ); |
|
if ( globs.pipe_action ) |
|
fcntl( err[ EXECCMD_PIPE_READ ], F_SETFL, O_NONBLOCK ); |
|
|
|
|
|
cmdtab[ slot ].fd[ OUT ] = out[ EXECCMD_PIPE_READ ]; |
|
cmdtab[ slot ].stream[ OUT ] = fdopen( cmdtab[ slot ].fd[ OUT ], "rb" ); |
|
if ( !cmdtab[ slot ].stream[ OUT ] ) |
|
{ |
|
perror( "fdopen" ); |
|
exit( EXITBAD ); |
|
} |
|
|
|
|
|
if ( globs.pipe_action ) |
|
{ |
|
cmdtab[ slot ].fd[ ERR ] = err[ EXECCMD_PIPE_READ ]; |
|
cmdtab[ slot ].stream[ ERR ] = fdopen( cmdtab[ slot ].fd[ ERR ], "rb" ); |
|
if ( !cmdtab[ slot ].stream[ ERR ] ) |
|
{ |
|
perror( "fdopen" ); |
|
exit( EXITBAD ); |
|
} |
|
} |
|
|
|
|
|
cmdtab[ slot ].func = func; |
|
cmdtab[ slot ].closure = closure; |
|
} |
|
|
|
#undef EXECCMD_PIPE_READ |
|
#undef EXECCMD_PIPE_WRITE |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int read_descriptor( int i, int s ) |
|
{ |
|
int ret; |
|
char buffer[ BUFSIZ ]; |
|
|
|
while ( 0 < ( ret = fread( buffer, sizeof( char ), BUFSIZ - 1, |
|
cmdtab[ i ].stream[ s ] ) ) ) |
|
{ |
|
buffer[ ret ] = 0; |
|
if ( !cmdtab[ i ].buffer[ s ] ) |
|
{ |
|
|
|
if ( globs.max_buf && ret > globs.max_buf ) |
|
{ |
|
ret = globs.max_buf; |
|
buffer[ ret ] = 0; |
|
} |
|
cmdtab[ i ].buf_size[ s ] = ret + 1; |
|
cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( ret + 1 ); |
|
memcpy( cmdtab[ i ].buffer[ s ], buffer, ret + 1 ); |
|
} |
|
else |
|
{ |
|
|
|
if ( cmdtab[ i ].buf_size[ s ] < globs.max_buf || !globs.max_buf ) |
|
{ |
|
char * tmp = cmdtab[ i ].buffer[ s ]; |
|
int const old_len = cmdtab[ i ].buf_size[ s ] - 1; |
|
int const new_len = old_len + ret + 1; |
|
cmdtab[ i ].buf_size[ s ] = new_len; |
|
cmdtab[ i ].buffer[ s ] = (char*)BJAM_MALLOC_ATOMIC( new_len ); |
|
memcpy( cmdtab[ i ].buffer[ s ], tmp, old_len ); |
|
memcpy( cmdtab[ i ].buffer[ s ] + old_len, buffer, ret + 1 ); |
|
BJAM_FREE( tmp ); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
if ( globs.max_buf && globs.max_buf <= cmdtab[ i ].buf_size[ s ] ) |
|
cmdtab[ i ].buffer[ s ][ cmdtab[ i ].buf_size[ s ] - 2 ] = '\n'; |
|
|
|
return feof( cmdtab[ i ].stream[ s ] ); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
static void close_streams( int const i, int const s ) |
|
{ |
|
fclose( cmdtab[ i ].stream[ s ] ); |
|
cmdtab[ i ].stream[ s ] = 0; |
|
|
|
close( cmdtab[ i ].fd[ s ] ); |
|
cmdtab[ i ].fd[ s ] = 0; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int populate_file_descriptors( fd_set * const fds ) |
|
{ |
|
int i; |
|
int fd_max = 0; |
|
|
|
FD_ZERO( fds ); |
|
for ( i = 0; i < globs.jobs; ++i ) |
|
{ |
|
int fd; |
|
if ( ( fd = cmdtab[ i ].fd[ OUT ] ) > 0 ) |
|
{ |
|
if ( fd > fd_max ) fd_max = fd; |
|
FD_SET( fd, fds ); |
|
} |
|
if ( globs.pipe_action ) |
|
{ |
|
if ( ( fd = cmdtab[ i ].fd[ ERR ] ) > 0 ) |
|
{ |
|
if ( fd > fd_max ) fd_max = fd; |
|
FD_SET( fd, fds ); |
|
} |
|
} |
|
} |
|
return fd_max; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void exec_wait() |
|
{ |
|
int finished = 0; |
|
|
|
|
|
while ( !finished ) |
|
{ |
|
int i; |
|
struct timeval tv; |
|
struct timeval * ptv = NULL; |
|
int select_timeout = globs.timeout; |
|
|
|
|
|
fd_set fds; |
|
int const fd_max = populate_file_descriptors( &fds ); |
|
|
|
|
|
|
|
|
|
|
|
if ( globs.timeout > 0 ) |
|
{ |
|
struct tms buf; |
|
clock_t const current = times( &buf ); |
|
for ( i = 0; i < globs.jobs; ++i ) |
|
if ( cmdtab[ i ].pid ) |
|
{ |
|
clock_t const consumed = |
|
( current - cmdtab[ i ].start_time ) / tps; |
|
if ( consumed >= globs.timeout ) |
|
{ |
|
killpg( cmdtab[ i ].pid, SIGKILL ); |
|
cmdtab[ i ].exit_reason = EXIT_TIMEOUT; |
|
} |
|
else if ( globs.timeout - consumed < select_timeout ) |
|
select_timeout = globs.timeout - consumed; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
tv.tv_sec = select_timeout; |
|
tv.tv_usec = 0; |
|
ptv = &tv; |
|
} |
|
|
|
|
|
{ |
|
int ret; |
|
while ( ( ret = select( fd_max + 1, &fds, 0, 0, ptv ) ) == -1 ) |
|
if ( errno != EINTR ) |
|
break; |
|
if ( ret <= 0 ) |
|
continue; |
|
} |
|
|
|
for ( i = 0; i < globs.jobs; ++i ) |
|
{ |
|
int out_done = 0; |
|
int err_done = 0; |
|
if ( FD_ISSET( cmdtab[ i ].fd[ OUT ], &fds ) ) |
|
out_done = read_descriptor( i, OUT ); |
|
|
|
if ( globs.pipe_action && FD_ISSET( cmdtab[ i ].fd[ ERR ], &fds ) ) |
|
err_done = read_descriptor( i, ERR ); |
|
|
|
|
|
if ( out_done || err_done ) |
|
{ |
|
int pid; |
|
int status; |
|
int rstat; |
|
timing_info time_info; |
|
|
|
|
|
finished = 1; |
|
|
|
|
|
close_streams( i, OUT ); |
|
if ( globs.pipe_action ) |
|
close_streams( i, ERR ); |
|
|
|
|
|
while ( ( pid = waitpid( cmdtab[ i ].pid, &status, 0 ) ) == -1 ) |
|
if ( errno != EINTR ) |
|
break; |
|
if ( pid != cmdtab[ i ].pid ) |
|
{ |
|
printf( "unknown pid %d with errno = %d\n", pid, errno ); |
|
exit( EXITBAD ); |
|
} |
|
|
|
|
|
if ( WIFEXITED( status ) ) |
|
cmdtab[ i ].exit_reason = WEXITSTATUS( status ) |
|
? EXIT_FAIL |
|
: EXIT_OK; |
|
|
|
{ |
|
struct tms new_time; |
|
times( &new_time ); |
|
time_info.system = (double)( new_time.tms_cstime - |
|
old_time.tms_cstime ) / CLOCKS_PER_SEC; |
|
time_info.user = (double)( new_time.tms_cutime - |
|
old_time.tms_cutime ) / CLOCKS_PER_SEC; |
|
timestamp_copy( &time_info.start, &cmdtab[ i ].start_dt ); |
|
timestamp_current( &time_info.end ); |
|
old_time = new_time; |
|
} |
|
|
|
|
|
if ( interrupted() ) |
|
rstat = EXEC_CMD_INTR; |
|
else if ( status ) |
|
rstat = EXEC_CMD_FAIL; |
|
else |
|
rstat = EXEC_CMD_OK; |
|
|
|
|
|
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time_info, |
|
cmdtab[ i ].buffer[ OUT ], cmdtab[ i ].buffer[ ERR ], |
|
cmdtab[ i ].exit_reason ); |
|
|
|
|
|
BJAM_FREE( cmdtab[ i ].buffer[ OUT ] ); |
|
cmdtab[ i ].buffer[ OUT ] = 0; |
|
cmdtab[ i ].buf_size[ OUT ] = 0; |
|
|
|
BJAM_FREE( cmdtab[ i ].buffer[ ERR ] ); |
|
cmdtab[ i ].buffer[ ERR ] = 0; |
|
cmdtab[ i ].buf_size[ ERR ] = 0; |
|
|
|
cmdtab[ i ].pid = 0; |
|
cmdtab[ i ].func = 0; |
|
cmdtab[ i ].closure = 0; |
|
cmdtab[ i ].start_time = 0; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
static int get_free_cmdtab_slot() |
|
{ |
|
int slot; |
|
for ( slot = 0; slot < MAXJOBS; ++slot ) |
|
if ( !cmdtab[ slot ].pid ) |
|
return slot; |
|
printf( "no slots for child!\n" ); |
|
exit( EXITBAD ); |
|
} |
|
|
|
# endif |
|
|