From a34669c4625e65ce2d7abf68efda5ad9ac959dbd Mon Sep 17 00:00:00 2001 From: Rene Rivera Date: Mon, 10 Oct 2005 20:33:22 +0000 Subject: [PATCH] Add "-lx" option which limits the amount of time actions can run (in seconds). Currently only implemented for Windows NT and above. [SVN r31266] --- src/engine/execnt.c | 137 ++++++++++++++++++++++++++++++++++++++++++-- src/engine/jam.c | 11 +++- src/engine/jam.h | 1 + 3 files changed, 142 insertions(+), 7 deletions(-) diff --git a/src/engine/execnt.c b/src/engine/execnt.c index 27f962f72..3d929bbd8 100644 --- a/src/engine/execnt.c +++ b/src/engine/execnt.c @@ -24,6 +24,9 @@ # define WIN32_LEAN_AND_MEAN # include /* do the ugly deed */ # include +# if !defined( __BORLANDC__ ) +# include +# endif # if !defined( __BORLANDC__ ) && !defined( OS_OS2 ) # define wait my_wait @@ -916,6 +919,107 @@ check_process_exit( return result; } +static double +running_time(HANDLE process) +{ + FILETIME creation, exit, kernel, user, current; + if (GetProcessTimes(process, &creation, &exit, &kernel, &user)) + { + /* Compute the elapsed time */ + GetSystemTimeAsFileTime(¤t); + { + double delta = filetime_seconds( + add_FILETIME( current, negate_FILETIME(creation) ) + ); + return delta; + } + } + return 0.0; +} + +/* it's just stupidly silly that one has to do this! */ +typedef struct PROCESS_BASIC_INFORMATION__ { + LONG ExitStatus; + PVOID PebBaseAddress; + ULONG AffinityMask; + LONG BasePriority; + ULONG UniqueProcessId; + ULONG InheritedFromUniqueProcessId; + } PROCESS_BASIC_INFORMATION_; +typedef LONG (__stdcall * NtQueryInformationProcess__)( + HANDLE ProcessHandle, + LONG ProcessInformationClass, + PVOID ProcessInformation, + ULONG ProcessInformationLength, + PULONG ReturnLength); +static NtQueryInformationProcess__ NtQueryInformationProcess_ = NULL; +static HMODULE NTDLL_ = NULL; +DWORD get_process_id(HANDLE process) +{ + PROCESS_BASIC_INFORMATION_ pinfo; + if ( ! NtQueryInformationProcess_ ) + { + if ( ! NTDLL_ ) + { + NTDLL_ = GetModuleHandleA("ntdll"); + } + if ( NTDLL_ ) + { + NtQueryInformationProcess_ + = (NtQueryInformationProcess__)GetProcAddress( NTDLL_,"NtQueryInformationProcess" ); + } + } + if ( NtQueryInformationProcess_ ) + { + LONG r = (*NtQueryInformationProcess_)( + process,/* ProcessBasicInformation == */ 0,&pinfo,sizeof(PROCESS_BASIC_INFORMATION_),NULL); + return pinfo.UniqueProcessId; + } + else + { + return 0; + } +} + +/* not really optimal, or efficient, but it's easier this way, and it's not +like we are going to be killing thousands, or even tens or processes. */ +static void +kill_all(DWORD pid, HANDLE process) +{ + HANDLE process_snapshot_h = INVALID_HANDLE_VALUE; + if ( !pid ) + { + pid = get_process_id(process); + } + process_snapshot_h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0); + + if (INVALID_HANDLE_VALUE != process_snapshot_h) + { + BOOL ok = TRUE; + PROCESSENTRY32 pinfo; + pinfo.dwSize = sizeof(PROCESSENTRY32); + for ( + ok = Process32First(process_snapshot_h,&pinfo); + TRUE == ok; + ok = Process32Next(process_snapshot_h,&pinfo) ) + { + if (pinfo.th32ParentProcessID == pid) + { + /* found a child, recurse to kill it and anything else below it */ + HANDLE ph = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pinfo.th32ProcessID); + if (NULL != ph) + { + kill_all(pinfo.th32ProcessID,ph); + CloseHandle(ph); + } + } + } + CloseHandle(process_snapshot_h); + } + /* now that the children are all dead, kill the root */ + TerminateProcess(process,-2); +} + static int my_wait( int *status ) { @@ -949,10 +1053,35 @@ my_wait( int *status ) return -1; } - waitcode = WaitForMultipleObjects( num_active, - active_handles, - FALSE, - INFINITE ); + if ( globs.timeout > 0 ) + { + /* with a timeout we wait for a finish or a timeout, we check every second + to see if something timed out */ + for (waitcode = WAIT_TIMEOUT; waitcode == WAIT_TIMEOUT;) + { + waitcode = WaitForMultipleObjects( num_active, active_handles, FALSE, 1*1000 /* 1 second */ ); + if ( waitcode == WAIT_TIMEOUT ) + { + /* check if any jobs have surpassed the maximum run time. */ + for ( i = 0; i < num_active; ++i ) + { + double t = running_time(active_handles[i]); + if ( t > (double)globs.timeout ) + { + /* we have a "runaway" job, kill it */ + kill_all(0,active_handles[i]); + /* indicate the job "finished" so we query its status below */ + waitcode = WAIT_ABANDONED_0+i; + } + } + } + } + } + else + { + /* no timeout, so just wait indefinately for something to finish */ + waitcode = WaitForMultipleObjects( num_active, active_handles, FALSE, INFINITE ); + } if ( waitcode != WAIT_FAILED ) { if ( waitcode >= WAIT_ABANDONED_0 diff --git a/src/engine/jam.c b/src/engine/jam.c index 646402929..0ccd2aadc 100644 --- a/src/engine/jam.c +++ b/src/engine/jam.c @@ -146,7 +146,8 @@ struct globs globs = { # else { 0, 1 }, /* debug ... */ # endif - 0 /* output commands, not run them */ + 0, /* output commands, not run them */ + 0 /* action timeout */ } ; /* Symbols to be defined as true for use in Jambase */ @@ -241,7 +242,7 @@ int main( int argc, char **argv, char **arg_environ ) argc--, argv++; - if( getoptions( argc, argv, "-:d:j:f:gs:t:ano:qv", optv ) < 0 ) + if( getoptions( argc, argv, "-:l:d:j:f:gs:t:ano:qv", optv ) < 0 ) { printf( "\nusage: %s [ options ] targets...\n\n", progname ); @@ -250,6 +251,7 @@ int main( int argc, char **argv, char **arg_environ ) printf( "-fx Read x instead of Jambase.\n" ); /* printf( "-g Build from newest sources first.\n" ); */ printf( "-jx Run up to x shell commands concurrently.\n" ); + printf( "-lx Limit actions to x number of seconds after which they are stopped.\n" ); printf( "-n Don't actually execute the updating actions.\n" ); printf( "-ox Write the updating actions to file x.\n" ); printf( "-q Quit quickly as soon as a target fails.\n" ); @@ -292,6 +294,9 @@ int main( int argc, char **argv, char **arg_environ ) if( ( s = getoptval( optv, 'g', 0 ) ) ) globs.newestfirst = 1; + if( ( s = getoptval( optv, 'l', 0 ) ) ) + globs.timeout = atoi( s ); + /* Turn on/off debugging */ for( n = 0; s = getoptval( optv, 'd', n ); n++ ) @@ -423,7 +428,7 @@ int main( int argc, char **argv, char **arg_environ ) { if ( arg_v[n][0] == '-' ) { - char *f = "-:d:j:f:gs:t:ano:qv"; + char *f = "-:l:d:j:f:gs:t:ano:qv"; for( ; *f; f++ ) if( *f == arg_v[n][1] ) break; if ( f[1] == ':' && arg_v[n][2] == '\0' ) { ++n; } } diff --git a/src/engine/jam.h b/src/engine/jam.h index ec9cd5f99..b2cabe928 100644 --- a/src/engine/jam.h +++ b/src/engine/jam.h @@ -520,6 +520,7 @@ struct globs { int newestfirst; /* build newest sources first */ char debug[DEBUG_MAX]; FILE *cmdout; /* print cmds, not run them */ + long timeout; /* number of seconds to limit actions to, default 0 for no limit. */ } ; extern struct globs globs;