diff --git a/std-script/box-win/execute.cpp b/std-script/box-win/execute.cpp new file mode 100644 --- /dev/null +++ b/std-script/box-win/execute.cpp @@ -0,0 +1,377 @@ +/* + This sandbox module is from [Fossil + grader](http://code.google.com/p/fossil-grader/). + + This library is a modification from a program called trun, taken + from an unknown source. (FIX THIS) + + When compiling with Mingw, add "-lpsapi" to link Windows'memory stat + library. +*/ +#include +#include +#include +#include +#include "execute.h" + +#define INITIAL_WAIT_FOR_MEM_CHECK 100 + +/* +==How execute works== + +===Start up=== +Set up basic configurations: input file, output file +into STARTUPINFO struct to be passed to CreateProcess. + +Create a child process with CreateProcess. + +===Wait=== +Use WaitForSingleObject to wait. + +===Killing chile process=== +This process is really involved, because (1) programs in +DOS mode actually runs inside NTVDM so killing them +requires to kill NTVDM, (2) something a program crashes +NTVDM and a dialog box pops up, and we need to close +that dialog box MANUALLY, and (3) for Win32 apps that crash, +some reporting service in Windows opens a dialog box, +and it has to be killed. + +Those extra steps are what's exactly done here: +1. Kill the process if there's any +2. In case that there's no real process, find NTVDM +and kill it (repeatedly until it's gone) +3. Check if NTVDM crashed and some warning dialog opens, +if there's any, signal the user and wait. +4. For real Win32 apps, find process "dwwin.exe" which +represents an agent for reporting service and also +opens a dialog. If finds it, kill it (repeatedly) +until it's gone. + +Step 4. might be problematic --- dwwin.exe might not +be a universal process for error reporting services??? +*/ + + + +/* +These are routines that check NTVDM crash dialog. +It works by enumerating all window titles, and +checks for "16 bit" or something with ".exe" somewhere +and starts with "cmd.exe". +*/ +bool NTVDMcrashed_found; + +/* this is a callback for window title enumeration */ +BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) +{ + char buffer[256]; + GetWindowText(hWnd, buffer, 256); + + if(strlen(buffer)!=0) { + if(strstr(buffer,"16 bit")!=0) { + NTVDMcrashed_found = true; + } + if((strstr(buffer,".exe")!=0) && + (strstr(buffer,"cmd.exe")==buffer)) { + NTVDMcrashed_found = true; + printf("Title: %s\n",buffer); + } + } + return TRUE; +} + +bool check_ntvdm_dialog() +{ + NTVDMcrashed_found = false; + + FARPROC EnumProcInstance = MakeProcInstance((FARPROC)EnumWindowsProc, + AfxGetInstanceHandle()); + EnumWindows((WNDENUMPROC)EnumProcInstance, (LPARAM)0); + FreeProcInstance(EnumProcInstance); + + return NTVDMcrashed_found; +} + +DWORD get_process_id(char *pname) +{ + HANDLE hProcessSnap; + HANDLE hProcess; + PROCESSENTRY32 pe32; + DWORD dwPriorityClass; + DWORD pid=0; + + hProcessSnap = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ); + if( hProcessSnap == INVALID_HANDLE_VALUE ) { + return 0; + } + + pe32.dwSize = sizeof( PROCESSENTRY32 ); + if( !Process32First( hProcessSnap, &pe32 ) ) { + CloseHandle( hProcessSnap ); + return 0; + } + + do { + if(strcasecmp(pe32.szExeFile ,pname)==0) + pid = pe32.th32ProcessID; + } while( Process32Next( hProcessSnap, &pe32 ) ); + + CloseHandle( hProcessSnap ); + return pid; +} + +DWORD get_ntvdm_pid() +{ + return get_process_id("ntvdm.exe"); +} + +void kill_error_report() +{ + DWORD pid; + do { + if((pid = get_process_id("dwwin.exe"))!=0) { + fprintf(stderr," -- with error report (pid: %ld)\n",pid); + HANDLE hProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid); + if(hProcess!=NULL) { + TerminateProcess(hProcess, 0); + Sleep(500); + while(get_process_id("dwwin.exe")==pid) { + fprintf(stderr,"wait for dwwin.exe to die...\n"); + Sleep(500); + } + } else + fprintf(stderr,"do not have permission (%d)\n", + GetLastError()); + } + } while(get_process_id("dwwin.exe")!=0); +} + +void wait_dialog() +{ + kill_error_report(); + if(check_ntvdm_dialog()) { + fprintf(stderr,"Some dialog opens; please MANUALLY kill it."); + fflush(stderr); + do { + Sleep(1000); + } while(check_ntvdm_dialog()); + fprintf(stderr,"... done\n"); + } +} + +void setstartupinfo(STARTUPINFO *si, char *inname, char *outname) +{ + SECURITY_ATTRIBUTES sa; + + ZeroMemory(&sa, sizeof(sa)); + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = NULL; + sa.bInheritHandle = TRUE; + + si->dwFlags = STARTF_USESTDHANDLES; + if((inname!=0) && (strcmp(inname,"-")!=0)) { + si->hStdInput = CreateFile(inname, + FILE_READ_DATA, + FILE_SHARE_READ, + &sa, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + } else + si->hStdInput = NULL; + + if((outname!=0) && (strcmp(outname,"-")!=0)) { + si->hStdOutput = CreateFile(outname, + FILE_WRITE_DATA, + FILE_SHARE_READ, + &sa, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + } else + si->hStdOutput = NULL; + + si->hStdError = NULL; +} + +// taken from http://msdn.microsoft.com/en-us/library/ms682050(VS.85).aspx +void PrintMemoryInfo(DWORD processID) +{ + HANDLE hProcess; + PROCESS_MEMORY_COUNTERS pmc; + + // Print the process identifier. + + printf("\nProcess ID: %u\n", processID); + + // Print information about the memory usage of the process. + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | + PROCESS_VM_READ, + FALSE,processID); + if(hProcess == NULL) + return; + + if(GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) { + printf("\tPageFaultCount: %d\n",pmc.PageFaultCount); + printf("\tPeakWorkingSetSize: %d\n", + pmc.PeakWorkingSetSize); + printf("\tWorkingSetSize: %d\n",pmc.WorkingSetSize); + printf("\tQuotaPeakPagedPoolUsage: %d\n", + pmc.QuotaPeakPagedPoolUsage); + printf("\tQuotaPagedPoolUsage: %d\n", + pmc.QuotaPagedPoolUsage); + printf("\tQuotaPeakNonPagedPoolUsage: %d\n", + pmc.QuotaPeakNonPagedPoolUsage); + printf("\tQuotaNonPagedPoolUsage: %d\n", + pmc.QuotaNonPagedPoolUsage); + printf("\tPagefileUsage: %d\n",pmc.PagefileUsage); + printf("\tPeakPagefileUsage: %d\n", + pmc.PeakPagefileUsage); + } + CloseHandle( hProcess ); +} + +int check_memory_usage(DWORD pid, int max_mem, int *actual_usage) { + // modified from http://msdn.microsoft.com/en-us/library/ms682050(VS.85).aspx + //PrintMemoryInfo(pid); + HANDLE hProcess; + PROCESS_MEMORY_COUNTERS pmc; + + if((max_mem==0) || (pid==0)) + return 1; + + if(pid == get_ntvdm_pid()) { + fprintf(stderr,"ntvdm: ignored\n"); + return 1; + } + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | + PROCESS_VM_READ, + FALSE, pid); + if(hProcess == NULL) + return 1; + + int max_mem_usage = 0; + if(GetProcessMemoryInfo(hProcess, &pmc, sizeof(pmc))) { + max_mem_usage = pmc.PeakWorkingSetSize; + if(pmc.PeakPagefileUsage > max_mem_usage) + max_mem_usage = pmc.PeakPagefileUsage; + } + CloseHandle(hProcess); + if(actual_usage != NULL) + (*actual_usage) = max_mem_usage; + return (max_mem_usage <= max_mem); +} + +int execute(char *exname, char *inname, char *outname, double t, int max_mem) +{ + STARTUPINFO si; + PROCESS_INFORMATION pi; + int ifsuccess = EXE_RESULT_OK; + + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + setstartupinfo(&si, inname, outname); + + if(!CreateProcess( NULL, // No module name (use command line). + TEXT(exname), // Command line. + NULL, // Process handle not inheritable. + NULL, // Thread handle not inheritable. + TRUE, // Set handle inheritance to FALSE. + 0, // No creation flags. + NULL, // Use parent's environment block. + NULL, // Use parent's starting directory. + &si, // Pointer to STARTUPINFO structure. + &pi)) // Pointer to PROCESS_INFORMATION structure. + { + //printf( "CreateProcess failed (%d).\n", GetLastError() ); + } + //fprintf(stderr,"Process ID: %ld\n",pi.dwProcessId); + //fprintf(stderr,"time limit = %d\n",t); + + // checking memory usage + // wait 0.1 sec before checking mem usage + + int actual_memory_usage = 0; + + Sleep(INITIAL_WAIT_FOR_MEM_CHECK); + if(!check_memory_usage(pi.dwProcessId,max_mem,&actual_memory_usage)) { + // using too much memory + fprintf(stderr,"Memory limit exceeded.\n"); + //PrintMemoryInfo(pi.dwProcessId); + ifsuccess = EXE_RESULT_MEMORY; + } + + if((ifsuccess == EXE_RESULT_MEMORY) || + (WaitForSingleObject(pi.hProcess, + (int)(t*1000) + 1 + - INITIAL_WAIT_FOR_MEM_CHECK)==WAIT_TIMEOUT)) { + // need to kill... + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pi.dwProcessId); + + if(ifsuccess != EXE_RESULT_MEMORY) + fprintf(stderr,"Time limit exceeded.\n"); + if(hProcess != NULL) { + fprintf(stderr,"killing pid: %ld\n",pi.dwProcessId); + TerminateProcess(hProcess, 0); + wait_dialog(); + } else { + DWORD dwNtvdmId = get_ntvdm_pid(); + fprintf(stderr,"killing (ntvdm) pid: %ld\n",dwNtvdmId); + if(dwNtvdmId!=0) { + hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwNtvdmId); + TerminateProcess(hProcess, 0); + } else { + fprintf(stderr,"killing process error\n"); + } + + if(get_ntvdm_pid()!=0) { + fprintf(stderr,"killing error, ntvdm.exe still remains;"); + fprintf(stderr,"please MANUALLY kill it."); + fflush(stderr); + do { + Sleep(1000); + } while(get_ntvdm_pid()!=0); + fprintf(stderr,"... done\n"); + wait_dialog(); + } + } + if(ifsuccess != EXE_RESULT_MEMORY) + ifsuccess = EXE_RESULT_TIMEOUT; + } + if((ifsuccess==EXE_RESULT_OK) && + (!check_memory_usage(pi.dwProcessId,max_mem, &actual_memory_usage))) { + // using too much memory + ifsuccess = EXE_RESULT_MEMORY; + } + wait_dialog(); + if(si.hStdInput!=NULL) + CloseHandle(si.hStdInput); + if(si.hStdOutput!=NULL) + CloseHandle(si.hStdOutput); + + if(ifsuccess==EXE_RESULT_OK) + fprintf(stderr,"OK\n"); + else if(ifsuccess==EXE_RESULT_TIMEOUT) + fprintf(stderr,"Time limit exceeded.\n"); + else + fprintf(stderr,"Memory limit exceeded.\n"); + + double actual_time_usage; + if(ifsuccess==EXE_RESULT_TIMEOUT) + actual_time_usage = t+1; + else + actual_time_usage = t; + + fprintf(stderr,"%.4lfr%.4lfu%.4lfs%dm\n", + actual_time_usage, + actual_time_usage, (double)0, + (actual_memory_usage + 1023)/1024); + + return ifsuccess; +} +