Implemented process and thread notification routines.
[reactos.git] / reactos / ntoskrnl / ps / create.c
1 /* $Id: create.c,v 1.42 2002/01/03 17:59:09 ekohl Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/thread.c
6 * PURPOSE: Thread managment
7 * PROGRAMMER: David Welch (welch@mcmail.com)
8 * REVISION HISTORY:
9 * 23/06/98: Created
10 * 12/10/99: Phillip Susi: Thread priorities, and APC work
11 */
12
13 /*
14 * NOTE:
15 *
16 * All of the routines that manipulate the thread queue synchronize on
17 * a single spinlock
18 *
19 */
20
21 /* INCLUDES ****************************************************************/
22
23 #include <ddk/ntddk.h>
24 #include <internal/ke.h>
25 #include <internal/ob.h>
26 #include <internal/ps.h>
27 #include <internal/ob.h>
28 #include <internal/id.h>
29 #include <internal/dbg.h>
30
31 #define NDEBUG
32 #include <internal/debug.h>
33
34 /* GLOBAL *******************************************************************/
35
36 static ULONG PiNextThreadUniqueId = 0;
37
38 extern KSPIN_LOCK PiThreadListLock;
39 extern ULONG PiNrThreads;
40
41 extern LIST_ENTRY PiThreadListHead;
42
43 #define MAX_THREAD_NOTIFY_ROUTINE_COUNT 8
44
45 static ULONG PiThreadNotifyRoutineCount = 0;
46 static PCREATE_THREAD_NOTIFY_ROUTINE
47 PiThreadNotifyRoutine[MAX_THREAD_NOTIFY_ROUTINE_COUNT];
48
49 /* FUNCTIONS ***************************************************************/
50
51 NTSTATUS STDCALL
52 PsAssignImpersonationToken(PETHREAD Thread,
53 HANDLE TokenHandle)
54 {
55 PACCESS_TOKEN Token;
56 SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
57 NTSTATUS Status;
58
59 if (TokenHandle != NULL)
60 {
61 Status = ObReferenceObjectByHandle(TokenHandle,
62 0,
63 SeTokenType,
64 UserMode,
65 (PVOID*)&Token,
66 NULL);
67 if (!NT_SUCCESS(Status))
68 {
69 return(Status);
70 }
71 ImpersonationLevel = Token->ImpersonationLevel;
72 }
73 else
74 {
75 Token = NULL;
76 ImpersonationLevel = 0;
77 }
78
79 PsImpersonateClient(Thread,
80 Token,
81 0,
82 0,
83 ImpersonationLevel);
84 if (Token != NULL)
85 {
86 ObDereferenceObject(Token);
87 }
88 return(STATUS_SUCCESS);
89 }
90
91 VOID STDCALL
92 PsRevertToSelf(VOID)
93 {
94 PETHREAD Thread;
95
96 Thread = PsGetCurrentThread();
97
98 if (Thread->ActiveImpersonationInfo != 0)
99 {
100 ObDereferenceObject(Thread->ImpersonationInfo->Token);
101 Thread->ActiveImpersonationInfo = 0;
102 }
103 }
104
105 VOID STDCALL
106 PsImpersonateClient(PETHREAD Thread,
107 PACCESS_TOKEN Token,
108 UCHAR b,
109 UCHAR c,
110 SECURITY_IMPERSONATION_LEVEL Level)
111 {
112 if (Token == 0)
113 {
114 if (Thread->ActiveImpersonationInfo != 0)
115 {
116 Thread->ActiveImpersonationInfo = 0;
117 if (Thread->ImpersonationInfo->Token != NULL)
118 {
119 ObDereferenceObject(Thread->ImpersonationInfo->Token);
120 }
121 }
122 return;
123 }
124 if (Thread->ActiveImpersonationInfo == 0 ||
125 Thread->ImpersonationInfo == NULL)
126 {
127 Thread->ImpersonationInfo = ExAllocatePool(NonPagedPool,
128 sizeof(PS_IMPERSONATION_INFO));
129 }
130 Thread->ImpersonationInfo->Level = Level;
131 Thread->ImpersonationInfo->Unknown2 = c;
132 Thread->ImpersonationInfo->Unknown1 = b;
133 Thread->ImpersonationInfo->Token = Token;
134 ObReferenceObjectByPointer(Token,
135 0,
136 SeTokenType,
137 KernelMode);
138 Thread->ActiveImpersonationInfo = 1;
139 }
140
141 PACCESS_TOKEN
142 PsReferenceEffectiveToken(PETHREAD Thread,
143 PTOKEN_TYPE TokenType,
144 PUCHAR b,
145 PSECURITY_IMPERSONATION_LEVEL Level)
146 {
147 PEPROCESS Process;
148 PACCESS_TOKEN Token;
149
150 if (Thread->ActiveImpersonationInfo == 0)
151 {
152 Process = Thread->ThreadsProcess;
153 *TokenType = TokenPrimary;
154 *b = 0;
155 Token = Process->Token;
156 }
157 else
158 {
159 Token = Thread->ImpersonationInfo->Token;
160 *TokenType = TokenImpersonation;
161 *b = Thread->ImpersonationInfo->Unknown2;
162 *Level = Thread->ImpersonationInfo->Level;
163 }
164 return(Token);
165 }
166
167 NTSTATUS STDCALL
168 NtImpersonateThread (IN HANDLE ThreadHandle,
169 IN HANDLE ThreadToImpersonateHandle,
170 IN PSECURITY_QUALITY_OF_SERVICE
171 SecurityQualityOfService)
172 {
173 PETHREAD Thread;
174 PETHREAD ThreadToImpersonate;
175 NTSTATUS Status;
176 SE_SOME_STRUCT2 b;
177
178 Status = ObReferenceObjectByHandle(ThreadHandle,
179 0,
180 PsThreadType,
181 UserMode,
182 (PVOID*)&Thread,
183 NULL);
184 if (!NT_SUCCESS(Status))
185 {
186 return(Status);
187 }
188
189 Status = ObReferenceObjectByHandle(ThreadToImpersonateHandle,
190 0,
191 PsThreadType,
192 UserMode,
193 (PVOID*)&ThreadToImpersonate,
194 NULL);
195 if (!NT_SUCCESS(Status))
196 {
197 ObDereferenceObject(Thread);
198 return(Status);
199 }
200
201 Status = SeCreateClientSecurity(ThreadToImpersonate,
202 SecurityQualityOfService,
203 0,
204 &b);
205 if (!NT_SUCCESS(Status))
206 {
207 ObDereferenceObject(Thread);
208 ObDereferenceObject(ThreadToImpersonate);
209 return(Status);
210 }
211
212 SeImpersonateClient(&b, Thread);
213 if (b.Token != NULL)
214 {
215 ObDereferenceObject(b.Token);
216 }
217 return(STATUS_SUCCESS);
218 }
219
220 NTSTATUS STDCALL
221 NtOpenThreadToken(IN HANDLE ThreadHandle,
222 IN ACCESS_MASK DesiredAccess,
223 IN BOOLEAN OpenAsSelf,
224 OUT PHANDLE TokenHandle)
225 {
226 #if 0
227 PETHREAD Thread;
228 NTSTATUS Status;
229 PACCESS_TOKEN Token;
230
231 Status = ObReferenceObjectByHandle(ThreadHandle,
232 0,
233 PsThreadType,
234 UserMode,
235 (PVOID*)&Thread,
236 NULL);
237 if (!NT_SUCCESS(Status))
238 {
239 return(Status);
240 }
241
242 Token = PsReferencePrimaryToken(Thread->ThreadsProcess);
243 SepCreateImpersonationTokenDacl(Token);
244 #endif
245 return(STATUS_UNSUCCESSFUL);
246 }
247
248 PACCESS_TOKEN STDCALL
249 PsReferenceImpersonationToken(PETHREAD Thread,
250 PULONG Unknown1,
251 PULONG Unknown2,
252 SECURITY_IMPERSONATION_LEVEL* Level)
253 {
254 if (Thread->ActiveImpersonationInfo == 0)
255 {
256 return(NULL);
257 }
258
259 *Level = Thread->ImpersonationInfo->Level;
260 *Unknown1 = Thread->ImpersonationInfo->Unknown1;
261 *Unknown2 = Thread->ImpersonationInfo->Unknown2;
262 ObReferenceObjectByPointer(Thread->ImpersonationInfo->Token,
263 TOKEN_ALL_ACCESS,
264 SeTokenType,
265 KernelMode);
266 return(Thread->ImpersonationInfo->Token);
267 }
268
269 VOID STDCALL
270 PiTimeoutThread(struct _KDPC *dpc,
271 PVOID Context,
272 PVOID arg1,
273 PVOID arg2)
274 {
275 // wake up the thread, and tell it it timed out
276 NTSTATUS Status = STATUS_TIMEOUT;
277
278 DPRINT("PiTimeoutThread()\n");
279
280 KeRemoveAllWaitsThread((PETHREAD)Context, Status);
281 }
282
283 VOID
284 PiBeforeBeginThread(CONTEXT c)
285 {
286 DPRINT("PiBeforeBeginThread(Eip %x)\n", c.Eip);
287 //KeReleaseSpinLock(&PiThreadListLock, PASSIVE_LEVEL);
288 KeLowerIrql(PASSIVE_LEVEL);
289 }
290
291 #if 0
292 VOID
293 PsBeginThread(PKSTART_ROUTINE StartRoutine, PVOID StartContext)
294 {
295 NTSTATUS Ret;
296
297 // KeReleaseSpinLock(&PiThreadListLock,PASSIVE_LEVEL);
298 KeLowerIrql(PASSIVE_LEVEL);
299 Ret = StartRoutine(StartContext);
300 PsTerminateSystemThread(Ret);
301 KeBugCheck(0);
302 }
303 #endif
304
305 VOID STDCALL
306 PiDeleteThread(PVOID ObjectBody)
307 {
308 KIRQL oldIrql;
309 PETHREAD Thread;
310 ULONG i;
311
312 DPRINT("PiDeleteThread(ObjectBody %x)\n",ObjectBody);
313
314 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
315 Thread = (PETHREAD)ObjectBody;
316
317 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
318 {
319 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
320 Thread->Cid.UniqueThread,
321 FALSE);
322 }
323
324 DPRINT("Process %x(%d)\n",
325 Thread->ThreadsProcess,
326 ObGetReferenceCount(Thread->ThreadsProcess));
327 ObDereferenceObject(Thread->ThreadsProcess);
328 Thread->ThreadsProcess = NULL;
329 PiNrThreads--;
330 RemoveEntryList(&Thread->Tcb.ThreadListEntry);
331 HalReleaseTask(Thread);
332 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
333 DPRINT("PiDeleteThread() finished\n");
334 }
335
336 VOID STDCALL
337 PiCloseThread(PVOID ObjectBody,
338 ULONG HandleCount)
339 {
340 DPRINT("PiCloseThread(ObjectBody %x)\n", ObjectBody);
341 DPRINT("ObGetReferenceCount(ObjectBody) %d "
342 "ObGetHandleCount(ObjectBody) %d\n",
343 ObGetReferenceCount(ObjectBody),
344 ObGetHandleCount(ObjectBody));
345 }
346
347 NTSTATUS
348 PsInitializeThread(HANDLE ProcessHandle,
349 PETHREAD* ThreadPtr,
350 PHANDLE ThreadHandle,
351 ACCESS_MASK DesiredAccess,
352 POBJECT_ATTRIBUTES ThreadAttributes,
353 BOOLEAN First)
354 {
355 PETHREAD Thread;
356 NTSTATUS Status;
357 KIRQL oldIrql;
358 PEPROCESS Process;
359 ULONG i;
360
361 /*
362 * Reference process
363 */
364 if (ProcessHandle != NULL)
365 {
366 Status = ObReferenceObjectByHandle(ProcessHandle,
367 PROCESS_CREATE_THREAD,
368 PsProcessType,
369 UserMode,
370 (PVOID*)&Process,
371 NULL);
372 if (Status != STATUS_SUCCESS)
373 {
374 DPRINT("Failed at %s:%d\n",__FILE__,__LINE__);
375 return(Status);
376 }
377 DPRINT( "Creating thread in process %x\n", Process );
378 }
379 else
380 {
381 Process = PsInitialSystemProcess;
382 ObReferenceObjectByPointer(Process,
383 PROCESS_CREATE_THREAD,
384 PsProcessType,
385 UserMode);
386 }
387
388 /*
389 * Create and initialize thread
390 */
391 Status = ObCreateObject(ThreadHandle,
392 DesiredAccess,
393 ThreadAttributes,
394 PsThreadType,
395 (PVOID*)&Thread);
396 if (!NT_SUCCESS(Status))
397 {
398 return(Status);
399 }
400
401 DPRINT("Thread = %x\n",Thread);
402
403 PiNrThreads++;
404
405 KeInitializeThread(&Process->Pcb, &Thread->Tcb, First);
406 Thread->ThreadsProcess = Process;
407 /*
408 * FIXME: What lock protects this?
409 */
410 InsertTailList(&Thread->ThreadsProcess->ThreadListHead,
411 &Thread->Tcb.ProcessThreadListEntry);
412 InitializeListHead(&Thread->TerminationPortList);
413 KeInitializeSpinLock(&Thread->ActiveTimerListLock);
414 InitializeListHead(&Thread->IrpList);
415 Thread->Cid.UniqueThread = (HANDLE)InterlockedIncrement(
416 &PiNextThreadUniqueId);
417 Thread->Cid.UniqueProcess = (HANDLE)Thread->ThreadsProcess->UniqueProcessId;
418 Thread->DeadThread = 0;
419 Thread->Win32Thread = 0;
420 DPRINT("Thread->Cid.UniqueThread %d\n",Thread->Cid.UniqueThread);
421
422 *ThreadPtr = Thread;
423
424 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
425 InsertTailList(&PiThreadListHead, &Thread->Tcb.ThreadListEntry);
426 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
427
428 Thread->Tcb.BasePriority = Thread->ThreadsProcess->Pcb.BasePriority;
429 Thread->Tcb.Priority = Thread->Tcb.BasePriority;
430
431 for (i = 0; i < PiThreadNotifyRoutineCount; i++)
432 {
433 PiThreadNotifyRoutine[i](Thread->Cid.UniqueProcess,
434 Thread->Cid.UniqueThread,
435 TRUE);
436 }
437
438 return(STATUS_SUCCESS);
439 }
440
441
442 static NTSTATUS
443 PsCreateTeb(HANDLE ProcessHandle,
444 PTEB *TebPtr,
445 PETHREAD Thread,
446 PINITIAL_TEB InitialTeb)
447 {
448 MEMORY_BASIC_INFORMATION Info;
449 NTSTATUS Status;
450 ULONG ByteCount;
451 ULONG RegionSize;
452 ULONG TebSize;
453 PVOID TebBase;
454 TEB Teb;
455 ULONG ResultLength;
456
457 TebBase = (PVOID)0x7FFDE000;
458 TebSize = PAGESIZE;
459
460 while (TRUE)
461 {
462 Status = NtQueryVirtualMemory(ProcessHandle,
463 TebBase,
464 MemoryBasicInformation,
465 &Info,
466 sizeof(Info),
467 &ResultLength);
468 if (!NT_SUCCESS(Status))
469 {
470 DbgPrint("NtQueryVirtualMemory (Status %x)\n", Status);
471 KeBugCheck(0);
472 }
473 /* FIXME: Race between this and the above check */
474 if (Info.State == MEM_FREE)
475 {
476 /* The TEB must reside in user space */
477 Status = NtAllocateVirtualMemory(ProcessHandle,
478 &TebBase,
479 0,
480 &TebSize,
481 MEM_RESERVE | MEM_COMMIT,
482 PAGE_READWRITE);
483 if (NT_SUCCESS(Status))
484 {
485 break;
486 }
487 }
488
489 TebBase = TebBase - TebSize;
490 }
491
492 DPRINT ("TebBase %p TebSize %lu\n", TebBase, TebSize);
493
494 /* set all pointers to and from the TEB */
495 Teb.Tib.Self = TebBase;
496 if (Thread->ThreadsProcess)
497 {
498 Teb.Peb = Thread->ThreadsProcess->Peb; /* No PEB yet!! */
499 }
500 DPRINT("Teb.Peb %x\n", Teb.Peb);
501
502 /* store stack information from InitialTeb */
503 if (InitialTeb != NULL)
504 {
505 Teb.Tib.StackBase = InitialTeb->StackBase;
506 Teb.Tib.StackLimit = InitialTeb->StackLimit;
507 Teb.DeallocationStack = InitialTeb->StackAllocate;
508 }
509
510 /* more initialization */
511 Teb.Cid.UniqueThread = Thread->Cid.UniqueThread;
512 Teb.Cid.UniqueProcess = Thread->Cid.UniqueProcess;
513 Teb.CurrentLocale = PsDefaultThreadLocaleId;
514
515 DPRINT("sizeof(TEB) %x\n", sizeof(TEB));
516
517 /* write TEB data into teb page */
518 Status = NtWriteVirtualMemory(ProcessHandle,
519 TebBase,
520 &Teb,
521 sizeof(TEB),
522 &ByteCount);
523
524 if (!NT_SUCCESS(Status))
525 {
526 /* free TEB */
527 DPRINT1 ("Writing TEB failed!\n");
528
529 RegionSize = 0;
530 NtFreeVirtualMemory(ProcessHandle,
531 TebBase,
532 &RegionSize,
533 MEM_RELEASE);
534
535 return Status;
536 }
537
538 if (TebPtr != NULL)
539 {
540 *TebPtr = (PTEB)TebBase;
541 }
542
543 DPRINT("TEB allocated at %p\n", TebBase);
544
545 return Status;
546 }
547
548
549 NTSTATUS STDCALL
550 NtCreateThread (PHANDLE ThreadHandle,
551 ACCESS_MASK DesiredAccess,
552 POBJECT_ATTRIBUTES ObjectAttributes,
553 HANDLE ProcessHandle,
554 PCLIENT_ID Client,
555 PCONTEXT ThreadContext,
556 PINITIAL_TEB InitialTeb,
557 BOOLEAN CreateSuspended)
558 {
559 PETHREAD Thread;
560 PTEB TebBase;
561 NTSTATUS Status;
562
563 DPRINT("NtCreateThread(ThreadHandle %x, PCONTEXT %x)\n",
564 ThreadHandle,ThreadContext);
565
566 Status = PsInitializeThread(ProcessHandle,
567 &Thread,
568 ThreadHandle,
569 DesiredAccess,
570 ObjectAttributes,
571 FALSE);
572 if (!NT_SUCCESS(Status))
573 {
574 return(Status);
575 }
576
577 Status = Ke386InitThreadWithContext(&Thread->Tcb,
578 ThreadContext);
579 if (!NT_SUCCESS(Status))
580 {
581 return(Status);
582 }
583
584 Status = PsCreateTeb(ProcessHandle,
585 &TebBase,
586 Thread,
587 InitialTeb);
588 if (!NT_SUCCESS(Status))
589 {
590 return(Status);
591 }
592
593 /* Attention: TebBase is in user memory space */
594 Thread->Tcb.Teb = TebBase;
595
596 Thread->StartAddress=NULL;
597
598 if (Client != NULL)
599 {
600 *Client=Thread->Cid;
601 }
602
603 /*
604 * Maybe send a message to the process's debugger
605 */
606 DbgkCreateThread((PVOID)ThreadContext->Eip);
607
608 /*
609 * Start the thread running
610 */
611 if (!CreateSuspended)
612 {
613 DPRINT("Not creating suspended\n");
614 PsUnblockThread(Thread, NULL);
615 }
616 else
617 {
618 KeBugCheck(0);
619 }
620 return(STATUS_SUCCESS);
621 }
622
623
624 NTSTATUS STDCALL
625 PsCreateSystemThread(PHANDLE ThreadHandle,
626 ACCESS_MASK DesiredAccess,
627 POBJECT_ATTRIBUTES ObjectAttributes,
628 HANDLE ProcessHandle,
629 PCLIENT_ID ClientId,
630 PKSTART_ROUTINE StartRoutine,
631 PVOID StartContext)
632 /*
633 * FUNCTION: Creates a thread which executes in kernel mode
634 * ARGUMENTS:
635 * ThreadHandle (OUT) = Caller supplied storage for the returned thread
636 * handle
637 * DesiredAccess = Requested access to the thread
638 * ObjectAttributes = Object attributes (optional)
639 * ProcessHandle = Handle of process thread will run in
640 * NULL to use system process
641 * ClientId (OUT) = Caller supplied storage for the returned client id
642 * of the thread (optional)
643 * StartRoutine = Entry point for the thread
644 * StartContext = Argument supplied to the thread when it begins
645 * execution
646 * RETURNS: Success or failure status
647 */
648 {
649 PETHREAD Thread;
650 NTSTATUS Status;
651
652 DPRINT("PsCreateSystemThread(ThreadHandle %x, ProcessHandle %x)\n",
653 ThreadHandle,ProcessHandle);
654
655 Status = PsInitializeThread(ProcessHandle,
656 &Thread,
657 ThreadHandle,
658 DesiredAccess,
659 ObjectAttributes,
660 FALSE);
661 if (!NT_SUCCESS(Status))
662 {
663 return(Status);
664 }
665
666 Thread->StartAddress=StartRoutine;
667 Status = Ke386InitThread(&Thread->Tcb,
668 StartRoutine,
669 StartContext);
670 if (!NT_SUCCESS(Status))
671 {
672 return(Status);
673 }
674
675 if (ClientId!=NULL)
676 {
677 *ClientId=Thread->Cid;
678 }
679
680 PsUnblockThread(Thread, NULL);
681
682 return(STATUS_SUCCESS);
683 }
684
685
686 NTSTATUS STDCALL
687 PsSetCreateThreadNotifyRoutine(IN PCREATE_THREAD_NOTIFY_ROUTINE NotifyRoutine)
688 {
689 if (PiThreadNotifyRoutineCount >= MAX_THREAD_NOTIFY_ROUTINE_COUNT)
690 return(STATUS_INSUFFICIENT_RESOURCES);
691
692 PiThreadNotifyRoutine[PiThreadNotifyRoutineCount] = NotifyRoutine;
693 PiThreadNotifyRoutineCount++;
694
695 return(STATUS_SUCCESS);
696 }
697
698 /* EOF */