blob: 95db83b88ae0610f2102e768afd8a334cda2211e [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2007 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
#define _WIN32_WINNT 0x0500
/* for Jobs on Win2000+ */
#include <windows.h>
#include <assert.h>
#include <stdio.h>
#include "share.h"
#define FP stderr
#ifndef USING_SDK
typedef struct _IO_COUNTERS {
ULONGLONG ReadOperationCount;
ULONGLONG WriteOperationCount;
ULONGLONG OtherOperationCount;
ULONGLONG ReadTransferCount;
ULONGLONG WriteTransferCount;
ULONGLONG OtherTransferCount;
} IO_COUNTERS;
typedef IO_COUNTERS *PIO_COUNTERS;
/* FIXME: this is really screwed up: they have added a field in the
* object and
* LastErrorValue: (Win32) 0x18 (24) - The program issued a command but the command length
* is incorrect.
*/
typedef struct _NEW_JOBOBJECT_BASIC_LIMIT_INFORMATION {
LARGE_INTEGER PerProcessUserTimeLimit;
LARGE_INTEGER PerJobUserTimeLimit;
DWORD LimitFlags;
SIZE_T MinimumWorkingSetSize;
SIZE_T MaximumWorkingSetSize;
DWORD ActiveProcessLimit;
ULONG *Affinity;
DWORD PriorityClass;
/* VC98 has only the above fields */
DWORD SchedulingClass;
} NEW_JOBOBJECT_BASIC_LIMIT_INFORMATION;
typedef struct _JOBOBJECT_EXTENDED_LIMIT_INFORMATION {
NEW_JOBOBJECT_BASIC_LIMIT_INFORMATION BasicLimitInformation;
IO_COUNTERS IoInfo;
SIZE_T ProcessMemoryLimit;
SIZE_T JobMemoryLimit;
SIZE_T PeakProcessMemoryUsed;
SIZE_T PeakJobMemoryUsed;
} JOBOBJECT_EXTENDED_LIMIT_INFORMATION, *PJOBOBJECT_EXTENDED_LIMIT_INFORMATION;
#endif
void
StartRestrictedProcess(LPTSTR app_name, LPTSTR app_cmdline, SIZE_T pagefile_limit)
{
// based on http://www.microsoft.com/msj/0399/jobkernelobj/jobkernelobj.aspx
// Create a job kernel object
HANDLE hjob = CreateJobObject(NULL, NULL);
NEW_JOBOBJECT_BASIC_LIMIT_INFORMATION jobli = { 0 };
JOBOBJECT_BASIC_UI_RESTRICTIONS jobuir;
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi;
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobext = { 0 };
BOOL ok;
// Place some restrictions on processes in the job
// First, set some basic restrictions
// The process always runs in the idle priority class
jobli.PriorityClass = IDLE_PRIORITY_CLASS;
// The job cannot use more than 10 second of CPU time
jobli.PerJobUserTimeLimit.QuadPart = 10 * 10000000; // 1 second in 100-ns intervals
// These are two 2 restrictions I want placed on the job (process)
jobli.LimitFlags = JOB_OBJECT_LIMIT_PRIORITY_CLASS | JOB_OBJECT_LIMIT_JOB_TIME;
ok = SetInformationJobObject(hjob, JobObjectBasicLimitInformation, &jobli,
sizeof(jobli));
if (!ok) {
fprintf(FP, "GLE %d", GetLastError());
}
// Second, set some UI restrictions
jobuir.UIRestrictionsClass = JOB_OBJECT_UILIMIT_NONE; // A fancy 0
// The process can’t logoff the system
jobuir.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS;
// The process can’t access USER object (like other windows) in the system
jobuir.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_HANDLES;
ok = SetInformationJobObject(hjob, JobObjectBasicUIRestrictions, &jobuir,
sizeof(jobuir));
if (!ok) {
fprintf(FP, "GLE %d", GetLastError());
}
// actually we really want to set page file limits as well
// and we'll set separately
// The process always runs in the idle priority class
jobext.ProcessMemoryLimit = pagefile_limit;
jobext.BasicLimitInformation.LimitFlags =
0x00000100 /* JOB_OBJECT_LIMIT_PROCESS_MEMORY */;
ok = SetInformationJobObject(hjob, 9 /* FIXME: JobObjectExtendedLimitInformation */,
&jobext, sizeof(jobext));
if (!ok) {
fprintf(FP, "GLE %d", GetLastError());
}
// Spawn the process that is to be in the job
// Note: You must first spawn the process and then place the process in the job.
// This means that the process's thread must be initially suspended so that
// it can't execute any code outside of the job's restrictions.
CreateProcess(app_name, app_cmdline, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL,
&si, &pi);
// Place the process in the job.
// Note: if this process spawns any children, the children are
// automatically associated with the same job
AssignProcessToJobObject(hjob, pi.hProcess);
// Now, we can allow the child process’s thread to execute code.
ResumeThread(pi.hThread);
CloseHandle(pi.hThread);
// Wait for the process to terminate or for all the job’s allotted CPU time to be used
{
DWORD dw;
HANDLE h[2];
h[0] = pi.hProcess;
h[1] = hjob;
dw = WaitForMultipleObjects(2, h, FALSE, INFINITE);
switch (dw - WAIT_OBJECT_0) {
case 0:
// The process has terminated
break;
case 1:
// All of the job's allotted CPU time or other limits were used
fprintf(stderr, "job limit reached\n");
break;
}
}
// Clean-up properly
CloseHandle(pi.hProcess);
CloseHandle(hjob);
}
// FIXME: note that we can use a Job limit to precisely alot the CPU time
int
usage(char *us)
{
fprintf(FP, "Usage: %s [-p page_limit in MB] [-kb] <program> <args...>\n", us);
fflush(FP);
return 0;
}
int __cdecl main(int argc, char *argv[], char *envp[])
{
LPTSTR app_name = NULL;
TCHAR full_app_name[2 * MAX_PATH];
TCHAR app_cmdline[2 * MAX_PATH];
int arg_offs = 1;
int allocation_unit = 1024 * 1024;
int pagelimit = 10 * allocation_unit; /* 10 MB */
if (argc < 2) {
return usage(argv[0]);
}
while (argv[arg_offs][0] == '-') {
if (strcmp(argv[arg_offs], "-p") == 0) {
if (argc <= arg_offs + 1)
return usage(argv[0]);
pagelimit = atoi(argv[arg_offs + 1]) * allocation_unit;
arg_offs += 2;
} else if (strcmp(argv[arg_offs], "-kb") == 0) {
allocation_unit = 1024;
arg_offs += 1;
}
}
/* FIXME: watch out for the usual /Program BUG */
_snwprintf(full_app_name, BUFFER_SIZE_ELEMENTS(full_app_name), L"%hs",
argv[arg_offs]);
/* FIXME: only one argument taken */
_snwprintf(app_cmdline, BUFFER_SIZE_ELEMENTS(app_cmdline), L"%hs %hs", argv[arg_offs],
argv[arg_offs + 1]);
StartRestrictedProcess(NULL /* FIXME: full_app_name */, app_cmdline, pagelimit);
fprintf(FP, "done\n");
fflush(FP);
return 0;
}