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