2008-03-05 09:52:00 -05:00
// ----------------------------------------------------------------------------
//
// Program: RunAll
//
// Purpose: Run all processes simultaneously with optional fatal error
// on any process failure or timeout. This is intended to be
// used by the automated build process to wrapper running
// bootd and the kernel debugger side-by-side.
//
2008-11-17 18:29:00 -05:00
// Copyright (c) Microsoft Corporation. All Rights Reserved.
2008-03-05 09:52:00 -05:00
//
// ----------------------------------------------------------------------------
# define _WIN32_WINNT 0x0500
# include <winlean.h>
# include <limits.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <time.h>
static BOOL g_bVerbose = FALSE ;
# define tprintf if (g_bVerbose) printf
static BOOL WINAPI CtrlHandler ( DWORD dwCtrlType )
{
printf ( " Caught control event %I32u " , dwCtrlType ) ;
switch ( dwCtrlType ) {
case CTRL_C_EVENT :
case CTRL_BREAK_EVENT :
GenerateConsoleCtrlEvent ( dwCtrlType , 0 ) ;
default :
break ;
}
ExitProcess ( ( UINT ) - 2 ) ;
}
static void
ErrorReport ( char * function )
{
VOID * message ;
DWORD error = GetLastError ( ) ;
FormatMessage (
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM ,
NULL ,
error ,
MAKELANGID ( LANG_NEUTRAL , SUBLANG_DEFAULT ) ,
( LPTSTR ) & message ,
0 , NULL ) ;
printf ( " runall: %s failed with error %d: %s " ,
function , error , message ) ;
LocalFree ( message ) ;
}
void usage ( )
{
printf ( " Usage: runall [options] cmd [cmd1] ... \n \n " ) ;
printf ( " Options: \n " ) ;
2008-11-17 18:29:00 -05:00
printf ( " /L:logdir - Write output from processes to logdir (must exist). \n " ) ;
printf ( " /T:seconds - Set timeout in seconds. \n " ) ;
printf ( " \n Runs specified commands in parallel and wait for all to terminate successfully. \n \n The program returns -1 if any process fails to launch, or exits with a non zero error-code, or the option timeout expires. " ) ;
2008-03-05 09:52:00 -05:00
}
static BOOL
ParseInt64 ( LPCSTR lpszValue , PINT64 pValue )
{
PCHAR pszStop ;
errno = 0 ;
* pValue = _strtoi64 ( lpszValue , & pszStop , 10 ) ;
// Return no overflow and all characters parsed.
return errno = = 0 & & * pszStop = = ' \0 ' ;
}
2008-11-17 18:29:00 -05:00
static HANDLE
OpenLogFile ( LPCSTR lpszLogDir , int fileNumber )
{
HANDLE hLogFile ;
CHAR lpszLogPath [ _MAX_PATH ] ;
_snprintf_s ( lpszLogPath , sizeof ( lpszLogPath ) , _TRUNCATE ,
" %s \\ %d.log " , lpszLogDir , fileNumber ) ;
lpszLogPath [ _MAX_PATH - 1 ] = ' 0 ' ;
hLogFile = CreateFile ( lpszLogPath , GENERIC_WRITE | GENERIC_READ ,
FILE_SHARE_READ , NULL ,
CREATE_ALWAYS , FILE_ATTRIBUTE_NORMAL , NULL ) ;
if ( INVALID_HANDLE_VALUE = = hLogFile ) {
ErrorReport ( " CreateFile (for log) " ) ;
}
return hLogFile ;
}
2008-03-05 09:52:00 -05:00
static int
RunAndWait ( int nProcesses ,
LPPROCESS_INFORMATION lpProcessInfo ,
LPCSTR lpszCommandLine [ ] ,
time_t endTime )
{
HANDLE hProcesses [ MAXIMUM_WAIT_OBJECTS ] ;
int nProcessOrdinal [ MAXIMUM_WAIT_OBJECTS ] ;
int i ;
for ( i = 0 ; i < nProcesses ; i + + ) {
if ( ResumeThread ( lpProcessInfo [ i ] . hThread ) = = - 1 ) {
ErrorReport ( " ResumeThread " ) ;
return - 1 ;
}
hProcesses [ i ] = lpProcessInfo [ i ] . hProcess ;
nProcessOrdinal [ i ] = i ;
}
while ( nProcesses > 0 ) {
DWORD dwMilliseconds ;
DWORD dwResult ;
time_t t ;
time ( & t ) ;
2008-11-17 18:29:00 -05:00
if ( t > = endTime ) {
2008-03-05 09:52:00 -05:00
printf ( " runall: timeout \n " ) ;
return - 1 ;
}
2008-11-17 18:29:00 -05:00
t = ( endTime - t ) * 1000 ;
// Note INFINITE - 1 so we don't sleep forever.
2008-03-05 09:52:00 -05:00
dwMilliseconds = ( DWORD ) ( ( t < INFINITE ) ? t : INFINITE - 1 ) ;
dwResult = WaitForMultipleObjects ( nProcesses ,
hProcesses ,
FALSE ,
dwMilliseconds ) ;
2008-11-17 18:29:00 -05:00
tprintf ( " Next timeout = %d ms \n " , dwMilliseconds ) ;
2008-03-05 09:52:00 -05:00
if ( dwResult = = WAIT_TIMEOUT ) {
2008-11-17 18:29:00 -05:00
tprintf ( " runall: WAIT_TIMEOUT \n " ) ;
2008-03-05 09:52:00 -05:00
}
else if ( dwResult > = WAIT_ABANDONED_0 & &
dwResult < = WAIT_ABANDONED_0 + nProcesses ) {
printf ( " runall: wait abandoned (process %d) \n " ,
dwResult - WAIT_ABANDONED_0 ) ;
return - 1 ;
}
else {
DWORD dwProcess = dwResult - WAIT_OBJECT_0 ;
DWORD dwExitCode ;
hProcesses [ dwProcess ] = hProcesses [ - - nProcesses ] ;
nProcessOrdinal [ dwProcess ] = nProcessOrdinal [ nProcesses ] ;
if ( GetExitCodeProcess ( hProcesses [ dwProcess ] , & dwExitCode ) = = 0 ) {
ErrorReport ( " GetExitCodeProcess " ) ;
return - 1 ;
}
printf ( " runall: Command \" %s \" => exited %d \n " ,
lpszCommandLine [ nProcessOrdinal [ dwProcess ] ] ,
dwExitCode ) ;
if ( dwExitCode ! = 0 ) {
return - 1 ;
}
}
}
return 0 ;
}
static int
2008-11-17 18:29:00 -05:00
RunAll ( int nProcesses ,
LPCSTR lpszCommandLine [ ] ,
LPCSTR logDir ,
time_t endTime )
2008-03-05 09:52:00 -05:00
{
PROCESS_INFORMATION pi [ MAXIMUM_WAIT_OBJECTS ] ;
STARTUPINFO si ;
int exitCode = - 1 ;
int i ;
for ( i = 0 ; i < nProcesses ; i + + ) {
DWORD dwCreationFlags = CREATE_SUSPENDED | CREATE_NO_WINDOW ;
ZeroMemory ( & si , sizeof ( si ) ) ;
si . cb = sizeof ( si ) ;
2008-11-17 18:29:00 -05:00
if ( NULL ! = logDir ) {
si . dwFlags = STARTF_USESTDHANDLES ;
si . hStdOutput = OpenLogFile ( logDir , i ) ;
if ( INVALID_HANDLE_VALUE = = si . hStdOutput ) {
break ;
}
SetHandleInformation ( si . hStdOutput ,
HANDLE_FLAG_INHERIT ,
HANDLE_FLAG_INHERIT ) ;
si . hStdError = si . hStdOutput ;
}
if ( ! CreateProcess ( NULL , ( LPSTR ) lpszCommandLine [ i ] , NULL , NULL , TRUE ,
2008-03-05 09:52:00 -05:00
dwCreationFlags , NULL , NULL , & si , & pi [ i ] ) ) {
ErrorReport ( " CreateProcess " ) ;
break ;
}
2008-11-17 18:29:00 -05:00
2008-03-05 09:52:00 -05:00
tprintf ( " runall: Created PID %u: %s \n " ,
pi [ i ] . dwProcessId , lpszCommandLine [ i ] ) ;
}
if ( i = = nProcesses ) {
exitCode = RunAndWait ( nProcesses , pi , lpszCommandLine , endTime ) ;
}
while ( - - i > = 0 ) {
if ( TerminateProcess ( pi [ i ] . hProcess , - 1 ) ) {
tprintf ( " runall: Terminated PID %u: %s \n " ,
pi [ i ] . dwProcessId , lpszCommandLine [ i ] ) ;
}
}
return exitCode ;
}
2008-11-17 18:29:00 -05:00
static double
FileTimeToSeconds ( FILETIME ftValue )
{
PINT64 pV = ( PINT64 ) & ftValue ;
double d = ( * pV ) * 1e-7 ;
return d ;
}
static void
DisplayProcessTimes ( )
{
FILETIME ftCreation ;
FILETIME ftExitTime ;
FILETIME ftKernelTime ;
FILETIME ftUserTime ;
GetProcessTimes ( ( HANDLE ) - 1 ,
& ftCreation ,
& ftExitTime ,
& ftKernelTime ,
& ftUserTime ) ;
printf ( " runall: Process Time Information \n " ) ;
printf ( " runall: Kernel time: %.2f \n " , FileTimeToSeconds ( ftKernelTime ) ) ;
printf ( " runall: User time: %.2f \n " , FileTimeToSeconds ( ftUserTime ) ) ;
}
2008-03-05 09:52:00 -05:00
int
main ( int argc , LPCSTR argv [ ] )
{
2008-11-17 18:29:00 -05:00
BOOL bAccounting = FALSE ;
2008-03-05 09:52:00 -05:00
BOOL bLaunchFailure = FALSE ;
time_t endTime = _I64_MAX ;
INT64 timeout = 0 ;
2008-11-17 18:29:00 -05:00
LPCSTR lpszLogDir = NULL ;
INT32 exitCode ;
2008-03-05 09:52:00 -05:00
argc - - ;
argv + + ;
while ( argc ! = 0 & & ( argv [ 0 ] [ 0 ] = = ' / ' | | argv [ 0 ] [ 0 ] = = ' - ' ) ) {
switch ( tolower ( argv [ 0 ] [ 1 ] ) ) {
2008-11-17 18:29:00 -05:00
case ' a ' :
bAccounting = TRUE ;
break ;
2008-03-05 09:52:00 -05:00
case ' t ' :
if ( argv [ 0 ] [ 2 ] = = ' : ' & & ParseInt64 ( & argv [ 0 ] [ 3 ] , & timeout ) ) {
time ( & endTime ) ;
endTime + = ( time_t ) timeout ;
}
else {
printf ( " Bad timeout argument. " ) ;
return - 1 ;
}
break ;
2008-11-17 18:29:00 -05:00
case ' l ' :
if ( argv [ 0 ] [ 2 ] = = ' : ' ) {
lpszLogDir = argv [ 0 ] + 3 ;
}
else {
printf ( " Bad log dir argument. " ) ;
return - 1 ;
}
break ;
2008-03-05 09:52:00 -05:00
case ' v ' :
g_bVerbose = TRUE ;
2008-11-17 18:29:00 -05:00
break ;
2008-03-05 09:52:00 -05:00
default :
usage ( ) ;
return - 1 ;
}
argc - - ;
argv + + ;
}
if ( argc = = 0 ) {
usage ( ) ;
return - 1 ;
} else if ( argc > MAXIMUM_WAIT_OBJECTS ) {
printf ( " Supported process limit exceeded. " ) ;
return - 1 ;
}
SetConsoleCtrlHandler ( CtrlHandler , TRUE ) ;
2008-11-17 18:29:00 -05:00
exitCode = RunAll ( argc , argv , lpszLogDir , endTime ) ;
if ( bAccounting ) {
DisplayProcessTimes ( ) ;
}
return exitCode ;
2008-03-05 09:52:00 -05:00
}