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)
11 /* INCLUDES *****************************************************************/
18 /* GLOBALS *******************************************************************/
20 POBJECT_TYPE PsJobType
= NULL
;
22 LIST_ENTRY PsJobListHead
;
23 static FAST_MUTEX PsJobListLock
;
25 BOOLEAN PspUseJobSchedulingClasses
;
27 CHAR PspJobSchedulingClasses
[PSP_JOB_SCHEDULING_CLASSES
] =
41 GENERIC_MAPPING PspJobMapping
=
43 STANDARD_RIGHTS_READ
| JOB_OBJECT_QUERY
,
45 STANDARD_RIGHTS_WRITE
| JOB_OBJECT_ASSIGN_PROCESS
|
46 JOB_OBJECT_SET_ATTRIBUTES
| JOB_OBJECT_TERMINATE
,
48 STANDARD_RIGHTS_EXECUTE
| SYNCHRONIZE
,
50 STANDARD_RIGHTS_ALL
| THREAD_ALL_ACCESS
// bug fixed only in vista
53 ULONG PspJobInfoLengths
[] =
56 sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION
),
57 sizeof(JOBOBJECT_BASIC_LIMIT_INFORMATION
),
58 sizeof(JOBOBJECT_BASIC_PROCESS_ID_LIST
),
59 sizeof(JOBOBJECT_BASIC_UI_RESTRICTIONS
),
60 sizeof(JOBOBJECT_SECURITY_LIMIT_INFORMATION
),
61 sizeof(JOBOBJECT_END_OF_JOB_TIME_INFORMATION
),
62 sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT
),
63 sizeof(JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION
),
64 sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION
),
68 ULONG PspJobInfoAlign
[] =
83 /* FUNCTIONS *****************************************************************/
87 PspDeleteJob ( PVOID ObjectBody
)
89 PEJOB Job
= (PEJOB
)ObjectBody
;
91 /* remove the reference to the completion port if associated */
92 if(Job
->CompletionPort
!= NULL
)
94 ObDereferenceObject(Job
->CompletionPort
);
97 /* unlink the job object */
98 if(Job
->JobLinks
.Flink
!= NULL
)
100 ExAcquireFastMutex(&PsJobListLock
);
101 RemoveEntryList(&Job
->JobLinks
);
102 ExReleaseFastMutex(&PsJobListLock
);
105 ExDeleteResource(&Job
->JobLock
);
111 PspInitializeJobStructures(VOID
)
113 InitializeListHead(&PsJobListHead
);
114 ExInitializeFastMutex(&PsJobListLock
);
119 PspAssignProcessToJob(PEPROCESS Process
,
122 DPRINT("PspAssignProcessToJob() is unimplemented!\n");
123 return STATUS_NOT_IMPLEMENTED
;
128 PspTerminateJobObject(PEJOB Job
,
129 KPROCESSOR_MODE AccessMode
,
130 NTSTATUS ExitStatus
)
132 DPRINT("PspTerminateJobObject() is unimplemented!\n");
133 return STATUS_NOT_IMPLEMENTED
;
138 PspRemoveProcessFromJob(IN PEPROCESS Process
,
146 PspExitProcessFromJob(IN PEJOB Job
,
147 IN PEPROCESS Process
)
157 NtAssignProcessToJobObject (
159 HANDLE ProcessHandle
)
162 KPROCESSOR_MODE PreviousMode
;
167 PreviousMode
= ExGetPreviousMode();
169 /* make sure we're having a handle with enough rights, especially the to
170 terminate the process. otherwise one could abuse the job objects to
171 terminate processes without having rights granted to do so! The reason
172 I open the process handle before the job handle is that a simple test showed
173 that it first complains about a invalid process handle! The other way around
174 would be simpler though... */
175 Status
= ObReferenceObjectByHandle(
182 if(NT_SUCCESS(Status
))
184 if(Process
->Job
== NULL
)
188 Status
= ObReferenceObjectByHandle(
190 JOB_OBJECT_ASSIGN_PROCESS
,
195 if(NT_SUCCESS(Status
))
197 /* lock the process so we can safely assign the process. Note that in the
198 meanwhile another thread could have assigned this process to a job! */
200 if(ExAcquireRundownProtection(&Process
->RundownProtect
))
202 if(Process
->Job
== NULL
&& PsGetProcessSessionId(Process
) == Job
->SessionId
)
204 /* Just store the pointer to the job object in the process, we'll
205 assign it later. The reason we can't do this here is that locking
206 the job object might require it to wait, which is a bad thing
207 while holding the process lock! */
209 ObReferenceObject(Job
);
213 /* process is already assigned to a job or session id differs! */
214 Status
= STATUS_ACCESS_DENIED
;
216 ExReleaseRundownProtection(&Process
->RundownProtect
);
218 if(NT_SUCCESS(Status
))
220 /* let's actually assign the process to the job as we're not holding
221 the process lock anymore! */
222 Status
= PspAssignProcessToJob(Process
, Job
);
226 ObDereferenceObject(Job
);
231 /* process is already assigned to a job or session id differs! */
232 Status
= STATUS_ACCESS_DENIED
;
235 ObDereferenceObject(Process
);
243 NtCreateJobSet(IN ULONG NumJob
,
244 IN PJOB_SET_ARRAY UserJobSet
,
248 return STATUS_NOT_IMPLEMENTED
;
258 ACCESS_MASK DesiredAccess
,
259 POBJECT_ATTRIBUTES ObjectAttributes
)
263 KPROCESSOR_MODE PreviousMode
;
264 PEPROCESS CurrentProcess
;
269 PreviousMode
= ExGetPreviousMode();
270 CurrentProcess
= PsGetCurrentProcess();
272 /* check for valid buffers */
273 if (PreviousMode
!= KernelMode
)
277 ProbeForWriteHandle(JobHandle
);
279 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
281 _SEH2_YIELD(return _SEH2_GetExceptionCode());
286 Status
= ObCreateObject(PreviousMode
,
296 if(NT_SUCCESS(Status
))
298 /* FIXME - Zero all fields as we don't yet implement all of them */
299 RtlZeroMemory(Job
, sizeof(EJOB
));
301 /* make sure that early destruction doesn't attempt to remove the object from
302 the list before it even gets added! */
303 Job
->JobLinks
.Flink
= NULL
;
305 /* setup the job object - FIXME: More to do! */
306 InitializeListHead(&Job
->JobSetLinks
);
307 InitializeListHead(&Job
->ProcessListHead
);
309 /* inherit the session id from the caller */
310 Job
->SessionId
= PsGetProcessSessionId(CurrentProcess
);
312 KeInitializeGuardedMutex(&Job
->MemoryLimitsLock
);
314 Status
= ExInitializeResource(&Job
->JobLock
);
315 if(!NT_SUCCESS(Status
))
317 DPRINT1("Failed to initialize job lock!!!\n");
318 ObDereferenceObject(Job
);
321 KeInitializeEvent(&Job
->Event
, NotificationEvent
, FALSE
);
323 /* link the object into the global job list */
324 ExAcquireFastMutex(&PsJobListLock
);
325 InsertTailList(&PsJobListHead
, &Job
->JobLinks
);
326 ExReleaseFastMutex(&PsJobListLock
);
328 Status
= ObInsertObject(Job
,
334 if(NT_SUCCESS(Status
))
336 /* pass the handle back to the caller */
339 /* NOTE: if the caller passed invalid buffers to receive the handle it's his
340 own fault! the object will still be created and live... It's possible
341 to find the handle using ObFindHandleForObject()! */
344 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
346 Status
= _SEH2_GetExceptionCode();
362 IN HANDLE ProcessHandle
,
363 IN HANDLE JobHandle OPTIONAL
)
365 KPROCESSOR_MODE PreviousMode
;
369 PreviousMode
= ExGetPreviousMode();
373 Status
= ObReferenceObjectByHandle(
375 PROCESS_QUERY_INFORMATION
,
380 if(NT_SUCCESS(Status
))
382 /* FIXME - make sure the job object doesn't get exchanged or deleted while trying to
383 reference it, e.g. by locking it somehow until it is referenced... */
385 PEJOB ProcessJob
= Process
->Job
;
387 if(ProcessJob
!= NULL
)
389 if(JobHandle
== NULL
)
391 /* the process is assigned to a job */
392 Status
= STATUS_PROCESS_IN_JOB
;
394 else /* JobHandle != NULL */
398 /* get the job object and compare the object pointer with the one assigned to the process */
399 Status
= ObReferenceObjectByHandle(JobHandle
,
405 if(NT_SUCCESS(Status
))
407 Status
= ((ProcessJob
== JobObject
) ? STATUS_PROCESS_IN_JOB
: STATUS_PROCESS_NOT_IN_JOB
);
408 ObDereferenceObject(JobObject
);
414 /* the process is not assigned to any job */
415 Status
= STATUS_PROCESS_NOT_IN_JOB
;
417 ObDereferenceObject(Process
);
431 ACCESS_MASK DesiredAccess
,
432 POBJECT_ATTRIBUTES ObjectAttributes
)
434 KPROCESSOR_MODE PreviousMode
;
440 PreviousMode
= ExGetPreviousMode();
442 /* check for valid buffers */
443 if (PreviousMode
!= KernelMode
)
447 ProbeForWriteHandle(JobHandle
);
449 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
451 _SEH2_YIELD(return _SEH2_GetExceptionCode());
456 Status
= ObOpenObjectByName(ObjectAttributes
,
463 if(NT_SUCCESS(Status
))
469 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
471 Status
= _SEH2_GetExceptionCode();
485 NtQueryInformationJobObject (
487 JOBOBJECTINFOCLASS JobInformationClass
,
488 PVOID JobInformation
,
489 ULONG JobInformationLength
,
490 PULONG ReturnLength
)
496 PLIST_ENTRY NextEntry
;
497 PKTHREAD CurrentThread
;
498 KPROCESSOR_MODE PreviousMode
;
499 JOBOBJECT_EXTENDED_LIMIT_INFORMATION ExtendedLimit
;
500 JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION BasicAndIo
;
501 ULONG RequiredLength
, RequiredAlign
, SizeToCopy
, NeededSize
;
505 CurrentThread
= KeGetCurrentThread();
508 if (JobInformationClass
> JobObjectJobSetInformation
|| JobInformationClass
< JobObjectBasicAccountingInformation
)
510 return STATUS_INVALID_INFO_CLASS
;
513 /* Get associated lengths & alignments */
514 RequiredLength
= PspJobInfoLengths
[JobInformationClass
];
515 RequiredAlign
= PspJobInfoAlign
[JobInformationClass
];
516 SizeToCopy
= RequiredLength
;
517 NeededSize
= RequiredLength
;
519 /* If length mismatch (needed versus provided) */
520 if (JobInformationLength
!= RequiredLength
)
522 /* This can only be accepted if: JobObjectBasicProcessIdList or JobObjectSecurityLimitInformation
523 * Or if size is bigger than needed
525 if ((JobInformationClass
!= JobObjectBasicProcessIdList
&& JobInformationClass
!= JobObjectSecurityLimitInformation
) ||
526 JobInformationLength
< RequiredLength
)
528 return STATUS_INFO_LENGTH_MISMATCH
;
531 /* Set what we need to copy out */
532 SizeToCopy
= JobInformationLength
;
535 PreviousMode
= ExGetPreviousMode();
536 /* If not comming from umode, we need to probe buffers */
537 if (PreviousMode
!= KernelMode
)
539 ASSERT(((RequiredAlign
) == 1) || ((RequiredAlign
) == 2) || ((RequiredAlign
) == 4) || ((RequiredAlign
) == 8) || ((RequiredAlign
) == 16));
543 /* Probe out buffer for write */
544 if (JobInformation
!= NULL
)
546 ProbeForWrite(JobInformation
, JobInformationLength
, RequiredAlign
);
549 /* But also return lenght if asked */
550 if (ReturnLength
!= NULL
)
552 ProbeForWrite(JobInformation
, sizeof(ULONG
), sizeof(ULONG
));
555 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
557 _SEH2_YIELD(return _SEH2_GetExceptionCode());
562 /* If a job handle was provided, use it */
563 if (JobHandle
!= NULL
)
565 Status
= ObReferenceObjectByHandle(JobHandle
,
571 if (!NT_SUCCESS(Status
))
576 /* Otherwise, get our current process' job, if any */
579 PEPROCESS CurrentProcess
;
581 CurrentProcess
= (PEPROCESS
)CurrentThread
->ApcState
.Process
;
582 Job
= CurrentProcess
->Job
;
585 return STATUS_ACCESS_DENIED
;
588 ObReferenceObject(Job
);
591 /* By default, assume we'll have to copy data */
594 switch (JobInformationClass
)
597 case JobObjectBasicAccountingInformation
:
598 case JobObjectBasicAndIoAccountingInformation
:
600 RtlZeroMemory(&BasicAndIo
.BasicInfo
, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION
));
603 KeEnterGuardedRegionThread(CurrentThread
);
604 ExAcquireResourceSharedLite(&Job
->JobLock
, TRUE
);
606 /* Initialize with job counters */
607 BasicAndIo
.BasicInfo
.TotalUserTime
.QuadPart
= Job
->TotalUserTime
.QuadPart
;
608 BasicAndIo
.BasicInfo
.TotalKernelTime
.QuadPart
= Job
->TotalKernelTime
.QuadPart
;
609 BasicAndIo
.BasicInfo
.ThisPeriodTotalUserTime
.QuadPart
= Job
->ThisPeriodTotalUserTime
.QuadPart
;
610 BasicAndIo
.BasicInfo
.ThisPeriodTotalKernelTime
.QuadPart
= Job
->ThisPeriodTotalKernelTime
.QuadPart
;
611 BasicAndIo
.BasicInfo
.TotalPageFaultCount
= Job
->TotalPageFaultCount
;
612 BasicAndIo
.BasicInfo
.TotalProcesses
= Job
->TotalProcesses
;
613 BasicAndIo
.BasicInfo
.ActiveProcesses
= Job
->ActiveProcesses
;
614 BasicAndIo
.BasicInfo
.TotalTerminatedProcesses
= Job
->TotalTerminatedProcesses
;
616 /* We also set IoInfo, even though we might not return it */
617 BasicAndIo
.IoInfo
.ReadOperationCount
= Job
->ReadOperationCount
;
618 BasicAndIo
.IoInfo
.WriteOperationCount
= Job
->WriteOperationCount
;
619 BasicAndIo
.IoInfo
.OtherOperationCount
= Job
->OtherOperationCount
;
620 BasicAndIo
.IoInfo
.ReadTransferCount
= Job
->ReadTransferCount
;
621 BasicAndIo
.IoInfo
.WriteTransferCount
= Job
->WriteTransferCount
;
622 BasicAndIo
.IoInfo
.OtherTransferCount
= Job
->OtherTransferCount
;
624 /* For every process, sum its counters */
625 for (NextEntry
= Job
->ProcessListHead
.Flink
;
626 NextEntry
!= &Job
->ProcessListHead
;
627 NextEntry
= NextEntry
->Flink
)
631 Process
= CONTAINING_RECORD(NextEntry
, EPROCESS
, JobLinks
);
632 if (!BooleanFlagOn(Process
->JobStatus
, 2))
634 /* FIXME: Call KeQueryValuesProcess()
635 * We should sum BasicInfo values here,
636 * but we don't have them
638 BasicAndIo
.IoInfo
.ReadOperationCount
+= Process
->ReadOperationCount
.QuadPart
;
639 BasicAndIo
.IoInfo
.WriteOperationCount
+= Process
->WriteOperationCount
.QuadPart
;
640 BasicAndIo
.IoInfo
.OtherOperationCount
+= Process
->OtherOperationCount
.QuadPart
;
641 BasicAndIo
.IoInfo
.ReadTransferCount
+= Process
->ReadTransferCount
.QuadPart
;
642 BasicAndIo
.IoInfo
.WriteTransferCount
+= Process
->WriteTransferCount
.QuadPart
;
643 BasicAndIo
.IoInfo
.OtherTransferCount
+= Process
->OtherTransferCount
.QuadPart
;
648 ExReleaseResourceLite(&Job
->JobLock
);
649 KeLeaveGuardedRegionThread(CurrentThread
);
651 /* We'll copy back the buffer */
652 GenericCopy
= &BasicAndIo
;
653 Status
= STATUS_SUCCESS
;
657 /* Limits information */
658 case JobObjectBasicLimitInformation
:
659 case JobObjectExtendedLimitInformation
:
661 KeEnterGuardedRegionThread(CurrentThread
);
662 ExAcquireResourceSharedLite(&Job
->JobLock
, TRUE
);
664 /* Copy basic information */
665 ExtendedLimit
.BasicLimitInformation
.LimitFlags
= Job
->LimitFlags
;
666 ExtendedLimit
.BasicLimitInformation
.MinimumWorkingSetSize
= Job
->MinimumWorkingSetSize
;
667 ExtendedLimit
.BasicLimitInformation
.MaximumWorkingSetSize
= Job
->MaximumWorkingSetSize
;
668 ExtendedLimit
.BasicLimitInformation
.ActiveProcessLimit
= Job
->ActiveProcessLimit
;
669 ExtendedLimit
.BasicLimitInformation
.PriorityClass
= Job
->PriorityClass
;
670 ExtendedLimit
.BasicLimitInformation
.SchedulingClass
= Job
->SchedulingClass
;
671 ExtendedLimit
.BasicLimitInformation
.Affinity
= Job
->Affinity
;
672 ExtendedLimit
.BasicLimitInformation
.PerProcessUserTimeLimit
.QuadPart
= Job
->PerProcessUserTimeLimit
.QuadPart
;
673 ExtendedLimit
.BasicLimitInformation
.PerJobUserTimeLimit
.QuadPart
= Job
->PerJobUserTimeLimit
.QuadPart
;
675 /* If asking for extending limits */
676 if (JobInformationClass
== JobObjectExtendedLimitInformation
)
678 /* Lock our memory lock */
679 KeAcquireGuardedMutexUnsafe(&Job
->MemoryLimitsLock
);
681 ExtendedLimit
.ProcessMemoryLimit
= Job
->ProcessMemoryLimit
<< PAGE_SHIFT
;
682 ExtendedLimit
.JobMemoryLimit
= Job
->JobMemoryLimit
<< PAGE_SHIFT
;
683 ExtendedLimit
.PeakProcessMemoryUsed
= Job
->PeakProcessMemoryUsed
<< PAGE_SHIFT
;
684 ExtendedLimit
.PeakJobMemoryUsed
= Job
->PeakJobMemoryUsed
<< PAGE_SHIFT
;
685 KeReleaseGuardedMutexUnsafe(&Job
->MemoryLimitsLock
);
688 ExReleaseResourceLite(&Job
->JobLock
);
689 KeLeaveGuardedRegionThread(CurrentThread
);
691 /* We'll never return IoInfo, so zero it out to avoid
694 RtlZeroMemory(&ExtendedLimit
.IoInfo
, sizeof(IO_COUNTERS
));
699 ExReleaseResourceLite(&Job
->JobLock
);
700 KeLeaveGuardedRegionThread(CurrentThread
);
703 /* We'll copy back the buffer */
704 GenericCopy
= &ExtendedLimit
;
705 Status
= STATUS_SUCCESS
;
710 DPRINT1("Class %d not implemented\n", JobInformationClass
);
711 Status
= STATUS_NOT_IMPLEMENTED
;
715 /* Job is no longer required */
716 ObDereferenceObject(Job
);
718 /* If we succeeed, copy back data */
719 if (NT_SUCCESS(Status
))
723 /* If we have anything to copy, do it */
726 RtlCopyMemory(JobInformation
, GenericCopy
, SizeToCopy
);
729 /* And return length if asked */
730 if (ReturnLength
!= NULL
)
732 *ReturnLength
= NeededSize
;
735 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
737 _SEH2_YIELD(return _SEH2_GetExceptionCode());
751 NtSetInformationJobObject (
753 JOBOBJECTINFOCLASS JobInformationClass
,
754 PVOID JobInformation
,
755 ULONG JobInformationLength
)
759 PKTHREAD CurrentThread
;
760 ACCESS_MASK DesiredAccess
;
761 KPROCESSOR_MODE PreviousMode
;
762 ULONG RequiredLength
, RequiredAlign
;
766 CurrentThread
= KeGetCurrentThread();
769 if (JobInformationClass
> JobObjectJobSetInformation
|| JobInformationClass
< JobObjectBasicAccountingInformation
)
771 return STATUS_INVALID_INFO_CLASS
;
774 /* Get associated lengths & alignments */
775 RequiredLength
= PspJobInfoLengths
[JobInformationClass
];
776 RequiredAlign
= PspJobInfoAlign
[JobInformationClass
];
778 PreviousMode
= ExGetPreviousMode();
779 /* If not comming from umode, we need to probe buffers */
780 if (PreviousMode
!= KernelMode
)
782 ASSERT(((RequiredAlign
) == 1) || ((RequiredAlign
) == 2) || ((RequiredAlign
) == 4) || ((RequiredAlign
) == 8) || ((RequiredAlign
) == 16));
786 /* Probe out buffer for read */
787 if (JobInformationLength
!= 0)
789 ProbeForRead(JobInformation
, JobInformationLength
, RequiredAlign
);
792 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
794 _SEH2_YIELD(return _SEH2_GetExceptionCode());
799 /* Validate input size */
800 if (JobInformationLength
!= RequiredLength
)
802 return STATUS_INFO_LENGTH_MISMATCH
;
805 /* Open the given job */
806 DesiredAccess
= JOB_OBJECT_SET_ATTRIBUTES
;
807 if (JobInformationClass
== JobObjectSecurityLimitInformation
)
809 DesiredAccess
|= JOB_OBJECT_SET_SECURITY_ATTRIBUTES
;
811 Status
= ObReferenceObjectByHandle(JobHandle
,
817 if (!NT_SUCCESS(Status
))
822 /* And set the information */
823 KeEnterGuardedRegionThread(CurrentThread
);
824 switch (JobInformationClass
)
826 case JobObjectExtendedLimitInformation
:
827 DPRINT1("Class JobObjectExtendedLimitInformation not implemented\n");
828 Status
= STATUS_SUCCESS
;
832 DPRINT1("Class %d not implemented\n", JobInformationClass
);
833 Status
= STATUS_NOT_IMPLEMENTED
;
836 KeLeaveGuardedRegionThread(CurrentThread
);
838 ObfDereferenceObject(Job
);
849 NtTerminateJobObject (
851 NTSTATUS ExitStatus
)
853 KPROCESSOR_MODE PreviousMode
;
859 PreviousMode
= ExGetPreviousMode();
861 Status
= ObReferenceObjectByHandle(
863 JOB_OBJECT_TERMINATE
,
868 if(NT_SUCCESS(Status
))
870 Status
= PspTerminateJobObject(
874 ObDereferenceObject(Job
);
886 PsGetJobLock ( PEJOB Job
)
889 return (PVOID
)&Job
->JobLock
;
898 PsGetJobSessionId ( PEJOB Job
)
901 return Job
->SessionId
;
910 PsGetJobUIRestrictionsClass ( PEJOB Job
)
913 return Job
->UIRestrictionsClass
;
922 PsSetJobUIRestrictionsClass(PEJOB Job
,
923 ULONG UIRestrictionsClass
)
926 (void)InterlockedExchangeUL(&Job
->UIRestrictionsClass
, UIRestrictionsClass
);
927 /* FIXME - walk through the job process list and update the restrictions? */