execnt.c #3

  • //
  • guest/
  • miklos_fazekas/
  • jam/
  • src/
  • execnt.c
  • View
  • Commits
  • Open Download .zip Download (11 KB)
/*
 * Copyright 1993, 1995 Christopher Seiwald.
 * Copyright 2001		Miklos Fazekas ([email protected])
 *
 * This file is part of Jam - see jam.c for Copyright information.
 */
# include "jam.h"
# include "lists.h"
# include "parse.h"
# include "rules.h"
# include "execcmd.h"

# include <errno.h>

#include <Windows.h>

/*
 * execnt.c - execute a shell script on WinNT/Win32
 *
 * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp().
 * The default is:
 *
 *	cmd.exe /q/c %		[ WinNT/Win98 ]
 *
 * 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
 *
 */

/*
 * cmdtab, the command table of currently executing processes.
 */
static struct
{
	HANDLE	pid;				/* Process id */
	void	(*func)( void *closure, int status );
	void 	*closure;
	
	char*	tempfile;			/* Unique temporary batch file name */
	HANDLE	tempfilelock;		/* Unique temporary batch lock handle */

} cmdtab[ MAXJOBS ] = {{0}};
/*
 * winapierror - prints a text information of a winapi error
 */
static void printwinapierror(char *str)
{
	TCHAR errorText[1024];
	DWORD error = GetLastError();
	DWORD result = FormatMessage (
							FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_MAX_WIDTH_MASK ,
							NULL,	/* source */
							error,	/* message id */
							0,		/* lang id */
							errorText,
							1024,
							NULL);
	if (result == 0) {
		fprintf (stderr, "%s - GetLastError = %d\n",str,error);
	} else {
		fprintf (stderr, "%s - GetLastError = %s\n",str,errorText);
	}					
}
/*
 * iswin9x()
 */
static int iswin9x()
{
	static int is_win9x = 0;
	static int is_win9x_defined = 0;

	if (!is_win9x_defined) {
		OSVERSIONINFO  os_info;

		os_info.dwOSVersionInfoSize = sizeof(os_info);
		os_info.dwPlatformId        = VER_PLATFORM_WIN32_WINDOWS;
		GetVersionEx( &os_info );
  
		is_win9x         = (os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS);
		is_win9x_defined = 1;		
	}

	return is_win9x;
}

/*
 * globals
 */  
static int cmdsrunning = 0;

static int intr = 0;
static void  (__cdecl *istat)( int );
/*
 * onintr() - bump intr to note command interruption
 */
