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