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 /* FUNCTIONS *****************************************************************/
57 PspDeleteJob ( PVOID ObjectBody
)
59 PEJOB Job
= (PEJOB
)ObjectBody
;
61 /* remove the reference to the completion port if associated */
62 if(Job
->CompletionPort
!= NULL
)
64 ObDereferenceObject(Job
->CompletionPort
);
67 /* unlink the job object */
68 if(Job
->JobLinks
.Flink
!= NULL
)
70 ExAcquireFastMutex(&PsJobListLock
);
71 RemoveEntryList(&Job
->JobLinks
);
72 ExReleaseFastMutex(&PsJobListLock
);
75 ExDeleteResource(&Job
->JobLock
);
81 PspInitializeJobStructures(VOID
)
83 InitializeListHead(&PsJobListHead
);
84 ExInitializeFastMutex(&PsJobListLock
);
89 PspAssignProcessToJob(PEPROCESS Process
,
92 DPRINT("PspAssignProcessToJob() is unimplemented!\n");
93 return STATUS_NOT_IMPLEMENTED
;
98 PspTerminateJobObject(PEJOB Job
,
99 KPROCESSOR_MODE AccessMode
,
100 NTSTATUS ExitStatus
)
102 DPRINT("PspTerminateJobObject() is unimplemented!\n");
103 return STATUS_NOT_IMPLEMENTED
;
108 PspRemoveProcessFromJob(IN PEPROCESS Process
,
116 PspExitProcessFromJob(IN PEJOB Job
,
117 IN PEPROCESS Process
)
127 NtAssignProcessToJobObject (
129 HANDLE ProcessHandle
)
132 KPROCESSOR_MODE PreviousMode
;
137 PreviousMode
= ExGetPreviousMode();
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(
152 if(NT_SUCCESS(Status
))
154 if(Process
->Job
== NULL
)
158 Status
= ObReferenceObjectByHandle(
160 JOB_OBJECT_ASSIGN_PROCESS
,
165 if(NT_SUCCESS(Status
))
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! */
170 if(ExAcquireRundownProtection(&Process
->RundownProtect
))
172 if(Process
->Job
== NULL
&& PsGetProcessSessionId(Process
) == Job
->SessionId
)
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! */
179 ObReferenceObject(Job
);
183 /* process is already assigned to a job or session id differs! */
184 Status
= STATUS_ACCESS_DENIED
;
186 ExReleaseRundownProtection(&Process
->RundownProtect
);
188 if(NT_SUCCESS(Status
))
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
);
196 ObDereferenceObject(Job
);
201 /* process is already assigned to a job or session id differs! */
202 Status
= STATUS_ACCESS_DENIED
;
205 ObDereferenceObject(Process
);
213 NtCreateJobSet(IN ULONG NumJob
,
214 IN PJOB_SET_ARRAY UserJobSet
,
218 return STATUS_NOT_IMPLEMENTED
;
228 ACCESS_MASK DesiredAccess
,
229 POBJECT_ATTRIBUTES ObjectAttributes
)
233 KPROCESSOR_MODE PreviousMode
;
234 PEPROCESS CurrentProcess
;
239 PreviousMode
= ExGetPreviousMode();
240 CurrentProcess
= PsGetCurrentProcess();
242 /* check for valid buffers */
243 if (PreviousMode
!= KernelMode
)
247 ProbeForWriteHandle(JobHandle
);
249 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
251 _SEH2_YIELD(return _SEH2_GetExceptionCode());
256 Status
= ObCreateObject(PreviousMode
,
266 if(NT_SUCCESS(Status
))
268 /* FIXME - Zero all fields as we don't yet implement all of them */
269 RtlZeroMemory(Job
, sizeof(EJOB
));
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
;
275 /* setup the job object - FIXME: More to do! */
276 InitializeListHead(&Job
->JobSetLinks
);
277 InitializeListHead(&Job
->ProcessListHead
);
279 /* inherit the session id from the caller */
280 Job
->SessionId
= PsGetProcessSessionId(CurrentProcess
);
282 KeInitializeGuardedMutex(&Job
->MemoryLimitsLock
);
284 Status
= ExInitializeResource(&Job
->JobLock
);
285 if(!NT_SUCCESS(Status
))
287 DPRINT1("Failed to initialize job lock!!!\n");
288 ObDereferenceObject(Job
);
291 KeInitializeEvent(&Job
->Event
, NotificationEvent
, FALSE
);
293 /* link the object into the global job list */
294 ExAcquireFastMutex(&PsJobListLock
);
295 InsertTailList(&PsJobListHead
, &Job
->JobLinks
);
296 ExReleaseFastMutex(&PsJobListLock
);
298 Status
= ObInsertObject(Job
,
304 if(NT_SUCCESS(Status
))
306 /* pass the handle back to the caller */
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()! */
314 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
316 Status
= _SEH2_GetExceptionCode();
332 IN HANDLE ProcessHandle
,
333 IN HANDLE JobHandle OPTIONAL
)
335 KPROCESSOR_MODE PreviousMode
;
339 PreviousMode
= ExGetPreviousMode();
343 Status
= ObReferenceObjectByHandle(
345 PROCESS_QUERY_INFORMATION
,
350 if(NT_SUCCESS(Status
))
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... */
355 PEJOB ProcessJob
= Process
->Job
;
357 if(ProcessJob
!= NULL
)
359 if(JobHandle
== NULL
)
361 /* the process is assigned to a job */
362 Status
= STATUS_PROCESS_IN_JOB
;
364 else /* JobHandle != NULL */
368 /* get the job object and compare the object pointer with the one assigned to the process */
369 Status
= ObReferenceObjectByHandle(JobHandle
,
375 if(NT_SUCCESS(Status
))
377 Status
= ((ProcessJob
== JobObject
) ? STATUS_PROCESS_IN_JOB
: STATUS_PROCESS_NOT_IN_JOB
);
378 ObDereferenceObject(JobObject
);
384 /* the process is not assigned to any job */
385 Status
= STATUS_PROCESS_NOT_IN_JOB
;
387 ObDereferenceObject(Process
);
401 ACCESS_MASK DesiredAccess
,
402 POBJECT_ATTRIBUTES ObjectAttributes
)
404 KPROCESSOR_MODE PreviousMode
;
410 PreviousMode
= ExGetPreviousMode();
412 /* check for valid buffers */
413 if (PreviousMode
!= KernelMode
)
417 ProbeForWriteHandle(JobHandle
);
419 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
421 _SEH2_YIELD(return _SEH2_GetExceptionCode());
426 Status
= ObOpenObjectByName(ObjectAttributes
,
433 if(NT_SUCCESS(Status
))
439 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
441 Status
= _SEH2_GetExceptionCode();
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 };
462 NtQueryInformationJobObject (
464 JOBOBJECTINFOCLASS JobInformationClass
,
465 PVOID JobInformation
,
466 ULONG JobInformationLength
,
467 PULONG ReturnLength
)
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
;
482 CurrentThread
= KeGetCurrentThread();
485 if (JobInformationClass
> JobObjectJobSetInformation
|| JobInformationClass
< JobObjectBasicAccountingInformation
)
487 return STATUS_INVALID_INFO_CLASS
;
490 /* Get associated lengths & alignments */
491 RequiredLength
= PspJobInfoLengths
[JobInformationClass
];
492 RequiredAlign
= PspJobInfoAlign
[JobInformationClass
];
493 SizeToCopy
= RequiredLength
;
494 NeededSize
= RequiredLength
;
496 /* If length mismatch (needed versus provided) */
497 if (JobInformationLength
!= RequiredLength
)
499 /* This can only be accepted if: JobObjectBasicProcessIdList or JobObjectSecurityLimitInformation
500 * Or if size is bigger than needed
502 if ((JobInformationClass
!= JobObjectBasicProcessIdList
&& JobInformationClass
!= JobObjectSecurityLimitInformation
) ||
503 JobInformationLength
< RequiredLength
)
505 return STATUS_INFO_LENGTH_MISMATCH
;
508 /* Set what we need to copy out */
509 SizeToCopy
= JobInformationLength
;
512 PreviousMode
= ExGetPreviousMode();
513 /* If not comming from umode, we need to probe buffers */
514 if (PreviousMode
!= KernelMode
)
516 ASSERT(((RequiredAlign
) == 1) || ((RequiredAlign
) == 2) || ((RequiredAlign
) == 4) || ((RequiredAlign
) == 8) || ((RequiredAlign
) == 16));
520 /* Probe out buffer for write */
521 if (JobInformation
!= NULL
)
523 ProbeForWrite(JobInformation
, JobInformationLength
, RequiredAlign
);
526 /* But also return lenght if asked */
527 if (ReturnLength
!= NULL
)
529 ProbeForWrite(JobInformation
, sizeof(ULONG
), sizeof(ULONG
));
532 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
534 _SEH2_YIELD(return _SEH2_GetExceptionCode());
539 /* If a job handle was provided, use it */
540 if (JobHandle
!= NULL
)
542 Status
= ObReferenceObjectByHandle(JobHandle
,
548 if (!NT_SUCCESS(Status
))
553 /* Otherwise, get our current process' job, if any */
556 PEPROCESS CurrentProcess
;
558 CurrentProcess
= (PEPROCESS
)CurrentThread
->ApcState
.Process
;
559 Job
= CurrentProcess
->Job
;
562 return STATUS_ACCESS_DENIED
;
565 ObReferenceObject(Job
);
568 /* By default, assume we'll have to copy data */
571 switch (JobInformationClass
)
574 case JobObjectBasicAccountingInformation
:
575 case JobObjectBasicAndIoAccountingInformation
:
577 RtlZeroMemory(&BasicAndIo
.BasicInfo
, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION
));
580 KeEnterGuardedRegionThread(CurrentThread
);
581 ExAcquireResourceSharedLite(&Job
->JobLock
, TRUE
);
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
;
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
;
601 /* For every process, sum its counters */
602 for (NextEntry
= Job
->ProcessListHead
.Flink
;
603 NextEntry
!= &Job
->ProcessListHead
;
604 NextEntry
= NextEntry
->Flink
)
608 Process
= CONTAINING_RECORD(NextEntry
, EPROCESS
, JobLinks
);
609 if (!BooleanFlagOn(Process
->JobStatus
, 2))
611 /* FIXME: Call KeQueryValuesProcess()
612 * We should sum BasicInfo values here,
613 * but we don't have them
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
;
625 ExReleaseResourceLite(&Job
->JobLock
);
626 KeLeaveGuardedRegionThread(CurrentThread
);
628 /* We'll copy back the buffer */
629 GenericCopy
= &BasicAndIo
;
630 Status
= STATUS_SUCCESS
;
634 /* Limits information */
635 case JobObjectBasicLimitInformation
:
636 case JobObjectExtendedLimitInformation
:
638 KeEnterGuardedRegionThread(CurrentThread
);
639 ExAcquireResourceSharedLite(&Job
->JobLock
, TRUE
);
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
;
652 /* If asking for extending limits */
653 if (JobInformationClass
== JobObjectExtendedLimitInformation
)
655 /* Lock our memory lock */
656 KeAcquireGuardedMutexUnsafe(&Job
->MemoryLimitsLock
);
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
);
665 ExReleaseResourceLite(&Job
->JobLock
);
666 KeLeaveGuardedRegionThread(CurrentThread
);
668 /* We'll never return IoInfo, so zero it out to avoid
671 RtlZeroMemory(&ExtendedLimit
.IoInfo
, sizeof(IO_COUNTERS
));
676 ExReleaseResourceLite(&Job
->JobLock
);
677 KeLeaveGuardedRegionThread(CurrentThread
);
680 /* We'll copy back the buffer */
681 GenericCopy
= &ExtendedLimit
;
682 Status
= STATUS_SUCCESS
;
687 DPRINT1("Class %d not implemented\n", JobInformationClass
);
688 Status
= STATUS_NOT_IMPLEMENTED
;
692 /* Job is no longer required */
693 ObDereferenceObject(Job
);
695 /* If we succeeed, copy back data */
696 if (NT_SUCCESS(Status
))
700 /* If we have anything to copy, do it */
703 RtlCopyMemory(JobInformation
, GenericCopy
, SizeToCopy
);
706 /* And return length if asked */
707 if (ReturnLength
!= NULL
)
709 *ReturnLength
= NeededSize
;
712 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
714 _SEH2_YIELD(return _SEH2_GetExceptionCode());
728 NtSetInformationJobObject (
730 JOBOBJECTINFOCLASS JobInformationClass
,
731 PVOID JobInformation
,
732 ULONG JobInformationLength
)
735 return STATUS_NOT_IMPLEMENTED
;
744 NtTerminateJobObject (
746 NTSTATUS ExitStatus
)
748 KPROCESSOR_MODE PreviousMode
;
754 PreviousMode
= ExGetPreviousMode();
756 Status
= ObReferenceObjectByHandle(
758 JOB_OBJECT_TERMINATE
,
763 if(NT_SUCCESS(Status
))
765 Status
= PspTerminateJobObject(
769 ObDereferenceObject(Job
);
781 PsGetJobLock ( PEJOB Job
)
784 return (PVOID
)&Job
->JobLock
;
793 PsGetJobSessionId ( PEJOB Job
)
796 return Job
->SessionId
;
805 PsGetJobUIRestrictionsClass ( PEJOB Job
)
808 return Job
->UIRestrictionsClass
;
817 PsSetJobUIRestrictionsClass(PEJOB Job
,
818 ULONG UIRestrictionsClass
)
821 (void)InterlockedExchangeUL(&Job
->UIRestrictionsClass
, UIRestrictionsClass
);
822 /* FIXME - walk through the job process list and update the restrictions? */