Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

611 lines
18 KiB

  1. /*++
  2. Copyright (c) 1995 Microsoft Corporation
  3. Module Name:
  4. job.c
  5. Abstract:
  6. A user mode app that allows creation and management of jobs.
  7. Environment:
  8. User mode only
  9. Revision History:
  10. 03-26-96 : Created
  11. --*/
  12. //
  13. // this module may be compiled at warning level 4 with the following
  14. // warnings disabled:
  15. //
  16. #include <string.h>
  17. #include <stdio.h>
  18. #include <stdlib.h>
  19. #include <tchar.h>
  20. #include <assert.h>
  21. #include <windows.h>
  22. #include <devioctl.h>
  23. #include "jobmgr.h"
  24. typedef struct {
  25. char Option;
  26. JOBOBJECTINFOCLASS InfoClass;
  27. char *Name;
  28. DWORD (*Function)(HANDLE Job, JOBOBJECTINFOCLASS InfoClass);
  29. } JOB_QUERY_OPTION, *PJOB_QUERY_OPTION;
  30. #define MKOPTION(optChar, optClass) {optChar, optClass, #optClass, Dump##optClass}
  31. DWORD DumpJobObjectBasicProcessIdList(HANDLE JobHandle,
  32. JOBOBJECTINFOCLASS InfoClass);
  33. DWORD DumpJobObjectBasicUIRestrictions(HANDLE JobHandle,
  34. JOBOBJECTINFOCLASS InfoClass);
  35. DWORD
  36. DumpJobObjectBasicAndIoAccountingInformation(
  37. HANDLE JobHandle,
  38. JOBOBJECTINFOCLASS InfoClass
  39. );
  40. DWORD
  41. DumpJobObjectExtendedLimitInformation(
  42. HANDLE Job,
  43. JOBOBJECTINFOCLASS InfoClass
  44. );
  45. DWORD
  46. DumpJobObjectSecurityLimitInformation(
  47. HANDLE JobHandle,
  48. JOBOBJECTINFOCLASS InfoClass
  49. );
  50. JOB_QUERY_OPTION JobInfoClasses[] = {
  51. MKOPTION('a', JobObjectBasicAndIoAccountingInformation),
  52. MKOPTION('l', JobObjectExtendedLimitInformation),
  53. MKOPTION('p', JobObjectBasicProcessIdList),
  54. MKOPTION('s', JobObjectSecurityLimitInformation),
  55. MKOPTION('u', JobObjectBasicUIRestrictions),
  56. {'\0', 0, NULL}
  57. };
  58. DWORD
  59. QueryJobCommand(
  60. IN PCOMMAND CommandEntry,
  61. IN int argc,
  62. IN char* argv[]
  63. )
  64. {
  65. TCHAR defaultOptions[] = {TEXT('p'), TEXT('\0')};
  66. PTSTR options;
  67. PTSTR jobName;
  68. HANDLE job;
  69. int i;
  70. BOOLEAN matchAll = FALSE;
  71. DWORD status;
  72. if(argc == 0) {
  73. return -1;
  74. }
  75. GetAllProcessInfo();
  76. if((argc > 1) && (argv[0][0] == '-')) {
  77. // a - accounting & io
  78. // l - extended limit info
  79. // p - process id list
  80. // u - basic ui restrictions
  81. // s - security limits
  82. options = &(argv[0][1]);
  83. argc -= 1;
  84. argv += 1;
  85. } else {
  86. options = defaultOptions;
  87. }
  88. if(_tcschr(options, TEXT('*')) != NULL) {
  89. if(_tcslen(options) != 1) {
  90. printf("Cannot specify '*' with other flags\n");
  91. return -1;
  92. } else {
  93. matchAll = TRUE;
  94. options = defaultOptions;
  95. }
  96. }
  97. jobName = argv[0];
  98. _tprintf("Opening job object %s\n", jobName);
  99. job = OpenJobObject(JOB_OBJECT_QUERY, FALSE, jobName);
  100. if(job == NULL) {
  101. return GetLastError();
  102. }
  103. for(i = 0; JobInfoClasses[i].Option != '\0'; i++) {
  104. LPTSTR match;
  105. if(!matchAll) {
  106. match = _tcschr(options, JobInfoClasses[i].Option);
  107. if(match == NULL) {
  108. continue;
  109. }
  110. //
  111. // Clear the option so we can report the invalid option flags at the
  112. // end.
  113. //
  114. *match = ' ';
  115. }
  116. _tprintf("%s [%#x]:\n", JobInfoClasses[i].Name,
  117. JobInfoClasses[i].InfoClass);
  118. status = JobInfoClasses[i].Function(job,
  119. JobInfoClasses[i].InfoClass);
  120. _tprintf("\n");
  121. if(status != ERROR_SUCCESS) {
  122. DWORD length;
  123. PVOID buffer;
  124. _tprintf("Error %s querying info: ",
  125. CommandArray[i].Name, status);
  126. length = FormatMessage((FORMAT_MESSAGE_ALLOCATE_BUFFER |
  127. FORMAT_MESSAGE_FROM_SYSTEM |
  128. FORMAT_MESSAGE_IGNORE_INSERTS |
  129. (FORMAT_MESSAGE_MAX_WIDTH_MASK & 0)),
  130. NULL,
  131. status,
  132. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  133. (LPTSTR) &buffer,
  134. 1,
  135. NULL);
  136. if(length != 0) {
  137. _tprintf("%s", buffer);
  138. LocalFree(buffer);
  139. }
  140. }
  141. }
  142. if(!matchAll) {
  143. LPSTR header = "Option flag not understood:";
  144. while(options[0] != TEXT('\0')) {
  145. if(options[0] != TEXT(' ')) {
  146. _tprintf("%s %c", header, options[0]);
  147. header = "";
  148. }
  149. options += 1;
  150. }
  151. }
  152. #if 0
  153. for(;options[0] != '\0'; options += 1) {
  154. int i;
  155. for(i = 0; JobInfoClasses[i].Option != '\0'; i++) {
  156. if((options[0] != TEXT('*')) &&
  157. (JobInfoClasses[i].Option != tolower(options[0]))) {
  158. continue;
  159. }
  160. _tprintf("%s [%#x]:\n", JobInfoClasses[i].Name,
  161. JobInfoClasses[i].InfoClass);
  162. status = JobInfoClasses[i].Function(job,
  163. JobInfoClasses[i].InfoClass);
  164. _tprintf("\n");
  165. if(status != ERROR_SUCCESS) {
  166. DWORD length;
  167. PVOID buffer;
  168. _tprintf("Error %s querying info: ",
  169. CommandArray[i].Name, status);
  170. length = FormatMessage((FORMAT_MESSAGE_ALLOCATE_BUFFER |
  171. FORMAT_MESSAGE_FROM_SYSTEM |
  172. FORMAT_MESSAGE_IGNORE_INSERTS |
  173. (FORMAT_MESSAGE_MAX_WIDTH_MASK & 0)),
  174. NULL,
  175. status,
  176. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  177. (LPTSTR) &buffer,
  178. 1,
  179. NULL);
  180. if(length != 0) {
  181. _tprintf("%s", buffer);
  182. LocalFree(buffer);
  183. }
  184. }
  185. break;
  186. }
  187. if(JobInfoClasses[i].Option == '\0') {
  188. _tprintf("invalid option flag '%c'\n", options[0]);
  189. }
  190. }
  191. #endif
  192. CloseHandle(job);
  193. return ERROR_SUCCESS;
  194. }
  195. DWORD
  196. DumpJobObjectBasicProcessIdList(
  197. HANDLE Job,
  198. JOBOBJECTINFOCLASS InfoClass
  199. )
  200. {
  201. JOBOBJECT_BASIC_PROCESS_ID_LIST buffer;
  202. PJOBOBJECT_BASIC_PROCESS_ID_LIST idList = NULL;
  203. ULONG bufferSize;
  204. BOOL result;
  205. DWORD status;
  206. DWORD i;
  207. result = QueryInformationJobObject(Job,
  208. InfoClass,
  209. &buffer,
  210. sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST),
  211. NULL);
  212. status = GetLastError();
  213. if((!result) && (status != ERROR_MORE_DATA)) {
  214. return status;
  215. }
  216. do {
  217. if(idList != NULL) {
  218. buffer.NumberOfAssignedProcesses =
  219. idList->NumberOfAssignedProcesses;
  220. LocalFree(idList);
  221. idList = NULL;
  222. }
  223. //
  224. // Calculate the actual size of the list and allocate a buffer to hold it.
  225. //
  226. bufferSize = sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST);
  227. bufferSize -= sizeof(ULONG_PTR);
  228. bufferSize += sizeof(ULONG_PTR) * buffer.NumberOfAssignedProcesses;
  229. assert(idList == NULL);
  230. idList = LocalAlloc(LPTR, bufferSize);
  231. if(idList == NULL) {
  232. return ERROR_NOT_ENOUGH_MEMORY;
  233. }
  234. result = QueryInformationJobObject(Job,
  235. InfoClass,
  236. idList,
  237. bufferSize,
  238. NULL);
  239. status = GetLastError();
  240. if((!result) && (status != ERROR_MORE_DATA)) {
  241. LocalFree(idList);
  242. return status;
  243. }
  244. } while(idList->NumberOfAssignedProcesses >
  245. idList->NumberOfProcessIdsInList);
  246. assert(idList->NumberOfAssignedProcesses ==
  247. idList->NumberOfProcessIdsInList);
  248. //
  249. // Dump the information.
  250. //
  251. _tprintf(" %d processes assigned to job:\n",
  252. idList->NumberOfAssignedProcesses);
  253. for(i = 0; i < idList->NumberOfAssignedProcesses; i++) {
  254. _tprintf("%8d", idList->ProcessIdList[i]);
  255. PrintProcessInfo(idList->ProcessIdList[i]);
  256. _tprintf("\n");
  257. }
  258. LocalFree(idList);
  259. return ERROR_SUCCESS;
  260. }
  261. DWORD
  262. DumpJobObjectBasicUIRestrictions(
  263. HANDLE Job,
  264. JOBOBJECTINFOCLASS InfoClass
  265. )
  266. {
  267. JOBOBJECT_BASIC_UI_RESTRICTIONS uiLimit;
  268. static FLAG_NAME jobUiLimitFlags[] = {
  269. FLAG_NAME(JOB_OBJECT_UILIMIT_HANDLES ), //0x00000001
  270. FLAG_NAME(JOB_OBJECT_UILIMIT_READCLIPBOARD ), //0x00000002
  271. FLAG_NAME(JOB_OBJECT_UILIMIT_WRITECLIPBOARD ), //0x00000004
  272. FLAG_NAME(JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS ), //0x00000008
  273. FLAG_NAME(JOB_OBJECT_UILIMIT_DISPLAYSETTINGS ), //0x00000010
  274. FLAG_NAME(JOB_OBJECT_UILIMIT_GLOBALATOMS ), //0x00000020
  275. FLAG_NAME(JOB_OBJECT_UILIMIT_DESKTOP ), //0x00000040
  276. FLAG_NAME(JOB_OBJECT_UILIMIT_EXITWINDOWS ), //0x00000080
  277. {0,0}
  278. };
  279. BOOL result;
  280. DWORD status;
  281. DWORD i;
  282. result = QueryInformationJobObject(Job,
  283. InfoClass,
  284. &uiLimit,
  285. sizeof(JOBOBJECT_BASIC_UI_RESTRICTIONS),
  286. NULL);
  287. status = GetLastError();
  288. if(!result) {
  289. return status;
  290. }
  291. if(uiLimit.UIRestrictionsClass == JOB_OBJECT_UILIMIT_NONE) {
  292. _tprintf(" Job has no UI restrictions\n");
  293. return ERROR_SUCCESS;
  294. }
  295. DumpFlags(2,
  296. "UI Restrictions",
  297. uiLimit.UIRestrictionsClass,
  298. jobUiLimitFlags);
  299. return ERROR_SUCCESS;
  300. }
  301. DWORD
  302. DumpJobObjectBasicAndIoAccountingInformation(
  303. HANDLE Job,
  304. JOBOBJECTINFOCLASS InfoClass
  305. )
  306. {
  307. JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION info;
  308. BOOL result;
  309. DWORD status;
  310. DWORD i;
  311. result = QueryInformationJobObject(Job,
  312. InfoClass,
  313. &info,
  314. sizeof(info),
  315. NULL);
  316. status = GetLastError();
  317. if(!result) {
  318. return status;
  319. }
  320. xprintf(2, "Basic Info\n");
  321. xprintf(4, "TotalUserTime: %s\n", TicksToString(info.BasicInfo.TotalUserTime));
  322. xprintf(4, "TotalKernelTime: %s\n", TicksToString(info.BasicInfo.TotalKernelTime));
  323. xprintf(4, "ThisPeriodTotalUserTime: %s\n", TicksToString(info.BasicInfo.ThisPeriodTotalUserTime));
  324. xprintf(4, "ThisPeriodTotalKernelTime: %s\n", TicksToString(info.BasicInfo.ThisPeriodTotalKernelTime));
  325. xprintf(4, "TotalPageFaultCount: %d\n", info.BasicInfo.TotalPageFaultCount);
  326. xprintf(4, "TotalProcesses: %d\n", info.BasicInfo.TotalProcesses);
  327. xprintf(4, "ActiveProcesses: %d\n", info.BasicInfo.ActiveProcesses);
  328. xprintf(4, "TotalTerminatedProcesses: %d\n", info.BasicInfo.TotalTerminatedProcesses);
  329. xprintf(2, "I/O Info\n");
  330. xprintf(4, "ReadOperationCount: %I64d\n", info.IoInfo.ReadOperationCount);
  331. xprintf(4, "WriteOperationCount: %I64d\n", info.IoInfo.WriteOperationCount);
  332. xprintf(4, "OtherOperationCount: %I64d\n", info.IoInfo.OtherOperationCount);
  333. xprintf(4, "ReadTransferCount: %I64d\n", info.IoInfo.ReadTransferCount);
  334. xprintf(4, "WriteTransferCount: %I64d\n", info.IoInfo.WriteTransferCount);
  335. xprintf(4, "OtherTransferCount: %I64d\n", info.IoInfo.OtherTransferCount);
  336. return ERROR_SUCCESS;
  337. }
  338. DWORD
  339. DumpJobObjectExtendedLimitInformation(
  340. HANDLE Job,
  341. JOBOBJECTINFOCLASS InfoClass
  342. )
  343. {
  344. JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
  345. ULONG limits;
  346. static FLAG_NAME basicJobLimitFlags[] = {
  347. FLAG_NAME(JOB_OBJECT_LIMIT_WORKINGSET ), //0x00000001
  348. FLAG_NAME(JOB_OBJECT_LIMIT_PROCESS_TIME ), //0x00000002
  349. FLAG_NAME(JOB_OBJECT_LIMIT_JOB_TIME ), //0x00000004
  350. FLAG_NAME(JOB_OBJECT_LIMIT_ACTIVE_PROCESS ), //0x00000008
  351. FLAG_NAME(JOB_OBJECT_LIMIT_AFFINITY ), //0x00000010
  352. FLAG_NAME(JOB_OBJECT_LIMIT_PRIORITY_CLASS ), //0x00000020
  353. FLAG_NAME(JOB_OBJECT_LIMIT_PRESERVE_JOB_TIME ), //0x00000040
  354. FLAG_NAME(JOB_OBJECT_LIMIT_SCHEDULING_CLASS ), //0x00000080
  355. {0,0}
  356. };
  357. //
  358. // Extended Limits
  359. //
  360. static FLAG_NAME extendedJobLimitFlags[] = {
  361. FLAG_NAME(JOB_OBJECT_LIMIT_PROCESS_MEMORY ), //0x00000100
  362. FLAG_NAME(JOB_OBJECT_LIMIT_JOB_MEMORY ), //0x00000200
  363. FLAG_NAME(JOB_OBJECT_LIMIT_DIE_ON_UNHANDLED_EXCEPTION ), //0x00000400
  364. FLAG_NAME(JOB_OBJECT_LIMIT_BREAKAWAY_OK ), //0x00000800
  365. FLAG_NAME(JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK ), //0x00001000
  366. FLAG_NAME(JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE ), //0x00002000
  367. FLAG_NAME(JOB_OBJECT_LIMIT_RESERVED2 ), //0x00004000
  368. FLAG_NAME(JOB_OBJECT_LIMIT_RESERVED3 ), //0x00008000
  369. FLAG_NAME(JOB_OBJECT_LIMIT_RESERVED4 ), //0x00010000
  370. FLAG_NAME(JOB_OBJECT_LIMIT_RESERVED5 ), //0x00020000
  371. FLAG_NAME(JOB_OBJECT_LIMIT_RESERVED6 ), //0x00040000
  372. {0,0}
  373. };
  374. BOOL result;
  375. DWORD status;
  376. DWORD i;
  377. result = QueryInformationJobObject(Job,
  378. InfoClass,
  379. &info,
  380. sizeof(info),
  381. NULL);
  382. status = GetLastError();
  383. if(!result) {
  384. return status;
  385. }
  386. limits = info.BasicLimitInformation.LimitFlags;
  387. if(TEST_FLAG(limits, JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS) == 0) {
  388. xprintf(2, "No basic limits on job\n");
  389. } else {
  390. DumpFlags(2, "Basic Limit Flags", limits & JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS, basicJobLimitFlags);
  391. if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_PROCESS_TIME)) {
  392. xprintf(4, "PerProcessUserTimeLimit: %s\n", TicksToString(info.BasicLimitInformation.PerProcessUserTimeLimit));
  393. }
  394. if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_JOB_TIME)) {
  395. xprintf(4, "PerJobUserTimeLimit: %s\n", TicksToString(info.BasicLimitInformation.PerJobUserTimeLimit));
  396. }
  397. if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_WORKINGSET)) {
  398. xprintf(4, "MinimumWorkingSetSize: %I64d\n", (ULONGLONG) info.BasicLimitInformation.MinimumWorkingSetSize);
  399. xprintf(4, "MaximumWorkingSetSize: %I64d\n", (ULONGLONG) info.BasicLimitInformation.MaximumWorkingSetSize);
  400. }
  401. if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_ACTIVE_PROCESS)) {
  402. xprintf(4, "ActiveProcessLimit: %d\n",info.BasicLimitInformation.ActiveProcessLimit);
  403. }
  404. if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_AFFINITY)) {
  405. xprintf(4, "Affinity: %#I64x\n", (ULONGLONG)info.BasicLimitInformation.Affinity);
  406. }
  407. if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_PRIORITY_CLASS)) {
  408. xprintf(4, "PriorityClass: %d\n",info.BasicLimitInformation.PriorityClass);
  409. }
  410. if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_SCHEDULING_CLASS)) {
  411. xprintf(4, "SchedulingClass: %d\n",info.BasicLimitInformation.SchedulingClass);
  412. }
  413. }
  414. if(TEST_FLAG(limits, JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS) == 0) {
  415. xprintf(2, "No extended limits on job\n");
  416. } else {
  417. DumpFlags(2, "Extended Limit Flags", limits & JOB_OBJECT_EXTENDED_LIMIT_VALID_FLAGS & ~JOB_OBJECT_BASIC_LIMIT_VALID_FLAGS, extendedJobLimitFlags);
  418. if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_PROCESS_MEMORY)) {
  419. xprintf(4, "ProcessMemoryLimit: %I64d\n", (ULONGLONG) info.ProcessMemoryLimit);
  420. }
  421. if(TEST_FLAG(limits, JOB_OBJECT_LIMIT_PROCESS_MEMORY)) {
  422. xprintf(4, "JobMemoryLimit: %I64d\n", (ULONGLONG) info.JobMemoryLimit);
  423. }
  424. }
  425. xprintf(2, "PeakProcessMemoryUsed: %I64d\n", (ULONGLONG) info.PeakProcessMemoryUsed);
  426. xprintf(2, "PeakJobMemoryUsed: %I64d\n", (ULONGLONG) info.PeakJobMemoryUsed);
  427. return ERROR_SUCCESS;
  428. }
  429. DWORD
  430. DumpJobObjectSecurityLimitInformation(
  431. HANDLE Job,
  432. JOBOBJECTINFOCLASS InfoClass
  433. )
  434. {
  435. JOBOBJECT_SECURITY_LIMIT_INFORMATION buffer;
  436. PJOBOBJECT_SECURITY_LIMIT_INFORMATION info = NULL;
  437. static FLAG_NAME jobSecurityLimitFlags[] = {
  438. FLAG_NAME(JOB_OBJECT_SECURITY_NO_ADMIN ), //00000001
  439. FLAG_NAME(JOB_OBJECT_SECURITY_RESTRICTED_TOKEN ), //00000002
  440. FLAG_NAME(JOB_OBJECT_SECURITY_ONLY_TOKEN ), //00000004
  441. FLAG_NAME(JOB_OBJECT_SECURITY_FILTER_TOKENS ), //00000008
  442. {0, 0}
  443. };
  444. ULONG bufferSize;
  445. BOOL result;
  446. DWORD status;
  447. DWORD i;
  448. result = QueryInformationJobObject(Job,
  449. InfoClass,
  450. &buffer,
  451. sizeof(buffer),
  452. &bufferSize);
  453. status = GetLastError();
  454. if((!result) && (status != ERROR_MORE_DATA)) {
  455. return status;
  456. }
  457. info = LocalAlloc(LPTR, bufferSize);
  458. if(info == NULL) {
  459. return GetLastError();
  460. }
  461. result = QueryInformationJobObject(Job, InfoClass, info, bufferSize, NULL);
  462. if(!result) {
  463. status = GetLastError();
  464. LocalFree(info);
  465. return status;
  466. }
  467. if(info->SecurityLimitFlags == 0) {
  468. xprintf(2, "No security limitations on job\n");
  469. } else {
  470. DumpFlags(2, "SecurityLimitFlags", info->SecurityLimitFlags, jobSecurityLimitFlags);
  471. }
  472. LocalFree(info);
  473. return ERROR_SUCCESS;
  474. }