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 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 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 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 188 PsaFreeCapture(IN PVOID Capture) 189 { 190 PsaiFree(Capture); 191 } 192 193 NTSTATUS NTAPI 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 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 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 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 238 PsaWalkFirstProcess(IN PSYSTEM_PROCESS_INFORMATION ProcessesAndThreads) 239 { 240 return ProcessesAndThreads; 241 } 242 243 PSYSTEM_PROCESS_INFORMATION FASTCALL 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 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 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