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