/* * Copyright 1993, 1995 Christopher Seiwald. * Copyright 2001 Miklos Fazekas (boga@mac.com) * * 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 */ static void createtemporaryfilelock (int slot) { 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 ++; } } /* * execcmdimp() - launch an async command execution (extraced for saving stack space) */ static void execcmdimp( char *string, void (*func)( void *closure, int status ), void *closure, LIST *shell ) { char *p; int slot; const 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) { createtemporaryfilelock (slot); } /* 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 ) { const 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; } /* * execcmd() - launch an async command execution */ void execcmd( char *string, void (*func)( void *closure, int status ), void *closure, LIST *shell ) { execcmdimp (string, func, closure, shell); /* 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; } } /* * execwaitimp() - wait and drive at most one execution completion */ static int execwaitimp (int* oterminatedslot, int* orstat) { 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; *oterminatedslot = terminatedslot; *orstat = rstat; return 1; } /* * execwait() - wait and drive at most one execution completion */ int execwait() { int rstat = 0; int terminatedslot = -1; int result = 0; result = execwaitimp (&terminatedslot, &rstat); if (result == 1) { (*cmdtab[ terminatedslot ].func)( cmdtab[ terminatedslot ].closure, rstat ); cmdtab[terminatedslot].func = NULL; cmdtab[terminatedslot].closure = NULL; } return result; }
# | 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. |