diff options
author | nirnimesh@chromium.org <nirnimesh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-07 23:52:38 +0000 |
---|---|---|
committer | nirnimesh@chromium.org <nirnimesh@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-07 23:52:38 +0000 |
commit | eab312ae5e4b4d27f5a6ab52cfbced3db89c36bf (patch) | |
tree | afb35476f4ab3b9e495b994521332358ffa7cb7f /third_party | |
parent | 0abb16507849cd22bb7bcfad3feb9fd694b0de2f (diff) | |
download | chromium_src-eab312ae5e4b4d27f5a6ab52cfbced3db89c36bf.zip chromium_src-eab312ae5e4b4d27f5a6ab52cfbced3db89c36bf.tar.gz chromium_src-eab312ae5e4b4d27f5a6ab52cfbced3db89c36bf.tar.bz2 |
Moving psutil to third_party. This is first step for Media Performance test project.
Review URL: http://codereview.chromium.org/6246123
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@74047 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party')
43 files changed, 10564 insertions, 0 deletions
diff --git a/third_party/psutil/CREDITS b/third_party/psutil/CREDITS new file mode 100644 index 0000000..1ac44fb --- /dev/null +++ b/third_party/psutil/CREDITS @@ -0,0 +1,48 @@ + +Intro +===== + +We would like to recognize some of the people who have been instrumental in the +development of psutil. +I'm sure we are forgetting some people (feel free to email us), but here is a +short list. +It's modeled after the Linux CREDITS file where the fields are: +name (N), e-mail (E), web-address (W), country (C), description (D), (I) issues +(issue tracker is at http://code.google.com/p/psutil/issues/list). +Really thanks to all of you. + + +Maintainers +=========== + +N: Giampaolo Rodola' +C: Italy +E: g.rodola@gmail.com + +N: Jay Loden +C: NJ, USA +E: jloden@gmail.com +W: http://www.jayloden.com + + +Contributors +============ + +N: wj32 +E: wj32.64@gmail.com +D: process username() and get_connections() on Windows +I: 114, 115 + +N: Yan Raber +C: Bologna, Italy +E: yanraber@gmail.com +D: help on Windows development + +N: Dave Daeschler +C: USA +E: ddaeschl@rsaisp.com + +N: cjgohlke +E: cjgohlke@gmail.com +D: Windows 64 bit support +I: 107 diff --git a/third_party/psutil/HISTORY b/third_party/psutil/HISTORY new file mode 100644 index 0000000..3e4ce96 --- /dev/null +++ b/third_party/psutil/HISTORY @@ -0,0 +1,152 @@ +Bug tracker at http://code.google.com/p/psutil/issues + +0.2.0 - 2010-11-13 +------------------ + +NEW FEATURES + + * Issue 79: per-process open files. + * Issue 88: total system physical cached memory. + * Issue 88: total system physical memory buffers used by the kernel. + * Issue 91: per-process send_signal() and terminate() methods. + * Issue 95: NoSuchProcess and AccessDenied exception classes now provide "pid", + "name" and "msg" attributes. + * Issue 97: per-process children. + * Issue 98: Process.get_cpu_times() and Process.get_memory_info now return + a namedtuple instead of a tuple. + * Issue 103: per-process opened TCP and UDP connections. + * Issue 107: add support for Windows 64 bit. (patch by cjgohlke) + * Issue 111: per-process executable name. + * Issue 113: exception messages now include process name and pid. + * Issue 114: process username Windows implementation has been rewritten in pure + C and no longer uses WMI resulting in a big speedup. Also, pywin32 is no + longer required as a third-party dependancy. (patch by wj32) + * Issue 117: added support for Windows 2000. + * Issue 123: psutil.cpu_percent() and psutil.Process.cpu_percent() accept a + new 'interval' parameter. + * Issue 129: per-process number of threads. + +BUGFIXES + + * Issue 80: fixed warnings when installing psutil with easy_install. + * Issue 81: psutil fails to compile with Visual Studio. + * Issue 94: suspend() raises OSError instead of AccessDenied. + * Issue 86: psutil didn't compile against FreeBSD 6.x. + * Issue 102: orphaned process handles obtained by using OpenProcess in C were + left behind every time Process class was instantiated. + * Issue 111: path and name Process properties report truncated or erroneous + values on UNIX. + * Issue 120: cpu_percent() always returning 100% on OS X. + * Issue 112: uid and gid properties don't change if process changes effective + user/group id at some point. + * Issue 126: ppid, uid, gid, name, exe, cmdline and create_time properties are + no longer cached and correctly raise NoSuchProcess exception if the process + disappears. + +API CHANGES + + * psutil.Process.path property is deprecated and works as an alias for "exe" + property. + * psutil.Process.kill(): signal argument was removed - to send a signal to the + process use send_signal(signal) method instead. + * psutil.Process.get_memory_info() returns a nametuple instead of a tuple. + * psutil.cpu_times() returns a nametuple instead of a tuple. + * New psutil.Process methods: get_open_files(), get_connections(), + send_signal() and terminate(). + * ppid, uid, gid, name, exe, cmdline and create_time properties are no longer + cached and raise NoSuchProcess exception if process disappears. + * psutil.cpu_percent() no longer returns immediately (see issue 123). + * psutil.Process.get_cpu_percent() and psutil.cpu_percent() no longer returns + immediately by default (see issue 123). + + +0.1.3 - 2010-03-02 +------------------ + +NEW FEATURES + + * Issue 14: per-process username + * Issue 51: per-process current working directory (Windows and Linux only) + * Issue 59: Process.is_running() is now 10 times faster + * Issue 61: added supoprt for FreeBSD 64 bit + * Issue 71: implemented suspend/resume process + * Issue 75: python 3 support + +BUGFIXES + + * Issue 36: process cpu_times() and memory_info() functions succeeded also for + dead processes while a NoSuchProcess exception is supposed to be raised. + * Issue 48: incorrect size for mib array defined in getcmdargs for BSD + * Issue 49: possible memory leak due to missing free() on error condition on + * Issue 50: fixed getcmdargs() memory fragmentation on BSD + * Issue 55: test_pid_4 was failing on Windows Vista + * Issue 57: some unit tests were failing on systems where no swap memory is + available + * Issue 58: is_running() is now called before kill() to make sure we are going + to kill the correct process. + * Issue 73: virtual memory size reported on OS X includes shared library size + * Issue 77: NoSuchProcess wasn't raised on Process.create_time if kill() was + used first. + + +0.1.2 - 2009-05-06 +------------------ + +NEW FEATURES + + * Issue 32: Per-process CPU user/kernel times + * Issue 33: Process create time + * Issue 34: Per-process CPU utilization percentage + * Issue 38: Per-process memory usage (bytes) + * Issue 41: Per-process memory utilization (percent) + * Issue 39: System uptime + * Issue 43: Total system virtual memory + * Issue 46: Total system physical memory + * Issue 44: Total system used/free virtual and physical memory + +BUGFIXES + + * Issue 36: NoSuchProcess not raised on Windows when accessing timing methods + * Issue 40: test_get_cpu_times() failing on FreeBSD and OS X + * Issue 42: get_memory_percent() raises AccessDenied on Windows + + +0.1.1 - 2009-03-06 +------------------ + +NEW FEATURES + + * Issue 4: FreeBSD support for all functions of psutil + * Issue 9: Process.uid and Process.gid now retrieve process UID and GID. + * Issue 11: Support for parent/ppid - Process.parent property returns a + Process object representing the parent process, and Process.ppid returns + the parent PID. + * Issue 12 & 15: NoSuchProcess exception now raised when creating an object + for a nonexistent process, or when retrieving information about a process + that has gone away. + * Issue 21: AccessDenied exception created for raising access denied errors + from OSError or WindowsError on individual platforms. + * Issue 26: psutil.process_iter() function to iterate over processes as + Process objects with a generator. + * Process objects can now also be compared with == operator for equality + (PID, name, command line are compared). + +BUGFIXES + + * Issue 16: Special case for Windows' "System Idle Process" (PID 0) which + otherwise would return an "invalid parameter" exception. + * Issue 17: get_process_list() ignores NoSuchProcess and AccessDenied + exceptions during building of the list. + * Issue 22: Process(0).kill() was failing on Windows with an unset exception + * Issue 23: Special case for pid_exists(0) + * Issue 24: Process(0).kill() now raises AccessDenied exception instead of + WindowsError. + * Issue 30: psutil.get_pid_list() was returning two instances of PID 0 on OS + X and FreeBSD platforms. + + +0.1.0 - 2009-01-27 +------------------ + + * Initial release. + diff --git a/third_party/psutil/LICENSE b/third_party/psutil/LICENSE new file mode 100644 index 0000000..e91b135 --- /dev/null +++ b/third_party/psutil/LICENSE @@ -0,0 +1,27 @@ +psutil is distributed under BSD license reproduced below. + +Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola' +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the psutil authors nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/third_party/psutil/MANIFEST.in b/third_party/psutil/MANIFEST.in new file mode 100644 index 0000000..1fc8328 --- /dev/null +++ b/third_party/psutil/MANIFEST.in @@ -0,0 +1,9 @@ +include CREDITS +include HISTORY +include LICENSE +include MANIFEST.in +include README +include setup.py +recursive-include docs *.html *.dia *.png +recursive-include psutil *.py *.c *.h +recursive-include test *.py diff --git a/third_party/psutil/PKG-INFO b/third_party/psutil/PKG-INFO new file mode 100644 index 0000000..1b6f134 --- /dev/null +++ b/third_party/psutil/PKG-INFO @@ -0,0 +1,40 @@ +Metadata-Version: 1.0 +Name: psutil +Version: 0.2.0 +Summary: A process utilities module for Python +Home-page: http://code.google.com/p/psutil/ +Author: Giampaolo Rodola, Dave Daeschler, Jay Loden +Author-email: psutil-dev@googlegroups.com +License: License :: OSI Approved :: BSD License +Description: + psutil is a module providing convenience functions for managing processes in a + portable way by using Python. +Keywords: psutil,ps,top,process,utility +Platform: Platform Independent +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows :: Windows NT/2000 +Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: POSIX :: BSD :: FreeBSD +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: C +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 2.4 +Classifier: Programming Language :: Python :: 2.5 +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.0 +Classifier: Programming Language :: Python :: 3.1 +Classifier: Programming Language :: Python :: 3.2 +Classifier: Topic :: System :: Monitoring +Classifier: Topic :: System :: Networking +Classifier: Topic :: System :: Benchmark +Classifier: Topic :: System :: Systems Administration +Classifier: Topic :: Utilities +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Classifier: Intended Audience :: Developers +Classifier: Intended Audience :: System Administrators +Classifier: License :: OSI Approved :: MIT License diff --git a/third_party/psutil/README b/third_party/psutil/README new file mode 100644 index 0000000..feffe98 --- /dev/null +++ b/third_party/psutil/README @@ -0,0 +1,118 @@ + +Introduction +============ + +psutil is a module providing an interface for retrieving information on running +processes in a portable way by using Python. +It currently supports Linux, OS X, FreeBSD and Windows. + +psutil website is at http://code.google.com/p/psutil/ + +The following document describes how to compile and install psutil from sources +on different platforms. + + +Using easy_install +================== + +The easiest way to install psutil from sources is using easy_install. +Get the latest easy_install version from http://pypi.python.org/pypi/setuptools +and just run: + + > python easy_install psutil + +This should get the most updated psutil version from the Python pypi repository, +unpack it, compile it and install it automatically. + + +Installing on Windows using mingw32 +=================================== + +After the mingw [1] environment is properly set up on your system you can +compile Windows sources by entering: + + > setup.py build -c mingw32 + +To compile and install just append the "install" keyword at the end of the +command line above, like this: + + > setup.py build -c mingw32 install + +It might be possible that distutils will complain about missing gcc executable. +That means you have to add mingw bin PATH variable first. +Entering this line in the command prompt should do the work: + + > SET PATH=C:\MinGW\bin;%PATH% + +NOTE: this assumes MinGW is installed in C:\MinGW, if not simply replace the +path in the command above with an appropriate location. + +[1] http://www.mingw.org/ + + +Installing on Windows using Visual Studio +========================================= + +To use Visual Studio to install psutil, you must have the same version of +Visual Studio used to compile your installation of Python. For older versions +of Python that will be Visual Studio 2003. For 2.6 and later it should be +Visual Studio 2008. If you do not have the requisite version of Visual Studio +available then it is recommended to use MinGW to compile psutil instead. + +If you do have Visual Studio installed, you can use the basic distutils +commands: + + > setup.py build + +or to install and build: + + > setup.py install + +distutils should take care of any necessary magic to compile from there. + + +Installing on OS X +================== + +OS X installation from source will require gcc which you can obtain as part of +the 'XcodeTools' installer from Apple. Then you can run the standard distutils +commands: + +to build only: + + > ./setup.py build + +to install and build: + + > ./setup.py install + +NOTE: due to developer's hardware limitations psutil has only been compiled and +tested on OS X 10.4.11 so may or may not work on other versions. + + +Installing on FreeBSD +===================== + +The same compiler used to install Python must be present on the system in order +to build modules using distutils. Assuming it is installed, you can build using +the standard distutils commands: + +build only: + + > ./setup.py build + +install and build: + + > ./setup.py install + + +Installing on Linux +=================== + +Standard distutils installation steps should apply here. At the current time +the Linux port of psutil does not require any C modules, so can be installed +without need for a compiler using disutils: + +install/build: + + > ./setup.py install diff --git a/third_party/psutil/README.chromium b/third_party/psutil/README.chromium new file mode 100644 index 0000000..083d5c3 --- /dev/null +++ b/third_party/psutil/README.chromium @@ -0,0 +1,16 @@ +Name: psutil +URL: http://code.google.com/p/psutil/ +Version: 0.2.0 +License: BSD +License File: LICENSE + +Description: +psutil is a module providing an interface for retrieving information +on running processes and system utilization (CPU, memory) in a portable +way by using Python, implementing many functionalities offered by +command line tools like ps, top, kill, lsof and netstat. + + +Local Modifications: +None + diff --git a/third_party/psutil/docs/class_diagram.dia b/third_party/psutil/docs/class_diagram.dia new file mode 100644 index 0000000..401ac72 --- /dev/null +++ b/third_party/psutil/docs/class_diagram.dia @@ -0,0 +1,1475 @@ +<?xml version="1.0" encoding="UTF-8"?> +<dia:diagram xmlns:dia="http://www.lysator.liu.se/~alla/dia/"> + <dia:diagramdata> + <dia:attribute name="background"> + <dia:color val="#ffffff"/> + </dia:attribute> + <dia:attribute name="pagebreak"> + <dia:color val="#000099"/> + </dia:attribute> + <dia:attribute name="paper"> + <dia:composite type="paper"> + <dia:attribute name="name"> + <dia:string>#A4#</dia:string> + </dia:attribute> + <dia:attribute name="tmargin"> + <dia:real val="2.8222000598907471"/> + </dia:attribute> + <dia:attribute name="bmargin"> + <dia:real val="2.8222000598907471"/> + </dia:attribute> + <dia:attribute name="lmargin"> + <dia:real val="2.8222000598907471"/> + </dia:attribute> + <dia:attribute name="rmargin"> + <dia:real val="2.8222000598907471"/> + </dia:attribute> + <dia:attribute name="is_portrait"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="scaling"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="fitto"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + </dia:attribute> + <dia:attribute name="grid"> + <dia:composite type="grid"> + <dia:attribute name="width_x"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="width_y"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="visible_x"> + <dia:int val="1"/> + </dia:attribute> + <dia:attribute name="visible_y"> + <dia:int val="1"/> + </dia:attribute> + <dia:composite type="color"/> + </dia:composite> + </dia:attribute> + <dia:attribute name="color"> + <dia:color val="#d8e5e5"/> + </dia:attribute> + <dia:attribute name="guides"> + <dia:composite type="guides"> + <dia:attribute name="hguides"/> + <dia:attribute name="vguides"/> + </dia:composite> + </dia:attribute> + </dia:diagramdata> + <dia:layer name="Background" visible="true" active="true"> + <dia:object type="UML - LargePackage" version="0" id="O0"> + <dia:attribute name="obj_pos"> + <dia:point val="4.25,0.01251"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="4.2,-1.03749;61.8,36.5375"/> + </dia:attribute> + <dia:attribute name="meta"> + <dia:composite type="dict"/> + </dia:attribute> + <dia:attribute name="elem_corner"> + <dia:point val="4.25,0.01251"/> + </dia:attribute> + <dia:attribute name="elem_width"> + <dia:real val="57.500000000000007"/> + </dia:attribute> + <dia:attribute name="elem_height"> + <dia:real val="36.474989999999977"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_colour"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="fill_colour"> + <dia:color val="#ffffff"/> + </dia:attribute> + <dia:attribute name="text_colour"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="name"> + <dia:string>#psutil#</dia:string> + </dia:attribute> + </dia:object> + <dia:object type="UML - Class" version="0" id="O1"> + <dia:attribute name="obj_pos"> + <dia:point val="27.7475,4.05"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="27.6975,4;35.1975,6.2"/> + </dia:attribute> + <dia:attribute name="elem_corner"> + <dia:point val="27.7475,4.05"/> + </dia:attribute> + <dia:attribute name="elem_width"> + <dia:real val="7.4000000000000004"/> + </dia:attribute> + <dia:attribute name="elem_height"> + <dia:real val="2.0999999999999996"/> + </dia:attribute> + <dia:attribute name="name"> + <dia:string>#psutil (module)#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>#main py module#</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_attributes"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_operations"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="visible_attributes"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="visible_operations"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="visible_comments"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_operations"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_after_char"> + <dia:int val="40"/> + </dia:attribute> + <dia:attribute name="comment_line_length"> + <dia:int val="17"/> + </dia:attribute> + <dia:attribute name="comment_tagging"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_color"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="fill_color"> + <dia:color val="#ffffff"/> + </dia:attribute> + <dia:attribute name="text_color"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="normal_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="abstract_font"> + <dia:font family="monospace" style="88" name="Courier-BoldOblique"/> + </dia:attribute> + <dia:attribute name="polymorphic_font"> + <dia:font family="monospace" style="8" name="Courier-Oblique"/> + </dia:attribute> + <dia:attribute name="classname_font"> + <dia:font family="sans" style="80" name="Helvetica-Bold"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font"> + <dia:font family="sans" style="88" name="Helvetica-BoldOblique"/> + </dia:attribute> + <dia:attribute name="comment_font"> + <dia:font family="sans" style="8" name="Helvetica-Oblique"/> + </dia:attribute> + <dia:attribute name="normal_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="polymorphic_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="abstract_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="comment_font_height"> + <dia:real val="0.69999999999999996"/> + </dia:attribute> + <dia:attribute name="attributes"/> + <dia:attribute name="operations"/> + <dia:attribute name="template"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="templates"/> + <dia:childnode parent="O0"/> + </dia:object> + <dia:object type="UML - Class" version="0" id="O2"> + <dia:attribute name="obj_pos"> + <dia:point val="12.7,9.3"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="12.65,9.25;19.41,19.65"/> + </dia:attribute> + <dia:attribute name="elem_corner"> + <dia:point val="12.7,9.3"/> + </dia:attribute> + <dia:attribute name="elem_width"> + <dia:real val="6.6600000000000001"/> + </dia:attribute> + <dia:attribute name="elem_height"> + <dia:real val="10.300000000000001"/> + </dia:attribute> + <dia:attribute name="name"> + <dia:string>#Process class#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>#higher-level public python wrapper#</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_attributes"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_operations"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="visible_attributes"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="visible_operations"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="visible_comments"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_operations"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_after_char"> + <dia:int val="40"/> + </dia:attribute> + <dia:attribute name="comment_line_length"> + <dia:int val="17"/> + </dia:attribute> + <dia:attribute name="comment_tagging"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_color"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="fill_color"> + <dia:color val="#ffffff"/> + </dia:attribute> + <dia:attribute name="text_color"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="normal_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="abstract_font"> + <dia:font family="monospace" style="88" name="Courier-BoldOblique"/> + </dia:attribute> + <dia:attribute name="polymorphic_font"> + <dia:font family="monospace" style="8" name="Courier-Oblique"/> + </dia:attribute> + <dia:attribute name="classname_font"> + <dia:font family="sans" style="80" name="Helvetica-Bold"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font"> + <dia:font family="sans" style="88" name="Helvetica-BoldOblique"/> + </dia:attribute> + <dia:attribute name="comment_font"> + <dia:font family="sans" style="8" name="Helvetica-Oblique"/> + </dia:attribute> + <dia:attribute name="normal_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="polymorphic_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="abstract_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="comment_font_height"> + <dia:real val="0.69999999999999996"/> + </dia:attribute> + <dia:attribute name="attributes"> + <dia:composite type="umlattribute"> + <dia:attribute name="name"> + <dia:string>#pid#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + <dia:composite type="umlattribute"> + <dia:attribute name="name"> + <dia:string>#name#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + <dia:composite type="umlattribute"> + <dia:attribute name="name"> + <dia:string>#cmdline#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + <dia:composite type="umlattribute"> + <dia:attribute name="name"> + <dia:string>#...#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + </dia:attribute> + <dia:attribute name="operations"> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#get_cpu_times#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#terminate#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#kill#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#...#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + </dia:attribute> + <dia:attribute name="template"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="templates"> + <dia:composite type="umlformalparameter"> + <dia:attribute name="name"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + </dia:composite> + </dia:attribute> + <dia:childnode parent="O0"/> + </dia:object> + <dia:object type="UML - Class" version="0" id="O3"> + <dia:attribute name="obj_pos"> + <dia:point val="24.85,16.25"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="24.8,16.2;36.4925,23.2"/> + </dia:attribute> + <dia:attribute name="elem_corner"> + <dia:point val="24.85,16.25"/> + </dia:attribute> + <dia:attribute name="elem_width"> + <dia:real val="11.592499999999999"/> + </dia:attribute> + <dia:attribute name="elem_height"> + <dia:real val="6.9000000000000004"/> + </dia:attribute> + <dia:attribute name="name"> + <dia:string>#_ps[platform].py module#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>#intermediate python wrapper module#</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_attributes"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_operations"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="visible_attributes"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="visible_operations"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="visible_comments"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_operations"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_after_char"> + <dia:int val="40"/> + </dia:attribute> + <dia:attribute name="comment_line_length"> + <dia:int val="17"/> + </dia:attribute> + <dia:attribute name="comment_tagging"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_color"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="fill_color"> + <dia:color val="#ffffff"/> + </dia:attribute> + <dia:attribute name="text_color"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="normal_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="abstract_font"> + <dia:font family="monospace" style="88" name="Courier-BoldOblique"/> + </dia:attribute> + <dia:attribute name="polymorphic_font"> + <dia:font family="monospace" style="8" name="Courier-Oblique"/> + </dia:attribute> + <dia:attribute name="classname_font"> + <dia:font family="sans" style="80" name="Helvetica-Bold"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font"> + <dia:font family="sans" style="88" name="Helvetica-BoldOblique"/> + </dia:attribute> + <dia:attribute name="comment_font"> + <dia:font family="sans" style="8" name="Helvetica-Oblique"/> + </dia:attribute> + <dia:attribute name="normal_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="polymorphic_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="abstract_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="comment_font_height"> + <dia:real val="0.69999999999999996"/> + </dia:attribute> + <dia:attribute name="attributes"/> + <dia:attribute name="operations"> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#process_exists#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#kill_process#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#get_pid_list#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#...#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + </dia:attribute> + <dia:attribute name="template"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="templates"/> + <dia:childnode parent="O0"/> + </dia:object> + <dia:object type="UML - Class" version="0" id="O4"> + <dia:attribute name="obj_pos"> + <dia:point val="22.835,26.9875"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="22.785,26.9375;38.4,32.9375"/> + </dia:attribute> + <dia:attribute name="elem_corner"> + <dia:point val="22.835,26.9875"/> + </dia:attribute> + <dia:attribute name="elem_width"> + <dia:real val="15.515000000000001"/> + </dia:attribute> + <dia:attribute name="elem_height"> + <dia:real val="5.9000000000000004"/> + </dia:attribute> + <dia:attribute name="name"> + <dia:string>#_psutil_[platform].c#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>#C extension#</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_attributes"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_operations"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="visible_attributes"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="visible_operations"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="visible_comments"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_operations"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_after_char"> + <dia:int val="40"/> + </dia:attribute> + <dia:attribute name="comment_line_length"> + <dia:int val="17"/> + </dia:attribute> + <dia:attribute name="comment_tagging"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.10000000000000001"/> + </dia:attribute> + <dia:attribute name="line_color"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="fill_color"> + <dia:color val="#ffffff"/> + </dia:attribute> + <dia:attribute name="text_color"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="normal_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="abstract_font"> + <dia:font family="monospace" style="88" name="Courier-BoldOblique"/> + </dia:attribute> + <dia:attribute name="polymorphic_font"> + <dia:font family="monospace" style="8" name="Courier-Oblique"/> + </dia:attribute> + <dia:attribute name="classname_font"> + <dia:font family="sans" style="80" name="Helvetica-Bold"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font"> + <dia:font family="sans" style="88" name="Helvetica-BoldOblique"/> + </dia:attribute> + <dia:attribute name="comment_font"> + <dia:font family="sans" style="8" name="Helvetica-Oblique"/> + </dia:attribute> + <dia:attribute name="normal_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="polymorphic_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="abstract_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="comment_font_height"> + <dia:real val="0.69999999999999996"/> + </dia:attribute> + <dia:attribute name="attributes"/> + <dia:attribute name="operations"> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#process_exists#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#bool#</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"> + <dia:composite type="umlparameter"> + <dia:attribute name="name"> + <dia:string>#pid#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#int#</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="kind"> + <dia:enum val="0"/> + </dia:attribute> + </dia:composite> + </dia:attribute> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#get_process_info#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#ProcessInfo#</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"> + <dia:composite type="umlparameter"> + <dia:attribute name="name"> + <dia:string>#pid#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#int#</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="kind"> + <dia:enum val="0"/> + </dia:attribute> + </dia:composite> + </dia:attribute> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#kill_process#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#int#</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"> + <dia:composite type="umlparameter"> + <dia:attribute name="name"> + <dia:string>#pid#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#int#</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="kind"> + <dia:enum val="0"/> + </dia:attribute> + </dia:composite> + </dia:attribute> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#get_pid_list#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>#int[]#</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + </dia:attribute> + <dia:attribute name="template"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="templates"/> + </dia:object> + <dia:object type="UML - Class" version="0" id="O5"> + <dia:attribute name="obj_pos"> + <dia:point val="38.7485,8.77375"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="38.6985,8.72375;49.761,14.6238"/> + </dia:attribute> + <dia:attribute name="elem_corner"> + <dia:point val="38.7485,8.77375"/> + </dia:attribute> + <dia:attribute name="elem_width"> + <dia:real val="10.9625"/> + </dia:attribute> + <dia:attribute name="elem_height"> + <dia:real val="5.8000000000000007"/> + </dia:attribute> + <dia:attribute name="name"> + <dia:string>#System-related objects#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_attributes"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="suppress_operations"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="visible_attributes"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="visible_operations"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="visible_comments"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_operations"> + <dia:boolean val="true"/> + </dia:attribute> + <dia:attribute name="wrap_after_char"> + <dia:int val="40"/> + </dia:attribute> + <dia:attribute name="comment_line_length"> + <dia:int val="17"/> + </dia:attribute> + <dia:attribute name="comment_tagging"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="line_width"> + <dia:real val="0.0010000000000000002"/> + </dia:attribute> + <dia:attribute name="line_color"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="fill_color"> + <dia:color val="#ffffff"/> + </dia:attribute> + <dia:attribute name="text_color"> + <dia:color val="#000000"/> + </dia:attribute> + <dia:attribute name="normal_font"> + <dia:font family="monospace" style="0" name="Courier"/> + </dia:attribute> + <dia:attribute name="abstract_font"> + <dia:font family="monospace" style="88" name="Courier-BoldOblique"/> + </dia:attribute> + <dia:attribute name="polymorphic_font"> + <dia:font family="monospace" style="8" name="Courier-Oblique"/> + </dia:attribute> + <dia:attribute name="classname_font"> + <dia:font family="sans" style="80" name="Helvetica-Bold"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font"> + <dia:font family="sans" style="88" name="Helvetica-BoldOblique"/> + </dia:attribute> + <dia:attribute name="comment_font"> + <dia:font family="sans" style="8" name="Helvetica-Oblique"/> + </dia:attribute> + <dia:attribute name="normal_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="polymorphic_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="abstract_font_height"> + <dia:real val="0.80000000000000004"/> + </dia:attribute> + <dia:attribute name="classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="abstract_classname_font_height"> + <dia:real val="1"/> + </dia:attribute> + <dia:attribute name="comment_font_height"> + <dia:real val="0.69999999999999996"/> + </dia:attribute> + <dia:attribute name="attributes"> + <dia:composite type="umlattribute"> + <dia:attribute name="name"> + <dia:string>#TOTAL_PHYMEM#</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="value"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + </dia:composite> + </dia:attribute> + <dia:attribute name="operations"> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#get_pid_list#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#cpu_times#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#pid_exists#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + <dia:composite type="umloperation"> + <dia:attribute name="name"> + <dia:string>#...#</dia:string> + </dia:attribute> + <dia:attribute name="stereotype"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="visibility"> + <dia:enum val="0"/> + </dia:attribute> + <dia:attribute name="comment"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="abstract"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="inheritance_type"> + <dia:enum val="2"/> + </dia:attribute> + <dia:attribute name="query"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="class_scope"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="parameters"/> + </dia:composite> + </dia:attribute> + <dia:attribute name="template"> + <dia:boolean val="false"/> + </dia:attribute> + <dia:attribute name="templates"> + <dia:composite type="umlformalparameter"> + <dia:attribute name="name"> + <dia:string>##</dia:string> + </dia:attribute> + <dia:attribute name="type"> + <dia:string>##</dia:string> + </dia:attribute> + </dia:composite> + </dia:attribute> + </dia:object> + <dia:object type="Standard - Line" version="0" id="O6"> + <dia:attribute name="obj_pos"> + <dia:point val="30.6462,23.15"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="30.2393,23.0993;30.9628,27.0993"/> + </dia:attribute> + <dia:attribute name="conn_endpoints"> + <dia:point val="30.6462,23.15"/> + <dia:point val="30.5925,26.9875"/> + </dia:attribute> + <dia:attribute name="numcp"> + <dia:int val="1"/> + </dia:attribute> + <dia:attribute name="end_arrow"> + <dia:enum val="22"/> + </dia:attribute> + <dia:attribute name="end_arrow_length"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:attribute name="end_arrow_width"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O3" connection="6"/> + <dia:connection handle="1" to="O4" connection="1"/> + </dia:connections> + </dia:object> + <dia:object type="Standard - Line" version="0" id="O7"> + <dia:attribute name="obj_pos"> + <dia:point val="19.36,14.9"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="19.2919,14.8319;24.9474,18.0694"/> + </dia:attribute> + <dia:attribute name="conn_endpoints"> + <dia:point val="19.36,14.9"/> + <dia:point val="24.85,18"/> + </dia:attribute> + <dia:attribute name="numcp"> + <dia:int val="1"/> + </dia:attribute> + <dia:attribute name="end_arrow"> + <dia:enum val="22"/> + </dia:attribute> + <dia:attribute name="end_arrow_length"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:attribute name="end_arrow_width"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O2" connection="13"/> + <dia:connection handle="1" to="O3" connection="3"/> + </dia:connections> + <dia:childnode parent="O0"/> + </dia:object> + <dia:object type="Standard - Line" version="0" id="O8"> + <dia:attribute name="obj_pos"> + <dia:point val="44.2298,14.5738"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="36.3402,14.5078;44.2957,18.0979"/> + </dia:attribute> + <dia:attribute name="conn_endpoints"> + <dia:point val="44.2298,14.5738"/> + <dia:point val="36.4425,18"/> + </dia:attribute> + <dia:attribute name="numcp"> + <dia:int val="1"/> + </dia:attribute> + <dia:attribute name="end_arrow"> + <dia:enum val="22"/> + </dia:attribute> + <dia:attribute name="end_arrow_length"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:attribute name="end_arrow_width"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O5" connection="6"/> + <dia:connection handle="1" to="O3" connection="4"/> + </dia:connections> + <dia:childnode parent="O0"/> + </dia:object> + <dia:object type="Standard - Line" version="0" id="O9"> + <dia:attribute name="obj_pos"> + <dia:point val="27.7475,5.1"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="15.9248,5.03606;27.8114,9.43732"/> + </dia:attribute> + <dia:attribute name="conn_endpoints"> + <dia:point val="27.7475,5.1"/> + <dia:point val="16.03,9.3"/> + </dia:attribute> + <dia:attribute name="numcp"> + <dia:int val="1"/> + </dia:attribute> + <dia:attribute name="end_arrow"> + <dia:enum val="22"/> + </dia:attribute> + <dia:attribute name="end_arrow_length"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:attribute name="end_arrow_width"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O1" connection="3"/> + <dia:connection handle="1" to="O2" connection="1"/> + </dia:connections> + <dia:childnode parent="O0"/> + </dia:object> + <dia:object type="Standard - Line" version="0" id="O10"> + <dia:attribute name="obj_pos"> + <dia:point val="35.1475,5.1"/> + </dia:attribute> + <dia:attribute name="obj_bb"> + <dia:rectangle val="35.0824,5.0349;44.3334,8.88859"/> + </dia:attribute> + <dia:attribute name="conn_endpoints"> + <dia:point val="35.1475,5.1"/> + <dia:point val="44.2298,8.77375"/> + </dia:attribute> + <dia:attribute name="numcp"> + <dia:int val="1"/> + </dia:attribute> + <dia:attribute name="end_arrow"> + <dia:enum val="22"/> + </dia:attribute> + <dia:attribute name="end_arrow_length"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:attribute name="end_arrow_width"> + <dia:real val="0.5"/> + </dia:attribute> + <dia:connections> + <dia:connection handle="0" to="O1" connection="4"/> + <dia:connection handle="1" to="O5" connection="1"/> + </dia:connections> + <dia:childnode parent="O0"/> + </dia:object> + </dia:layer> +</dia:diagram> diff --git a/third_party/psutil/docs/documentation.html b/third_party/psutil/docs/documentation.html new file mode 100644 index 0000000..990913e --- /dev/null +++ b/third_party/psutil/docs/documentation.html @@ -0,0 +1,77 @@ +<html> + <head> + <title>Documentation</title> + </head> + <body> + +<style> +. +</style> + + + <div id="wikicontent"> + <table width="100%" border="0" cellspacing="0" cellpadding="0"> + <tr> + + <td class="vt" id="wikimaincol" width="100%"> + + <div id="wikiheader" style="margin-bottom:1em"> + + </div> + <h1><a name="API_Reference"></a>API Reference<a href="#API_Reference" class="section_anchor"></a></h1><h2><a name="Exceptions"></a>Exceptions<a href="#Exceptions" class="section_anchor"></a></h2><p>psutil.<strong>NoSuchProcess</strong><font size="3"><strong><tt>(</tt></strong></font><i>pid, name=None, msg=None</i><font size="3"><strong><tt>)</tt></strong></font> <blockquote>Raised when no process with the given PID was found in the current process list or when a process no longer exists (zombie). +</blockquote></p><blockquote><i>Changed in 0.2.0: <tt>pid</tt>, <tt>name</tt> and <tt>msg</tt> arguments were added </i> +</blockquote><p>psutil.<strong>AccessDenied</a></strong><font size="3"><strong><tt>(</tt></strong></font><i>pid=None, name=None, msg=None</i><font size="3"><strong><tt>)</tt></strong></font> <blockquote>Raised when permission to perform an action is denied. +</blockquote></p><blockquote><i>Changed in 0.2.0: <tt>pid</tt>, <tt>name</tt> and <tt>msg</tt> arguments were added </i> +</blockquote><hr/><h2><a name="Classes"></a>Classes<a href="#Classes" class="section_anchor"></a></h2><p>psutil.<strong>Process</strong><font size="3"><strong><tt>(</tt></strong></font><i>pid</i><font size="3"><strong><tt>)</tt></strong></font> </p><blockquote>A class which represents an OS process. +</blockquote><ul><li><strong>pid</strong><br>The process pid. </li></ul><ul><li><strong>ppid</strong><br>The process parent pid. </li></ul><ul><li><strong>parent</strong><br>Return the parent process as a <tt>Process</tt> object. If no ppid is known then return <tt>None</tt>. </li></ul><ul><li><strong>name</strong><br>The process name. </li></ul><ul><li><strong>exe</strong><br>The process executable as an absolute path name. </li></ul><blockquote><i>New in 0.2.0</i> +</blockquote><ul><li><strong>cmdline</strong><br>The command line process has been called with. </li></ul><ul><li><strong>path</strong><br>The process path. </li></ul><blockquote><i>Deprecated in 0.2.0</i> +</blockquote><ul><li><strong>create_time</strong><br> The process creation time as a floating point number expressed in seconds since the epoch, in <a href="http://en.wikipedia.org/wiki/Coordinated_Universal_Time" rel="nofollow">UTC</a>. </li></ul><ul><li><strong>uid</strong><br>The real user id of the current process. Where uid doesn't make sense (<i>Windows</i>) always return <tt>-1</tt>. </li></ul><ul><li><strong>gid</strong><br>The real group id of the current process. Where gid doesn't make sense (<i>Windows</i>) always return <tt>-1</tt>. </li></ul><ul><li><strong>username</strong><br>The name of the user that owns the process. </li></ul><blockquote><i>Changed in 2.0: Windows implementation has been rewritten in C for performace and <a href="http://sourceforge.net/projects/pywin32/" rel="nofollow">pywin32</a> extension is no longer required.</i> +</blockquote><ul><li><strong>getcwd</strong><font size="3"><strong><tt>()</tt></strong></font><br>Return a string representing the process current working directory. </li></ul><ul><li><strong>get_num_threads</strong><font size="3"><strong><tt>()</tt></strong></font><br>Return the number of threads used by this process. </li></ul><blockquote><i>New in 0.2.0</i> +</blockquote><ul><li><strong>get_cpu_times</strong><font size="3"><strong><tt>()</tt></strong></font><br>Return a tuple whose values are process CPU user and system times which means the amount of time expressed in seconds that a process has spent in <a href="http://stackoverflow.com/questions/556405/what-do-real-user-and-sys-mean-in-the-output-of-time1" rel="nofollow">user/system mode</a>. </li></ul><ul><li><strong>cpu_percent</strong><font size="3"><strong><tt>(</tt></strong></font><i>interval=0.1</i><font size="3"><strong><tt>)</tt></strong></font><br>Return a float representing the process CPU utilization as a percentage. When interval is > 0.0 compares process times to system CPU times elapsed before and after the interval (blocking). When interval is 0.0 or None compares process times to system CPU times elapsed since last call, returning immediately. In this case is recommended for accuracy that this function be called with at least 0.1 seconds between calls. </li></ul><blockquote><i>Changed in 0.2.0: interval parameter was added</i> +</blockquote><ul><li><strong>get_memory_info</strong><font size="3"><strong><tt>()</tt></strong></font><br>Return a tuple representing <a href="http://en.wikipedia.org/wiki/Resident_Set_Size" rel="nofollow">RSS</a> (Resident Set Size) and VMS (Virtual Memory Size) in bytes.<br>On UNIX RSS and VMS are the same values shown by ps. On Windows RSS and VMS refer to "Mem Usage" and "VM Size" columns of taskmgr.exe. </li></ul><ul><li><strong>get_memory_percent</strong><font size="3"><strong><tt>()</tt></strong></font><br>Compare physical system memory to process resident memory and calculate process memory utilization as a percentage. </li></ul><ul><li><strong>get_children</strong><font size="3"><strong><tt>()</tt></strong></font><br>Return the children of this process as a list of <tt>Process</tt> objects. </li></ul><blockquote><i>New in 0.2.0</i> +</blockquote><ul><li><strong>get_open_files</strong><font size="3"><strong><tt>()</tt></strong></font><br>Return files opened by process as a list of <a href="http://docs.python.org/library/collections.html?highlight=namedtuple#collections.namedtuple" rel="nofollow">namedtuples</a> including file absolute path name and file descriptor. On <strong>OSX</strong> and <strong>FreeBSD</strong> this is done by parsing <a href="http://en.wikipedia.org/wiki/Lsof" rel="nofollow">lsof</a> command output. If lsof is not installed on the system <a href="http://docs.python.org/library/exceptions.html?highlight=notimplementederror#exceptions.NotImplementedError" rel="nofollow">NotImplementedError</a> exception is raised. Example: </li><pre class="prettyprint">>>> import psutil, os +>>> f = open('file.ext', 'w') +>>> psutil.Process(os.getpid()).get_open_files() +[openfile(path='/home/giampaolo/svn/psutil/file.ext', fd=3)]</pre></ul><blockquote><i>New in 0.2.0</i> +</blockquote><ul><li><strong>get_connections</strong><font size="3"><strong><tt>()</tt></strong></font><br>Return all TCP and UPD connections opened by process as a list of <a href="http://docs.python.org/library/collections.html?highlight=namedtuple#collections.namedtuple" rel="nofollow">namedtuples</a>. Every namedtuple provides 6 attributes:<br></br> </li><ul><li><strong>fd</strong>: the socket file descriptor. This can be passed to <a href="http://docs.python.org/library/socket.html#socket.fromfd" rel="nofollow">socket.fromfd</a> to obtain a usable socket object. This is only available on UNIX; on Windows <tt>-1</tt> is always returned.<br> </li><li><strong>family</strong>: the address family, either <a href="http://docs.python.org/library/socket.html#socket.AF_INET" rel="nofollow">AF_INET</a> or <a href="http://docs.python.org/library/socket.html#socket.AF_INET6" rel="nofollow">AF_INET6</a> <br> </li><li><strong>type</strong>: the address type, either <a href="http://docs.python.org/library/socket.html#socket.SOCK_STREAM" rel="nofollow">SOCK_STREAM</a> or <a href="http://docs.python.org/library/socket.html#socket.SOCK_DGRAM" rel="nofollow">SOCK_DGRAM</a> <br> </li><li><strong>local_address</strong>: the local address as a <tt>(ip, port)</tt> tuple. <br> </li><li><strong>remote_address</strong>: the remote address as a <tt>(ip, port)</tt> tuple. When the remote endpoint is not connected the tuple is empty. <br> </li><li><strong>status</strong>: a string representing the TCP connections status. String values are supposed to match Linux's <a href="http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h" rel="nofollow">tcp_states.h</a> header file across all platforms. For UDP sockets this is always going to be an empty string.<br> </li></ul></ul><blockquote>Example: +<pre class="prettyprint">>>> p = psutil.Process(1694) +>>> p.name +'firefox' +>>> p.get_connections() +[connection(fd=115, family=2, type=1, local_address=('10.0.0.1', 48776), remote_address=('93.186.135.91', 80), status='ESTABLISHED'), + connection(fd=117, family=2, type=1, local_address=('10.0.0.1', 43761), remote_address=('72.14.234.100', 80), status='CLOSING'), + connection(fd=119, family=2, type=1, local_address=('10.0.0.1', 60759), remote_address=('72.14.234.104', 80), status='ESTABLISHED'), + connection(fd=123, family=2, type=1, local_address=('10.0.0.1', 51314), remote_address=('72.14.234.83', 443), status='SYN_SENT')]</pre>On <strong>OSX</strong> and <strong>FreeBSD</strong> this is implemented by parsing <a href="http://en.wikipedia.org/wiki/Lsof" rel="nofollow">lsof</a> command output. If lsof is not installed on the system <a href="http://docs.python.org/library/exceptions.html?highlight=notimplementederror#exceptions.NotImplementedError" rel="nofollow">NotImplementedError</a> exception is raised. For third party processes (different than <tt>os.getpid()</tt>) results can differ depending on user privileges.<br> +</blockquote><blockquote><i>New in 0.2.0</i> +</blockquote><ul><li><strong>is_running</strong><font size="3"><strong><tt>()</tt></strong></font><br>Return whether the current process is running in the current process list. </li></ul><ul><li><strong>suspend</strong><font size="3"><strong><tt>()</tt></strong></font><br>Suspend process execution. </li></ul><ul><li><strong>resume</strong><font size="3"><strong><tt>()</tt></strong></font><br>Resume process execution. </li></ul><ul><li><strong>terminate</strong><font size="3"><strong><tt>()</tt></strong></font><br>Terminate the process with <tt>SIGTERM</tt> signal. On Windows this is an alias for <tt>kill()</tt>. </li></ul><blockquote><i>New in 0.2.0</i> +</blockquote><ul><li><strong>send_signal</strong><font size="3"><strong><tt>(</tt></strong></font><i>signal</i><font size="3"><strong><tt>)</tt></strong></font><br>Send a signal to process (see <a href="http://docs.python.org/library/signal.html" rel="nofollow">signal module</a> constants). On Windows only <tt>SIGTERM</tt> is valid and is treated as an alias for <tt>kill()</tt>. </li></ul><blockquote><i>New in 0.2.0</i> +</blockquote><ul><li><strong>kill</strong><font size="3"><strong><tt>()</tt></strong></font><br>Kill the current process by using <tt>SIGKILL</tt> signal. </li></ul><blockquote><i>Changed in 0.2.0: no longer accepts <tt>sig</tt> keyword argument - use <tt>send_signal()</tt> instead. </i> +</blockquote><hr/><h2><a name="Functions"></a>Functions<a href="#Functions" class="section_anchor"></a></h2><p>psutil.<strong>get_pid_list</strong><font size="3"><strong><tt>()</tt></strong></font><br> <blockquote>Return a list of current running PIDs. +</blockquote></p><p>psutil.<strong>pid_exists</strong><font size="3"><strong><tt>(</tt></strong></font><i>pid</i><font size="3"><strong><tt>)</tt></strong></font><br> <blockquote>Check whether the given PID exists in the current process list. +</blockquote></p><p>psutil.<strong>process_iter</strong><font size="3"><strong><tt>()</tt></strong></font><br> <blockquote>Return an iterator yielding a Process class instances for all running processes on the local machine. +</blockquote></p><hr/><h2><a name="System_related_objects"></a>System related objects<a href="#System_related_objects" class="section_anchor"></a></h2><p>psutil.<strong>cpu_percent</strong><font size="3"><strong><tt>(</tt></strong></font><i>interval=0.1</i><font size="3"><strong><tt>)</tt></strong></font><br> <blockquote>Return a float representing the current system-wide CPU utilization as a percentage. When interval is > 0.0 compares system CPU times elapsed before and after the interval (blocking). When interval is 0.0 or None compares system CPU times elapsed since last call or module import, returning immediately. In this case is recommended for accuracy that this function be called with at least 0.1 seconds between calls. +</blockquote></p><blockquote><i>Changed in 0.2.0: interval parameter was added</i> +</blockquote><p>psutil.<strong>cpu_times</strong><font size="3"><strong><tt>()</tt></strong></font><br> <blockquote>Return system CPU times as a <tt>CPUTimes</tt> class object. Every CPU time is accessible in form of a <tt>CPUTimes</tt> attribute and represents the time CPU has spent in the given mode.<br>The attributes availability varies depending on the platform. Here follows a list of all available attributes: +</blockquote></p><blockquote>- <strong>user</strong><br> +- <strong>system</strong><br> +- <strong>idle</strong><br> +- <strong>nice</strong> <i>(UNIX)</i><br> +- <strong>iowait</strong> <i>(Linux)</i><br> +- <strong>irq</strong> <i>(Linux, FreeBSD)</i><br> +- <strong>softirq</strong> <i>(Linux)</i><br> +</blockquote><p>psutil.<strong>TOTAL_PHYMEM</strong><br> psutil.<strong>avail_phymem</strong><font size="3"><strong><tt>()</tt></strong></font><br> psutil.<strong>used_phymem</strong><font size="3"><strong><tt>()</tt></strong></font><br> <blockquote>Return the amount of total, available and used physical memory on the system, in bytes. +</blockquote></p><p>psutil.<strong>cached_phymem</strong><font size="3"><strong><tt>()</tt></strong></font><br> <blockquote>Return the amount of cached memory on the system, in bytes. This reflects the "cached" column of free command line utility on Linux. +</blockquote></p><blockquote><i>New in 0.2.0</i><br><i>Availability: <strong>Linux</strong></i> +</blockquote><p>psutil.<strong>phymem_buffers</strong><font size="3"><strong><tt>()</tt></strong></font><br> <blockquote>Return the amount of physical memory buffers used by the kernel in bytes. This reflects the "buffers" column of free command line utility on Linux. +</blockquote></p><blockquote><i>New in 0.2.0</i><br><i>Availability: <strong>Linux</strong></i> +</blockquote><p>psutil.<strong>total_virtmem</strong><font size="3"><strong><tt>()</tt></strong></font><br> psutil.<strong>avail_virtmem</strong><font size="3"><strong><tt>()</tt></strong></font><br> psutil.<strong>used_virtmem</strong><font size="3"><strong><tt>()</tt></strong></font><br> <blockquote>Return the amount of total, available and used virtual memory on the system, in bytes.<br>On Linux they match the values returned by <tt>free</tt> command line utility. On OS X and FreeBSD they represent the same values as returned by <tt>sysctl vm.vmtotal</tt>. On Windows they are determined by reading the <tt>*PageFile</tt> values of <a href="http://msdn.microsoft.com/en-us/library/aa366770(VS.85).aspx" rel="nofollow">MEMORYSTATUSEX</a> structure. +</blockquote></p> + </td> + </tr> + </table> + </div> + + + </body> +</html> + diff --git a/third_party/psutil/docs/index.html b/third_party/psutil/docs/index.html new file mode 100644 index 0000000..8395837 --- /dev/null +++ b/third_party/psutil/docs/index.html @@ -0,0 +1,99 @@ + + + +<html> + <head> + <title>psutil</title> + </head> + <body> + + + + + <div id="wikicontent"> + <table width="100%" border="0" cellspacing="0" cellpadding="0"> + <tr> + + <td class="vt" id="wikimaincol" width="100%"> + + <div id="wikiheader" style="margin-bottom:1em"> + + + </div> + <h1><a name="Summary"></a>Summary<a href="#Summary" class="section_anchor"></a></h1><p>psutil is a module providing an interface for retrieving information on running processes and system utilization (CPU, memory) in a portable way by using Python, implementing many functionalities offered by command line tools like <strong>ps</strong>, <strong>top</strong>, <strong>kill</strong> and Windows <strong>task manager</strong>. </p><p>It currently supports <strong>Linux</strong>, <strong>OS X</strong>, <strong>FreeBSD</strong> and <strong>Windows</strong> with Python versions from <strong>2.4</strong> to <strong>3.1</strong> by using a unique code base. </p><h1><a name="Example_usage"></a>Example usage<a href="#Example_usage" class="section_anchor"></a></h1><h3><a name="process_management"></a>process management<a href="#process_management" class="section_anchor"></a></h3><pre class="prettyprint">>>> import psutil +>>> psutil.get_pid_list() +[1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224, +268, 1215, 1216, 1220, 1221, 1243, 1244, 1301, 1601, 2237, 2355, +2637, 2774, 3932, 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245, +4263, 4282, 4306, 4311, 4312, 4313, 4314, 4337, 4339, 4357, 4358, +4363, 4383, 4395, 4408, 4433, 4443, 4445, 4446, 5167, 5234, 5235, +5252, 5318, 5424, 5644, 6987, 7054, 7055, 7071] +>>> p = psutil.Process(7055) +>>> p.name +'python' +>>> p.path +'/usr/bin' +>>> p.cmdline +['/usr/bin/python', '-O', 'run.py'] +>>> p.uid +1000 +>>> p.gid +1000 +>>> p.username +'jake' +>>> p.create_time +1267551141.5019531 +>>> p.get_cpu_percent() +12.31243 +>>> p.get_memory_percent() +0.63423 +>>> rss, vms = p.get_memory_info() +>>> "Resident memory: %s KB" %(rss / 1024) +'Resident memory: 3768 KB' +>>> "Virtual memory: %s KB" %(vms / 1024) +'Virtual memory: 6176 KB' +>>> +>>> p.suspend() +>>> p.resume() +>>> psutil.test() +UID PID %CPU %MEM VSZ RSS START TIME COMMAND +0 0 0.0 0.0 0 0 00:12 00:00 [sched] +0 1 0.0 0.3 1740 600 00:12 00:04 /sbin/init +0 2 0.0 0.0 0 0 00:12 00:00 [kthreadd] +0 3 0.0 0.0 0 0 00:12 00:00 [migration/0] +... +0 13239 0.0 2.6 13604 1044 00:38 00:00 /usr/sbin/smbd -D +1000 23648 0.0 2.4 12512 2008 14:43 00:06 sshd: user@pts/2 +1000 23649 0.0 1.2 5944 3340 14:43 00:00 -bash +0 25926 0.0 1.1 5432 3072 17:55 00:00 -su +0 28655 0.0 1.0 4932 3204 21:58 00:00 python _psutil.py +>>></pre><h3><a name="System_monitoring_(CPU_and_memory)"></a>System monitoring (CPU and memory)<a href="#System_monitoring_(CPU_and_memory)" class="section_anchor"></a></h3><pre class="prettyprint">>>> import psutil, time +>>> print psutil.cpu_times() +softirq=50.87; iowait=39.63; system=1130.67; idle=164171.41; user=965.15; irq=7.08; nice=0.0 +>>> +>>> while 1: +... print round(psutil.cpu_percent(), 1) +... time.sleep(1) +... +5.4 +3.2 +7.3 +7.1 +2.5 +Traceback (most recent call last): + File "<stdin>", line 3, in <module> +KeyboardInterrupt +>>> +>>> psutil.TOTAL_PHYMEM +526458880 +>>> psutil.avail_phymem() +153530368 +>>> psutil.total_virtmem() +197365760 +>>> psutil.avail_virtmem() +194277376</pre><h1><a name="Mailing_lists"></a>Mailing lists<a href="#Mailing_lists" class="section_anchor"></a></h1><p><strong>Users</strong><br><a href="http://groups.google.com/group/psutil/topics" rel="nofollow">http://groups.google.com/group/psutil/topics</a> </p><p><strong>Developers</strong><br><a href="http://groups.google.com/group/psutil-dev/topics" rel="nofollow">http://groups.google.com/group/psutil-dev/topics</a> </p><p><strong>SVN commits and issue tracker changes</strong><br><a href="http://groups.google.com/group/psutil-commits/topics" rel="nofollow">http://groups.google.com/group/psutil-commits/topics</a> </p><h1><a name="Contribute"></a>Contribute<a href="#Contribute" class="section_anchor"></a></h1><p>If you want to help or just give us suggestions about the project and other related things, subscribe to the <a href="http://groups.google.com/group/psutil" rel="nofollow">discussion mailing list</a>. If you want to talk with project team members about psutil and other related things feel free to contact us at the following addresses: </p><p><table><tr><td style="border: 1px solid #aaa; padding: 5px;"> <strong>Name</strong> </td><td style="border: 1px solid #aaa; padding: 5px;"> <strong>Country</strong> </td><td style="border: 1px solid #aaa; padding: 5px;"> <strong>E-mail</strong> </td></tr> <tr><td style="border: 1px solid #aaa; padding: 5px;"> Jay Loden </td><td style="border: 1px solid #aaa; padding: 5px;"> New Jersey (USA) </td><td style="border: 1px solid #aaa; padding: 5px;"> jloden at gmail dot com </td></tr> <tr><td style="border: 1px solid #aaa; padding: 5px;"> Giampaolo Rodola' </td><td style="border: 1px solid #aaa; padding: 5px;"> Turin (Italy) </td><td style="border: 1px solid #aaa; padding: 5px;"> g.rodola at gmail dot com </td></tr> </table></p><p>Feedbacks and suggestions are greatly appreciated as well as new testers and coders willing to join the development.<br> For any bug report, patch proposal or feature request, add an entry into the <a href="http://code.google.com/p/psutil/issues/list" rel="nofollow">Issue Tracker</a>.<br> </p><p>Thank you. </p> + + + </body> +</html> + diff --git a/third_party/psutil/docs/milestones.lnk.html b/third_party/psutil/docs/milestones.lnk.html new file mode 100644 index 0000000..83904c8 --- /dev/null +++ b/third_party/psutil/docs/milestones.lnk.html @@ -0,0 +1,12 @@ +<html> +<head> + <meta http-equiv="refresh" content="2; http://code.google.com/p/psutil/wiki/Milestones"> + <title>Redirecting to Milestones...</title> +</head> + +<body> +<p>Redirecting to: <a href="http://code.google.com/p/psutil/wiki/Milestones">http://code.google.com/p/psutil/wiki/Milestones</a><br></p> +<p>You'll be redirected in 2 seconds...</p> + +</body> +</html> diff --git a/third_party/psutil/psutil/__init__.py b/third_party/psutil/psutil/__init__.py new file mode 100644 index 0000000..09b7955 --- /dev/null +++ b/third_party/psutil/psutil/__init__.py @@ -0,0 +1,543 @@ +#!/usr/bin/env python +# +# $Id: __init__.py 806 2010-11-12 23:09:35Z g.rodola $ +# + +"""psutil is a module providing convenience functions for managing +processes in a portable way by using Python. +""" + +__version__ = "0.2.0" +version_info = tuple([int(num) for num in __version__.split('.')]) + +__all__ = [ + # exceptions + "Error", "NoSuchProcess", "AccessDenied", + # constants + "NUM_CPUS", "TOTAL_PHYMEM", "version_info", "__version__", + # classes + "Process", + # functions + "test", "pid_exists", "get_pid_list", "process_iter", "get_process_list", + "avail_phymem", "used_phymem", "total_virtmem", "avail_virtmem", + "used_virtmem", "cpu_times", "cpu_percent", + ] + +import sys +import os +import time +import signal +import warnings +import errno +try: + import pwd +except ImportError: + pwd = None + +from psutil.error import Error, NoSuchProcess, AccessDenied + +# import the appropriate module for our platform only +if sys.platform.lower().startswith("linux"): + from psutil._pslinux import * + __all__.extend(["cached_phymem", "phymem_buffers"]) + +elif sys.platform.lower().startswith("win32"): + from psutil._psmswindows import * + +elif sys.platform.lower().startswith("darwin"): + from psutil._psosx import * + +elif sys.platform.lower().startswith("freebsd"): + from psutil._psbsd import * + +else: + raise NotImplementedError('platform %s is not supported' % sys.platform) + + +class CPUTimes: + """This class contains information about CPU times. + It is not used directly but it's returned as an instance by + psutil.cpu_times() function. + + Every CPU time is accessible in form of an attribute and represents + the time CPU has spent in the given mode. + + The attributes availability varies depending on the platform. + Here follows a list of all available attributes: + + - user + - system + - idle + - nice (UNIX) + - iowait (Linux) + - irq (Linux, FreeBSD) + - softirq (Linux) + """ + + def __init__(self, **kwargs): + self.__attrs = [] + for name in kwargs: + setattr(self, name, kwargs[name]) + self.__attrs.append(name) + + def __str__(self): + string = [] + for attr in self.__attrs: + value = getattr(self, attr) + string.append("%s=%s" %(attr, value)) + return '; '.join(string) + + def __iter__(self): + for attr in self.__attrs: + yield getattr(self, attr) + + +class Process(object): + """Represents an OS process.""" + + def __init__(self, pid): + """Create a new Process object, raises NoSuchProcess if the PID + does not exist, and ValueError if the parameter is not an + integer PID.""" + if not isinstance(pid, int): + raise ValueError("An integer is required") + if not pid_exists(pid): + raise NoSuchProcess(pid, None, "no process found with PID %s" % pid) + self._pid = pid + # platform-specific modules define an PlatformProcess + # implementation class + self._platform_impl = PlatformProcess(pid) + self._last_sys_cpu_times = None + self._last_proc_cpu_times = None + + def __str__(self): + try: + pid = self.pid + name = repr(self.name) + cmdline = self.cmdline and repr(' '.join(self.cmdline)) + except NoSuchProcess: + details = "(pid=%s (terminated))" % self.pid + except AccessDenied: + details = "(pid=%s)" % (self.pid) + else: + if cmdline: + details = "(pid=%s, name=%s, cmdline=%s)" % (pid, name, cmdline) + else: + details = "(pid=%s, name=%s)" % (pid, name) + return "%s.%s %s" % (self.__class__.__module__, + self.__class__.__name__, details) + + def __repr__(self): + return "<%s at %s>" % (self.__str__(), id(self)) + + def __eq__(self, other): + """Test for equality with another Process object based on pid + and creation time. + """ + h1 = (self.pid, self.create_time) + h2 = (other.pid, other.create_time) + return h1 == h2 + + @property + def pid(self): + """The process pid.""" + return self._pid + + @property + def ppid(self): + """The process parent pid.""" + return self._platform_impl.get_process_ppid() + + @property + def parent(self): + """Return the parent process as a Process object. If no ppid is + known then return None.""" + if self.ppid is not None: + return Process(self.ppid) + return None + + @property + def name(self): + """The process name.""" + name = self._platform_impl.get_process_name() + if os.name == 'posix': + # On UNIX the name gets truncated to the first 15 characters. + # If it matches the first part of the cmdline we return that + # one instead because it's usually more explicative. + # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon". + cmdline = self.cmdline + if cmdline: + extended_name = os.path.basename(cmdline[0]) + if extended_name.startswith(name): + name = extended_name + # XXX - perhaps needs refactoring + self._platform_impl._process_name = name + return name + + @property + def exe(self): + """The process executable as an absolute path name.""" + exe = self._platform_impl.get_process_exe() + # if we have the cmdline but not the exe, figure it out from argv[0] + if not exe: + cmdline = self.cmdline + if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'): + _exe = os.path.realpath(cmdline[0]) + if os.path.isfile(_exe) and os.access(_exe, os.X_OK): + return _exe + return exe + + @property + def path(self): + msg = "'path' property is deprecated; use 'os.path.dirname(exe)' instead" + warnings.warn(msg, DeprecationWarning) + return os.path.dirname(self.exe) + + @property + def cmdline(self): + """The command line process has been called with.""" + return self._platform_impl.get_process_cmdline() + + @property + def uid(self): + """The real user id of the current process.""" + return self._platform_impl.get_process_uid() + + @property + def gid(self): + """The real group id of the current process.""" + return self._platform_impl.get_process_gid() + + @property + def username(self): + """The name of the user that owns the process.""" + if os.name == 'posix': + if pwd is None: + # might happen on compiled-from-sources python + raise ImportError("requires pwd module shipped with standard python") + return pwd.getpwuid(self.uid).pw_name + else: + return self._platform_impl.get_process_username() + + @property + def create_time(self): + """The process creation time as a floating point number + expressed in seconds since the epoch, in UTC. + """ + return self._platform_impl.get_process_create_time() + + # available for Windows and Linux only + if hasattr(PlatformProcess, "get_process_cwd"): + def getcwd(self): + """Return a string representing the process current working + directory. + """ + return self._platform_impl.get_process_cwd() + + def get_num_threads(self): + """Return the number of threads used by this process.""" + return self._platform_impl.get_process_num_threads() + + def get_children(self): + """Return the children of this process as a list of Process + objects. + """ + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + retlist = [] + for proc in process_iter(): + try: + if proc.ppid == self.pid: + retlist.append(proc) + except NoSuchProcess: + pass + return retlist + + def get_cpu_percent(self, interval=0.1): + """Return a float representing the current process CPU + utilization as a percentage. + + When interval is > 0.0 compares process times to system CPU + times elapsed before and after the interval (blocking). + + When interval is 0.0 or None compares process times to system CPU + times elapsed since last call, returning immediately. + In this case is recommended for accuracy that this function be + called with at least 0.1 seconds between calls. + """ + blocking = interval is not None and interval > 0.0 + if blocking: + st1 = sum(cpu_times()) + pt1 = self._platform_impl.get_cpu_times() + time.sleep(interval) + st2 = sum(cpu_times()) + pt2 = self._platform_impl.get_cpu_times() + else: + st1 = self._last_sys_cpu_times + pt1 = self._last_proc_cpu_times + st2 = sum(cpu_times()) + pt2 = self._platform_impl.get_cpu_times() + if st1 is None or pt1 is None: + self._last_sys_cpu_times = st2 + self._last_proc_cpu_times = pt2 + return 0.0 + + delta_proc = (pt2.user - pt1.user) + (pt2.system - pt1.system) + delta_time = st2 - st1 + # reset values for next call in case of interval == None + self._last_sys_cpu_times = st2 + self._last_proc_cpu_times = pt2 + + try: + # the utilization split between all CPUs + overall_percent = (delta_proc / delta_time) * 100 + except ZeroDivisionError: + # interval was too low + return 0.0 + # the utilization of a single CPU + single_cpu_percent = overall_percent * NUM_CPUS + # ugly hack to avoid troubles with float precision issues + if single_cpu_percent > 100.0: + return 100.0 + return round(single_cpu_percent, 1) + + def get_cpu_times(self): + """Return a tuple whose values are process CPU user and system + time. These are the same first two values that os.times() + returns for the current process. + """ + return self._platform_impl.get_cpu_times() + + def get_memory_info(self): + """Return a tuple representing RSS (Resident Set Size) and VMS + (Virtual Memory Size) in bytes. + + On UNIX RSS and VMS are the same values shown by ps. + + On Windows RSS and VMS refer to "Mem Usage" and "VM Size" columns + of taskmgr.exe. + """ + return self._platform_impl.get_memory_info() + + def get_memory_percent(self): + """Compare physical system memory to process resident memory and + calculate process memory utilization as a percentage. + """ + rss = self._platform_impl.get_memory_info()[0] + try: + return (rss / float(TOTAL_PHYMEM)) * 100 + except ZeroDivisionError: + return 0.0 + + def get_open_files(self): + """Return files opened by process as a list of namedtuples + including absolute file name and file descriptor. + """ + return self._platform_impl.get_open_files() + + def get_connections(self): + """Return TCP and UPD connections opened by process as a list + of namedtuple/s. + For third party processes (!= os.getpid()) results can differ + depending on user privileges. + """ + return self._platform_impl.get_connections() + + def is_running(self): + """Return whether the current process is running in the current + process list. + """ + try: + newproc = Process(self.pid) + return self == newproc + except NoSuchProcess: + return False + + def send_signal(self, sig): + """Send a signal to process (see signal module constants). + On Windows only SIGTERM is valid and is treated as an alias + for kill(). + """ + # safety measure in case the current process has been killed in + # meantime and the kernel reused its PID + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + if os.name == 'posix': + try: + os.kill(self.pid, sig) + except OSError, err: + name = self._platform_impl._process_name + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, name) + if err.errno == errno.EPERM: + raise AccessDenied(self.pid, name) + raise + else: + if sig == signal.SIGTERM: + self._platform_impl.kill_process() + else: + raise ValueError("only SIGTERM is supported on Windows") + + def suspend(self): + """Suspend process execution.""" + # safety measure in case the current process has been killed in + # meantime and the kernel reused its PID + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + # windows + if hasattr(self._platform_impl, "suspend_process"): + self._platform_impl.suspend_process() + else: + # posix + self.send_signal(signal.SIGSTOP) + + def resume(self): + """Resume process execution.""" + # safety measure in case the current process has been killed in + # meantime and the kernel reused its PID + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + # windows + if hasattr(self._platform_impl, "resume_process"): + self._platform_impl.resume_process() + else: + # posix + self.send_signal(signal.SIGCONT) + + def terminate(self): + """Terminate the process with SIGTERM. + On Windows this is an alias for kill(). + """ + self.send_signal(signal.SIGTERM) + + def kill(self): + """Kill the current process.""" + # safety measure in case the current process has been killed in + # meantime and the kernel reused its PID + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + if os.name == 'posix': + self.send_signal(signal.SIGKILL) + else: + self._platform_impl.kill_process() + + +def process_iter(): + """Return an iterator yielding a Process class instances for all + running processes on the local machine. + """ + pids = get_pid_list() + # for each PID, create a proxyied Process object + # it will lazy init it's name and path later if required + for pid in pids: + try: + yield Process(pid) + except (NoSuchProcess, AccessDenied): + continue + +def get_process_list(): + """Return a list of Process class instances for all running + processes on the local machine. + """ + return list(process_iter()) + +def cpu_times(): + """Return system CPU times as a CPUTimes object.""" + values = get_system_cpu_times() + return CPUTimes(**values) + + +_last_cpu_times = cpu_times() + +def cpu_percent(interval=0.1): + """Return a float representing the current system-wide CPU + utilization as a percentage. + + When interval is > 0.0 compares system CPU times elapsed before + and after the interval (blocking). + + When interval is 0.0 or None compares system CPU times elapsed + since last call or module import, returning immediately. + In this case is recommended for accuracy that this function be + called with at least 0.1 seconds between calls. + """ + global _last_cpu_times + + blocking = interval is not None and interval > 0.0 + if blocking: + t1 = cpu_times() + time.sleep(interval) + else: + t1 = _last_cpu_times + + t1_all = sum(t1) + t1_busy = t1_all - t1.idle + + t2 = cpu_times() + t2_all = sum(t2) + t2_busy = t2_all - t2.idle + + _last_cpu_times = t1 + # this usually indicates a float precision issue + if t2_busy <= t1_busy: + return 0.0 + + busy_delta = t2_busy - t1_busy + all_delta = t2_all - t1_all + busy_perc = (busy_delta / all_delta) * 100 + return round(busy_perc, 1) + + +def test(): + """List info of all currently running processes emulating a + ps -aux output. + """ + import datetime + today_day = datetime.date.today() + + def get_process_info(pid): + proc = Process(pid) + user = proc.username + if os.name == 'nt' and '\\' in user: + user = user.split('\\')[1] + pid = proc.pid + cpu = round(proc.get_cpu_percent(interval=None), 1) + mem = round(proc.get_memory_percent(), 1) + rss, vsz = [x / 1024 for x in proc.get_memory_info()] + + # If process has been created today print H:M, else MonthDay + start = datetime.datetime.fromtimestamp(proc.create_time) + if start.date() == today_day: + start = start.strftime("%H:%M") + else: + start = start.strftime("%b%d") + + cputime = time.strftime("%M:%S", time.localtime(sum(proc.get_cpu_times()))) + cmd = ' '.join(proc.cmdline) + # where cmdline is not available UNIX shows process name between + # [] parentheses + if not cmd: + cmd = "[%s]" % proc.name + return "%-9s %-5s %-4s %4s %7s %7s %5s %8s %s" \ + % (user, pid, cpu, mem, vsz, rss, start, cputime, cmd) + + print "%-9s %-5s %-4s %4s %7s %7s %5s %7s %s" \ + % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "START", "TIME", "COMMAND") + pids = get_pid_list() + pids.sort() + for pid in pids: + try: + line = get_process_info(pid) + except (AccessDenied, NoSuchProcess): + pass + else: + print line + +if __name__ == "__main__": + test() + diff --git a/third_party/psutil/psutil/_psbsd.py b/third_party/psutil/psutil/_psbsd.py new file mode 100644 index 0000000..6c4624e --- /dev/null +++ b/third_party/psutil/psutil/_psbsd.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# +# $Id: _psbsd.py 806 2010-11-12 23:09:35Z g.rodola $ +# + +import errno +import os + +try: + from collections import namedtuple +except ImportError: + from psutil.compat import namedtuple # python < 2.6 + +import _psutil_bsd +import _psposix +from psutil.error import AccessDenied, NoSuchProcess + + +# --- constants + +NUM_CPUS = _psutil_bsd.get_num_cpus() +TOTAL_PHYMEM = _psutil_bsd.get_total_phymem() + +# --- public functions + +def avail_phymem(): + "Return the amount of physical memory available on the system, in bytes." + return _psutil_bsd.get_avail_phymem() + +def used_phymem(): + "Return the amount of physical memory currently in use on the system, in bytes." + return TOTAL_PHYMEM - _psutil_bsd.get_avail_phymem() + +def total_virtmem(): + "Return the amount of total virtual memory available on the system, in bytes." + return _psutil_bsd.get_total_virtmem() + +def avail_virtmem(): + "Return the amount of virtual memory currently in use on the system, in bytes." + return _psutil_bsd.get_avail_virtmem() + +def used_virtmem(): + """Return the amount of used memory currently in use on the system, in bytes.""" + return _psutil_bsd.get_total_virtmem() - _psutil_bsd.get_avail_virtmem() + +def get_system_cpu_times(): + """Return a dict representing the following CPU times: + user, nice, system, idle, interrupt.""" + values = _psutil_bsd.get_system_cpu_times() + return dict(user=values[0], nice=values[1], system=values[2], + idle=values[3], irq=values[4]) + +def get_pid_list(): + """Returns a list of PIDs currently running on the system.""" + return _psutil_bsd.get_pid_list() + +def pid_exists(pid): + """Check For the existence of a unix pid.""" + return _psposix.pid_exists(pid) + + +def wrap_exceptions(method): + """Call method(self, pid) into a try/except clause so that if an + OSError "No such process" exception is raised we assume the process + has died and raise psutil.NoSuchProcess instead. + """ + def wrapper(self, *args, **kwargs): + try: + return method(self, *args, **kwargs) + except OSError, err: + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) + raise + return wrapper + + +class BSDProcess(object): + """Wrapper class around underlying C implementation.""" + + _meminfo_ntuple = namedtuple('meminfo', 'rss vms') + _cputimes_ntuple = namedtuple('cputimes', 'user system') + __slots__ = ["pid", "_process_name"] + + def __init__(self, pid): + self.pid = pid + self._process_name = None + + @wrap_exceptions + def get_process_name(self): + """Return process name as a string of limited len (15).""" + return _psutil_bsd.get_process_name(self.pid) + + def get_process_exe(self): + # no such thing as "exe" on BSD; it will maybe be determined + # later from cmdline[0] + return "" + + @wrap_exceptions + def get_process_cmdline(self): + """Return process cmdline as a list of arguments.""" + return _psutil_bsd.get_process_cmdline(self.pid) + + @wrap_exceptions + def get_process_ppid(self): + """Return process parent pid.""" + return _psutil_bsd.get_process_ppid(self.pid) + + @wrap_exceptions + def get_process_uid(self): + """Return process real user id.""" + return _psutil_bsd.get_process_uid(self.pid) + + @wrap_exceptions + def get_process_gid(self): + """Return process real group id.""" + return _psutil_bsd.get_process_gid(self.pid) + + @wrap_exceptions + def get_cpu_times(self): + """return a tuple containing process user/kernel time.""" + user, system = _psutil_bsd.get_cpu_times(self.pid) + return self._cputimes_ntuple(user, system) + + @wrap_exceptions + def get_memory_info(self): + """Return a tuple with the process' RSS and VMS size.""" + rss, vms = _psutil_bsd.get_memory_info(self.pid) + return self._meminfo_ntuple(rss, vms) + + @wrap_exceptions + def get_process_create_time(self): + """Return the start time of the process as a number of seconds since + the epoch.""" + return _psutil_bsd.get_process_create_time(self.pid) + + @wrap_exceptions + def get_process_num_threads(self): + """Return the number of threads belonging to the process.""" + return _psutil_bsd.get_process_num_threads(self.pid) + + def get_open_files(self): + """Return files opened by process by parsing lsof output.""" + lsof = _psposix.LsofParser(self.pid, self._process_name) + return lsof.get_process_open_files() + + def get_connections(self): + """Return network connections opened by a process as a list of + namedtuples by parsing lsof output. + """ + lsof = _psposix.LsofParser(self.pid, self._process_name) + return lsof.get_process_connections() + + +PlatformProcess = BSDProcess + diff --git a/third_party/psutil/psutil/_pslinux.py b/third_party/psutil/psutil/_pslinux.py new file mode 100644 index 0000000..26a8abf --- /dev/null +++ b/third_party/psutil/psutil/_pslinux.py @@ -0,0 +1,471 @@ +#!/usr/bin/env python +# +# $Id: _pslinux.py 800 2010-11-12 21:51:25Z g.rodola $ +# + +__all__ = ["NUM_CPUS", "TOTAL_PHYMEM", + "PlatformProcess", + "avail_phymem", "used_phymem", "total_virtmem", "avail_virtmem", + "used_virtmem", "get_system_cpu_times", "pid_exists", "get_pid_list", + "phymem_buffers", "cached_phymem" + ] + + +import os +import errno +import socket +import struct +import sys +import base64 + +try: + from collections import namedtuple +except ImportError: + from psutil.compat import namedtuple # python < 2.6 + +from psutil import _psposix +from psutil.error import AccessDenied, NoSuchProcess + + +def _get_uptime(): + """Return system boot time (epoch in seconds)""" + f = open('/proc/stat', 'r') + for line in f: + if line.startswith('btime'): + f.close() + return float(line.strip().split()[1]) + +def _get_num_cpus(): + """Return the number of CPUs on the system""" + num = 0 + f = open('/proc/cpuinfo', 'r') + for line in f: + if line.startswith('processor'): + num += 1 + f.close() + return num + +def _get_total_phymem(): + """Return the total amount of physical memory, in bytes""" + f = open('/proc/meminfo', 'r') + for line in f: + if line.startswith('MemTotal:'): + f.close() + return int(line.split()[1]) * 1024 + + +# Number of clock ticks per second +_CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"]) +_UPTIME = _get_uptime() +NUM_CPUS = _get_num_cpus() +TOTAL_PHYMEM = _get_total_phymem() + +del _get_uptime, _get_num_cpus, _get_total_phymem + +# http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h +_TCP_STATES_TABLE = {"01" : "ESTABLISHED", + "02" : "SYN_SENT", + "03" : "SYN_RECV", + "04" : "FIN_WAIT1", + "05" : "FIN_WAIT2", + "06" : "TIME_WAIT", + "07" : "CLOSE", + "08" : "CLOSE_WAIT", + "09" : "LAST_ACK", + "0A" : "LISTEN", + "0B" : "CLOSING" + } + +def avail_phymem(): + """Return the amount of physical memory available, in bytes.""" + f = open('/proc/meminfo', 'r') + free = None + _flag = False + for line in f: + if line.startswith('MemFree:'): + free = int(line.split()[1]) * 1024 + break + f.close() + return free + +def used_phymem(): + """"Return the amount of physical memory used, in bytes.""" + return (TOTAL_PHYMEM - avail_phymem()) + +def total_virtmem(): + """"Return the total amount of virtual memory, in bytes.""" + f = open('/proc/meminfo', 'r') + for line in f: + if line.startswith('SwapTotal:'): + f.close() + return int(line.split()[1]) * 1024 + +def avail_virtmem(): + """Return the amount of virtual memory currently in use on the + system, in bytes. + """ + f = open('/proc/meminfo', 'r') + for line in f: + if line.startswith('SwapFree:'): + f.close() + return int(line.split()[1]) * 1024 + +def used_virtmem(): + """Return the amount of used memory currently in use on the system, + in bytes. + """ + return total_virtmem() - avail_virtmem() + +def cached_phymem(): + """Return the amount of cached memory on the system, in bytes. + This reflects the "cached" column of free command line utility. + """ + f = open('/proc/meminfo', 'r') + for line in f: + if line.startswith('Cached:'): + f.close() + return int(line.split()[1]) * 1024 + +def phymem_buffers(): + """Return the amount of physical memory buffers used by the + kernel in bytes. + This reflects the "buffers" column of free command line utility. + """ + f = open('/proc/meminfo', 'r') + for line in f: + if line.startswith('Buffers:'): + f.close() + return int(line.split()[1]) * 1024 + +def get_system_cpu_times(): + """Return a dict representing the following CPU times: + user, nice, system, idle, iowait, irq, softirq. + """ + f = open('/proc/stat', 'r') + values = f.readline().split() + f.close() + + values = values[1:8] + values = tuple([float(x) / _CLOCK_TICKS for x in values]) + + return dict(user=values[0], nice=values[1], system=values[2], idle=values[3], + iowait=values[4], irq=values[5], softirq=values[6]) + +def get_pid_list(): + """Returns a list of PIDs currently running on the system.""" + pids = [int(x) for x in os.listdir('/proc') if x.isdigit()] + # special case for 0 (kernel process) PID + pids.insert(0, 0) + return pids + +def pid_exists(pid): + """Check For the existence of a unix pid.""" + return _psposix.pid_exists(pid) + + +# --- decorators + +def wrap_exceptions(callable): + """Call callable into a try/except clause and translate ENOENT, + EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. + """ + def wrapper(self, *args, **kwargs): + try: + return callable(self, *args, **kwargs) + except (OSError, IOError), err: + if err.errno == errno.ENOENT: # no such file or directory + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) + raise + return wrapper + + +class LinuxProcess(object): + """Linux process implementation.""" + + _meminfo_ntuple = namedtuple('meminfo', 'rss vms') + _cputimes_ntuple = namedtuple('cputimes', 'user system') + _openfile_ntuple = namedtuple('openfile', 'path fd') + _connection_ntuple = namedtuple('connection', 'fd family type local_address ' + 'remote_address status') + __slots__ = ["pid", "_process_name"] + + def __init__(self, pid): + self.pid = pid + self._process_name = None + + @wrap_exceptions + def get_process_name(self): + if self.pid == 0: + return 'sched' # special case for kernel process + f = open("/proc/%s/stat" % self.pid) + try: + name = f.read().split(' ')[1].replace('(', '').replace(')', '') + finally: + f.close() + # XXX - gets changed later and probably needs refactoring + return name + + def get_process_exe(self): + if self.pid in (0, 2): + return "" # special case for kernel processes + try: + exe = os.readlink("/proc/%s/exe" % self.pid) + except (OSError, IOError), err: + if err.errno == errno.ENOENT: + # no such file error; might be raised also if the + # path actually exists for system processes with + # low pids (about 0-20) + if os.path.lexists("/proc/%s/exe" % self.pid): + return "" + else: + # ok, it is a process which has gone away + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) + raise + + # It seems symlinks can point to a deleted/invalid location + # (this usually happens with "pulseaudio" process). + # However, if we had permissions to execute readlink() it's + # likely that we'll be able to figure out exe from argv[0] + # later on. + if exe.endswith(" (deleted)") and not os.path.isfile(exe): + return "" + return exe + + @wrap_exceptions + def get_process_cmdline(self): + if self.pid == 0: + return [] # special case for kernel process + f = open("/proc/%s/cmdline" % self.pid) + try: + # return the args as a list + return [x for x in f.read().split('\x00') if x] + finally: + f.close() + + @wrap_exceptions + def get_cpu_times(self): + # special case for 0 (kernel process) PID + if self.pid == 0: + return self._cputimes_ntuple(0.0, 0.0) + f = open("/proc/%s/stat" % self.pid) + st = f.read().strip() + f.close() + # ignore the first two values ("pid (exe)") + st = st[st.find(')') + 2:] + values = st.split(' ') + utime = float(values[11]) / _CLOCK_TICKS + stime = float(values[12]) / _CLOCK_TICKS + return self._cputimes_ntuple(utime, stime) + + @wrap_exceptions + def get_process_create_time(self): + # special case for 0 (kernel processes) PID; return system uptime + if self.pid == 0: + return _UPTIME + f = open("/proc/%s/stat" % self.pid) + st = f.read().strip() + f.close() + # ignore the first two values ("pid (exe)") + st = st[st.find(')') + 2:] + values = st.split(' ') + # According to documentation, starttime is in field 21 and the + # unit is jiffies (clock ticks). + # We first divide it for clock ticks and then add uptime returning + # seconds since the epoch, in UTC. + starttime = (float(values[19]) / _CLOCK_TICKS) + _UPTIME + return starttime + + @wrap_exceptions + def get_memory_info(self): + # special case for 0 (kernel processes) PID + if self.pid == 0: + return self._meminfo_ntuple(0, 0) + f = open("/proc/%s/status" % self.pid) + virtual_size = 0 + resident_size = 0 + _flag = False + for line in f: + if (not _flag) and line.startswith("VmSize:"): + virtual_size = int(line.split()[1]) * 1024 + _flag = True + elif line.startswith("VmRSS"): + resident_size = int(line.split()[1]) * 1024 + break + f.close() + return self._meminfo_ntuple(resident_size, virtual_size) + + @wrap_exceptions + def get_process_cwd(self): + if self.pid == 0: + return '' + return os.readlink("/proc/%s/cwd" % self.pid) + + @wrap_exceptions + def get_process_num_threads(self): + if self.pid == 0: + return 0 + f = open("/proc/%s/status" % self.pid) + for line in f: + if line.startswith("Threads:"): + f.close() + return int(line.split()[1]) + + @wrap_exceptions + def get_open_files(self): + retlist = [] + files = os.listdir("/proc/%s/fd" % self.pid) + for fd in files: + file = "/proc/%s/fd/%s" % (self.pid, fd) + if os.path.islink(file): + file = os.readlink(file) + if file.startswith("socket:["): + continue + if file.startswith("pipe:["): + continue + if file == "[]": + continue + if os.path.isfile(file) and not file in retlist: + ntuple = self._openfile_ntuple(file, int(fd)) + retlist.append(ntuple) + return retlist + +# --- lsof implementation +# +# def get_open_files(self): +# lsof = _psposix.LsofParser(self.pid, self._process_name) +# return lsof.get_process_open_files() + + @wrap_exceptions + def get_connections(self): + if self.pid == 0: + return [] + inodes = {} + # os.listdir() is gonna raise a lot of access denied + # exceptions in case of unprivileged user; that's fine: + # lsof does the same so it's unlikely that we can to better. + for fd in os.listdir("/proc/%s/fd" % self.pid): + try: + inode = os.readlink("/proc/%s/fd/%s" % (self.pid, fd)) + except OSError: + continue + if inode.startswith('socket:['): + # the process is using a socket + inode = inode[8:][:-1] + inodes[inode] = fd + + if not inodes: + # no connections for this process + return [] + + def process(file, family, _type): + retlist = [] + f = open(file) + f.readline() # skip the first line + for line in f: + _, laddr, raddr, status, _, _, _, _, _, inode = line.split()[:10] + if inode in inodes: + laddr = self._decode_address(laddr, family) + raddr = self._decode_address(raddr, family) + if _type == socket.SOCK_STREAM: + status = _TCP_STATES_TABLE[status] + else: + status = "" + fd = int(inodes[inode]) + conn = self._connection_ntuple(fd, family, _type, laddr, + raddr, status) + retlist.append(conn) + f.close() + return retlist + + tcp4 = process("/proc/net/tcp", socket.AF_INET, socket.SOCK_STREAM) + tcp6 = process("/proc/net/tcp6", socket.AF_INET6, socket.SOCK_STREAM) + udp4 = process("/proc/net/udp", socket.AF_INET, socket.SOCK_DGRAM) + udp6 = process("/proc/net/udp6", socket.AF_INET6, socket.SOCK_DGRAM) + return tcp4 + tcp6 + udp4 + udp6 + +# --- lsof implementation +# +# def get_connections(self): +# lsof = _psposix.LsofParser(self.pid, self._process_name) +# return lsof.get_process_connections() + + @wrap_exceptions + def get_process_ppid(self): + if self.pid == 0: + return 0 + f = open("/proc/%s/status" % self.pid) + for line in f: + if line.startswith("PPid:"): + # PPid: nnnn + f.close() + return int(line.split()[1]) + + @wrap_exceptions + def get_process_uid(self): + if self.pid == 0: + return 0 + f = open("/proc/%s/status" % self.pid) + for line in f: + if line.startswith('Uid:'): + # Uid line provides 4 values which stand for real, + # effective, saved set, and file system UIDs. + # We want to provide real UID only. + f.close() + return int(line.split()[1]) + + @wrap_exceptions + def get_process_gid(self): + if self.pid == 0: + return 0 + f = open("/proc/%s/status" % self.pid) + for line in f: + if line.startswith('Gid:'): + # Uid line provides 4 values which stand for real, + # effective, saved set, and file system GIDs. + # We want to provide real GID only. + f.close() + return int(line.split()[1]) + + @staticmethod + def _decode_address(addr, family): + """Accept an "ip:port" address as displayed in /proc/net/* + and convert it into a human readable form, like: + + "0500000A:0016" -> ("10.0.0.5", 22) + "0000000000000000FFFF00000100007F:9E49" -> ("::ffff:127.0.0.1", 40521) + + The IPv4 address portion is a little-endian four-byte hexadecimal + number; that is, the least significant byte is listed first, + so we need to reverse the order of the bytes to convert it + to an IP address. + The port is represented as a two-byte hexadecimal number. + + Reference: + http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html + """ + ip, port = addr.split(':') + port = int(port, 16) + if sys.version_info >= (3,): + ip = ip.encode('ascii') + # this usually refers to a local socket in listen mode with + # no end-points connected + if not port: + return () + if family == socket.AF_INET: + ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1]) + else: # IPv6 + # old version - let's keep it, just in case... + #ip = ip.decode('hex') + #return socket.inet_ntop(socket.AF_INET6, + # ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4))) + ip = base64.b16decode(ip) + ip = socket.inet_ntop(socket.AF_INET6, + struct.pack('>4I', *struct.unpack('<4I', ip))) + return (ip, port) + +PlatformProcess = LinuxProcess + diff --git a/third_party/psutil/psutil/_psmswindows.py b/third_party/psutil/psutil/_psmswindows.py new file mode 100644 index 0000000..ec75649 --- /dev/null +++ b/third_party/psutil/psutil/_psmswindows.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +# +# $Id: _psmswindows.py 802 2010-11-12 22:15:29Z g.rodola $ +# + +import errno +import os +import subprocess +import socket +import re +import sys +import platform + +try: + from collections import namedtuple +except ImportError: + from psutil.compat import namedtuple # python < 2.6 + +import _psutil_mswindows +from psutil.error import AccessDenied, NoSuchProcess + + + +# --- module level constants (gets pushed up to psutil module) + +NUM_CPUS = _psutil_mswindows.get_num_cpus() +TOTAL_PHYMEM = _psutil_mswindows.get_total_phymem() +_UPTIME = _psutil_mswindows.get_system_uptime() +_WIN2000 = platform.win32_ver()[0] == '2000' + +ERROR_ACCESS_DENIED = 5 + +# --- public functions + +def avail_phymem(): + "Return the amount of physical memory available on the system, in bytes." + return _psutil_mswindows.get_avail_phymem() + +def used_phymem(): + "Return the amount of physical memory currently in use on the system, in bytes." + return TOTAL_PHYMEM - _psutil_mswindows.get_avail_phymem() + +def total_virtmem(): + "Return the amount of total virtual memory available on the system, in bytes." + return _psutil_mswindows.get_total_virtmem() + +def avail_virtmem(): + "Return the amount of virtual memory currently in use on the system, in bytes." + return _psutil_mswindows.get_avail_virtmem() + +def used_virtmem(): + """Return the amount of used memory currently in use on the system, in bytes.""" + return _psutil_mswindows.get_total_virtmem() - _psutil_mswindows.get_avail_virtmem() + +def get_system_cpu_times(): + """Return a dict representing the following CPU times: user, system, idle.""" + times = _psutil_mswindows.get_system_cpu_times() + return dict(user=times[0], system=times[1], idle=times[2]) + +def get_pid_list(): + """Returns a list of PIDs currently running on the system.""" + return _psutil_mswindows.get_pid_list() + +def pid_exists(pid): + return _psutil_mswindows.pid_exists(pid) + + +# --- decorator + +def wrap_exceptions(callable): + """Call callable into a try/except clause so that if a + WindowsError 5 AccessDenied exception is raised we translate it + into psutil.AccessDenied + """ + def wrapper(self, *args, **kwargs): + try: + return callable(self, *args, **kwargs) + except OSError, err: + if err.errno in (errno.EPERM, errno.EACCES, ERROR_ACCESS_DENIED): + raise AccessDenied(self.pid, self._process_name) + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, self._process_name) + raise + return wrapper + + +class WindowsProcess(object): + """Wrapper class around underlying C implementation.""" + + _meminfo_ntuple = namedtuple('meminfo', 'rss vms') + _cputimes_ntuple = namedtuple('cputimes', 'user system') + _openfile_ntuple = namedtuple('openfile', 'path fd') + _connection_ntuple = namedtuple('connection', 'fd family type local_address ' + 'remote_address status') + __slots__ = ["pid", "_process_name"] + + def __init__(self, pid): + self.pid = pid + self._process_name = None + + + @wrap_exceptions + def get_process_name(self): + """Return process name as a string of limited len (15).""" + return _psutil_mswindows.get_process_name(self.pid) + + def get_process_exe(self): + # no such thing as "exe" on BSD; it will maybe be determined + # later from cmdline[0] + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + return "" + + @wrap_exceptions + def get_process_cmdline(self): + """Return process cmdline as a list of arguments.""" + return _psutil_mswindows.get_process_cmdline(self.pid) + + @wrap_exceptions + def get_process_ppid(self): + """Return process parent pid.""" + return _psutil_mswindows.get_process_ppid(self.pid) + + def get_process_uid(self): + # no such thing as uid on Windows + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + return -1 + + def get_process_gid(self): + # no such thing as gid on Windows + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + return -1 + + @wrap_exceptions + def get_memory_info(self): + """Returns a tuple or RSS/VMS memory usage in bytes.""" + # special case for 0 (kernel processes) PID + if self.pid == 0: + return self._meminfo_ntuple(0, 0) + rss, vms = _psutil_mswindows.get_memory_info(self.pid) + return self._meminfo_ntuple(rss, vms) + + @wrap_exceptions + def kill_process(self): + """Terminates the process with the given PID.""" + return _psutil_mswindows.kill_process(self.pid) + + @wrap_exceptions + def get_process_username(self): + """Return the name of the user that owns the process""" + if self.pid in (0, 4) or self.pid == 8 and _WIN2000: + return 'NT AUTHORITY\\SYSTEM' + return _psutil_mswindows.get_process_username(self.pid); + + @wrap_exceptions + def get_process_create_time(self): + # special case for kernel process PIDs; return system uptime + if self.pid in (0, 4) or self.pid == 8 and _WIN2000: + return _UPTIME + return _psutil_mswindows.get_process_create_time(self.pid) + + @wrap_exceptions + def get_process_num_threads(self): + return _psutil_mswindows.get_process_num_threads(self.pid) + + @wrap_exceptions + def get_cpu_times(self): + user, system = _psutil_mswindows.get_process_cpu_times(self.pid) + return self._cputimes_ntuple(user, system) + + def suspend_process(self): + return _psutil_mswindows.suspend_process(self.pid) + + def resume_process(self): + return _psutil_mswindows.resume_process(self.pid) + + @wrap_exceptions + def get_process_cwd(self): + if self.pid in (0, 4) or self.pid == 8 and _WIN2000: + return '' + # return a normalized pathname since the native C function appends + # "\\" at the and of the path + path = _psutil_mswindows.get_process_cwd(self.pid) + return os.path.normpath(path) + + @wrap_exceptions + def get_open_files(self): + if self.pid in (0, 4) or self.pid == 8 and _WIN2000: + return [] + retlist = [] + # Filenames come in in native format like: + # "\Device\HarddiskVolume1\Windows\systemew\file.txt" + # Convert the first part in the corresponding drive letter + # (e.g. "C:\") by using Windows's QueryDosDevice() + raw_file_names = _psutil_mswindows.get_process_open_files(self.pid) + for file in raw_file_names: + if sys.version_info >= (3,): + file = file.decode('utf8') + if file.startswith('\\Device\\'): + rawdrive = '\\'.join(file.split('\\')[:3]) + driveletter = _psutil_mswindows._QueryDosDevice(rawdrive) + file = file.replace(rawdrive, driveletter) + if os.path.isfile(file) and file not in retlist: + ntuple = self._openfile_ntuple(file, -1) + retlist.append(ntuple) + return retlist + + @wrap_exceptions + def get_connections(self): + retlist = _psutil_mswindows.get_process_connections(self.pid) + return [self._connection_ntuple(*conn) for conn in retlist] + +PlatformProcess = WindowsProcess diff --git a/third_party/psutil/psutil/_psosx.py b/third_party/psutil/psutil/_psosx.py new file mode 100644 index 0000000..f8c867e --- /dev/null +++ b/third_party/psutil/psutil/_psosx.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# +# $Id: _psosx.py 794 2010-11-12 13:29:52Z g.rodola $ +# + +import errno +import os + +try: + from collections import namedtuple +except ImportError: + from psutil.compat import namedtuple # python < 2.6 + +import _psutil_osx +import _psposix +from psutil.error import AccessDenied, NoSuchProcess + +# --- constants + +NUM_CPUS = _psutil_osx.get_num_cpus() +TOTAL_PHYMEM = _psutil_osx.get_total_phymem() + +# --- functions + +def avail_phymem(): + "Return the amount of physical memory available on the system, in bytes." + return _psutil_osx.get_avail_phymem() + +def used_phymem(): + "Return the amount of physical memory currently in use on the system, in bytes." + return TOTAL_PHYMEM - _psutil_osx.get_avail_phymem() + +def total_virtmem(): + "Return the amount of total virtual memory available on the system, in bytes." + return _psutil_osx.get_total_virtmem() + +def avail_virtmem(): + "Return the amount of virtual memory currently in use on the system, in bytes." + return _psutil_osx.get_avail_virtmem() + +def used_virtmem(): + """Return the amount of used memory currently in use on the system, in bytes.""" + return _psutil_osx.get_total_virtmem() - _psutil_osx.get_avail_virtmem() + +def get_system_cpu_times(): + """Return a dict representing the following CPU times: + user, nice, system, idle.""" + values = _psutil_osx.get_system_cpu_times() + return dict(user=values[0], nice=values[1], system=values[2], idle=values[3]) + +def get_pid_list(): + """Returns a list of PIDs currently running on the system.""" + return _psutil_osx.get_pid_list() + +def pid_exists(pid): + """Check For the existence of a unix pid.""" + return _psposix.pid_exists(pid) + +# --- decorator + +def wrap_exceptions(callable): + """Call callable into a try/except clause so that if an + OSError EPERM exception is raised we translate it into + psutil.AccessDenied. + """ + def wrapper(self, *args, **kwargs): + try: + return callable(self, *args, **kwargs) + except OSError, err: + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) + raise + return wrapper + + +class OSXProcess(object): + """Wrapper class around underlying C implementation.""" + + _meminfo_ntuple = namedtuple('meminfo', 'rss vms') + _cputimes_ntuple = namedtuple('cputimes', 'user system') + __slots__ = ["pid", "_process_name"] + + def __init__(self, pid): + self.pid = pid + self._process_name = None + + @wrap_exceptions + def get_process_name(self): + """Return process name as a string of limited len (15).""" + return _psutil_osx.get_process_name(self.pid) + + def get_process_exe(self): + # no such thing as "exe" on OS X; it will maybe be determined + # later from cmdline[0] + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + return "" + + @wrap_exceptions + def get_process_cmdline(self): + """Return process cmdline as a list of arguments.""" + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + return _psutil_osx.get_process_cmdline(self.pid) + + @wrap_exceptions + def get_process_ppid(self): + """Return process parent pid.""" + return _psutil_osx.get_process_ppid(self.pid) + + @wrap_exceptions + def get_process_uid(self): + """Return process real user id.""" + return _psutil_osx.get_process_uid(self.pid) + + @wrap_exceptions + def get_process_gid(self): + """Return process real group id.""" + return _psutil_osx.get_process_gid(self.pid) + + @wrap_exceptions + def get_memory_info(self): + """Return a tuple with the process' RSS and VMS size.""" + rss, vms = _psutil_osx.get_memory_info(self.pid) + return self._meminfo_ntuple(rss, vms) + + @wrap_exceptions + def get_cpu_times(self): + user, system = _psutil_osx.get_cpu_times(self.pid) + return self._cputimes_ntuple(user, system) + + @wrap_exceptions + def get_process_create_time(self): + """Return the start time of the process as a number of seconds since + the epoch.""" + return _psutil_osx.get_process_create_time(self.pid) + + @wrap_exceptions + def get_process_num_threads(self): + """Return the number of threads belonging to the process.""" + return _psutil_osx.get_process_num_threads(self.pid) + + def get_open_files(self): + """Return files opened by process by parsing lsof output.""" + lsof = _psposix.LsofParser(self.pid, self._process_name) + return lsof.get_process_open_files() + + def get_connections(self): + """Return etwork connections opened by a process as a list of + namedtuples.""" + lsof = _psposix.LsofParser(self.pid, self._process_name) + return lsof.get_process_connections() + +PlatformProcess = OSXProcess + diff --git a/third_party/psutil/psutil/_psposix.py b/third_party/psutil/psutil/_psposix.py new file mode 100644 index 0000000..cd860a5 --- /dev/null +++ b/third_party/psutil/psutil/_psposix.py @@ -0,0 +1,240 @@ +#!/usr/bin/env python +# +# $Id: _psposix.py 800 2010-11-12 21:51:25Z g.rodola $ +# + +"""Routines common to all posix systems.""" + +import os +import errno +import subprocess +import psutil +import socket +import re +import sys +import warnings + +try: + from collections import namedtuple +except ImportError: + from psutil.compat import namedtuple # python < 2.6 + +from psutil.error import AccessDenied, NoSuchProcess + + +def pid_exists(pid): + """Check whether pid exists in the current process table.""" + if pid < 0: + return False + try: + os.kill(pid, 0) + except OSError, e: + return e.errno == errno.EPERM + else: + return True + + +class LsofParser: + """A wrapper for lsof command line utility. + Executes lsof in subprocess and parses its output. + """ + socket_table = {'TCP' : socket.SOCK_STREAM, + 'UDP' : socket.SOCK_DGRAM, + 'IPv4' : socket.AF_INET, + 'IPv6' : socket.AF_INET6} + _openfile_ntuple = namedtuple('openfile', 'path fd') + _connection_ntuple = namedtuple('connection', 'fd family type local_address ' + 'remote_address status') + + def __init__(self, pid, name): + self.pid = pid + self.process_name = name + + def get_process_open_files(self): + """Return files opened by process by parsing lsof output.""" + # Options: + # -i == network files only + # -a == ANDing of all options + # -p == process with given PID only + # -n == do not resolve IP addresses + # -P == do not resolve port numbers + # -w == suppresses warnings + # -F0nPt == (0) separate lines with "\x00" + # (n) file name + # (t) file type + # (f) file descriptr + cmd = "lsof -a -p %s -n -P -F0ftn" % self.pid + stdout = self.runcmd(cmd) + if not stdout: + return [] + files = [] + lines = stdout.split("\n") + del lines[0] # first line contains the PID + for line in lines: + if not line: + continue + line = line.strip("\x00") + fields = {} + for field in line.split("\x00"): + key, value = field[0], field[1:] + fields[key] = value + if not 't' in fields: + continue + _type = fields['t'] + fd = fields['f'] + name = fields['n'] + if 'REG' in _type and fd.isdigit(): + if not os.path.isfile(os.path.realpath(name)): + continue + ntuple = self._openfile_ntuple(name, int(fd)) + files.append(ntuple) + return files + + def get_process_connections(self): + """Return connections opened by a process by parsing lsof output.""" + # Options: + # -i == network files only + # -a == ANDing of all options + # -p == process with given PID only + # -n == do not resolve IP addresses + # -P == do not resolve port numbers + # -w == suppresses warnings + # -F0nPt == (0) separate lines with "\x00" + # (n) and show internet addresses only + # (P) protocol type (TCP, UPD, Unix) + # (t) socket family (IPv4, IPv6) + # (T) connection status + # (f) file descriptors + cmd = "lsof -p %s -i -a -F0nPtTf -n -P" % self.pid + stdout = self.runcmd(cmd) + if not stdout: + return [] + connections = [] + lines = stdout.split() + del lines[0] # first line contains the PID + for line in lines: + line = line.strip("\x00") + fields = {} + for field in line.split("\x00"): + if field.startswith('T'): + key, value = field.split('=') + else: + key, value = field[0], field[1:] + fields[key] = value + + # XXX - might trow execption; needs "continue on unsupported + # family or type" (e.g. unix sockets) + # we consider TCP and UDP sockets only + stype = fields['P'] + if stype not in self.socket_table: + continue + else: + _type = self.socket_table[fields['P']] + family = self.socket_table[fields['t']] + peers = fields['n'] + fd = int(fields['f']) + if _type == socket.SOCK_STREAM: + status = fields['TST'] + # OS X shows "CLOSED" instead of "CLOSE" so translate them + if status == "CLOSED": + status = "CLOSE" + else: + status = "" + if not '->' in peers: + local_addr = self._normaddress(peers, family) + remote_addr = () + # OS X processes e.g. SystemUIServer can return *:* for local + # address, so we return 0 and move on + if local_addr == 0: + continue + else: + local_addr, remote_addr = peers.split("->") + local_addr = self._normaddress(local_addr, family) + remote_addr = self._normaddress(remote_addr, family) + + conn = self._connection_ntuple(fd, family, _type, local_addr, + remote_addr, status) + connections.append(conn) + + return connections + + def runcmd(self, cmd): + """Expects an lsof-related command line, execute it in a + subprocess and return its output. + If something goes bad stderr is parsed and proper exceptions + raised as necessary. + """ + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + if sys.version_info >= (3,): + stdout, stderr = map(lambda x: x.decode(sys.stdout.encoding), + (stdout, stderr)) + if stderr: + utility = cmd.split(' ')[0] + if self._which(utility) is None: + msg = "this functionnality requires %s command line utility " \ + "to be installed on the system" % utility + raise NotImplementedError(msg) + elif "permission denied" in stderr.lower(): + # "permission denied" can be found also in case of zombie + # processes; + p = psutil.Process(self.pid) + if not p.is_running(): + raise NoSuchProcess(self.pid, self.process_name) + raise AccessDenied(self.pid, self.process_name) + elif "lsof: warning:" in stderr.lower(): + # usually appears when lsof is run for the first time and + # complains about missing cache file in user home + warnings.warn(stderr, RuntimeWarning) + else: + # this must be considered an application bug + raise RuntimeError(stderr) + if not stdout: + p = psutil.Process(self.pid) + if not p.is_running(): + raise NoSuchProcess(self.pid, self.process_name) + return "" + return stdout + + @staticmethod + def _which(program): + """Same as UNIX which command. Return None on command not found.""" + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + + @staticmethod + def _normaddress(addr, family): + """Normalize an IP address.""" + assert family in (socket.AF_INET, socket.AF_INET6), "unsupported family" + if family == socket.AF_INET: + ip, port = addr.split(':') + else: + if "]" in addr: + ip, port = re.findall('\[([^]]+)\]:([0-9]+)', addr)[0] + else: + ip, port = addr.split(':') + if ip == '*': + if family == socket.AF_INET: + ip = "0.0.0.0" + elif family == socket.AF_INET6: + ip = "::" + # OS X can have some procs e.g. SystemUIServer listening on *:* + else: + raise ValueError("invalid IP %s" %addr) + if port == "*": + return 0 + return (ip, int(port)) + + diff --git a/third_party/psutil/psutil/_psutil_bsd.c b/third_party/psutil/psutil/_psutil_bsd.c new file mode 100644 index 0000000..7586d9f --- /dev/null +++ b/third_party/psutil/psutil/_psutil_bsd.c @@ -0,0 +1,566 @@ +/* + * $Id: _psutil_bsd.c 780 2010-11-10 18:42:47Z jloden $ + * + * FreeBSD platform-specific module methods for _psutil_bsd + */ + +#include <Python.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/param.h> +#include <sys/user.h> +#include <sys/proc.h> +#include <sys/vmmeter.h> /* needed for vmtotal struct */ + +#include "_psutil_bsd.h" +#include "arch/bsd/process_info.h" + +/* + * define the psutil C module methods and initialize the module. + */ +static PyMethodDef +PsutilMethods[] = +{ + // --- per-process functions + + {"get_process_name", get_process_name, METH_VARARGS, + "Return process name"}, + {"get_process_cmdline", get_process_cmdline, METH_VARARGS, + "Return process cmdline as a list of cmdline arguments"}, + {"get_process_ppid", get_process_ppid, METH_VARARGS, + "Return process ppid as an integer"}, + {"get_process_uid", get_process_uid, METH_VARARGS, + "Return process real user id as an integer"}, + {"get_process_gid", get_process_gid, METH_VARARGS, + "Return process real group id as an integer"}, + {"get_cpu_times", get_cpu_times, METH_VARARGS, + "Return tuple of user/kern time for the given PID"}, + {"get_process_create_time", get_process_create_time, METH_VARARGS, + "Return a float indicating the process create time expressed in " + "seconds since the epoch"}, + {"get_memory_info", get_memory_info, METH_VARARGS, + "Return a tuple of RSS/VMS memory information"}, + {"get_process_num_threads", get_process_num_threads, METH_VARARGS, + "Return number of threads used by process"}, + + + // --- system-related functions + + {"get_pid_list", get_pid_list, METH_VARARGS, + "Returns a list of PIDs currently running on the system"}, + {"get_num_cpus", get_num_cpus, METH_VARARGS, + "Return number of CPUs on the system"}, + {"get_total_phymem", get_total_phymem, METH_VARARGS, + "Return the total amount of physical memory, in bytes"}, + {"get_avail_phymem", get_avail_phymem, METH_VARARGS, + "Return the amount of available physical memory, in bytes"}, + {"get_total_virtmem", get_total_virtmem, METH_VARARGS, + "Return the total amount of virtual memory, in bytes"}, + {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, + "Return the amount of available virtual memory, in bytes"}, + {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, + "Return system cpu times as a tuple (user, system, nice, idle, irc)"}, + + {NULL, NULL, 0, NULL} +}; + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +static struct module_state _state; +#endif + +#if PY_MAJOR_VERSION >= 3 + +static int +psutil_bsd_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +static int +psutil_bsd_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + +static struct PyModuleDef +moduledef = { + PyModuleDef_HEAD_INIT, + "psutil_bsd", + NULL, + sizeof(struct module_state), + PsutilMethods, + NULL, + psutil_bsd_traverse, + psutil_bsd_clear, + NULL +}; + +#define INITERROR return NULL + +PyObject * +PyInit__psutil_bsd(void) + +#else +#define INITERROR return + +void init_psutil_bsd(void) +#endif +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); +#else + PyObject *module = Py_InitModule("_psutil_bsd", PsutilMethods); +#endif + if (module == NULL) { + INITERROR; + } + struct module_state *st = GETSTATE(module); + + st->error = PyErr_NewException("_psutil_bsd.Error", NULL, NULL); + if (st->error == NULL) { + Py_DECREF(module); + INITERROR; + } +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} + + +/* + * Utility function which fills a kinfo_proc struct based on process pid + */ +static int +get_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) +{ + int mib[4]; + size_t size; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + + size = sizeof(struct kinfo_proc); + + if (sysctl((int*)mib, 4, proc, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + /* + * sysctl stores 0 in the size if we can't find the process information. + * Set errno to ESRCH which will be translated in NoSuchProcess later on. + */ + if (size == 0) { + errno = ESRCH; + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; +} + + +/* + * Return a Python list of all the PIDs running on the system. + */ +static PyObject* +get_pid_list(PyObject* self, PyObject* args) +{ + kinfo_proc *proclist = NULL; + kinfo_proc *orig_address = NULL; + size_t num_processes; + size_t idx; + PyObject* retlist = PyList_New(0); + PyObject* pid; + + if (get_proc_list(&proclist, &num_processes) != 0) { + Py_DECREF(retlist); + PyErr_SetString(PyExc_RuntimeError, "failed to retrieve process list."); + return NULL; + } + + if (num_processes > 0) { + orig_address = proclist; // save so we can free it after we're done + for (idx=0; idx < num_processes; idx++) { + pid = Py_BuildValue("i", proclist->ki_pid); + PyList_Append(retlist, pid); + Py_XDECREF(pid); + proclist++; + } + free(orig_address); + } + + return retlist; +} + + +/* + * Return process name from kinfo_proc as a Python string. + */ +static PyObject* +get_process_name(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("s", kp.ki_comm); +} + + +/* + * Return process cmdline as a Python list of cmdline arguments. + */ +static PyObject* +get_process_cmdline(PyObject* self, PyObject* args) +{ + long pid; + PyObject* arglist = NULL; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + // get the commandline, defined in arch/bsd/process_info.c + arglist = get_arg_list(pid); + + // get_arg_list() returns NULL only if getcmdargs failed with ESRCH + // (no process with that PID) + if (NULL == arglist) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return Py_BuildValue("N", arglist); +} + + +/* + * Return process parent pid from kinfo_proc as a Python integer. + */ +static PyObject* +get_process_ppid(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.ki_ppid); +} + + +/* + * Return process real uid from kinfo_proc as a Python integer. + */ +static PyObject* +get_process_uid(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.ki_ruid); +} + + +/* + * Return process real group id from ki_comm as a Python integer. + */ +static PyObject* +get_process_gid(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.ki_rgid); +} + + +/* + * Return number of threads used by process as a Python integer. + */ +static PyObject* +get_process_num_threads(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.ki_numthreads); +} + + + +// convert a timeval struct to a double +#define TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) + + +/* + * Return a Python tuple (user_time, kernel_time) + */ +static PyObject* +get_cpu_times(PyObject* self, PyObject* args) +{ + long pid; + double user_t, sys_t; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + // convert from microseconds to seconds + user_t = TV2DOUBLE(kp.ki_rusage.ru_utime); + sys_t = TV2DOUBLE(kp.ki_rusage.ru_stime); + return Py_BuildValue("(dd)", user_t, sys_t); +} + + +/* + * Return a Python integer indicating the number of CPUs on the system + */ +static PyObject* +get_num_cpus(PyObject* self, PyObject* args) +{ + int mib[2]; + int ncpu; + size_t len; + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + len = sizeof(ncpu); + + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + return Py_BuildValue("i", ncpu); +} + + +/* + * Return a Python float indicating the process create time expressed in + * seconds since the epoch. + */ +static PyObject* +get_process_create_time(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("d", TV2DOUBLE(kp.ki_start)); +} + + +/* + * Return the RSS and VMS as a Python tuple. + */ +static PyObject* +get_memory_info(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("(ll)", ptoa(kp.ki_rssize), (long)kp.ki_size); +} + + +/* + * Return a Python integer indicating the total amount of physical memory + * in bytes. + */ +static PyObject* +get_total_phymem(PyObject* self, PyObject* args) +{ + long total_phymem; + int mib[2]; + size_t len; + + mib[0] = CTL_HW; + mib[1] = HW_PHYSMEM; + len = sizeof(total_phymem); + + if (sysctl(mib, 2, &total_phymem, &len, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + return Py_BuildValue("l", total_phymem); +} + + +/* + * Return a Python long indicating the amount of available physical memory in + * bytes. + */ +static PyObject* +get_avail_phymem(PyObject* self, PyObject* args) +{ + unsigned long v_inactive_count = 0; + unsigned long v_cache_count = 0; + unsigned long v_free_count = 0; + long total_mem = 0; + long long avail_mem = 0; + size_t size = sizeof(unsigned long); + size_t psize = sizeof(total_mem); + int pagesize = getpagesize(); + + if (sysctlbyname("hw.physmem", &total_mem, &psize, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + if (sysctlbyname("vm.stats.vm.v_inactive_count", &v_inactive_count, + &size, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + if (sysctlbyname("vm.stats.vm.v_cache_count", + &v_cache_count, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + if (sysctlbyname("vm.stats.vm.v_free_count", + &v_free_count, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + avail_mem = (v_inactive_count + v_cache_count + v_free_count) * pagesize; + // used_mem = total_mem - avail_mem; + + return Py_BuildValue("L", avail_mem); +} + + +/* + * Return a Python long indicating the total amount of virtual memory + * in bytes. + */ +static PyObject* +get_total_virtmem(PyObject* self, PyObject* args) +{ + int mib[2]; + struct vmtotal vm; + size_t size; + long long total_vmem; + + mib[0] = CTL_VM; + mib[1] = VM_METER; + size = sizeof(vm); + sysctl(mib, 2, &vm, &size, NULL, 0); + + // vmtotal struct: + // http://fxr.watson.org/fxr/source/sys/vmmeter.h?v=FREEBSD54 + // note: value is returned in page, so we must multiply by size of a page + total_vmem = (long long)vm.t_vm * (long long)getpagesize(); + return Py_BuildValue("L", total_vmem); +} + + +/* + * Return a Python long indicating the avail amount of virtual memory + * in bytes. + */ +static PyObject* +get_avail_virtmem(PyObject* self, PyObject* args) +{ + int mib[2]; + struct vmtotal vm; + size_t size; + long long total_vmem; + long long avail_vmem; + + mib[0] = CTL_VM; + mib[1] = VM_METER; + size = sizeof(vm); + sysctl(mib, 2, &vm, &size, NULL, 0); + + // vmtotal struct: + // http://fxr.watson.org/fxr/source/sys/vmmeter.h?v=FREEBSD54 + // note: value is returned in page, so we must multiply by size of a page + total_vmem = (long long)vm.t_vm * (long long)getpagesize(); + avail_vmem = total_vmem - ((long long)vm.t_avm * (long long)getpagesize()); + return Py_BuildValue("L", avail_vmem); +} + + +/* + * Return a Python tuple representing user, kernel and idle CPU times + */ +static PyObject* +get_system_cpu_times(PyObject* self, PyObject* args) +{ + long cpu_time[CPUSTATES]; + size_t size; + + size = sizeof(cpu_time); + + if (sysctlbyname("kern.cp_time", &cpu_time, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + /* + #define CP_USER 0 + #define CP_NICE 1 + #define CP_SYS 2 + #define CP_INTR 3 + #define CP_IDLE 4 + #define CPUSTATES 5 + */ + //user, nice, system, idle, iowait, irqm, softirq + return Py_BuildValue("(ddddd)", + (double)cpu_time[CP_USER] / CLOCKS_PER_SEC, + (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC, + (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC, + (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC, + (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC + ); +} + diff --git a/third_party/psutil/psutil/_psutil_bsd.h b/third_party/psutil/psutil/_psutil_bsd.h new file mode 100644 index 0000000..80ccc1e --- /dev/null +++ b/third_party/psutil/psutil/_psutil_bsd.h @@ -0,0 +1,30 @@ +/* + * $Id: _psutil_bsd.h 777 2010-11-08 18:29:13Z g.rodola $ + * + * BSD platform-specific module methods for _psutil_bsd + */ + +#include <Python.h> + +// --- per-process functions + +static PyObject* get_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_process_name(PyObject* self, PyObject* args); +static PyObject* get_process_cmdline(PyObject* self, PyObject* args); +static PyObject* get_process_ppid(PyObject* self, PyObject* args); +static PyObject* get_process_uid(PyObject* self, PyObject* args); +static PyObject* get_process_gid(PyObject* self, PyObject* args); +static PyObject* get_process_create_time(PyObject* self, PyObject* args); +static PyObject* get_memory_info(PyObject* self, PyObject* args); +static PyObject* get_process_num_threads(PyObject* self, PyObject* args); + +// --- system-related functions + +static PyObject* get_pid_list(PyObject* self, PyObject* args); +static PyObject* get_num_cpus(PyObject* self, PyObject* args); +static PyObject* get_total_phymem(PyObject* self, PyObject* args); +static PyObject* get_avail_phymem(PyObject* self, PyObject* args); +static PyObject* get_total_virtmem(PyObject* self, PyObject* args); +static PyObject* get_avail_virtmem(PyObject* self, PyObject* args); +static PyObject* get_system_cpu_times(PyObject* self, PyObject* args); + diff --git a/third_party/psutil/psutil/_psutil_mswindows.c b/third_party/psutil/psutil/_psutil_mswindows.c new file mode 100644 index 0000000..94f9057 --- /dev/null +++ b/third_party/psutil/psutil/_psutil_mswindows.c @@ -0,0 +1,1655 @@ +/* + * $Id: _psutil_mswindows.c 796 2010-11-12 20:31:28Z g.rodola $ + * + * Windows platform-specific module methods for _psutil_mswindows + */ + +#include <Python.h> +#include <windows.h> +#include <Psapi.h> +#include <time.h> +#include <lm.h> +#include <tchar.h> +#include <tlhelp32.h> +#include <iphlpapi.h> + +#include "_psutil_mswindows.h" +#include "arch/mswindows/security.h" +#include "arch/mswindows/process_info.h" +#include "arch/mswindows/process_handles.h" + + +// ------------------------ Python init --------------------------- + +static PyMethodDef +PsutilMethods[] = +{ + // --- per-process functions + + {"get_process_name", get_process_name, METH_VARARGS, + "Return process name"}, + {"get_process_cmdline", get_process_cmdline, METH_VARARGS, + "Return process cmdline as a list of cmdline arguments"}, + {"get_process_ppid", get_process_ppid, METH_VARARGS, + "Return process ppid as an integer"}, + {"kill_process", kill_process, METH_VARARGS, + "Kill the process identified by the given PID"}, + {"get_process_cpu_times", get_process_cpu_times, METH_VARARGS, + "Return tuple of user/kern time for the given PID"}, + {"get_process_create_time", get_process_create_time, METH_VARARGS, + "Return a float indicating the process create time expressed in " + "seconds since the epoch"}, + {"get_memory_info", get_memory_info, METH_VARARGS, + "Return a tuple of RSS/VMS memory information"}, + {"get_process_cwd", get_process_cwd, METH_VARARGS, + "Return process current working directory"}, + {"suspend_process", suspend_process, METH_VARARGS, + "Suspend a process"}, + {"resume_process", resume_process, METH_VARARGS, + "Resume a process"}, + {"get_process_open_files", get_process_open_files, METH_VARARGS, + "Return files opened by process"}, + {"_QueryDosDevice", _QueryDosDevice, METH_VARARGS, + "QueryDosDevice binding"}, + {"get_process_username", get_process_username, METH_VARARGS, + "Return the username of a process"}, + {"get_process_connections", get_process_connections, METH_VARARGS, + "Return the network connections of a process"}, + {"get_process_num_threads", get_process_num_threads, METH_VARARGS, + "Return the network connections of a process"}, + + // --- system-related functions + + {"get_pid_list", get_pid_list, METH_VARARGS, + "Returns a list of PIDs currently running on the system"}, + {"pid_exists", pid_exists, METH_VARARGS, + "Determine if the process exists in the current process list."}, + {"get_num_cpus", get_num_cpus, METH_VARARGS, + "Returns the number of CPUs on the system"}, + {"get_system_uptime", get_system_uptime, METH_VARARGS, + "Return system uptime"}, + {"get_total_phymem", get_total_phymem, METH_VARARGS, + "Return the total amount of physical memory, in bytes"}, + {"get_total_virtmem", get_total_virtmem, METH_VARARGS, + "Return the total amount of virtual memory, in bytes"}, + {"get_avail_phymem", get_avail_phymem, METH_VARARGS, + "Return the amount of available physical memory, in bytes"}, + {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, + "Return the amount of available virtual memory, in bytes"}, + {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, + "Return system cpu times as a tuple (user, system, idle)"}, + + {NULL, NULL, 0, NULL} +}; + + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 + #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else + #define GETSTATE(m) (&_state) + static struct module_state _state; +#endif + +#if PY_MAJOR_VERSION >= 3 + + static int psutil_mswindows_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; + } + + static int psutil_mswindows_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; + } + + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "psutil_mswindows", + NULL, + sizeof(struct module_state), + PsutilMethods, + NULL, + psutil_mswindows_traverse, + psutil_mswindows_clear, + NULL + }; + +#define INITERROR return NULL + + PyObject* PyInit__psutil_mswindows(void) + +#else + #define INITERROR return + void init_psutil_mswindows(void) +#endif +{ + struct module_state *st = NULL; +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); +#else + PyObject *module = Py_InitModule("_psutil_mswindows", PsutilMethods); +#endif + + if (module == NULL) { + INITERROR; + } + + st = GETSTATE(module); + st->error = PyErr_NewException("_psutil_mswindow.Error", NULL, NULL); + if (st->error == NULL) { + Py_DECREF(module); + INITERROR; + } + + SetSeDebug(); + +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} + + +// ------------------------ Public functions --------------------------- + +/* + * Return a Python float representing the system uptime expressed in seconds + * since the epoch. + */ +static PyObject* +get_system_uptime(PyObject* self, PyObject* args) +{ + double uptime; + time_t pt; + FILETIME fileTime; + long long ll; + + GetSystemTimeAsFileTime(&fileTime); + + /* + HUGE thanks to: + http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry + + This function converts the FILETIME structure to the 32 bit + Unix time structure. + The time_t is a 32-bit value for the number of seconds since + January 1, 1970. A FILETIME is a 64-bit for the number of + 100-nanosecond periods since January 1, 1601. Convert by + subtracting the number of 100-nanosecond period betwee 01-01-1970 + and 01-01-1601, from time_t the divide by 1e+7 to get to the same + base granularity. + */ + ll = (((LONGLONG)(fileTime.dwHighDateTime)) << 32) + fileTime.dwLowDateTime; + pt = (time_t)((ll - 116444736000000000ull) / 10000000ull); + + // XXX - By using GetTickCount() time will wrap around to zero if the + // system is run continuously for 49.7 days. + uptime = GetTickCount() / 1000.00f; + return Py_BuildValue("d", (double)pt - uptime); +} + + +/* + * Return 1 if PID exists in the current process list, else 0. + */ +static PyObject* +pid_exists(PyObject* self, PyObject* args) +{ + long pid; + int status; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + status = pid_is_running(pid); + if (-1 == status) { + return NULL; // exception raised in pid_is_running() + } + return PyBool_FromLong(status); +} + + +/* + * Return a Python list of all the PIDs running on the system. + */ +static PyObject* +get_pid_list(PyObject* self, PyObject* args) +{ + DWORD *proclist = NULL; + DWORD numberOfReturnedPIDs; + DWORD i; + PyObject* pid = NULL; + PyObject* retlist = PyList_New(0); + + proclist = get_pids(&numberOfReturnedPIDs); + if (NULL == proclist) { + Py_DECREF(retlist); + return NULL; + } + + for (i = 0; i < numberOfReturnedPIDs; i++) { + pid = Py_BuildValue("I", proclist[i]); + PyList_Append(retlist, pid); + Py_XDECREF(pid); + } + + // free C array allocated for PIDs + free(proclist); + + return retlist; +} + + +/* + * Kill a process given its PID. + */ +static PyObject* +kill_process(PyObject* self, PyObject* args) +{ + HANDLE hProcess; + long pid; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if (pid == 0) + return AccessDenied(); + + hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + if (hProcess == NULL) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + // see http://code.google.com/p/psutil/issues/detail?id=24 + NoSuchProcess(); + } + else { + PyErr_SetFromWindowsErr(0); + } + return NULL; + } + + // kill the process + if (! TerminateProcess(hProcess, 0)) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hProcess); + return NULL; + } + + CloseHandle(hProcess); + Py_INCREF(Py_None); + return Py_None; +} + + + +/* + * Return a Python tuple (user_time, kernel_time) + */ +static PyObject* +get_process_cpu_times(PyObject* self, PyObject* args) +{ + long pid; + HANDLE hProcess; + FILETIME ftCreate, ftExit, ftKernel, ftUser; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + // special case for PID 0 + if (0 == pid){ + return Py_BuildValue("(dd)", 0.0, 0.0); + } + + hProcess = handle_from_pid(pid); + if (hProcess == NULL) { + return NULL; + } + + if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { + if (GetLastError() == ERROR_ACCESS_DENIED) { + // usually means the process has died so we throw a NoSuchProcess + // here + CloseHandle(hProcess); + return NoSuchProcess(); + } + PyErr_SetFromWindowsErr(0); + CloseHandle(hProcess); + return NULL; + } + + CloseHandle(hProcess); + + /* + user and kernel times are represented as a FILETIME structure wich contains + a 64-bit value representing the number of 100-nanosecond intervals since + January 1, 1601 (UTC). + http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx + + To convert it into a float representing the seconds that the process has + executed in user/kernel mode I borrowed the code below from Python's + Modules/posixmodule.c + */ + + return Py_BuildValue( + "(dd)", + (double)(ftUser.dwHighDateTime*429.4967296 + \ + ftUser.dwLowDateTime*1e-7), + (double)(ftKernel.dwHighDateTime*429.4967296 + \ + ftKernel.dwLowDateTime*1e-7) + ); +} + + +/* + * Return a Python float indicating the process create time expressed in + * seconds since the epoch. + */ +static PyObject* +get_process_create_time(PyObject* self, PyObject* args) +{ + long pid; + long long unix_time; + HANDLE hProcess; + FILETIME ftCreate, ftExit, ftKernel, ftUser; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + // special case for PIDs 0 and 4 + if ( (0 == pid) || (4 == pid) ){ + return Py_BuildValue("d", 0.0); + } + + hProcess = handle_from_pid(pid); + if (hProcess == NULL) { + return NULL; + } + + if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { + if (GetLastError() == ERROR_ACCESS_DENIED) { + // usually means the process has died so we throw a NoSuchProcess here + return NoSuchProcess(); + } + else { + PyErr_SetFromWindowsErr(0); + } + CloseHandle(hProcess); + return NULL; + } + + CloseHandle(hProcess); + + /* + Convert the FILETIME structure to a Unix time. + It's the best I could find by googling and borrowing code here and there. + The time returned has a precision of 1 second. + */ + unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32; + unix_time += ftCreate.dwLowDateTime - 116444736000000000LL; + unix_time /= 10000000; + return Py_BuildValue("d", (double)unix_time); +} + + +/* + * Return a Python integer indicating the number of CPUs on the system. + */ +static PyObject* +get_num_cpus(PyObject* self, PyObject* args) +{ + SYSTEM_INFO system_info; + system_info.dwNumberOfProcessors = 0; + + GetSystemInfo(&system_info); + if (system_info.dwNumberOfProcessors == 0){ + // GetSystemInfo failed for some reason; return 1 as default + return Py_BuildValue("I", 1); + } + return Py_BuildValue("I", system_info.dwNumberOfProcessors); +} + +/* + * Return process name as a Python string. + */ +static PyObject* +get_process_name(PyObject* self, PyObject* args) { + long pid; + int pid_return; + PyObject* name; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + + if (pid == 0) { + return Py_BuildValue("s", "System Idle Process"); + } + else if (pid == 4) { + return Py_BuildValue("s", "System"); + } + + pid_return = pid_is_running(pid); + if (pid_return == 0) { + return NoSuchProcess(); + } + if (pid_return == -1) { + return NULL; + } + + name = get_name(pid); + if (name == NULL) + return NULL; // exception set in get_name() + return name; +} + + +/* + * Return process parent pid as a Python integer. + */ +static PyObject* +get_process_ppid(PyObject* self, PyObject* args) { + long pid; + int pid_return; + PyObject* ppid; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if ((pid == 0) || (pid == 4)) + return Py_BuildValue("l", 0); + + pid_return = pid_is_running(pid); + if (pid_return == 0) { + return NoSuchProcess(); + } + if (pid_return == -1) { + return NULL; + } + + ppid = get_ppid(pid); + if (ppid == NULL) + return NULL; // exception set in get_ppid() + return ppid; +} + +/* + * Return process cmdline as a Python list of cmdline arguments. + */ +static PyObject* +get_process_cmdline(PyObject* self, PyObject* args) { + long pid; + int pid_return; + PyObject* arglist; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if ((pid == 0) || (pid == 4)) + return Py_BuildValue("[]"); + + pid_return = pid_is_running(pid); + if (pid_return == 0) { + return NoSuchProcess(); + } + if (pid_return == -1) { + return NULL; + } + + // May fail any of several ReadProcessMemory calls etc. and not indicate + // a real problem so we ignore any errors and just live without commandline + arglist = get_arg_list(pid); + if ( NULL == arglist ) { + // carry on anyway, clear any exceptions too + PyErr_Clear(); + return Py_BuildValue("[]"); + } + + return arglist; +} + + +/* + * Return the RSS and VMS as a Python tuple. + */ +static PyObject* +get_memory_info(PyObject* self, PyObject* args) +{ + HANDLE hProcess; + PROCESS_MEMORY_COUNTERS counters; + DWORD pid; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + hProcess = handle_from_pid(pid); + if (NULL == hProcess) { + return NULL; + } + + if (! GetProcessMemoryInfo(hProcess, &counters, sizeof(counters)) ) { + return PyErr_SetFromWindowsErr(0); + } + + return Py_BuildValue("(nn)", counters.WorkingSetSize, counters.PagefileUsage); +} + + +/* + * Return a Python integer indicating the total amount of physical memory + * in bytes. + */ +static PyObject* +get_total_phymem(PyObject* self, PyObject* args) +{ + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + + if (! GlobalMemoryStatusEx(&memInfo) ) { + return PyErr_SetFromWindowsErr(0); + } + + return Py_BuildValue("L", memInfo.ullTotalPhys); +} + + +/* + * Return a Python integer indicating the total amount of virtual memory + * in bytes. + */ +static PyObject* +get_total_virtmem(PyObject* self, PyObject* args) +{ + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + + if (! GlobalMemoryStatusEx(&memInfo) ) { + return PyErr_SetFromWindowsErr(0); + } + + return Py_BuildValue("L", memInfo.ullTotalPageFile); +} + + +/* + * Return a Python integer indicating the amount of available physical memory + * in bytes. + */ +static PyObject* +get_avail_phymem(PyObject* self, PyObject* args) +{ + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + if (! GlobalMemoryStatusEx(&memInfo) ) { + return PyErr_SetFromWindowsErr(0); + } + return Py_BuildValue("L", memInfo.ullAvailPhys); +} + + +/* + * Return a Python integer indicating the amount of available virtual memory + * in bytes. + */ +static PyObject* +get_avail_virtmem(PyObject* self, PyObject* args) +{ + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + + if (! GlobalMemoryStatusEx(&memInfo) ) { + return PyErr_SetFromWindowsErr(0); + } + return Py_BuildValue("L", memInfo.ullAvailPageFile); +} + + +#define LO_T ((float)1e-7) +#define HI_T (LO_T*4294967296.0) + +// structures and enums from winternl.h (not available under mingw) +typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { + LARGE_INTEGER IdleTime; + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER Reserved1[2]; + ULONG Reserved2; +} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; + + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemBasicInformation = 0, + SystemPerformanceInformation = 2, + SystemTimeOfDayInformation = 3, + SystemProcessInformation = 5, + SystemProcessorPerformanceInformation = 8, + SystemInterruptInformation = 23, + SystemExceptionInformation = 33, + SystemRegistryQuotaInformation = 37, + SystemLookasideInformation = 45 +} SYSTEM_INFORMATION_CLASS; + + +/* + * Return a Python tuple representing user, kernel and idle CPU times + */ +static PyObject* +get_system_cpu_times(PyObject* self, PyObject* args) +{ + typedef BOOL (_stdcall *GST_PROC) (LPFILETIME, LPFILETIME, LPFILETIME); + static GST_PROC GetSystemTimes; + static BOOL bFirstCall = TRUE; + float idle, kernel, user; + + // Improves performance calling GetProcAddress only the first time + if (bFirstCall) { + // retrieves GetSystemTimes address in Kernel32 + GetSystemTimes=(GST_PROC)GetProcAddress(GetModuleHandle + (TEXT("Kernel32.dll")), + "GetSystemTimes"); + bFirstCall = FALSE; + } + + + // Uses GetSystemTimes if supported (winXP sp1+) + if (NULL!=GetSystemTimes) { + // GetSystemTimes supported + + FILETIME idle_time; + FILETIME kernel_time; + FILETIME user_time; + + if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) { + return PyErr_SetFromWindowsErr(0); + } + + idle = (float)((HI_T * idle_time.dwHighDateTime) + \ + (LO_T * idle_time.dwLowDateTime)); + user = (float)((HI_T * user_time.dwHighDateTime) + \ + (LO_T * user_time.dwLowDateTime)); + kernel = (float)((HI_T * kernel_time.dwHighDateTime) + \ + (LO_T * kernel_time.dwLowDateTime)); + + // kernel time includes idle time on windows + // we return only busy kernel time subtracting idle time from kernel time + return Py_BuildValue("(fff)", user, + kernel - idle, + idle); + + } + + else { + // GetSystemTimes NOT supported, use NtQuerySystemInformation instead + + typedef DWORD (_stdcall *NTQSI_PROC) (int, PVOID, ULONG, PULONG); + NTQSI_PROC NtQuerySystemInformation; + HINSTANCE hNtDll; + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; + SYSTEM_INFO si; + UINT i; + + // dynamic linking is mandatory to use NtQuerySystemInformation + hNtDll = LoadLibrary(TEXT("ntdll.dll")); + if (hNtDll != NULL) { + // gets NtQuerySystemInformation address + NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress( + hNtDll, "NtQuerySystemInformation"); + + if (NtQuerySystemInformation != NULL) + { + // retrives number of processors + GetSystemInfo(&si); + + // allocates an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION + // structures, one per processor + sppi=(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ + malloc(si.dwNumberOfProcessors * \ + sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); + if (sppi != NULL) + { + // gets cpu time informations + if (0 == NtQuerySystemInformation( + SystemProcessorPerformanceInformation, + sppi, + si.dwNumberOfProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), + NULL)) + { + // computes system global times summing each processor value + idle = user = kernel = 0; + for (i=0; i<si.dwNumberOfProcessors; i++) { + idle += (float)((HI_T * sppi[i].IdleTime.HighPart) + \ + (LO_T * sppi[i].IdleTime.LowPart)); + user += (float)((HI_T * sppi[i].UserTime.HighPart) + \ + (LO_T * sppi[i].UserTime.LowPart)); + kernel += (float)((HI_T * sppi[i].KernelTime.HighPart) + \ + (LO_T * sppi[i].KernelTime.LowPart)); + } + + // kernel time includes idle time on windows + // we return only busy kernel time subtracting idle + // time from kernel time + return Py_BuildValue("(ddd)", user, + kernel - idle, + idle + ); + + } // END NtQuerySystemInformation + + } // END malloc SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION + + } // END GetProcAddress + + } // END LoadLibrary + + PyErr_SetFromWindowsErr(0); + if (sppi) { + free(sppi); + } + if (hNtDll) { + FreeLibrary(hNtDll); + } + return 0; + + } // END GetSystemTimes NOT supported +} + + +/* + * Sid to User convertion + */ +BOOL SidToUser(PSID pSid, LPTSTR szUser, DWORD dwUserLen, LPTSTR szDomain, DWORD +dwDomainLen, DWORD pid) +{ + SID_NAME_USE snuSIDNameUse; + DWORD dwULen; + DWORD dwDLen; + + dwULen = dwUserLen; + dwDLen = dwDomainLen; + + if ( IsValidSid( pSid ) ) { + // Get user and domain name based on SID + if ( LookupAccountSid( NULL, pSid, szUser, &dwULen, szDomain, &dwDLen, &snuSIDNameUse) ) { + // LocalSystem processes are incorrectly reported as owned + // by BUILTIN\Administrators We modify that behavior to + // conform to standard taskmanager only if the process is + // actually a System process + if (is_system_proc(pid) == 1) { + // default to *not* changing the data if we fail to + // check for local system privileges, so only look for + // definite confirmed system processes and ignore errors + if ( lstrcmpi(szDomain, TEXT("builtin")) == 0 && lstrcmpi(szUser, TEXT("administrators")) == 0) { + strncpy (szUser, "SYSTEM", dwUserLen); + strncpy (szDomain, "NT AUTHORITY", dwDomainLen); + } + } + + return TRUE; + } + } + + return FALSE; +} + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +/* + * Return process current working directory as a Python string. + */ + +static PyObject* +get_process_cwd(PyObject* self, PyObject* args) +{ + long pid; + HANDLE processHandle; + PVOID pebAddress; + PVOID rtlUserProcParamsAddress; + UNICODE_STRING currentDirectory; + WCHAR *currentDirectoryContent; + PyObject *returnPyObj = NULL; + PyObject *cwd_from_wchar = NULL; + PyObject *cwd = NULL; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + processHandle = handle_from_pid(pid); + if (processHandle == NULL) { + return NULL; + } + + pebAddress = GetPebAddress(processHandle); + + /* get the address of ProcessParameters */ +#ifdef _WIN64 + if (!ReadProcessMemory(processHandle, (PCHAR)pebAddress + 32, + &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) +#else + if (!ReadProcessMemory(processHandle, (PCHAR)pebAddress + 0x10, + &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) +#endif + { + CloseHandle(processHandle); + + if (GetLastError() == ERROR_PARTIAL_COPY) { + /* Usually means the process has gone in the meantime */ + return NoSuchProcess(); + } + else { + return PyErr_SetFromWindowsErr(0); + } + + } + + /* read the currentDirectory UNICODE_STRING structure. + 0x24 refers to "CurrentDirectoryPath" of RTL_USER_PROCESS_PARAMETERS + structure (http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/) + */ +#ifdef _WIN64 + if (!ReadProcessMemory(processHandle, (PCHAR)rtlUserProcParamsAddress + 56, + ¤tDirectory, sizeof(currentDirectory), NULL)) +#else + if (!ReadProcessMemory(processHandle, (PCHAR)rtlUserProcParamsAddress + 0x24, + ¤tDirectory, sizeof(currentDirectory), NULL)) +#endif + { + CloseHandle(processHandle); + if (GetLastError() == ERROR_PARTIAL_COPY) { + /* Usually means the process has gone in the meantime */ + return NoSuchProcess(); + } + else { + return PyErr_SetFromWindowsErr(0); + } + } + + /* allocate memory to hold the command line */ + currentDirectoryContent = (WCHAR *)malloc(currentDirectory.Length+1); + + /* read the command line */ + if (!ReadProcessMemory(processHandle, currentDirectory.Buffer, + currentDirectoryContent, currentDirectory.Length, NULL)) + { + CloseHandle(processHandle); + free(currentDirectoryContent); + + if (GetLastError() == ERROR_PARTIAL_COPY) { + /* Usually means the process has gone in the meantime */ + return NoSuchProcess(); + } + else { + return PyErr_SetFromWindowsErr(0); + } + } + + // null-terminate the string to prevent wcslen from returning incorrect length + // the length specifier is in characters, but commandLine.Length is in bytes + currentDirectoryContent[(currentDirectory.Length/sizeof(WCHAR))] = '\0'; + + // convert wchar array to a Python unicode string, and then to UTF8 + cwd_from_wchar = PyUnicode_FromWideChar(currentDirectoryContent, + wcslen(currentDirectoryContent)); + + #if PY_MAJOR_VERSION >= 3 + cwd = PyUnicode_FromObject(cwd_from_wchar); + #else + cwd = PyUnicode_AsUTF8String(cwd_from_wchar); + #endif + + // decrement the reference count on our temp unicode str to avoid mem leak + Py_XDECREF(cwd_from_wchar); + returnPyObj = Py_BuildValue("N", cwd); + + CloseHandle(processHandle); + free(currentDirectoryContent); + return returnPyObj; +} + + +/* + * Resume or suspends a process + */ +int +suspend_resume_process(DWORD pid, int suspend) +{ + // a huge thanks to http://www.codeproject.com/KB/threads/pausep.aspx + HANDLE hThreadSnap = NULL; + THREADENTRY32 te32 = {0}; + + hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hThreadSnap == INVALID_HANDLE_VALUE) { + PyErr_SetFromWindowsErr(0); + return FALSE; + } + + // Fill in the size of the structure before using it + te32.dwSize = sizeof(THREADENTRY32); + + if (! Thread32First(hThreadSnap, &te32)) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hThreadSnap); + return FALSE; + } + + // Walk the thread snapshot to find all threads of the process. + // If the thread belongs to the process, add its information + // to the display list. + do + { + if (te32.th32OwnerProcessID == pid) + { + HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, + te32.th32ThreadID); + if (hThread == NULL) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hThread); + CloseHandle(hThreadSnap); + return FALSE; + } + if (suspend == 1) + { + if (SuspendThread(hThread) == (DWORD)-1) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hThread); + CloseHandle(hThreadSnap); + return FALSE; + } + } + else + { + if (ResumeThread(hThread) == (DWORD)-1) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hThread); + CloseHandle(hThreadSnap); + return FALSE; + } + } + CloseHandle(hThread); + } + } while (Thread32Next(hThreadSnap, &te32)); + + return TRUE; +} + + +static PyObject* +suspend_process(PyObject* self, PyObject* args) +{ + long pid; + int suspend = 1; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + if (! suspend_resume_process(pid, suspend)) { + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject* +resume_process(PyObject* self, PyObject* args) +{ + long pid; + int suspend = 0; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + if (! suspend_resume_process(pid, suspend)) { + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject* +get_process_num_threads(PyObject* self, PyObject* args) +{ + long pid; + int pid_return; + long nthreads = 0; + HANDLE hThreadSnap = NULL; + THREADENTRY32 te32 = {0}; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if (pid == 0) { + // raise AD instead of returning 0 as procexp is able to + // retrieve useful information somehow + return AccessDenied(); + } + + pid_return = pid_is_running(pid); + if (pid_return == 0) { + return NoSuchProcess(); + } + if (pid_return == -1) { + return NULL; + } + + hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hThreadSnap == INVALID_HANDLE_VALUE) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + + // Fill in the size of the structure before using it + te32.dwSize = sizeof(THREADENTRY32); + + if (! Thread32First(hThreadSnap, &te32)) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hThreadSnap); + return NULL; + } + + // Walk the thread snapshot to find all threads of the process. + // If the thread belongs to the process, increase the counter. + do + { + if (te32.th32OwnerProcessID == pid) + { + HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, + FALSE, te32.th32ThreadID); + if (hThread == NULL) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + NoSuchProcess(); + } + else { + PyErr_SetFromWindowsErr(0); + } + CloseHandle(hThread); + CloseHandle(hThreadSnap); + return NULL; + } + nthreads += 1; + CloseHandle(hThread); + } + } while (Thread32Next(hThreadSnap, &te32)); + + // every process should be supposed to have at least one thread + if (nthreads == 0) { + return NoSuchProcess(); + } + + return Py_BuildValue("l", nthreads); +} + + +static PyObject* +get_process_open_files(PyObject* self, PyObject* args) +{ + long pid; + HANDLE processHandle; + DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; + PyObject* filesList; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + processHandle = handle_from_pid_waccess(pid, access); + if (processHandle == NULL) { + return NULL; + } + + filesList = get_open_files(pid, processHandle); + if (filesList == NULL) { + return PyErr_SetFromWindowsErr(0); + } + return filesList; +} + + +/* + Accept a filename's drive in native format like "\Device\HarddiskVolume1\" + and return the corresponding drive letter (e.g. "C:\\"). + If no match is found return an empty string. +*/ +static PyObject* +_QueryDosDevice(PyObject* self, PyObject* args) +{ + LPCTSTR lpDevicePath; + TCHAR d = TEXT('A'); + TCHAR szBuff[5]; + + if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) { + return NULL; + } + + while(d <= TEXT('Z')) + { + TCHAR szDeviceName[3] = {d,TEXT(':'),TEXT('\0')}; + TCHAR szTarget[512] = {0}; + if (QueryDosDevice(szDeviceName, szTarget, 511) != 0){ + //_tprintf (TEXT("%c:\\ => %s\n"), d, szTarget); + if(_tcscmp(lpDevicePath, szTarget) == 0) { + _stprintf(szBuff, TEXT("%c:"), d); + return Py_BuildValue("s", szBuff); + } + } + d++; + } + return Py_BuildValue("s", ""); +} + +/* + * Return process username as a "DOMAIN//USERNAME" string. + */ +static PyObject* +get_process_username(PyObject* self, PyObject* args) +{ + long pid; + HANDLE processHandle; + HANDLE tokenHandle; + PTOKEN_USER user; + ULONG bufferSize; + PTSTR name; + ULONG nameSize; + PTSTR domainName; + ULONG domainNameSize; + SID_NAME_USE nameUse; + PTSTR fullName; + PyObject* returnObject; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + processHandle = handle_from_pid_waccess(pid, PROCESS_QUERY_INFORMATION); + if (processHandle == NULL) { + return NULL; + } + + if (!OpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle)) { + CloseHandle(processHandle); + return PyErr_SetFromWindowsErr(0); + } + + CloseHandle(processHandle); + + /* Get the user SID. */ + + bufferSize = 0x100; + user = malloc(bufferSize); + + if (!GetTokenInformation(tokenHandle, + TokenUser, + user, + bufferSize, + &bufferSize)) + { + free(user); + user = malloc(bufferSize); + + if (!GetTokenInformation(tokenHandle, + TokenUser, + user, + bufferSize, + &bufferSize)) + { + free(user); + CloseHandle(tokenHandle); + return PyErr_SetFromWindowsErr(0); + } + } + + CloseHandle(tokenHandle); + + /* Resolve the SID to a name. */ + + nameSize = 0x100; + domainNameSize = 0x100; + + name = malloc(nameSize * sizeof(TCHAR)); + domainName = malloc(domainNameSize * sizeof(TCHAR)); + + if (!LookupAccountSid(NULL, user->User.Sid, name, &nameSize, domainName, + &domainNameSize, &nameUse)) + { + free(name); + free(domainName); + name = malloc(nameSize * sizeof(TCHAR)); + domainName = malloc(domainNameSize * sizeof(TCHAR)); + + if (!LookupAccountSid(NULL, user->User.Sid, name, &nameSize, domainName, + &domainNameSize, &nameUse)) + { + free(name); + free(domainName); + free(user); + + return PyErr_SetFromWindowsErr(0); + } + } + + nameSize = _tcslen(name); + domainNameSize = _tcslen(domainName); + + /* Build the full username string. */ + fullName = malloc((domainNameSize + 1 + nameSize + 1) * sizeof(TCHAR)); + memcpy(fullName, domainName, domainNameSize); + fullName[domainNameSize] = '\\'; + memcpy(&fullName[domainNameSize + 1], name, nameSize); + fullName[domainNameSize + 1 + nameSize] = '\0'; + + returnObject = Py_BuildValue("s", fullName); + + free(fullName); + free(name); + free(domainName); + free(user); + + return returnObject; +} + +#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff) + +#ifndef AF_INET6 +#define AF_INET6 23 +#endif + +static char *state_to_string(ULONG state) +{ + switch (state) + { + case MIB_TCP_STATE_CLOSED: + return "CLOSE"; + case MIB_TCP_STATE_LISTEN: + return "LISTEN"; + case MIB_TCP_STATE_SYN_SENT: + return "SYN_SENT"; + case MIB_TCP_STATE_SYN_RCVD: + return "SYN_RECV"; + case MIB_TCP_STATE_ESTAB: + return "ESTABLISHED"; + case MIB_TCP_STATE_FIN_WAIT1: + return "FIN_WAIT1"; + case MIB_TCP_STATE_FIN_WAIT2: + return "FIN_WAIT2"; + case MIB_TCP_STATE_CLOSE_WAIT: + return "CLOSE_WAIT"; + case MIB_TCP_STATE_CLOSING: + return "CLOSING"; + case MIB_TCP_STATE_LAST_ACK: + return "LAST_ACK"; + case MIB_TCP_STATE_TIME_WAIT: + return "TIME_WAIT"; + case MIB_TCP_STATE_DELETE_TCB: + return "DELETE_TCB"; + default: + return ""; + } +} + +/* mingw support */ +#ifndef _IPRTRMIB_H +typedef struct _MIB_TCP6ROW_OWNER_PID +{ + UCHAR ucLocalAddr[16]; + DWORD dwLocalScopeId; + DWORD dwLocalPort; + UCHAR ucRemoteAddr[16]; + DWORD dwRemoteScopeId; + DWORD dwRemotePort; + DWORD dwState; + DWORD dwOwningPid; +} MIB_TCP6ROW_OWNER_PID, *PMIB_TCP6ROW_OWNER_PID; + +typedef struct _MIB_TCP6TABLE_OWNER_PID +{ + DWORD dwNumEntries; + MIB_TCP6ROW_OWNER_PID table[ANY_SIZE]; +} MIB_TCP6TABLE_OWNER_PID, *PMIB_TCP6TABLE_OWNER_PID; +#endif + +#ifndef __IPHLPAPI_H__ +typedef struct in6_addr { + union { + UCHAR Byte[16]; + USHORT Word[8]; + } u; +} IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR; + + +typedef enum _UDP_TABLE_CLASS { + UDP_TABLE_BASIC, + UDP_TABLE_OWNER_PID, + UDP_TABLE_OWNER_MODULE +} UDP_TABLE_CLASS, *PUDP_TABLE_CLASS; + + +typedef struct _MIB_UDPROW_OWNER_PID { + DWORD dwLocalAddr; + DWORD dwLocalPort; + DWORD dwOwningPid; +} MIB_UDPROW_OWNER_PID, *PMIB_UDPROW_OWNER_PID; + +typedef struct _MIB_UDPTABLE_OWNER_PID +{ + DWORD dwNumEntries; + MIB_UDPROW_OWNER_PID table[ANY_SIZE]; +} MIB_UDPTABLE_OWNER_PID, *PMIB_UDPTABLE_OWNER_PID; +#endif +/* end of mingw support */ + +typedef struct _MIB_UDP6ROW_OWNER_PID { + UCHAR ucLocalAddr[16]; + DWORD dwLocalScopeId; + DWORD dwLocalPort; + DWORD dwOwningPid; +} MIB_UDP6ROW_OWNER_PID, *PMIB_UDP6ROW_OWNER_PID; + +typedef struct _MIB_UDP6TABLE_OWNER_PID +{ + DWORD dwNumEntries; + MIB_UDP6ROW_OWNER_PID table[ANY_SIZE]; +} MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID; + + +/* + * Return a list of network connections opened by a process + */ +static PyObject* +get_process_connections(PyObject* self, PyObject* args) +{ + static long null_address[4] = { 0, 0, 0, 0 }; + + unsigned long pid; + PyObject* connectionsList; + PyObject* connectionTuple; + typedef PSTR (NTAPI *_RtlIpv4AddressToStringA)(struct in_addr *, + PSTR /* __out_ecount(16) */); + _RtlIpv4AddressToStringA rtlIpv4AddressToStringA; + typedef PSTR (NTAPI *_RtlIpv6AddressToStringA)(struct in6_addr *, + PSTR /* __out_ecount(65) */); + _RtlIpv6AddressToStringA rtlIpv6AddressToStringA; + typedef DWORD (WINAPI *_GetExtendedTcpTable)(PVOID, PDWORD, BOOL, ULONG, + TCP_TABLE_CLASS, ULONG); + _GetExtendedTcpTable getExtendedTcpTable; + typedef DWORD (WINAPI *_GetExtendedUdpTable)(PVOID, PDWORD, BOOL, ULONG, + UDP_TABLE_CLASS, ULONG); + _GetExtendedUdpTable getExtendedUdpTable; + PVOID table; + DWORD tableSize; + PMIB_TCPTABLE_OWNER_PID tcp4Table; + PMIB_UDPTABLE_OWNER_PID udp4Table; + PMIB_TCP6TABLE_OWNER_PID tcp6Table; + PMIB_UDP6TABLE_OWNER_PID udp6Table; + ULONG i; + CHAR addressBufferLocal[65]; + PyObject* addressTupleLocal; + CHAR addressBufferRemote[65]; + PyObject* addressTupleRemote; + + if (!PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + if (pid_is_running(pid) == 0) { + return NoSuchProcess(); + } + + /* Import some functions. */ + { + HMODULE ntdll; + HMODULE iphlpapi; + + ntdll = LoadLibrary(TEXT("ntdll.dll")); + rtlIpv4AddressToStringA = (_RtlIpv4AddressToStringA)GetProcAddress(ntdll, + "RtlIpv4AddressToStringA"); + rtlIpv6AddressToStringA = (_RtlIpv6AddressToStringA)GetProcAddress(ntdll, + "RtlIpv6AddressToStringA"); + /* TODO: Check these two function pointers */ + + iphlpapi = LoadLibrary(TEXT("iphlpapi.dll")); + getExtendedTcpTable = (_GetExtendedTcpTable)GetProcAddress(iphlpapi, + "GetExtendedTcpTable"); + getExtendedUdpTable = (_GetExtendedUdpTable)GetProcAddress(iphlpapi, + "GetExtendedUdpTable"); + } + + if ((getExtendedTcpTable == NULL) || (getExtendedUdpTable == NULL)) { + PyErr_SetString(PyExc_NotImplementedError, + "feature not supported on this Windows version"); + return NULL; + } + + connectionsList = PyList_New(0); + + /* TCP IPv4 */ + + tableSize = 0; + getExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET, + TCP_TABLE_OWNER_PID_ALL, 0); + + table = malloc(tableSize); + + if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET, + TCP_TABLE_OWNER_PID_ALL, 0) == 0) + { + tcp4Table = table; + + for (i = 0; i < tcp4Table->dwNumEntries; i++) + { + if (tcp4Table->table[i].dwOwningPid != pid) + continue; + + if (tcp4Table->table[i].dwLocalAddr != 0 || + tcp4Table->table[i].dwLocalPort != 0) + { + struct in_addr addr; + + addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr; + rtlIpv4AddressToStringA(&addr, addressBufferLocal); + addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, + BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort)); + } + else + { + addressTupleLocal = PyTuple_New(0); + } + + // On Windows <= XP, remote addr is filled even if socket + // is in LISTEN mode in which case we just ignore it. + if ((tcp4Table->table[i].dwRemoteAddr != 0 || + tcp4Table->table[i].dwRemotePort != 0) && + (tcp4Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) + { + struct in_addr addr; + + addr.S_un.S_addr = tcp4Table->table[i].dwRemoteAddr; + rtlIpv4AddressToStringA(&addr, addressBufferRemote); + addressTupleRemote = Py_BuildValue("(si)", addressBufferRemote, + BYTESWAP_USHORT(tcp4Table->table[i].dwRemotePort)); + } + else + { + addressTupleRemote = PyTuple_New(0); + } + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET, + SOCK_STREAM, + addressTupleLocal, + addressTupleRemote, + state_to_string(tcp4Table->table[i].dwState) + ); + PyList_Append(connectionsList, connectionTuple); + } + } + + free(table); + + /* TCP IPv6 */ + + tableSize = 0; + getExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET6, + TCP_TABLE_OWNER_PID_ALL, 0); + + table = malloc(tableSize); + + if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET6, + TCP_TABLE_OWNER_PID_ALL, 0) == 0) + { + tcp6Table = table; + + for (i = 0; i < tcp6Table->dwNumEntries; i++) + { + if (tcp6Table->table[i].dwOwningPid != pid) { + continue; + } + + if (memcmp(tcp6Table->table[i].ucLocalAddr, null_address, 16) != 0 || + tcp6Table->table[i].dwLocalPort != 0) + { + struct in6_addr addr; + + memcpy(&addr, tcp6Table->table[i].ucLocalAddr, 16); + rtlIpv6AddressToStringA(&addr, addressBufferLocal); + addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, + BYTESWAP_USHORT(tcp6Table->table[i].dwLocalPort)); + } + else + { + addressTupleLocal = PyTuple_New(0); + } + + // On Windows <= XP, remote addr is filled even if socket + // is in LISTEN mode in which case we just ignore it. + if ((memcmp(tcp6Table->table[i].ucRemoteAddr, null_address, 16) != 0 || + tcp6Table->table[i].dwRemotePort != 0) && + (tcp6Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) + { + struct in6_addr addr; + + memcpy(&addr, tcp6Table->table[i].ucRemoteAddr, 16); + rtlIpv6AddressToStringA(&addr, addressBufferRemote); + addressTupleRemote = Py_BuildValue("(si)", addressBufferRemote, + BYTESWAP_USHORT(tcp6Table->table[i].dwRemotePort)); + } + else + { + addressTupleRemote = PyTuple_New(0); + } + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET6, + SOCK_STREAM, + addressTupleLocal, + addressTupleRemote, + state_to_string(tcp6Table->table[i].dwState) + ); + PyList_Append(connectionsList, connectionTuple); + } + } + + free(table); + + /* UDP IPv4 */ + + tableSize = 0; + getExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET, + UDP_TABLE_OWNER_PID, 0); + + table = malloc(tableSize); + + if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET, + UDP_TABLE_OWNER_PID, 0) == 0) + { + udp4Table = table; + + for (i = 0; i < udp4Table->dwNumEntries; i++) + { + if (udp4Table->table[i].dwOwningPid != pid) + continue; + + if (udp4Table->table[i].dwLocalAddr != 0 || + udp4Table->table[i].dwLocalPort != 0) + { + struct in_addr addr; + + addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr; + rtlIpv4AddressToStringA(&addr, addressBufferLocal); + addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, + BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort)); + } + else + { + addressTupleLocal = PyTuple_New(0); + } + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET, + SOCK_DGRAM, + addressTupleLocal, + PyTuple_New(0), + "" + ); + PyList_Append(connectionsList, connectionTuple); + } + } + + free(table); + + /* UDP IPv6 */ + + tableSize = 0; + getExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_PID, 0); + + table = malloc(tableSize); + + if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_PID, 0) == 0) + { + udp6Table = table; + + for (i = 0; i < udp6Table->dwNumEntries; i++) + { + if (udp6Table->table[i].dwOwningPid != pid) { + continue; + } + + if (memcmp(udp6Table->table[i].ucLocalAddr, null_address, 16) != 0 || + udp6Table->table[i].dwLocalPort != 0) + { + struct in6_addr addr; + + memcpy(&addr, udp6Table->table[i].ucLocalAddr, 16); + rtlIpv6AddressToStringA(&addr, addressBufferLocal); + addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, + BYTESWAP_USHORT(udp6Table->table[i].dwLocalPort)); + } + else + { + addressTupleLocal = PyTuple_New(0); + } + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET6, + SOCK_DGRAM, + addressTupleLocal, + PyTuple_New(0), + "" + ); + PyList_Append(connectionsList, connectionTuple); + } + } + + free(table); + + return connectionsList; +} + diff --git a/third_party/psutil/psutil/_psutil_mswindows.h b/third_party/psutil/psutil/_psutil_mswindows.h new file mode 100644 index 0000000..f844a14 --- /dev/null +++ b/third_party/psutil/psutil/_psutil_mswindows.h @@ -0,0 +1,43 @@ +/* + * $Id: _psutil_mswindows.h 778 2010-11-08 19:59:08Z g.rodola $ + * + * Windows platform-specific module methods for _psutil_mswindows + * + */ + +#include <Python.h> +#include <windows.h> + +// --- per-process functions + +static PyObject* kill_process(PyObject* self, PyObject* args); +static PyObject* get_process_name(PyObject* self, PyObject* args); +static PyObject* get_process_cmdline(PyObject* self, PyObject* args); +static PyObject* get_process_ppid(PyObject* self, PyObject* args); +static PyObject* get_process_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_process_create_time(PyObject* self, PyObject* args); +static PyObject* get_memory_info(PyObject* self, PyObject* args); +static PyObject* get_process_cwd(PyObject* self, PyObject* args); +static PyObject* suspend_process(PyObject* self, PyObject* args); +static PyObject* resume_process(PyObject* self, PyObject* args); +static PyObject* get_process_open_files(PyObject* self, PyObject* args); +static PyObject* get_process_username(PyObject* self, PyObject* args); +static PyObject* get_process_connections(PyObject* self, PyObject* args); +static PyObject* get_process_num_threads(PyObject* self, PyObject* args); + +// --- system-related functions + +static PyObject* get_pid_list(PyObject* self, PyObject* args); +static PyObject* get_num_cpus(PyObject* self, PyObject* args); +static PyObject* get_system_uptime(PyObject* self, PyObject* args); +static PyObject* get_total_phymem(PyObject* self, PyObject* args); +static PyObject* get_total_virtmem(PyObject* self, PyObject* args); +static PyObject* get_avail_phymem(PyObject* self, PyObject* args); +static PyObject* get_avail_virtmem(PyObject* self, PyObject* args); +static PyObject* get_system_cpu_times(PyObject* self, PyObject* args); +static PyObject* _QueryDosDevice(PyObject* self, PyObject* args); +static PyObject* pid_exists(PyObject* self, PyObject* args); + +// --- others + +int suspend_resume_process(DWORD pid, int suspend); diff --git a/third_party/psutil/psutil/_psutil_osx.c b/third_party/psutil/psutil/_psutil_osx.c new file mode 100644 index 0000000..64947a7 --- /dev/null +++ b/third_party/psutil/psutil/_psutil_osx.c @@ -0,0 +1,689 @@ +/* + * $Id: _psutil_osx.c 780 2010-11-10 18:42:47Z jloden $ + * + * OS X platform-specific module methods for _psutil_osx + */ + +#include <Python.h> +#include <assert.h> +#include <errno.h> +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <signal.h> +#include <sys/sysctl.h> +#include <sys/vmmeter.h> + +#include <mach/mach.h> +#include <mach/task.h> +#include <mach/mach_init.h> +#include <mach/host_info.h> +#include <mach/mach_host.h> +#include <mach/mach_traps.h> +#include <mach/shared_memory_server.h> + +#include "_psutil_osx.h" +#include "arch/osx/process_info.h" + + +/* + * define the psutil C module methods and initialize the module. + */ +static PyMethodDef +PsutilMethods[] = +{ + // --- per-process functions + + {"get_process_name", get_process_name, METH_VARARGS, + "Return process name"}, + {"get_process_cmdline", get_process_cmdline, METH_VARARGS, + "Return process cmdline as a list of cmdline arguments"}, + {"get_process_ppid", get_process_ppid, METH_VARARGS, + "Return process ppid as an integer"}, + {"get_process_uid", get_process_uid, METH_VARARGS, + "Return process real user id as an integer"}, + {"get_process_gid", get_process_gid, METH_VARARGS, + "Return process real group id as an integer"}, + {"get_cpu_times", get_cpu_times, METH_VARARGS, + "Return tuple of user/kern time for the given PID"}, + {"get_process_create_time", get_process_create_time, METH_VARARGS, + "Return a float indicating the process create time expressed in " + "seconds since the epoch"}, + {"get_memory_info", get_memory_info, METH_VARARGS, + "Return a tuple of RSS/VMS memory information"}, + {"get_process_num_threads", get_process_num_threads, METH_VARARGS, + "Return number of threads used by process"}, + + // --- system-related functions + + {"get_pid_list", get_pid_list, METH_VARARGS, + "Returns a list of PIDs currently running on the system"}, + {"get_num_cpus", get_num_cpus, METH_VARARGS, + "Return number of CPUs on the system"}, + {"get_total_phymem", get_total_phymem, METH_VARARGS, + "Return the total amount of physical memory, in bytes"}, + {"get_avail_phymem", get_avail_phymem, METH_VARARGS, + "Return the amount of available physical memory, in bytes"}, + {"get_total_virtmem", get_total_virtmem, METH_VARARGS, + "Return the total amount of virtual memory, in bytes"}, + {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, + "Return the amount of available virtual memory, in bytes"}, + {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, + "Return system cpu times as a tuple (user, system, nice, idle, irc)"}, + + {NULL, NULL, 0, NULL} +}; + + +/* + * Raises an OSError(errno=ESRCH, strerror="No such process") exception + * in Python. + */ +static PyObject* +NoSuchProcess(void) { + errno = ESRCH; + return PyErr_SetFromErrno(PyExc_OSError); +} + +/* + * Raises an OSError(errno=EPERM, strerror="Operation not permitted") exception + * in Python. + */ +static PyObject* +AccessDenied(void) { + errno = EPERM; + return PyErr_SetFromErrno(PyExc_OSError); +} + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +static struct module_state _state; +#endif + +#if PY_MAJOR_VERSION >= 3 + +static int +psutil_osx_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +static int +psutil_osx_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + + +static struct PyModuleDef +moduledef = { + PyModuleDef_HEAD_INIT, + "psutil_osx", + NULL, + sizeof(struct module_state), + PsutilMethods, + NULL, + psutil_osx_traverse, + psutil_osx_clear, + NULL +}; + +#define INITERROR return NULL + +PyObject * +PyInit__psutil_osx(void) + +#else +#define INITERROR return + +void +init_psutil_osx(void) +#endif +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); +#else + PyObject *module = Py_InitModule("_psutil_osx", PsutilMethods); +#endif + if (module == NULL) { + INITERROR; + } + struct module_state *st = GETSTATE(module); + + st->error = PyErr_NewException("_psutil_osx.Error", NULL, NULL); + if (st->error == NULL) { + Py_DECREF(module); + INITERROR; + } +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} + + +/* + * Return a Python list of all the PIDs running on the system. + */ +static PyObject* +get_pid_list(PyObject* self, PyObject* args) +{ + kinfo_proc *proclist = NULL; + kinfo_proc *orig_address = NULL; + size_t num_processes; + size_t idx; + PyObject *pid; + PyObject *retlist = PyList_New(0); + + if (get_proc_list(&proclist, &num_processes) != 0) { + Py_DECREF(retlist); + PyErr_SetString(PyExc_RuntimeError, "failed to retrieve process list."); + return NULL; + } + + if (num_processes > 0) { + // save the address of proclist so we can free it later + orig_address = proclist; + for (idx=0; idx < num_processes; idx++) { + pid = Py_BuildValue("i", proclist->kp_proc.p_pid); + PyList_Append(retlist, pid); + Py_XDECREF(pid); + proclist++; + } + } + free(orig_address); + return retlist; +} + + +/* + * Return process name from kinfo_proc as a Python string. + */ +static PyObject* +get_process_name(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("s", kp.kp_proc.p_comm); +} + + +/* + * Return process cmdline as a Python list of cmdline arguments. + */ +static PyObject* +get_process_cmdline(PyObject* self, PyObject* args) +{ + long pid; + PyObject* arglist = NULL; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + // get the commandline, defined in arch/osx/process_info.c + arglist = get_arg_list(pid); + + // get_arg_list() returns NULL only if getcmdargs failed with ESRCH + // (no process with that PID) + if (NULL == arglist) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return Py_BuildValue("N", arglist); +} + + +/* + * Return process parent pid from kinfo_proc as a Python integer. + */ +static PyObject* +get_process_ppid(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.kp_eproc.e_ppid); +} + + +/* + * Return process real uid from kinfo_proc as a Python integer. + */ +static PyObject* +get_process_uid(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.kp_eproc.e_pcred.p_ruid); +} + + +/* + * Return process real group id from ki_comm as a Python integer. + */ +static PyObject* +get_process_gid(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.kp_eproc.e_pcred.p_rgid); +} + + +/* + * Return 1 if PID exists in the current process list, else 0. + */ +static int +pid_exists(long pid) { + int kill_ret; + + // save some time if it's an invalid PID + if (pid < 0) { + return 0; + } + + // if kill returns success of permission denied we know it's a valid PID + kill_ret = kill(pid , 0); + if ( (0 == kill_ret) || (EPERM == errno) ) { + return 1; + } + + // otherwise return 0 for PID not found + return 0; +} + + +/* + * Return a Python integer indicating the number of CPUs on the system. + */ +static PyObject* +get_num_cpus(PyObject* self, PyObject* args) +{ + + int mib[2]; + int ncpu; + size_t len; + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + len = sizeof(ncpu); + + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + return Py_BuildValue("i", ncpu); +} + + +#define TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) + +/* + * Return a Python tuple (user_time, kernel_time) + */ +static PyObject* +get_cpu_times(PyObject* self, PyObject* args) +{ + long pid; + int err; + unsigned int info_count = TASK_BASIC_INFO_COUNT; + task_port_t task;// = (task_port_t)NULL; + time_value_t user_time, system_time; + struct task_basic_info tasks_info; + struct task_thread_times_info task_times; + + // the argument passed should be a process id + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + /* task_for_pid() requires special privileges + * "This function can be called only if the process is owned by the + * procmod group or if the caller is root." + * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html + */ + err = task_for_pid(mach_task_self(), pid, &task); + if ( err == KERN_SUCCESS) { + info_count = TASK_BASIC_INFO_COUNT; + err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); + if (err != KERN_SUCCESS) { + if (err == 4) { // errcode 4 is "invalid argument" (access denied) + return AccessDenied(); + } + + //otherwise throw a runtime error with appropriate error code + return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_INFO) failed for pid %lu - %s (%i)", + pid, mach_error_string(err), err); + + } + + info_count = TASK_THREAD_TIMES_INFO_COUNT; + err = task_info(task, TASK_THREAD_TIMES_INFO, (task_info_t)&task_times, &info_count); + if (err != KERN_SUCCESS) { + if (err == 4) { // errcode 4 is "invalid argument" (access denied) + return AccessDenied(); + } + + return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_THREAD_TIMES_INFO) failed for pid %lu - %s (%i)", + pid, mach_error_string(err), err); + } + } + + else { // task_for_pid failed + if (! pid_exists(pid) ) { + return NoSuchProcess(); + } + + // pid exists, so return AccessDenied error since task_for_pid() failed + return AccessDenied(); + } + + float user_t = -1.0; + float sys_t = -1.0; + user_time = tasks_info.user_time; + system_time = tasks_info.system_time; + + time_value_add(&user_time, &task_times.user_time); + time_value_add(&system_time, &task_times.system_time); + + user_t = (float)user_time.seconds + ((float)user_time.microseconds / 1000000.0); + sys_t = (float)system_time.seconds + ((float)system_time.microseconds / 1000000.0); + return Py_BuildValue("(dd)", user_t, sys_t); +} + + +/* + * Return a Python float indicating the process create time expressed in + * seconds since the epoch. + */ +static PyObject* +get_process_create_time(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("d", TV2DOUBLE(kp.kp_proc.p_starttime)); +} + + +/* + * Return a tuple of RSS and VMS memory usage. + */ +static PyObject* +get_memory_info(PyObject* self, PyObject* args) +{ + long pid; + int err; + unsigned int info_count = TASK_BASIC_INFO_COUNT; + mach_port_t task; + struct task_basic_info tasks_info; + vm_region_basic_info_data_64_t b_info; + vm_address_t address = GLOBAL_SHARED_TEXT_SEGMENT; + vm_size_t size; + mach_port_t object_name; + + // the argument passed should be a process id + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + /* task_for_pid() requires special privileges + * "This function can be called only if the process is owned by the + * procmod group or if the caller is root." + * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html + */ + err = task_for_pid(mach_task_self(), pid, &task); + if ( err == KERN_SUCCESS) { + info_count = TASK_BASIC_INFO_COUNT; + err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); + if (err != KERN_SUCCESS) { + if (err == 4) { // errcode 4 is "invalid argument" (access denied) + return AccessDenied(); + } + + //otherwise throw a runtime error with appropriate error code + return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_INFO) failed for pid %lu - %s (%i)", + pid, mach_error_string(err), err); + } + + /* Issue #73 http://code.google.com/p/psutil/issues/detail?id=73 + * adjust the virtual memory size down to account for + * shared memory that task_info.virtual_size includes w/every process + */ + info_count = VM_REGION_BASIC_INFO_COUNT_64; + err = vm_region_64(task, &address, &size, VM_REGION_BASIC_INFO, + (vm_region_info_t)&b_info, &info_count, &object_name); + if (err == KERN_SUCCESS) { + if (b_info.reserved && size == (SHARED_TEXT_REGION_SIZE) && + tasks_info.virtual_size > (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE)) { + tasks_info.virtual_size -= (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE); + } + } + } + + else { + if (! pid_exists(pid) ) { + return NoSuchProcess(); + } + + // pid exists, so return AccessDenied error since task_for_pid() failed + return AccessDenied(); + } + + return Py_BuildValue("(ll)", tasks_info.resident_size, tasks_info.virtual_size); +} + + +/* + * Return number of threads used by process as a Python integer. + */ +static PyObject* +get_process_num_threads(PyObject* self, PyObject* args) +{ + long pid; + int err; + unsigned int info_count = TASK_BASIC_INFO_COUNT; + mach_port_t task; + struct task_basic_info tasks_info; + thread_act_port_array_t thread_list; + mach_msg_type_number_t thread_count; + + // the argument passed should be a process id + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + /* task_for_pid() requires special privileges + * "This function can be called only if the process is owned by the + * procmod group or if the caller is root." + * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html + */ + err = task_for_pid(mach_task_self(), pid, &task); + if ( err == KERN_SUCCESS) { + info_count = TASK_BASIC_INFO_COUNT; + err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); + if (err != KERN_SUCCESS) { + if (err == 4) { // errcode 4 is "invalid argument" (access denied) + return AccessDenied(); + } + + //otherwise throw a runtime error with appropriate error code + return PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_INFO) failed for pid %lu - %s (%i)", + pid, mach_error_string(err), err); + } + + err = task_threads(task, &thread_list, &thread_count); + if (err == KERN_SUCCESS) { + return Py_BuildValue("l", (long)thread_count); + } + } + + + else { + if (! pid_exists(pid) ) { + return NoSuchProcess(); + } + + // pid exists, so return AccessDenied error since task_for_pid() failed + return AccessDenied(); + } + return NULL; +} + + +/* + * Return a Python integer indicating the total amount of physical memory + * in bytes. + */ +static PyObject* +get_total_phymem(PyObject* self, PyObject* args) +{ + int mib[2]; + uint64_t total_phymem; + size_t len; + + mib[0] = CTL_HW; + mib[1] = HW_MEMSIZE; + len = sizeof(total_phymem); + + if (sysctl(mib, 2, &total_phymem, &len, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + return Py_BuildValue("L", total_phymem); +} + + +/* + * Return a Python long indicating the amount of available physical memory in + * bytes. + */ +static PyObject* +get_avail_phymem(PyObject* self, PyObject* args) +{ + vm_statistics_data_t vm_stat; + mach_msg_type_number_t count; + kern_return_t error; + unsigned long long mem_free; + int pagesize = getpagesize(); + mach_port_t mport = mach_host_self(); + + count = sizeof(vm_stat) / sizeof(natural_t); + error = host_statistics(mport, HOST_VM_INFO, (host_info_t)&vm_stat, &count); + + if (error != KERN_SUCCESS) { + return PyErr_Format(PyExc_RuntimeError, + "Error in host_statistics(): %s", mach_error_string(error)); + } + + mem_free = (unsigned long long) vm_stat.free_count * pagesize; + return Py_BuildValue("L", mem_free); +} + + +/* + * Return a Python integer indicating the total amount of virtual memory + * in bytes. + */ +static PyObject* +get_total_virtmem(PyObject* self, PyObject* args) +{ + int mib[2]; + size_t size; + struct xsw_usage totals; + + mib[0] = CTL_VM; + mib[1] = VM_SWAPUSAGE; + size = sizeof(totals); + + if (sysctl(mib, 2, &totals, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + return Py_BuildValue("L", totals.xsu_total); +} + +/* + * Return a Python integer indicating the avail amount of virtual memory + * in bytes. + */ +static PyObject* +get_avail_virtmem(PyObject* self, PyObject* args) +{ + int mib[2]; + size_t size; + struct xsw_usage totals; + + mib[0] = CTL_VM; + mib[1] = VM_SWAPUSAGE; + size = sizeof(totals); + + if (sysctl(mib, 2, &totals, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + return Py_BuildValue("L", totals.xsu_avail); +} + +/* + * Return a Python tuple representing user, kernel and idle CPU times + */ +static PyObject* +get_system_cpu_times(PyObject* self, PyObject* args) +{ + mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; + kern_return_t error; + host_cpu_load_info_data_t r_load; + + error = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&r_load, &count); + if (error != KERN_SUCCESS) { + return PyErr_Format(PyExc_RuntimeError, + "Error in host_statistics(): %s", mach_error_string(error)); + } + + //user, nice, system, idle, iowait, irqm, softirq + return Py_BuildValue("(dddd)", + (double)r_load.cpu_ticks[CPU_STATE_USER] / CLK_TCK, + (double)r_load.cpu_ticks[CPU_STATE_NICE] / CLK_TCK, + (double)r_load.cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK, + (double)r_load.cpu_ticks[CPU_STATE_IDLE] / CLK_TCK + ); +} + diff --git a/third_party/psutil/psutil/_psutil_osx.h b/third_party/psutil/psutil/_psutil_osx.h new file mode 100644 index 0000000..ebef764 --- /dev/null +++ b/third_party/psutil/psutil/_psutil_osx.h @@ -0,0 +1,29 @@ +/* + * $Id: _psutil_osx.h 780 2010-11-10 18:42:47Z jloden $ + * + * OS X platform-specific module methods for _psutil_osx + */ + +#include <Python.h> + +// --- per-process functions +static PyObject* get_process_name(PyObject* self, PyObject* args); +static PyObject* get_process_cmdline(PyObject* self, PyObject* args); +static PyObject* get_process_ppid(PyObject* self, PyObject* args); +static PyObject* get_process_uid(PyObject* self, PyObject* args); +static PyObject* get_process_gid(PyObject* self, PyObject* args); +static PyObject* get_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_process_create_time(PyObject* self, PyObject* args); +static PyObject* get_memory_info(PyObject* self, PyObject* args); +static PyObject* get_process_num_threads(PyObject* self, PyObject* args); + +// --- system-related functions +static int pid_exists(long pid); +static PyObject* get_pid_list(PyObject* self, PyObject* args); +static PyObject* get_num_cpus(PyObject* self, PyObject* args); +static PyObject* get_total_phymem(PyObject* self, PyObject* args); +static PyObject* get_avail_phymem(PyObject* self, PyObject* args); +static PyObject* get_total_virtmem(PyObject* self, PyObject* args); +static PyObject* get_avail_virtmem(PyObject* self, PyObject* args); +static PyObject* get_system_cpu_times(PyObject* self, PyObject* args); + diff --git a/third_party/psutil/psutil/arch/bsd/process_info.c b/third_party/psutil/psutil/arch/bsd/process_info.c new file mode 100644 index 0000000..64004c9 --- /dev/null +++ b/third_party/psutil/psutil/arch/bsd/process_info.c @@ -0,0 +1,244 @@ +/* + * $Id: process_info.c 772 2010-11-03 13:51:11Z g.rodola $ + * + * Helper functions related to fetching process information. Used by _psutil_bsd + * module methods. + */ + +#include <Python.h> +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/sysctl.h> +#include <sys/param.h> +#include <sys/user.h> +#include <sys/proc.h> + +#include "process_info.h" + + +/* + * Returns a list of all BSD processes on the system. This routine + * allocates the list and puts it in *procList and a count of the + * number of entries in *procCount. You are responsible for freeing + * this list (use "free" from System framework). + * On success, the function returns 0. + * On error, the function returns a BSD errno value. + */ +int +get_proc_list(struct kinfo_proc **procList, size_t *procCount) +{ + int err; + struct kinfo_proc * result; + int done; + static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 }; + // Declaring name as const requires us to cast it when passing it to + // sysctl because the prototype doesn't include the const modifier. + size_t length; + + assert( procList != NULL); + assert(*procList == NULL); + assert(procCount != NULL); + + *procCount = 0; + + /* + * We start by calling sysctl with result == NULL and length == 0. + * That will succeed, and set length to the appropriate length. + * We then allocate a buffer of that size and call sysctl again + * with that buffer. If that succeeds, we're done. If that fails + * with ENOMEM, we have to throw away our buffer and loop. Note + * that the loop causes use to call sysctl with NULL again; this + * is necessary because the ENOMEM failure case sets length to + * the amount of data returned, not the amount of data that + * could have been returned. + */ + result = NULL; + done = 0; + do { + assert(result == NULL); + // Call sysctl with a NULL buffer. + length = 0; + err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, + NULL, &length, NULL, 0); + if (err == -1) + err = errno; + + // Allocate an appropriately sized buffer based on the results + // from the previous call. + if (err == 0) { + result = malloc(length); + if (result == NULL) + err = ENOMEM; + } + + // Call sysctl again with the new buffer. If we get an ENOMEM + // error, toss away our buffer and start again. + if (err == 0) { + err = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1, + result, &length, NULL, 0); + if (err == -1) + err = errno; + if (err == 0) { + done = 1; + } + else if (err == ENOMEM) { + assert(result != NULL); + free(result); + result = NULL; + err = 0; + } + } + } while (err == 0 && ! done); + + // Clean up and establish post conditions. + if (err != 0 && result != NULL) { + free(result); + result = NULL; + } + + *procList = result; + *procCount = length / sizeof(struct kinfo_proc); + + assert((err == 0) == (*procList != NULL)); + return err; +} + + +char +*getcmdpath(long pid, size_t *pathsize) +{ + int mib[4]; + char *path; + size_t size = 0; + + /* + * Make a sysctl() call to get the raw argument space of the process. + */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = pid; + + // call with a null buffer first to determine if we need a buffer + if (sysctl(mib, 4, NULL, &size, NULL, 0) == -1) { + return NULL; + } + + path = malloc(size); + if (path == NULL) + return NULL; + + *pathsize = size; + if (sysctl(mib, 4, path, &size, NULL, 0) == -1) { + free(path); + return NULL; /* Insufficient privileges */ + } + + return path; +} + + + +/* + * Borrowed from psi Python System Information project + * + * Get command arguments and environment variables. + * + * Based on code from ps. + * + * Returns: + * 0 for success; + * -1 for failure (Exception raised); + * 1 for insufficient privileges. + */ +char +*getcmdargs(long pid, size_t *argsize) +{ + int mib[4]; + size_t size, argmax; + char *procargs = NULL; + + /* Get the maximum process arguments size. */ + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + size = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) + return NULL; + + /* Allocate space for the arguments. */ + procargs = (char *)malloc(argmax); + if (procargs == NULL) + return NULL; + + /* + * Make a sysctl() call to get the raw argument space of the process. + */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_ARGS; + mib[3] = pid; + + size = argmax; + if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) { + free(procargs); + return NULL; /* Insufficient privileges */ + } + + // return string and set the length of arguments + *argsize = size; + return procargs; +} + + +/* returns the command line as a python list object */ +PyObject* +get_arg_list(long pid) +{ + char *argstr = NULL; + int pos = 0; + size_t argsize = 0; + PyObject *retlist = Py_BuildValue("[]"); + PyObject *item = NULL; + + if (pid < 0) { + return retlist; + } + + // XXX - this leaks memory (grrr) + argstr = getcmdargs(pid, &argsize); + + if (NULL == argstr) { + if (ESRCH == errno) { + PyErr_Format(PyExc_RuntimeError, + "getcmdargs() failed - no process found with pid %lu", pid); + return NULL; + } + + // ignore other errors for now, since we don't want to bail on + // get_process_info() if cmdline is the only thing we couldn't get. + // In that case, we just return an empty list return + // PyErr_Format(PyExc_RuntimeError, "getcmdargs() failed for pid %lu", pid); + return retlist; + } + + // args are returned as a flattened string with \0 separators between + // arguments add each string to the list then step forward to the next + // separator + if (argsize > 0) { + while(pos < argsize) { + item = Py_BuildValue("s", &argstr[pos]); + PyList_Append(retlist, item); + Py_DECREF(item); + pos = pos + strlen(&argstr[pos]) + 1; + } + } + + free(argstr); + return retlist; +} + diff --git a/third_party/psutil/psutil/arch/bsd/process_info.h b/third_party/psutil/psutil/arch/bsd/process_info.h new file mode 100644 index 0000000..d72e69c --- /dev/null +++ b/third_party/psutil/psutil/arch/bsd/process_info.h @@ -0,0 +1,16 @@ +/* + * $Id: process_info.h 213 2009-03-05 15:48:22Z jloden $ + * + * Helper functions related to fetching process information. Used by _psutil_bsd + * module methods. + */ + +#include <Python.h> + +typedef struct kinfo_proc kinfo_proc; + +int get_proc_list(struct kinfo_proc **procList, size_t *procCount); +char *getcmdargs(long pid, size_t *argsize); +char *getcmdpath(long pid, size_t *pathsize); +PyObject* get_arg_list(long pid); + diff --git a/third_party/psutil/psutil/arch/mswindows/process_handles.c b/third_party/psutil/psutil/arch/mswindows/process_handles.c new file mode 100644 index 0000000..fd272f2 --- /dev/null +++ b/third_party/psutil/psutil/arch/mswindows/process_handles.c @@ -0,0 +1,295 @@ +#ifndef UNICODE +#define UNICODE +#endif + +#include <Python.h> +#include <windows.h> +#include <stdio.h> +#include "process_handles.h" + +#ifndef NT_SUCCESS + #define NT_SUCCESS(x) ((x) >= 0) +#endif +#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 + +#define SystemHandleInformation 16 +#define ObjectBasicInformation 0 +#define ObjectNameInformation 1 +#define ObjectTypeInformation 2 + + +typedef LONG NTSTATUS; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength + ); + +typedef NTSTATUS (NTAPI *_NtDuplicateObject)( + HANDLE SourceProcessHandle, + HANDLE SourceHandle, + HANDLE TargetProcessHandle, + PHANDLE TargetHandle, + ACCESS_MASK DesiredAccess, + ULONG Attributes, + ULONG Options + ); + +typedef NTSTATUS (NTAPI *_NtQueryObject)( + HANDLE ObjectHandle, + ULONG ObjectInformationClass, + PVOID ObjectInformation, + ULONG ObjectInformationLength, + PULONG ReturnLength + ); + +typedef struct _SYSTEM_HANDLE +{ + ULONG ProcessId; + BYTE ObjectTypeNumber; + BYTE Flags; + USHORT Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; +} SYSTEM_HANDLE, *PSYSTEM_HANDLE; + +typedef struct _SYSTEM_HANDLE_INFORMATION +{ + ULONG HandleCount; + SYSTEM_HANDLE Handles[1]; +} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; + +typedef enum _POOL_TYPE +{ + NonPagedPool, + PagedPool, + NonPagedPoolMustSucceed, + DontUseThisType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS +} POOL_TYPE, *PPOOL_TYPE; + +typedef struct _OBJECT_TYPE_INFORMATION +{ + UNICODE_STRING Name; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccess; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + USHORT MaintainTypeList; + POOL_TYPE PoolType; + ULONG PagedPoolUsage; + ULONG NonPagedPoolUsage; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; + +PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) +{ + return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); +} + + +PyObject* +get_open_files(long pid, HANDLE processHandle) +{ + _NtQuerySystemInformation NtQuerySystemInformation = + GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); + _NtDuplicateObject NtDuplicateObject = + GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject"); + _NtQueryObject NtQueryObject = + GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); + + NTSTATUS status; + PSYSTEM_HANDLE_INFORMATION handleInfo; + ULONG handleInfoSize = 0x10000; + + ULONG i; + ULONG fileNameLength; + PyObject *filesList = Py_BuildValue("[]"); + PyObject *arg = NULL; + PyObject *fileFromWchar = NULL; + + + + handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize); + + /* NtQuerySystemInformation won't give us the correct buffer size, + so we guess by doubling the buffer size. */ + while ((status = NtQuerySystemInformation( + SystemHandleInformation, + handleInfo, + handleInfoSize, + NULL + )) == STATUS_INFO_LENGTH_MISMATCH) + handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2); + + /* NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH. */ + if (!NT_SUCCESS(status)) { + //printf("NtQuerySystemInformation failed!\n"); + return NULL; + } + + for (i = 0; i < handleInfo->HandleCount; i++) + { + SYSTEM_HANDLE handle = handleInfo->Handles[i]; + HANDLE dupHandle = NULL; + POBJECT_TYPE_INFORMATION objectTypeInfo; + PVOID objectNameInfo; + UNICODE_STRING objectName; + ULONG returnLength; + + /* Check if this handle belongs to the PID the user specified. */ + if (handle.ProcessId != pid) + continue; + + /* Skip handles with the following access codes as the next call + to NtDuplicateObject() or NtQueryObject() might hang forever. */ + if((handle.GrantedAccess == 0x0012019f) + || (handle.GrantedAccess == 0x001a019f) + || (handle.GrantedAccess == 0x00120189) + || (handle.GrantedAccess == 0x00100000)) { + continue; + } + + /* Duplicate the handle so we can query it. */ + if (!NT_SUCCESS(NtDuplicateObject( + processHandle, + handle.Handle, + GetCurrentProcess(), + &dupHandle, + 0, + 0, + 0 + ))) + { + //printf("[%#x] Error!\n", handle.Handle); + continue; + } + + /* Query the object type. */ + objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); + if (!NT_SUCCESS(NtQueryObject( + dupHandle, + ObjectTypeInformation, + objectTypeInfo, + 0x1000, + NULL + ))) + { + //printf("[%#x] Error!\n", handle.Handle); + CloseHandle(dupHandle); + continue; + } + + objectNameInfo = malloc(0x1000); + if (!NT_SUCCESS(NtQueryObject( + dupHandle, + ObjectNameInformation, + objectNameInfo, + 0x1000, + &returnLength + ))) + { + /* Reallocate the buffer and try again. */ + objectNameInfo = realloc(objectNameInfo, returnLength); + if (!NT_SUCCESS(NtQueryObject( + dupHandle, + ObjectNameInformation, + objectNameInfo, + returnLength, + NULL + ))) + { + /* We have the type name, so just display that.*/ + /* + printf( + "[%#x] %.*S: (could not get name)\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer + ); + */ + free(objectTypeInfo); + free(objectNameInfo); + CloseHandle(dupHandle); + continue; + + } + } + + /* Cast our buffer into an UNICODE_STRING. */ + objectName = *(PUNICODE_STRING)objectNameInfo; + + /* Print the information! */ + if (objectName.Length) + { + /* The object has a name. Make sure it is a file otherwise + ignore it */ + fileNameLength = objectName.Length / 2; + if (wcscmp(objectTypeInfo->Name.Buffer, L"File") == 0) { + //printf("%.*S\n", objectName.Length / 2, objectName.Buffer); + fileFromWchar = PyUnicode_FromWideChar(objectName.Buffer, + fileNameLength); + #if PY_MAJOR_VERSION >= 3 + arg = Py_BuildValue("N", PyUnicode_AsUTF8String(fileFromWchar)); + #else + arg = Py_BuildValue("N", PyUnicode_FromObject(fileFromWchar)); + #endif + Py_XDECREF(fileFromWchar); + PyList_Append(filesList, arg); + Py_XDECREF(arg); + } + /* + printf( + "[%#x] %.*S: %.*S\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer, + objectName.Length / 2, + objectName.Buffer + ); + */ + } + else + { + /* Print something else. */ + /* + printf( + "[%#x] %.*S: (unnamed)\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer + ); + */ + ;; + } + free(objectTypeInfo); + free(objectNameInfo); + CloseHandle(dupHandle); + } + free(handleInfo); + CloseHandle(processHandle); + return filesList; +} + diff --git a/third_party/psutil/psutil/arch/mswindows/process_handles.h b/third_party/psutil/psutil/arch/mswindows/process_handles.h new file mode 100644 index 0000000..bd9d39b --- /dev/null +++ b/third_party/psutil/psutil/arch/mswindows/process_handles.h @@ -0,0 +1,4 @@ +#include <Python.h> +#include <windows.h> + +PyObject* get_open_files(long pid, HANDLE processHandle); diff --git a/third_party/psutil/psutil/arch/mswindows/process_info.c b/third_party/psutil/psutil/arch/mswindows/process_info.c new file mode 100644 index 0000000..3713eae --- /dev/null +++ b/third_party/psutil/psutil/arch/mswindows/process_info.c @@ -0,0 +1,479 @@ +/* + * $Id: process_info.c 778 2010-11-08 19:59:08Z g.rodola $ + * + * Helper functions related to fetching process information. Used by + * _psutil_mswindows module methods. + */ + +#include <Python.h> +#include <windows.h> +#include <Psapi.h> +#include <tlhelp32.h> + +#include "security.h" +#include "process_info.h" + + +/* + * NtQueryInformationProcess code taken from + * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/ + * typedefs needed to compile against ntdll functions not exposted in the API + */ +typedef LONG NTSTATUS; + +typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)( + HANDLE ProcessHandle, + DWORD ProcessInformationClass, + PVOID ProcessInformation, + DWORD ProcessInformationLength, + PDWORD ReturnLength + ); + +typedef struct _UNICODE_STRING +{ + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef struct _PROCESS_BASIC_INFORMATION +{ + PVOID Reserved1; + PVOID PebBaseAddress; + PVOID Reserved2[2]; + ULONG_PTR UniqueProcessId; + PVOID Reserved3; +} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; + + +/* + * Set OSError(errno=ESRCH, strerror="No such process") Python exception. + */ +PyObject * +NoSuchProcess(void) { + PyObject *exc; + char *msg = strerror(ESRCH); + exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg); + PyErr_SetObject(PyExc_OSError, exc); + Py_XDECREF(exc); + return NULL; +} + +/* + * Set OSError(errno=EACCES, strerror="No such process") Python exception. + */ +PyObject * +AccessDenied(void) { + PyObject *exc; + char *msg = strerror(EACCES); + exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg); + PyErr_SetObject(PyExc_OSError, exc); + Py_XDECREF(exc); + return NULL; +} + + +/* + * A wrapper around OpenProcess setting NSP exception if process + * no longer exists. + * "pid" is the process pid, "dwDesiredAccess" is the first argument + * exptected by OpenProcess. + * Return a process handle or NULL. + */ +HANDLE +handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) +{ + HANDLE hProcess; + DWORD processExitCode = 0; + + hProcess = OpenProcess(dwDesiredAccess, FALSE, pid); + if (hProcess == NULL) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + NoSuchProcess(); + } + else { + PyErr_SetFromWindowsErr(0); + } + return NULL; + } + + /* make sure the process is running */ + GetExitCodeProcess(hProcess, &processExitCode); + if (processExitCode == 0) { + NoSuchProcess(); + CloseHandle(hProcess); + return NULL; + } + return hProcess; +} + + +/* + * Same as handle_from_pid_waccess but implicitly uses + * PROCESS_QUERY_INFORMATION | PROCESS_VM_READ as dwDesiredAccess + * parameter for OpenProcess. + */ +HANDLE +handle_from_pid(DWORD pid) { + DWORD dwDesiredAccess = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ; + return handle_from_pid_waccess(pid, dwDesiredAccess); +} + + +// fetch the PEB base address from NtQueryInformationProcess() +PVOID +GetPebAddress(HANDLE ProcessHandle) +{ + _NtQueryInformationProcess NtQueryInformationProcess = + (_NtQueryInformationProcess)GetProcAddress( + GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); + PROCESS_BASIC_INFORMATION pbi; + + NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL); + return pbi.PebBaseAddress; +} + + +DWORD* +get_pids(DWORD *numberOfReturnedPIDs) { + int procArraySz = 1024; + + /* Win32 SDK says the only way to know if our process array + * wasn't large enough is to check the returned size and make + * sure that it doesn't match the size of the array. + * If it does we allocate a larger array and try again*/ + + /* Stores the actual array */ + DWORD *procArray = NULL; + DWORD procArrayByteSz; + + /* Stores the byte size of the returned array from enumprocesses */ + DWORD enumReturnSz = 0; + + do { + free(procArray); + procArrayByteSz = procArraySz * sizeof(DWORD); + procArray = malloc(procArrayByteSz); + + if (! EnumProcesses(procArray, procArrayByteSz, &enumReturnSz)) { + free(procArray); + PyErr_SetFromWindowsErr(0); + return NULL; + } + else if (enumReturnSz == procArrayByteSz) { + /* Process list was too large. Allocate more space*/ + procArraySz += 1024; + } + + /* else we have a good list */ + + } while(enumReturnSz == procArraySz * sizeof(DWORD)); + + /* The number of elements is the returned size / size of each element */ + *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD); + + return procArray; +} + + +int +is_system_proc(DWORD pid) { + HANDLE hProcess; + + // Special case for PID 0 System Idle Process + // and PID 4 (SYSTEM) + if ((pid == 0) || (pid == 4)) { + return 1; + } + if (pid < 0) { + return 0; + } + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); + if (NULL == hProcess) { + // invalid parameter is no such process + if (GetLastError() == ERROR_INVALID_PARAMETER) { + return 0; + } + + // access denied obviously means there's a process to deny access to... + if (GetLastError() == ERROR_ACCESS_DENIED) { + return 1; + } + + PyErr_SetFromWindowsErr(0); + return -1; + } + + return HasSystemPrivilege(hProcess); +} + + +int +pid_is_running(DWORD pid) +{ + HANDLE hProcess; + DWORD exitCode; + + // Special case for PID 0 System Idle Process + if (pid == 0) { + return 1; + } + + if (pid < 0) { + return 0; + } + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, pid); + if (NULL == hProcess) { + // invalid parameter is no such process + if (GetLastError() == ERROR_INVALID_PARAMETER) { + CloseHandle(hProcess); + return 0; + } + + // access denied obviously means there's a process to deny access to... + if (GetLastError() == ERROR_ACCESS_DENIED) { + CloseHandle(hProcess); + return 1; + } + + CloseHandle(hProcess); + PyErr_SetFromWindowsErr(0); + return -1; + } + + if (GetExitCodeProcess(hProcess, &exitCode)) { + CloseHandle(hProcess); + return (exitCode == STILL_ACTIVE); + } + + // access denied means there's a process there so we'll assume it's running + if (GetLastError() == ERROR_ACCESS_DENIED) { + CloseHandle(hProcess); + return 1; + } + + PyErr_SetFromWindowsErr(0); + CloseHandle(hProcess); + return -1; +} + + +int +pid_in_proclist(DWORD pid) +{ + DWORD *proclist = NULL; + DWORD numberOfReturnedPIDs; + DWORD i; + + proclist = get_pids(&numberOfReturnedPIDs); + if (NULL == proclist) { + return NULL; + } + + for (i = 0; i < numberOfReturnedPIDs; i++) { + if (pid == proclist[i]) { + free(proclist); + return 1; + } + } + + free(proclist); + return 0; +} + + +// Check exit code from a process handle. Return FALSE on an error also +BOOL is_running(HANDLE hProcess) +{ + DWORD dwCode; + + if (NULL == hProcess) { + return FALSE; + } + + if (GetExitCodeProcess(hProcess, &dwCode)) { + return (dwCode == STILL_ACTIVE); + } + return FALSE; +} + + +// Return None to represent NoSuchProcess, else return NULL for +// other exception or the name as a Python string +PyObject* +get_name(long pid) +{ + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe = { 0 }; + pe.dwSize = sizeof(PROCESSENTRY32); + + if( Process32First(h, &pe)) { + do { + if (pe.th32ProcessID == pid) { + CloseHandle(h); + return Py_BuildValue("s", pe.szExeFile); + } + } while(Process32Next(h, &pe)); + + // the process was never found, set NoSuchProcess exception + NoSuchProcess(); + CloseHandle(h); + return NULL; + } + + CloseHandle(h); + return PyErr_SetFromWindowsErr(0); +} + + +/* returns parent pid (as a Python int) for given pid or None on failure */ +PyObject* +get_ppid(long pid) +{ + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe = { 0 }; + pe.dwSize = sizeof(PROCESSENTRY32); + + if( Process32First(h, &pe)) { + do { + if (pe.th32ProcessID == pid) { + //printf("PID: %i; PPID: %i\n", pid, pe.th32ParentProcessID); + CloseHandle(h); + return Py_BuildValue("I", pe.th32ParentProcessID); + } + } while(Process32Next(h, &pe)); + + // the process was never found, set NoSuchProcess exception + NoSuchProcess(); + CloseHandle(h); + return NULL; + } + + CloseHandle(h); + return PyErr_SetFromWindowsErr(0); +} + + + +/* + * returns a Python list representing the arguments for the process + * with given pid or NULL on error. + */ +PyObject* +get_arg_list(long pid) +{ + int nArgs, i; + LPWSTR *szArglist; + HANDLE hProcess; + PVOID pebAddress; + PVOID rtlUserProcParamsAddress; + UNICODE_STRING commandLine; + WCHAR *commandLineContents; + PyObject *arg = NULL; + PyObject *arg_from_wchar = NULL; + PyObject *argList = NULL; + + + hProcess = handle_from_pid(pid); + if(hProcess == NULL) { + return NULL; + } + + pebAddress = GetPebAddress(hProcess); + + /* get the address of ProcessParameters */ +#ifdef _WIN64 + if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32, + &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) +#else + if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10, + &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) +#endif + { + //printf("Could not read the address of ProcessParameters!\n"); + PyErr_SetFromWindowsErr(0); + CloseHandle(hProcess); + return NULL; + } + + /* read the CommandLine UNICODE_STRING structure */ +#ifdef _WIN64 + if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112, + &commandLine, sizeof(commandLine), NULL)) +#else + if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40, + &commandLine, sizeof(commandLine), NULL)) +#endif + { + //printf("Could not read CommandLine!\n"); + PyErr_SetFromWindowsErr(0); + CloseHandle(hProcess); + return NULL; + } + + + /* allocate memory to hold the command line */ + commandLineContents = (WCHAR *)malloc(commandLine.Length+1); + + /* read the command line */ + if (!ReadProcessMemory(hProcess, commandLine.Buffer, + commandLineContents, commandLine.Length, NULL)) + { + //printf("Could not read the command line string!\n"); + PyErr_SetFromWindowsErr(0); + CloseHandle(hProcess); + free(commandLineContents); + return NULL; + } + + /* print the commandline */ + //printf("%.*S\n", commandLine.Length / 2, commandLineContents); + + // null-terminate the string to prevent wcslen from returning incorrect length + // the length specifier is in characters, but commandLine.Length is in bytes + commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0'; + + // attemempt tp parse the command line using Win32 API, fall back on string + // cmdline version otherwise + szArglist = CommandLineToArgvW(commandLineContents, &nArgs); + if (NULL == szArglist) { + // failed to parse arglist + // encode as a UTF8 Python string object from WCHAR string + arg_from_wchar = PyUnicode_FromWideChar(commandLineContents, + commandLine.Length / 2); + #if PY_MAJOR_VERSION >= 3 + argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar)); + #else + argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar)); + #endif + } + else { + // arglist parsed as array of UNICODE_STRING, so convert each to Python + // string object and add to arg list + argList = Py_BuildValue("[]"); + for(i=0; i<nArgs; i++) { + //printf("%d: %.*S (%d characters)\n", i, wcslen(szArglist[i]), + // szArglist[i], wcslen(szArglist[i])); + arg_from_wchar = PyUnicode_FromWideChar(szArglist[i], + wcslen(szArglist[i]) + ); + #if PY_MAJOR_VERSION >= 3 + arg = PyUnicode_FromObject(arg_from_wchar); + #else + arg = PyUnicode_AsUTF8String(arg_from_wchar); + #endif + Py_XDECREF(arg_from_wchar); + PyList_Append(argList, arg); + Py_XDECREF(arg); + } + } + + LocalFree(szArglist); + free(commandLineContents); + CloseHandle(hProcess); + return argList; +} + diff --git a/third_party/psutil/psutil/arch/mswindows/process_info.h b/third_party/psutil/psutil/arch/mswindows/process_info.h new file mode 100644 index 0000000..9bce0f2 --- /dev/null +++ b/third_party/psutil/psutil/arch/mswindows/process_info.h @@ -0,0 +1,24 @@ +/* + * $Id: process_info.h 779 2010-11-08 20:34:16Z g.rodola $ + * + * Helper functions related to fetching process information. Used by _psutil_mswindows + * module methods. + */ + +#include <Python.h> +#include <windows.h> + +PyObject * NoSuchProcess(void); +PyObject * AccessDenied(void); +HANDLE handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess); +HANDLE handle_from_pid(DWORD pid); +PVOID GetPebAddress(HANDLE ProcessHandle); +HANDLE handle_from_pid(DWORD pid); +BOOL is_running(HANDLE hProcess); +int pid_in_proclist(DWORD pid); +int pid_is_running(DWORD pid); +int is_system_proc(DWORD pid); +PyObject* get_arg_list(long pid); +PyObject* get_ppid(long pid); +PyObject* get_name(long pid); +DWORD* get_pids(DWORD *numberOfReturnedPIDs); diff --git a/third_party/psutil/psutil/arch/mswindows/security.c b/third_party/psutil/psutil/arch/mswindows/security.c new file mode 100644 index 0000000..ee6ff9d --- /dev/null +++ b/third_party/psutil/psutil/arch/mswindows/security.c @@ -0,0 +1,233 @@ +/* + * $Id: security.c 772 2010-11-03 13:51:11Z g.rodola $ + * + * Security related functions for Windows platform (Set privileges such as + * SeDebug), as well as security helper functions. + */ + +#include <windows.h> +#include <Python.h> + +/* + * Convert a process handle to a process token handle. + */ +HANDLE +token_from_handle(HANDLE hProcess) { + HANDLE hToken = NULL; + + if (! OpenProcessToken(hProcess, TOKEN_QUERY, &hToken) ) { + return PyErr_SetFromWindowsErr(0); + } + + return hToken; +} + + +/* + * http://www.ddj.com/windows/184405986 + * + * there’s a way to determine whether we’re running under the Local System + * account. However (you guessed it), we have to call more Win32 functions to + * determine this. Backing up through the code listing, we need to make another + * call to GetTokenInformation, but instead of passing through the TOKEN_USER + * constant, we pass through the TOKEN_PRIVILEGES constant. This value returns + * an array of privileges that the account has in the environment. Iterating + * through the array, we call the function LookupPrivilegeName looking for the + * string “SeTcbPrivilege. If the function returns this string, then this + * account has Local System privileges + */ +int HasSystemPrivilege(HANDLE hProcess) { + DWORD i; + DWORD dwSize = 0; + DWORD dwRetval = 0; + TCHAR privName[256]; + DWORD dwNameSize = 256; + //PTOKEN_PRIVILEGES tp = NULL; + BYTE *pBuffer = NULL; + TOKEN_PRIVILEGES* tp = NULL; + HANDLE hToken = token_from_handle(hProcess); + + if (NULL == hToken) { + return -1; + } + + // call GetTokenInformation first to get the buffer size + if (! GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize)) { + dwRetval = GetLastError(); + // if it failed for a reason other than the buffer, bail out + if (dwRetval != ERROR_INSUFFICIENT_BUFFER ) { + PyErr_SetFromWindowsErr(dwRetval); + return 0; + } + } + + // allocate buffer and call GetTokenInformation again + //tp = (PTOKEN_PRIVILEGES) GlobalAlloc(GPTR, dwSize); + pBuffer = (BYTE *) malloc(dwSize); + + if (pBuffer == NULL) { + PyErr_SetFromWindowsErr(0); + LocalFree(pBuffer); + return -1; + } + + if (! GetTokenInformation(hToken, TokenPrivileges, pBuffer, dwSize, &dwSize) ) { + PyErr_SetFromWindowsErr(0); + LocalFree(pBuffer); + return -1; + } + + // convert the BYTE buffer to a TOKEN_PRIVILEGES struct pointer + tp = (TOKEN_PRIVILEGES*)pBuffer; + + // check all the privileges looking for SeTcbPrivilege + for(i=0; i < tp->PrivilegeCount; i++) { + // reset the buffer contents and the buffer size + strcpy(privName, ""); + dwNameSize = sizeof(privName) / sizeof(TCHAR); + if (! LookupPrivilegeName(NULL, + &tp->Privileges[i].Luid, + (LPTSTR)privName, + &dwNameSize)) { + + PyErr_SetFromWindowsErr(0); + free(pBuffer); + return -1; + } + + // if we find the SeTcbPrivilege then it's a LocalSystem process + if (! lstrcmpi(privName, TEXT("SeTcbPrivilege"))) { + free(pBuffer); + return 1; + } + + } //for + + free(pBuffer); + return 0; +} + + +BOOL SetPrivilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) +{ + TOKEN_PRIVILEGES tp; + LUID luid; + TOKEN_PRIVILEGES tpPrevious; + DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES); + + if(!LookupPrivilegeValue( NULL, Privilege, &luid )) return FALSE; + + // first pass. get current privilege setting + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = 0; + + AdjustTokenPrivileges( + hToken, + FALSE, + &tp, + sizeof(TOKEN_PRIVILEGES), + &tpPrevious, + &cbPrevious + ); + + if (GetLastError() != ERROR_SUCCESS) return FALSE; + + // second pass. set privilege based on previous setting + tpPrevious.PrivilegeCount = 1; + tpPrevious.Privileges[0].Luid = luid; + + if(bEnablePrivilege) { + tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED); + } + + else { + tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED & + tpPrevious.Privileges[0].Attributes); + } + + AdjustTokenPrivileges( + hToken, + FALSE, + &tpPrevious, + cbPrevious, + NULL, + NULL + ); + + if (GetLastError() != ERROR_SUCCESS) return FALSE; + + return TRUE; +} + + +int SetSeDebug() +{ + HANDLE hToken; + if(! OpenThreadToken(GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + FALSE, + &hToken) + ){ + if (GetLastError() == ERROR_NO_TOKEN){ + if (!ImpersonateSelf(SecurityImpersonation)){ + //Log2File("Error setting impersonation [SetSeDebug()]", L_DEBUG); + return 0; + } + if (!OpenThreadToken(GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + FALSE, + &hToken) + ){ + //Log2File("Error Opening Thread Token", L_DEBUG); + return 0; + } + } + } + + // enable SeDebugPrivilege (open any process) + if (! SetPrivilege(hToken, SE_DEBUG_NAME, TRUE)){ + //Log2File("Error setting SeDebug Privilege [SetPrivilege()]", L_WARN); + return 0; + } + + CloseHandle(hToken); + return 1; +} + + +int UnsetSeDebug() +{ + HANDLE hToken; + if(! OpenThreadToken(GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + FALSE, + &hToken) + ){ + if(GetLastError() == ERROR_NO_TOKEN){ + if(! ImpersonateSelf(SecurityImpersonation)){ + //Log2File("Error setting impersonation! [UnsetSeDebug()]", L_DEBUG); + return 0; + } + + if(!OpenThreadToken(GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + FALSE, + &hToken) + ){ + //Log2File("Error Opening Thread Token! [UnsetSeDebug()]", L_DEBUG); + return 0; + } + } + } + + //now disable SeDebug + if(!SetPrivilege(hToken, SE_DEBUG_NAME, FALSE)){ + //Log2File("Error unsetting SeDebug Privilege [SetPrivilege()]", L_WARN); + return 0; + } + + CloseHandle(hToken); + return 1; +} + diff --git a/third_party/psutil/psutil/arch/mswindows/security.h b/third_party/psutil/psutil/arch/mswindows/security.h new file mode 100644 index 0000000..5c55643 --- /dev/null +++ b/third_party/psutil/psutil/arch/mswindows/security.h @@ -0,0 +1,16 @@ +/* + * $Id: security.h 435 2009-09-19 09:32:29Z jloden $ + * + * Security related functions for Windows platform (Set privileges such as + * SeDebug), as well as security helper functions. + */ + +#include <windows.h> + + +BOOL SetPrivilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege); +int SetSeDebug(); +int UnsetSeDebug(); +HANDLE token_from_handle(HANDLE hProcess); +int HasSystemPrivilege(HANDLE hProcess); + diff --git a/third_party/psutil/psutil/arch/osx/process_info.c b/third_party/psutil/psutil/arch/osx/process_info.c new file mode 100644 index 0000000..53b0409 --- /dev/null +++ b/third_party/psutil/psutil/arch/osx/process_info.c @@ -0,0 +1,301 @@ +/* + * $Id: process_info.c 772 2010-11-03 13:51:11Z g.rodola $ + * + * Helper functions related to fetching process information. Used by _psutil_osx + * module methods. + */ + +#include <Python.h> +#include <assert.h> +#include <errno.h> +#include <limits.h> /* for INT_MAX */ +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/sysctl.h> + +#include "process_info.h" + +#define ARGS_ACCESS_DENIED -2 + + + +/* + * Returns a list of all BSD processes on the system. This routine + * allocates the list and puts it in *procList and a count of the + * number of entries in *procCount. You are responsible for freeing + * this list (use "free" from System framework). + * On success, the function returns 0. + * On error, the function returns a BSD errno value. + */ +int +get_proc_list(kinfo_proc **procList, size_t *procCount) +{ + /* Declaring mib as const requires use of a cast since the + * sysctl prototype doesn't include the const modifier. */ + static const int mib3[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + size_t size, size2; + void *ptr; + int err, lim = 8; /* some limit */ + + assert( procList != NULL); + assert(*procList == NULL); + assert(procCount != NULL); + + *procCount = 0; + + /* We start by calling sysctl with ptr == NULL and size == 0. + * That will succeed, and set size to the appropriate length. + * We then allocate a buffer of at least that size and call + * sysctl with that buffer. If that succeeds, we're done. + * If that call fails with ENOMEM, we throw the buffer away + * and try again. + * Note that the loop calls sysctl with NULL again. This is + * is necessary because the ENOMEM failure case sets size to + * the amount of data returned, not the amount of data that + * could have been returned. + */ + while (lim-- > 0) { + size = 0; + if (sysctl((int *)mib3, 3, NULL, &size, NULL, 0) == -1) { + return errno; + } + + size2 = size + (size >> 3); /* add some */ + if (size2 > size) { + ptr = malloc(size2); + if (ptr == NULL) { + ptr = malloc(size); + } else { + size = size2; + } + } + else { + ptr = malloc(size); + } + if (ptr == NULL) { + return ENOMEM; + } + + if (sysctl((int *)mib3, 3, ptr, &size, NULL, 0) == -1) { + err = errno; + free(ptr); + if (err != ENOMEM) { + return err; + } + + } else { + *procList = (kinfo_proc *)ptr; + *procCount = size / sizeof(kinfo_proc); + return 0; + } + } + return ENOMEM; +} + + +/* + * Modified from psi Python System Information project + * + * Get command path, arguments and environment variables. + * + * Based on code from ps. + * + * Returns: + * 0 for success + * 1 for sysctl error, errno exception raised + * -1 for failure, system or memory exception raised + * -2 rather ARGS_ACCESS_DENIED, for insufficient privileges + */ +int +getcmdargs(long pid, PyObject **exec_path, PyObject **envlist, PyObject **arglist) +{ + int nargs, mib[3]; + size_t size, argmax; + char *curr_arg, *start_args, *iter_args, *end_args; + char *procargs = NULL; + char *err = NULL; + PyObject *arg; + + *arglist = Py_BuildValue("[]"); /* empty list */ + if (*arglist == NULL) { + err = "getcmdargs(): arglist exception"; + goto ERROR_RETURN; + } + + /* Get the maximum process arguments size. */ + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + size = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(NULL); + return errno; + } + + /* Allocate space for the arguments. */ + procargs = (char *)malloc(argmax); + if (procargs == NULL) { + PyErr_SetString(PyExc_MemoryError, + "getcmdargs(): insufficient memory for procargs"); + return ENOMEM; + } + + /* + * Make a sysctl() call to get the raw argument space of the process. + */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS2; + mib[2] = (int)pid; + + size = argmax; + if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) { + if (EINVAL == errno) { // invalid == access denied for some reason + free(procargs); + return ARGS_ACCESS_DENIED; /* Insufficient privileges */ + } + + PyErr_SetFromErrno(PyExc_OSError); + free(procargs); + return errno; + } + + // copy the number of argument to nargs + memcpy(&nargs, procargs, sizeof(nargs)); + iter_args = procargs + sizeof(nargs); + end_args = &procargs[size]; // end of the argument space + if (iter_args >= end_args) { + err = "getcmdargs(): argument length mismatch"; + goto ERROR_RETURN; + } + + // Save the path + if (NULL != exec_path) { + *exec_path = Py_BuildValue("s", iter_args); + if (*exec_path == NULL) { + err = "getcmdargs(): exec_path exception"; + goto ERROR_RETURN; + } + } + + //TODO: save the environment variables to envlist as well + // Skip over the exec_path and '\0' characters. + while (iter_args < end_args && *iter_args != '\0') { iter_args++; } + while (iter_args < end_args && *iter_args == '\0') { iter_args++; } + + /* Iterate through the '\0'-terminated strings and add each string + * to the Python List arglist as a Python string. + * Stop when nargs strings have been extracted. That should be all + * the arguments. The rest of the strings will be environment + * strings for the command. + */ + curr_arg = iter_args; + start_args = iter_args; //reset start position to beginning of cmdline + while (iter_args < end_args && nargs > 0) { + if (*iter_args++ == '\0') { + /* Fetch next argument */ + arg = Py_BuildValue("s", curr_arg); + if (arg == NULL) { + err = "getcmdargs(): exception building argument string"; + goto ERROR_RETURN; + } + PyList_Append(*arglist, arg); + Py_DECREF(arg); + + curr_arg = iter_args; + nargs--; + } + } + + /* + * curr_arg position should be further than the start of the argspace + * and number of arguments should be 0 after iterating above. Otherwise + * we had an empty argument space or a missing terminating \0 etc. + */ + if (curr_arg == start_args || nargs > 0) { + err = "getcmdargs(): argument parsing failed"; + goto ERROR_RETURN; + } + +ERROR_RETURN: + // Clean up. + if (NULL != procargs) { + free(procargs); + } + if (NULL != err) { + PyErr_SetString(PyExc_SystemError, err); + return -1; + } + return 0; +} + + +/* return process args as a python list */ +PyObject* +get_arg_list(long pid) +{ + int r; + PyObject *argList; + PyObject *env = NULL; + PyObject *args = NULL; + PyObject *exec_path = NULL; + + //special case for PID 0 (kernel_task) where cmdline cannot be fetched + if (pid == 0) { + return Py_BuildValue("[]"); + } + + /* Fetch the command-line arguments and environment variables */ + //printf("pid: %ld\n", pid); + if (pid < 0 || pid > (long)INT_MAX) { + return Py_BuildValue(""); + } + + r = getcmdargs(pid, &exec_path, &env, &args); + if (r == 0) { + //PySequence_Tuple(args); + argList = PySequence_List(args); + } + else if (r == ARGS_ACCESS_DENIED) { //-2 + argList = Py_BuildValue("[]"); + } + else { + argList = Py_BuildValue(""); + } + + Py_XDECREF(args); + Py_XDECREF(exec_path); + Py_XDECREF(env); + return argList; +} + + +int +get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) +{ + int mib[4]; + size_t len; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + + // fetch the info with sysctl() + len = sizeof(struct kinfo_proc); + + // now read the data from sysctl + if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) { + // raise an exception and throw errno as the error + PyErr_SetFromErrno(PyExc_OSError); + } + + /* + * sysctl succeeds but len is zero, happens when process has gone away + */ + if (len == 0) { + errno = ESRCH; + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + return 0; +} diff --git a/third_party/psutil/psutil/arch/osx/process_info.h b/third_party/psutil/psutil/arch/osx/process_info.h new file mode 100644 index 0000000..db93f06 --- /dev/null +++ b/third_party/psutil/psutil/arch/osx/process_info.h @@ -0,0 +1,16 @@ +/* + * $Id: process_info.h 760 2010-10-30 17:41:07Z g.rodola $ + * + * Helper functions related to fetching process information. Used by _psutil_osx + * module methods. + */ + +#include <Python.h> + + +typedef struct kinfo_proc kinfo_proc; + +int get_proc_list(kinfo_proc **procList, size_t *procCount); +int getcmdargs(long pid, PyObject **exec_path, PyObject **envlist, PyObject **arglist); +PyObject* get_arg_list(long pid); +int get_kinfo_proc(pid_t pid, struct kinfo_proc *kp); diff --git a/third_party/psutil/psutil/compat.py b/third_party/psutil/psutil/compat.py new file mode 100644 index 0000000..9e05dfc --- /dev/null +++ b/third_party/psutil/psutil/compat.py @@ -0,0 +1,111 @@ +#!/usr/bin/env python +# +# $Id: compat.py 712 2010-10-20 15:25:57Z g.rodola $ +# + +"""Module which provides compatibility with older Python versions.""" + +from operator import itemgetter as _itemgetter +from keyword import iskeyword as _iskeyword +import sys as _sys + + +def namedtuple(typename, field_names, verbose=False, rename=False): + """A collections.namedtuple implementation written in Python + to support Python versions < 2.6. + + Taken from: http://code.activestate.com/recipes/500261/ + """ + # Parse and validate the field names. Validation serves two + # purposes, generating informative error messages and preventing + # template injection attacks. + if isinstance(field_names, basestring): + # names separated by whitespace and/or commas + field_names = field_names.replace(',', ' ').split() + field_names = tuple(map(str, field_names)) + if rename: + names = list(field_names) + seen = set() + for i, name in enumerate(names): + if (not min(c.isalnum() or c=='_' for c in name) or _iskeyword(name) + or not name or name[0].isdigit() or name.startswith('_') + or name in seen): + names[i] = '_%d' % i + seen.add(name) + field_names = tuple(names) + for name in (typename,) + field_names: + if not min(c.isalnum() or c=='_' for c in name): + raise ValueError('Type names and field names can only contain ' \ + 'alphanumeric characters and underscores: %r' + % name) + if _iskeyword(name): + raise ValueError('Type names and field names cannot be a keyword: %r' \ + % name) + if name[0].isdigit(): + raise ValueError('Type names and field names cannot start with a ' \ + 'number: %r' % name) + seen_names = set() + for name in field_names: + if name.startswith('_') and not rename: + raise ValueError('Field names cannot start with an underscore: %r' + % name) + if name in seen_names: + raise ValueError('Encountered duplicate field name: %r' % name) + seen_names.add(name) + + # Create and fill-in the class template + numfields = len(field_names) + # tuple repr without parens or quotes + argtxt = repr(field_names).replace("'", "")[1:-1] + reprtxt = ', '.join('%s=%%r' % name for name in field_names) + template = '''class %(typename)s(tuple): + '%(typename)s(%(argtxt)s)' \n + __slots__ = () \n + _fields = %(field_names)r \n + def __new__(_cls, %(argtxt)s): + return _tuple.__new__(_cls, (%(argtxt)s)) \n + @classmethod + def _make(cls, iterable, new=tuple.__new__, len=len): + 'Make a new %(typename)s object from a sequence or iterable' + result = new(cls, iterable) + if len(result) != %(numfields)d: + raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) + return result \n + def __repr__(self): + return '%(typename)s(%(reprtxt)s)' %% self \n + def _asdict(self): + 'Return a new dict which maps field names to their values' + return dict(zip(self._fields, self)) \n + def _replace(_self, **kwds): + 'Return a new %(typename)s object replacing specified fields with new values' + result = _self._make(map(kwds.pop, %(field_names)r, _self)) + if kwds: + raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) + return result \n + def __getnewargs__(self): + return tuple(self) \n\n''' % locals() + for i, name in enumerate(field_names): + template += ' %s = _property(_itemgetter(%d))\n' % (name, i) + if verbose: + print template + + # Execute the template string in a temporary namespace + namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, + _property=property, _tuple=tuple) + try: + exec template in namespace + except SyntaxError, e: + raise SyntaxError(e.message + ':\n' + template) + result = namespace[typename] + + # For pickling to work, the __module__ variable needs to be set + # to the frame where the named tuple is created. Bypass this + # step in enviroments where sys._getframe is not defined (Jython + # for example) or sys._getframe is not defined for arguments + # greater than 0 (IronPython). + try: + result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + + return result diff --git a/third_party/psutil/psutil/error.py b/third_party/psutil/psutil/error.py new file mode 100644 index 0000000..214a241 --- /dev/null +++ b/third_party/psutil/psutil/error.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# +# $Id: error.py 744 2010-10-27 22:42:42Z jloden $ +# + +"""psutil exception classes""" + + +class Error(Exception): + """Base exception class. All other psutil exceptions inherit + from this one. + """ + +class NoSuchProcess(Error): + """Exception raised when a process with a certain PID doesn't + or no longer exists (zombie). + """ + + def __init__(self, pid, name=None, msg=None): + self.pid = pid + self.name = name + self.msg = msg + if msg is None: + if name: + details = "(pid=%s, name=%s)" % (self.pid, repr(self.name)) + else: + details = "(pid=%s)" % self.pid + self.msg = "process no longer exists " + details + + def __str__(self): + return self.msg + + +class AccessDenied(Error): + """Exception raised when permission to perform an action is denied.""" + + def __init__(self, pid=None, name=None, msg=None): + self.pid = pid + self.name = name + self.msg = msg + if msg is None: + if (pid is not None) and (name is not None): + self.msg = "(pid=%s, name=%s)" % (pid, repr(name)) + elif (pid is not None): + self.msg = "(pid=%s)" % self.pid + else: + self.msg = "" + + def __str__(self): + return self.msg + diff --git a/third_party/psutil/setup.py b/third_party/psutil/setup.py new file mode 100644 index 0000000..fb944bf --- /dev/null +++ b/third_party/psutil/setup.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# +# $Id: setup.py 748 2010-10-29 12:51:58Z g.rodola $ +# + +import sys +import os +import shutil +from distutils.core import setup, Extension + +# Hack for Python 3 to tell distutils to run 2to3 against the files +# copied in the build directory before installing. +# Reference: http://osdir.com/ml/python.python-3000.cvs/2008-03/msg00127.html +try: + from distutils.command.build_py import build_py_2to3 as build_py +except ImportError: + from distutils.command.build_py import build_py + + +# Windows +if sys.platform.lower().startswith("win"): + + def get_winver(): + maj,min = sys.getwindowsversion()[0:2] + return '0x0%s' % ((maj * 100) + min) + + extensions = Extension('_psutil_mswindows', + sources=['psutil/_psutil_mswindows.c', + 'psutil/arch/mswindows/process_info.c', + 'psutil/arch/mswindows/process_handles.c', + 'psutil/arch/mswindows/security.c'], + define_macros=[('_WIN32_WINNT', get_winver()), + ('_AVAIL_WINVER_', get_winver())], + libraries=["psapi", "kernel32", "advapi32", "shell32", + "netapi32"] + ) +# OS X +elif sys.platform.lower().startswith("darwin"): + extensions = Extension('_psutil_osx', + sources = ['psutil/_psutil_osx.c', + 'psutil/arch/osx/process_info.c'] + ) +# FreeBSD +elif sys.platform.lower().startswith("freebsd"): + extensions = Extension('_psutil_bsd', + sources = ['psutil/_psutil_bsd.c', + 'psutil/arch/bsd/process_info.c'] + ) +# Others +elif sys.platform.lower().startswith("linux"): + extensions = None +else: + raise NotImplementedError('platform %s is not supported' % sys.platform) + + +def main(): + setup_args = dict( + name='psutil', + version="0.2.0", + description='A process utilities module for Python', + long_description=""" +psutil is a module providing convenience functions for managing processes in a +portable way by using Python.""", + keywords=['psutil', 'ps', 'top', 'process', 'utility'], + author='Giampaolo Rodola, Dave Daeschler, Jay Loden', + author_email='psutil-dev@googlegroups.com', + url='http://code.google.com/p/psutil/', + platforms='Platform Independent', + license='License :: OSI Approved :: BSD License', + packages=['psutil'], + cmdclass={'build_py':build_py}, # Python 3.X + classifiers=[ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Console', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows :: Windows NT/2000', + 'Operating System :: POSIX :: Linux', + 'Operating System :: POSIX :: BSD :: FreeBSD', + 'Operating System :: OS Independent', + 'Programming Language :: C', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.4', + 'Programming Language :: Python :: 2.5', + 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.0', + 'Programming Language :: Python :: 3.1', + 'Programming Language :: Python :: 3.2', + 'Topic :: System :: Monitoring', + 'Topic :: System :: Networking', + 'Topic :: System :: Benchmark', + 'Topic :: System :: Systems Administration', + 'Topic :: Utilities', + 'Topic :: Software Development :: Libraries :: Python Modules', + 'Intended Audience :: Developers', + 'Intended Audience :: System Administrators', + 'License :: OSI Approved :: MIT License', + ], + ) + if extensions is not None: + setup_args["ext_modules"] = [extensions] + + setup(**setup_args) + + +if __name__ == '__main__': + main() + diff --git a/third_party/psutil/test/_bsd.py b/third_party/psutil/test/_bsd.py new file mode 100644 index 0000000..f2df15d --- /dev/null +++ b/third_party/psutil/test/_bsd.py @@ -0,0 +1,136 @@ +#!/usr/bin/env python +# +# $Id: _bsd.py 664 2010-10-09 16:14:34Z g.rodola $ +# + +import unittest +import subprocess +import time +import re +import sys + +import psutil + +from test_psutil import reap_children, get_test_subprocess +from _posix import ps + + +def sysctl(cmdline): + """Expects a sysctl command with an argument and parse the result + returning only the value of interest. + """ + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + result = p.communicate()[0].strip().split()[1] + if sys.version_info >= (3,): + result = str(result, sys.stdout.encoding) + try: + return int(result) + except ValueError: + return result + +def parse_sysctl_vmtotal(output): + """Parse sysctl vm.vmtotal output returning total and free memory + values. + """ + line = output.split('\n')[4] # our line of interest + mobj = re.match(r'Virtual\s+Memory.*Total:\s+(\d+)K,\s+Active\s+(\d+)K.*', line) + total, active = mobj.groups() + # values are represented in kilo bytes + total = int(total) * 1024 + active = int(active) * 1024 + free = total - active + return total, free + + +class BSDSpecificTestCase(unittest.TestCase): + + def setUp(self): + self.pid = get_test_subprocess().pid + + def tearDown(self): + reap_children() + + def test_TOTAL_PHYMEM(self): + sysctl_hwphymem = sysctl('sysctl hw.physmem') + self.assertEqual(sysctl_hwphymem, psutil.TOTAL_PHYMEM) + + def test_avail_phymem(self): + # This test is not particularly accurate and may fail if the OS is + # consuming memory for other applications. + # We just want to test that the difference between psutil result + # and sysctl's is not too high. + _sum = sum((sysctl("sysctl vm.stats.vm.v_inactive_count"), + sysctl("sysctl vm.stats.vm.v_cache_count"), + sysctl("sysctl vm.stats.vm.v_free_count") + )) + _pagesize = sysctl("sysctl hw.pagesize") + sysctl_avail_phymem = _sum * _pagesize + psutil_avail_phymem = psutil.avail_phymem() + difference = abs(psutil_avail_phymem - sysctl_avail_phymem) + # On my system both sysctl and psutil report the same values. + # Let's use a tollerance of 0.5 MB and consider the test as failed + # if we go over it. + if difference > (0.5 * 2**20): + self.fail("sysctl=%s; psutil=%s; difference=%s;" %( + sysctl_avail_phymem, psutil_avail_phymem, difference)) + + def test_total_virtmem(self): + # This test is not particularly accurate and may fail if the OS is + # consuming memory for other applications. + # We just want to test that the difference between psutil result + # and sysctl's is not too high. + p = subprocess.Popen("sysctl vm.vmtotal", shell=1, stdout=subprocess.PIPE) + result = p.communicate()[0].strip() + if sys.version_info >= (3,): + result = str(result, sys.stdout.encoding) + sysctl_total_virtmem, _ = parse_sysctl_vmtotal(result) + psutil_total_virtmem = psutil.total_virtmem() + difference = abs(sysctl_total_virtmem - psutil_total_virtmem) + + # On my system I get a difference of 4657152 bytes, probably because + # the system is consuming memory for this same test. + # Assuming psutil is right, let's use a tollerance of 10 MB and consider + # the test as failed if we go over it. + if difference > (10 * 2**20): + self.fail("sysctl=%s; psutil=%s; difference=%s;" %( + sysctl_total_virtmem, psutil_total_virtmem, difference) + ) + + def test_avail_virtmem(self): + # This test is not particularly accurate and may fail if the OS is + # consuming memory for other applications. + # We just want to test that the difference between psutil result + # and sysctl's is not too high. + p = subprocess.Popen("sysctl vm.vmtotal", shell=1, stdout=subprocess.PIPE) + result = p.communicate()[0].strip() + if sys.version_info >= (3,): + result = str(result, sys.stdout.encoding) + _, sysctl_avail_virtmem = parse_sysctl_vmtotal(result) + psutil_avail_virtmem = psutil.avail_virtmem() + difference = abs(sysctl_avail_virtmem - psutil_avail_virtmem) + # let's assume the test is failed if difference is > 0.5 MB + if difference > (0.5 * 2**20): + self.fail("sysctl=%s; psutil=%s; difference=%s;" %( + sysctl_avail_virtmem, psutil_avail_virtmem, difference)) + + def test_process_create_time(self): + cmdline = "ps -o lstart -p %s" %self.pid + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0] + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + start_ps = output.replace('STARTED', '').strip() + start_psutil = psutil.Process(self.pid).create_time + start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", + time.localtime(start_psutil)) + self.assertEqual(start_ps, start_psutil) + + +if __name__ == '__main__': + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase)) + unittest.TextTestRunner(verbosity=2).run(test_suite) + + + + diff --git a/third_party/psutil/test/_linux.py b/third_party/psutil/test/_linux.py new file mode 100644 index 0000000..187c058 --- /dev/null +++ b/third_party/psutil/test/_linux.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# +# $Id: _linux.py 707 2010-10-19 18:16:08Z g.rodola $ +# + +import unittest +import subprocess +import sys + +import psutil + + +class LinuxSpecificTestCase(unittest.TestCase): + + def test_cached_phymem(self): + # test psutil.cached_phymem against "cached" column of free + # command line utility + p = subprocess.Popen("free", shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + free_cmem = int(output.split('\n')[1].split()[6]) + psutil_cmem = psutil.cached_phymem() / 1024 + self.assertEqual(free_cmem, psutil_cmem) + + def test_phymem_buffers(self): + # test psutil.phymem_buffers against "buffers" column of free + # command line utility + p = subprocess.Popen("free", shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + free_cmem = int(output.split('\n')[1].split()[5]) + psutil_cmem = psutil.phymem_buffers() / 1024 + self.assertEqual(free_cmem, psutil_cmem) + + +if __name__ == '__main__': + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(LinuxSpecificTestCase)) + unittest.TextTestRunner(verbosity=2).run(test_suite) + + + + diff --git a/third_party/psutil/test/_osx.py b/third_party/psutil/test/_osx.py new file mode 100644 index 0000000..aaf9215 --- /dev/null +++ b/third_party/psutil/test/_osx.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# $Id: _osx.py 664 2010-10-09 16:14:34Z g.rodola $ +# + +import unittest +import subprocess +import time +import re +import sys + +import psutil + +from test_psutil import reap_children, get_test_subprocess +#from _posix import ps + + +def sysctl(cmdline): + """Expects a sysctl command with an argument and parse the result + returning only the value of interest. + """ + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + result = p.communicate()[0].strip().split()[1] + if sys.version_info >= (3,): + result = str(result, sys.stdout.encoding) + try: + return int(result) + except ValueError: + return result + + +class OSXSpecificTestCase(unittest.TestCase): + + def setUp(self): + self.pid = get_test_subprocess().pid + + def tearDown(self): + reap_children() + + def test_TOTAL_PHYMEM(self): + sysctl_hwphymem = sysctl('sysctl hw.physmem') + self.assertEqual(sysctl_hwphymem, psutil.TOTAL_PHYMEM) + + def test_process_create_time(self): + cmdline = "ps -o lstart -p %s" %self.pid + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0] + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + start_ps = output.replace('STARTED', '').strip() + start_psutil = psutil.Process(self.pid).create_time + start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", + time.localtime(start_psutil)) + self.assertEqual(start_ps, start_psutil) + + +if __name__ == '__main__': + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(OSXSpecificTestCase)) + unittest.TextTestRunner(verbosity=2).run(test_suite) + + + + diff --git a/third_party/psutil/test/_posix.py b/third_party/psutil/test/_posix.py new file mode 100644 index 0000000..1879dc7 --- /dev/null +++ b/third_party/psutil/test/_posix.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# +# $Id: _posix.py 744 2010-10-27 22:42:42Z jloden $ +# + +import unittest +import subprocess +import time +import sys +import os + +import psutil + +from test_psutil import kill, get_test_subprocess, PYTHON, LINUX, OSX + + +def ps(cmd): + """Expects a ps command with a -o argument and parse the result + returning only the value of interest. + """ + if not LINUX: + cmd = cmd.replace(" --no-headers ", " ") + p = subprocess.Popen(cmd, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + if not LINUX: + output = output.split('\n')[1] + try: + return int(output) + except ValueError: + return output + + +class PosixSpecificTestCase(unittest.TestCase): + """Compare psutil results against 'ps' command line utility.""" + + # for ps -o arguments see: http://unixhelp.ed.ac.uk/CGI/man-cgi?ps + + def setUp(self): + self.pid = get_test_subprocess([PYTHON, "-E", "-O"]).pid + + def tearDown(self): + kill(self.pid) + + def test_process_parent_pid(self): + ppid_ps = ps("ps --no-headers -o ppid -p %s" %self.pid) + ppid_psutil = psutil.Process(self.pid).ppid + self.assertEqual(ppid_ps, ppid_psutil) + + def test_process_uid(self): + uid_ps = ps("ps --no-headers -o uid -p %s" %self.pid) + uid_psutil = psutil.Process(self.pid).uid + self.assertEqual(uid_ps, uid_psutil) + + def test_process_gid(self): + gid_ps = ps("ps --no-headers -o rgid -p %s" %self.pid) + gid_psutil = psutil.Process(self.pid).gid + self.assertEqual(gid_ps, gid_psutil) + + def test_process_username(self): + username_ps = ps("ps --no-headers -o user -p %s" %self.pid) + username_psutil = psutil.Process(self.pid).username + self.assertEqual(username_ps, username_psutil) + + def test_process_rss_memory(self): + # give python interpreter some time to properly initialize + # so that the results are the same + time.sleep(0.1) + rss_ps = ps("ps --no-headers -o rss -p %s" %self.pid) + rss_psutil = psutil.Process(self.pid).get_memory_info()[0] / 1024 + self.assertEqual(rss_ps, rss_psutil) + + def test_process_vsz_memory(self): + # give python interpreter some time to properly initialize + # so that the results are the same + time.sleep(0.1) + vsz_ps = ps("ps --no-headers -o vsz -p %s" %self.pid) + vsz_psutil = psutil.Process(self.pid).get_memory_info()[1] / 1024 + self.assertEqual(vsz_ps, vsz_psutil) + + def test_process_name(self): + # use command + arg since "comm" keyword not supported on all platforms + name_ps = ps("ps --no-headers -o command -p %s" %self.pid).split(' ')[0] + # remove path if there is any, from the command + name_ps = os.path.basename(name_ps) + name_psutil = psutil.Process(self.pid).name + if OSX: + self.assertEqual(name_psutil, "Python") + else: + self.assertEqual(name_ps, name_psutil) + + + def test_process_exe(self): + ps_pathname = ps("ps --no-headers -o command -p %s" %self.pid).split(' ')[0] + psutil_pathname = psutil.Process(self.pid).exe + self.assertEqual(ps_pathname, psutil_pathname) + + def test_process_cmdline(self): + ps_cmdline = ps("ps --no-headers -o command -p %s" %self.pid) + psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline) + self.assertEqual(ps_cmdline, psutil_cmdline) + + def test_get_pids(self): + # Note: this test might fail if the OS is starting/killing + # other processes in the meantime + p = get_test_subprocess(["ps", "ax", "-o", "pid"], stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + output = output.replace('PID', '') + p.wait() + pids_ps = [] + for pid in output.split('\n'): + if pid: + pids_ps.append(int(pid.strip())) + # remove ps subprocess pid which is supposed to be dead in meantime + pids_ps.remove(p.pid) + # not all systems include pid 0 in their list but psutil does so + # we force it + if 0 not in pids_ps: + pids_ps.append(0) + + pids_psutil = psutil.get_pid_list() + pids_ps.sort() + pids_psutil.sort() + + if pids_ps != pids_psutil: + difference = filter(lambda x:x not in pids_ps, pids_psutil) + \ + filter(lambda x:x not in pids_psutil, pids_ps) + self.fail("difference: " + str(difference)) + + +if __name__ == '__main__': + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(PosixSpecificTestCase)) + unittest.TextTestRunner(verbosity=2).run(test_suite) + + diff --git a/third_party/psutil/test/_windows.py b/third_party/psutil/test/_windows.py new file mode 100644 index 0000000..95a45b6 --- /dev/null +++ b/third_party/psutil/test/_windows.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python +# +# $Id: _windows.py 798 2010-11-12 20:52:57Z g.rodola $ +# + + +import os +import unittest +import platform +import subprocess +import signal +import time +import warnings +import atexit + +import psutil +from test_psutil import reap_children, get_test_subprocess, wait_for_pid +try: + import wmi +except ImportError, err: + atexit.register(warnings.warn, "Couldn't run wmi tests: %s" % str(err), + RuntimeWarning) + wmi = None + +WIN2000 = platform.win32_ver()[0] == '2000' + + +class WindowsSpecificTestCase(unittest.TestCase): + + def setUp(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + self.pid = sproc.pid + + def tearDown(self): + reap_children() + + def test_issue_24(self): + p = psutil.Process(0) + self.assertRaises(psutil.AccessDenied, p.kill) + + def test_special_pid(self): + if not WIN2000: + p = psutil.Process(4) + else: + p = psutil.Process(8) + self.assertEqual(p.name, 'System') + # use __str__ to access all common Process properties to check + # that nothing strange happens + str(p) + p.username + self.assertTrue(p.create_time >= 0.0) + try: + rss, vms = p.get_memory_info() + except psutil.AccessDenied: + # expected on Windows Vista and Windows 7 + if not platform.uname()[1] in ('vista', 'win-7', 'win7'): + raise + else: + self.assertTrue(rss > 0) + if not WIN2000: + self.assertEqual(vms, 0) + + def test_signal(self): + p = psutil.Process(self.pid) + self.assertRaises(ValueError, p.send_signal, signal.SIGINT) + + if wmi is not None: + + # --- Process class tests + + def test_process_name(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + self.assertEqual(p.name, w.Caption) + + def test_process_path(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + self.assertEqual(p.exe, w.ExecutablePath) + + if not WIN2000: + def test_process_cmdline(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + self.assertEqual(' '.join(p.cmdline), w.CommandLine.replace('"', '')) + + def test_process_username(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + domain, _, username = w.GetOwner() + username = "%s\\%s" %(domain, username) + self.assertEqual(p.username, username) + + def test_process_rss_memory(self): + time.sleep(0.1) + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + rss = p.get_memory_info().rss + self.assertEqual(rss, int(w.WorkingSetSize)) + + def test_process_vms_memory(self): + time.sleep(0.1) + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + vms = p.get_memory_info().vms + # http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx + # ...claims that PageFileUsage is represented in Kilo + # bytes but funnily enough on certain platforms bytes are + # returned instead. + wmi_usage = int(w.PageFileUsage) + if (vms != wmi_usage) and (vms != wmi_usage * 1024): + self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms)) + + def test_process_create_time(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + wmic_create = str(w.CreationDate.split('.')[0]) + psutil_create = time.strftime("%Y%m%d%H%M%S", + time.localtime(p.create_time)) + self.assertEqual(wmic_create, psutil_create) + + + # --- psutil namespace functions and constants tests + + def test_NUM_CPUS(self): + num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) + self.assertEqual(num_cpus, psutil.NUM_CPUS) + + def test_TOTAL_PHYMEM(self): + w = wmi.WMI().Win32_ComputerSystem()[0] + self.assertEqual(int(w.TotalPhysicalMemory), psutil.TOTAL_PHYMEM) + + def test__UPTIME(self): + # _UPTIME constant is not public but it is used internally + # as value to return for pid 0 creation time. + # WMI behaves the same. + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(0) + wmic_create = str(w.CreationDate.split('.')[0]) + psutil_create = time.strftime("%Y%m%d%H%M%S", + time.localtime(p.create_time)) + + def test_get_pids(self): + # Note: this test might fail if the OS is starting/killing + # other processes in the meantime + w = wmi.WMI().Win32_Process() + wmi_pids = [x.ProcessId for x in w] + wmi_pids.sort() + psutil_pids = psutil.get_pid_list() + psutil_pids.sort() + if wmi_pids != psutil_pids: + difference = filter(lambda x:x not in wmi_pids, psutil_pids) + \ + filter(lambda x:x not in psutil_pids, wmi_pids) + self.fail("difference: " + str(difference)) + + +if __name__ == '__main__': + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase)) + unittest.TextTestRunner(verbosity=2).run(test_suite) + diff --git a/third_party/psutil/test/test_memory_leaks.py b/third_party/psutil/test/test_memory_leaks.py new file mode 100644 index 0000000..d5591a3 --- /dev/null +++ b/third_party/psutil/test/test_memory_leaks.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +# +# $Id: test_memory_leaks.py 777 2010-11-08 18:29:13Z g.rodola $ +# + +""" +Note: this is targeted for python 2.x. +To run it under python 3.x you need to use 2to3 tool first: + +$ 2to3 -w test/test_memory_leaks.py +""" + + +import os +import gc +import sys +import unittest + +import psutil +from test_psutil import reap_children, skipUnless, skipIf, \ + POSIX, LINUX, WINDOWS, OSX, BSD + +LOOPS = 1000 +TOLERANCE = 4096 + + +class TestProcessObjectLeaks(unittest.TestCase): + """Test leaks of Process class methods and properties""" + + def setUp(self): + gc.collect() + + def tearDown(self): + reap_children() + + def execute(self, method, *args, **kwarks): + # step 1 + p = psutil.Process(os.getpid()) + for x in xrange(LOOPS): + obj = getattr(p, method) + if callable(obj): + retvalue = obj(*args, **kwarks) + else: + retvalue = obj # property + del x, p, obj, retvalue + gc.collect() + rss1 = psutil.Process(os.getpid()).get_memory_info()[0] + + # step 2 + p = psutil.Process(os.getpid()) + for x in xrange(LOOPS): + obj = getattr(p, method) + if callable(obj): + retvalue = obj(*args, **kwarks) + else: + retvalue = obj # property + del x, p, obj, retvalue + gc.collect() + rss2 = psutil.Process(os.getpid()).get_memory_info()[0] + + # comparison + difference = rss2 - rss1 + if difference > TOLERANCE: + self.fail("rss1=%s, rss2=%s, difference=%s" %(rss1, rss2, difference)) + + def test_name(self): + self.execute('name') + + def test_cmdline(self): + self.execute('cmdline') + + def test_ppid(self): + self.execute('ppid') + + def test_uid(self): + self.execute('uid') + + def test_uid(self): + self.execute('gid') + + @skipIf(POSIX) + def test_username(self): + self.execute('username') + + def test_create_time(self): + self.execute('create_time') + + def test_get_num_threads(self): + self.execute('get_num_threads') + + def test_get_cpu_times(self): + self.execute('get_cpu_times') + + def test_get_memory_info(self): + self.execute('get_memory_info') + + def test_is_running(self): + self.execute('is_running') + + @skipUnless(WINDOWS) + def test_resume(self): + self.execute('resume') + + @skipUnless(WINDOWS) + def test_getcwd(self): + self.execute('getcwd') + + @skipUnless(WINDOWS) + def test_get_open_files(self): + self.execute('get_open_files') + + @skipUnless(WINDOWS) + def test_get_connections(self): + self.execute('get_connections') + + +class TestModuleFunctionsLeaks(unittest.TestCase): + """Test leaks of psutil module functions.""" + + def setUp(self): + gc.collect() + + def execute(self, function, *args, **kwarks): + # step 1 + for x in xrange(LOOPS): + obj = getattr(psutil, function) + if callable(obj): + retvalue = obj(*args, **kwarks) + else: + retvalue = obj # property + del x, obj, retvalue + gc.collect() + rss1 = psutil.Process(os.getpid()).get_memory_info()[0] + + # step 2 + for x in xrange(LOOPS): + obj = getattr(psutil, function) + if callable(obj): + retvalue = obj(*args, **kwarks) + else: + retvalue = obj # property + del x, obj, retvalue + gc.collect() + rss2 = psutil.Process(os.getpid()).get_memory_info()[0] + + # comparison + difference = rss2 - rss1 + if difference > TOLERANCE: + self.fail("rss1=%s, rss2=%s, difference=%s" %(rss1, rss2, difference)) + + def test_get_pid_list(self): + self.execute('get_pid_list') + + @skipIf(POSIX) + def test_pid_exists(self): + self.execute('pid_exists', os.getpid()) + + def test_process_iter(self): + self.execute('process_iter') + + def test_used_phymem(self): + self.execute('used_phymem') + + def test_avail_phymem(self): + self.execute('avail_phymem') + + def test_total_virtmem(self): + self.execute('total_virtmem') + + def test_used_virtmem(self): + self.execute('used_virtmem') + + def test_avail_virtmem(self): + self.execute('avail_virtmem') + + def test_cpu_times(self): + self.execute('cpu_times') + + +def test_main(): + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(TestProcessObjectLeaks)) + test_suite.addTest(unittest.makeSuite(TestModuleFunctionsLeaks)) + unittest.TextTestRunner(verbosity=2).run(test_suite) + +if __name__ == '__main__': + test_main() + diff --git a/third_party/psutil/test/test_psutil.py b/third_party/psutil/test/test_psutil.py new file mode 100644 index 0000000..c067dee --- /dev/null +++ b/third_party/psutil/test/test_psutil.py @@ -0,0 +1,1062 @@ +#!/usr/bin/env python +# +# $Id: test_psutil.py 806 2010-11-12 23:09:35Z g.rodola $ +# + +"""psutil test suite. +Note: this is targeted for python 2.x. +To run it under python 3.x you need to use 2to3 tool first: + +$ 2to3 -w test/*.py +""" + +import unittest +import os +import sys +import subprocess +import time +import signal +import types +import traceback +import socket +import warnings +import atexit +import errno +import threading + +import psutil + + +PYTHON = os.path.realpath(sys.executable) +DEVNULL = open(os.devnull, 'r+') + +POSIX = os.name == 'posix' +LINUX = sys.platform.lower().startswith("linux") +WINDOWS = sys.platform.lower().startswith("win32") +OSX = sys.platform.lower().startswith("darwin") +BSD = sys.platform.lower().startswith("freebsd") + +try: + psutil.Process(os.getpid()).get_connections() +except NotImplementedError, err: + SUPPORT_CONNECTIONS = False + atexit.register(warnings.warn, "get_connections() not supported on this platform", + RuntimeWarning) +else: + SUPPORT_CONNECTIONS = True + + +_subprocesses_started = set() + +def get_test_subprocess(cmd=None, stdout=DEVNULL, stderr=DEVNULL, stdin=None): + """Return a subprocess.Popen object to use in tests. + By default stdout and stderr are redirected to /dev/null and the + python interpreter is used as test process. + """ + if cmd is None: + cmd = [PYTHON, "-c", "import time; time.sleep(3600);"] + sproc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr, stdin=stdin) + _subprocesses_started.add(sproc.pid) + return sproc + +def wait_for_pid(pid, timeout=1): + """Wait for pid to show up in the process list then return. + Used in the test suite to give time the sub process to initialize. + """ + raise_at = time.time() + timeout + while 1: + if pid in psutil.get_pid_list(): + # give it one more iteration to allow full initialization + time.sleep(0.01) + return + time.sleep(0.0001) + if time.time() >= raise_at: + raise RuntimeError("Timed out") + +def kill(pid): + """Kill a process given its PID.""" + if hasattr(os, 'kill'): + os.kill(pid, signal.SIGKILL) + else: + psutil.Process(pid).kill() + +def reap_children(search_all=False): + """Kill any subprocess started by this test suite and ensure that + no zombies stick around to hog resources and create problems when + looking for refleaks. + """ + if POSIX: + def waitpid(process): + # on posix we are free to wait for any pending process by + # passing -1 to os.waitpid() + while True: + try: + any_process = -1 + pid, status = os.waitpid(any_process, os.WNOHANG) + if pid == 0 and not process.is_running(): + break + except OSError: + if not process.is_running(): + break + else: + def waitpid(process): + # on non-posix systems we just wait for the given process + # to go away + while process.is_running(): + time.sleep(0.01) + + if search_all: + this_process = psutil.Process(os.getpid()) + pids = [x.pid for x in this_process.get_children()] + else: + pids =_subprocesses_started + while pids: + pid = pids.pop() + try: + child = psutil.Process(pid) + child.kill() + except psutil.NoSuchProcess: + pass + else: + waitpid(child) + +# we want to search through all processes before exiting +atexit.register(reap_children, search_all=True) + +def skipIf(condition, reason="", warn=False): + """Decorator which skip a test under if condition is satisfied. + This is a substitute of unittest.skipIf which is available + only in python 2.7 and 3.2. + If 'reason' argument is provided this will be printed during + tests execution. + If 'warn' is provided a RuntimeWarning will be shown when all + tests are run. + """ + def outer(fun, *args, **kwargs): + def inner(self): + if condition: + sys.stdout.write("skipped-") + sys.stdout.flush() + if warn: + objname = "%s.%s" % (self.__class__.__name__, fun.__name__) + msg = "%s was skipped" % objname + if reason: + msg += "; reason: " + repr(reason) + atexit.register(warnings.warn, msg, RuntimeWarning) + return + else: + return fun(self, *args, **kwargs) + return inner + return outer + +def skipUnless(condition, reason="", warn=False): + """Contrary of skipIf.""" + if not condition: + return skipIf(True, reason, warn) + return skipIf(False) + + +class TestCase(unittest.TestCase): + + def tearDown(self): + reap_children() + + def test_get_process_list(self): + pids = [x.pid for x in psutil.get_process_list()] + self.assertTrue(os.getpid() in pids) + self.assertTrue(0 in pids) + + def test_process_iter(self): + pids = [x.pid for x in psutil.process_iter()] + self.assertTrue(os.getpid() in pids) + self.assertTrue(0 in pids) + + def test_kill(self): + sproc = get_test_subprocess() + test_pid = sproc.pid + wait_for_pid(test_pid) + p = psutil.Process(test_pid) + name = p.name + p.kill() + sproc.wait() + self.assertFalse(psutil.pid_exists(test_pid) and name == PYTHON) + + def test_terminate(self): + sproc = get_test_subprocess() + test_pid = sproc.pid + wait_for_pid(test_pid) + p = psutil.Process(test_pid) + name = p.name + p.terminate() + sproc.wait() + self.assertFalse(psutil.pid_exists(test_pid) and name == PYTHON) + + def test_send_signal(self): + if POSIX: + sig = signal.SIGKILL + else: + sig = signal.SIGTERM + sproc = get_test_subprocess() + test_pid = sproc.pid + p = psutil.Process(test_pid) + name = p.name + p.send_signal(sig) + sproc.wait() + self.assertFalse(psutil.pid_exists(test_pid) and name == PYTHON) + + def test_TOTAL_PHYMEM(self): + x = psutil.TOTAL_PHYMEM + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x > 0) + + def test_used_phymem(self): + x = psutil.used_phymem() + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x > 0) + + def test_avail_phymem(self): + x = psutil.avail_phymem() + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x > 0) + + def test_total_virtmem(self): + x = psutil.total_virtmem() + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x >= 0) + + def test_used_virtmem(self): + x = psutil.used_virtmem() + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x >= 0) + + def test_avail_virtmem(self): + x = psutil.avail_virtmem() + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x >= 0) + + @skipUnless(LINUX) + def test_cached_phymem(self): + x = psutil.cached_phymem() + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x >= 0) + + @skipUnless(LINUX) + def test_phymem_buffers(self): + x = psutil.phymem_buffers() + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x >= 0) + + def test_system_cpu_times(self): + total = 0 + times = psutil.cpu_times() + self.assertTrue(isinstance(times, psutil.CPUTimes)) + sum(times) + for cp_time in times: + self.assertTrue(isinstance(cp_time, float)) + self.assertTrue(cp_time >= 0.0) + total += cp_time + # test CPUTimes's __iter__ and __str__ implementation + self.assertEqual(total, sum(times)) + str(times) + + def test_system_cpu_times2(self): + t1 = sum(psutil.cpu_times()) + time.sleep(0.1) + t2 = sum(psutil.cpu_times()) + difference = t2 - t1 + if not difference >= 0.05: + self.fail("difference %s" % difference) + + def test_system_cpu_percent(self): + psutil.cpu_percent(interval=0.001) + psutil.cpu_percent(interval=0.001) + for x in xrange(1000): + percent = psutil.cpu_percent(interval=None) + self.assertTrue(isinstance(percent, float)) + self.assertTrue(percent >= 0.0) + self.assertTrue(percent <= 100.0) + + def test_process_cpu_percent(self): + p = psutil.Process(os.getpid()) + p.get_cpu_percent(interval=0.001) + p.get_cpu_percent(interval=0.001) + for x in xrange(100): + percent = p.get_cpu_percent(interval=None) + self.assertTrue(isinstance(percent, float)) + self.assertTrue(percent >= 0.0) + self.assertTrue(percent <= 100.0) + + def test_get_process_cpu_times(self): + times = psutil.Process(os.getpid()).get_cpu_times() + self.assertTrue((times.user > 0.0) or (times.system > 0.0)) + # make sure returned values can be pretty printed with strftime + time.strftime("%H:%M:%S", time.localtime(times.user)) + time.strftime("%H:%M:%S", time.localtime(times.system)) + + # Test Process.cpu_times() against os.times() + # os.times() is broken on Python 2.6 + # http://bugs.python.org/issue1040026 + # XXX fails on OSX: not sure if it's for os.times(). We should + # try this with Python 2.7 and re-enable the test. + + @skipUnless(sys.version_info > (2, 6, 1) and not OSX) + def test_get_process_cpu_times2(self): + user_time, kernel_time = psutil.Process(os.getpid()).get_cpu_times() + utime, ktime = os.times()[:2] + + # Use os.times()[:2] as base values to compare our results + # using a tolerance of +/- 0.1 seconds. + # It will fail if the difference between the values is > 0.1s. + if (max([user_time, utime]) - min([user_time, utime])) > 0.1: + self.fail("expected: %s, found: %s" %(utime, user_time)) + + if (max([kernel_time, ktime]) - min([kernel_time, ktime])) > 0.1: + self.fail("expected: %s, found: %s" %(ktime, kernel_time)) + + def test_create_time(self): + sproc = get_test_subprocess() + now = time.time() + wait_for_pid(sproc.pid) + p = psutil.Process(sproc.pid) + create_time = p.create_time + + # Use time.time() as base value to compare our result using a + # tolerance of +/- 1 second. + # It will fail if the difference between the values is > 2s. + difference = abs(create_time - now) + if difference > 2: + self.fail("expected: %s, found: %s, difference: %s" + % (now, create_time, difference)) + + # make sure returned value can be pretty printed with strftime + time.strftime("%Y %m %d %H:%M:%S", time.localtime(p.create_time)) + + def test_get_num_threads(self): + p = psutil.Process(os.getpid()) + numt1 = p.get_num_threads() + if not WINDOWS and not OSX: + # test is unreliable on Windows and OS X + # NOTE: sleep(1) is too long for OS X, works with sleep(.5) + self.assertEqual(numt1, 1) + t = threading.Thread(target=lambda:time.sleep(1)) + t.start() + numt2 = p.get_num_threads() + if WINDOWS: + self.assertTrue(numt2 > numt1) + else: + self.assertEqual(numt2, 2) + + def test_get_memory_info(self): + p = psutil.Process(os.getpid()) + + # step 1 - get a base value to compare our results + rss1, vms1 = p.get_memory_info() + percent1 = p.get_memory_percent() + self.assertTrue(rss1 > 0) + self.assertTrue(vms1 > 0) + + # step 2 - allocate some memory + memarr = [None] * 1500000 + + rss2, vms2 = p.get_memory_info() + percent2 = p.get_memory_percent() + # make sure that the memory usage bumped up + self.assertTrue(rss2 > rss1) + self.assertTrue(vms2 >= vms1) # vms might be equal + self.assertTrue(percent2 > percent1) + del memarr + + def test_get_memory_percent(self): + p = psutil.Process(os.getpid()) + self.assertTrue(p.get_memory_percent() > 0.0) + + def test_pid(self): + sproc = get_test_subprocess() + self.assertEqual(psutil.Process(sproc.pid).pid, sproc.pid) + + def test_eq(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + self.assertTrue(psutil.Process(sproc.pid) == psutil.Process(sproc.pid)) + + def test_is_running(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + p = psutil.Process(sproc.pid) + self.assertTrue(p.is_running()) + psutil.Process(sproc.pid).kill() + sproc.wait() + self.assertFalse(p.is_running()) + + def test_pid_exists(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + self.assertTrue(psutil.pid_exists(sproc.pid)) + psutil.Process(sproc.pid).kill() + sproc.wait() + self.assertFalse(psutil.pid_exists(sproc.pid)) + self.assertFalse(psutil.pid_exists(-1)) + + def test_exe(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + self.assertEqual(psutil.Process(sproc.pid).exe, PYTHON) + for p in psutil.process_iter(): + try: + exe = p.exe + except psutil.Error: + continue + if not exe: + continue + if not os.path.exists(exe): + self.fail("%s does not exist (pid=%s, name=%s, cmdline=%s)" \ + % (repr(exe), p.pid, p.name, p.cmdline)) + if hasattr(os, 'access') and hasattr(os, "X_OK"): + if not os.access(p.exe, os.X_OK): + self.fail("%s is not executable (pid=%s, name=%s, cmdline=%s)" \ + % (repr(p.exe), p.pid, p.name, p.cmdline)) + + def test_path(self): + proc = psutil.Process(os.getpid()) + warnings.filterwarnings("error") + try: + self.assertRaises(DeprecationWarning, getattr, proc, 'path') + finally: + warnings.resetwarnings() + + def test_cmdline(self): + sproc = get_test_subprocess([PYTHON, "-E"]) + wait_for_pid(sproc.pid) + self.assertEqual(psutil.Process(sproc.pid).cmdline, [PYTHON, "-E"]) + + def test_name(self): + sproc = get_test_subprocess(PYTHON) + wait_for_pid(sproc.pid) + if OSX: + self.assertEqual(psutil.Process(sproc.pid).name, "Python") + else: + self.assertEqual(psutil.Process(sproc.pid).name, os.path.basename(PYTHON)) + + def test_uid(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + uid = psutil.Process(sproc.pid).uid + if hasattr(os, 'getuid'): + self.assertEqual(uid, os.getuid()) + else: + # On those platforms where UID doesn't make sense (Windows) + # we expect it to be -1 + self.assertEqual(uid, -1) + + def test_gid(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + gid = psutil.Process(sproc.pid).gid + if hasattr(os, 'getgid'): + self.assertEqual(gid, os.getgid()) + else: + # On those platforms where GID doesn't make sense (Windows) + # we expect it to be -1 + self.assertEqual(gid, -1) + + def test_username(self): + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + if POSIX: + import pwd + user = pwd.getpwuid(p.uid).pw_name + self.assertEqual(p.username, user) + elif WINDOWS: + expected_username = os.environ['USERNAME'] + expected_domain = os.environ['USERDOMAIN'] + domain, username = p.username.split('\\') + self.assertEqual(domain, expected_domain) + self.assertEqual(username, expected_username) + else: + p.username + + @skipUnless(WINDOWS or LINUX) + def test_getcwd(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + p = psutil.Process(sproc.pid) + self.assertEqual(p.getcwd(), os.getcwd()) + + @skipUnless(WINDOWS or LINUX) + def test_getcwd_2(self): + cmd = [PYTHON, "-c", "import os, time; os.chdir('..'); time.sleep(10)"] + sproc = get_test_subprocess(cmd) + wait_for_pid(sproc.pid) + p = psutil.Process(sproc.pid) + time.sleep(0.1) + expected_dir = os.path.dirname(os.getcwd()) + self.assertEqual(p.getcwd(), expected_dir) + + def test_get_open_files(self): + thisfile = os.path.join(os.getcwd(), __file__) + + # current process + p = psutil.Process(os.getpid()) + files = p.get_open_files() + self.assertFalse(thisfile in files) + f = open(thisfile, 'r') + filenames = [x.path for x in p.get_open_files()] + self.assertTrue(thisfile in filenames) + f.close() + for file in filenames: + self.assertTrue(os.path.isfile(file)) + + # another process + cmdline = "import time; f = open(r'%s', 'r'); time.sleep(100);" % thisfile + sproc = get_test_subprocess([PYTHON, "-c", cmdline]) + wait_for_pid(sproc.pid) + time.sleep(0.1) + p = psutil.Process(sproc.pid) + filenames = [x.path for x in p.get_open_files()] + self.assertTrue(thisfile in filenames) + for file in filenames: + self.assertTrue(os.path.isfile(file)) + # all processes + for proc in psutil.process_iter(): + try: + files = proc.get_open_files() + except psutil.Error: + pass + else: + for file in filenames: + self.assertTrue(os.path.isfile(file)) + + def test_get_open_files2(self): + # test fd and path fields + fileobj = open(os.path.join(os.getcwd(), __file__), 'r') + p = psutil.Process(os.getpid()) + for path, fd in p.get_open_files(): + if path == fileobj.name or fd == fileobj.fileno(): + break + else: + self.fail("no file found; files=%s" % repr(p.get_open_files())) + self.assertEqual(path, fileobj.name) + if WINDOWS: + self.assertEqual(fd, -1) + else: + self.assertEqual(fd, fileobj.fileno()) + # test positions + ntuple = p.get_open_files()[0] + self.assertEqual(ntuple[0], ntuple.path) + self.assertEqual(ntuple[1], ntuple.fd) + # test file is gone + fileobj.close() + self.assertTrue(fileobj.name not in p.get_open_files()) + + @skipUnless(SUPPORT_CONNECTIONS, warn=1) + def test_get_connections(self): + arg = "import socket, time;" \ + "s = socket.socket();" \ + "s.bind(('127.0.0.1', 0));" \ + "s.listen(1);" \ + "conn, addr = s.accept();" \ + "time.sleep(100);" + sproc = get_test_subprocess([PYTHON, "-c", arg]) + time.sleep(0.1) + p = psutil.Process(sproc.pid) + cons = p.get_connections() + self.assertTrue(len(cons) == 1) + con = cons[0] + self.assertEqual(con.family, socket.AF_INET) + self.assertEqual(con.type, socket.SOCK_STREAM) + self.assertEqual(con.status, "LISTEN") + ip, port = con.local_address + self.assertEqual(ip, '127.0.0.1') + self.assertEqual(con.remote_address, ()) + if WINDOWS: + self.assertEqual(con.fd, -1) + else: + self.assertTrue(con.fd > 0) + # test positions + self.assertEqual(con[0], con.fd) + self.assertEqual(con[1], con.family) + self.assertEqual(con[2], con.type) + self.assertEqual(con[3], con.local_address) + self.assertEqual(con[4], con.remote_address) + self.assertEqual(con[5], con.status) + + @skipUnless(hasattr(socket, "fromfd") and not WINDOWS) + def test_connection_fromfd(self): + sock = socket.socket() + sock.bind(('127.0.0.1', 0)) + sock.listen(1) + p = psutil.Process(os.getpid()) + for conn in p.get_connections(): + if conn.fd == sock.fileno(): + break + else: + sock.close() + self.fail("couldn't find socket fd") + dupsock = socket.fromfd(conn.fd, conn.family, conn.type) + try: + self.assertEqual(dupsock.getsockname(), conn.local_address) + self.assertNotEqual(sock.fileno(), dupsock.fileno()) + finally: + sock.close() + dupsock.close() + + @skipUnless(SUPPORT_CONNECTIONS, warn=1) + def test_get_connections_all(self): + + def check_address(addr, family): + if not addr: + return + ip, port = addr + self.assertTrue(isinstance(port, int)) + if family == socket.AF_INET: + ip = map(int, ip.split('.')) + self.assertTrue(len(ip) == 4) + for num in ip: + self.assertTrue(0 <= num <= 255) + self.assertTrue(0 <= port <= 65535) + + def supports_ipv6(): + if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"): + return False + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.bind(("::1", 0)) + except (socket.error, socket.gaierror): + return False + else: + sock.close() + return True + + # all values are supposed to match Linux's tcp_states.h states + # table across all platforms. + valid_states = ["ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", + "FIN_WAIT2", "TIME_WAIT", "CLOSE", "CLOSE_WAIT", + "LAST_ACK", "LISTEN", "CLOSING", ""] + + tcp_template = "import socket;" \ + "s = socket.socket($family, socket.SOCK_STREAM);" \ + "s.bind(('$addr', 0));" \ + "s.listen(1);" \ + "conn, addr = s.accept();" + + udp_template = "import socket, time;" \ + "s = socket.socket($family, socket.SOCK_DGRAM);" \ + "s.bind(('$addr', 0));" \ + "time.sleep(100);" + + from string import Template + tcp4_template = Template(tcp_template).substitute(family=socket.AF_INET, + addr="127.0.0.1") + udp4_template = Template(udp_template).substitute(family=socket.AF_INET, + addr="127.0.0.1") + tcp6_template = Template(tcp_template).substitute(family=socket.AF_INET6, + addr="::1") + udp6_template = Template(udp_template).substitute(family=socket.AF_INET6, + addr="::1") + + # launch various subprocess instantiating a socket of various + # families and tupes to enrich psutil results + tcp4_proc = get_test_subprocess([PYTHON, "-c", tcp4_template]) + udp4_proc = get_test_subprocess([PYTHON, "-c", udp4_template]) + if supports_ipv6(): + tcp6_proc = get_test_subprocess([PYTHON, "-c", tcp6_template]) + udp6_proc = get_test_subprocess([PYTHON, "-c", udp6_template]) + else: + tcp6_proc = None + udp6_proc = None + + time.sleep(0.1) + this_proc = psutil.Process(os.getpid()) + children_pids = [p.pid for p in this_proc.get_children()] + for p in psutil.process_iter(): + try: + cons = p.get_connections() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + else: + for conn in cons: + self.assertTrue(conn.type in (socket.SOCK_STREAM, + socket.SOCK_DGRAM)) + self.assertTrue(conn.family in (socket.AF_INET, + socket.AF_INET6)) + check_address(conn.local_address, conn.family) + check_address(conn.remote_address, conn.family) + if conn.status not in valid_states: + self.fail("%s is not a valid status" %conn.status) + # actually try to bind the local socket; ignore IPv6 + # sockets as their address might be represented as + # an IPv4-mapped-address (e.g. "::127.0.0.1") + # and that's rejected by bind() + if conn.family == socket.AF_INET: + s = socket.socket(conn.family, conn.type) + s.bind((conn.local_address[0], 0)) + s.close() + + if not WINDOWS and hasattr(socket, 'fromfd'): + try: + dupsock = socket.fromfd(conn.fd, conn.family, + conn.type) + except (socket.error, OSError), err: + if err.args[0] == errno.EBADF: + continue + else: + raise + self.assertEqual(dupsock.family, conn.family) + self.assertEqual(dupsock.type, conn.type) + + # check matches against subprocesses + if p.pid in children_pids: + self.assertTrue(len(cons) == 1) + conn = cons[0] + # TCP v4 + if p.pid == tcp4_proc.pid: + self.assertEqual(conn.family, socket.AF_INET) + self.assertEqual(conn.type, socket.SOCK_STREAM) + self.assertEqual(conn.local_address[0], "127.0.0.1") + self.assertEqual(conn.remote_address, ()) + self.assertEqual(conn.status, "LISTEN") + # UDP v4 + elif p.pid == udp4_proc.pid: + self.assertEqual(conn.family, socket.AF_INET) + self.assertEqual(conn.type, socket.SOCK_DGRAM) + self.assertEqual(conn.local_address[0], "127.0.0.1") + self.assertEqual(conn.remote_address, ()) + self.assertEqual(conn.status, "") + # TCP v6 + elif p.pid == getattr(tcp6_proc, "pid", None): + self.assertEqual(conn.family, socket.AF_INET6) + self.assertEqual(conn.type, socket.SOCK_STREAM) + self.assertEqual(conn.local_address[0], "::1") + self.assertEqual(conn.remote_address, ()) + self.assertEqual(conn.status, "LISTEN") + # UDP v6 + elif p.pid == getattr(udp6_proc, "pid", None): + self.assertEqual(conn.family, socket.AF_INET6) + self.assertEqual(conn.type, socket.SOCK_DGRAM) + self.assertEqual(conn.local_address[0], "::1") + self.assertEqual(conn.remote_address, ()) + self.assertEqual(conn.status, "") + + def test_parent_ppid(self): + this_parent = os.getpid() + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + self.assertEqual(p.ppid, this_parent) + self.assertEqual(p.parent.pid, this_parent) + # no other process is supposed to have us as parent + for p in psutil.process_iter(): + if p.pid == sproc.pid: + continue + self.assertTrue(p.ppid != this_parent) + + def test_get_children(self): + p = psutil.Process(os.getpid()) + self.assertEqual(p.get_children(), []) + sproc = get_test_subprocess() + children = p.get_children() + self.assertEqual(len(children), 1) + self.assertEqual(children[0].pid, sproc.pid) + self.assertEqual(children[0].ppid, os.getpid()) + + def test_suspend_resume(self): + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.suspend() + p.resume() + + def test_get_pid_list(self): + plist = [x.pid for x in psutil.get_process_list()] + pidlist = psutil.get_pid_list() + self.assertEqual(plist.sort(), pidlist.sort()) + # make sure every pid is unique + self.assertEqual(len(pidlist), len(set(pidlist))) + + def test_test(self): + # test for psutil.test() function + stdout = sys.stdout + sys.stdout = DEVNULL + try: + psutil.test() + finally: + sys.stdout = stdout + + def test_types(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + p = psutil.Process(sproc.pid) + self.assert_(isinstance(p.pid, int)) + self.assert_(isinstance(p.ppid, int)) + self.assert_(isinstance(p.parent, psutil.Process)) + self.assert_(isinstance(p.name, str)) + if self.__class__.__name__ != "LimitedUserTestCase": + self.assert_(isinstance(p.exe, str)) + self.assert_(isinstance(p.cmdline, list)) + self.assert_(isinstance(p.uid, int)) + self.assert_(isinstance(p.gid, int)) + self.assert_(isinstance(p.create_time, float)) + self.assert_(isinstance(p.username, (unicode, str))) + if hasattr(p, 'getcwd'): + if not POSIX and self.__class__.__name__ != "LimitedUserTestCase": + self.assert_(isinstance(p.getcwd(), str)) + if not POSIX and self.__class__.__name__ != "LimitedUserTestCase": + self.assert_(isinstance(p.get_open_files(), list)) + for path, fd in p.get_open_files(): + self.assert_(isinstance(path, (unicode, str))) + self.assert_(isinstance(fd, int)) + if not POSIX and self.__class__.__name__ != "LimitedUserTestCase" \ + and SUPPORT_CONNECTIONS: + self.assert_(isinstance(p.get_connections(), list)) + self.assert_(isinstance(p.is_running(), bool)) + if not OSX or self.__class__.__name__ != "LimitedUserTestCase": + self.assert_(isinstance(p.get_cpu_times(), tuple)) + self.assert_(isinstance(p.get_cpu_times()[0], float)) + self.assert_(isinstance(p.get_cpu_times()[1], float)) + self.assert_(isinstance(p.get_cpu_percent(0), float)) + self.assert_(isinstance(p.get_memory_info(), tuple)) + self.assert_(isinstance(p.get_memory_info()[0], int)) + self.assert_(isinstance(p.get_memory_info()[1], int)) + self.assert_(isinstance(p.get_memory_percent(), float)) + self.assert_(isinstance(p.get_num_threads(), int)) + self.assert_(isinstance(psutil.get_process_list(), list)) + self.assert_(isinstance(psutil.get_process_list()[0], psutil.Process)) + self.assert_(isinstance(psutil.process_iter(), types.GeneratorType)) + self.assert_(isinstance(psutil.process_iter().next(), psutil.Process)) + self.assert_(isinstance(psutil.get_pid_list(), list)) + self.assert_(isinstance(psutil.get_pid_list()[0], int)) + self.assert_(isinstance(psutil.pid_exists(1), bool)) + + def test_invalid_pid(self): + self.assertRaises(ValueError, psutil.Process, "1") + self.assertRaises(ValueError, psutil.Process, None) + # Refers to Issue #12 + self.assertRaises(psutil.NoSuchProcess, psutil.Process, -1) + + def test_zombie_process(self): + # Test that NoSuchProcess exception gets raised in the event the + # process dies after we create the Process object. + # Example: + # >>> proc = Process(1234) + # >>> time.sleep(5) # time-consuming task, process dies in meantime + # >>> proc.name + # Refers to Issue #15 + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.kill() + sproc.wait() + + self.assertRaises(psutil.NoSuchProcess, getattr, p, "ppid") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "parent") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "name") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "exe") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "cmdline") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "uid") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "gid") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "create_time") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "username") + if hasattr(p, 'getcwd'): + self.assertRaises(psutil.NoSuchProcess, p.getcwd) + self.assertRaises(psutil.NoSuchProcess, p.get_open_files) + self.assertRaises(psutil.NoSuchProcess, p.get_connections) + self.assertRaises(psutil.NoSuchProcess, p.suspend) + self.assertRaises(psutil.NoSuchProcess, p.resume) + self.assertRaises(psutil.NoSuchProcess, p.kill) + self.assertRaises(psutil.NoSuchProcess, p.terminate) + self.assertRaises(psutil.NoSuchProcess, p.send_signal, signal.SIGTERM) + self.assertRaises(psutil.NoSuchProcess, p.get_cpu_times) + self.assertRaises(psutil.NoSuchProcess, p.get_cpu_percent, 0) + self.assertRaises(psutil.NoSuchProcess, p.get_memory_info) + self.assertRaises(psutil.NoSuchProcess, p.get_memory_percent) + self.assertRaises(psutil.NoSuchProcess, p.get_children) + self.assertRaises(psutil.NoSuchProcess, p.get_num_threads) + self.assertFalse(p.is_running()) + + def test__str__(self): + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + self.assertTrue(str(sproc.pid) in str(p)) + self.assertTrue(os.path.basename(PYTHON) in str(p)) + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.kill() + sproc.wait() + self.assertTrue(str(sproc.pid) in str(p)) + self.assertTrue("terminated" in str(p)) + + def test_fetch_all(self): + valid_procs = 0 + attrs = ['__str__', 'create_time', 'username', 'getcwd', 'get_cpu_times', + 'get_memory_info', 'get_memory_percent', 'get_open_files', + 'get_num_threads'] + for p in psutil.process_iter(): + for attr in attrs: + # skip slow Python implementation; we're reasonably sure + # it works anyway + if POSIX and attr == 'get_open_files': + continue + try: + attr = getattr(p, attr, None) + if attr is not None and callable(attr): + attr() + valid_procs += 1 + except (psutil.NoSuchProcess, psutil.AccessDenied), err: + self.assertEqual(err.pid, p.pid) + if err.name: + self.assertEqual(err.name, p.name) + self.assertTrue(str(err)) + self.assertTrue(err.msg) + except: + trace = traceback.format_exc() + self.fail('Exception raised for method %s, pid %s:\n%s' + %(attr, p.pid, trace)) + + # we should always have a non-empty list, not including PID 0 etc. + # special cases. + self.assertTrue(valid_procs > 0) + + def test_pid_0(self): + # Process(0) is supposed to work on all platforms even if with + # some differences + p = psutil.Process(0) + if WINDOWS: + self.assertEqual(p.name, 'System Idle Process') + elif LINUX: + self.assertEqual(p.name, 'sched') + elif BSD: + self.assertEqual(p.name, 'swapper') + elif OSX: + self.assertEqual(p.name, 'kernel_task') + + if os.name == 'posix': + self.assertEqual(p.uid, 0) + self.assertEqual(p.gid, 0) + else: + self.assertEqual(p.uid, -1) + self.assertEqual(p.gid, -1) + + self.assertTrue(p.ppid in (0, 1)) + self.assertEqual(p.exe, "") + self.assertEqual(p.cmdline, []) + # this can either raise AD (Win) or return 0 (UNIX) + try: + self.assertTrue(p.get_num_threads() in (0, 1)) + except psutil.AccessDenied: + pass + + if OSX : #and os.geteuid() != 0: + self.assertRaises(psutil.AccessDenied, p.get_memory_info) + self.assertRaises(psutil.AccessDenied, p.get_cpu_times) + else: + p.get_memory_info() + + # username property + if POSIX: + self.assertEqual(p.username, 'root') + elif WINDOWS: + self.assertEqual(p.username, 'NT AUTHORITY\\SYSTEM') + else: + p.username + + # PID 0 is supposed to be available on all platforms + self.assertTrue(0 in psutil.get_pid_list()) + self.assertTrue(psutil.pid_exists(0)) + + # --- OS specific tests + + +if hasattr(os, 'getuid'): + class LimitedUserTestCase(TestCase): + """Repeat the previous tests by using a limited user. + Executed only on UNIX and only if the user who run the test script + is root. + """ + # the uid/gid the test suite runs under + PROCESS_UID = os.getuid() + PROCESS_GID = os.getgid() + + def setUp(self): + os.setegid(1000) + os.seteuid(1000) + TestCase.setUp(self) + + def tearDown(self): + os.setegid(self.PROCESS_UID) + os.seteuid(self.PROCESS_GID) + TestCase.tearDown(self) + + def test_path(self): + # DeprecationWarning is only raised once + pass + + # overridden tests known to raise AccessDenied when run + # as limited user on different platforms + + if LINUX: + + def test_getcwd(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_getcwd, self) + + def test_getcwd_2(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_getcwd_2, self) + + def test_get_open_files(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_get_open_files, self) + + def test_get_connections(self): + pass + + def test_exe(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_exe, self) + + if BSD: + + def test_get_open_files(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_get_open_files, self) + + def test_get_open_files2(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_get_open_files, self) + + def test_get_connections(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_get_connections, self) + + def test_connection_fromfd(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_connection_fromfd, self) + + +def test_main(): + tests = [] + test_suite = unittest.TestSuite() + + tests.append(TestCase) + + if hasattr(os, 'getuid'): + if os.getuid() == 0: + tests.append(LimitedUserTestCase) + else: + atexit.register(warnings.warn, "Couldn't run limited user tests (" + "super-user privileges are required)", RuntimeWarning) + + if POSIX: + from _posix import PosixSpecificTestCase + tests.append(PosixSpecificTestCase) + + # import the specific platform test suite + if LINUX: + from _linux import LinuxSpecificTestCase as stc + elif WINDOWS: + from _windows import WindowsSpecificTestCase as stc + elif OSX: + from _osx import OSXSpecificTestCase as stc + elif BSD: + from _bsd import BSDSpecificTestCase as stc + tests.append(stc) + + for test_class in tests: + test_suite.addTest(unittest.makeSuite(test_class)) + + unittest.TextTestRunner(verbosity=2).run(test_suite) + DEVNULL.close() + +if __name__ == '__main__': + test_main() + + |