/*++ 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 #include #include #include #include #include #include #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[] ) { TCHAR defaultOptions[] = {TEXT('p'), TEXT('\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(_tcschr(options, TEXT('*')) != NULL) { if(_tcslen(options) != 1) { printf("Cannot specify '*' with other flags\n"); return -1; } else { matchAll = TRUE; options = defaultOptions; } } jobName = argv[0]; _tprintf("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++) { LPTSTR match; if(!matchAll) { match = _tcschr(options, JobInfoClasses[i].Option); if(match == NULL) { continue; } // // Clear the option so we can report the invalid option flags at the // end. // *match = ' '; } _tprintf("%s [%#x]:\n", JobInfoClasses[i].Name, JobInfoClasses[i].InfoClass); status = JobInfoClasses[i].Function(job, JobInfoClasses[i].InfoClass); _tprintf("\n"); if(status != ERROR_SUCCESS) { DWORD length; PVOID buffer; _tprintf("Error %s querying info: ", CommandArray[i].Name, 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), (LPTSTR) &buffer, 1, NULL); if(length != 0) { _tprintf("%s", buffer); LocalFree(buffer); } } } if(!matchAll) { LPSTR header = "Option flag not understood:"; while(options[0] != TEXT('\0')) { if(options[0] != TEXT(' ')) { _tprintf("%s %c", header, options[0]); header = ""; } options += 1; } } #if 0 for(;options[0] != '\0'; options += 1) { int i; for(i = 0; JobInfoClasses[i].Option != '\0'; i++) { if((options[0] != TEXT('*')) && (JobInfoClasses[i].Option != tolower(options[0]))) { continue; } _tprintf("%s [%#x]:\n", JobInfoClasses[i].Name, JobInfoClasses[i].InfoClass); status = JobInfoClasses[i].Function(job, JobInfoClasses[i].InfoClass); _tprintf("\n"); if(status != ERROR_SUCCESS) { DWORD length; PVOID buffer; _tprintf("Error %s querying info: ", CommandArray[i].Name, 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), (LPTSTR) &buffer, 1, NULL); if(length != 0) { _tprintf("%s", buffer); LocalFree(buffer); } } break; } if(JobInfoClasses[i].Option == '\0') { _tprintf("invalid option flag '%c'\n", options[0]); } } #endif 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 = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST); bufferSize -= sizeof(ULONG_PTR); bufferSize += sizeof(ULONG_PTR) * buffer.NumberOfAssignedProcesses; assert(idList == NULL); idList = LocalAlloc(LPTR, bufferSize); if(idList == NULL) { return ERROR_NOT_ENOUGH_MEMORY; } 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. // _tprintf(" %d processes assigned to job:\n", idList->NumberOfAssignedProcesses); for(i = 0; i < idList->NumberOfAssignedProcesses; i++) { _tprintf("%8d", idList->ProcessIdList[i]); PrintProcessInfo(idList->ProcessIdList[i]); _tprintf("\n"); } 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) { _tprintf(" 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(info), 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(info), 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(buffer), &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; }