| /* ********************************************************** |
| * 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 cant logoff the system |
| jobuir.UIRestrictionsClass |= JOB_OBJECT_UILIMIT_EXITWINDOWS; |
| |
| // The process cant 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 processs thread to execute code. |
| ResumeThread(pi.hThread); |
| CloseHandle(pi.hThread); |
| |
| // Wait for the process to terminate or for all the jobs 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; |
| } |