Synchronize up to trunk's revision r57784.
[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
44 STANDARD_RIGHTS_WRITE | JOB_OBJECT_ASSIGN_PROCESS |
45 JOB_OBJECT_SET_ATTRIBUTES | JOB_OBJECT_TERMINATE,
46
47 STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
48
49 STANDARD_RIGHTS_ALL | THREAD_ALL_ACCESS // bug fixed only in vista
50 };
51
52 /* FUNCTIONS *****************************************************************/
53
54 VOID
55 NTAPI
56 PspDeleteJob ( PVOID ObjectBody )
57 {
58 PEJOB Job = (PEJOB)ObjectBody;
59
60 /* remove the reference to the completion port if associated */
61 if(Job->CompletionPort != NULL)
62 {
63 ObDereferenceObject(Job->CompletionPort);
64 }
65
66 /* unlink the job object */
67 if(Job->JobLinks.Flink != NULL)
68 {
69 ExAcquireFastMutex(&PsJobListLock);
70 RemoveEntryList(&Job->JobLinks);
71 ExReleaseFastMutex(&PsJobListLock);
72 }
73
74 ExDeleteResource(&Job->JobLock);
75 }
76
77 VOID
78 NTAPI
79 INIT_FUNCTION
80 PspInitializeJobStructures(VOID)
81 {
82 InitializeListHead(&PsJobListHead);
83 ExInitializeFastMutex(&PsJobListLock);
84 }
85
86 NTSTATUS
87 NTAPI
88 PspAssignProcessToJob(PEPROCESS Process,
89 PEJOB Job)
90 {
91 DPRINT("PspAssignProcessToJob() is unimplemented!\n");
92 return STATUS_NOT_IMPLEMENTED;
93 }
94
95 NTSTATUS
96 NTAPI
97 PspTerminateJobObject(PEJOB Job,
98 KPROCESSOR_MODE AccessMode,
99 NTSTATUS ExitStatus )
100 {
101 DPRINT("PspTerminateJobObject() is unimplemented!\n");
102 return STATUS_NOT_IMPLEMENTED;
103 }
104
105 VOID
106 NTAPI
107 PspRemoveProcessFromJob(IN PEPROCESS Process,
108 IN PEJOB Job)
109 {
110 /* FIXME */
111 }
112
113 VOID
114 NTAPI
115 PspExitProcessFromJob(IN PEJOB Job,
116 IN PEPROCESS Process)
117 {
118 /* FIXME */
119 }
120
121 /*
122 * @unimplemented
123 */
124 NTSTATUS
125 NTAPI
126 NtAssignProcessToJobObject (
127 HANDLE JobHandle,
128 HANDLE ProcessHandle)
129 {
130 PEPROCESS Process;
131 KPROCESSOR_MODE PreviousMode;
132 NTSTATUS Status;
133
134 PAGED_CODE();
135
136 PreviousMode = ExGetPreviousMode();
137
138 /* make sure we're having a handle with enough rights, especially the to
139 terminate the process. otherwise one could abuse the job objects to
140 terminate processes without having rights granted to do so! The reason
141 I open the process handle before the job handle is that a simple test showed
142 that it first complains about a invalid process handle! The other way around
143 would be simpler though... */
144 Status = ObReferenceObjectByHandle(
145 ProcessHandle,
146 PROCESS_TERMINATE,
147 PsProcessType,
148 PreviousMode,
149 (PVOID*)&Process,
150 NULL);
151 if(NT_SUCCESS(Status))
152 {
153 if(Process->Job == NULL)
154 {
155 PEJOB Job;
156
157 Status = ObReferenceObjectByHandle(
158 JobHandle,
159 JOB_OBJECT_ASSIGN_PROCESS,
160 PsJobType,
161 PreviousMode,
162 (PVOID*)&Job,
163 NULL);
164 if(NT_SUCCESS(Status))
165 {
166 /* lock the process so we can safely assign the process. Note that in the
167 meanwhile another thread could have assigned this process to a job! */
168
169 ExAcquireRundownProtection(&Process->RundownProtect);
170 if(NT_SUCCESS(Status))
171 {
172 // FIXME: This is broken
173 if(Process->Job == NULL && PtrToUlong(Process->Session) == Job->SessionId)
174 {
175 /* Just store the pointer to the job object in the process, we'll
176 assign it later. The reason we can't do this here is that locking
177 the job object might require it to wait, which is a bad thing
178 while holding the process lock! */
179 Process->Job = 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 */
276 InitializeListHead(&Job->ProcessListHead);
277 Job->SessionId = PtrToUlong(CurrentProcess->Session); /* inherit the session id from the caller, FIXME: broken */
278
279 Status = ExInitializeResource(&Job->JobLock);
280 if(!NT_SUCCESS(Status))
281 {
282 DPRINT1("Failed to initialize job lock!!!\n");
283 ObDereferenceObject(Job);
284 return Status;
285 }
286 KeInitializeEvent(&Job->Event, NotificationEvent, FALSE);
287
288 /* link the object into the global job list */
289 ExAcquireFastMutex(&PsJobListLock);
290 InsertTailList(&PsJobListHead, &Job->JobLinks);
291 ExReleaseFastMutex(&PsJobListLock);
292
293 Status = ObInsertObject(Job,
294 NULL,
295 DesiredAccess,
296 0,
297 NULL,
298 &hJob);
299 if(NT_SUCCESS(Status))
300 {
301 /* pass the handle back to the caller */
302 _SEH2_TRY
303 {
304 /* NOTE: if the caller passed invalid buffers to receive the handle it's his
305 own fault! the object will still be created and live... It's possible
306 to find the handle using ObFindHandleForObject()! */
307 *JobHandle = hJob;
308 }
309 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
310 {
311 Status = _SEH2_GetExceptionCode();
312 }
313 _SEH2_END;
314 }
315 }
316
317 return Status;
318 }
319
320
321 /*
322 * @implemented
323 */
324 NTSTATUS
325 NTAPI
326 NtIsProcessInJob (
327 IN HANDLE ProcessHandle,
328 IN HANDLE JobHandle OPTIONAL )
329 {
330 KPROCESSOR_MODE PreviousMode;
331 PEPROCESS Process;
332 NTSTATUS Status;
333
334 PreviousMode = ExGetPreviousMode();
335
336 PAGED_CODE();
337
338 Status = ObReferenceObjectByHandle(
339 ProcessHandle,
340 PROCESS_QUERY_INFORMATION,
341 PsProcessType,
342 PreviousMode,
343 (PVOID*)&Process,
344 NULL);
345 if(NT_SUCCESS(Status))
346 {
347 /* FIXME - make sure the job object doesn't get exchanged or deleted while trying to
348 reference it, e.g. by locking it somehow until it is referenced... */
349
350 PEJOB ProcessJob = Process->Job;
351
352 if(ProcessJob != NULL)
353 {
354 if(JobHandle == NULL)
355 {
356 /* the process is assigned to a job */
357 Status = STATUS_PROCESS_IN_JOB;
358 }
359 else /* JobHandle != NULL */
360 {
361 PEJOB JobObject;
362
363 /* get the job object and compare the object pointer with the one assigned to the process */
364 Status = ObReferenceObjectByHandle(JobHandle,
365 JOB_OBJECT_QUERY,
366 PsJobType,
367 PreviousMode,
368 (PVOID*)&JobObject,
369 NULL);
370 if(NT_SUCCESS(Status))
371 {
372 Status = ((ProcessJob == JobObject) ? STATUS_PROCESS_IN_JOB : STATUS_PROCESS_NOT_IN_JOB);
373 ObDereferenceObject(JobObject);
374 }
375 }
376 }
377 else
378 {
379 /* the process is not assigned to any job */
380 Status = STATUS_PROCESS_NOT_IN_JOB;
381 }
382 ObDereferenceObject(Process);
383 }
384
385 return Status;
386 }
387
388
389 /*
390 * @implemented
391 */
392 NTSTATUS
393 NTAPI
394 NtOpenJobObject (
395 PHANDLE JobHandle,
396 ACCESS_MASK DesiredAccess,
397 POBJECT_ATTRIBUTES ObjectAttributes)
398 {
399 KPROCESSOR_MODE PreviousMode;
400 HANDLE hJob;
401 NTSTATUS Status;
402
403 PAGED_CODE();
404
405 PreviousMode = ExGetPreviousMode();
406
407 /* check for valid buffers */
408 if (PreviousMode != KernelMode)
409 {
410 _SEH2_TRY
411 {
412 ProbeForWriteHandle(JobHandle);
413 }
414 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
415 {
416 _SEH2_YIELD(return _SEH2_GetExceptionCode());
417 }
418 _SEH2_END;
419 }
420
421 Status = ObOpenObjectByName(ObjectAttributes,
422 PsJobType,
423 PreviousMode,
424 NULL,
425 DesiredAccess,
426 NULL,
427 &hJob);
428 if(NT_SUCCESS(Status))
429 {
430 _SEH2_TRY
431 {
432 *JobHandle = hJob;
433 }
434 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
435 {
436 Status = _SEH2_GetExceptionCode();
437 }
438 _SEH2_END;
439 }
440
441 return Status;
442 }
443
444
445 /*
446 * @unimplemented
447 */
448 NTSTATUS
449 NTAPI
450 NtQueryInformationJobObject (
451 HANDLE JobHandle,
452 JOBOBJECTINFOCLASS JobInformationClass,
453 PVOID JobInformation,
454 ULONG JobInformationLength,
455 PULONG ReturnLength )
456 {
457 UNIMPLEMENTED;
458 return STATUS_NOT_IMPLEMENTED;
459 }
460
461
462 /*
463 * @unimplemented
464 */
465 NTSTATUS
466 NTAPI
467 NtSetInformationJobObject (
468 HANDLE JobHandle,
469 JOBOBJECTINFOCLASS JobInformationClass,
470 PVOID JobInformation,
471 ULONG JobInformationLength)
472 {
473 UNIMPLEMENTED;
474 return STATUS_NOT_IMPLEMENTED;
475 }
476
477
478 /*
479 * @unimplemented
480 */
481 NTSTATUS
482 NTAPI
483 NtTerminateJobObject (
484 HANDLE JobHandle,
485 NTSTATUS ExitStatus )
486 {
487 KPROCESSOR_MODE PreviousMode;
488 PEJOB Job;
489 NTSTATUS Status;
490
491 PAGED_CODE();
492
493 PreviousMode = ExGetPreviousMode();
494
495 Status = ObReferenceObjectByHandle(
496 JobHandle,
497 JOB_OBJECT_TERMINATE,
498 PsJobType,
499 PreviousMode,
500 (PVOID*)&Job,
501 NULL);
502 if(NT_SUCCESS(Status))
503 {
504 Status = PspTerminateJobObject(
505 Job,
506 PreviousMode,
507 ExitStatus);
508 ObDereferenceObject(Job);
509 }
510
511 return Status;
512 }
513
514
515 /*
516 * @implemented
517 */
518 PVOID
519 NTAPI
520 PsGetJobLock ( PEJOB Job )
521 {
522 ASSERT(Job);
523 return (PVOID)&Job->JobLock;
524 }
525
526
527 /*
528 * @implemented
529 */
530 PVOID
531 NTAPI
532 PsGetJobSessionId ( PEJOB Job )
533 {
534 ASSERT(Job);
535 return (PVOID)Job->SessionId;
536 }
537
538
539 /*
540 * @implemented
541 */
542 ULONG
543 NTAPI
544 PsGetJobUIRestrictionsClass ( PEJOB Job )
545 {
546 ASSERT(Job);
547 return Job->UIRestrictionsClass;
548 }
549
550
551 /*
552 * @unimplemented
553 */
554 VOID
555 NTAPI
556 PsSetJobUIRestrictionsClass(PEJOB Job,
557 ULONG UIRestrictionsClass)
558 {
559 ASSERT(Job);
560 (void)InterlockedExchangeUL(&Job->UIRestrictionsClass, UIRestrictionsClass);
561 /* FIXME - walk through the job process list and update the restrictions? */
562 }
563
564 /* EOF */