execunix.c #2

  • //
  • guest/
  • miklos_fazekas/
  • jamfix/
  • execunix.c
  • View
  • Commits
  • Open Download .zip Download (8 KB)
/*
 * 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>
# if !defined( __BORLANDC__ ) 
# define WIN32_LEAN_AND_MEAN
# include <windows.h>		/* do the ugly deed */
# define USE_MYWAIT
# 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 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
	/* 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;

	/* 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( "waif child found!\n" );
	    exit( EXITBAD );
	}

	/* 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 */
# Change User Description Committed
#2 2576 Miklos Fazekas Integrate to jam 2.5 rc1
#1 2522 Miklos Fazekas Graphisoft bugfix jam
//guest/perforce_software/jam/src/execunix.c
#11 2493 rmg Rewrite the past: update all jam's source with comments to
reflect changes since about 2.3, very early 2001.

Whitespace only change.

=== computer:1666: Change 37660 by seiwald@play-seiwald on 2002/11/06 22:41:35

Note: I regenerated jamgram.c on my linux 7.3 system prior to
the submit, since patch was so unhappy trying to lay down the
changes from Christopher's change. Presumably this is just due to
different yacc/bison/whatever particulars on the system where
Christopher made the changes originally. - rmg
#10 2491 rmg Some consting in jam to make it more compilable by C++ compilers.

No functional change.

=== computer:1666: Change 37433 by perforce@perforce on 2002/10/30 16:08:51

Recreational const-ing of jam, for compilers that don't allow
"string" to be passed as a non-const char *.

This included a few places where we were modifying what could
possibly have been read-only storage, oddly enough.

No functional change.

=== computer:1666: Change 37602 by seiwald@play-seiwald on 2002/11/04 17:25:40
#9 1351 rmg This change is integration history only.
(Accept -ay'ed
integrating:

    Change 216 by peter_glasscock@peter_glasscock on 1999/07/27 03:25:01

    Integrate recent changes to public source

So, these were apparently Matt's tweaks to changes being
integrated from //public/jam, and we'll ignore them here.
#8 1350 rmg No change - this is just to record integration history.
(Resolve -ay'ed from Change 292 by Matt Armstrong
#7 486 Perforce staff Jam 2.3.
 See RELNOTES for a list of changes from 2.2.x.

Just about every source file was touched when jam got ANSI-fied.
#6 212 Perforce staff An interpretative integration of Peter Glasscock's -o file support.
This is handled in the make1() routine, rather than in all the
exec*.c files.  -o x writes the actions to file x rather than
actually running them.  Implies -n (but not -d2).
#5 67 Laura Wingerd Integrate Perforce's jam changes & project page update
(change 59, change 60, change 61, change 62, change 63, change 64,
change 66)
#4 5 Perforce maintenance Jam/MR 2.2.4 (HDRPATTERN, JAMUNAME, JAMSHELL, plus misc tweaks)
#3 4 Perforce maintenance Jam/MR 2.2.2 (AmigaOS support)
#2 3 Perforce maintenance Jam/MR 2.2.1 (fix for NT handle leak)
#1 2 laura Add Jam/MR 2.2 source