/* 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; }