/* * 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> /* 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 NO_VFORK if ((pid = fork()) == 0) { execvp( argv[0], argv ); _exit(127); } # else if ((pid = vfork()) == 0) { /* hpux doesn't like const here */ execvp( argv[0], (char **)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 ); } # 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 */
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#20 | 23298 | Nick Poole |
Updating Jam source The changes here include a simplified Windows build process: the makefile now has 2 MSVC sections, one 32-bit the other 64-bit. |
||
#19 | 9902 | Perforce staff |
Build jam without so many warnings on macosxx86_64. p4transfer.py: Transferred from production |
||
#18 | 9901 | Perforce staff |
Compute max line length on windows by OS type using GetVersionEx() call in execmax() function. Now instead of just 996 for the default, Win NT 4.0 and Win 2K get 2047, and Win XP and 2003+ get 8191. The wheels of progress grind slowly. Bumped JAMVERSION to 2.5.1. New feature documented in RELNOTES. p4transfer.py: Transferred from production |
||
#17 | 9900 | Perforce staff |
Maximum command line length now determined by execmax() function, which still just returns the compiled-in MAXLINE. p4transfer.py: Transferred from production |
||
#16 | 9899 | tony |
Port Jam to Zeta. Zeta is a fork of BeOS so this port is broadly the same as the BeOS port. p4transfer.py: Transferred from production |
||
#15 | 9898 | Perforce maintenance |
HPUX 11 jam porting: a cast for exec() and include unistd.h. Porting change. p4transfer.py: Transferred from production |
||
#14 | 3069 | Perforce staff |
Remove temp .bat files created on NT. Bug fix documented in RELNOTES. |
||
#13 | 2845 | rmg |
Make jam 2.5rc2 build with BorlandC 5.5 Porting Change, note in RELNOTES. === computer:1666: Change 40450 by anton@anton-diogenes on 2003/01/21 10:26:05 |
||
#12 | 2562 | rmg |
Grist jam temp file names with pid on NT so as to ensure uniqueness. Bug fix documented in RELNOTES. === computer:1666: Change 39577 by seiwald@tricks-seiwald on 2002/12/27 15:55:21 |
||
#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 |