OLD | NEW |
1 /* | 1 /* |
2 * $Id: _psutil_osx.c 780 2010-11-10 18:42:47Z jloden $ | 2 * $Id: _psutil_osx.c 1142 2011-10-05 18:45:49Z g.rodola $ |
| 3 * |
| 4 * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. |
| 5 * Use of this source code is governed by a BSD-style license that can be |
| 6 * found in the LICENSE file. |
3 * | 7 * |
4 * OS X platform-specific module methods for _psutil_osx | 8 * OS X platform-specific module methods for _psutil_osx |
5 */ | 9 */ |
6 | 10 |
7 #include <Python.h> | 11 #include <Python.h> |
8 #include <assert.h> | 12 #include <assert.h> |
9 #include <errno.h> | 13 #include <errno.h> |
10 #include <stdbool.h> | 14 #include <stdbool.h> |
11 #include <stdlib.h> | 15 #include <stdlib.h> |
12 #include <stdio.h> | 16 #include <stdio.h> |
13 #include <signal.h> | |
14 #include <sys/sysctl.h> | 17 #include <sys/sysctl.h> |
15 #include <sys/vmmeter.h> | 18 #include <sys/vmmeter.h> |
| 19 #include <libproc.h> |
| 20 #include <sys/proc_info.h> |
| 21 #include <netinet/tcp_fsm.h> |
| 22 #include <arpa/inet.h> |
| 23 #include <net/if_dl.h> |
16 | 24 |
17 #include <mach/mach.h> | 25 #include <mach/mach.h> |
18 #include <mach/task.h> | 26 #include <mach/task.h> |
19 #include <mach/mach_init.h> | 27 #include <mach/mach_init.h> |
20 #include <mach/host_info.h> | 28 #include <mach/host_info.h> |
21 #include <mach/mach_host.h> | 29 #include <mach/mach_host.h> |
22 #include <mach/mach_traps.h> | 30 #include <mach/mach_traps.h> |
23 #include <mach/shared_memory_server.h> | 31 #include <mach/shared_memory_server.h> |
24 | 32 |
| 33 #include <CoreFoundation/CoreFoundation.h> |
| 34 #include <IOKit/IOKitLib.h> |
| 35 #include <IOKit/storage/IOBlockStorageDriver.h> |
| 36 #include <IOKit/storage/IOMedia.h> |
| 37 #include <IOKit/IOBSD.h> |
| 38 |
25 #include "_psutil_osx.h" | 39 #include "_psutil_osx.h" |
| 40 #include "_psutil_common.h" |
26 #include "arch/osx/process_info.h" | 41 #include "arch/osx/process_info.h" |
27 | 42 |
28 | 43 |
29 /* | 44 /* |
30 * define the psutil C module methods and initialize the module. | |
31 */ | |
32 static PyMethodDef | |
33 PsutilMethods[] = | |
34 { | |
35 // --- per-process functions | |
36 | |
37 {"get_process_name", get_process_name, METH_VARARGS, | |
38 "Return process name"}, | |
39 {"get_process_cmdline", get_process_cmdline, METH_VARARGS, | |
40 "Return process cmdline as a list of cmdline arguments"}, | |
41 {"get_process_ppid", get_process_ppid, METH_VARARGS, | |
42 "Return process ppid as an integer"}, | |
43 {"get_process_uid", get_process_uid, METH_VARARGS, | |
44 "Return process real user id as an integer"}, | |
45 {"get_process_gid", get_process_gid, METH_VARARGS, | |
46 "Return process real group id as an integer"}, | |
47 {"get_cpu_times", get_cpu_times, METH_VARARGS, | |
48 "Return tuple of user/kern time for the given PID"}, | |
49 {"get_process_create_time", get_process_create_time, METH_VARARGS, | |
50 "Return a float indicating the process create time expressed in " | |
51 "seconds since the epoch"}, | |
52 {"get_memory_info", get_memory_info, METH_VARARGS, | |
53 "Return a tuple of RSS/VMS memory information"}, | |
54 {"get_process_num_threads", get_process_num_threads, METH_VARARGS, | |
55 "Return number of threads used by process"}, | |
56 | |
57 // --- system-related functions | |
58 | |
59 {"get_pid_list", get_pid_list, METH_VARARGS, | |
60 "Returns a list of PIDs currently running on the system"}, | |
61 {"get_num_cpus", get_num_cpus, METH_VARARGS, | |
62 "Return number of CPUs on the system"}, | |
63 {"get_total_phymem", get_total_phymem, METH_VARARGS, | |
64 "Return the total amount of physical memory, in bytes"}, | |
65 {"get_avail_phymem", get_avail_phymem, METH_VARARGS, | |
66 "Return the amount of available physical memory, in bytes"}, | |
67 {"get_total_virtmem", get_total_virtmem, METH_VARARGS, | |
68 "Return the total amount of virtual memory, in bytes"}, | |
69 {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, | |
70 "Return the amount of available virtual memory, in bytes"}, | |
71 {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, | |
72 "Return system cpu times as a tuple (user, system, nice, idle, irc)"}, | |
73 | |
74 {NULL, NULL, 0, NULL} | |
75 }; | |
76 | |
77 | |
78 /* | |
79 * Raises an OSError(errno=ESRCH, strerror="No such process") exception | |
80 * in Python. | |
81 */ | |
82 static PyObject* | |
83 NoSuchProcess(void) { | |
84 errno = ESRCH; | |
85 return PyErr_SetFromErrno(PyExc_OSError); | |
86 } | |
87 | |
88 /* | |
89 * Raises an OSError(errno=EPERM, strerror="Operation not permitted") exception | |
90 * in Python. | |
91 */ | |
92 static PyObject* | |
93 AccessDenied(void) { | |
94 errno = EPERM; | |
95 return PyErr_SetFromErrno(PyExc_OSError); | |
96 } | |
97 | |
98 struct module_state { | |
99 PyObject *error; | |
100 }; | |
101 | |
102 #if PY_MAJOR_VERSION >= 3 | |
103 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) | |
104 #else | |
105 #define GETSTATE(m) (&_state) | |
106 static struct module_state _state; | |
107 #endif | |
108 | |
109 #if PY_MAJOR_VERSION >= 3 | |
110 | |
111 static int | |
112 psutil_osx_traverse(PyObject *m, visitproc visit, void *arg) { | |
113 Py_VISIT(GETSTATE(m)->error); | |
114 return 0; | |
115 } | |
116 | |
117 static int | |
118 psutil_osx_clear(PyObject *m) { | |
119 Py_CLEAR(GETSTATE(m)->error); | |
120 return 0; | |
121 } | |
122 | |
123 | |
124 static struct PyModuleDef | |
125 moduledef = { | |
126 PyModuleDef_HEAD_INIT, | |
127 "psutil_osx", | |
128 NULL, | |
129 sizeof(struct module_state), | |
130 PsutilMethods, | |
131 NULL, | |
132 psutil_osx_traverse, | |
133 psutil_osx_clear, | |
134 NULL | |
135 }; | |
136 | |
137 #define INITERROR return NULL | |
138 | |
139 PyObject * | |
140 PyInit__psutil_osx(void) | |
141 | |
142 #else | |
143 #define INITERROR return | |
144 | |
145 void | |
146 init_psutil_osx(void) | |
147 #endif | |
148 { | |
149 #if PY_MAJOR_VERSION >= 3 | |
150 PyObject *module = PyModule_Create(&moduledef); | |
151 #else | |
152 PyObject *module = Py_InitModule("_psutil_osx", PsutilMethods); | |
153 #endif | |
154 if (module == NULL) { | |
155 INITERROR; | |
156 } | |
157 struct module_state *st = GETSTATE(module); | |
158 | |
159 st->error = PyErr_NewException("_psutil_osx.Error", NULL, NULL); | |
160 if (st->error == NULL) { | |
161 Py_DECREF(module); | |
162 INITERROR; | |
163 } | |
164 #if PY_MAJOR_VERSION >= 3 | |
165 return module; | |
166 #endif | |
167 } | |
168 | |
169 | |
170 /* | |
171 * Return a Python list of all the PIDs running on the system. | 45 * Return a Python list of all the PIDs running on the system. |
172 */ | 46 */ |
173 static PyObject* | 47 static PyObject* |
174 get_pid_list(PyObject* self, PyObject* args) | 48 get_pid_list(PyObject* self, PyObject* args) |
175 { | 49 { |
176 kinfo_proc *proclist = NULL; | 50 kinfo_proc *proclist = NULL; |
177 kinfo_proc *orig_address = NULL; | 51 kinfo_proc *orig_address = NULL; |
178 size_t num_processes; | 52 size_t num_processes; |
179 size_t idx; | 53 size_t idx; |
180 PyObject *pid; | 54 PyObject *pid; |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
227 { | 101 { |
228 long pid; | 102 long pid; |
229 PyObject* arglist = NULL; | 103 PyObject* arglist = NULL; |
230 | 104 |
231 if (! PyArg_ParseTuple(args, "l", &pid)) { | 105 if (! PyArg_ParseTuple(args, "l", &pid)) { |
232 return NULL; | 106 return NULL; |
233 } | 107 } |
234 | 108 |
235 // get the commandline, defined in arch/osx/process_info.c | 109 // get the commandline, defined in arch/osx/process_info.c |
236 arglist = get_arg_list(pid); | 110 arglist = get_arg_list(pid); |
237 | 111 return arglist; |
238 // get_arg_list() returns NULL only if getcmdargs failed with ESRCH | |
239 // (no process with that PID) | |
240 if (NULL == arglist) { | |
241 return PyErr_SetFromErrno(PyExc_OSError); | |
242 } | |
243 return Py_BuildValue("N", arglist); | |
244 } | 112 } |
245 | 113 |
246 | 114 |
247 /* | 115 /* |
248 * Return process parent pid from kinfo_proc as a Python integer. | 116 * Return process parent pid from kinfo_proc as a Python integer. |
249 */ | 117 */ |
250 static PyObject* | 118 static PyObject* |
251 get_process_ppid(PyObject* self, PyObject* args) | 119 get_process_ppid(PyObject* self, PyObject* args) |
252 { | 120 { |
253 long pid; | 121 long pid; |
254 struct kinfo_proc kp; | 122 struct kinfo_proc kp; |
255 if (! PyArg_ParseTuple(args, "l", &pid)) { | 123 if (! PyArg_ParseTuple(args, "l", &pid)) { |
256 return NULL; | 124 return NULL; |
257 } | 125 } |
258 if (get_kinfo_proc(pid, &kp) == -1) { | 126 if (get_kinfo_proc(pid, &kp) == -1) { |
259 return NULL; | 127 return NULL; |
260 } | 128 } |
261 return Py_BuildValue("l", (long)kp.kp_eproc.e_ppid); | 129 return Py_BuildValue("l", (long)kp.kp_eproc.e_ppid); |
262 } | 130 } |
263 | 131 |
264 | 132 |
265 /* | 133 /* |
266 * Return process real uid from kinfo_proc as a Python integer. | 134 * Return process real uid from kinfo_proc as a Python integer. |
267 */ | 135 */ |
268 static PyObject* | 136 static PyObject* |
269 get_process_uid(PyObject* self, PyObject* args) | 137 get_process_uids(PyObject* self, PyObject* args) |
270 { | 138 { |
271 long pid; | 139 long pid; |
272 struct kinfo_proc kp; | 140 struct kinfo_proc kp; |
273 if (! PyArg_ParseTuple(args, "l", &pid)) { | 141 if (! PyArg_ParseTuple(args, "l", &pid)) { |
274 return NULL; | 142 return NULL; |
275 } | 143 } |
276 if (get_kinfo_proc(pid, &kp) == -1) { | 144 if (get_kinfo_proc(pid, &kp) == -1) { |
277 return NULL; | 145 return NULL; |
278 } | 146 } |
279 return Py_BuildValue("l", (long)kp.kp_eproc.e_pcred.p_ruid); | 147 return Py_BuildValue("lll", (long)kp.kp_eproc.e_pcred.p_ruid, |
| 148 (long)kp.kp_eproc.e_ucred.cr_uid, |
| 149 (long)kp.kp_eproc.e_pcred.p_svuid); |
280 } | 150 } |
281 | 151 |
282 | 152 |
283 /* | 153 /* |
284 * Return process real group id from ki_comm as a Python integer. | 154 * Return process real group id from ki_comm as a Python integer. |
285 */ | 155 */ |
286 static PyObject* | 156 static PyObject* |
287 get_process_gid(PyObject* self, PyObject* args) | 157 get_process_gids(PyObject* self, PyObject* args) |
288 { | 158 { |
289 long pid; | 159 long pid; |
290 struct kinfo_proc kp; | 160 struct kinfo_proc kp; |
291 if (! PyArg_ParseTuple(args, "l", &pid)) { | 161 if (! PyArg_ParseTuple(args, "l", &pid)) { |
292 return NULL; | 162 return NULL; |
293 } | 163 } |
294 if (get_kinfo_proc(pid, &kp) == -1) { | 164 if (get_kinfo_proc(pid, &kp) == -1) { |
295 return NULL; | 165 return NULL; |
296 } | 166 } |
297 return Py_BuildValue("l", (long)kp.kp_eproc.e_pcred.p_rgid); | 167 return Py_BuildValue("lll", (long)kp.kp_eproc.e_pcred.p_rgid, |
| 168 (long)kp.kp_eproc.e_ucred.cr_groups[0], |
| 169 (long)kp.kp_eproc.e_pcred.p_svgid); |
298 } | 170 } |
299 | 171 |
300 | 172 |
301 /* | 173 /* |
302 * Return 1 if PID exists in the current process list, else 0. | 174 * Return process controlling terminal number as an integer. |
303 */ | 175 */ |
304 static int | 176 static PyObject* |
305 pid_exists(long pid) { | 177 get_process_tty_nr(PyObject* self, PyObject* args) |
306 int kill_ret; | 178 { |
307 | 179 long pid; |
308 // save some time if it's an invalid PID | 180 struct kinfo_proc kp; |
309 if (pid < 0) { | 181 if (! PyArg_ParseTuple(args, "l", &pid)) { |
310 return 0; | 182 return NULL; |
311 } | 183 } |
312 | 184 if (get_kinfo_proc(pid, &kp) == -1) { |
313 // if kill returns success of permission denied we know it's a valid PID | 185 return NULL; |
314 kill_ret = kill(pid , 0); | |
315 if ( (0 == kill_ret) || (EPERM == errno) ) { | |
316 return 1; | |
317 } | 186 } |
318 | 187 return Py_BuildValue("i", kp.kp_eproc.e_tdev); |
319 // otherwise return 0 for PID not found | |
320 return 0; | |
321 } | 188 } |
322 | 189 |
323 | 190 |
324 /* | 191 /* |
325 * Return a Python integer indicating the number of CPUs on the system. | 192 * Return a Python integer indicating the number of CPUs on the system. |
326 */ | 193 */ |
327 static PyObject* | 194 static PyObject* |
328 get_num_cpus(PyObject* self, PyObject* args) | 195 get_num_cpus(PyObject* self, PyObject* args) |
329 { | 196 { |
330 | 197 |
(...skipping 18 matching lines...) Expand all Loading... |
349 | 216 |
350 /* | 217 /* |
351 * Return a Python tuple (user_time, kernel_time) | 218 * Return a Python tuple (user_time, kernel_time) |
352 */ | 219 */ |
353 static PyObject* | 220 static PyObject* |
354 get_cpu_times(PyObject* self, PyObject* args) | 221 get_cpu_times(PyObject* self, PyObject* args) |
355 { | 222 { |
356 long pid; | 223 long pid; |
357 int err; | 224 int err; |
358 unsigned int info_count = TASK_BASIC_INFO_COUNT; | 225 unsigned int info_count = TASK_BASIC_INFO_COUNT; |
359 task_port_t task;// = (task_port_t)NULL; | 226 task_port_t task; // = (task_port_t)NULL; |
360 time_value_t user_time, system_time; | 227 time_value_t user_time, system_time; |
361 struct task_basic_info tasks_info; | 228 struct task_basic_info tasks_info; |
362 struct task_thread_times_info task_times; | 229 struct task_thread_times_info task_times; |
363 | 230 |
364 // the argument passed should be a process id | |
365 if (! PyArg_ParseTuple(args, "l", &pid)) { | 231 if (! PyArg_ParseTuple(args, "l", &pid)) { |
366 return NULL; | 232 return NULL; |
367 } | 233 } |
368 | 234 |
369 /* task_for_pid() requires special privileges | 235 /* task_for_pid() requires special privileges |
370 * "This function can be called only if the process is owned by the | 236 * "This function can be called only if the process is owned by the |
371 * procmod group or if the caller is root." | 237 * procmod group or if the caller is root." |
372 * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_bi
nary/universal_binary_tips/chapter_5_section_19.html | 238 * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_bi
nary/universal_binary_tips/chapter_5_section_19.html */ |
373 */ | |
374 err = task_for_pid(mach_task_self(), pid, &task); | 239 err = task_for_pid(mach_task_self(), pid, &task); |
375 if ( err == KERN_SUCCESS) { | 240 if ( err == KERN_SUCCESS) { |
376 info_count = TASK_BASIC_INFO_COUNT; | 241 info_count = TASK_BASIC_INFO_COUNT; |
377 err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_c
ount); | 242 err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_c
ount); |
378 if (err != KERN_SUCCESS) { | 243 if (err != KERN_SUCCESS) { |
379 if (err == 4) { // errcode 4 is "invalid argument" (access denie
d) | 244 // errcode 4 is "invalid argument" (access denied) |
| 245 if (err == 4) { |
380 return AccessDenied(); | 246 return AccessDenied(); |
381 } | 247 } |
382 | 248 |
383 //otherwise throw a runtime error with appropriate error code | 249 // otherwise throw a runtime error with appropriate error code |
384 return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_IN
FO) failed for pid %lu - %s (%i)", | 250 return PyErr_Format(PyExc_RuntimeError, |
385 pid, mach_error_string(err), err); | 251 "task_info(TASK_BASIC_INFO) failed"); |
386 | |
387 } | 252 } |
388 | 253 |
389 info_count = TASK_THREAD_TIMES_INFO_COUNT; | 254 info_count = TASK_THREAD_TIMES_INFO_COUNT; |
390 err = task_info(task, TASK_THREAD_TIMES_INFO, (task_info_t)&task_times,
&info_count); | 255 err = task_info(task, TASK_THREAD_TIMES_INFO, |
| 256 (task_info_t)&task_times, &info_count); |
391 if (err != KERN_SUCCESS) { | 257 if (err != KERN_SUCCESS) { |
392 if (err == 4) { // errcode 4 is "invalid argument" (access denie
d) | 258 // errcode 4 is "invalid argument" (access denied) |
| 259 if (err == 4) { |
393 return AccessDenied(); | 260 return AccessDenied(); |
394 } | 261 } |
395 | 262 return PyErr_Format(PyExc_RuntimeError, |
396 return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_THREAD_T
IMES_INFO) failed for pid %lu - %s (%i)", | 263 "task_info(TASK_BASIC_INFO) failed"); |
397 pid, mach_error_string(err), err); | |
398 } | 264 } |
399 } | 265 } |
400 | 266 |
401 else { // task_for_pid failed | 267 else { // task_for_pid failed |
402 if (! pid_exists(pid) ) { | 268 if (! pid_exists(pid) ) { |
403 return NoSuchProcess(); | 269 return NoSuchProcess(); |
404 } | 270 } |
405 | |
406 // pid exists, so return AccessDenied error since task_for_pid() failed | 271 // pid exists, so return AccessDenied error since task_for_pid() failed |
407 return AccessDenied(); | 272 return AccessDenied(); |
408 } | 273 } |
409 | 274 |
410 float user_t = -1.0; | 275 float user_t = -1.0; |
411 float sys_t = -1.0; | 276 float sys_t = -1.0; |
412 user_time = tasks_info.user_time; | 277 user_time = tasks_info.user_time; |
413 system_time = tasks_info.system_time; | 278 system_time = tasks_info.system_time; |
414 | 279 |
415 time_value_add(&user_time, &task_times.user_time); | 280 time_value_add(&user_time, &task_times.user_time); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
457 mach_port_t object_name; | 322 mach_port_t object_name; |
458 | 323 |
459 // the argument passed should be a process id | 324 // the argument passed should be a process id |
460 if (! PyArg_ParseTuple(args, "l", &pid)) { | 325 if (! PyArg_ParseTuple(args, "l", &pid)) { |
461 return NULL; | 326 return NULL; |
462 } | 327 } |
463 | 328 |
464 /* task_for_pid() requires special privileges | 329 /* task_for_pid() requires special privileges |
465 * "This function can be called only if the process is owned by the | 330 * "This function can be called only if the process is owned by the |
466 * procmod group or if the caller is root." | 331 * procmod group or if the caller is root." |
467 * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_bi
nary/universal_binary_tips/chapter_5_section_19.html | 332 * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_bi
nary/universal_binary_tips/chapter_5_section_19.html */ |
468 */ | |
469 err = task_for_pid(mach_task_self(), pid, &task); | 333 err = task_for_pid(mach_task_self(), pid, &task); |
470 if ( err == KERN_SUCCESS) { | 334 if ( err == KERN_SUCCESS) { |
471 info_count = TASK_BASIC_INFO_COUNT; | 335 info_count = TASK_BASIC_INFO_COUNT; |
472 err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_c
ount); | 336 err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_c
ount); |
473 if (err != KERN_SUCCESS) { | 337 if (err != KERN_SUCCESS) { |
474 if (err == 4) { // errcode 4 is "invalid argument" (access denie
d) | 338 if (err == 4) { |
| 339 // errcode 4 is "invalid argument" (access denied) |
475 return AccessDenied(); | 340 return AccessDenied(); |
476 } | 341 } |
477 | 342 // otherwise throw a runtime error with appropriate error code |
478 //otherwise throw a runtime error with appropriate error code | 343 return PyErr_Format(PyExc_RuntimeError, |
479 return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_IN
FO) failed for pid %lu - %s (%i)", | 344 "task_info(TASK_BASIC_INFO) failed"); |
480 pid, mach_error_string(err), err); | |
481 } | 345 } |
482 | 346 |
483 /* Issue #73 http://code.google.com/p/psutil/issues/detail?id=73 | 347 /* Issue #73 http://code.google.com/p/psutil/issues/detail?id=73 |
484 * adjust the virtual memory size down to account for | 348 * adjust the virtual memory size down to account for |
485 * shared memory that task_info.virtual_size includes w/every process | 349 * shared memory that task_info.virtual_size includes w/every process |
486 */ | 350 */ |
487 info_count = VM_REGION_BASIC_INFO_COUNT_64; | 351 info_count = VM_REGION_BASIC_INFO_COUNT_64; |
488 err = vm_region_64(task, &address, &size, VM_REGION_BASIC_INFO, | 352 err = vm_region_64(task, &address, &size, VM_REGION_BASIC_INFO, |
489 (vm_region_info_t)&b_info, &info_count, &object_name); | 353 (vm_region_info_t)&b_info, &info_count, &object_name); |
490 if (err == KERN_SUCCESS) { | 354 if (err == KERN_SUCCESS) { |
491 if (b_info.reserved && size == (SHARED_TEXT_REGION_SIZE) && | 355 if (b_info.reserved && size == (SHARED_TEXT_REGION_SIZE) && |
492 tasks_info.virtual_size > (SHARED_TEXT_REGION_SIZE + SHARED_DATA
_REGION_SIZE)) { | 356 tasks_info.virtual_size > (SHARED_TEXT_REGION_SIZE + SHARED_DATA
_REGION_SIZE)) |
493 tasks_info.virtual_size -= (SHARED_TEXT_REGION_SIZE + SHARED
_DATA_REGION_SIZE); | 357 { |
| 358 tasks_info.virtual_size -= (SHARED_TEXT_REGION_SIZE + SHARED_DAT
A_REGION_SIZE); |
494 } | 359 } |
495 } | 360 } |
496 } | 361 } |
497 | 362 |
498 else { | 363 else { |
499 if (! pid_exists(pid) ) { | 364 if (! pid_exists(pid) ) { |
500 return NoSuchProcess(); | 365 return NoSuchProcess(); |
501 } | 366 } |
502 | 367 |
503 // pid exists, so return AccessDenied error since task_for_pid() failed | 368 // pid exists, so return AccessDenied error since task_for_pid() failed |
504 return AccessDenied(); | 369 return AccessDenied(); |
505 } | 370 } |
506 | 371 |
507 return Py_BuildValue("(ll)", tasks_info.resident_size, tasks_info.virtual_si
ze); | 372 return Py_BuildValue("(ll)", tasks_info.resident_size, tasks_info.virtual_si
ze); |
508 } | 373 } |
509 | 374 |
510 | 375 |
511 /* | 376 /* |
512 * Return number of threads used by process as a Python integer. | 377 * Return number of threads used by process as a Python integer. |
513 */ | 378 */ |
514 static PyObject* | 379 static PyObject* |
515 get_process_num_threads(PyObject* self, PyObject* args) | 380 get_process_num_threads(PyObject* self, PyObject* args) |
516 { | 381 { |
517 long pid; | 382 long pid; |
518 int err; | 383 int err, ret; |
519 unsigned int info_count = TASK_BASIC_INFO_COUNT; | 384 unsigned int info_count = TASK_BASIC_INFO_COUNT; |
520 mach_port_t task; | 385 mach_port_t task; |
521 struct task_basic_info tasks_info; | 386 struct task_basic_info tasks_info; |
522 thread_act_port_array_t thread_list; | 387 thread_act_port_array_t thread_list; |
523 mach_msg_type_number_t thread_count; | 388 mach_msg_type_number_t thread_count; |
524 | 389 |
525 // the argument passed should be a process id | 390 // the argument passed should be a process id |
526 if (! PyArg_ParseTuple(args, "l", &pid)) { | 391 if (! PyArg_ParseTuple(args, "l", &pid)) { |
527 return NULL; | 392 return NULL; |
528 } | 393 } |
529 | 394 |
530 /* task_for_pid() requires special privileges | 395 /* task_for_pid() requires special privileges |
531 * "This function can be called only if the process is owned by the | 396 * "This function can be called only if the process is owned by the |
532 * procmod group or if the caller is root." | 397 * procmod group or if the caller is root." |
533 * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_bi
nary/universal_binary_tips/chapter_5_section_19.html | 398 * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_bi
nary/universal_binary_tips/chapter_5_section_19.html |
534 */ | 399 */ |
535 err = task_for_pid(mach_task_self(), pid, &task); | 400 err = task_for_pid(mach_task_self(), pid, &task); |
536 if ( err == KERN_SUCCESS) { | 401 if ( err == KERN_SUCCESS) { |
537 info_count = TASK_BASIC_INFO_COUNT; | 402 info_count = TASK_BASIC_INFO_COUNT; |
538 err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_c
ount); | 403 err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_c
ount); |
539 if (err != KERN_SUCCESS) { | 404 if (err != KERN_SUCCESS) { |
540 if (err == 4) { // errcode 4 is "invalid argument" (access denie
d) | 405 // errcode 4 is "invalid argument" (access denied) |
| 406 if (err == 4) { |
541 return AccessDenied(); | 407 return AccessDenied(); |
542 } | 408 } |
543 | 409 |
544 //otherwise throw a runtime error with appropriate error code | 410 // otherwise throw a runtime error with appropriate error code |
545 return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_IN
FO) failed for pid %lu - %s (%i)", | 411 return PyErr_Format(PyExc_RuntimeError, |
546 pid, mach_error_string(err), err); | 412 "task_info(TASK_BASIC_INFO) failed"); |
547 } | 413 } |
548 | |
549 err = task_threads(task, &thread_list, &thread_count); | 414 err = task_threads(task, &thread_list, &thread_count); |
550 if (err == KERN_SUCCESS) { | 415 if (err == KERN_SUCCESS) { |
| 416 ret = vm_deallocate(task, (vm_address_t)thread_list, |
| 417 thread_count * sizeof(int)); |
| 418 if (ret != KERN_SUCCESS) { |
| 419 printf("vm_deallocate() failed\n"); |
| 420 } |
551 return Py_BuildValue("l", (long)thread_count); | 421 return Py_BuildValue("l", (long)thread_count); |
552 } | 422 } |
| 423 else { |
| 424 return PyErr_Format(PyExc_RuntimeError, "task_thread() failed"); |
| 425 } |
553 } | 426 } |
554 | |
555 | |
556 else { | 427 else { |
557 if (! pid_exists(pid) ) { | 428 if (! pid_exists(pid) ) { |
558 return NoSuchProcess(); | 429 return NoSuchProcess(); |
559 } | 430 } |
560 | 431 |
561 // pid exists, so return AccessDenied error since task_for_pid() failed | 432 // pid exists, so return AccessDenied error since task_for_pid() failed |
562 return AccessDenied(); | 433 return AccessDenied(); |
563 } | 434 } |
564 return NULL; | 435 return NULL; |
565 } | 436 } |
(...skipping 11 matching lines...) Expand all Loading... |
577 size_t len; | 448 size_t len; |
578 | 449 |
579 mib[0] = CTL_HW; | 450 mib[0] = CTL_HW; |
580 mib[1] = HW_MEMSIZE; | 451 mib[1] = HW_MEMSIZE; |
581 len = sizeof(total_phymem); | 452 len = sizeof(total_phymem); |
582 | 453 |
583 if (sysctl(mib, 2, &total_phymem, &len, NULL, 0) == -1) { | 454 if (sysctl(mib, 2, &total_phymem, &len, NULL, 0) == -1) { |
584 PyErr_SetFromErrno(0); | 455 PyErr_SetFromErrno(0); |
585 return NULL; | 456 return NULL; |
586 } | 457 } |
587 | |
588 return Py_BuildValue("L", total_phymem); | 458 return Py_BuildValue("L", total_phymem); |
589 } | 459 } |
590 | 460 |
591 | 461 |
592 /* | 462 /* |
593 * Return a Python long indicating the amount of available physical memory in | 463 * Return a Python long indicating the amount of available physical memory in |
594 * bytes. | 464 * bytes. |
595 */ | 465 */ |
596 static PyObject* | 466 static PyObject* |
597 get_avail_phymem(PyObject* self, PyObject* args) | 467 get_avail_phymem(PyObject* self, PyObject* args) |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
671 mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; | 541 mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; |
672 kern_return_t error; | 542 kern_return_t error; |
673 host_cpu_load_info_data_t r_load; | 543 host_cpu_load_info_data_t r_load; |
674 | 544 |
675 error = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&
r_load, &count); | 545 error = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&
r_load, &count); |
676 if (error != KERN_SUCCESS) { | 546 if (error != KERN_SUCCESS) { |
677 return PyErr_Format(PyExc_RuntimeError, | 547 return PyErr_Format(PyExc_RuntimeError, |
678 "Error in host_statistics(): %s", mach_error_string(error)); | 548 "Error in host_statistics(): %s", mach_error_string(error)); |
679 } | 549 } |
680 | 550 |
681 //user, nice, system, idle, iowait, irqm, softirq | |
682 return Py_BuildValue("(dddd)", | 551 return Py_BuildValue("(dddd)", |
683 (double)r_load.cpu_ticks[CPU_STATE_USER] / CLK_TCK, | 552 (double)r_load.cpu_ticks[CPU_STATE_USER] / CLK_TCK, |
684 (double)r_load.cpu_ticks[CPU_STATE_NICE] / CLK_TCK, | 553 (double)r_load.cpu_ticks[CPU_STATE_NICE] / CLK_TCK, |
685 (double)r_load.cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK, | 554 (double)r_load.cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK, |
686 (double)r_load.cpu_ticks[CPU_STATE_IDLE] / CLK_TCK | 555 (double)r_load.cpu_ticks[CPU_STATE_IDLE] / CLK_TCK |
687 ); | 556 ); |
688 } | 557 } |
689 | 558 |
| 559 |
| 560 /* |
| 561 * Return a Python list of tuple representing per-cpu times |
| 562 */ |
| 563 static PyObject* |
| 564 get_system_per_cpu_times(PyObject* self, PyObject* args) |
| 565 { |
| 566 natural_t cpu_count; |
| 567 processor_info_array_t info_array; |
| 568 mach_msg_type_number_t info_count; |
| 569 kern_return_t error; |
| 570 processor_cpu_load_info_data_t* cpu_load_info; |
| 571 PyObject* py_retlist = PyList_New(0); |
| 572 PyObject* py_cputime; |
| 573 int i, ret; |
| 574 |
| 575 error = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, |
| 576 &cpu_count, &info_array, &info_count); |
| 577 if (error != KERN_SUCCESS) { |
| 578 return PyErr_Format(PyExc_RuntimeError, |
| 579 "Error in host_processor_info(): %s", mach_error_string(error)); |
| 580 } |
| 581 |
| 582 cpu_load_info = (processor_cpu_load_info_data_t*) info_array; |
| 583 |
| 584 for (i = 0; i < cpu_count; i++) { |
| 585 py_cputime = Py_BuildValue("(dddd)", |
| 586 (double)cpu_load_info[i].cpu_ticks[CPU_STATE_USER] / CLK_TCK, |
| 587 (double)cpu_load_info[i].cpu_ticks[CPU_STATE_NICE] / CLK_TCK, |
| 588 (double)cpu_load_info[i].cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK, |
| 589 (double)cpu_load_info[i].cpu_ticks[CPU_STATE_IDLE] / CLK_TCK |
| 590 ); |
| 591 PyList_Append(py_retlist, py_cputime); |
| 592 Py_XDECREF(py_cputime); |
| 593 } |
| 594 |
| 595 ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array, |
| 596 info_count * sizeof(int)); |
| 597 if (ret != KERN_SUCCESS) { |
| 598 printf("vm_deallocate() failed\n"); |
| 599 } |
| 600 return py_retlist; |
| 601 } |
| 602 |
| 603 |
| 604 /* |
| 605 * Return a Python float indicating the system boot time expressed in |
| 606 * seconds since the epoch. |
| 607 */ |
| 608 static PyObject* |
| 609 get_system_boot_time(PyObject* self, PyObject* args) |
| 610 { |
| 611 /* fetch sysctl "kern.boottime" */ |
| 612 static int request[2] = { CTL_KERN, KERN_BOOTTIME }; |
| 613 struct timeval result; |
| 614 size_t result_len = sizeof result; |
| 615 time_t boot_time = 0; |
| 616 |
| 617 if (sysctl(request, 2, &result, &result_len, NULL, 0) == -1) { |
| 618 PyErr_SetFromErrno(0); |
| 619 return NULL; |
| 620 } |
| 621 boot_time = result.tv_sec; |
| 622 return Py_BuildValue("f", (float)boot_time); |
| 623 } |
| 624 |
| 625 |
| 626 /* |
| 627 * Return a list of tuples including device, mount point and fs type |
| 628 * for all partitions mounted on the system. |
| 629 */ |
| 630 static PyObject* |
| 631 get_disk_partitions(PyObject* self, PyObject* args) |
| 632 { |
| 633 int num; |
| 634 int i; |
| 635 long len; |
| 636 struct statfs *fs; |
| 637 PyObject* py_retlist = PyList_New(0); |
| 638 PyObject* py_tuple; |
| 639 |
| 640 // get the number of mount points |
| 641 Py_BEGIN_ALLOW_THREADS |
| 642 num = getfsstat(NULL, 0, MNT_NOWAIT); |
| 643 Py_END_ALLOW_THREADS |
| 644 if (num == -1) { |
| 645 PyErr_SetFromErrno(0); |
| 646 return NULL; |
| 647 } |
| 648 |
| 649 len = sizeof(*fs) * num; |
| 650 fs = malloc(len); |
| 651 |
| 652 Py_BEGIN_ALLOW_THREADS |
| 653 num = getfsstat(fs, len, MNT_NOWAIT); |
| 654 Py_END_ALLOW_THREADS |
| 655 if (num == -1) { |
| 656 free(fs); |
| 657 PyErr_SetFromErrno(0); |
| 658 return NULL; |
| 659 } |
| 660 |
| 661 for (i = 0; i < num; i++) { |
| 662 py_tuple = Py_BuildValue("(sss)", fs[i].f_mntfromname, // device |
| 663 fs[i].f_mntonname, // mount point |
| 664 fs[i].f_fstypename); // fs type |
| 665 PyList_Append(py_retlist, py_tuple); |
| 666 Py_XDECREF(py_tuple); |
| 667 } |
| 668 |
| 669 free(fs); |
| 670 return py_retlist; |
| 671 } |
| 672 |
| 673 |
| 674 /* |
| 675 * Return process status as a Python integer. |
| 676 */ |
| 677 static PyObject* |
| 678 get_process_status(PyObject* self, PyObject* args) |
| 679 { |
| 680 long pid; |
| 681 struct kinfo_proc kp; |
| 682 if (! PyArg_ParseTuple(args, "l", &pid)) { |
| 683 return NULL; |
| 684 } |
| 685 if (get_kinfo_proc(pid, &kp) == -1) { |
| 686 return NULL; |
| 687 } |
| 688 return Py_BuildValue("i", (int)kp.kp_proc.p_stat); |
| 689 } |
| 690 |
| 691 |
| 692 /* |
| 693 * Return process threads |
| 694 */ |
| 695 static PyObject* |
| 696 get_process_threads(PyObject* self, PyObject* args) |
| 697 { |
| 698 long pid; |
| 699 int err, j, ret; |
| 700 kern_return_t kr; |
| 701 unsigned int info_count = TASK_BASIC_INFO_COUNT; |
| 702 mach_port_t task; |
| 703 struct task_basic_info tasks_info; |
| 704 thread_act_port_array_t thread_list; |
| 705 thread_info_data_t thinfo; |
| 706 thread_basic_info_t basic_info_th; |
| 707 mach_msg_type_number_t thread_count, thread_info_count; |
| 708 |
| 709 PyObject* retList = PyList_New(0); |
| 710 PyObject* pyTuple = NULL; |
| 711 |
| 712 // the argument passed should be a process id |
| 713 if (! PyArg_ParseTuple(args, "l", &pid)) { |
| 714 return NULL; |
| 715 } |
| 716 |
| 717 // task_for_pid() requires special privileges |
| 718 err = task_for_pid(mach_task_self(), pid, &task); |
| 719 if (err != KERN_SUCCESS) { |
| 720 if (! pid_exists(pid) ) { |
| 721 return NoSuchProcess(); |
| 722 } |
| 723 return AccessDenied(); |
| 724 } |
| 725 |
| 726 info_count = TASK_BASIC_INFO_COUNT; |
| 727 err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count
); |
| 728 if (err != KERN_SUCCESS) { |
| 729 // errcode 4 is "invalid argument" (access denied) |
| 730 if (err == 4) { |
| 731 return AccessDenied(); |
| 732 } |
| 733 // otherwise throw a runtime error with appropriate error code |
| 734 return PyErr_Format(PyExc_RuntimeError, |
| 735 "task_info(TASK_BASIC_INFO) failed"); |
| 736 } |
| 737 |
| 738 err = task_threads(task, &thread_list, &thread_count); |
| 739 if (err != KERN_SUCCESS) { |
| 740 return PyErr_Format(PyExc_RuntimeError, "task_threads() failed"); |
| 741 } |
| 742 |
| 743 for (j = 0; j < thread_count; j++) { |
| 744 thread_info_count = THREAD_INFO_MAX; |
| 745 kr = thread_info(thread_list[j], THREAD_BASIC_INFO, |
| 746 (thread_info_t)thinfo, &thread_info_count); |
| 747 if (kr != KERN_SUCCESS) { |
| 748 return PyErr_Format(PyExc_RuntimeError, "thread_info() failed"); |
| 749 } |
| 750 basic_info_th = (thread_basic_info_t)thinfo; |
| 751 // XXX - thread_info structure does not provide any process id; |
| 752 // the best we can do is assigning an incremental bogus value |
| 753 pyTuple = Py_BuildValue("Iff", j + 1, |
| 754 (float)basic_info_th->user_time.microseconds / 1000000.0, |
| 755 (float)basic_info_th->system_time.microseconds / 1000000.0 |
| 756 ); |
| 757 PyList_Append(retList, pyTuple); |
| 758 Py_XDECREF(pyTuple); |
| 759 } |
| 760 |
| 761 ret = vm_deallocate(task, (vm_address_t)thread_list, |
| 762 thread_count * sizeof(int)); |
| 763 if (ret != KERN_SUCCESS) { |
| 764 printf("vm_deallocate() failed\n"); |
| 765 } |
| 766 |
| 767 return retList; |
| 768 } |
| 769 |
| 770 |
| 771 /* |
| 772 * Return process open files as a Python tuple. |
| 773 * References: |
| 774 * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/m78fd |
| 775 * - /usr/include/sys/proc_info.h |
| 776 */ |
| 777 static PyObject* |
| 778 get_process_open_files(PyObject* self, PyObject* args) |
| 779 { |
| 780 long pid; |
| 781 int pidinfo_result; |
| 782 int iterations; |
| 783 int i; |
| 784 int nb; |
| 785 |
| 786 struct proc_fdinfo *fds_pointer; |
| 787 struct proc_fdinfo *fdp_pointer; |
| 788 struct vnode_fdinfowithpath vi; |
| 789 |
| 790 PyObject *retList = PyList_New(0); |
| 791 PyObject *tuple = NULL; |
| 792 |
| 793 if (! PyArg_ParseTuple(args, "l", &pid)) { |
| 794 return NULL; |
| 795 } |
| 796 |
| 797 pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); |
| 798 if (pidinfo_result <= 0) { |
| 799 goto error; |
| 800 } |
| 801 |
| 802 fds_pointer = malloc(pidinfo_result); |
| 803 pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, |
| 804 pidinfo_result); |
| 805 free(fds_pointer); |
| 806 |
| 807 if (pidinfo_result <= 0) { |
| 808 goto error; |
| 809 } |
| 810 |
| 811 iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE); |
| 812 |
| 813 for (i = 0; i < iterations; i++) { |
| 814 fdp_pointer = &fds_pointer[i]; |
| 815 |
| 816 // |
| 817 if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) |
| 818 { |
| 819 nb = proc_pidfdinfo(pid, |
| 820 fdp_pointer->proc_fd, |
| 821 PROC_PIDFDVNODEPATHINFO, |
| 822 &vi, |
| 823 sizeof(vi)); |
| 824 |
| 825 // --- errors checking |
| 826 if (nb <= 0) { |
| 827 if ((errno == ENOENT) || (errno == EBADF)) { |
| 828 // no such file or directory or bad file descriptor; |
| 829 // let's assume the file has been closed or removed |
| 830 continue; |
| 831 } |
| 832 if (errno != 0) { |
| 833 return PyErr_SetFromErrno(PyExc_OSError); |
| 834 } |
| 835 else |
| 836 return PyErr_Format(PyExc_RuntimeError, |
| 837 "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed"); |
| 838 } |
| 839 if (nb < sizeof(vi)) { |
| 840 return PyErr_Format(PyExc_RuntimeError, |
| 841 "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed (buffer mismatch)
"); |
| 842 } |
| 843 // --- /errors checking |
| 844 |
| 845 // --- construct python list |
| 846 tuple = Py_BuildValue("(si)", vi.pvip.vip_path, |
| 847 (int)fdp_pointer->proc_fd); |
| 848 PyList_Append(retList, tuple); |
| 849 Py_DECREF(tuple); |
| 850 // --- /construct python list |
| 851 } |
| 852 } |
| 853 |
| 854 return retList; |
| 855 |
| 856 error: |
| 857 if (errno != 0) |
| 858 return PyErr_SetFromErrno(PyExc_OSError); |
| 859 else if (! pid_exists(pid) ) |
| 860 return NoSuchProcess(); |
| 861 else |
| 862 return PyErr_Format(PyExc_RuntimeError, |
| 863 "proc_pidinfo(PROC_PIDLISTFDS) failed"); |
| 864 } |
| 865 |
| 866 |
| 867 /* |
| 868 * mathes Linux net/tcp_states.h: |
| 869 * http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h |
| 870 */ |
| 871 static char * |
| 872 get_connection_status(int st) { |
| 873 switch (st) { |
| 874 case TCPS_CLOSED: |
| 875 return "CLOSE"; |
| 876 case TCPS_CLOSING: |
| 877 return "CLOSING"; |
| 878 case TCPS_CLOSE_WAIT: |
| 879 return "CLOSE_WAIT"; |
| 880 case TCPS_LISTEN: |
| 881 return "LISTEN"; |
| 882 case TCPS_ESTABLISHED: |
| 883 return "ESTABLISHED"; |
| 884 case TCPS_SYN_SENT: |
| 885 return "SYN_SENT"; |
| 886 case TCPS_SYN_RECEIVED: |
| 887 return "SYN_RECV"; |
| 888 case TCPS_FIN_WAIT_1: |
| 889 return "FIN_WAIT_1"; |
| 890 case TCPS_FIN_WAIT_2: |
| 891 return "FIN_WAIT_2"; |
| 892 case TCPS_LAST_ACK: |
| 893 return "LAST_ACK"; |
| 894 case TCPS_TIME_WAIT: |
| 895 return "TIME_WAIT"; |
| 896 default: |
| 897 return ""; |
| 898 } |
| 899 } |
| 900 |
| 901 |
| 902 /* |
| 903 * Return process TCP and UDP connections as a list of tuples. |
| 904 * References: |
| 905 * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/wNrC0 |
| 906 * - /usr/include/sys/proc_info.h |
| 907 */ |
| 908 static PyObject* |
| 909 get_process_connections(PyObject* self, PyObject* args) |
| 910 { |
| 911 long pid; |
| 912 int pidinfo_result; |
| 913 int iterations; |
| 914 int i; |
| 915 int nb; |
| 916 |
| 917 struct proc_fdinfo *fds_pointer; |
| 918 struct proc_fdinfo *fdp_pointer; |
| 919 struct socket_fdinfo si; |
| 920 |
| 921 |
| 922 PyObject *retList = PyList_New(0); |
| 923 PyObject *tuple = NULL; |
| 924 PyObject *laddr = NULL; |
| 925 PyObject *raddr = NULL; |
| 926 |
| 927 if (! PyArg_ParseTuple(args, "l", &pid)) { |
| 928 return NULL; |
| 929 } |
| 930 |
| 931 if (pid == 0) { |
| 932 return retList; |
| 933 } |
| 934 |
| 935 pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); |
| 936 if (pidinfo_result <= 0) { |
| 937 goto error; |
| 938 } |
| 939 |
| 940 fds_pointer = malloc(pidinfo_result); |
| 941 pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, |
| 942 pidinfo_result); |
| 943 free(fds_pointer); |
| 944 |
| 945 if (pidinfo_result <= 0) { |
| 946 goto error; |
| 947 } |
| 948 |
| 949 iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE); |
| 950 |
| 951 for (i = 0; i < iterations; i++) { |
| 952 errno = 0; |
| 953 fdp_pointer = &fds_pointer[i]; |
| 954 |
| 955 // |
| 956 if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET) |
| 957 { |
| 958 nb = proc_pidfdinfo(pid, fdp_pointer->proc_fd, PROC_PIDFDSOCKETINFO, |
| 959 &si, sizeof(si)); |
| 960 |
| 961 // --- errors checking |
| 962 if (nb <= 0) { |
| 963 if (errno == EBADF) { |
| 964 // let's assume socket has been closed |
| 965 continue; |
| 966 } |
| 967 if (errno != 0) { |
| 968 return PyErr_SetFromErrno(PyExc_OSError); |
| 969 } |
| 970 else { |
| 971 return PyErr_Format(PyExc_RuntimeError, |
| 972 "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed"); |
| 973 } |
| 974 } |
| 975 if (nb < sizeof(si)) { |
| 976 return PyErr_Format(PyExc_RuntimeError, |
| 977 "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed (buffer mismatch)
"); |
| 978 } |
| 979 // --- /errors checking |
| 980 |
| 981 // |
| 982 int fd, family, type, lport, rport; |
| 983 char lip[200], rip[200]; |
| 984 char *state; |
| 985 |
| 986 fd = (int)fdp_pointer->proc_fd; |
| 987 family = si.psi.soi_family; |
| 988 type = si.psi.soi_kind; |
| 989 |
| 990 if ((family != AF_INET) && (family != AF_INET6)) { |
| 991 continue; |
| 992 } |
| 993 |
| 994 if (type == 2) |
| 995 type = SOCK_STREAM; |
| 996 else if (type == 1) |
| 997 type = SOCK_DGRAM; |
| 998 else |
| 999 continue; |
| 1000 |
| 1001 if (errno != 0) { |
| 1002 printf("errno 1 = %i\n", errno); |
| 1003 return PyErr_SetFromErrno(PyExc_OSError); |
| 1004 } |
| 1005 |
| 1006 |
| 1007 if (family == AF_INET) { |
| 1008 inet_ntop(AF_INET, |
| 1009 &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_46.
i46a_addr4, |
| 1010 lip, |
| 1011 sizeof(lip)); |
| 1012 inet_ntop(AF_INET, |
| 1013 &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_46.
i46a_addr4, |
| 1014 rip, |
| 1015 sizeof(lip)); |
| 1016 } |
| 1017 else { |
| 1018 inet_ntop(AF_INET6, |
| 1019 &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_6, |
| 1020 lip, sizeof(lip)); |
| 1021 inet_ntop(AF_INET6, |
| 1022 &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_6, |
| 1023 lip, sizeof(rip)); |
| 1024 } |
| 1025 |
| 1026 // check for inet_ntop failures |
| 1027 if (errno != 0) { |
| 1028 return PyErr_SetFromErrno(PyExc_OSError); |
| 1029 } |
| 1030 |
| 1031 lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport); |
| 1032 rport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport); |
| 1033 if (type == SOCK_STREAM) |
| 1034 state = get_connection_status((int)si.psi.soi_proto.pri_tcp.tcps
i_state); |
| 1035 else |
| 1036 state = ""; |
| 1037 |
| 1038 laddr = Py_BuildValue("(si)", lip, lport); |
| 1039 if (rport != 0) |
| 1040 raddr = Py_BuildValue("(si)", rip, rport); |
| 1041 else |
| 1042 raddr = PyTuple_New(0); |
| 1043 |
| 1044 // --- construct python list |
| 1045 tuple = Py_BuildValue("(iiiNNs)", fd, family, type, laddr, raddr, |
| 1046 state); |
| 1047 PyList_Append(retList, tuple); |
| 1048 Py_DECREF(tuple); |
| 1049 // --- /construct python list |
| 1050 } |
| 1051 } |
| 1052 |
| 1053 return retList; |
| 1054 |
| 1055 error: |
| 1056 if (errno != 0) |
| 1057 return PyErr_SetFromErrno(PyExc_OSError); |
| 1058 else if (! pid_exists(pid) ) |
| 1059 return NoSuchProcess(); |
| 1060 else |
| 1061 return PyErr_Format(PyExc_RuntimeError, |
| 1062 "proc_pidinfo(PROC_PIDLISTFDS) failed"); |
| 1063 } |
| 1064 |
| 1065 |
| 1066 /* |
| 1067 * Return a Python list of named tuples with overall network I/O information |
| 1068 */ |
| 1069 static PyObject* |
| 1070 get_network_io_counters(PyObject* self, PyObject* args) |
| 1071 { |
| 1072 PyObject* py_retdict = PyDict_New(); |
| 1073 PyObject* py_ifc_info; |
| 1074 |
| 1075 char *buf = NULL, *lim, *next; |
| 1076 struct if_msghdr *ifm; |
| 1077 int mib[6]; |
| 1078 size_t len; |
| 1079 |
| 1080 mib[0] = CTL_NET; // networking subsystem |
| 1081 mib[1] = PF_ROUTE; // type of information |
| 1082 mib[2] = 0; // protocol (IPPROTO_xxx) |
| 1083 mib[3] = 0; // address family |
| 1084 mib[4] = NET_RT_IFLIST2; // operation |
| 1085 mib[5] = 0; |
| 1086 |
| 1087 if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { |
| 1088 Py_DECREF(py_retdict); |
| 1089 PyErr_SetFromErrno(0); |
| 1090 return NULL; |
| 1091 } |
| 1092 |
| 1093 buf = malloc(len); |
| 1094 |
| 1095 if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { |
| 1096 if (buf) { |
| 1097 free(buf); |
| 1098 } |
| 1099 Py_DECREF(py_retdict); |
| 1100 PyErr_SetFromErrno(0); |
| 1101 return NULL; |
| 1102 } |
| 1103 |
| 1104 lim = buf + len; |
| 1105 |
| 1106 for (next = buf; next < lim; ) { |
| 1107 ifm = (struct if_msghdr *)next; |
| 1108 next += ifm->ifm_msglen; |
| 1109 |
| 1110 if (ifm->ifm_type == RTM_IFINFO2) { |
| 1111 struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm; |
| 1112 struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1); |
| 1113 char ifc_name[32]; |
| 1114 |
| 1115 strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen); |
| 1116 ifc_name[sdl->sdl_nlen] = 0; |
| 1117 |
| 1118 py_ifc_info = Py_BuildValue("(KKKK)", |
| 1119 if2m->ifm_data.ifi_obytes, |
| 1120 if2m->ifm_data.ifi_ibytes, |
| 1121 if2m->ifm_data.ifi_opackets, |
| 1122 if2m->ifm_data.ifi_ipackets); |
| 1123 PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info); |
| 1124 Py_XDECREF(py_ifc_info); |
| 1125 } |
| 1126 else { |
| 1127 continue; |
| 1128 } |
| 1129 } |
| 1130 |
| 1131 free(buf); |
| 1132 |
| 1133 return py_retdict; |
| 1134 } |
| 1135 |
| 1136 |
| 1137 /* |
| 1138 * Return a Python dict of tuples for disk I/O information |
| 1139 */ |
| 1140 static PyObject* |
| 1141 get_disk_io_counters(PyObject* self, PyObject* args) |
| 1142 { |
| 1143 PyObject* py_retdict = PyDict_New(); |
| 1144 PyObject* py_disk_info; |
| 1145 |
| 1146 CFDictionaryRef parent_dict; |
| 1147 CFDictionaryRef props_dict; |
| 1148 CFDictionaryRef stats_dict; |
| 1149 io_registry_entry_t parent; |
| 1150 io_registry_entry_t disk; |
| 1151 io_iterator_t disk_list; |
| 1152 |
| 1153 /* Get list of disks */ |
| 1154 if (IOServiceGetMatchingServices(kIOMasterPortDefault, |
| 1155 IOServiceMatching(kIOMediaClass), |
| 1156 &disk_list) != kIOReturnSuccess) { |
| 1157 Py_DECREF(py_retdict); |
| 1158 PyErr_SetString(PyExc_RuntimeError, "Unable to get the list of disks."); |
| 1159 return NULL; |
| 1160 } |
| 1161 |
| 1162 /* Iterate over disks */ |
| 1163 while ((disk = IOIteratorNext(disk_list)) != 0) { |
| 1164 parent_dict = NULL; |
| 1165 props_dict = NULL; |
| 1166 stats_dict = NULL; |
| 1167 |
| 1168 if (IORegistryEntryGetParentEntry(disk, kIOServicePlane, &parent) != kIO
ReturnSuccess) { |
| 1169 PyErr_SetString(PyExc_RuntimeError, "Unable to get the disk's parent
."); |
| 1170 Py_DECREF(py_retdict); |
| 1171 IOObjectRelease(disk); |
| 1172 return NULL; |
| 1173 } |
| 1174 |
| 1175 if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) { |
| 1176 if(IORegistryEntryCreateCFProperties( |
| 1177 disk, |
| 1178 (CFMutableDictionaryRef *) &parent_dict, |
| 1179 kCFAllocatorDefault, |
| 1180 kNilOptions) != kIOReturnSuccess) |
| 1181 { |
| 1182 PyErr_SetString(PyExc_RuntimeError, |
| 1183 "Unable to get the parent's properties."); |
| 1184 Py_DECREF(py_retdict); |
| 1185 IOObjectRelease(disk); |
| 1186 IOObjectRelease(parent); |
| 1187 return NULL; |
| 1188 } |
| 1189 |
| 1190 if (IORegistryEntryCreateCFProperties(parent, |
| 1191 (CFMutableDictionaryRef *) &props_dict
, |
| 1192 kCFAllocatorDefault, |
| 1193 kNilOptions) != kIOReturnSuccess) |
| 1194 { |
| 1195 PyErr_SetString(PyExc_RuntimeError, |
| 1196 "Unable to get the disk properties."); |
| 1197 Py_DECREF(py_retdict); |
| 1198 IOObjectRelease(disk); |
| 1199 return NULL; |
| 1200 } |
| 1201 |
| 1202 const int kMaxDiskNameSize = 64; |
| 1203 CFStringRef disk_name_ref = (CFStringRef)CFDictionaryGetValue( |
| 1204 parent_dict, |
| 1205 CFSTR(kIOBSDNameKey)); |
| 1206 char disk_name[kMaxDiskNameSize]; |
| 1207 |
| 1208 CFStringGetCString(disk_name_ref, |
| 1209 disk_name, |
| 1210 kMaxDiskNameSize, |
| 1211 CFStringGetSystemEncoding()); |
| 1212 |
| 1213 stats_dict = (CFDictionaryRef)CFDictionaryGetValue( |
| 1214 props_dict, |
| 1215 CFSTR(kIOBlockStorageDriverStatisticsKey)); |
| 1216 |
| 1217 if (stats_dict == NULL) { |
| 1218 PyErr_SetString(PyExc_RuntimeError, "Unable to get disk stats.")
; |
| 1219 Py_DECREF(py_retdict); |
| 1220 CFRelease(props_dict); |
| 1221 IOObjectRelease(disk); |
| 1222 IOObjectRelease(parent); |
| 1223 return NULL; |
| 1224 } |
| 1225 |
| 1226 CFNumberRef number; |
| 1227 int64_t reads, writes, read_bytes, write_bytes, read_time, write_tim
e = 0; |
| 1228 |
| 1229 /* Get disk reads/writes */ |
| 1230 if ((number = (CFNumberRef)CFDictionaryGetValue( |
| 1231 stats_dict, |
| 1232 CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) |
| 1233 { |
| 1234 CFNumberGetValue(number, kCFNumberSInt64Type, &reads); |
| 1235 } |
| 1236 if ((number = (CFNumberRef)CFDictionaryGetValue( |
| 1237 stats_dict, |
| 1238 CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) |
| 1239 { |
| 1240 CFNumberGetValue(number, kCFNumberSInt64Type, &writes); |
| 1241 } |
| 1242 |
| 1243 /* Get disk bytes read/written */ |
| 1244 if ((number = (CFNumberRef)CFDictionaryGetValue( |
| 1245 stats_dict, |
| 1246 CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) |
| 1247 { |
| 1248 CFNumberGetValue(number, kCFNumberSInt64Type, &read_bytes); |
| 1249 } |
| 1250 if ((number = (CFNumberRef)CFDictionaryGetValue( |
| 1251 stats_dict, |
| 1252 CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) |
| 1253 { |
| 1254 CFNumberGetValue(number, kCFNumberSInt64Type, &write_bytes); |
| 1255 } |
| 1256 |
| 1257 /* Get disk time spent reading/writing (nanoseconds) */ |
| 1258 if ((number = (CFNumberRef)CFDictionaryGetValue( |
| 1259 stats_dict, |
| 1260 CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) |
| 1261 { |
| 1262 CFNumberGetValue(number, kCFNumberSInt64Type, &read_time); |
| 1263 } |
| 1264 if ((number = (CFNumberRef)CFDictionaryGetValue( |
| 1265 stats_dict, |
| 1266 CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) { |
| 1267 CFNumberGetValue(number, kCFNumberSInt64Type, &write_time); |
| 1268 } |
| 1269 |
| 1270 py_disk_info = Py_BuildValue("(KKKKKK)", |
| 1271 reads, writes, |
| 1272 read_bytes, write_bytes, |
| 1273 read_time, write_time); |
| 1274 PyDict_SetItemString(py_retdict, disk_name, py_disk_info); |
| 1275 Py_XDECREF(py_disk_info); |
| 1276 |
| 1277 CFRelease(parent_dict); |
| 1278 IOObjectRelease(parent); |
| 1279 CFRelease(props_dict); |
| 1280 IOObjectRelease(disk); |
| 1281 } |
| 1282 } |
| 1283 |
| 1284 IOObjectRelease (disk_list); |
| 1285 |
| 1286 return py_retdict; |
| 1287 } |
| 1288 |
| 1289 |
| 1290 /* |
| 1291 * define the psutil C module methods and initialize the module. |
| 1292 */ |
| 1293 static PyMethodDef |
| 1294 PsutilMethods[] = |
| 1295 { |
| 1296 // --- per-process functions |
| 1297 |
| 1298 {"get_process_name", get_process_name, METH_VARARGS, |
| 1299 "Return process name"}, |
| 1300 {"get_process_cmdline", get_process_cmdline, METH_VARARGS, |
| 1301 "Return process cmdline as a list of cmdline arguments"}, |
| 1302 {"get_process_ppid", get_process_ppid, METH_VARARGS, |
| 1303 "Return process ppid as an integer"}, |
| 1304 {"get_process_uids", get_process_uids, METH_VARARGS, |
| 1305 "Return process real user id as an integer"}, |
| 1306 {"get_process_gids", get_process_gids, METH_VARARGS, |
| 1307 "Return process real group id as an integer"}, |
| 1308 {"get_cpu_times", get_cpu_times, METH_VARARGS, |
| 1309 "Return tuple of user/kern time for the given PID"}, |
| 1310 {"get_process_create_time", get_process_create_time, METH_VARARGS, |
| 1311 "Return a float indicating the process create time expressed in " |
| 1312 "seconds since the epoch"}, |
| 1313 {"get_memory_info", get_memory_info, METH_VARARGS, |
| 1314 "Return a tuple of RSS/VMS memory information"}, |
| 1315 {"get_process_num_threads", get_process_num_threads, METH_VARARGS, |
| 1316 "Return number of threads used by process"}, |
| 1317 {"get_process_status", get_process_status, METH_VARARGS, |
| 1318 "Return process status as an integer"}, |
| 1319 {"get_process_threads", get_process_threads, METH_VARARGS, |
| 1320 "Return process threads as a list of tuples"}, |
| 1321 {"get_process_open_files", get_process_open_files, METH_VARARGS, |
| 1322 "Return files opened by process as a list of tuples"}, |
| 1323 {"get_process_connections", get_process_connections, METH_VARARGS, |
| 1324 "Get process TCP and UDP connections as a list of tuples"}, |
| 1325 {"get_process_tty_nr", get_process_tty_nr, METH_VARARGS, |
| 1326 "Return process tty number as an integer"}, |
| 1327 |
| 1328 // --- system-related functions |
| 1329 |
| 1330 {"get_pid_list", get_pid_list, METH_VARARGS, |
| 1331 "Returns a list of PIDs currently running on the system"}, |
| 1332 {"get_num_cpus", get_num_cpus, METH_VARARGS, |
| 1333 "Return number of CPUs on the system"}, |
| 1334 {"get_total_phymem", get_total_phymem, METH_VARARGS, |
| 1335 "Return the total amount of physical memory, in bytes"}, |
| 1336 {"get_avail_phymem", get_avail_phymem, METH_VARARGS, |
| 1337 "Return the amount of available physical memory, in bytes"}, |
| 1338 {"get_total_virtmem", get_total_virtmem, METH_VARARGS, |
| 1339 "Return the total amount of virtual memory, in bytes"}, |
| 1340 {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, |
| 1341 "Return the amount of available virtual memory, in bytes"}, |
| 1342 {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, |
| 1343 "Return system cpu times as a tuple (user, system, nice, idle, irc)"}, |
| 1344 {"get_system_per_cpu_times", get_system_per_cpu_times, METH_VARARGS, |
| 1345 "Return system per-cpu times as a list of tuples"}, |
| 1346 {"get_system_boot_time", get_system_boot_time, METH_VARARGS, |
| 1347 "Return a float indicating the system boot time expressed in " |
| 1348 "seconds since the epoch"}, |
| 1349 {"get_disk_partitions", get_disk_partitions, METH_VARARGS, |
| 1350 "Return a list of tuples including device, mount point and " |
| 1351 "fs type for all partitions mounted on the system."}, |
| 1352 {"get_network_io_counters", get_network_io_counters, METH_VARARGS, |
| 1353 "Return dict of tuples of networks I/O information."}, |
| 1354 {"get_disk_io_counters", get_disk_io_counters, METH_VARARGS, |
| 1355 "Return dict of tuples of disks I/O information."}, |
| 1356 |
| 1357 {NULL, NULL, 0, NULL} |
| 1358 }; |
| 1359 |
| 1360 |
| 1361 struct module_state { |
| 1362 PyObject *error; |
| 1363 }; |
| 1364 |
| 1365 #if PY_MAJOR_VERSION >= 3 |
| 1366 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) |
| 1367 #else |
| 1368 #define GETSTATE(m) (&_state) |
| 1369 #endif |
| 1370 |
| 1371 #if PY_MAJOR_VERSION >= 3 |
| 1372 |
| 1373 static int |
| 1374 psutil_osx_traverse(PyObject *m, visitproc visit, void *arg) { |
| 1375 Py_VISIT(GETSTATE(m)->error); |
| 1376 return 0; |
| 1377 } |
| 1378 |
| 1379 static int |
| 1380 psutil_osx_clear(PyObject *m) { |
| 1381 Py_CLEAR(GETSTATE(m)->error); |
| 1382 return 0; |
| 1383 } |
| 1384 |
| 1385 |
| 1386 static struct PyModuleDef |
| 1387 moduledef = { |
| 1388 PyModuleDef_HEAD_INIT, |
| 1389 "psutil_osx", |
| 1390 NULL, |
| 1391 sizeof(struct module_state), |
| 1392 PsutilMethods, |
| 1393 NULL, |
| 1394 psutil_osx_traverse, |
| 1395 psutil_osx_clear, |
| 1396 NULL |
| 1397 }; |
| 1398 |
| 1399 #define INITERROR return NULL |
| 1400 |
| 1401 PyObject * |
| 1402 PyInit__psutil_osx(void) |
| 1403 |
| 1404 #else |
| 1405 #define INITERROR return |
| 1406 |
| 1407 void |
| 1408 init_psutil_osx(void) |
| 1409 #endif |
| 1410 { |
| 1411 #if PY_MAJOR_VERSION >= 3 |
| 1412 PyObject *module = PyModule_Create(&moduledef); |
| 1413 #else |
| 1414 PyObject *module = Py_InitModule("_psutil_osx", PsutilMethods); |
| 1415 #endif |
| 1416 // process status constants, defined in: |
| 1417 // http://fxr.watson.org/fxr/source/bsd/sys/proc.h?v=xnu-792.6.70#L149 |
| 1418 PyModule_AddIntConstant(module, "SIDL", SIDL); |
| 1419 PyModule_AddIntConstant(module, "SRUN", SRUN); |
| 1420 PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); |
| 1421 PyModule_AddIntConstant(module, "SSTOP", SSTOP); |
| 1422 PyModule_AddIntConstant(module, "SZOMB", SZOMB); |
| 1423 |
| 1424 if (module == NULL) { |
| 1425 INITERROR; |
| 1426 } |
| 1427 #if PY_MAJOR_VERSION >= 3 |
| 1428 return module; |
| 1429 #endif |
| 1430 } |
OLD | NEW |