1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * LICENSE: See LGPL.txt in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: reactos/lib/epsapi/enum/processes.c
6 * PURPOSE: Enumerate processes and threads
7 * PROGRAMMER: KJK::Hyperion <noog@libero.it>
8 * UPDATE HISTORY:
9 * 10/06/2002: Created
10 * 29/08/2002: Generalized the interface to improve reusability,
11 * more efficient use of memory operations
12 * 12/02/2003: malloc and free renamed to PsaiMalloc and PsaiFree,
13 * for better reusability. PsaEnumerateProcesses now
14 * expanded into:
15 * - PsaCaptureProcessesAndThreads
16 * - PsaFreeCapture
17 * - PsaWalkProcessesAndThreads
18 * - PsaWalkProcesses
19 * - PsaWalkThreads
20 * - PsaWalkFirstProcess
21 * - PsaWalkNextProcess
22 * - PsaWalkFirstThread
23 * - PsaWalkNextThread
24 * - PsaEnumerateProcessesAndThreads
25 * - PsaEnumerateProcesses
26 * - PsaEnumerateThreads
27 * 12/04/2003: internal PSAPI renamed EPSAPI (Extended PSAPI) and
28 * isolated in its own library to clear the confusion
29 * and improve reusability
30 */
31
32 #include "precomp.h"
33
34 #define NDEBUG
35 #include <debug.h>
36
37 NTSTATUS NTAPI
PsaCaptureProcessesAndThreads(OUT PSYSTEM_PROCESS_INFORMATION * ProcessesAndThreads)38 PsaCaptureProcessesAndThreads(OUT PSYSTEM_PROCESS_INFORMATION *ProcessesAndThreads)
39 {
40 PSYSTEM_PROCESS_INFORMATION pInfoBuffer = NULL;
41 SIZE_T nSize = 0x8000;
42 NTSTATUS Status;
43
44 if(ProcessesAndThreads == NULL)
45 {
46 return STATUS_INVALID_PARAMETER_1;
47 }
48
49 /* FIXME: if the system has loaded several processes and threads, the buffer
50 could get really big. But if there's several processes and threads, the
51 system is already under stress, and a huge buffer could only make things
52 worse. The function should be profiled to see what's the average minimum
53 buffer size, to succeed on the first shot */
54 do
55 {
56 PVOID pTmp;
57
58 /* free the buffer, and reallocate it to the new size. RATIONALE: since we
59 ignore the buffer's contents at this point, there's no point in a realloc()
60 that could end up copying a large chunk of data we'd discard anyway */
61 PsaiFree(pInfoBuffer);
62 pTmp = PsaiMalloc(nSize);
63
64 if(pTmp == NULL)
65 {
66 DPRINT(FAILED_WITH_STATUS, "PsaiMalloc", STATUS_NO_MEMORY);
67 Status = STATUS_NO_MEMORY;
68 break;
69 }
70
71 pInfoBuffer = pTmp;
72
73 /* query the information */
74 Status = NtQuerySystemInformation(SystemProcessInformation,
75 pInfoBuffer,
76 nSize,
77 NULL);
78
79 /* double the buffer size */
80 nSize *= 2;
81 } while(Status == STATUS_INFO_LENGTH_MISMATCH);
82
83 if(!NT_SUCCESS(Status))
84 {
85 DPRINT(FAILED_WITH_STATUS, "NtQuerySystemInformation", Status);
86 return Status;
87 }
88
89 *ProcessesAndThreads = pInfoBuffer;
90 return STATUS_SUCCESS;
91 }
92
93 NTSTATUS NTAPI
PsaWalkProcessesAndThreads(IN PSYSTEM_PROCESS_INFORMATION ProcessesAndThreads,IN PPROC_ENUM_ROUTINE ProcessCallback,IN OUT PVOID ProcessCallbackContext,IN PTHREAD_ENUM_ROUTINE ThreadCallback,IN OUT PVOID ThreadCallbackContext)94 PsaWalkProcessesAndThreads(IN PSYSTEM_PROCESS_INFORMATION ProcessesAndThreads,
95 IN PPROC_ENUM_ROUTINE ProcessCallback,
96 IN OUT PVOID ProcessCallbackContext,
97 IN PTHREAD_ENUM_ROUTINE ThreadCallback,
98 IN OUT PVOID ThreadCallbackContext)
99 {
100 NTSTATUS Status;
101
102 if(ProcessCallback == NULL && ThreadCallback == NULL)
103 {
104 return STATUS_INVALID_PARAMETER;
105 }
106
107 Status = STATUS_SUCCESS;
108
109 ProcessesAndThreads = PsaWalkFirstProcess(ProcessesAndThreads);
110
111 /* scan the process list */
112 do
113 {
114 if(ProcessCallback)
115 {
116 Status = ProcessCallback(ProcessesAndThreads, ProcessCallbackContext);
117
118 if(!NT_SUCCESS(Status))
119 {
120 break;
121 }
122 }
123
124 /* if the caller provided a thread callback */
125 if(ThreadCallback)
126 {
127 ULONG i;
128 PSYSTEM_THREAD_INFORMATION pCurThread;
129
130 /* scan the current process's thread list */
131 for(i = 0, pCurThread = PsaWalkFirstThread(ProcessesAndThreads);
132 i < ProcessesAndThreads->NumberOfThreads;
133 i++, pCurThread = PsaWalkNextThread(pCurThread))
134 {
135 Status = ThreadCallback(pCurThread, ThreadCallbackContext);
136
137 if(!NT_SUCCESS(Status))
138 {
139 goto Bail;
140 }
141 }
142 }
143
144 /* move to the next process */
145 ProcessesAndThreads = PsaWalkNextProcess(ProcessesAndThreads);
146 } while(ProcessesAndThreads);
147
148 Bail:
149 return Status;
150 }
151
152 NTSTATUS NTAPI
PsaEnumerateProcessesAndThreads(IN PPROC_ENUM_ROUTINE ProcessCallback,IN OUT PVOID ProcessCallbackContext,IN PTHREAD_ENUM_ROUTINE ThreadCallback,IN OUT PVOID ThreadCallbackContext)153 PsaEnumerateProcessesAndThreads(IN PPROC_ENUM_ROUTINE ProcessCallback,
154 IN OUT PVOID ProcessCallbackContext,
155 IN PTHREAD_ENUM_ROUTINE ThreadCallback,
156 IN OUT PVOID ThreadCallbackContext)
157 {
158 PSYSTEM_PROCESS_INFORMATION pInfoBuffer = NULL;
159 NTSTATUS Status;
160
161 if(ProcessCallback == NULL && ThreadCallback == NULL)
162 {
163 return STATUS_INVALID_PARAMETER;
164 }
165
166 /* get the processes and threads list */
167 Status = PsaCaptureProcessesAndThreads(&pInfoBuffer);
168
169 if(!NT_SUCCESS(Status))
170 {
171 goto Bail;
172 }
173
174 /* walk the processes and threads list */
175 Status = PsaWalkProcessesAndThreads(pInfoBuffer,
176 ProcessCallback,
177 ProcessCallbackContext,
178 ThreadCallback,
179 ThreadCallbackContext);
180
181 Bail:
182 PsaFreeCapture(pInfoBuffer);
183
184 return Status;
185 }
186
187 VOID NTAPI
PsaFreeCapture(IN PVOID Capture)188 PsaFreeCapture(IN PVOID Capture)
189 {
190 PsaiFree(Capture);
191 }
192
193 NTSTATUS NTAPI
PsaWalkProcesses(IN PSYSTEM_PROCESS_INFORMATION ProcessesAndThreads,IN PPROC_ENUM_ROUTINE Callback,IN OUT PVOID CallbackContext)194 PsaWalkProcesses(IN PSYSTEM_PROCESS_INFORMATION ProcessesAndThreads,
195 IN PPROC_ENUM_ROUTINE Callback,
196 IN OUT PVOID CallbackContext)
197 {
198 return PsaWalkProcessesAndThreads(ProcessesAndThreads,
199 Callback,
200 CallbackContext,
201 NULL,
202 NULL);
203 }
204
205 NTSTATUS NTAPI
PsaWalkThreads(IN PSYSTEM_PROCESS_INFORMATION ProcessesAndThreads,IN PTHREAD_ENUM_ROUTINE Callback,IN OUT PVOID CallbackContext)206 PsaWalkThreads(IN PSYSTEM_PROCESS_INFORMATION ProcessesAndThreads,
207 IN PTHREAD_ENUM_ROUTINE Callback,
208 IN OUT PVOID CallbackContext)
209 {
210 return PsaWalkProcessesAndThreads(ProcessesAndThreads,
211 NULL,
212 NULL,
213 Callback,
214 CallbackContext);
215 }
216
217 NTSTATUS NTAPI
PsaEnumerateProcesses(IN PPROC_ENUM_ROUTINE Callback,IN OUT PVOID CallbackContext)218 PsaEnumerateProcesses(IN PPROC_ENUM_ROUTINE Callback,
219 IN OUT PVOID CallbackContext)
220 {
221 return PsaEnumerateProcessesAndThreads(Callback,
222 CallbackContext,
223 NULL,
224 NULL);
225 }
226
227 NTSTATUS NTAPI
PsaEnumerateThreads(IN PTHREAD_ENUM_ROUTINE Callback,IN OUT PVOID CallbackContext)228 PsaEnumerateThreads(IN PTHREAD_ENUM_ROUTINE Callback,
229 IN OUT PVOID CallbackContext)
230 {
231 return PsaEnumerateProcessesAndThreads(NULL,
232 NULL,
233 Callback,
234 CallbackContext);
235 }
236
237 PSYSTEM_PROCESS_INFORMATION FASTCALL
PsaWalkFirstProcess(IN PSYSTEM_PROCESS_INFORMATION ProcessesAndThreads)238 PsaWalkFirstProcess(IN PSYSTEM_PROCESS_INFORMATION ProcessesAndThreads)
239 {
240 return ProcessesAndThreads;
241 }
242
243 PSYSTEM_PROCESS_INFORMATION FASTCALL
PsaWalkNextProcess(IN PSYSTEM_PROCESS_INFORMATION CurrentProcess)244 PsaWalkNextProcess(IN PSYSTEM_PROCESS_INFORMATION CurrentProcess)
245 {
246 if(CurrentProcess->NextEntryOffset == 0)
247 {
248 return NULL;
249 }
250 else
251 {
252 return (PSYSTEM_PROCESS_INFORMATION)((ULONG_PTR)CurrentProcess + CurrentProcess->NextEntryOffset);
253 }
254 }
255
256 PSYSTEM_THREAD_INFORMATION FASTCALL
PsaWalkFirstThread(IN PSYSTEM_PROCESS_INFORMATION CurrentProcess)257 PsaWalkFirstThread(IN PSYSTEM_PROCESS_INFORMATION CurrentProcess)
258 {
259 static SIZE_T nOffsetOfThreads = 0;
260
261 /* get the offset of the Threads field */
262 nOffsetOfThreads = sizeof(SYSTEM_PROCESS_INFORMATION);
263
264 return (PSYSTEM_THREAD_INFORMATION)((ULONG_PTR)CurrentProcess + nOffsetOfThreads);
265 }
266
267 PSYSTEM_THREAD_INFORMATION FASTCALL
PsaWalkNextThread(IN PSYSTEM_THREAD_INFORMATION CurrentThread)268 PsaWalkNextThread(IN PSYSTEM_THREAD_INFORMATION CurrentThread)
269 {
270 return (PSYSTEM_THREAD_INFORMATION)((ULONG_PTR)CurrentThread +
271 ((sizeof(SYSTEM_PROCESS_INFORMATION) + sizeof(SYSTEM_THREAD_INFORMATION)) -
272 sizeof(SYSTEM_PROCESS_INFORMATION)));
273 }
274
275 /* EOF */
276