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