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