|
|
/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
job.c
Abstract:
A user mode app that allows creation and management of jobs.
Environment:
User mode only
Revision History:
03-26-96 : Created
--*/
//
// this module may be compiled at warning level 4 with the following
// warnings disabled:
//
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <assert.h>
#include <windows.h>
#include <devioctl.h>
#include "jobmgr.h"
typedef struct { char Option; JOBOBJECTINFOCLASS InfoClass; char *Name; DWORD (*Function)(HANDLE Job, JOBOBJECTINFOCLASS InfoClass); } JOB_QUERY_OPTION, *PJOB_QUERY_OPTION;
#define MKOPTION(optChar, optClass) {optChar, optClass, #optClass, Dump##optClass}
DWORD DumpJobObjectBasicProcessIdList(HANDLE JobHandle, JOBOBJECTINFOCLASS InfoClass); DWORD DumpJobObjectBasicUIRestrictions(HANDLE JobHandle, JOBOBJECTINFOCLASS InfoClass); DWORD DumpJobObjectBasicAndIoAccountingInformation( HANDLE JobHandle, JOBOBJECTINFOCLASS InfoClass );
DWORD DumpJobObjectExtendedLimitInformation( HANDLE Job, JOBOBJECTINFOCLASS InfoClass );
DWORD DumpJobObjectSecurityLimitInformation( HANDLE JobHandle, JOBOBJECTINFOCLASS InfoClass );
JOB_QUERY_OPTION JobInfoClasses[] = { MKOPTION('a', JobObjectBasicAndIoAccountingInformation), MKOPTION('l', JobObjectExtendedLimitInformation), MKOPTION('p', JobObjectBasicProcessIdList), MKOPTION('s', JobObjectSecurityLimitInformation), MKOPTION('u', JobObjectBasicUIRestrictions), {'\0', 0, NULL} };
DWORD QueryJobCommand( IN PCOMMAND CommandEntry, IN int argc, IN char* argv[] ) { CHAR defaultOptions[] = {'p', '\0'}; PTSTR options; PTSTR jobName;
HANDLE job;
int i;
BOOLEAN matchAll = FALSE;
DWORD status;
if(argc == 0) { return -1; }
GetAllProcessInfo();
if((argc > 1) && (argv[0][0] == '-')) {
// a - accounting & io
// l - extended limit info
// p - process id list
// u - basic ui restrictions
// s - security limits
options = &(argv[0][1]);
argc -= 1; argv += 1; } else { options = defaultOptions; }
if(strchr(options, '*') != NULL) { if(strlen(options) != 1) { puts("Cannot specify '*' with other flags"); return -1; } else { matchAll = TRUE; options = defaultOptions; } }
jobName = argv[0];
printf("Opening job object %s\n", jobName);
job = OpenJobObject(JOB_OBJECT_QUERY, FALSE, jobName);
if(job == NULL) { return GetLastError(); }
for(i = 0; JobInfoClasses[i].Option != '\0'; i++) { LPSTR match;
if(!matchAll) { match = strchr(options, JobInfoClasses[i].Option); if(match == NULL) { continue; } //
// Clear the option so we can report the invalid option flags at the
// end.
//
*match = ' '; }
printf("%s [%#x]:\n", JobInfoClasses[i].Name, JobInfoClasses[i].InfoClass);
status = JobInfoClasses[i].Function(job, JobInfoClasses[i].InfoClass); puts("");
if(status != ERROR_SUCCESS) { DWORD length; LPSTR buffer;
printf("Error %d querying info: ", status);
length = FormatMessage((FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | (FORMAT_MESSAGE_MAX_WIDTH_MASK & 0)), NULL, status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &buffer, 1, NULL);
if(length != 0) { puts(buffer); LocalFree(buffer); } } }
if(!matchAll) { LPSTR header = "Option flag not understood:"; while(options[0] != '\0') { if(options[0] != ' ') { printf("%s %c", header, options[0]); header = ""; } options += 1; } }
CloseHandle(job); return ERROR_SUCCESS; }
DWORD DumpJobObjectBasicProcessIdList( HANDLE Job, JOBOBJECTINFOCLASS InfoClass ) { JOBOBJECT_BASIC_PROCESS_ID_LIST buffer; PJOBOBJECT_BASIC_PROCESS_ID_LIST idList = NULL; ULONG bufferSize;
BOOL result; DWORD status;
DWORD i;
result = QueryInformationJobObject(Job, InfoClass, &buffer, sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST), NULL); status = GetLastError();
if((!result) && (status != ERROR_MORE_DATA)) { return status; }
do { if(idList != NULL) { buffer.NumberOfAssignedProcesses = idList->NumberOfAssignedProcesses; LocalFree(idList); idList = NULL; }
//
// Calculate the actual size of the list and allocate a buffer to hold it.
//
bufferSize = offsetof(JOBOBJECT_BASIC_PROCESS_ID_LIST, ProcessIdList); bufferSize += sizeof(ULONG_PTR) * buffer.NumberOfAssignedProcesses; assert(idList == NULL); idList = LocalAlloc(LPTR, bufferSize); if(idList == NULL) { return GetLastError(); }
result = QueryInformationJobObject(Job, InfoClass, idList, bufferSize, NULL);
status = GetLastError();
if((!result) && (status != ERROR_MORE_DATA)) { LocalFree(idList); return status; }
} while(idList->NumberOfAssignedProcesses > idList->NumberOfProcessIdsInList);
assert(idList->NumberOfAssignedProcesses == idList->NumberOfProcessIdsInList);
//
// Dump the information.
//
printf(" %d processes assigned to job:\n", idList->NumberOfAssignedProcesses);
for(i = 0; i < idList->NumberOfAssignedProcesses; i++) { printf("%8d", idList->ProcessIdList[i]); PrintProcessInfo(idList->ProcessIdList[i]); puts(""); }
FreeProcessInfo(); LocalFree(idList); return ERROR_SUCCESS; }
DWORD DumpJobObjectBasicUIRestrictions( HANDLE Job, JOBOBJECTINFOCLASS InfoClass ) { JOBOBJECT_BASIC_UI_RESTRICTIONS uiLimit; static FLAG_NAME jobUiLimitFlags[] = { FLAG_NAME(JOB_OBJECT_UILIMIT_HANDLES ), //0x00000001
FLAG_NAME(JOB_OBJECT_UILIMIT_READCLIPBOARD ), //0x00000002
FLAG_NAME(JOB_OBJECT_UILIMIT_WRITECLIPBOARD ), //0x00000004
FLAG_NAME(JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS ), //0x00000008
FLAG_NAME(JOB_OBJECT_UILIMIT_DISPLAYSETTINGS ), //0x00000010
FLAG_NAME(JOB_OBJECT_UILIMIT_GLOBALATOMS ), //0x00000020
FLAG_NAME(JOB_OBJECT_UILIMIT_DESKTOP ), //0x00000040
FLAG_NAME(JOB_OBJECT_UILIMIT_EXITWINDOWS ), //0x00000080
{0,0} }; BOOL result; DWORD status;
DWORD i;
result = QueryInformationJobObject(Job, InfoClass, &uiLimit, sizeof(JOBOBJECT_BASIC_UI_RESTRICTIONS), NULL); status = GetLastError();
if(!result) { return status; }
if(uiLimit.UIRestrictionsClass == JOB_OBJECT_UILIMIT_NONE) { printf(" Job has no UI restrictions\n"); return ERROR_SUCCESS; }
DumpFlags(2, "UI Restrictions", uiLimit.UIRestrictionsClass, jobUiLimitFlags);
return ERROR_SUCCESS; }
DWORD DumpJobObjectBasicAndIoAccountingInformation( HANDLE Job, JOBOBJECTINFOCLASS InfoClass ) { JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION info; BOOL result; DWORD status;
DWORD i;
result = QueryInformationJobObject( Job, InfoClass, &info, sizeof(JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION), NULL); status = GetLastError();
if(!result) { return status; }
xprintf(2, "Basic Info\n"); xprintf(4, "TotalUserTime: %s\n", TicksToString(info.BasicInfo.TotalUserTime)); xprintf(4, "TotalKernelTime: %s\n", TicksToString(info.BasicInfo.TotalKernelTime)); xprintf(4, "ThisPeriodTotalUserTime: %s\n", TicksToString(info.BasicInfo.ThisPeriodTotalUserTime)); xprintf(4, "ThisPeriodTotalKernelTime: %s\n", TicksToString(info.BasicInfo.ThisPeriodTotalKernelTime)); xprintf(4, "TotalPageFaultCount: %d\n", info.BasicInfo.TotalPageFaultCount); xprintf(4, "TotalProcesses: %d\n", info.BasicInfo.TotalProcesses); xprintf(4, "ActiveProcesses: %d\n", info.BasicInfo.ActiveProcesses); xprintf(4, "TotalTerminatedProcesses: %d\n", info.BasicInfo.TotalTerminatedProcesses);
xprintf(2, "I/O Info\n");
xprintf(4, "ReadOperationCount: %I64d\n", info.IoInfo.ReadOperationCount); xprintf(4, "WriteOperationCount: %I64d\n", info.IoInfo.WriteOperationCount); xprintf(4, "OtherOperationCount: %I64d\n", info.IoInfo.OtherOperationCount); xprintf(4, "ReadTransferCount: %I64d\n", info.IoInfo.ReadTransferCount); xprintf(4, "WriteTransferCount: %I64d\n", info.IoInfo.WriteTransferCount); xprintf(4, "OtherTransferCount: %I64d\n", info.IoInfo.OtherTransferCount);
return ERROR_SUCCESS; }
DWORD DumpJobObjectExtendedLimitInformation( HANDLE Job, JOBOBJECTINFOCLASS InfoClass ) { JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; ULONG limits; static FLAG_NAME basicJobLimitFlags[] = { FLAG_NAME(JOB_OBJECT_LIMIT_WORKINGSET ), //0x00000001
FLAG_NAME(JOB_OBJECT_LIMIT_PROCESS_TIME ), //0x00000002
FLAG_NAME(JOB_OBJECT_LIMIT_JOB_TIME ), //0x00000004
FLAG_NAME(JOB_OBJECT_LIMIT_ACTIVE_PROCESS ), //0x00000008
FLAG_NAME(JOB_OBJECT_LIMIT_AFFINITY ), //0x00000010
FLAG_NAME(JOB_OBJECT_LIMIT_PRIORITY_CLASS ), //0x00000020
FLAG_NAME(JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME ), //0x00000040
FLAG_NAME(JOB_OBJECT_LIMIT_SCHEDULING_CLASS ), //0x00000080
{0,0} };
//
// Extended Limits
//
static FLAG_NAME extendedJobLimitFlags[] = { FLAG_NAME(JOB_OBJECT_LIMIT_PROCESS_MEMORY ), //0x00000100
FLAG_NAME(JOB_OBJECT_LIMIT_JOB_MEMORY ), //0x00000200
FLAG_NAME(JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION ), //0x00000400
FLAG_NAME(JOB_OBJECT_LIMIT_BREAKAWAY_OK ), //0x00000800
FLAG_NAME(JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK ), //0x00001000
FLAG_NAME(JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE ), //0x00002000
FLAG_NAME(JOB_OBJECT_LIMIT_RESERVED2 ), //0x00004000
FLAG_NAME(JOB_OBJECT_LIMIT_RESERVED3 ), //0x00008000
FLAG_NAME(JOB_OBJECT_LIMIT_RESERVED4 ), //0x00010000
FLAG_NAME(JOB_OBJECT_LIMIT_RESERVED5 ), //0x00020000
FLAG_NAME(JOB_OBJECT_LIMIT_RESERVED6 ), //0x00040000
{0,0} };
BOOL result; DWORD status;
DWORD i;
result = QueryInformationJobObject( Job, InfoClass, &info, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), NULL);
status = GetLastError();
if(!result) { return status; }
limits = info.BasicLimitInformation.LimitFlags;
if(TEST_FLAG(limits, JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS) == 0) { xprintf(2, "No basic limits on job\n"); } else { DumpFlags(2, "Basic Limit Flags", limits & JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS, basicJobLimitFlags); if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_PROCESS_TIME)) { xprintf(4, "PerProcessUserTimeLimit: %s\n", TicksToString(info.BasicLimitInformation.PerProcessUserTimeLimit)); } if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_JOB_TIME)) { xprintf(4, "PerJobUserTimeLimit: %s\n", TicksToString(info.BasicLimitInformation.PerJobUserTimeLimit)); } if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_WORKINGSET)) { xprintf(4, "MinimumWorkingSetSize: %I64d\n", (ULONGLONG) info.BasicLimitInformation.MinimumWorkingSetSize); xprintf(4, "MaximumWorkingSetSize: %I64d\n", (ULONGLONG) info.BasicLimitInformation.MaximumWorkingSetSize); } if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_ACTIVE_PROCESS)) { xprintf(4, "ActiveProcessLimit: %d\n",info.BasicLimitInformation.ActiveProcessLimit); } if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_AFFINITY)) { xprintf(4, "Affinity: %#I64x\n", (ULONGLONG)info.BasicLimitInformation.Affinity); } if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_PRIORITY_CLASS)) { xprintf(4, "PriorityClass: %d\n",info.BasicLimitInformation.PriorityClass); } if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_SCHEDULING_CLASS)) { xprintf(4, "SchedulingClass: %d\n",info.BasicLimitInformation.SchedulingClass); } }
if(TEST_FLAG(limits, JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS) == 0) { xprintf(2, "No extended limits on job\n"); } else {
DumpFlags(2, "Extended Limit Flags", limits & JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS & ~JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS, extendedJobLimitFlags); if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_PROCESS_MEMORY)) { xprintf(4, "ProcessMemoryLimit: %I64d\n", (ULONGLONG) info.ProcessMemoryLimit); } if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_PROCESS_MEMORY)) { xprintf(4, "JobMemoryLimit: %I64d\n", (ULONGLONG) info.JobMemoryLimit); } }
xprintf(2, "PeakProcessMemoryUsed: %I64d\n", (ULONGLONG) info.PeakProcessMemoryUsed); xprintf(2, "PeakJobMemoryUsed: %I64d\n", (ULONGLONG) info.PeakJobMemoryUsed);
return ERROR_SUCCESS; }
DWORD DumpJobObjectSecurityLimitInformation( HANDLE Job, JOBOBJECTINFOCLASS InfoClass ) { JOBOBJECT_SECURITY_LIMIT_INFORMATION buffer; PJOBOBJECT_SECURITY_LIMIT_INFORMATION info = NULL; static FLAG_NAME jobSecurityLimitFlags[] = { FLAG_NAME(JOB_OBJECT_SECURITY_NO_ADMIN ), //00000001
FLAG_NAME(JOB_OBJECT_SECURITY_RESTRICTED_TOKEN ), //00000002
FLAG_NAME(JOB_OBJECT_SECURITY_ONLY_TOKEN ), //00000004
FLAG_NAME(JOB_OBJECT_SECURITY_FILTER_TOKENS ), //00000008
{0, 0} };
ULONG bufferSize;
BOOL result; DWORD status;
DWORD i;
result = QueryInformationJobObject( Job, InfoClass, &buffer, sizeof(JOBOBJECT_SECURITY_LIMIT_INFORMATION), &bufferSize);
status = GetLastError();
if((!result) && (status != ERROR_MORE_DATA)) { return status; }
info = LocalAlloc(LPTR, bufferSize);
if(info == NULL) { return GetLastError(); }
result = QueryInformationJobObject(Job, InfoClass, info, bufferSize, NULL);
if(!result) { status = GetLastError(); LocalFree(info); return status; }
if(info->SecurityLimitFlags == 0) { xprintf(2, "No security limitations on job\n"); } else { DumpFlags(2, "SecurityLimitFlags", info->SecurityLimitFlags, jobSecurityLimitFlags); }
LocalFree(info); return ERROR_SUCCESS; }
|