PageRenderTime 54ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/psutil-0.2.1/psutil/arch/mswindows/process_info.c

https://bitbucket.org/maresystem/dogtown-nagios-plugins
C | 522 lines | 352 code | 92 blank | 78 comment | 74 complexity | 0107ee88f767373bfdfdb662c3b699be MD5 | raw file
Possible License(s): BSD-3-Clause
  1. /*
  2. * $Id: process_info.c 931 2011-02-22 04:10:46Z g.rodola $
  3. *
  4. * Helper functions related to fetching process information. Used by
  5. * _psutil_mswindows module methods.
  6. */
  7. #include <Python.h>
  8. #include <windows.h>
  9. #include <Psapi.h>
  10. #include <tlhelp32.h>
  11. #include "security.h"
  12. #include "process_info.h"
  13. #include "ntextapi.h"
  14. /*
  15. * NtQueryInformationProcess code taken from
  16. * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/
  17. * typedefs needed to compile against ntdll functions not exposted in the API
  18. */
  19. typedef LONG NTSTATUS;
  20. typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)(
  21. HANDLE ProcessHandle,
  22. DWORD ProcessInformationClass,
  23. PVOID ProcessInformation,
  24. DWORD ProcessInformationLength,
  25. PDWORD ReturnLength
  26. );
  27. typedef struct _PROCESS_BASIC_INFORMATION
  28. {
  29. PVOID Reserved1;
  30. PVOID PebBaseAddress;
  31. PVOID Reserved2[2];
  32. ULONG_PTR UniqueProcessId;
  33. PVOID Reserved3;
  34. } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION;
  35. /*
  36. * A wrapper around OpenProcess setting NSP exception if process
  37. * no longer exists.
  38. * "pid" is the process pid, "dwDesiredAccess" is the first argument
  39. * exptected by OpenProcess.
  40. * Return a process handle or NULL.
  41. */
  42. HANDLE
  43. handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess)
  44. {
  45. HANDLE hProcess;
  46. DWORD processExitCode = 0;
  47. hProcess = OpenProcess(dwDesiredAccess, FALSE, pid);
  48. if (hProcess == NULL) {
  49. if (GetLastError() == ERROR_INVALID_PARAMETER) {
  50. NoSuchProcess();
  51. }
  52. else {
  53. PyErr_SetFromWindowsErr(0);
  54. }
  55. return NULL;
  56. }
  57. /* make sure the process is running */
  58. GetExitCodeProcess(hProcess, &processExitCode);
  59. if (processExitCode == 0) {
  60. NoSuchProcess();
  61. CloseHandle(hProcess);
  62. return NULL;
  63. }
  64. return hProcess;
  65. }
  66. /*
  67. * Same as handle_from_pid_waccess but implicitly uses
  68. * PROCESS_QUERY_INFORMATION | PROCESS_VM_READ as dwDesiredAccess
  69. * parameter for OpenProcess.
  70. */
  71. HANDLE
  72. handle_from_pid(DWORD pid) {
  73. DWORD dwDesiredAccess = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ;
  74. return handle_from_pid_waccess(pid, dwDesiredAccess);
  75. }
  76. // fetch the PEB base address from NtQueryInformationProcess()
  77. PVOID
  78. GetPebAddress(HANDLE ProcessHandle)
  79. {
  80. _NtQueryInformationProcess NtQueryInformationProcess =
  81. (_NtQueryInformationProcess)GetProcAddress(
  82. GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess");
  83. PROCESS_BASIC_INFORMATION pbi;
  84. NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL);
  85. return pbi.PebBaseAddress;
  86. }
  87. DWORD*
  88. get_pids(DWORD *numberOfReturnedPIDs) {
  89. int procArraySz = 1024;
  90. /* Win32 SDK says the only way to know if our process array
  91. * wasn't large enough is to check the returned size and make
  92. * sure that it doesn't match the size of the array.
  93. * If it does we allocate a larger array and try again*/
  94. /* Stores the actual array */
  95. DWORD *procArray = NULL;
  96. DWORD procArrayByteSz;
  97. /* Stores the byte size of the returned array from enumprocesses */
  98. DWORD enumReturnSz = 0;
  99. do {
  100. free(procArray);
  101. procArrayByteSz = procArraySz * sizeof(DWORD);
  102. procArray = malloc(procArrayByteSz);
  103. if (! EnumProcesses(procArray, procArrayByteSz, &enumReturnSz)) {
  104. free(procArray);
  105. PyErr_SetFromWindowsErr(0);
  106. return NULL;
  107. }
  108. else if (enumReturnSz == procArrayByteSz) {
  109. /* Process list was too large. Allocate more space*/
  110. procArraySz += 1024;
  111. }
  112. /* else we have a good list */
  113. } while(enumReturnSz == procArraySz * sizeof(DWORD));
  114. /* The number of elements is the returned size / size of each element */
  115. *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD);
  116. return procArray;
  117. }
  118. int
  119. is_system_proc(DWORD pid) {
  120. HANDLE hProcess;
  121. // Special case for PID 0 System Idle Process
  122. // and PID 4 (SYSTEM)
  123. if ((pid == 0) || (pid == 4)) {
  124. return 1;
  125. }
  126. if (pid < 0) {
  127. return 0;
  128. }
  129. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
  130. if (NULL == hProcess) {
  131. // invalid parameter is no such process
  132. if (GetLastError() == ERROR_INVALID_PARAMETER) {
  133. return 0;
  134. }
  135. // access denied obviously means there's a process to deny access to...
  136. if (GetLastError() == ERROR_ACCESS_DENIED) {
  137. return 1;
  138. }
  139. PyErr_SetFromWindowsErr(0);
  140. return -1;
  141. }
  142. return HasSystemPrivilege(hProcess);
  143. }
  144. int
  145. pid_is_running(DWORD pid)
  146. {
  147. HANDLE hProcess;
  148. DWORD exitCode;
  149. // Special case for PID 0 System Idle Process
  150. if (pid == 0) {
  151. return 1;
  152. }
  153. if (pid < 0) {
  154. return 0;
  155. }
  156. hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
  157. FALSE, pid);
  158. if (NULL == hProcess) {
  159. // invalid parameter is no such process
  160. if (GetLastError() == ERROR_INVALID_PARAMETER) {
  161. CloseHandle(hProcess);
  162. return 0;
  163. }
  164. // access denied obviously means there's a process to deny access to...
  165. if (GetLastError() == ERROR_ACCESS_DENIED) {
  166. CloseHandle(hProcess);
  167. return 1;
  168. }
  169. CloseHandle(hProcess);
  170. PyErr_SetFromWindowsErr(0);
  171. return -1;
  172. }
  173. if (GetExitCodeProcess(hProcess, &exitCode)) {
  174. CloseHandle(hProcess);
  175. return (exitCode == STILL_ACTIVE);
  176. }
  177. // access denied means there's a process there so we'll assume it's running
  178. if (GetLastError() == ERROR_ACCESS_DENIED) {
  179. CloseHandle(hProcess);
  180. return 1;
  181. }
  182. PyErr_SetFromWindowsErr(0);
  183. CloseHandle(hProcess);
  184. return -1;
  185. }
  186. int
  187. pid_in_proclist(DWORD pid)
  188. {
  189. DWORD *proclist = NULL;
  190. DWORD numberOfReturnedPIDs;
  191. DWORD i;
  192. proclist = get_pids(&numberOfReturnedPIDs);
  193. if (NULL == proclist) {
  194. return -1;
  195. }
  196. for (i = 0; i < numberOfReturnedPIDs; i++) {
  197. if (pid == proclist[i]) {
  198. free(proclist);
  199. return 1;
  200. }
  201. }
  202. free(proclist);
  203. return 0;
  204. }
  205. // Check exit code from a process handle. Return FALSE on an error also
  206. BOOL is_running(HANDLE hProcess)
  207. {
  208. DWORD dwCode;
  209. if (NULL == hProcess) {
  210. return FALSE;
  211. }
  212. if (GetExitCodeProcess(hProcess, &dwCode)) {
  213. return (dwCode == STILL_ACTIVE);
  214. }
  215. return FALSE;
  216. }
  217. // Return None to represent NoSuchProcess, else return NULL for
  218. // other exception or the name as a Python string
  219. PyObject*
  220. get_name(long pid)
  221. {
  222. HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  223. PROCESSENTRY32 pe = { 0 };
  224. pe.dwSize = sizeof(PROCESSENTRY32);
  225. if( Process32First(h, &pe)) {
  226. do {
  227. if (pe.th32ProcessID == pid) {
  228. CloseHandle(h);
  229. return Py_BuildValue("s", pe.szExeFile);
  230. }
  231. } while(Process32Next(h, &pe));
  232. // the process was never found, set NoSuchProcess exception
  233. NoSuchProcess();
  234. CloseHandle(h);
  235. return NULL;
  236. }
  237. CloseHandle(h);
  238. return PyErr_SetFromWindowsErr(0);
  239. }
  240. /* returns parent pid (as a Python int) for given pid or None on failure */
  241. PyObject*
  242. get_ppid(long pid)
  243. {
  244. HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  245. PROCESSENTRY32 pe = { 0 };
  246. pe.dwSize = sizeof(PROCESSENTRY32);
  247. if( Process32First(h, &pe)) {
  248. do {
  249. if (pe.th32ProcessID == pid) {
  250. //printf("PID: %i; PPID: %i\n", pid, pe.th32ParentProcessID);
  251. CloseHandle(h);
  252. return Py_BuildValue("I", pe.th32ParentProcessID);
  253. }
  254. } while(Process32Next(h, &pe));
  255. // the process was never found, set NoSuchProcess exception
  256. NoSuchProcess();
  257. CloseHandle(h);
  258. return NULL;
  259. }
  260. CloseHandle(h);
  261. return PyErr_SetFromWindowsErr(0);
  262. }
  263. /*
  264. * returns a Python list representing the arguments for the process
  265. * with given pid or NULL on error.
  266. */
  267. PyObject*
  268. get_arg_list(long pid)
  269. {
  270. int nArgs, i;
  271. LPWSTR *szArglist;
  272. HANDLE hProcess;
  273. PVOID pebAddress;
  274. PVOID rtlUserProcParamsAddress;
  275. UNICODE_STRING commandLine;
  276. WCHAR *commandLineContents;
  277. PyObject *arg = NULL;
  278. PyObject *arg_from_wchar = NULL;
  279. PyObject *argList = NULL;
  280. hProcess = handle_from_pid(pid);
  281. if(hProcess == NULL) {
  282. return NULL;
  283. }
  284. pebAddress = GetPebAddress(hProcess);
  285. /* get the address of ProcessParameters */
  286. #ifdef _WIN64
  287. if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32,
  288. &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
  289. #else
  290. if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10,
  291. &rtlUserProcParamsAddress, sizeof(PVOID), NULL))
  292. #endif
  293. {
  294. //printf("Could not read the address of ProcessParameters!\n");
  295. PyErr_SetFromWindowsErr(0);
  296. CloseHandle(hProcess);
  297. return NULL;
  298. }
  299. /* read the CommandLine UNICODE_STRING structure */
  300. #ifdef _WIN64
  301. if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112,
  302. &commandLine, sizeof(commandLine), NULL))
  303. #else
  304. if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40,
  305. &commandLine, sizeof(commandLine), NULL))
  306. #endif
  307. {
  308. //printf("Could not read CommandLine!\n");
  309. PyErr_SetFromWindowsErr(0);
  310. CloseHandle(hProcess);
  311. return NULL;
  312. }
  313. /* allocate memory to hold the command line */
  314. commandLineContents = (WCHAR *)malloc(commandLine.Length+1);
  315. /* read the command line */
  316. if (!ReadProcessMemory(hProcess, commandLine.Buffer,
  317. commandLineContents, commandLine.Length, NULL))
  318. {
  319. //printf("Could not read the command line string!\n");
  320. PyErr_SetFromWindowsErr(0);
  321. CloseHandle(hProcess);
  322. free(commandLineContents);
  323. return NULL;
  324. }
  325. /* print the commandline */
  326. //printf("%.*S\n", commandLine.Length / 2, commandLineContents);
  327. // null-terminate the string to prevent wcslen from returning incorrect length
  328. // the length specifier is in characters, but commandLine.Length is in bytes
  329. commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0';
  330. // attemempt tp parse the command line using Win32 API, fall back on string
  331. // cmdline version otherwise
  332. szArglist = CommandLineToArgvW(commandLineContents, &nArgs);
  333. if (NULL == szArglist) {
  334. // failed to parse arglist
  335. // encode as a UTF8 Python string object from WCHAR string
  336. arg_from_wchar = PyUnicode_FromWideChar(commandLineContents,
  337. commandLine.Length / 2);
  338. #if PY_MAJOR_VERSION >= 3
  339. argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar));
  340. #else
  341. argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar));
  342. #endif
  343. }
  344. else {
  345. // arglist parsed as array of UNICODE_STRING, so convert each to Python
  346. // string object and add to arg list
  347. argList = Py_BuildValue("[]");
  348. for(i=0; i<nArgs; i++) {
  349. //printf("%d: %.*S (%d characters)\n", i, wcslen(szArglist[i]),
  350. // szArglist[i], wcslen(szArglist[i]));
  351. arg_from_wchar = PyUnicode_FromWideChar(szArglist[i],
  352. wcslen(szArglist[i])
  353. );
  354. #if PY_MAJOR_VERSION >= 3
  355. arg = PyUnicode_FromObject(arg_from_wchar);
  356. #else
  357. arg = PyUnicode_AsUTF8String(arg_from_wchar);
  358. #endif
  359. Py_XDECREF(arg_from_wchar);
  360. PyList_Append(argList, arg);
  361. Py_XDECREF(arg);
  362. }
  363. }
  364. LocalFree(szArglist);
  365. free(commandLineContents);
  366. CloseHandle(hProcess);
  367. return argList;
  368. }
  369. #define PH_FIRST_PROCESS(Processes) ((PSYSTEM_PROCESS_INFORMATION)(Processes))
  370. #define PH_NEXT_PROCESS(Process) ( \
  371. ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \
  372. (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \
  373. ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : \
  374. NULL \
  375. )
  376. const STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
  377. const STATUS_BUFFER_TOO_SMALL = 0xC0000023L;
  378. /*
  379. * Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure
  380. * fills the structure with process information.
  381. * On success return 1, else 0 with Python exception already set.
  382. */
  383. int
  384. get_process_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess)
  385. {
  386. static ULONG initialBufferSize = 0x4000;
  387. NTSTATUS status;
  388. PVOID buffer;
  389. ULONG bufferSize;
  390. PSYSTEM_PROCESS_INFORMATION process;
  391. // get NtQuerySystemInformation
  392. typedef DWORD (_stdcall *NTQSI_PROC) (int, PVOID, ULONG, PULONG);
  393. NTQSI_PROC NtQuerySystemInformation;
  394. HINSTANCE hNtDll;
  395. hNtDll = LoadLibrary(TEXT("ntdll.dll"));
  396. NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress(
  397. hNtDll, "NtQuerySystemInformation");
  398. bufferSize = initialBufferSize;
  399. buffer = malloc(bufferSize);
  400. while (TRUE) {
  401. status = NtQuerySystemInformation(SystemProcessInformation, buffer,
  402. bufferSize, &bufferSize);
  403. if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH)
  404. {
  405. free(buffer);
  406. buffer = malloc(bufferSize);
  407. }
  408. else {
  409. break;
  410. }
  411. }
  412. if (status != 0) {
  413. free(buffer);
  414. PyErr_Format(PyExc_RuntimeError, "NtQuerySystemInformation() failed");
  415. return 0;
  416. }
  417. if (bufferSize <= 0x20000)
  418. initialBufferSize = bufferSize;
  419. process = PH_FIRST_PROCESS(buffer);
  420. do {
  421. if (process->UniqueProcessId == (HANDLE)pid) {
  422. free(buffer);
  423. *retProcess = process;
  424. return 1;
  425. }
  426. } while (process = PH_NEXT_PROCESS(process));
  427. free(buffer);
  428. NoSuchProcess();
  429. return 0;
  430. }