Added seting of exit status in NtTerminateProcess.
[reactos.git] / reactos / ntoskrnl / ps / kill.c
1 /* $Id: kill.c,v 1.52 2002/04/27 19:25:16 hbirr Exp $
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ps/kill.c
6 * PURPOSE: Terminating a thread
7 * PROGRAMMER: David Welch (welch@cwcom.net)
8 * UPDATE HISTORY:
9 * Created 22/05/98
10 */
11
12 /* INCLUDES *****************************************************************/
13
14 #include <ddk/ntddk.h>
15 #include <internal/ps.h>
16 #include <internal/ke.h>
17 #include <internal/mm.h>
18 #include <internal/ob.h>
19 #include <internal/port.h>
20 #include <internal/pool.h>
21
22 #define NDEBUG
23 #include <internal/debug.h>
24
25 /* GLOBALS *******************************************************************/
26
27 extern ULONG PiNrThreads;
28 extern ULONG PiNrRunnableThreads;
29 extern KSPIN_LOCK PiThreadListLock;
30 extern LIST_ENTRY PiThreadListHead;
31 extern KSPIN_LOCK PiApcLock;
32
33 VOID PsTerminateCurrentThread(NTSTATUS ExitStatus);
34
35 #define TAG_TERMINATE_APC TAG('T', 'A', 'P', 'C')
36
37 /* FUNCTIONS *****************************************************************/
38
39 VOID
40 PiTerminateProcessThreads(PEPROCESS Process,
41 NTSTATUS ExitStatus)
42 {
43 KIRQL oldlvl;
44 PLIST_ENTRY current_entry;
45 PETHREAD current;
46
47 DPRINT("PiTerminateProcessThreads(Process %x, ExitStatus %x)\n",
48 Process, ExitStatus);
49
50 KeAcquireSpinLock(&PiThreadListLock, &oldlvl);
51
52 current_entry = Process->ThreadListHead.Flink;
53 while (current_entry != &Process->ThreadListHead)
54 {
55 current = CONTAINING_RECORD(current_entry, ETHREAD,
56 Tcb.ProcessThreadListEntry);
57 if (current != PsGetCurrentThread() &&
58 current->DeadThread == 0)
59 {
60 DPRINT("Terminating %x, current thread: %x, "
61 "thread's process: %x\n", current, PsGetCurrentThread(),
62 current->ThreadsProcess);
63 KeReleaseSpinLock(&PiThreadListLock, oldlvl);
64 PsTerminateOtherThread(current, ExitStatus);
65 KeAcquireSpinLock(&PiThreadListLock, &oldlvl);
66 current_entry = Process->ThreadListHead.Flink;
67 }
68 else
69 {
70 current_entry = current_entry->Flink;
71 }
72 }
73 KeReleaseSpinLock(&PiThreadListLock, oldlvl);
74 DPRINT("Finished PiTerminateProcessThreads()\n");
75 }
76
77 VOID
78 PsReapThreads(VOID)
79 {
80 PLIST_ENTRY current_entry;
81 PETHREAD current;
82 KIRQL oldIrql;
83
84 // DPRINT1("PsReapThreads()\n");
85
86 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
87
88 current_entry = PiThreadListHead.Flink;
89
90 while (current_entry != &PiThreadListHead)
91 {
92 current = CONTAINING_RECORD(current_entry, ETHREAD,
93 Tcb.ThreadListEntry);
94
95 current_entry = current_entry->Flink;
96
97 if (current->Tcb.State == THREAD_STATE_TERMINATED_1)
98 {
99 PEPROCESS Process = current->ThreadsProcess;
100 NTSTATUS Status = current->ExitStatus;
101
102 DPRINT("PsProcessType %x\n", PsProcessType);
103 DPRINT("Reaping thread %x\n", current);
104 DPRINT("Pointer count %d\n", ObGetObjectPointerCount(Process));
105 current->Tcb.State = THREAD_STATE_TERMINATED_2;
106 RemoveEntryList(&current->Tcb.ProcessThreadListEntry);
107 if (IsListEmpty(&Process->ThreadListHead))
108 {
109 DPRINT("Last thread terminated, terminating process\n");
110 KeReleaseSpinLock( &PiThreadListLock, oldIrql );
111 PiTerminateProcess(Process, Status);
112 KeAcquireSpinLock( &PiThreadListLock, &oldIrql );
113 }
114 DPRINT("Pointer count %d\n", ObGetObjectPointerCount(Process));
115 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
116 ObDereferenceObject(current);
117 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
118 current_entry = PiThreadListHead.Flink;
119 }
120 }
121 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
122 }
123
124 VOID
125 PsTerminateCurrentThread(NTSTATUS ExitStatus)
126 /*
127 * FUNCTION: Terminates the current thread
128 */
129 {
130 KIRQL oldIrql;
131 PETHREAD CurrentThread;
132 PKTHREAD Thread;
133 PLIST_ENTRY current_entry;
134 PKMUTANT Mutant;
135
136 CurrentThread = PsGetCurrentThread();
137
138 DPRINT("terminating %x\n",CurrentThread);
139 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
140
141 CurrentThread->ExitStatus = ExitStatus;
142 Thread = KeGetCurrentThread();
143 KeCancelTimer(&Thread->Timer);
144 KeReleaseSpinLock(&PiThreadListLock, oldIrql);
145
146 /* abandon all owned mutants */
147 current_entry = Thread->MutantListHead.Flink;
148 while (current_entry != &Thread->MutantListHead)
149 {
150 Mutant = CONTAINING_RECORD(current_entry, KMUTANT,
151 MutantListEntry);
152 KeReleaseMutant(Mutant,
153 MUTANT_INCREMENT,
154 TRUE,
155 FALSE);
156 current_entry = Thread->MutantListHead.Flink;
157 }
158
159 KeAcquireDispatcherDatabaseLock(FALSE);
160 CurrentThread->Tcb.DispatcherHeader.SignalState = TRUE;
161 KeDispatcherObjectWake(&CurrentThread->Tcb.DispatcherHeader);
162 KeReleaseDispatcherDatabaseLock(FALSE);
163
164 KeAcquireSpinLock(&PiThreadListLock, &oldIrql);
165 PsDispatchThreadNoLock(THREAD_STATE_TERMINATED_1);
166 KeBugCheck(0);
167 }
168
169 VOID STDCALL
170 PiTerminateThreadRundownRoutine(PKAPC Apc)
171 {
172 ExFreePool(Apc);
173 }
174
175 VOID STDCALL
176 PiTerminateThreadKernelRoutine(PKAPC Apc,
177 PKNORMAL_ROUTINE* NormalRoutine,
178 PVOID* NormalContext,
179 PVOID* SystemArgument1,
180 PVOID* SystemArguemnt2)
181 {
182 ExFreePool(Apc);
183 }
184
185 VOID STDCALL
186 PiTerminateThreadNormalRoutine(PVOID NormalContext,
187 PVOID SystemArgument1,
188 PVOID SystemArgument2)
189 {
190 PsTerminateCurrentThread(PsGetCurrentThread()->ExitStatus);
191 }
192
193 VOID
194 PsTerminateOtherThread(PETHREAD Thread,
195 NTSTATUS ExitStatus)
196 /*
197 * FUNCTION: Terminate a thread when calling from another thread's context
198 * NOTES: This function must be called with PiThreadListLock held
199 */
200 {
201 PKAPC Apc;
202
203 DPRINT("PsTerminateOtherThread(Thread %x, ExitStatus %x)\n",
204 Thread, ExitStatus);
205
206 Thread->DeadThread = 1;
207 Thread->ExitStatus = ExitStatus;
208 Apc = ExAllocatePoolWithTag(NonPagedPool, sizeof(KAPC), TAG_TERMINATE_APC);
209 KeInitializeApc(Apc,
210 &Thread->Tcb,
211 0,
212 PiTerminateThreadKernelRoutine,
213 PiTerminateThreadRundownRoutine,
214 PiTerminateThreadNormalRoutine,
215 KernelMode,
216 NULL);
217 KeInsertQueueApc(Apc,
218 NULL,
219 NULL,
220 KernelMode);
221 }
222
223 NTSTATUS STDCALL
224 PiTerminateProcess(PEPROCESS Process,
225 NTSTATUS ExitStatus)
226 {
227 DPRINT("PiTerminateProcess(Process %x, ExitStatus %x) PC %d HC %d\n",
228 Process, ExitStatus, ObGetObjectPointerCount(Process),
229 ObGetObjectHandleCount(Process));
230
231 if (InterlockedExchange((PLONG)&Process->Pcb.State,
232 PROCESS_STATE_TERMINATED) ==
233 PROCESS_STATE_TERMINATED)
234 {
235 return(STATUS_SUCCESS);
236 }
237 KeAttachProcess( Process );
238 ObCloseAllHandles(Process);
239 KeDetachProcess();
240 KeAcquireDispatcherDatabaseLock(FALSE);
241 Process->Pcb.DispatcherHeader.SignalState = TRUE;
242 KeDispatcherObjectWake(&Process->Pcb.DispatcherHeader);
243 KeReleaseDispatcherDatabaseLock(FALSE);
244 return(STATUS_SUCCESS);
245 }
246
247 NTSTATUS STDCALL
248 NtTerminateProcess(IN HANDLE ProcessHandle,
249 IN NTSTATUS ExitStatus)
250 {
251 NTSTATUS Status;
252 PEPROCESS Process;
253
254 DPRINT("NtTerminateProcess(ProcessHandle %x, ExitStatus %x)\n",
255 ProcessHandle, ExitStatus);
256
257 Status = ObReferenceObjectByHandle(ProcessHandle,
258 PROCESS_TERMINATE,
259 PsProcessType,
260 UserMode,
261 (PVOID*)&Process,
262 NULL);
263 if (!NT_SUCCESS(Status))
264 {
265 return(Status);
266 }
267 Process->ExitStatus = ExitStatus;
268 PiTerminateProcessThreads(Process, ExitStatus);
269 if (PsGetCurrentThread()->ThreadsProcess == Process)
270 {
271 ObDereferenceObject(Process);
272 PsTerminateCurrentThread(ExitStatus);
273 }
274 ObDereferenceObject(Process);
275 return(STATUS_SUCCESS);
276 }
277
278
279 NTSTATUS STDCALL
280 NtTerminateThread(IN HANDLE ThreadHandle,
281 IN NTSTATUS ExitStatus)
282 {
283 PETHREAD Thread;
284 NTSTATUS Status;
285
286 Status = ObReferenceObjectByHandle(ThreadHandle,
287 THREAD_TERMINATE,
288 PsThreadType,
289 UserMode,
290 (PVOID*)&Thread,
291 NULL);
292 if (Status != STATUS_SUCCESS)
293 {
294 return(Status);
295 }
296
297 ObDereferenceObject(Thread);
298
299 if (Thread == PsGetCurrentThread())
300 {
301 PsTerminateCurrentThread(ExitStatus);
302 }
303 else
304 {
305 PsTerminateOtherThread(Thread, ExitStatus);
306 }
307 return(STATUS_SUCCESS);
308 }
309
310
311 NTSTATUS STDCALL
312 PsTerminateSystemThread(NTSTATUS ExitStatus)
313 /*
314 * FUNCTION: Terminates the current thread
315 * ARGUMENTS:
316 * ExitStatus = Status to pass to the creater
317 * RETURNS: Doesn't
318 */
319 {
320 PsTerminateCurrentThread(ExitStatus);
321 return(STATUS_SUCCESS);
322 }
323
324 NTSTATUS STDCALL
325 NtCallTerminatePorts(PETHREAD Thread)
326 {
327 KIRQL oldIrql;
328 PLIST_ENTRY current_entry;
329 PEPORT_TERMINATION_REQUEST current;
330
331 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
332 while ((current_entry = RemoveHeadList(&Thread->TerminationPortList)) !=
333 &Thread->TerminationPortList);
334 {
335 current = CONTAINING_RECORD(current_entry,
336 EPORT_TERMINATION_REQUEST,
337 ThreadListEntry);
338 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
339 LpcSendTerminationPort(current->Port,
340 Thread->CreateTime);
341 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
342 }
343 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
344 return(STATUS_SUCCESS);
345 }
346
347 NTSTATUS STDCALL
348 NtRegisterThreadTerminatePort(HANDLE TerminationPortHandle)
349 {
350 NTSTATUS Status;
351 PEPORT_TERMINATION_REQUEST Request;
352 PEPORT TerminationPort;
353 KIRQL oldIrql;
354 PETHREAD Thread;
355
356 Status = ObReferenceObjectByHandle(TerminationPortHandle,
357 PORT_ALL_ACCESS,
358 ExPortType,
359 UserMode,
360 (PVOID*)&TerminationPort,
361 NULL);
362 if (!NT_SUCCESS(Status))
363 {
364 return(Status);
365 }
366
367 Request = ExAllocatePool(NonPagedPool, sizeof(Request));
368 Request->Port = TerminationPort;
369 Thread = PsGetCurrentThread();
370 KeAcquireSpinLock(&Thread->ActiveTimerListLock, &oldIrql);
371 InsertTailList(&Thread->TerminationPortList, &Request->ThreadListEntry);
372 KeReleaseSpinLock(&Thread->ActiveTimerListLock, oldIrql);
373
374 return(STATUS_SUCCESS);
375 }