Show More
Commit Description:
added box for windows
Commit Description:
added box for windows
File last commit:
Show/Diff file:
Action:
std-script/box-win/execute.cpp | 377 lines | 10.0 KiB | text/x-c | CppLexer |
/*
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 <windows.h>
#include <psapi.h>
#include <tlhelp32.h>
#include <stdio.h>
#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;
}