* Copyright 1993, 1995 Christopher Seiwald.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
* execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS
*
* If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp().
* The default is:
*
* /bin/sh -c % [ on UNIX/AmigaOS ]
* cmd.exe /c % [ on OS2/WinNT ]
*
* Each word must be an individual element in a jam variable value.
*
* In $(JAMSHELL), % expands to the command string and ! expands to
* the slot number (starting at 1) for multiprocess (-j) invocations.
* If $(JAMSHELL) doesn't include a %, it is tacked on as the last
* argument.
*
* Don't just set JAMSHELL to /bin/sh or cmd.exe - it won't work!
*
* External routines:
* execcmd() - launch an async command execution
* execwait() - wait and drive at most one execution completion
*
* Internal routines:
* onintr() - bump intr to note command interruption
*
* 04/08/94 (seiwald) - Coherent/386 support added.
* 05/04/94 (seiwald) - async multiprocess interface
* 01/22/95 (seiwald) - $(JAMSHELL) support
* 06/02/97 (gsar) - full async multiprocess support for Win32
* 01/20/00 (seiwald) - Upgraded from K&R to ANSI C
* 11/04/02 (seiwald) - const-ing for string literals
* 12/27/02 (seiwald) - grist .bat file with pid for system uniqueness
*/
# include "jam.h"
# include "lists.h"
# include "execcmd.h"
# include <errno.h>
# ifdef USE_EXECUNIX
# ifdef OS_OS2
# define USE_EXECNT
# include <process.h>
# endif
# ifdef OS_NT
# define USE_EXECNT
# include <process.h>
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
# define USE_MYWAIT
# if !defined( __BORLANDC__ )
# define wait my_wait
static int my_wait( int *status );
# endif
# endif
static int intr = 0;
static int cmdsrunning = 0;
static void (*istat)( int );
static struct
{
int pid;
void (*func)( void *closure, int status );
void *closure;
# ifdef USE_EXECNT
char *tempfile;
# endif
} cmdtab[ MAXJOBS ] = {{0}};
* onintr() - bump intr to note command interruption
*/
void
onintr( int disp )
{
intr++;
printf( "...interrupted\n" );
}
* execcmd() - launch an async command execution
*/
void
execcmd(
char *string,
void (*func)( void *closure, int status ),
void *closure,
LIST *shell )
{
int pid;
int slot;
const char *argv[ MAXARGC + 1 ];
# ifdef USE_EXECNT
char *p;
# endif
for( slot = 0; slot < MAXJOBS; slot++ )
if( !cmdtab[ slot ].pid )
break;
if( slot == MAXJOBS )
{
printf( "no slots for child!\n" );
exit( EXITBAD );
}
# ifdef USE_EXECNT
if( !cmdtab[ slot ].tempfile )
{
char *tempdir;
if( !( tempdir = getenv( "TEMP" ) ) &&
!( tempdir = getenv( "TMP" ) ) )
tempdir = "\\temp";
cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 32 );
sprintf( cmdtab[ slot ].tempfile, "%s\\jam%dt%d.bat",
tempdir, GetCurrentProcessId(), slot );
}
while( isspace( *string ) )
++string;
p = strchr( string, '\n' );
while( p && isspace( *p ) )
++p;
if( p && *p || strlen( string ) > MAXLINE || shell )
{
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
{
# ifdef USE_EXECNT
argv[0] = "cmd.exe";
argv[1] = "/Q/C";
# else
argv[0] = "/bin/sh";
argv[1] = "-c";
# endif
argv[2] = string;
argv[3] = 0;
}
if( !cmdsrunning++ )
istat = signal( SIGINT, onintr );
# ifdef USE_EXECNT
if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
{
perror( "spawn" );
exit( EXITBAD );
}
# else
# ifdef NO_VFORK
if ((pid = fork()) == 0)
{
execvp( argv[0], argv );
_exit(127);
}
# else
if ((pid = vfork()) == 0)
{
execvp( argv[0], argv );
_exit(127);
}
# endif
if( pid == -1 )
{
perror( "vfork" );
exit( EXITBAD );
}
# endif
cmdtab[ slot ].pid = pid;
cmdtab[ slot ].func = func;
cmdtab[ slot ].closure = closure;
while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
if( !execwait() )
break;
}
* execwait() - wait and drive at most one execution completion
*/
int
execwait()
{
int i;
int status, w;
int rstat;
if( !cmdsrunning )
return 0;
do
{
while( ( w = wait( &status ) ) == -1 && errno == EINTR )
;
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( "jam: waif child process %ld found, ignoring it!\n", w );
} while( i == MAXJOBS );
# ifdef USE_EXECNT
unlink( cmdtab[ i ].tempfile );
# endif
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;
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat );
return 1;
}
# ifdef USE_MYWAIT
static int
my_wait( 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) );
* and return if so.
*/
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