static void __cdecl
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 )
{
	char *p;
	int slot;
	char *programname = NULL;
	char *arguments = NULL;

	/* Find a slot in the running commands table for this one. */
	if (iswin9x()) 
	{
		/* only synchronous spans are supported */
		slot = 0;
	}
	else 
	{
		for( slot = 0; slot < MAXJOBS; slot++ )
			if( !cmdtab[ slot ].pid )
			break;
	
		if( slot == MAXJOBS )
		{
		    fprintf(stderr, "no slots for child!\n" );
			 exit( EXITBAD );
		}
	}

	/* Create new temporary file for slot */
	if (cmdtab[slot].tempfilelock == NULL) 
	{
		TCHAR		tempDirPath [_MAX_PATH] = {0} ;
		char*		tempfile ;

		/* Constans for temporary file generation */
		const char*			tempFileLockTemplate = "%sjam%04X.lck" ;
		const char*			tempFileNameTemplate = "%sjam%04X.bat" ;
		const unsigned int	maxTempFileNumber = 1024;
	
		/* Temp file number, increase it until an empty one is found */
		unsigned int tempFileIdx = 0;

		/* Get temporary path */
		if (GetTempPath (_MAX_PATH,tempDirPath) == 0) {
			printwinapierror("GetTempPath failed" );
			exit( EXITBAD );
		}
		
		tempfile = (char*)malloc (strlen(tempDirPath)+14);
		if (tempfile == NULL) 
		{
			fprintf(stderr, "Out of memory!\n" );
			exit( EXITBAD );			
		}

		cmdtab[slot].tempfilelock = NULL;
		cmdtab[slot].tempfile = NULL;
		

		while (cmdtab[slot].tempfilelock == NULL) 
		{
			TCHAR	tempFileLockPath[_MAX_PATH] ;
			
			/* Check temporary idx */
			if (tempFileIdx >= maxTempFileNumber) 
			{
				fprintf(stderr, "All temporary files are used, or bad temporary folder!\n" );
				exit( EXITBAD );					
			}

			/* Try to create a lock file */
			sprintf (tempFileLockPath, tempFileLockTemplate,tempDirPath,tempFileIdx); 
			
			/* Try to create the file: */
			cmdtab[slot].tempfilelock = 
				CreateFile (tempFileLockPath, 
					GENERIC_WRITE | DELETE,					/* dwDesiredAccess */
					0,										/* shareMode */
					NULL,									/* security */
					CREATE_NEW,								/* how to create */
					FILE_FLAG_DELETE_ON_CLOSE,				/* file attributes */
					NULL);									

			if (cmdtab[slot].tempfilelock == INVALID_HANDLE_VALUE) 
				cmdtab[slot].tempfilelock = NULL;

			if (cmdtab[slot].tempfilelock != NULL) {
				sprintf (tempfile, tempFileNameTemplate,tempDirPath,tempFileIdx); 
				cmdtab[slot].tempfile = tempfile;
			}

			tempFileIdx ++;
		}
	}
	/* Trim leading/trailing whitespaces */
	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 )
	{	
		/* write command to temp file */
		FILE *f = fopen(cmdtab[slot].tempfile,"w");
		if (! f) 
		{
			fprintf (stderr, "Cannot open file named:%s!",cmdtab[slot].tempfile);
			exit (EXITBAD);
		}
		fputs( string, f );
	    fclose( f );
	    string = (char*)cmdtab[slot].tempfile;
	}
	/* Forumulate argv */
	/* If shell was defined, be prepared for % and ! subs. */
	/* Otherwise, use stock cmd.exe */
	programname = NULL;
	arguments = NULL;
	if( shell )
	{
		char *argv[ MAXARGC + 1 ];	/* +1 for NULL */
	    int i;
		int j;
	    char jobno[4];
	    int gotpercent = 0;
		int argumentslen = 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] );
			argumentslen += strlen(argv[i])+1;
		}

		if( !gotpercent )
			argv[i++] = string;

		argv[i] = 0;

		/* Fill in progrmname, and arguments */
		programname = argv[0] ;
		arguments = (char*)malloc(argumentslen+1);
		if (arguments == NULL) 
		{
			fprintf(stderr, "Out of memory %d!\n",argumentslen+1);
			exit( EXITBAD );					
		}

		strcpy (arguments, argv[0]);
		for (j = 1; j < i; j++)
		{
			strcat (arguments," ");
			strcat(arguments,argv[j]);				
		}
	}
	else
	{
		/* this is ignored on Win95/98, see later.. */
		const char* shellarguments = "/Q/C" ; 	/* anything more is non-portable */
	    programname = "cmd.exe";
	    arguments = (char*)malloc (strlen(programname)+1+strlen(shellarguments)+1+strlen(string)+1);
		if (arguments == NULL) 
		{
			fprintf(stderr, "Not enough memory!\n" );
			exit( EXITBAD );					
		}
		sprintf(arguments,"%s %s %s",programname,shellarguments,string);
	}

	/* Catch interrupts whenever commands are running. */

	if( !cmdsrunning++ )
	    istat = signal( SIGINT, onintr );

	/* Start the command */
	{
		STARTUPINFO				startupInfo = {0};
		PROCESS_INFORMATION		processInfo = {0};
		BOOL					result ;
		startupInfo.cb = sizeof(startupInfo);
		startupInfo.lpDesktop	= NULL;      /* Default desktop */
		startupInfo.hStdOutput	= GetStdHandle(STD_OUTPUT_HANDLE);	// Standard handle for output
		startupInfo.hStdError	= GetStdHandle(STD_ERROR_HANDLE);	// Standard handle for error
		startupInfo.hStdInput	= GetStdHandle(STD_INPUT_HANDLE);	// Standard handle for input
		startupInfo.dwFlags		= STARTF_USESTDHANDLES;

		result = CreateProcess (
						NULL,			/* Name of executable */
						arguments,		/* CommandLine */
						NULL,			/* Process attributes */
						NULL,			/* Thread attributes */
						TRUE,			/* Inherit handles */
						NORMAL_PRIORITY_CLASS , /* Creation flags */
						NULL,			/* Environment */
						NULL,			/* Current directory */
						&startupInfo,
						&processInfo
						); 
		free (arguments);
		if (! result )
		{
			cmdtab[slot].pid = 0;
			printwinapierror("CreateProcess failed !");
			exit( EXITBAD );		
		}
		if (processInfo.hThread != 0) {
			CloseHandle (processInfo.hThread);
			processInfo.hThread = 0;
		}
		cmdtab[slot].pid = processInfo.hProcess;	
	}

	/* Save the operation for execwait() to find. */
	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 rstat;
	int numjobs;
	HANDLE	jobs[MAXJOBS];
	int		jobslot[MAXJOBS];
	DWORD	result;
	int		terminatedslot = -1;
	DWORD	exitcode ;
	/* Handle naive make1() which doesn't know if cmds are running. */
	if( !cmdsrunning )
	    return 0;
	
	if (iswin9x())
		return 0;

	numjobs = 0;
	for (i = 0; i < MAXJOBS; i++) 
	{
		if (cmdtab[i].pid) {
			jobslot[numjobs] = i;
			jobs[numjobs++] = cmdtab[i].pid;
		}
	}

	while (terminatedslot == -1)
	{
		result = WaitForMultipleObjects (numjobs, jobs, FALSE, INFINITE);
		
		if (result == WAIT_FAILED) {
		    fprintf(stderr, "Child process(es) lost!\n" );
		    exit( EXITBAD );		
		}

		if ((result >= WAIT_OBJECT_0) && (result < WAIT_OBJECT_0+numjobs)) 
		{
			HANDLE pid = jobs[result-WAIT_OBJECT_0];
			if (GetExitCodeProcess (pid, &exitcode)) 
			{
				if (exitcode != STILL_ACTIVE) 
					terminatedslot = jobslot[result-WAIT_OBJECT_0];	
			} 
			else 
			{
				printwinapierror ("GetExitCodeProcess failed");
				exit (EXITBAD);
			}
		} 
		else if ((result >= WAIT_ABANDONED_0) && (result < WAIT_ABANDONED_0+numjobs)) 
		{
			HANDLE pid = jobs[result-WAIT_ABANDONED_0];
			if (GetExitCodeProcess (pid, &exitcode)) 
			{
				if (exitcode == STILL_ACTIVE) {
					fprintf(stderr, "Unexpected result (STILL_ACTIVE) from GetExitCodeProcess!\n" );
					exit( EXITBAD );	
				}
				terminatedslot = jobslot[result-WAIT_ABANDONED_0];	
			}
			else 
			{
				printwinapierror ("GetExitCodeProcess failed");
				exit (EXITBAD);
			}
		} 
		else if (result == WAIT_TIMEOUT) 
		{
		    fprintf(stderr, "Unexpected result (WAIT_TIMEOUT) from WaitForMultipleObjects!\n" );
		    exit( EXITBAD );	
		} 
		else 
		{
		    fprintf(stderr, "Child process(es) lost!\n" );
		    exit( EXITBAD );					
		}
	}

	if (terminatedslot < 0) 
	{
		fprintf(stderr, "Child process(es) lost!\n" );
		exit( EXITBAD );				
	}
	CloseHandle(cmdtab[terminatedslot].pid);
	cmdtab[terminatedslot].pid = 0;

	if( !--cmdsrunning )
	    signal( SIGINT, istat );

	if( intr )
	    rstat = EXEC_CMD_INTR;
	else if(exitcode != 0)
	    rstat = EXEC_CMD_FAIL;
	else
	    rstat = EXEC_CMD_OK;

	(*cmdtab[ terminatedslot ].func)( cmdtab[ terminatedslot ].closure, rstat );
	cmdtab[terminatedslot].func = NULL;
	cmdtab[terminatedslot].closure = NULL;
	return 1;
}
# Change User Description Committed
#4 2539 Miklos Fazekas Updated sources
#3 1573 Miklos Fazekas Merge to jam mainline.
#2 1398 Miklos Fazekas Cleanup.
#1 1217 Miklos Fazekas The following changes were made to execunix.c:
- Fixed bug with running two jam at the same time (temp .bat files got messed)
- Used native Win32 tools instead of unix emulation.