# include "jam.h"
# include "execcmd.h"
# include "lists.h"
# include "variable.h"
# include <errno.h>
#ifdef APPLE_EXTENSIONS
# include "timingdata.h"
# include <sys/resource.h>
#endif
# if defined( unix ) || defined( NT ) || defined( __OS2__ )
# if defined( _AIX) || \
(defined (COHERENT) && defined (_I386)) || \
defined(__sgi) || \
defined(__Lynx__) || \
defined(M_XENIX) || \
defined(__QNX__) || \
defined(__BEOS__) || \
defined(__ISC)
# define vfork() fork()
# endif
# if defined( NT ) || defined( __OS2__ )
# include <process.h>
# if !defined( __BORLANDC__ ) && !defined( __OS2__ )
# define wait my_wait
static int my_wait(int *status);
# endif
# endif
# include <unistd.h>
# include <sys/wait.h>
static int intr = 0;
static int cmdsrunning = 0;
# ifdef NT
static void (*istat)( int );
void onintr( int );
# else
static void (*istat)();
#endif
static struct
{
int pid;
void (*func)();
void *closure;
# if defined( NT ) || defined( __OS2__ )
char *tempfile;
# endif
#ifdef APPLE_EXTENSIONS
int output_fd;
char * output_buffer;
int output_capacity;
int output_length;
#endif
} cmdtab[ MAXJOBS ] = {{0}};
void
onintr( disp )
int disp;
{
intr++;
#ifdef APPLE_EXTENSIONS
pbx_printf( "GMSG", "...interrupted\n" );
#else
printf( "...interrupted\n" );
#endif
}
int next_available_cmd_slot ()
{
int slot;
for( slot = 0; slot < MAXJOBS; slot++ )
if( !cmdtab[ slot ].pid )
return slot;
return -1;
}
void
execcmd( string, func, closure, shell, exportvars )
char *string;
void (*func)();
void *closure;
LIST *shell;
int exportvars;
{
int pid;
int slot;
char *argv[ MAXARGC + 1 ];
#ifdef APPLE_EXTENSIONS
int pipeForStdInToShell[2];
static char usePipeForShellCommands = -1;
#endif
#ifdef APPLE_EXTENSIONS
int output_pipe[2];
#endif
# if defined( NT ) || defined( __OS2__ )
static char *comspec;
char *p;
if( !comspec && !( comspec = getenv( "COMSPEC" ) ) )
comspec = "cmd.exe";
# endif
#ifdef APPLE_EXTENSIONS
if( usePipeForShellCommands == -1 ) {
if( getenv( "USE_CMDLINE_FOR_SHELL_COMMANDS" ) != NULL ) {
usePipeForShellCommands = 0;
} else {
usePipeForShellCommands = 1;
}
}
#endif
for( slot = 0; slot < MAXJOBS; slot++ )
if( !cmdtab[ slot ].pid )
break;
if( slot == MAXJOBS )
{
printf( "no slots for child!\n" );
exit( EXITBAD );
}
# if defined( NT ) || defined( __OS2__ )
if( !cmdtab[ slot ].tempfile )
{
char *tempdir;
if( !( tempdir = getenv( "TEMP" ) ) &&
!( tempdir = getenv( "TMP" ) ) )
tempdir = "\\temp";
cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 14 );
sprintf( cmdtab[ slot ].tempfile, "%s\\jamtmp%02d.bat",
tempdir, slot );
}
while( isspace( *string ) )
++string;
p = strchr( string, '\n' );
while( p && isspace( *p ) )
++p;
if( p && *p || strlen( string ) > MAXLINE )
{
FILE *f;
f = fopen( cmdtab[ slot ].tempfile, "w" );
fputs( string, f );
fclose( f );
string = cmdtab[ slot ].tempfile;
}
# endif
if( shell )
{
int i;
char jobno[4];
int gotpercent = 0;
sprintf( jobno, "%d", slot + 1 );
for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) )
{
switch( shell->string[0] )
{
case '%': argv[i] = string; gotpercent++; break;
case '!': argv[i] = jobno; break;
default: argv[i] = shell->string;
}
if( DEBUG_EXECCMD )
printf( "argv[%d] = '%s'\n", i, argv[i] );
}
if( !gotpercent )
argv[i++] = string;
argv[i] = 0;
}
else
{
# if defined( NT ) || defined( __OS2__ )
argv[0] = comspec;
argv[1] = "/Q/C";
argv[2] = string;
argv[3] = 0;
# else
argv[0] = "/bin/sh";
#ifdef APPLE_EXTENSIONS
if (usePipeForShellCommands) {
argv[1] = "-s";
argv[2] = NULL;
} else {
argv[1] = "-c";
argv[2] = string;
argv[3] = 0;
}
#else
argv[1] = "-c";
argv[2] = string;
argv[3] = 0;
#endif
# endif
}
if( !cmdsrunning++ )
istat = signal( SIGINT, onintr );
# if defined( NT ) || defined( __OS2__ )
if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) < 0 )
{
perror( "spawn" );
exit( EXITBAD );
}
# else
#ifdef APPLE_EXTENSIONS
if (usePipeForShellCommands) {
pipe(pipeForStdInToShell);
}
#endif
#ifdef APPLE_EXTENSIONS
if (PARSABLE_OUTPUT) {
pipe(output_pipe);
}
#endif
if ((pid = vfork()) == 0)
{
#ifdef APPLE_EXTENSIONS
if (usePipeForShellCommands) {
close(pipeForStdInToShell[1]);
dup2(pipeForStdInToShell[0], 0);
}
#endif
#ifdef APPLE_EXTENSIONS
if (PARSABLE_OUTPUT) {
close(output_pipe[0]);
dup2(output_pipe[1], 1);
dup2(output_pipe[1], 2);
}
#endif
if (exportvars)
var_setenv_all_exported_variables();
execvp( argv[0], argv );
_exit(127);
}
if( pid == -1 )
{
perror( "vfork" );
exit( EXITBAD );
}
# endif
cmdtab[ slot ].pid = pid;
cmdtab[ slot ].func = func;
cmdtab[ slot ].closure = closure;
#ifdef APPLE_EXTENSIONS
if (usePipeForShellCommands) {
close(pipeForStdInToShell[0]);
{
int toWrite = strlen(string);
int written = write(pipeForStdInToShell[1], string, toWrite);
if (toWrite != written) {
printf("Error: only wrote %d bytes of %d bytes of command to sub-shell.", written, toWrite);
}
}
close(pipeForStdInToShell[1]);
}
#endif
#ifdef APPLE_EXTENSIONS
if (PARSABLE_OUTPUT) {
cmdtab[slot].output_fd = output_pipe[0];
close(output_pipe[1]);
cmdtab[slot].output_capacity = 16 * 1024;
cmdtab[slot].output_buffer = malloc(cmdtab[slot].output_capacity);
cmdtab[slot].output_length = 0;
if (DEBUG_PARSABLE_OUTPUT) {
printf("cmdtab[%i] = {output_fd=%i, output_capacity=%i, output_buffer=%p, output_length=%u}\n", slot, cmdtab[slot].output_fd, cmdtab[slot].output_capacity, cmdtab[slot].output_buffer, cmdtab[slot].output_length);
}
}
else {
cmdtab[slot].output_fd = -1;
cmdtab[slot].output_capacity = 0;
cmdtab[slot].output_buffer = NULL;
cmdtab[slot].output_length = 0;
}
#endif
while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
if( !execwait() )
break;
}
#ifdef APPLE_EXTENSIONS
int find_cmd_slot_for_fd (int fd)
{
int i;
for (i = 0; i < MAXJOBS; i++) {
if (cmdtab[i].pid != 0 && cmdtab[i].output_fd == fd) {
return i;
}
}
return -1;
}
void emit_annotated_output_line (const char * line_prefix_string, const unsigned char * string, unsigned length, int add_newline_if_needed)
{
if (DEBUG_PARSABLE_OUTPUT) {
}
pbx_printf(line_prefix_string, "");
fwrite(string, length, 1, stdout);
if (add_newline_if_needed && (length == 0 || string[length-1] != '\n')) {
printf("\n");
}
}
void emit_annotated_output_lines_for_cmd_slot (int slot, int empty_the_buffer)
{
unsigned char line_prefix_string[5] = "RO00";
line_prefix_string[2] = '0' + slot / 10;
line_prefix_string[3] = '0' + slot % 10;
const char * buf_ptr = cmdtab[slot].output_buffer;
const char * first_unemitted_char = buf_ptr;
const char * buf_limit = buf_ptr + cmdtab[slot].output_length;
while (buf_ptr < buf_limit) {
while (buf_ptr < buf_limit && *buf_ptr++ != '\n');
if (buf_ptr < buf_limit || (buf_ptr == buf_limit && buf_ptr > first_unemitted_char && buf_ptr[-1] == '\n')) {
emit_annotated_output_line(line_prefix_string, first_unemitted_char, buf_ptr - first_unemitted_char, 0 );
first_unemitted_char = buf_ptr;
}
}
if (empty_the_buffer) {
if (buf_limit - first_unemitted_char > 0) {
emit_annotated_output_line(line_prefix_string, first_unemitted_char, buf_limit - first_unemitted_char, 1 );
}
cmdtab[slot].output_length = 0;
}
else {
unsigned num_remaining_chars = buf_limit - first_unemitted_char;
memcpy(cmdtab[slot].output_buffer, first_unemitted_char, num_remaining_chars);
cmdtab[slot].output_length = num_remaining_chars;
}
}
void append_to_cmd_slot_buffer (int slot, const char * bytes, unsigned length)
{
if (DEBUG_PARSABLE_OUTPUT) {
printf("append_to_cmd_slot_buffer(%i, %p, %u)\n", slot, bytes, length);
}
int capacity = cmdtab[slot].output_capacity;
while (cmdtab[slot].output_length + length > (unsigned)capacity) {
capacity += (capacity < 65536) ? capacity : 16384;
}
if (capacity != cmdtab[slot].output_capacity) {
cmdtab[slot].output_buffer = realloc(cmdtab[slot].output_buffer, capacity);
cmdtab[slot].output_capacity = capacity;
}
memcpy(cmdtab[slot].output_buffer + cmdtab[slot].output_length, bytes, length);
cmdtab[slot].output_length += length;
}
int fetch_data_from_cmd_in_slot (int slot)
{
if (DEBUG_PARSABLE_OUTPUT) {
printf("fetch_data_from_cmd_in_slot(%i)\n", slot);
}
unsigned char buffer[4096];
int result = read(cmdtab[slot].output_fd, buffer, sizeof(buffer));
if (result == 0) {
if (DEBUG_PARSABLE_OUTPUT) {
printf("closing %i\n", cmdtab[slot].output_fd);
}
close(cmdtab[slot].output_fd);
cmdtab[slot].output_fd = -1;
emit_annotated_output_lines_for_cmd_slot(slot, 1 );
return 0;
}
else if (result >= 0) {
append_to_cmd_slot_buffer(slot, buffer, result);
emit_annotated_output_lines_for_cmd_slot(slot, 0 );
}
return result;
}
void print_fd_set (const fd_set * fd_set_ptr, unsigned highest_fd)
{
unsigned i, is_first = 1;
for (i = 0; i <= highest_fd; i++) {
if (FD_ISSET(i, fd_set_ptr)) {
printf("%s%i", is_first ? "" : ",", i);
is_first = 0;
}
}
}
#endif
int
execwait()
{
int i;
int status, w;
int rstat;
#ifdef APPLE_EXTENSIONS
struct rusage ru;
#endif
if( !cmdsrunning )
return 0;
#ifdef APPLE_EXTENSIONS
int slot_to_wait_for = -1;
if (PARSABLE_OUTPUT) {
do {
fd_set readable_fd_set;
fd_set exception_fd_set;
int i, lowest_descriptor = 1024, highest_descriptor = -1;
FD_ZERO(&readable_fd_set);
FD_ZERO(&exception_fd_set);
for (i = 0; i < MAXJOBS; i++) {
if (cmdtab[i].pid != 0) {
if (cmdtab[i].output_fd >= 0) {
FD_SET(cmdtab[i].output_fd, &readable_fd_set);
FD_SET(cmdtab[i].output_fd, &exception_fd_set);
if (cmdtab[i].output_fd > highest_descriptor) {
highest_descriptor = cmdtab[i].output_fd;
}
if (cmdtab[i].output_fd < lowest_descriptor) {
lowest_descriptor = cmdtab[i].output_fd;
}
}
}
}
if (DEBUG_PARSABLE_OUTPUT) {
}
if (highest_descriptor < 0) {
break;
}
if (DEBUG_PARSABLE_OUTPUT) {
printf("select(num_bits=%i, readable_fd_set={", highest_descriptor+1);
print_fd_set(&readable_fd_set, highest_descriptor);
printf("}, writable_fd_set=NULL, exception_fd_set={");
print_fd_set(&exception_fd_set, highest_descriptor);
printf("}, timeout=NULL) -> ");
}
fflush(stdout);
int num_ready = select(highest_descriptor+1, &readable_fd_set, NULL, &exception_fd_set, NULL);
if (DEBUG_PARSABLE_OUTPUT) {
printf("num_ready=%i, readable_fd_set={", num_ready);
print_fd_set(&readable_fd_set, highest_descriptor);
printf("}, exception_fd_set={");
print_fd_set(&exception_fd_set, highest_descriptor);
printf("}");
if (num_ready < 0) {
printf(", errno=%i(%s)\n", errno, strerror(errno));
}
printf("\n");
}
if (num_ready < 0) {
break;
}
for (i = lowest_descriptor; i <= highest_descriptor; i++) {
if (FD_ISSET(i, &readable_fd_set)) {
int slot = find_cmd_slot_for_fd(i);
if (DEBUG_PARSABLE_OUTPUT) {
printf("find_cmd_slot_for_fd(%i) -> %i\n", i, slot);
}
if (slot >= 0) {
fetch_data_from_cmd_in_slot(slot);
if (cmdtab[slot].output_fd == -1) {
slot_to_wait_for = slot;
}
break;
}
}
}
for (i = lowest_descriptor; i <= highest_descriptor; i++) {
if (FD_ISSET(i, &exception_fd_set)) {
break;
}
}
}
while (slot_to_wait_for == -1);
}
#endif
if (DEBUG_PARSABLE_OUTPUT) {
printf("waitpid(cmdtab[%i].pid)\n", slot_to_wait_for);
}
#ifdef APPLE_EXTENSIONS
while( ( w = wait4( slot_to_wait_for >= 0 ? cmdtab[slot_to_wait_for].pid : -1, &status, 0, &ru ) ) == -1 && errno == EINTR )
;
#else
while( ( w = waitpid( slot_to_wait_for >= 0 ? cmdtab[slot_to_wait_for].pid : -1, &status, 0 ) ) == -1 && errno == EINTR )
;
#endif
if( w == -1 )
{
printf( "child process(es) lost!\n" );
perror("wait");
exit( EXITBAD );
}
for( i = 0; i < MAXJOBS; i++ )
if( w == cmdtab[ i ].pid )
break;
if( i == MAXJOBS )
{
printf( "waif child found!\n" );
exit( EXITBAD );
}
if( !--cmdsrunning )
signal( SIGINT, istat );
if( intr )
rstat = EXEC_CMD_INTR;
else if( w == -1 || status != 0 )
rstat = EXEC_CMD_FAIL;
else
rstat = EXEC_CMD_OK;
cmdtab[ i ].pid = 0;
#ifdef APPLE_EXTENSIONS
if( globs.enable_timings ) {
record_last_resource_usage( &ru );
}
if (cmdtab[i].output_fd != -1) {
close(cmdtab[i].output_fd);
cmdtab[i].output_fd = -1;
}
if (DEBUG_PARSABLE_OUTPUT) {
printf("getting rid of buffer for slot %i\n", i);
}
free(cmdtab[i].output_buffer), cmdtab[i].output_buffer = NULL;
cmdtab[i].output_capacity = 0;
cmdtab[i].output_length = 0;
#endif
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat );
return 1;
}
# if defined( NT ) && !defined( __BORLANDC__ )
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
static int
my_wait( status )
int *status;
{
int i, num_active = 0;
DWORD exitcode, waitcode;
static HANDLE *active_handles = 0;
if (!active_handles)
active_handles = (HANDLE *)malloc(globs.jobs * sizeof(HANDLE) );
for ( i = 0; i < globs.jobs; i++ ) {
if ( cmdtab[i].pid ) {
if ( GetExitCodeProcess((HANDLE)cmdtab[i].pid, &exitcode) ) {
if ( exitcode == STILL_ACTIVE )
active_handles[num_active++] = (HANDLE)cmdtab[i].pid;
else {
CloseHandle((HANDLE)cmdtab[i].pid);
*status = (int)((exitcode & 0xff) << 8);
return cmdtab[i].pid;
}
}
else
goto FAILED;
}
}
if ( !num_active ) {
errno = ECHILD;
return -1;
}
waitcode = WaitForMultipleObjects( num_active,
active_handles,
FALSE,
INFINITE );
if ( waitcode != WAIT_FAILED ) {
if ( waitcode >= WAIT_ABANDONED_0
&& waitcode < WAIT_ABANDONED_0 + num_active )
i = waitcode - WAIT_ABANDONED_0;
else
i = waitcode - WAIT_OBJECT_0;
if ( GetExitCodeProcess(active_handles[i], &exitcode) ) {
CloseHandle(active_handles[i]);
*status = (int)((exitcode & 0xff) << 8);
return (int)active_handles[i];
}
}
FAILED:
errno = GetLastError();
return -1;
}
# endif
# endif