[NTOSKRNL] Implement NtQueryInformationJobObject().
[reactos.git] / ntoskrnl / ps / job.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ps/job.c
5 * PURPOSE: Job Native Functions
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net) (stubs)
7 * Thomas Weidenmueller <w3seek@reactos.com>
8 * Pierre Schweitzer (pierre@reactos.org)
9 */
10
11 /* INCLUDES *****************************************************************/
12
13 #include <ntoskrnl.h>
14 #define NDEBUG
15 #include <debug.h>
16
17
18 /* GLOBALS *******************************************************************/
19
20 POBJECT_TYPE PsJobType = NULL;
21
22 LIST_ENTRY PsJobListHead;
23 static FAST_MUTEX PsJobListLock;
24
25 BOOLEAN PspUseJobSchedulingClasses;
26
27 CHAR PspJobSchedulingClasses[PSP_JOB_SCHEDULING_CLASSES] =
28 {
29 1 * 6,
30 2 * 6,
31 3 * 6,
32 4 * 6,
33 5 * 6,
34 6 * 6,
35 7 * 6,
36 8 * 6,
37 9 * 6,
38 10 * 6
39 };
40
41 GENERIC_MAPPING PspJobMapping =
42 {
43 STANDARD_RIGHTS_READ | JOB_OBJECT_QUERY,
44
45 STANDARD_RIGHTS_WRITE | JOB_OBJECT_ASSIGN_PROCESS |
46 JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_TERMINATE,
47
48 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
49
50 STANDARD_RIGHTS_ALL | THREAD_ALL_ACCESS // bug fixed only in vista
51 };
52
53 /* FUNCTIONS *****************************************************************/
54
55 VOID
56 NTAPI
57 PspDeleteJob ( PVOID ObjectBody )
58 {
59 PEJOB Job = (PEJOB)ObjectBody;
60
61 /* remove the reference to the completion port if associated */
62 if(Job->CompletionPort != NULL)
63 {
64 ObDereferenceObject(Job->CompletionPort);
65 }
66
67 /* unlink the job object */
68 if(Job->JobLinks.Flink != NULL)
69 {
70 ExAcquireFastMutex(&PsJobListLock);
71 RemoveEntryList(&Job->JobLinks);
72 ExReleaseFastMutex(&PsJobListLock);
73 }
74
75 ExDeleteResource(&Job->JobLock);
76 }
77
78 VOID
79 NTAPI
80 INIT_FUNCTION
81 PspInitializeJobStructures(VOID)
82 {
83 InitializeListHead(&PsJobListHead);
84 ExInitializeFastMutex(&PsJobListLock);
85 }
86
87 NTSTATUS
88 NTAPI
89 PspAssignProcessToJob(PEPROCESS Process,
90 PEJOB Job)
91 {
92 DPRINT("PspAssignProcessToJob() is unimplemented!\n");
93 return STATUS_NOT_IMPLEMENTED;
94 }
95
96 NTSTATUS
97 NTAPI
98 PspTerminateJobObject(PEJOB Job,
99 KPROCESSOR_MODE AccessMode,
100 NTSTATUS ExitStatus )
101 {
102 DPRINT("PspTerminateJobObject() is unimplemented!\n");
103 return STATUS_NOT_IMPLEMENTED;
104 }
105
106 VOID
107 NTAPI
108 PspRemoveProcessFromJob(IN PEPROCESS Process,
109 IN PEJOB Job)
110 {
111 /* FIXME */
112 }
113
114 VOID
115 NTAPI
116 PspExitProcessFromJob(IN PEJOB Job,
117 IN PEPROCESS Process)
118 {
119 /* FIXME */
120 }
121
122 /*
123 * @unimplemented
124 */
125 NTSTATUS
126 NTAPI
127 NtAssignProcessToJobObject (
128 HANDLE JobHandle,
129 HANDLE ProcessHandle)
130 {
131 PEPROCESS Process;
132 KPROCESSOR_MODE PreviousMode;
133 NTSTATUS Status;
134
135 PAGED_CODE();
136
137 PreviousMode = ExGetPreviousMode();
138
139 /* make sure we're having a handle with enough rights, especially the to
140 terminate the process. otherwise one could abuse the job objects to
141 terminate processes without having rights granted to do so! The reason
142 I open the process handle before the job handle is that a simple test showed
143 that it first complains about a invalid process handle! The other way around
144 would be simpler though... */
145 Status = ObReferenceObjectByHandle(
146 ProcessHandle,
147 PROCESS_TERMINATE,
148 PsProcessType,
149 PreviousMode,
150 (PVOID*)&Process,
151 NULL);
152 if(NT_SUCCESS(Status))
153 {
154 if(Process->Job == NULL)
155 {
156 PEJOB Job;
157
158 Status = ObReferenceObjectByHandle(
159 JobHandle,
160 JOB_OBJECT_ASSIGN_PROCESS,
161 PsJobType,
162 PreviousMode,
163 (PVOID*)&Job,
164 NULL);
165 if(NT_SUCCESS(Status))
166 {
167 /* lock the process so we can safely assign the process. Note that in the
168 meanwhile another thread could have assigned this process to a job! */
169
170 if(ExAcquireRundownProtection(&Process->RundownProtect))
171 {
172 if(Process->Job == NULL && PsGetProcessSessionId(Process) == Job->SessionId)
173 {
174 /* Just store the pointer to the job object in the process, we'll
175 assign it later. The reason we can't do this here is that locking
176 the job object might require it to wait, which is a bad thing
177 while holding the process lock! */
178 Process->Job = Job;
179 ObReferenceObject(Job);
180 }
181 else
182 {
183 /* process is already assigned to a job or session id differs! */
184 Status = STATUS_ACCESS_DENIED;
185 }
186 ExReleaseRundownProtection(&Process->RundownProtect);
187
188 if(NT_SUCCESS(Status))
189 {
190 /* let's actually assign the process to the job as we're not holding
191 the process lock anymore! */
192 Status = PspAssignProcessToJob(Process, Job);
193 }
194 }
195
196 ObDereferenceObject(Job);
197 }
198 }
199 else
200 {
201 /* process is already assigned to a job or session id differs! */
202 Status = STATUS_ACCESS_DENIED;
203 }
204
205 ObDereferenceObject(Process);
206 }
207
208 return Status;
209 }
210
211 NTSTATUS
212 NTAPI
213 NtCreateJobSet(IN ULONG NumJob,
214 IN PJOB_SET_ARRAY UserJobSet,
215 IN ULONG Flags)
216 {
217 UNIMPLEMENTED;
218 return STATUS_NOT_IMPLEMENTED;
219 }
220
221 /*
222 * @unimplemented
223 */
224 NTSTATUS
225 NTAPI
226 NtCreateJobObject (
227 PHANDLE JobHandle,
228 ACCESS_MASK DesiredAccess,
229 POBJECT_ATTRIBUTES ObjectAttributes )
230 {
231 HANDLE hJob;
232 PEJOB Job;
233 KPROCESSOR_MODE PreviousMode;
234 PEPROCESS CurrentProcess;
235 NTSTATUS Status;
236
237 PAGED_CODE();
238
239 PreviousMode = ExGetPreviousMode();
240 CurrentProcess = PsGetCurrentProcess();
241
242 /* check for valid buffers */
243 if (PreviousMode != KernelMode)
244 {
245 _SEH2_TRY
246 {
247 ProbeForWriteHandle(JobHandle);
248 }
249 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
250 {
251 _SEH2_YIELD(return _SEH2_GetExceptionCode());
252 }
253 _SEH2_END;
254 }
255
256 Status = ObCreateObject(PreviousMode,
257 PsJobType,
258 ObjectAttributes,
259 PreviousMode,
260 NULL,
261 sizeof(EJOB),
262 0,
263 0,
264 (PVOID*)&Job);
265
266 if(NT_SUCCESS(Status))
267 {
268 /* FIXME - Zero all fields as we don't yet implement all of them */
269 RtlZeroMemory(Job, sizeof(EJOB));
270
271 /* make sure that early destruction doesn't attempt to remove the object from
272 the list before it even gets added! */
273 Job->JobLinks.Flink = NULL;
274
275 /* setup the job object - FIXME: More to do! */
276 InitializeListHead(&Job->JobSetLinks);
277 InitializeListHead(&Job->ProcessListHead);
278
279 /* inherit the session id from the caller */
280 Job->SessionId = PsGetProcessSessionId(CurrentProcess);
281
282 KeInitializeGuardedMutex(&Job->MemoryLimitsLock);
283
284 Status = ExInitializeResource(&Job->JobLock);
285 if(!NT_SUCCESS(Status))
286 {
287 DPRINT1("Failed to initialize job lock!!!\n");
288 ObDereferenceObject(Job);
289 return Status;
290 }
291 KeInitializeEvent(&Job->Event, NotificationEvent, FALSE);
292
293 /* link the object into the global job list */
294 ExAcquireFastMutex(&PsJobListLock);
295 InsertTailList(&PsJobListHead, &Job->JobLinks);
296 ExReleaseFastMutex(&PsJobListLock);
297
298 Status = ObInsertObject(Job,
299 NULL,
300 DesiredAccess,
301 0,
302 NULL,
303 &hJob);
304 if(NT_SUCCESS(Status))
305 {
306 /* pass the handle back to the caller */
307 _SEH2_TRY
308 {
309 /* NOTE: if the caller passed invalid buffers to receive the handle it's his
310 own fault! the object will still be created and live... It's possible
311 to find the handle using ObFindHandleForObject()! */
312 *JobHandle = hJob;
313 }
314 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
315 {
316 Status = _SEH2_GetExceptionCode();
317 }
318 _SEH2_END;
319 }
320 }
321
322 return Status;
323 }
324
325
326 /*
327 * @implemented
328 */
329 NTSTATUS
330 NTAPI
331 NtIsProcessInJob (
332 IN HANDLE ProcessHandle,
333 IN HANDLE JobHandle OPTIONAL )
334 {
335 KPROCESSOR_MODE PreviousMode;
336 PEPROCESS Process;
337 NTSTATUS Status;
338
339 PreviousMode = ExGetPreviousMode();
340
341 PAGED_CODE();
342
343 Status = ObReferenceObjectByHandle(
344 ProcessHandle,
345 PROCESS_QUERY_INFORMATION,
346 PsProcessType,
347 PreviousMode,
348 (PVOID*)&Process,
349 NULL);
350 if(NT_SUCCESS(Status))
351 {
352 /* FIXME - make sure the job object doesn't get exchanged or deleted while trying to
353 reference it, e.g. by locking it somehow until it is referenced... */
354
355 PEJOB ProcessJob = Process->Job;
356
357 if(ProcessJob != NULL)
358 {
359 if(JobHandle == NULL)
360 {
361 /* the process is assigned to a job */
362 Status = STATUS_PROCESS_IN_JOB;
363 }
364 else /* JobHandle != NULL */
365 {
366 PEJOB JobObject;
367
368 /* get the job object and compare the object pointer with the one assigned to the process */
369 Status = ObReferenceObjectByHandle(JobHandle,
370 JOB_OBJECT_QUERY,
371 PsJobType,
372 PreviousMode,
373 (PVOID*)&JobObject,
374 NULL);
375 if(NT_SUCCESS(Status))
376 {
377 Status = ((ProcessJob == JobObject) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB);
378 ObDereferenceObject(JobObject);
379 }
380 }
381 }
382 else
383 {
384 /* the process is not assigned to any job */
385 Status = STATUS_PROCESS_NOT_IN_JOB;
386 }
387 ObDereferenceObject(Process);
388 }
389
390 return Status;
391 }
392
393
394 /*
395 * @implemented
396 */
397 NTSTATUS
398 NTAPI
399 NtOpenJobObject (
400 PHANDLE JobHandle,
401 ACCESS_MASK DesiredAccess,
402 POBJECT_ATTRIBUTES ObjectAttributes)
403 {
404 KPROCESSOR_MODE PreviousMode;
405 HANDLE hJob;
406 NTSTATUS Status;
407
408 PAGED_CODE();
409
410 PreviousMode = ExGetPreviousMode();
411
412 /* check for valid buffers */
413 if (PreviousMode != KernelMode)
414 {
415 _SEH2_TRY
416 {
417 ProbeForWriteHandle(JobHandle);
418 }
419 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
420 {
421 _SEH2_YIELD(return _SEH2_GetExceptionCode());
422 }
423 _SEH2_END;
424 }
425
426 Status = ObOpenObjectByName(ObjectAttributes,
427 PsJobType,
428 PreviousMode,
429 NULL,
430 DesiredAccess,
431 NULL,
432 &hJob);
433 if(NT_SUCCESS(Status))
434 {
435 _SEH2_TRY
436 {
437 *JobHandle = hJob;
438 }
439 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
440 {
441 Status = _SEH2_GetExceptionCode();
442 }
443 _SEH2_END;
444 }
445
446 return Status;
447 }
448
449
450 ULONG PspJobInfoLengths[] = { 0x0, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
451 sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION),
452 0x0C, 0x4, 0x14, 0x4, 0x8,
453 sizeof(JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION),
454 sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), 0x4 };
455 ULONG PspJobInfoAlign[] = { 0x0, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, 0x4 };
456
457 /*
458 * @implemented
459 */
460 NTSTATUS
461 NTAPI
462 NtQueryInformationJobObject (
463 HANDLE JobHandle,
464 JOBOBJECTINFOCLASS JobInformationClass,
465 PVOID JobInformation,
466 ULONG JobInformationLength,
467 PULONG ReturnLength )
468 {
469 PEJOB Job;
470 NTSTATUS Status;
471 BOOLEAN NoOutput;
472 PVOID GenericCopy;
473 PLIST_ENTRY NextEntry;
474 PKTHREAD CurrentThread;
475 KPROCESSOR_MODE PreviousMode;
476 JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimit;
477 JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION BasicAndIo;
478 ULONG RequiredLength, RequiredAlign, SizeToCopy, NeededSize;
479
480 PAGED_CODE();
481
482 CurrentThread = KeGetCurrentThread();
483
484 /* Validate class */
485 if (JobInformationClass > JobObjectJobSetInformation || JobInformationClass < JobObjectBasicAccountingInformation)
486 {
487 return STATUS_INVALID_INFO_CLASS;
488 }
489
490 /* Get associated lengths & alignments */
491 RequiredLength = PspJobInfoLengths[JobInformationClass];
492 RequiredAlign = PspJobInfoAlign[JobInformationClass];
493 SizeToCopy = RequiredLength;
494 NeededSize = RequiredLength;
495
496 /* If length mismatch (needed versus provided) */
497 if (JobInformationLength != RequiredLength)
498 {
499 /* This can only be accepted if: JobObjectBasicProcessIdList or JobObjectSecurityLimitInformation
500 * Or if size is bigger than needed
501 */
502 if ((JobInformationClass != JobObjectBasicProcessIdList && JobInformationClass != JobObjectSecurityLimitInformation) ||
503 JobInformationLength < RequiredLength)
504 {
505 return STATUS_INFO_LENGTH_MISMATCH;
506 }
507
508 /* Set what we need to copy out */
509 SizeToCopy = JobInformationLength;
510 }
511
512 PreviousMode = ExGetPreviousMode();
513 /* If not comming from umode, we need to probe buffers */
514 if (PreviousMode != KernelMode)
515 {
516 ASSERT(((RequiredAlign) == 1) || ((RequiredAlign) == 2) || ((RequiredAlign) == 4) || ((RequiredAlign) == 8) || ((RequiredAlign) == 16));
517
518 _SEH2_TRY
519 {
520 /* Probe out buffer for write */
521 if (JobInformation != NULL)
522 {
523 ProbeForWrite(JobInformation, JobInformationLength, RequiredAlign);
524 }
525
526 /* But also return lenght if asked */
527 if (ReturnLength != NULL)
528 {
529 ProbeForWrite(JobInformation, sizeof(ULONG), sizeof(ULONG));
530 }
531 }
532 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
533 {
534 _SEH2_YIELD(return _SEH2_GetExceptionCode());
535 }
536 _SEH2_END;
537 }
538
539 /* If a job handle was provided, use it */
540 if (JobHandle != NULL)
541 {
542 Status = ObReferenceObjectByHandle(JobHandle,
543 JOB_OBJECT_QUERY,
544 PsJobType,
545 PreviousMode,
546 (PVOID*)&Job,
547 NULL);
548 if (!NT_SUCCESS(Status))
549 {
550 return Status;
551 }
552 }
553 /* Otherwise, get our current process' job, if any */
554 else
555 {
556 PEPROCESS CurrentProcess;
557
558 CurrentProcess = (PEPROCESS)CurrentThread->ApcState.Process;
559 Job = CurrentProcess->Job;
560 if (Job == NULL)
561 {
562 return STATUS_ACCESS_DENIED;
563 }
564
565 ObReferenceObject(Job);
566 }
567
568 /* By default, assume we'll have to copy data */
569 NoOutput = FALSE;
570 /* Select class */
571 switch (JobInformationClass)
572 {
573 /* Basic counters */
574 case JobObjectBasicAccountingInformation:
575 case JobObjectBasicAndIoAccountingInformation:
576 /* Zero basics */
577 RtlZeroMemory(&BasicAndIo.BasicInfo, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION));
578
579 /* Lock */
580 KeEnterGuardedRegionThread(CurrentThread);
581 ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
582
583 /* Initialize with job counters */
584 BasicAndIo.BasicInfo.TotalUserTime.QuadPart = Job->TotalUserTime.QuadPart;
585 BasicAndIo.BasicInfo.TotalKernelTime.QuadPart = Job->TotalKernelTime.QuadPart;
586 BasicAndIo.BasicInfo.ThisPeriodTotalUserTime.QuadPart = Job->ThisPeriodTotalUserTime.QuadPart;
587 BasicAndIo.BasicInfo.ThisPeriodTotalKernelTime.QuadPart = Job->ThisPeriodTotalKernelTime.QuadPart;
588 BasicAndIo.BasicInfo.TotalPageFaultCount = Job->TotalPageFaultCount;
589 BasicAndIo.BasicInfo.TotalProcesses = Job->TotalProcesses;
590 BasicAndIo.BasicInfo.ActiveProcesses = Job->ActiveProcesses;
591 BasicAndIo.BasicInfo.TotalTerminatedProcesses = Job->TotalTerminatedProcesses;
592
593 /* We also set IoInfo, even though we might not return it */
594 BasicAndIo.IoInfo.ReadOperationCount = Job->ReadOperationCount;
595 BasicAndIo.IoInfo.WriteOperationCount = Job->WriteOperationCount;
596 BasicAndIo.IoInfo.OtherOperationCount = Job->OtherOperationCount;
597 BasicAndIo.IoInfo.ReadTransferCount = Job->ReadTransferCount;
598 BasicAndIo.IoInfo.WriteTransferCount = Job->WriteTransferCount;
599 BasicAndIo.IoInfo.OtherTransferCount = Job->OtherTransferCount;
600
601 /* For every process, sum its counters */
602 for (NextEntry = Job->ProcessListHead.Flink;
603 NextEntry != &Job->ProcessListHead;
604 NextEntry = NextEntry->Flink)
605 {
606 PEPROCESS Process;
607
608 Process = CONTAINING_RECORD(NextEntry, EPROCESS, JobLinks);
609 if (!BooleanFlagOn(Process->JobStatus, 2))
610 {
611 /* FIXME: Call KeQueryValuesProcess()
612 * We should sum BasicInfo values here,
613 * but we don't have them
614 */
615 BasicAndIo.IoInfo.ReadOperationCount += Process->ReadOperationCount.QuadPart;
616 BasicAndIo.IoInfo.WriteOperationCount += Process->WriteOperationCount.QuadPart;
617 BasicAndIo.IoInfo.OtherOperationCount += Process->OtherOperationCount.QuadPart;
618 BasicAndIo.IoInfo.ReadTransferCount += Process->ReadTransferCount.QuadPart;
619 BasicAndIo.IoInfo.WriteTransferCount += Process->WriteTransferCount.QuadPart;
620 BasicAndIo.IoInfo.OtherTransferCount += Process->OtherTransferCount.QuadPart;
621 }
622 }
623
624 /* And done */
625 ExReleaseResourceLite(&Job->JobLock);
626 KeLeaveGuardedRegionThread(CurrentThread);
627
628 /* We'll copy back the buffer */
629 GenericCopy = &BasicAndIo;
630 Status = STATUS_SUCCESS;
631
632 break;
633
634 /* Limits information */
635 case JobObjectBasicLimitInformation:
636 case JobObjectExtendedLimitInformation:
637 /* Lock */
638 KeEnterGuardedRegionThread(CurrentThread);
639 ExAcquireResourceSharedLite(&Job->JobLock, TRUE);
640
641 /* Copy basic information */
642 ExtendedLimit.BasicLimitInformation.LimitFlags = Job->LimitFlags;
643 ExtendedLimit.BasicLimitInformation.MinimumWorkingSetSize = Job->MinimumWorkingSetSize;
644 ExtendedLimit.BasicLimitInformation.MaximumWorkingSetSize = Job->MaximumWorkingSetSize;
645 ExtendedLimit.BasicLimitInformation.ActiveProcessLimit = Job->ActiveProcessLimit;
646 ExtendedLimit.BasicLimitInformation.PriorityClass = Job->PriorityClass;
647 ExtendedLimit.BasicLimitInformation.SchedulingClass = Job->SchedulingClass;
648 ExtendedLimit.BasicLimitInformation.Affinity = Job->Affinity;
649 ExtendedLimit.BasicLimitInformation.PerProcessUserTimeLimit.QuadPart = Job->PerProcessUserTimeLimit.QuadPart;
650 ExtendedLimit.BasicLimitInformation.PerJobUserTimeLimit.QuadPart = Job->PerJobUserTimeLimit.QuadPart;
651
652 /* If asking for extending limits */
653 if (JobInformationClass == JobObjectExtendedLimitInformation)
654 {
655 /* Lock our memory lock */
656 KeAcquireGuardedMutexUnsafe(&Job->MemoryLimitsLock);
657 /* Return limits */
658 ExtendedLimit.ProcessMemoryLimit = Job->ProcessMemoryLimit << PAGE_SHIFT;
659 ExtendedLimit.JobMemoryLimit = Job->JobMemoryLimit << PAGE_SHIFT;
660 ExtendedLimit.PeakProcessMemoryUsed = Job->PeakProcessMemoryUsed << PAGE_SHIFT;
661 ExtendedLimit.PeakJobMemoryUsed = Job->PeakJobMemoryUsed << PAGE_SHIFT;
662 KeReleaseGuardedMutexUnsafe(&Job->MemoryLimitsLock);
663
664 /* And done */
665 ExReleaseResourceLite(&Job->JobLock);
666 KeLeaveGuardedRegionThread(CurrentThread);
667
668 /* We'll never return IoInfo, so zero it out to avoid
669 * kernel memory leak
670 */
671 RtlZeroMemory(&ExtendedLimit.IoInfo, sizeof(IO_COUNTERS));
672 }
673 else
674 {
675 /* And done */
676 ExReleaseResourceLite(&Job->JobLock);
677 KeLeaveGuardedRegionThread(CurrentThread);
678 }
679
680 /* We'll copy back the buffer */
681 GenericCopy = &ExtendedLimit;
682 Status = STATUS_SUCCESS;
683
684 break;
685
686 default:
687 DPRINT1("Class %d not implemented\n", JobInformationClass);
688 Status = STATUS_NOT_IMPLEMENTED;
689 break;
690 }
691
692 /* Job is no longer required */
693 ObDereferenceObject(Job);
694
695 /* If we succeeed, copy back data */
696 if (NT_SUCCESS(Status))
697 {
698 _SEH2_TRY
699 {
700 /* If we have anything to copy, do it */
701 if (!NoOutput)
702 {
703 RtlCopyMemory(JobInformation, GenericCopy, SizeToCopy);
704 }
705
706 /* And return length if asked */
707 if (ReturnLength != NULL)
708 {
709 *ReturnLength = NeededSize;
710 }
711 }
712 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
713 {
714 _SEH2_YIELD(return _SEH2_GetExceptionCode());
715 }
716 _SEH2_END;
717 }
718
719 return Status;
720 }
721
722
723 /*
724 * @unimplemented
725 */
726 NTSTATUS
727 NTAPI
728 NtSetInformationJobObject (
729 HANDLE JobHandle,
730 JOBOBJECTINFOCLASS JobInformationClass,
731 PVOID JobInformation,
732 ULONG JobInformationLength)
733 {
734 UNIMPLEMENTED;
735 return STATUS_NOT_IMPLEMENTED;
736 }
737
738
739 /*
740 * @unimplemented
741 */
742 NTSTATUS
743 NTAPI
744 NtTerminateJobObject (
745 HANDLE JobHandle,
746 NTSTATUS ExitStatus )
747 {
748 KPROCESSOR_MODE PreviousMode;
749 PEJOB Job;
750 NTSTATUS Status;
751
752 PAGED_CODE();
753
754 PreviousMode = ExGetPreviousMode();
755
756 Status = ObReferenceObjectByHandle(
757 JobHandle,
758 JOB_OBJECT_TERMINATE,
759 PsJobType,
760 PreviousMode,
761 (PVOID*)&Job,
762 NULL);
763 if(NT_SUCCESS(Status))
764 {
765 Status = PspTerminateJobObject(
766 Job,
767 PreviousMode,
768 ExitStatus);
769 ObDereferenceObject(Job);
770 }
771
772 return Status;
773 }
774
775
776 /*
777 * @implemented
778 */
779 PVOID
780 NTAPI
781 PsGetJobLock ( PEJOB Job )
782 {
783 ASSERT(Job);
784 return (PVOID)&Job->JobLock;
785 }
786
787
788 /*
789 * @implemented
790 */
791 ULONG
792 NTAPI
793 PsGetJobSessionId ( PEJOB Job )
794 {
795 ASSERT(Job);
796 return Job->SessionId;
797 }
798
799
800 /*
801 * @implemented
802 */
803 ULONG
804 NTAPI
805 PsGetJobUIRestrictionsClass ( PEJOB Job )
806 {
807 ASSERT(Job);
808 return Job->UIRestrictionsClass;
809 }
810
811
812 /*
813 * @unimplemented
814 */
815 VOID
816 NTAPI
817 PsSetJobUIRestrictionsClass(PEJOB Job,
818 ULONG UIRestrictionsClass)
819 {
820 ASSERT(Job);
821 (void)InterlockedExchangeUL(&Job->UIRestrictionsClass, UIRestrictionsClass);
822 /* FIXME - walk through the job process list and update the restrictions? */
823 }
824
825 /* EOF */