mirror of
https://review.haiku-os.org/buildtools
synced 2024-11-23 07:18:49 +01:00
cc988c8c90
Warning was: Cc bin.linuxx86/execunix.o execunix.c: In function ‘execwait’: execunix.c:301:19: warning: implicit declaration of function ‘wait’ [-Wimplicit-function-declaration] 301 | while( ( w = wait( &status ) ) == -1 && errno == EINTR ) | Change-Id: I7c0e53db8d18e9e6cb317980cb9be9026b7fce20 Reviewed-on: https://review.haiku-os.org/c/buildtools/+/4400 Reviewed-by: Jérôme Duval <jerome.duval@gmail.com>
409 lines
8.2 KiB
C
409 lines
8.2 KiB
C
/*
|
|
* 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
|
|
|
|
# if defined(__linux__) || defined(__HAIKU__)
|
|
# define USE_POSIX_SPAWN
|
|
# endif
|
|
|
|
# ifdef unix
|
|
# include <unistd.h>
|
|
# include <sys/wait.h>
|
|
|
|
# ifdef USE_POSIX_SPAWN
|
|
extern char **environ;
|
|
# include <spawn.h>
|
|
# endif
|
|
|
|
# endif
|
|
|
|
# ifdef OS_NT
|
|
# define USE_EXECNT
|
|
# include <process.h>
|
|
# define WIN32_LEAN_AND_MEAN
|
|
# include <windows.h> /* do the ugly deed */
|
|
# 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; /* on win32, a real process handle */
|
|
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 ]; /* +1 for NULL */
|
|
|
|
# ifdef USE_EXECNT
|
|
char *p;
|
|
# endif
|
|
|
|
/* Find a slot in the running commands table for this one. */
|
|
|
|
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";
|
|
|
|
/* +32 is room for \jamXXXXXtSS.bat (at least) */
|
|
|
|
cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 32 );
|
|
|
|
sprintf( cmdtab[ slot ].tempfile, "%s\\jam%dt%d.bat",
|
|
tempdir, GetCurrentProcessId(), slot );
|
|
}
|
|
|
|
/* Trim leading, ending white space */
|
|
|
|
while( isspace( *string ) )
|
|
++string;
|
|
|
|
p = strchr( string, '\n' );
|
|
|
|
while( p && isspace( *p ) )
|
|
++p;
|
|
|
|
/* If multi line, or too long, or JAMSHELL is set, write to bat file. */
|
|
/* Otherwise, exec directly. */
|
|
/* Frankly, if it is a single long line I don't think the */
|
|
/* command interpreter will do any better -- it will fail. */
|
|
|
|
if( p && *p || strlen( string ) > MAXLINE || shell )
|
|
{
|
|
FILE *f;
|
|
|
|
/* Write command to bat file. */
|
|
|
|
f = fopen( cmdtab[ slot ].tempfile, "w" );
|
|
fputs( string, f );
|
|
fclose( f );
|
|
|
|
string = cmdtab[ slot ].tempfile;
|
|
}
|
|
# endif
|
|
|
|
/* Forumulate argv */
|
|
/* If shell was defined, be prepared for % and ! subs. */
|
|
/* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */
|
|
|
|
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"; /* anything more is non-portable */
|
|
# else
|
|
argv[0] = "/bin/sh";
|
|
argv[1] = "-c";
|
|
# endif
|
|
argv[2] = string;
|
|
argv[3] = 0;
|
|
}
|
|
|
|
/* Catch interrupts whenever commands are running. */
|
|
|
|
if( !cmdsrunning++ )
|
|
istat = signal( SIGINT, onintr );
|
|
|
|
/* Start the command */
|
|
|
|
# ifdef USE_EXECNT
|
|
if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
|
|
{
|
|
perror( "spawn" );
|
|
exit( EXITBAD );
|
|
}
|
|
# else
|
|
# ifdef USE_POSIX_SPAWN
|
|
if (posix_spawnp(&pid, argv[0], NULL, NULL, (char * const*)argv, environ) != 0)
|
|
{
|
|
perror( "posix_spawnp" );
|
|
exit( EXITBAD );
|
|
}
|
|
# else
|
|
# ifdef NO_VFORK
|
|
if ((pid = fork()) == 0)
|
|
{
|
|
execvp( argv[0], argv );
|
|
_exit(127);
|
|
}
|
|
# else
|
|
if ((pid = vfork()) == 0)
|
|
{
|
|
execvp( argv[0], (char* const*)argv );
|
|
_exit(127);
|
|
}
|
|
# endif
|
|
|
|
if( pid == -1 )
|
|
{
|
|
perror( "vfork" );
|
|
exit( EXITBAD );
|
|
}
|
|
# endif
|
|
# endif
|
|
/* Save the operation for execwait() to find. */
|
|
|
|
cmdtab[ slot ].pid = pid;
|
|
cmdtab[ slot ].func = func;
|
|
cmdtab[ slot ].closure = closure;
|
|
|
|
/* Wait until we're under the limit of concurrent commands. */
|
|
/* Don't trust globs.jobs alone. */
|
|
|
|
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;
|
|
|
|
/* Handle naive make1() which doesn't know if cmds are running. */
|
|
|
|
if( !cmdsrunning )
|
|
return 0;
|
|
|
|
do
|
|
{
|
|
/* Pick up process pid and status */
|
|
|
|
while( ( w = wait( &status ) ) == -1 && errno == EINTR )
|
|
;
|
|
|
|
if( w == -1 )
|
|
{
|
|
printf( "child process(es) lost!\n" );
|
|
perror("wait");
|
|
exit( EXITBAD );
|
|
}
|
|
|
|
/* Find the process in the cmdtab. */
|
|
|
|
for( i = 0; i < MAXJOBS; i++ )
|
|
if( w == cmdtab[ i ].pid )
|
|
break;
|
|
|
|
if( i == MAXJOBS )
|
|
printf( "jam: waif child process %d found, ignoring it!\n", w );
|
|
} while( i == MAXJOBS );
|
|
|
|
# ifdef USE_EXECNT
|
|
/* Clear the temp file */
|
|
|
|
unlink( cmdtab[ i ].tempfile );
|
|
# endif
|
|
|
|
/* Drive the completion */
|
|
|
|
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) );
|
|
|
|
/* first see if any non-waited-for processes are dead,
|
|
* 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 a child exists, wait for it to die */
|
|
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 /* USE_MYWAIT */
|
|
|
|
# endif /* USE_EXECUNIX */
|