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