7bded05b5efb309742dc31f097a5eb1fe3e208e4
[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 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16
17 /* GLOBALS *******************************************************************/
18
19 POBJECT_TYPE PsJobType = NULL;
20
21 LIST_ENTRY PsJobListHead;
22 static FAST_MUTEX PsJobListLock;
23
24 BOOLEAN PspUseJobSchedulingClasses;
25
26 CHAR PspJobSchedulingClasses[PSP_JOB_SCHEDULING_CLASSES] =
27 {
28 1 * 6,
29 2 * 6,
30 3 * 6,
31 4 * 6,
32 5 * 6,
33 6 * 6,
34 7 * 6,
35 8 * 6,
36 9 * 6,
37 10 * 6
38 };
39
40 GENERIC_MAPPING PspJobMapping =
41 {
42 STANDARD_RIGHTS_READ | JOB_OBJECT_QUERY,
43 STANDARD_RIGHTS_WRITE | JOB_OBJECT_ASSIGN_PROCESS | JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_TERMINATE | JOB_OBJECT_SET_SECURITY_ATTRIBUTES,
44 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
45 STANDARD_RIGHTS_ALL | JOB_OBJECT_ALL_ACCESS
46 };
47
48 /* FUNCTIONS *****************************************************************/
49
50 VOID
51 NTAPI
52 PspDeleteJob ( PVOID ObjectBody )
53 {
54 PEJOB Job = (PEJOB)ObjectBody;
55
56 /* remove the reference to the completion port if associated */
57 if(Job->CompletionPort != NULL)
58 {
59 ObDereferenceObject(Job->CompletionPort);
60 }
61
62 /* unlink the job object */
63 if(Job->JobLinks.Flink != NULL)
64 {
65 ExAcquireFastMutex(&PsJobListLock);
66 RemoveEntryList(&Job->JobLinks);
67 ExReleaseFastMutex(&PsJobListLock);
68 }
69
70 ExDeleteResource(&Job->JobLock);
71 }
72
73 VOID
74 NTAPI
75 INIT_FUNCTION
76 PspInitializeJobStructures(VOID)
77 {
78 InitializeListHead(&PsJobListHead);
79 ExInitializeFastMutex(&PsJobListLock);
80 }
81
82 NTSTATUS
83 NTAPI
84 PspAssignProcessToJob(PEPROCESS Process,
85 PEJOB Job)
86 {
87 DPRINT("PspAssignProcessToJob() is unimplemented!\n");
88 return STATUS_NOT_IMPLEMENTED;
89 }
90
91 NTSTATUS
92 NTAPI
93 PspTerminateJobObject(PEJOB Job,
94 KPROCESSOR_MODE AccessMode,
95 NTSTATUS ExitStatus )
96 {
97 DPRINT("PspTerminateJobObject() is unimplemented!\n");
98 return STATUS_NOT_IMPLEMENTED;
99 }
100
101 VOID
102 NTAPI
103 PspRemoveProcessFromJob(IN PEPROCESS Process,
104 IN PEJOB Job)
105 {
106 /* FIXME */
107 }
108
109 VOID
110 NTAPI
111 PspExitProcessFromJob(IN PEJOB Job,
112 IN PEPROCESS Process)
113 {
114 /* FIXME */
115 }
116
117 /*
118 * @unimplemented
119 */
120 NTSTATUS
121 NTAPI
122 NtAssignProcessToJobObject (
123 HANDLE JobHandle,
124 HANDLE ProcessHandle)
125 {
126 PEPROCESS Process;
127 KPROCESSOR_MODE PreviousMode;
128 NTSTATUS Status;
129
130 PAGED_CODE();
131
132 PreviousMode = ExGetPreviousMode();
133
134 /* make sure we're having a handle with enough rights, especially the to
135 terminate the process. otherwise one could abuse the job objects to
136 terminate processes without having rights granted to do so! The reason
137 I open the process handle before the job handle is that a simple test showed
138 that it first complains about a invalid process handle! The other way around
139 would be simpler though... */
140 Status = ObReferenceObjectByHandle(
141 ProcessHandle,
142 PROCESS_TERMINATE,
143 PsProcessType,
144 PreviousMode,
145 (PVOID*)&Process,
146 NULL);
147 if(NT_SUCCESS(Status))
148 {
149 if(Process->Job == NULL)
150 {
151 PEJOB Job;
152
153 Status = ObReferenceObjectByHandle(
154 JobHandle,
155 JOB_OBJECT_ASSIGN_PROCESS,
156 PsJobType,
157 PreviousMode,
158 (PVOID*)&Job,
159 NULL);
160 if(NT_SUCCESS(Status))
161 {
162 /* lock the process so we can safely assign the process. Note that in the
163 meanwhile another thread could have assigned this process to a job! */
164
165 ExAcquireRundownProtection(&Process->RundownProtect);
166 if(NT_SUCCESS(Status))
167 {
168 if(Process->Job == NULL && Process->Session == Job->SessionId)
169 {
170 /* Just store the pointer to the job object in the process, we'll
171 assign it later. The reason we can't do this here is that locking
172 the job object might require it to wait, which is a bad thing
173 while holding the process lock! */
174 Process->Job = Job;
175 }
176 else
177 {
178 /* process is already assigned to a job or session id differs! */
179 Status = STATUS_ACCESS_DENIED;
180 }
181 ExReleaseRundownProtection(&Process->RundownProtect);
182
183 if(NT_SUCCESS(Status))
184 {
185 /* let's actually assign the process to the job as we're not holding
186 the process lock anymore! */
187 Status = PspAssignProcessToJob(Process, Job);
188 }
189 }
190
191 ObDereferenceObject(Job);
192 }
193 }
194 else
195 {
196 /* process is already assigned to a job or session id differs! */
197 Status = STATUS_ACCESS_DENIED;
198 }
199
200 ObDereferenceObject(Process);
201 }
202
203 return Status;
204 }
205
206 NTSTATUS
207 NTAPI
208 NtCreateJobSet(IN ULONG NumJob,
209 IN PJOB_SET_ARRAY UserJobSet,
210 IN ULONG Flags)
211 {
212 UNIMPLEMENTED;
213 return STATUS_NOT_IMPLEMENTED;
214 }
215
216 /*
217 * @unimplemented
218 */
219 NTSTATUS
220 NTAPI
221 NtCreateJobObject (
222 PHANDLE JobHandle,
223 ACCESS_MASK DesiredAccess,
224 POBJECT_ATTRIBUTES ObjectAttributes )
225 {
226 HANDLE hJob;
227 PEJOB Job;
228 KPROCESSOR_MODE PreviousMode;
229 PEPROCESS CurrentProcess;
230 NTSTATUS Status;
231
232 PAGED_CODE();
233
234 PreviousMode = ExGetPreviousMode();
235 CurrentProcess = PsGetCurrentProcess();
236
237 /* check for valid buffers */
238 if (PreviousMode != KernelMode)
239 {
240 _SEH2_TRY
241 {
242 ProbeForWriteHandle(JobHandle);
243 }
244 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
245 {
246 _SEH2_YIELD(return _SEH2_GetExceptionCode());
247 }
248 _SEH2_END;
249 }
250
251 Status = ObCreateObject(PreviousMode,
252 PsJobType,
253 ObjectAttributes,
254 PreviousMode,
255 NULL,
256 sizeof(EJOB),
257 0,
258 0,
259 (PVOID*)&Job);
260
261 if(NT_SUCCESS(Status))
262 {
263 /* FIXME - Zero all fields as we don't yet implement all of them */
264 RtlZeroMemory(Job, sizeof(EJOB));
265
266 /* make sure that early destruction doesn't attempt to remove the object from
267 the list before it even gets added! */
268 Job->JobLinks.Flink = NULL;
269
270 /* setup the job object */
271 InitializeListHead(&Job->ProcessListHead);
272 Job->SessionId = CurrentProcess->Session; /* inherit the session id from the caller */
273
274 Status = ExInitializeResource(&Job->JobLock);
275 if(!NT_SUCCESS(Status))
276 {
277 DPRINT1("Failed to initialize job lock!!!\n");
278 ObDereferenceObject(Job);
279 return Status;
280 }
281 KeInitializeEvent(&Job->Event, NotificationEvent, FALSE);
282
283 /* link the object into the global job list */
284 ExAcquireFastMutex(&PsJobListLock);
285 InsertTailList(&PsJobListHead, &Job->JobLinks);
286 ExReleaseFastMutex(&PsJobListLock);
287
288 Status = ObInsertObject(Job,
289 NULL,
290 DesiredAccess,
291 0,
292 NULL,
293 &hJob);
294 if(NT_SUCCESS(Status))
295 {
296 /* pass the handle back to the caller */
297 _SEH2_TRY
298 {
299 /* NOTE: if the caller passed invalid buffers to receive the handle it's his
300 own fault! the object will still be created and live... It's possible
301 to find the handle using ObFindHandleForObject()! */
302 *JobHandle = hJob;
303 }
304 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
305 {
306 Status = _SEH2_GetExceptionCode();
307 }
308 _SEH2_END;
309 }
310 }
311
312 return Status;
313 }
314
315
316 /*
317 * @implemented
318 */
319 NTSTATUS
320 NTAPI
321 NtIsProcessInJob (
322 IN HANDLE ProcessHandle,
323 IN HANDLE JobHandle OPTIONAL )
324 {
325 KPROCESSOR_MODE PreviousMode;
326 PEPROCESS Process;
327 NTSTATUS Status;
328
329 PreviousMode = ExGetPreviousMode();
330
331 PAGED_CODE();
332
333 Status = ObReferenceObjectByHandle(
334 ProcessHandle,
335 PROCESS_QUERY_INFORMATION,
336 PsProcessType,
337 PreviousMode,
338 (PVOID*)&Process,
339 NULL);
340 if(NT_SUCCESS(Status))
341 {
342 /* FIXME - make sure the job object doesn't get exchanged or deleted while trying to
343 reference it, e.g. by locking it somehow until it is referenced... */
344
345 PEJOB ProcessJob = Process->Job;
346
347 if(ProcessJob != NULL)
348 {
349 if(JobHandle == NULL)
350 {
351 /* the process is assigned to a job */
352 Status = STATUS_PROCESS_IN_JOB;
353 }
354 else /* JobHandle != NULL */
355 {
356 PEJOB JobObject;
357
358 /* get the job object and compare the object pointer with the one assigned to the process */
359 Status = ObReferenceObjectByHandle(JobHandle,
360 JOB_OBJECT_QUERY,
361 PsJobType,
362 PreviousMode,
363 (PVOID*)&JobObject,
364 NULL);
365 if(NT_SUCCESS(Status))
366 {
367 Status = ((ProcessJob == JobObject) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB);
368 ObDereferenceObject(JobObject);
369 }
370 }
371 }
372 else
373 {
374 /* the process is not assigned to any job */
375 Status = STATUS_PROCESS_NOT_IN_JOB;
376 }
377 ObDereferenceObject(Process);
378 }
379
380 return Status;
381 }
382
383
384 /*
385 * @implemented
386 */
387 NTSTATUS
388 NTAPI
389 NtOpenJobObject (
390 PHANDLE JobHandle,
391 ACCESS_MASK DesiredAccess,
392 POBJECT_ATTRIBUTES ObjectAttributes)
393 {
394 KPROCESSOR_MODE PreviousMode;
395 HANDLE hJob;
396 NTSTATUS Status;
397
398 PAGED_CODE();
399
400 PreviousMode = ExGetPreviousMode();
401
402 /* check for valid buffers */
403 if (PreviousMode != KernelMode)
404 {
405 _SEH2_TRY
406 {
407 ProbeForWriteHandle(JobHandle);
408 }
409 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
410 {
411 _SEH2_YIELD(return _SEH2_GetExceptionCode());
412 }
413 _SEH2_END;
414 }
415
416 Status = ObOpenObjectByName(ObjectAttributes,
417 PsJobType,
418 PreviousMode,
419 NULL,
420 DesiredAccess,
421 NULL,
422 &hJob);
423 if(NT_SUCCESS(Status))
424 {
425 _SEH2_TRY
426 {
427 *JobHandle = hJob;
428 }
429 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
430 {
431 Status = _SEH2_GetExceptionCode();
432 }
433 _SEH2_END;
434 }
435
436 return Status;
437 }
438
439
440 /*
441 * @unimplemented
442 */
443 NTSTATUS
444 NTAPI
445 NtQueryInformationJobObject (
446 HANDLE JobHandle,
447 JOBOBJECTINFOCLASS JobInformationClass,
448 PVOID JobInformation,
449 ULONG JobInformationLength,
450 PULONG ReturnLength )
451 {
452 UNIMPLEMENTED;
453 return STATUS_NOT_IMPLEMENTED;
454 }
455
456
457 /*
458 * @unimplemented
459 */
460 NTSTATUS
461 NTAPI
462 NtSetInformationJobObject (
463 HANDLE JobHandle,
464 JOBOBJECTINFOCLASS JobInformationClass,
465 PVOID JobInformation,
466 ULONG JobInformationLength)
467 {
468 UNIMPLEMENTED;
469 return STATUS_NOT_IMPLEMENTED;
470 }
471
472
473 /*
474 * @unimplemented
475 */
476 NTSTATUS
477 NTAPI
478 NtTerminateJobObject (
479 HANDLE JobHandle,
480 NTSTATUS ExitStatus )
481 {
482 KPROCESSOR_MODE PreviousMode;
483 PEJOB Job;
484 NTSTATUS Status;
485
486 PAGED_CODE();
487
488 PreviousMode = ExGetPreviousMode();
489
490 Status = ObReferenceObjectByHandle(
491 JobHandle,
492 JOB_OBJECT_TERMINATE,
493 PsJobType,
494 PreviousMode,
495 (PVOID*)&Job,
496 NULL);
497 if(NT_SUCCESS(Status))
498 {
499 Status = PspTerminateJobObject(
500 Job,
501 PreviousMode,
502 ExitStatus);
503 ObDereferenceObject(Job);
504 }
505
506 return Status;
507 }
508
509
510 /*
511 * @implemented
512 */
513 PVOID
514 NTAPI
515 PsGetJobLock ( PEJOB Job )
516 {
517 ASSERT(Job);
518 return (PVOID)&Job->JobLock;
519 }
520
521
522 /*
523 * @implemented
524 */
525 PVOID
526 NTAPI
527 PsGetJobSessionId ( PEJOB Job )
528 {
529 ASSERT(Job);
530 return (PVOID)Job->SessionId;
531 }
532
533
534 /*
535 * @implemented
536 */
537 ULONG
538 NTAPI
539 PsGetJobUIRestrictionsClass ( PEJOB Job )
540 {
541 ASSERT(Job);
542 return Job->UIRestrictionsClass;
543 }
544
545
546 /*
547 * @unimplemented
548 */
549 VOID
550 NTAPI
551 PsSetJobUIRestrictionsClass(PEJOB Job,
552 ULONG UIRestrictionsClass)
553 {
554 ASSERT(Job);
555 (void)InterlockedExchangeUL(&Job->UIRestrictionsClass, UIRestrictionsClass);
556 /* FIXME - walk through the job process list and update the restrictions? */
557 }
558
559 /* EOF */