Bug 783727 - Part 4: Add psutil Python package; r=glandium
authorGregory Szorc <gps@mozilla.com>
Wed, 19 Sep 2012 11:20:16 -0700
changeset 107534 11374d8585f65fafba354be478e4e7b624d975c5
parent 107533 faf1c66154f43863e39c60db9b4919579d33c515
child 107535 d5720a3513ca42ffd136e9f5a5f1d9b4ecebcc60
push id1100
push userdcamp@campd.org
push dateThu, 20 Sep 2012 22:53:07 +0000
treeherderfx-team@f731fa718465 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersglandium
bugs783727
milestone18.0a1
Bug 783727 - Part 4: Add psutil Python package; r=glandium Version 0.6.1 obtained from https://psutil.googlecode.com/files/psutil-0.6.1.tar.gz
python/psutil/CREDITS
python/psutil/HISTORY
python/psutil/INSTALL
python/psutil/LICENSE
python/psutil/MANIFEST.in
python/psutil/PKG-INFO
python/psutil/README
python/psutil/examples/disk_usage.py
python/psutil/examples/free.py
python/psutil/examples/iotop.py
python/psutil/examples/killall.py
python/psutil/examples/meminfo.py
python/psutil/examples/netstat.py
python/psutil/examples/nettop.py
python/psutil/examples/pmap.py
python/psutil/examples/process_detail.py
python/psutil/examples/top.py
python/psutil/examples/who.py
python/psutil/psutil/__init__.py
python/psutil/psutil/_common.py
python/psutil/psutil/_compat.py
python/psutil/psutil/_psbsd.py
python/psutil/psutil/_pslinux.py
python/psutil/psutil/_psmswindows.py
python/psutil/psutil/_psosx.py
python/psutil/psutil/_psposix.py
python/psutil/psutil/_psutil_bsd.c
python/psutil/psutil/_psutil_bsd.h
python/psutil/psutil/_psutil_common.c
python/psutil/psutil/_psutil_common.h
python/psutil/psutil/_psutil_linux.c
python/psutil/psutil/_psutil_linux.h
python/psutil/psutil/_psutil_mswindows.c
python/psutil/psutil/_psutil_mswindows.h
python/psutil/psutil/_psutil_osx.c
python/psutil/psutil/_psutil_osx.h
python/psutil/psutil/_psutil_posix.c
python/psutil/psutil/_psutil_posix.h
python/psutil/psutil/arch/bsd/process_info.c
python/psutil/psutil/arch/bsd/process_info.h
python/psutil/psutil/arch/mswindows/ntextapi.h
python/psutil/psutil/arch/mswindows/process_handles.c
python/psutil/psutil/arch/mswindows/process_handles.h
python/psutil/psutil/arch/mswindows/process_info.c
python/psutil/psutil/arch/mswindows/process_info.h
python/psutil/psutil/arch/mswindows/security.c
python/psutil/psutil/arch/mswindows/security.h
python/psutil/psutil/arch/osx/process_info.c
python/psutil/psutil/arch/osx/process_info.h
python/psutil/psutil/error.py
python/psutil/setup.cfg
python/psutil/setup.py
python/psutil/test/_bsd.py
python/psutil/test/_linux.py
python/psutil/test/_osx.py
python/psutil/test/_posix.py
python/psutil/test/_windows.py
python/psutil/test/test_memory_leaks.py
python/psutil/test/test_psutil.py
new file mode 100644
--- /dev/null
+++ b/python/psutil/CREDITS
@@ -0,0 +1,119 @@
+
+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: Jeremy Whitlock
+E: jcscoobyrs@gmail.com
+I: 125, 150, 174, 206
+
+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: david.daeschler@gmail.com
+D: initial design/bootstrap and continuing bug fixes
+
+N: cjgohlke
+E: cjgohlke@gmail.com
+D: Windows 64 bit support
+I: 107
+
+N: Jeffery Kline
+E: jeffery.kline@gmail.com
+I: 130
+
+N: Grabriel Monnerat
+E: gabrielmonnerat@gmail.com
+I: 146
+
+N: Philip Roberts
+E: philip.roberts@gmail.com
+I: 168
+
+N: jcscoobyrs
+E: jcscoobyrs@gmail.com
+I: 125
+
+N: Sandro Tosi
+E: sandro.tosi@gmail.com
+I: 200, 201
+
+N: Andrew Colin
+E: andrew.colin@gmail.com
+I: 248
+
+N: Amoser
+E: amoser@google.com
+I: 266, 267
+
+N: Matthew Grant
+E: matthewgrant5@gmail.com
+I: 271
+
+N: oweidner
+E: oweidner@cct.lsu.edu
+I: 275
+
+N: Tarek Ziade
+E: ziade.tarek
+I: 281
+
+N: Luca Cipriani
+C: Turin, Italy
+E: luca.opensource@gmail.com
+I: 278
+
+N: Maciej Lach,
+E: maciej.lach@gmail.com
+I: 294
+
+N: James Pye
+E: james.pye@gmail.com
+I: 305, 306
+
+N: Stanchev Emil
+E: stanchev.emil
+I: 314
+
+N: Kim Gräsman
+E: kim.grasman@gmail.com
+D: ...also kindly donated some money.
+I: 316
+
+N: Riccardo Murri
+C: Italy
+I: 318
new file mode 100644
--- /dev/null
+++ b/python/psutil/HISTORY
@@ -0,0 +1,463 @@
+Bug tracker at http://code.google.com/p/psutil/issues
+
+0.6.1 - 2012-08-16
+------------------
+
+NEW FEATURES
+
+ * Issue 316: process cmdline property now makes a better job at guessing the
+   process executable from the cmdline.
+
+BUG FIXES
+
+ * Issue 316: process exe was resolved in case it was a symlink.
+ * Issue 318: python 2.4 compatibility was broken.
+
+API CHANGES
+
+ * process exe can now return an empty string instead of raising AccessDenied.
+ * process exe is no longer resolved in case it's a symlink.
+
+
+0.6.0 - 2012-08-13
+------------------
+
+NEW FEATURES
+
+ * Issue 216: (UNIX) get_connections() UNIX sockets support.
+ * Issue 220: (BSD) get_connections() has been rewritten in C and no longer
+   requires lsof.
+ * Issue 222: (OSX) add support for process cwd.
+ * Issue 261: process extended memory info.
+ * Issue 295: (OSX) process executable path is now determined by asking the OS
+   instead of being guessed from process cmdline.
+ * Issue 297: (OSX) the Process methods below were always raising AccessDenied
+   for any process except the current one. Now this is no longer true. Also they
+   are 2.5x faster.
+    - name
+    - get_memory_info()
+    - get_memory_percent()
+    - get_cpu_times()
+    - get_cpu_percent()
+    - get_num_threads()
+ * Issue 300: examples/pmap.py script.
+ * Issue 301: process_iter() now yields processes sorted by their PIDs.
+ * Issue 302: process number of voluntary and involuntary context switches.
+ * Issue 303: (Windows) the Process methods below were always raising
+   AccessDenied for any process not owned by current user. Now this is no longer
+   true:
+    - create_time
+    - get_cpu_times()
+    - get_cpu_percent()
+    - get_memory_info()
+    - get_memory_percent()
+    - get_num_handles()
+    - get_io_counters()
+ * Issue 305: add examples/netstat.py script.
+ * Issue 311: system memory functions has been refactorized and rewritten and
+   now provide a more detailed and consistent representation of the system
+   memory.
+   New psutil.virtual_memory() function provides the following memory amounts:
+    - total
+    - available
+    - percent
+    - used
+    - active (UNIX)
+    - inactive (UNIX)
+    - buffers (BSD, Linux)
+    - cached (BSD, OSX)
+    - wired (OSX, BSD)
+    - shared (BSD)
+   New psutil.swap_memory() provides:
+    - total
+    - used
+    - free
+    - percent
+    - sin (no. of bytes the system has swapped in from disk (cumulative))
+    - sout (no. of bytes the system has swapped out from disk (cumulative))
+   All old memory-related functions are deprecated.
+   Also two new example scripts were added:  free.py and meminfo.py.
+ * Issue 312: psutil.network_io_counters() namedtuple includes 4 new fields:
+   errin, errout dropin and dropout, reflecting the number of packets dropped
+   and with errors.
+
+BUGFIXES
+
+ * Issue 298: (OSX - BSD) memory leak in get_num_fds().
+ * Issue 299: potential memory leak every time PyList_New(0) is used.
+ * Issue 303: (Windows) potential heap corruption in get_num_threads() and
+   get_status() Process methods.
+ * Issue 305: (BSD) psutil can't compile on FreeBSD 9 due to removal of utmp.h.
+ * Issue 306: at C level, errors are not checked when invoking Py* functions
+   which create or manipulate Python objects leading to potential memory related
+   errors and/or segmentation faults.
+ * Issue 307: (BSD) values returned by psutil.network_io_counters() are wrong.
+ * Issue 308: (BSD / Windows) psutil.virtmem_usage() wasn't actually returning
+   information about swap memory usage as it was supposed to do. It does now.
+ * Issue 309: get_open_files() might not return files which can not be accessed
+   due to limited permissions. AccessDenied is now raised instead.
+
+API CHANGES
+
+ * psutil.phymem_usage() is deprecated             (use psutil.virtual_memory())
+ * psutil.virtmem_usage() is deprecated            (use psutil.swap_memory())
+ * psutil.phymem_buffers() on Linux is deprecated  (use psutil.virtual_memory())
+ * psutil.cached_phymem() on Linux is deprecated   (use psutil.virtual_memory())
+ * (Windows and BSD) psutil.virtmem_usage() now returns information about swap
+   memory instead of virtual memory.
+
+
+0.5.1 - 2012-06-29
+------------------
+
+NEW FEATURES
+
+ * Issue 293: (Windows) process executable path is now determined by asking the
+   OS instead of being guessed from process cmdline.
+
+BUGFIXES
+
+ * Issue 292: (Linux) race condition in process files/threads/connections.
+ * Issue 294: (Windows) Process CPU affinity is only able to set CPU #0.
+
+
+0.5.0 - 2012-06-27
+------------------
+
+NEW FEATURES
+
+ * Issue #195: (Windows) number of handles opened by process.
+ * Issue #209: psutil.disk_partitions() now provides also mount options.
+ * Issue #229: list users currently connected on the system (psutil.get_users()).
+ * Issue #238: (Linux, Windows) process CPU affinity (get and set).
+ * Issue #242: Process.get_children(recursive=True): return all process
+   descendants.
+ * Issue #245: (POSIX) Process.wait() incrementally consumes less CPU cycles.
+ * Issue #257: (Windows) removed Windows 2000 support.
+ * Issue #258: (Linux) Process.get_memory_info() is now 0.5x faster.
+ * Issue #260: process's mapped memory regions. (Windows patch by wj32.64, OSX
+   patch by Jeremy Whitlock)
+ * Issue #262: (Windows) psutil.disk_partitions() was slow due to inspecting
+   the floppy disk drive also when "all" argument was False.
+ * Issue #273: psutil.get_process_list() is deprecated.
+ * Issue #274: psutil no longer requires 2to3 at installation time in order to
+   work with Python 3.
+ * Issue #278: new Process.as_dict() method.
+ * Issue #281: ppid, name, exe, cmdline and create_time properties of Process
+   class are now cached after being accessed.
+ * Issue #282: psutil.STATUS_* constants can now be compared by using their
+   string representation.
+ * Issue #283: speedup Process.is_running() by caching its return value in case
+   the process is terminated.
+ * Issue #284: (POSIX) per-process number of opened file descriptors.
+ * Issue #287: psutil.process_iter() now caches Process instances between calls.
+ * Issue #290: Process.nice property is deprecated in favor of new get_nice()
+   and set_nice() methods.
+
+BUGFIXES
+
+ * Issue #193: psutil.Popen constructor can throw an exception if the spawned
+   process terminates quickly.
+ * Issue #240: (OSX) incorrect use of free() for Process.get_connections().
+ * Issue #244: (POSIX) Process.wait() can hog CPU resources if called against
+   a process which is not our children.
+ * Issue #248: (Linux) psutil.network_io_counters() might return erroneous NIC
+   names.
+ * Issue #252: (Windows) process getcwd() erroneously raise NoSuchProcess for
+   processes owned by another user.  It now raises AccessDenied instead.
+ * Issue #266: (Windows) psutil.get_pid_list() only shows 1024 processes.
+   (patch by Amoser)
+ * Issue #267: (OSX) Process.get_connections() - an erroneous remote address
+   was returned. (Patch by Amoser)
+ * Issue #272: (Linux) Porcess.get_open_files() - potential race condition can
+   lead to unexpected NoSuchProcess exception.  Also, we can get incorrect
+   reports of not absolutized path names.
+ * Issue #275: (Linux) Process.get_io_counters() erroneously raise NoSuchProcess
+   on old Linux versions. Where not available it now raises NotImplementedError.
+ * Issue #286: Process.is_running() doesn't actually check whether PID has been
+   reused.
+ * Issue #314: Process.get_children() can sometimes return non-children.
+
+API CHANGES
+
+ * Process.nice property is deprecated in favor of new get_nice() and set_nice()
+   methods.
+ * psutil.get_process_list() is deprecated.
+ * ppid, name, exe, cmdline and create_time properties of Process class are now
+   cached after being accessed, meaning NoSuchProcess will no longer be raised
+   in case the process is gone in the meantime.
+ * psutil.STATUS_* constants can now be compared by using their string
+   representation.
+
+
+0.4.1 - 2011-12-14
+------------------
+
+BUGFIXES
+
+ * Issue 228: some example scripts were not working with python 3.
+ * Issue 230: (Windows / OSX) memory leak in Process.get_connections().
+ * Issue 232: (Linux) psutil.phymem_usage() can report erroneous values which
+   are different than "free" command.
+ * Issue 236: (Windows) memory/handle leak in Process's get_memory_info(),
+   suspend() and resume() methods.
+
+
+0.4.0 - 2011-10-29
+------------------
+
+NEW FEATURES
+
+ * Issue 150: network I/O counters. (OSX and Windows patch by Jeremy Whitlock)
+ * Issue 154: (FreeBSD) add support for process getcwd()
+ * Issue 157: (Windows) provide installer for Python 3.2 64-bit.
+ * Issue 198: Process.wait(timeout=0) can now be used to make wait() return
+   immediately.
+ * Issue 206: disk I/O counters. (OSX and Windows patch by Jeremy Whitlock)
+ * Issue 213: examples/iotop.py script.
+ * Issue 217: Process.get_connections() now has a "kind" argument to filter
+   for connections with different criteria.
+ * Issue 221: (FreeBSD) Process.get_open_files has been rewritten in C and no
+   longer relies on lsof.
+ * Issue 223: examples/top.py script.
+ * Issue 227: examples/nettop.py script.
+
+BUGFIXES
+
+ * Issue 135: (OS X) psutil cannot create Process object
+ * Issue 144: (Linux) no longer support 0 special PID.
+ * Issue 188: (Linux) psutil import error on Linux ARM architectures.
+ * Issue 194: (POSIX) psutil.Process.get_cpu_percent() now reports a percentage
+   over 100 on multicore processors.
+ * Issue 197: (Linux) Process.get_connections() is broken on platforms not
+   supporting IPv6.
+ * Issue 200: (Linux) psutil.NUM_CPUS not working on armel and sparc
+   architectures and causing crash on module import.
+ * Issue 201: (Linux) Process.get_connections() is broken on big-endian
+   architectures.
+ * Issue 211: Process instance can unexpectedly raise NoSuchProcess if tested
+   for equality with another object.
+ * Issue 218: (Linux) crash at import time on Debian 64-bit because of a missing
+   line in /proc/meminfo.
+ * Issue 226: (FreeBSD) crash at import time on FreeBSD 7 and minor.
+
+
+0.3.0 - 2011-07-08
+------------------
+
+NEW FEATURES
+
+ * Issue 125: system per-cpu percentage utilization and times.
+ * Issue 163: per-process associated terminal (TTY).
+ * Issue 171: added get_phymem() and get_virtmem() functions returning system
+   memory information (total, used, free) and memory percent usage.
+   total_* avail_* and used_* memory functions are deprecated.
+ * Issue 172: disk usage statistics.
+ * Issue 174: mounted disk partitions.
+ * Issue 179: setuptools is now used in setup.py
+
+BUGFIXES
+
+ * Issue 159: SetSeDebug() does not close handles or unset impersonation on
+   return.
+ * Issue 164: wait function raises a TimeoutException when a process returns
+   -1 (Windows).
+ * Issue 165: process.status raises an unhandled exception.
+ * Issue 166: get_memory_info() leaks handles hogging system resources.
+ * Issue 168: psutil.cpu_percent() returns erroneous results when used in
+   non-blocking mode.  (patch by Philip Roberts)
+ * Issue 178: OSX - Process.get_threads() leaks memory
+ * Issue 180: Windows - Process's get_num_threads() and get_threads() methods
+   can raise NoSuchProcess exception while process still exists.
+
+
+0.2.1 - 2011-03-20
+------------------
+
+NEW FEATURES
+
+ * Issue 64: per-process I/O counters.
+ * Issue 116: per-process wait() (wait for process to terminate and return its
+   exit code).
+ * Issue 134: per-process get_threads() returning information (id, user and
+   kernel times) about threads opened by process.
+ * Issue 136: process executable path on FreeBSD is now determined by asking
+   the kernel instead of guessing it from cmdline[0].
+ * Issue 137: per-process real, effective and saved user and group ids.
+ * Issue 140: system boot time.
+ * Issue 142: per-process get and set niceness (priority).
+ * Issue 143: per-process status.
+ * Issue 147: per-process I/O nice (priority) - Linux only.
+ * Issue 148: psutil.Popen class which tidies up subprocess.Popen and
+   psutil.Process in a unique interface.
+ * Issue 152: (OSX) get_process_open_files() implementation has been rewritten
+   in C and no longer relies on lsof resulting in a 3x speedup.
+ * Issue 153: (OSX) get_process_connection() implementation has been rewritten
+   in C and no longer relies on lsof resulting in a 3x speedup.
+
+BUGFIXES
+
+ * Issue 83: process cmdline is empty on OSX 64-bit.
+ * Issue 130: a race condition can cause IOError exception be raised on
+   Linux if process disappears between open() and subsequent read() calls.
+ * Issue 145: WindowsError was raised instead of psutil.AccessDenied when using
+   process resume() or suspend() on Windows.
+ * Issue 146: 'exe' property on Linux can raise TypeError if path contains NULL
+   bytes.
+ * Issue 151: exe and getcwd() for PID 0 on Linux return inconsistent data.
+
+API CHANGES
+
+ * Process "uid" and "gid" properties are deprecated in favor of "uids" and
+   "gids" properties.
+
+
+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.
new file mode 100644
--- /dev/null
+++ b/python/psutil/INSTALL
@@ -0,0 +1,110 @@
+==================
+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:
+
+    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:
+
+     python setup.py build
+
+To install and build:
+
+     python 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:
+
+     python setup.py build
+
+Install and build:
+
+     python setup.py install
+
+
+===================
+Installing on Linux
+===================
+
+gcc is required and so the python headers. They can easily be installed by using
+the distro package manager. For example, on Ubuntu:
+
+     sudo apt-get install python-dev
+
+Once done, you can install/build psutil with:
+
+     python setup.py install
+
new file mode 100644
--- /dev/null
+++ b/python/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.
new file mode 100644
--- /dev/null
+++ b/python/psutil/MANIFEST.in
@@ -0,0 +1,9 @@
+include CREDITS
+include HISTORY
+include LICENSE
+include MANIFEST.in
+include README
+include setup.py
+recursive-include psutil *.py *.c *.h
+recursive-include test *.py
+recursive-include examples *.py
new file mode 100644
--- /dev/null
+++ b/python/psutil/PKG-INFO
@@ -0,0 +1,252 @@
+Metadata-Version: 1.1
+Name: psutil
+Version: 0.6.1
+Summary: A process and system utilities module for Python
+Home-page: http://code.google.com/p/psutil/
+Author: Giampaolo Rodola
+Author-email: g.rodola <at> gmail <dot> com
+License: License :: OSI Approved :: BSD License
+Download-URL: http://psutil.googlecode.com/files/psutil-0.6.1.tar.gz
+Description: ===========
+        Quick links
+        ===========
+        
+        * `Home page <http://code.google.com/p/psutil>`_
+        * `Download <http://code.google.com/p/psutil/downloads/list>`_
+        * `Documentation <http://code.google.com/p/psutil/wiki/Documentation>`_
+        
+        =======
+        Summary
+        =======
+        
+        psutil is a module providing an interface for retrieving information on all
+        running processes and system utilization (CPU, memory, disks, network, users) in
+        a portable way by using Python, implementing many functionalities offered by
+        command line tools such as:  **ps, top, df, kill, free, lsof, free, netstat,
+        ifconfig, nice, ionice, iostat, iotop, uptime, pidof, tty, who, taskset, pmap**.
+        
+        It currently supports **Linux**, **Windows**, **OSX** and **FreeBSD** both
+        **32-bit** and **64-bit** with Python versions from **2.4** to **3.3** by using
+        a single code base.
+        
+        ==============
+        Example usages
+        ==============
+        
+        CPU
+        ===
+        
+        >>> import psutil
+        >>> psutil.cpu_times()
+        cputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540,
+                 iowait=629.509, irq=0.0, softirq=19.422)
+        >>>
+        >>> for x in range(3):
+        ...     psutil.cpu_percent(interval=1)
+        ...
+        4.0
+        5.9
+        3.8
+        >>>
+        >>> for x in range(3):
+        ...     psutil.cpu_percent(interval=1, percpu=True)
+        ...
+        [4.0, 6.9]
+        [7.0, 8.5]
+        [1.2, 9.0]
+        >>>
+        
+        
+        Memory
+        ======
+        
+        >>> psutil.virtual_memory()
+        vmem(total=8374149120L, available=2081050624L, percent=75.1, used=8074080256L,
+             free=300068864L, active=3294920704, inactive=1361616896, buffers=529895424L,
+             cached=1251086336)
+        >>> psutil.swap_memory()
+        swap(total=2097147904L, used=296128512L, free=1801019392L, percent=14.1,
+             sin=304193536, sout=677842944)
+        >>>
+        
+        
+        Disks
+        =====
+        
+        >>> psutil.disk_partitions()
+        [partition(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'),
+         partition(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw')]
+        >>>
+        >>> psutil.disk_usage('/')
+        usage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
+        >>>
+        >>> psutil.disk_io_counters()
+        iostat(read_count=719566, write_count=1082197, read_bytes=18626220032,
+               write_bytes=24081764352, read_time=5023392, write_time=63199568)
+        >>>
+        
+        
+        Network
+        =======
+        
+        >>> psutil.network_io_counters(pernic=True)
+        {'lo': iostat(bytes_sent=799953745, bytes_recv=799953745,
+                      packets_sent=453698, packets_recv=453698),
+         'eth0': iostat(bytes_sent=734324837, bytes_recv=4163935363,
+                        packets_sent=3605828, packets_recv=4096685)}
+        >>>
+        
+        
+        Users
+        =====
+        
+        >>> psutil.get_users()
+        [user(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0),
+         user(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0)]
+        >>>
+        
+        
+        Process management
+        ==================
+        
+        >>> 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.exe
+        '/usr/bin/python'
+        >>> p.getcwd()
+        '/home/giampaolo'
+        >>> p.cmdline
+        ['/usr/bin/python', 'main.py']
+        >>>
+        >>> str(p.status)
+        'running'
+        >>> p.username
+        'giampaolo'
+        >>> p.create_time
+        1267551141.5019531
+        >>> p.terminal
+        '/dev/pts/0'
+        >>>
+        >>> p.uids
+        user(real=1000, effective=1000, saved=1000)
+        >>> p.gids
+        group(real=1000, effective=1000, saved=1000)
+        >>>
+        >>> p.get_cpu_times()
+        cputimes(user=1.02, system=0.31)
+        >>> p.get_cpu_percent(interval=1.0)
+        12.1
+        >>> p.get_cpu_affinity()
+        [0, 1, 2, 3]
+        >>> p.set_cpu_affinity([0])
+        >>>
+        >>> p.get_memory_percent()
+        0.63423
+        >>>
+        >>> p.get_memory_info()
+        meminfo(rss=7471104, vms=68513792)
+        >>> p.get_ext_memory_info()
+        meminfo(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0)
+        >>> p.get_memory_maps()
+        [mmap(path='/lib/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0),
+         mmap(path='/lib/x86_64-linux-gnu/libc-2.15.so', rss=6384, anonymous=15, swap=0),
+         mmap(path='/lib/x86_64-linux-gnu/libcrypto.so.1.0.0', rss=34124, anonymous=1245, swap=0),
+         mmap(path='[heap]', rss=54653, anonymous=8192, swap=0),
+         mmap(path='[stack]', rss=1542, anonymous=166, swap=0),
+         ...]
+        >>>
+        >>> p.get_io_counters()
+        io(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632)
+        >>>
+        >>> p.get_open_files()
+        [openfile(path='/home/giampaolo/svn/psutil/somefile', fd=3)]
+        >>>
+        >>> 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')]
+        >>>
+        >>> p.get_num_threads()
+        4
+        >>> p.get_num_fds()
+        8
+        >>> p.get_threads()
+        [thread(id=5234, user_time=22.5, system_time=9.2891),
+         thread(id=5235, user_time=0.0, system_time=0.0),
+         thread(id=5236, user_time=0.0, system_time=0.0),
+         thread(id=5237, user_time=0.0707, system_time=1.1)]
+        >>>
+        >>> p.get_num_ctx_switches()
+        amount(voluntary=78, involuntary=19)
+        >>>
+        >>> p.get_nice()
+        0
+        >>> p.set_nice(10)
+        >>>
+        >>> p.suspend()
+        >>> p.resume()
+        >>>
+        >>> p.terminate()
+        >>> p.wait(timeout=3)
+        0
+        >>>
+        >>> psutil.test()
+        USER         PID %CPU %MEM     VSZ     RSS TTY        START    TIME  COMMAND
+        root           1  0.0  0.0   24584    2240 ?          Jun17   00:00  init
+        root           2  0.0  0.0       0       0 ?          Jun17   00:00  kthreadd
+        root           3  0.0  0.0       0       0 ?          Jun17   00:05  ksoftirqd/0
+        ...
+        giampaolo  31475  0.0  0.0   20760    3024 /dev/pts/0 Jun19   00:00  python2.4
+        giampaolo  31721  0.0  2.2  773060  181896 ?          00:04   10:30  chrome
+        root       31763  0.0  0.0       0       0 ?          00:05   00:00  kworker/0:1
+        >>>
+        
+Keywords: ps,top,kill,free,lsof,netstat,nice,tty,ionice,uptime,taskmgr,process,df,iotop,iostat,ifconfig,taskset,who,pidof,pmap,smem,monitoring
+Platform: Platform Independent
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Operating System :: Microsoft
+Classifier: Operating System :: Microsoft :: Windows :: Windows NT/2000
+Classifier: Operating System :: POSIX
+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 :: Networking :: Monitoring
+Classifier: Topic :: System :: Benchmark
+Classifier: Topic :: System :: Hardware
+Classifier: Topic :: System :: Systems Administration
+Classifier: Topic :: Utilities
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: BSD License
new file mode 100644
--- /dev/null
+++ b/python/psutil/README
@@ -0,0 +1,208 @@
+===========
+Quick links
+===========
+
+* `Home page <http://code.google.com/p/psutil>`_
+* `Download <http://code.google.com/p/psutil/downloads/list>`_
+* `Documentation <http://code.google.com/p/psutil/wiki/Documentation>`_
+
+=======
+Summary
+=======
+
+psutil is a module providing an interface for retrieving information on all
+running processes and system utilization (CPU, memory, disks, network, users) in
+a portable way by using Python, implementing many functionalities offered by
+command line tools such as:  **ps, top, df, kill, free, lsof, free, netstat,
+ifconfig, nice, ionice, iostat, iotop, uptime, pidof, tty, who, taskset, pmap**.
+
+It currently supports **Linux**, **Windows**, **OSX** and **FreeBSD** both
+**32-bit** and **64-bit** with Python versions from **2.4** to **3.3** by using
+a single code base.
+
+==============
+Example usages
+==============
+
+CPU
+===
+
+>>> import psutil
+>>> psutil.cpu_times()
+cputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540,
+         iowait=629.509, irq=0.0, softirq=19.422)
+>>>
+>>> for x in range(3):
+...     psutil.cpu_percent(interval=1)
+...
+4.0
+5.9
+3.8
+>>>
+>>> for x in range(3):
+...     psutil.cpu_percent(interval=1, percpu=True)
+...
+[4.0, 6.9]
+[7.0, 8.5]
+[1.2, 9.0]
+>>>
+
+
+Memory
+======
+
+>>> psutil.virtual_memory()
+vmem(total=8374149120L, available=2081050624L, percent=75.1, used=8074080256L,
+     free=300068864L, active=3294920704, inactive=1361616896, buffers=529895424L,
+     cached=1251086336)
+>>> psutil.swap_memory()
+swap(total=2097147904L, used=296128512L, free=1801019392L, percent=14.1,
+     sin=304193536, sout=677842944)
+>>>
+
+
+Disks
+=====
+
+>>> psutil.disk_partitions()
+[partition(device='/dev/sda1', mountpoint='/', fstype='ext4', opts='rw,nosuid'),
+ partition(device='/dev/sda2', mountpoint='/home', fstype='ext, opts='rw')]
+>>>
+>>> psutil.disk_usage('/')
+usage(total=21378641920, used=4809781248, free=15482871808, percent=22.5)
+>>>
+>>> psutil.disk_io_counters()
+iostat(read_count=719566, write_count=1082197, read_bytes=18626220032,
+       write_bytes=24081764352, read_time=5023392, write_time=63199568)
+>>>
+
+
+Network
+=======
+
+>>> psutil.network_io_counters(pernic=True)
+{'lo': iostat(bytes_sent=799953745, bytes_recv=799953745,
+              packets_sent=453698, packets_recv=453698),
+ 'eth0': iostat(bytes_sent=734324837, bytes_recv=4163935363,
+                packets_sent=3605828, packets_recv=4096685)}
+>>>
+
+
+Users
+=====
+
+>>> psutil.get_users()
+[user(name='giampaolo', terminal='pts/2', host='localhost', started=1340737536.0),
+ user(name='giampaolo', terminal='pts/3', host='localhost', started=1340737792.0)]
+>>>
+
+
+Process management
+==================
+
+>>> 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.exe
+'/usr/bin/python'
+>>> p.getcwd()
+'/home/giampaolo'
+>>> p.cmdline
+['/usr/bin/python', 'main.py']
+>>>
+>>> str(p.status)
+'running'
+>>> p.username
+'giampaolo'
+>>> p.create_time
+1267551141.5019531
+>>> p.terminal
+'/dev/pts/0'
+>>>
+>>> p.uids
+user(real=1000, effective=1000, saved=1000)
+>>> p.gids
+group(real=1000, effective=1000, saved=1000)
+>>>
+>>> p.get_cpu_times()
+cputimes(user=1.02, system=0.31)
+>>> p.get_cpu_percent(interval=1.0)
+12.1
+>>> p.get_cpu_affinity()
+[0, 1, 2, 3]
+>>> p.set_cpu_affinity([0])
+>>>
+>>> p.get_memory_percent()
+0.63423
+>>>
+>>> p.get_memory_info()
+meminfo(rss=7471104, vms=68513792)
+>>> p.get_ext_memory_info()
+meminfo(rss=9662464, vms=49192960, shared=3612672, text=2564096, lib=0, data=5754880, dirty=0)
+>>> p.get_memory_maps()
+[mmap(path='/lib/x86_64-linux-gnu/libutil-2.15.so', rss=16384, anonymous=8192, swap=0),
+ mmap(path='/lib/x86_64-linux-gnu/libc-2.15.so', rss=6384, anonymous=15, swap=0),
+ mmap(path='/lib/x86_64-linux-gnu/libcrypto.so.1.0.0', rss=34124, anonymous=1245, swap=0),
+ mmap(path='[heap]', rss=54653, anonymous=8192, swap=0),
+ mmap(path='[stack]', rss=1542, anonymous=166, swap=0),
+ ...]
+>>>
+>>> p.get_io_counters()
+io(read_count=478001, write_count=59371, read_bytes=700416, write_bytes=69632)
+>>>
+>>> p.get_open_files()
+[openfile(path='/home/giampaolo/svn/psutil/somefile', fd=3)]
+>>>
+>>> 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')]
+>>>
+>>> p.get_num_threads()
+4
+>>> p.get_num_fds()
+8
+>>> p.get_threads()
+[thread(id=5234, user_time=22.5, system_time=9.2891),
+ thread(id=5235, user_time=0.0, system_time=0.0),
+ thread(id=5236, user_time=0.0, system_time=0.0),
+ thread(id=5237, user_time=0.0707, system_time=1.1)]
+>>>
+>>> p.get_num_ctx_switches()
+amount(voluntary=78, involuntary=19)
+>>>
+>>> p.get_nice()
+0
+>>> p.set_nice(10)
+>>>
+>>> p.suspend()
+>>> p.resume()
+>>>
+>>> p.terminate()
+>>> p.wait(timeout=3)
+0
+>>>
+>>> psutil.test()
+USER         PID %CPU %MEM     VSZ     RSS TTY        START    TIME  COMMAND
+root           1  0.0  0.0   24584    2240 ?          Jun17   00:00  init
+root           2  0.0  0.0       0       0 ?          Jun17   00:00  kthreadd
+root           3  0.0  0.0       0       0 ?          Jun17   00:05  ksoftirqd/0
+...
+giampaolo  31475  0.0  0.0   20760    3024 /dev/pts/0 Jun19   00:00  python2.4
+giampaolo  31721  0.0  2.2  773060  181896 ?          00:04   10:30  chrome
+root       31763  0.0  0.0       0       0 ?          00:05   00:00  kworker/0:1
+>>>
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/disk_usage.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# $Id: disk_usage.py 1340 2012-06-09 13:42:21Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+List all mounted disk partitions a-la "df -h" command.
+"""
+
+import sys
+import psutil
+from psutil._compat import print_
+
+def bytes2human(n):
+    # http://code.activestate.com/recipes/578019
+    # >>> bytes2human(10000)
+    # '9.8K'
+    # >>> bytes2human(100001221)
+    # '95.4M'
+    symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
+    prefix = {}
+    for i, s in enumerate(symbols):
+        prefix[s] = 1 << (i+1)*10
+    for s in reversed(symbols):
+        if n >= prefix[s]:
+            value = float(n) / prefix[s]
+            return '%.1f%s' % (value, s)
+    return "%sB" % n
+
+
+def main():
+    templ = "%-17s %8s %8s %8s %5s%% %9s  %s"
+    print_(templ % ("Device", "Total", "Used", "Free", "Use ", "Type", "Mount"))
+    for part in psutil.disk_partitions(all=False):
+        usage = psutil.disk_usage(part.mountpoint)
+        print_(templ % (part.device,
+                        bytes2human(usage.total),
+                        bytes2human(usage.used),
+                        bytes2human(usage.free),
+                        int(usage.percent),
+                        part.fstype,
+                        part.mountpoint))
+
+if __name__ == '__main__':
+    sys.exit(main())
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/free.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# $Id: free.py 1508 2012-08-13 12:30:07Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+A clone of 'free' cmdline utility.
+"""
+
+import psutil
+from psutil._compat import print_
+
+def main():
+    virt = psutil.virtual_memory()
+    swap = psutil.swap_memory()
+    templ = "%-7s %10s %10s %10s %10s %10s %10s"
+    print_(templ % ('', 'total', 'used', 'free', 'shared', 'buffers', 'cache'))
+    print_(templ % ('Mem:', int(virt.total / 1024),
+                            int(virt.used / 1024),
+                            int(virt.free / 1024),
+                            int(getattr(virt, 'shared', 0) / 1024),
+                            int(getattr(virt, 'buffers', 0) / 1024),
+                            int(getattr(virt, 'cached', 0) / 1024)))
+    print_(templ % ('Swap:', int(swap.total / 1024),
+                             int(swap.used / 1024),
+                             int(swap.free / 1024),
+                             '', '', ''))
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/iotop.py
@@ -0,0 +1,161 @@
+#!/usr/bin/env python
+#
+# $Id: iotop.py 1236 2011-12-13 19:00:35Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+A clone of iotop (http://guichaz.free.fr/iotop/) showing real time
+disk I/O statistics.
+
+It works on Linux only (FreeBSD and OSX are missing support for IO
+counters).
+It doesn't work on Windows as curses module is required.
+
+Author: Giampaolo Rodola' <g.rodola@gmail.com>
+"""
+
+import os
+import sys
+import psutil
+if not hasattr(psutil.Process, 'get_io_counters') or os.name != 'posix':
+    sys.exit('platform not supported')
+import time
+import curses
+import atexit
+
+
+# --- curses stuff
+def tear_down():
+    win.keypad(0)
+    curses.nocbreak()
+    curses.echo()
+    curses.endwin()
+
+win = curses.initscr()
+atexit.register(tear_down)
+curses.endwin()
+lineno = 0
+
+def print_line(line, highlight=False):
+    """A thin wrapper around curses's addstr()."""
+    global lineno
+    try:
+        if highlight:
+            line += " " * (win.getmaxyx()[1] - len(line))
+            win.addstr(lineno, 0, line, curses.A_REVERSE)
+        else:
+            win.addstr(lineno, 0, line, 0)
+    except curses.error:
+        lineno = 0
+        win.refresh()
+        raise
+    else:
+        lineno += 1
+# --- /curses stuff
+
+
+def bytes2human(n):
+    """
+    >>> bytes2human(10000)
+    '9.8 K/s'
+    >>> bytes2human(100001221)
+    '95.4 M/s'
+    """
+    symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
+    prefix = {}
+    for i, s in enumerate(symbols):
+        prefix[s] = 1 << (i+1)*10
+    for s in reversed(symbols):
+        if n >= prefix[s]:
+            value = float(n) / prefix[s]
+            return '%.2f %s/s' % (value, s)
+    return '%.2f B/s' % (n)
+
+def poll(interval):
+    """Calculate IO usage by comparing IO statics before and
+    after the interval.
+    Return a tuple including all currently running processes
+    sorted by IO activity and total disks I/O activity.
+    """
+    # first get a list of all processes and disk io counters
+    procs = [p for p in psutil.process_iter()]
+    for p in procs[:]:
+        try:
+            p._before = p.get_io_counters()
+        except psutil.Error:
+            procs.remove(p)
+            continue
+    disks_before = psutil.disk_io_counters()
+
+    # sleep some time
+    time.sleep(interval)
+
+    # then retrieve the same info again
+    for p in procs[:]:
+        try:
+            p._after = p.get_io_counters()
+            p._cmdline = ' '.join(p.cmdline)
+            if not p._cmdline:
+                p._cmdline = p.name
+            p._username = p.username
+        except psutil.NoSuchProcess:
+            procs.remove(p)
+    disks_after = psutil.disk_io_counters()
+
+    # finally calculate results by comparing data before and
+    # after the interval
+    for p in procs:
+        p._read_per_sec = p._after.read_bytes - p._before.read_bytes
+        p._write_per_sec = p._after.write_bytes - p._before.write_bytes
+        p._total = p._read_per_sec + p._write_per_sec
+
+    disks_read_per_sec = disks_after.read_bytes - disks_before.read_bytes
+    disks_write_per_sec = disks_after.write_bytes - disks_before.write_bytes
+
+    # sort processes by total disk IO so that the more intensive
+    # ones get listed first
+    processes = sorted(procs, key=lambda p: p._total, reverse=True)
+
+    return (processes, disks_read_per_sec, disks_write_per_sec)
+
+
+def refresh_window(procs, disks_read, disks_write):
+    """Print results on screen by using curses."""
+    curses.endwin()
+    templ = "%-5s %-7s %11s %11s  %s"
+    win.erase()
+
+    disks_tot = "Total DISK READ: %s | Total DISK WRITE: %s" \
+                % (bytes2human(disks_read), bytes2human(disks_write))
+    print_line(disks_tot)
+
+    header = templ % ("PID", "USER", "DISK READ", "DISK WRITE", "COMMAND")
+    print_line(header, highlight=True)
+
+    for p in procs:
+        line = templ % (p.pid,
+                        p._username[:7],
+                        bytes2human(p._read_per_sec),
+                        bytes2human(p._write_per_sec),
+                        p._cmdline)
+        try:
+            print_line(line)
+        except curses.error:
+            break
+    win.refresh()
+
+def main():
+    try:
+        interval = 0
+        while 1:
+            args = poll(interval)
+            refresh_window(*args)
+            interval = 1
+    except (KeyboardInterrupt, SystemExit):
+        pass
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/killall.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# $Id: killall.py 1143 2011-10-05 19:11:59Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Kill a process by name.
+"""
+
+import os
+import sys
+import psutil
+
+def main():
+    if len(sys.argv) != 2:
+        sys.exit('usage: %s name' % __file__)
+    else:
+        NAME = sys.argv[1]
+
+    killed = []
+    for proc in psutil.process_iter():
+        if proc.name == NAME and proc.pid != os.getpid():
+            proc.kill()
+            killed.append(proc.pid)
+    if not killed:
+        sys.exit('%s: no process found' % NAME)
+    else:
+        sys.exit(0)
+
+sys.exit(main())
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/meminfo.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# $Id: meminfo.py 1509 2012-08-13 12:31:18Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Print system memory information.
+"""
+
+import psutil
+from psutil._compat import print_
+
+def to_meg(n):
+    return str(int(n / 1024 / 1024)) + "M"
+
+def pprint_ntuple(nt):
+    for name in nt._fields:
+        value = getattr(nt, name)
+        if name != 'percent':
+            value = to_meg(value)
+        print_('%-10s : %7s' % (name.capitalize(), value))
+
+def main():
+    print_('MEMORY\n------')
+    pprint_ntuple(psutil.virtual_memory())
+    print_('\nSWAP\n----')
+    pprint_ntuple(psutil.swap_memory())
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/netstat.py
@@ -0,0 +1,52 @@
+#!/usr/bin/env python
+#
+# $Id: netstat.py 1457 2012-07-14 18:09:36Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+A clone of 'netstat'.
+"""
+
+import socket
+from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
+
+import psutil
+from psutil._compat import print_
+
+
+AD = "-"
+AF_INET6 = getattr(socket, 'AF_INET6', object())
+proto_map = {(AF_INET, SOCK_STREAM)  : 'tcp',
+             (AF_INET6, SOCK_STREAM) : 'tcp6',
+             (AF_INET, SOCK_DGRAM)   : 'udp',
+             (AF_INET6, SOCK_DGRAM)  : 'udp6'}
+
+def main():
+    templ = "%-5s %-22s %-22s %-13s %-6s %s"
+    print_(templ % ("Proto", "Local addr", "Remote addr", "Status", "PID",
+                    "Program name"))
+    for p in psutil.process_iter():
+        name = '?'
+        try:
+            name = p.name
+            cons = p.get_connections(kind='inet')
+        except psutil.AccessDenied:
+            print_(templ % (AD, AD, AD, AD, p.pid, name))
+        else:
+            for c in cons:
+                raddr = ""
+                laddr = "%s:%s" % (c.local_address)
+                if c.remote_address:
+                    raddr = "%s:%s" % (c.remote_address)
+                print_(templ % (proto_map[(c.family, c.type)],
+                                laddr,
+                                raddr,
+                                str(c.status),
+                                p.pid,
+                                name[:15]))
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/nettop.py
@@ -0,0 +1,144 @@
+#!/usr/bin/env python
+#
+# $Id: iotop.py 1160 2011-10-14 18:50:36Z g.rodola@gmail.com $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Shows real-time network statistics.
+
+Author: Giampaolo Rodola' <g.rodola@gmail.com>
+"""
+
+import sys
+import os
+if os.name != 'posix':
+    sys.exit('platform not supported')
+import curses
+import atexit
+import time
+
+import psutil
+
+
+# --- curses stuff
+def tear_down():
+    win.keypad(0)
+    curses.nocbreak()
+    curses.echo()
+    curses.endwin()
+
+win = curses.initscr()
+atexit.register(tear_down)
+curses.endwin()
+lineno = 0
+
+def print_line(line, highlight=False):
+    """A thin wrapper around curses's addstr()."""
+    global lineno
+    try:
+        if highlight:
+            line += " " * (win.getmaxyx()[1] - len(line))
+            win.addstr(lineno, 0, line, curses.A_REVERSE)
+        else:
+            win.addstr(lineno, 0, line, 0)
+    except curses.error:
+        lineno = 0
+        win.refresh()
+        raise
+    else:
+        lineno += 1
+# --- curses stuff
+
+
+def bytes2human(n):
+    """
+    >>> bytes2human(10000)
+    '9.8 K'
+    >>> bytes2human(100001221)
+    '95.4 M'
+    """
+    symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
+    prefix = {}
+    for i, s in enumerate(symbols):
+        prefix[s] = 1 << (i+1)*10
+    for s in reversed(symbols):
+        if n >= prefix[s]:
+            value = float(n) / prefix[s]
+            return '%.2f %s' % (value, s)
+    return '%.2f B' % (n)
+
+def poll(interval):
+    """Retrieve raw stats within an interval window."""
+    tot_before = psutil.network_io_counters()
+    pnic_before = psutil.network_io_counters(pernic=True)
+    # sleep some time
+    time.sleep(interval)
+    tot_after = psutil.network_io_counters()
+    pnic_after = psutil.network_io_counters(pernic=True)
+    return (tot_before, tot_after, pnic_before, pnic_after)
+
+
+def refresh_window(tot_before, tot_after, pnic_before, pnic_after):
+    """Print stats on screen."""
+    global lineno
+
+    # totals
+    print_line("total bytes:           sent: %-10s   received: %s" \
+          % (bytes2human(tot_after.bytes_sent),
+             bytes2human(tot_after.bytes_recv))
+    )
+    print_line("total packets:         sent: %-10s   received: %s" \
+          % (tot_after.packets_sent, tot_after.packets_recv)
+    )
+
+
+    # per-network interface details: let's sort network interfaces so
+    # that the ones which generated more traffic are shown first
+    print_line("")
+    nic_names = list(pnic_after.keys())
+    nic_names.sort(key=lambda x: sum(pnic_after[x]), reverse=True)
+    for name in nic_names:
+        stats_before = pnic_before[name]
+        stats_after = pnic_after[name]
+        templ = "%-15s %15s %15s"
+        print_line(templ % (name, "TOTAL", "PER-SEC"), highlight=True)
+        print_line(templ % (
+            "bytes-sent",
+            bytes2human(stats_after.bytes_sent),
+            bytes2human(stats_after.bytes_sent - stats_before.bytes_sent) + '/s',
+        ))
+        print_line(templ % (
+            "bytes-recv",
+            bytes2human(stats_after.bytes_recv),
+            bytes2human(stats_after.bytes_recv - stats_before.bytes_recv) + '/s',
+        ))
+        print_line(templ % (
+            "pkts-sent",
+            stats_after.packets_sent,
+            stats_after.packets_sent - stats_before.packets_sent,
+        ))
+        print_line(templ % (
+            "pkts-recv",
+            stats_after.packets_recv,
+            stats_after.packets_recv - stats_before.packets_recv,
+        ))
+        print_line("")
+    win.refresh()
+    lineno = 0
+
+
+def main():
+    try:
+        interval = 0
+        while 1:
+            args = poll(interval)
+            refresh_window(*args)
+            interval = 1
+    except (KeyboardInterrupt, SystemExit):
+        pass
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/pmap.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+#
+# $Id: pmap.py 1420 2012-07-08 12:12:01Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+A clone of 'pmap' utility on Linux, 'vmmap' on OSX and 'procstat -v' on BSD.
+Report memory map of a process.
+"""
+
+import sys
+
+import psutil
+from psutil._compat import print_
+
+def main():
+    if len(sys.argv) != 2:
+        sys.exit('usage: pmap pid')
+    p = psutil.Process(int(sys.argv[1]))
+    print_("pid=%s, name=%s" % (p.pid, p.name))
+    templ = "%-16s %10s  %-7s %s"
+    print_(templ % ("Address", "RSS", "Mode", "Mapping"))
+    total_rss = 0
+    for m in p.get_memory_maps(grouped=False):
+        total_rss += m.rss
+        print_(templ % (m.addr.split('-')[0].zfill(16),
+                        str(m.rss / 1024) + 'K' ,
+                        m.perms,
+                        m.path))
+    print_("-" * 33)
+    print_(templ % ("Total", str(total_rss / 1024) + 'K', '', ''))
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/process_detail.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+#
+# $Id: process_detail.py 1498 2012-07-24 21:41:28Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+Print detailed information about a process.
+
+Author: Giampaolo Rodola' <g.rodola@gmail.com>
+"""
+
+import os
+import datetime
+import socket
+import sys
+
+import psutil
+
+
+def convert_bytes(n):
+    symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
+    prefix = {}
+    for i, s in enumerate(symbols):
+        prefix[s] = 1 << (i+1)*10
+    for s in reversed(symbols):
+        if n >= prefix[s]:
+            value = float(n) / prefix[s]
+            return '%.1f%s' % (value, s)
+    return "%sB" % n
+
+def print_(a, b):
+    if sys.stdout.isatty() and os.name == 'posix':
+        fmt = '\x1b[1;32m%-17s\x1b[0m %s' %(a, b)
+    else:
+        fmt = '%-15s %s' %(a, b)
+    # python 2/3 compatibility layer
+    sys.stdout.write(fmt + '\n')
+    sys.stdout.flush()
+
+def run(pid):
+    ACCESS_DENIED = ''
+    try:
+        p = psutil.Process(pid)
+        pinfo = p.as_dict(ad_value=ACCESS_DENIED)
+    except psutil.NoSuchProcess:
+        sys.exit(str(sys.exc_info()[1]))
+
+    try:
+        if p.parent:
+            parent = '(%s)' % p.parent.name
+        else:
+            parent = ''
+    except psutil.Error:
+        parent = ''
+    started = datetime.datetime.fromtimestamp(pinfo['create_time']
+                                                ).strftime('%Y-%M-%d %H:%M')
+    io = pinfo.get('io_counters', None)
+    mem = '%s%% (resident=%s, virtual=%s) ' % (
+                                      round(pinfo['memory_percent'], 1),
+                                      convert_bytes(pinfo['memory_info'].rss),
+                                      convert_bytes(pinfo['memory_info'].vms))
+    children = p.get_children()
+
+    print_('pid', pinfo['pid'])
+    print_('name', pinfo['name'])
+    print_('exe', pinfo['exe'])
+    print_('parent', '%s %s' % (pinfo['ppid'], parent))
+    print_('cmdline', ' '.join(pinfo['cmdline']))
+    print_('started', started)
+    print_('user', pinfo['username'])
+    if os.name == 'posix':
+        print_('uids', 'real=%s, effective=%s, saved=%s' % pinfo['uids'])
+        print_('gids', 'real=%s, effective=%s, saved=%s' % pinfo['gids'])
+        print_('terminal', pinfo['terminal'] or '')
+    if hasattr(p, 'getcwd'):
+        print_('cwd', pinfo['cwd'])
+    print_('memory', mem)
+    print_('cpu', '%s%% (user=%s, system=%s)' % (pinfo['cpu_percent'],
+                                                 pinfo['cpu_times'].user,
+                                                 pinfo['cpu_times'].system))
+    print_('status', pinfo['status'])
+    print_('niceness', pinfo['nice'])
+    print_('num threads', pinfo['num_threads'])
+    if io != ACCESS_DENIED:
+        print_('I/O', 'bytes-read=%s, bytes-written=%s' % \
+                                               (convert_bytes(io.read_bytes),
+                                                convert_bytes(io.write_bytes)))
+    if children:
+        print_('children', '')
+        for child in children:
+            print_('', 'pid=%s name=%s' % (child.pid, child.name))
+
+    if pinfo['open_files'] != ACCESS_DENIED:
+        print_('open files', '')
+        for file in pinfo['open_files']:
+            print_('',  'fd=%s %s ' % (file.fd, file.path))
+
+    if pinfo['threads']:
+        print_('running threads', '')
+        for thread in pinfo['threads']:
+            print_('',  'id=%s, user-time=%s, sys-time=%s' \
+                         % (thread.id, thread.user_time, thread.system_time))
+    if pinfo['connections'] != ACCESS_DENIED:
+        print_('open connections', '')
+        for conn in pinfo['connections']:
+            if conn.type == socket.SOCK_STREAM:
+                type = 'TCP'
+            elif conn.type == socket.SOCK_DGRAM:
+                type = 'UDP'
+            else:
+                type = 'UNIX'
+            lip, lport = conn.local_address
+            if not conn.remote_address:
+                rip, rport = '*', '*'
+            else:
+                rip, rport = conn.remote_address
+            print_('',  '%s:%s -> %s:%s type=%s status=%s' \
+                         % (lip, lport, rip, rport, type, conn.status))
+
+def main(argv=None):
+    if argv is None:
+        argv = sys.argv
+    if len(argv) == 1:
+        sys.exit(run(os.getpid()))
+    elif len(argv) == 2:
+        sys.exit(run(int(argv[1])))
+    else:
+        sys.exit('usage: %s [pid]' % __file__)
+
+if __name__ == '__main__':
+    sys.exit(main())
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/top.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+#
+# $Id: top.py 1498 2012-07-24 21:41:28Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+A clone of top / htop.
+
+Author: Giampaolo Rodola' <g.rodola@gmail.com>
+"""
+
+import os
+import sys
+if os.name != 'posix':
+    sys.exit('platform not supported')
+import time
+import curses
+import atexit
+from datetime import datetime, timedelta
+
+import psutil
+
+
+# --- curses stuff
+def tear_down():
+    win.keypad(0)
+    curses.nocbreak()
+    curses.echo()
+    curses.endwin()
+
+win = curses.initscr()
+atexit.register(tear_down)
+curses.endwin()
+lineno = 0
+
+def print_line(line, highlight=False):
+    """A thin wrapper around curses's addstr()."""
+    global lineno
+    try:
+        if highlight:
+            line += " " * (win.getmaxyx()[1] - len(line))
+            win.addstr(lineno, 0, line, curses.A_REVERSE)
+        else:
+            win.addstr(lineno, 0, line, 0)
+    except curses.error:
+        lineno = 0
+        win.refresh()
+        raise
+    else:
+        lineno += 1
+# --- /curses stuff
+
+
+def bytes2human(n):
+    """
+    >>> bytes2human(10000)
+    '9K'
+    >>> bytes2human(100001221)
+    '95M'
+    """
+    symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
+    prefix = {}
+    for i, s in enumerate(symbols):
+        prefix[s] = 1 << (i+1)*10
+    for s in reversed(symbols):
+        if n >= prefix[s]:
+            value = int(float(n) / prefix[s])
+            return '%s%s' % (value, s)
+    return "%sB" % n
+
+def poll(interval):
+    # sleep some time
+    time.sleep(interval)
+    procs = []
+    procs_status = {}
+    for p in psutil.process_iter():
+        try:
+            p.dict = p.as_dict(['username', 'get_nice', 'get_memory_info',
+                                'get_memory_percent', 'get_cpu_percent',
+                                'get_cpu_times', 'name', 'status'])
+            try:
+                procs_status[str(p.dict['status'])] += 1
+            except KeyError:
+                procs_status[str(p.dict['status'])] = 1
+        except psutil.NoSuchProcess:
+            pass
+        else:
+            procs.append(p)
+
+    # return processes sorted by CPU percent usage
+    processes = sorted(procs, key=lambda p: p.dict['cpu_percent'], reverse=True)
+    return (processes, procs_status)
+
+def print_header(procs_status, num_procs):
+    """Print system-related info, above the process list."""
+
+    def get_dashes(perc):
+        dashes =  "|" * int((float(perc) / 10 * 4))
+        empty_dashes = " " * (40 - len(dashes))
+        return dashes, empty_dashes
+
+    # cpu usage
+    for cpu_num, perc in enumerate(psutil.cpu_percent(interval=0, percpu=True)):
+        dashes, empty_dashes = get_dashes(perc)
+        print_line(" CPU%-2s [%s%s] %5s%%" % (cpu_num, dashes, empty_dashes,
+                                              perc))
+    mem = psutil.virtual_memory()
+    dashes, empty_dashes = get_dashes(mem.percent)
+    used = mem.total - mem.available
+    line = " Mem   [%s%s] %5s%% %6s/%s" % (
+        dashes, empty_dashes,
+        mem.percent,
+        str(int(used / 1024 / 1024)) + "M",
+        str(int(mem.total / 1024 / 1024)) + "M"
+    )
+    print_line(line)
+
+    # swap usage
+    swap = psutil.swap_memory()
+    dashes, empty_dashes = get_dashes(swap.percent)
+    line = " Swap  [%s%s] %5s%% %6s/%s" % (
+        dashes, empty_dashes,
+        swap.percent,
+        str(int(swap.used / 1024 / 1024)) + "M",
+        str(int(swap.total / 1024 / 1024)) + "M"
+    )
+    print_line(line)
+
+    # processes number and status
+    st = []
+    for x, y in procs_status.items():
+        if y:
+            st.append("%s=%s" % (x, y))
+    st.sort(key=lambda x: x[:3] in ('run', 'sle'), reverse=1)
+    print_line(" Processes: %s (%s)" % (num_procs, ' '.join(st)))
+    # load average, uptime
+    uptime = datetime.now() - datetime.fromtimestamp(psutil.BOOT_TIME)
+    av1, av2, av3 = os.getloadavg()
+    line = " Load average: %.2f %.2f %.2f  Uptime: %s" \
+            % (av1, av2, av3, str(uptime).split('.')[0])
+    print_line(line)
+
+def refresh_window(procs, procs_status):
+    """Print results on screen by using curses."""
+    curses.endwin()
+    templ = "%-6s %-8s %4s %5s %5s %6s %4s %9s  %2s"
+    win.erase()
+    header = templ % ("PID", "USER", "NI", "VIRT", "RES", "CPU%", "MEM%",
+                      "TIME+", "NAME")
+    print_header(procs_status, len(procs))
+    print_line("")
+    print_line(header, highlight=True)
+    for p in procs:
+        # TIME+ column shows process CPU cumulative time and it
+        # is expressed as: "mm:ss.ms"
+        if p.dict['cpu_times'] != None:
+            ctime = timedelta(seconds=sum(p.dict['cpu_times']))
+            ctime = "%s:%s.%s" % (ctime.seconds // 60 % 60,
+                                  str((ctime.seconds % 60)).zfill(2),
+                                  str(ctime.microseconds)[:2])
+        else:
+            ctime = ''
+        if p.dict['memory_percent'] is not None:
+            p.dict['memory_percent'] = round(p.dict['memory_percent'], 1)
+        else:
+            p.dict['memory_percent'] = ''
+        if p.dict['cpu_percent'] is None:
+            p.dict['cpu_percent'] = ''
+        line = templ % (p.pid,
+                        p.dict['username'][:8],
+                        p.dict['nice'],
+                        bytes2human(getattr(p.dict['memory_info'], 'vms', 0)),
+                        bytes2human(getattr(p.dict['memory_info'], 'rss', 0)),
+                        p.dict['cpu_percent'],
+                        p.dict['memory_percent'],
+                        ctime,
+                        p.dict['name'] or '',
+                        )
+        try:
+            print_line(line)
+        except curses.error:
+            break
+        win.refresh()
+
+
+def main():
+    try:
+        interval = 0
+        while 1:
+            args = poll(interval)
+            refresh_window(*args)
+            interval = 1
+    except (KeyboardInterrupt, SystemExit):
+        pass
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/python/psutil/examples/who.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+#
+# $Id: who.py 1340 2012-06-09 13:42:21Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""
+A clone of 'who' command; print information about users who are
+currently logged in.
+"""
+
+import sys
+from datetime import datetime
+
+import psutil
+from psutil._compat import print_
+
+
+def main():
+    users = psutil.get_users()
+    for user in users:
+        print_("%-15s %-15s %s  (%s)" % \
+            (user.name,
+             user.terminal or '-',
+             datetime.fromtimestamp(user.started).strftime("%Y-%m-%d %H:%M"),
+             user.host)
+        )
+
+if __name__ == '__main__':
+    main()
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/__init__.py
@@ -0,0 +1,1195 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# $Id: __init__.py 1525 2012-08-16 16:32:03Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""psutil is a module providing convenience functions for managing
+processes and gather system information in a portable way by using
+Python.
+"""
+
+from __future__ import division
+
+__version__ = "0.6.1"
+version_info = tuple([int(num) for num in __version__.split('.')])
+
+__all__ = [
+    # exceptions
+    "Error", "NoSuchProcess", "AccessDenied", "TimeoutExpired",
+    # constants
+    "NUM_CPUS", "TOTAL_PHYMEM", "BOOT_TIME",
+    "version_info", "__version__",
+    "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP",
+    "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD",
+    "STATUS_WAKING", "STATUS_LOCKED",
+    # classes
+    "Process", "Popen",
+    # functions
+    "test", "pid_exists", "get_pid_list", "process_iter", "get_process_list",
+    "virtual_memory", "swap_memory",
+    "cpu_times", "cpu_percent", "per_cpu_percent",
+    "network_io_counters", "disk_io_counters",
+    ]
+
+import sys
+import os
+import time
+import signal
+import warnings
+import errno
+import subprocess
+try:
+    import pwd
+except ImportError:
+    pwd = None
+
+from psutil.error import Error, NoSuchProcess, AccessDenied, TimeoutExpired
+from psutil._compat import property, callable, defaultdict
+from psutil._common import cached_property
+from psutil._common import (deprecated as _deprecated,
+                            nt_disk_iostat as _nt_disk_iostat,
+                            nt_net_iostat as _nt_net_iostat,
+                            nt_sysmeminfo as _nt_sysmeminfo,
+                            isfile_strict as _isfile_strict)
+from psutil._common import (STATUS_RUNNING, STATUS_IDLE, STATUS_SLEEPING,
+                            STATUS_DISK_SLEEP, STATUS_STOPPED,
+                            STATUS_TRACING_STOP, STATUS_ZOMBIE, STATUS_DEAD,
+                            STATUS_WAKING, STATUS_LOCKED)
+
+# import the appropriate module for our platform only
+if sys.platform.startswith("linux"):
+    import psutil._pslinux as _psplatform
+    from psutil._pslinux import (phymem_buffers,
+                                 cached_phymem,
+                                 IOPRIO_CLASS_NONE,
+                                 IOPRIO_CLASS_RT,
+                                 IOPRIO_CLASS_BE,
+                                 IOPRIO_CLASS_IDLE)
+    phymem_buffers = _psplatform.phymem_buffers
+    cached_phymem = _psplatform.cached_phymem
+
+elif sys.platform.startswith("win32"):
+    import psutil._psmswindows as _psplatform
+    from psutil._psmswindows import (ABOVE_NORMAL_PRIORITY_CLASS,
+                                     BELOW_NORMAL_PRIORITY_CLASS,
+                                     HIGH_PRIORITY_CLASS,
+                                     IDLE_PRIORITY_CLASS,
+                                     NORMAL_PRIORITY_CLASS,
+                                     REALTIME_PRIORITY_CLASS)
+
+elif sys.platform.startswith("darwin"):
+    import psutil._psosx as _psplatform
+
+elif sys.platform.startswith("freebsd"):
+    import psutil._psbsd as _psplatform
+
+else:
+    raise NotImplementedError('platform %s is not supported' % sys.platform)
+
+__all__.extend(_psplatform.__extra__all__)
+
+NUM_CPUS = _psplatform.NUM_CPUS
+BOOT_TIME = _psplatform.BOOT_TIME
+TOTAL_PHYMEM = _psplatform.TOTAL_PHYMEM
+
+
+class Process(object):
+    """Represents an OS process."""
+
+    def __init__(self, pid):
+        """Create a new Process object for the given pid.
+        Raises NoSuchProcess if pid does not exist.
+        """
+        self._pid = pid
+        self._gone = False
+        # platform-specific modules define an _psplatform.Process
+        # implementation class
+        self._platform_impl = _psplatform.Process(pid)
+        self._last_sys_cpu_times = None
+        self._last_proc_cpu_times = None
+        # cache creation time for later use in is_running() method
+        try:
+            self.create_time
+        except AccessDenied:
+            pass
+        except NoSuchProcess:
+            raise NoSuchProcess(pid, None, 'no process found with pid %s' % pid)
+
+    def __str__(self):
+        try:
+            pid = self.pid
+            name = repr(self.name)
+        except NoSuchProcess:
+            details = "(pid=%s (terminated))" % self.pid
+        except AccessDenied:
+            details = "(pid=%s)" % (self.pid)
+        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 as_dict(self, attrs=[], ad_value=None):
+        """Utility method returning process information as a hashable
+        dictionary.
+
+        If 'attrs' is specified it must be a list of strings reflecting
+        available Process class's attribute names (e.g. ['get_cpu_times',
+        'name']) else all public (read only) attributes are assumed.
+
+        'ad_value' is the value which gets assigned to a dict key in case
+        AccessDenied exception is raised when retrieving that particular
+        process information.
+        """
+        excluded_names = set(['send_signal', 'suspend', 'resume', 'terminate',
+                              'kill', 'wait', 'is_running', 'as_dict', 'parent',
+                              'get_children', 'nice'])
+        retdict = dict()
+        for name in set(attrs or dir(self)):
+            if name.startswith('_'):
+                continue
+            if name.startswith('set_'):
+                continue
+            if name in excluded_names:
+                continue
+            try:
+                attr = getattr(self, name)
+                if callable(attr):
+                    if name == 'get_cpu_percent':
+                        ret = attr(interval=0)
+                    else:
+                        ret = attr()
+                else:
+                    ret = attr
+            except AccessDenied:
+                ret = ad_value
+            except NotImplementedError:
+                # in case of not implemented functionality (may happen
+                # on old or exotic systems) we want to crash only if
+                # the user explicitly asked for that particular attr
+                if attrs:
+                    raise
+                continue
+            if name.startswith('get'):
+                if name[3] == '_':
+                    name = name[4:]
+                elif name == 'getcwd':
+                    name = 'cwd'
+            retdict[name] = ret
+        return retdict
+
+    @property
+    def pid(self):
+        """The process pid."""
+        return self._pid
+
+    @cached_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 parent
+        pid is known return None.
+        """
+        ppid = self.ppid
+        if ppid is not None:
+            try:
+                return Process(ppid)
+            except NoSuchProcess:
+                pass
+
+    @cached_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".
+            try:
+                cmdline = self.cmdline
+            except AccessDenied:
+                pass
+            else:
+                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
+
+    @cached_property
+    def exe(self):
+        """The process executable path. May also be an empty string."""
+        def guess_it(fallback):
+            # try to guess exe from cmdline[0] in absence of a native
+            # exe representation
+            cmdline = self.cmdline
+            if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'):
+                exe = cmdline[0]  # the possible exe
+                rexe = os.path.realpath(exe)  # ...in case it's a symlink
+                if os.path.isabs(rexe) and os.path.isfile(rexe) \
+                and os.access(rexe, os.X_OK):
+                    return exe
+            if isinstance(fallback, AccessDenied):
+                raise fallback
+            return fallback
+
+        try:
+            exe = self._platform_impl.get_process_exe()
+        except AccessDenied:
+            err = sys.exc_info()[1]
+            return guess_it(fallback=err)
+        else:
+            if not exe:
+                # underlying implementation can legitimately return an
+                # empty string; if that's the case we don't want to
+                # raise AD while guessing from the cmdline
+                try:
+                    exe = guess_it(fallback=exe)
+                except AccessDenied:
+                    pass
+            return exe
+
+    @cached_property
+    def cmdline(self):
+        """The command line process has been called with."""
+        return self._platform_impl.get_process_cmdline()
+
+    @property
+    def status(self):
+        """The process current status as a STATUS_* constant."""
+        return self._platform_impl.get_process_status()
+
+    if os.name == 'posix':
+
+        @property
+        def uids(self):
+            """Return a named tuple denoting the process real,
+            effective, and saved user ids.
+            """
+            return self._platform_impl.get_process_uids()
+
+        @property
+        def gids(self):
+            """Return a named tuple denoting the process real,
+            effective, and saved group ids.
+            """
+            return self._platform_impl.get_process_gids()
+
+        @property
+        def terminal(self):
+            """The terminal associated with this process, if any,
+            else None.
+            """
+            return self._platform_impl.get_process_terminal()
+
+    @property
+    def username(self):
+        """The name of the user that owns the process.
+        On UNIX this is calculated by using *real* process uid.
+        """
+        if os.name == 'posix':
+            if pwd is None:
+                # might happen if python was installed from sources
+                raise ImportError("requires pwd module shipped with standard python")
+            return pwd.getpwuid(self.uids.real).pw_name
+        else:
+            return self._platform_impl.get_process_username()
+
+    @cached_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()
+
+    def getcwd(self):
+        """Return a string representing the process current working
+        directory.
+        """
+        return self._platform_impl.get_process_cwd()
+
+    # Linux, BSD and Windows only
+    if hasattr(_psplatform.Process, "get_process_io_counters"):
+
+        def get_io_counters(self):
+            """Return process I/O statistics as a namedtuple including
+            the number of read/write calls performed and the amount of
+            bytes read and written by the process.
+            """
+            return self._platform_impl.get_process_io_counters()
+
+    def get_nice(self):
+        """Get process niceness (priority)."""
+        return self._platform_impl.get_process_nice()
+
+    def set_nice(self, value):
+        """Set process niceness (priority)."""
+        return self._platform_impl.set_process_nice(value)
+
+    # available only on Linux
+    if hasattr(_psplatform.Process, "get_process_ionice"):
+
+        def get_ionice(self):
+            """Return process I/O niceness (priority) as a namedtuple."""
+            return self._platform_impl.get_process_ionice()
+
+        def set_ionice(self, ioclass, value=None):
+            """Set process I/O niceness (priority).
+            ioclass is one of the IOPRIO_CLASS_* constants.
+            iodata is a number which goes from 0 to 7. The higher the
+            value, the lower the I/O priority of the process.
+            """
+            return self._platform_impl.set_process_ionice(ioclass, value)
+
+    # available on Windows and Linux only
+    if hasattr(_psplatform.Process, "get_process_cpu_affinity"):
+
+        def get_cpu_affinity(self):
+            """Get process current CPU affinity."""
+            return self._platform_impl.get_process_cpu_affinity()
+
+        def set_cpu_affinity(self, cpus):
+            """Set process current CPU affinity.
+            'cpus' is a list of CPUs for which you want to set the
+            affinity (e.g. [0, 1]).
+            """
+            return self._platform_impl.set_process_cpu_affinity(cpus)
+
+    if os.name == 'nt':
+
+        def get_num_handles(self):
+            """Return the number of handles opened by this process
+            (Windows only).
+            """
+            return self._platform_impl.get_num_handles()
+
+    if os.name == 'posix':
+
+        def get_num_fds(self):
+            """Return the number of file descriptors opened by this
+            process (POSIX only).
+            """
+            return self._platform_impl.get_num_fds()
+
+    def get_num_ctx_switches(self):
+        """Return the number voluntary and involuntary context switches
+        performed by this process.
+        """
+        return self._platform_impl.get_num_ctx_switches()
+
+    def get_num_threads(self):
+        """Return the number of threads used by this process."""
+        return self._platform_impl.get_process_num_threads()
+
+    def get_threads(self):
+        """Return threads opened by process as a list of namedtuples
+        including thread id and thread CPU times (user/system).
+        """
+        return self._platform_impl.get_process_threads()
+
+    def get_children(self, recursive=False):
+        """Return the children of this process as a list of Process
+        objects.
+        If recursive is True return all the parent descendants.
+
+        Example (A == this process):
+
+         A ─┐
+            │
+            ├─ B (child) ─┐
+            │             └─ X (grandchild) ─┐
+            │                                └─ Y (great grandchild)
+            ├─ C (child)
+            └─ D (child)
+
+        >>> p.get_children()
+        B, C, D
+        >>> p.get_children(recursive=True)
+        B, X, Y, C, D
+
+        Note that in the example above if process X disappears
+        process Y won't be returned either as the reference to
+        process A is lost.
+        """
+        if not self.is_running():
+            name = self._platform_impl._process_name
+            raise NoSuchProcess(self.pid, name)
+
+        ret = []
+        if not recursive:
+            for p in process_iter():
+                try:
+                    if p.ppid == self.pid:
+                        # if child happens to be older than its parent
+                        # (self) it means child's PID has been reused
+                        if self.create_time <= p.create_time:
+                            ret.append(p)
+                except NoSuchProcess:
+                    pass
+        else:
+            # construct a dict where 'values' are all the processes
+            # having 'key' as their parent
+            table = defaultdict(list)
+            for p in process_iter():
+                try:
+                    table[p.ppid].append(p)
+                except NoSuchProcess:
+                    pass
+            # At this point we have a mapping table where table[self.pid]
+            # are the current process's children.
+            # Below, we look for all descendants recursively, similarly
+            # to a recursive function call.
+            checkpids = [self.pid]
+            for pid in checkpids:
+                for child in table[pid]:
+                    try:
+                        # if child happens to be older than its parent
+                        # (self) it means child's PID has been reused
+                        intime = self.create_time <= child.create_time
+                    except NoSuchProcess:
+                        pass
+                    else:
+                        if intime:
+                            ret.append(child)
+                            if child.pid not in checkpids:
+                                checkpids.append(child.pid)
+        return ret
+
+    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
+        # on posix a percentage > 100 is legitimate
+        # http://stackoverflow.com/questions/1032357/comprehending-top-cpu-usage
+        # on windows we use this ugly hack to avoid troubles with float
+        # precision issues
+        if os.name != 'posix':
+            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
+        times. The same as os.times() but per-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_ext_memory_info(self):
+        """Return a namedtuple with variable fields depending on the
+        platform representing extended memory information about
+        the process. All numbers are expressed in bytes.
+        """
+        return self._platform_impl.get_ext_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_memory_maps(self, grouped=True):
+        """Return process's mapped memory regions as a list of nameduples
+        whose fields are variable depending on the platform.
+
+        If 'grouped' is True the mapped regions with the same 'path'
+        are grouped together and the different memory fields are summed.
+
+        If 'grouped' is False every mapped region is shown as a single
+        entity and the namedtuple will also include the mapped region's
+        address space ('addr') and permission set ('perms').
+        """
+        it = self._platform_impl.get_memory_maps()
+        if grouped:
+            d = {}
+            for tupl in it:
+                path = tupl[2]
+                nums = tupl[3:]
+                try:
+                    d[path] = map(lambda x, y: x+y, d[path], nums)
+                except KeyError:
+                    d[path] = nums
+            nt = self._platform_impl.nt_mmap_grouped
+            return [nt(path, *d[path]) for path in d]
+        else:
+            nt = self._platform_impl.nt_mmap_ext
+            return [nt(*x) for x in it]
+
+    def get_open_files(self):
+        """Return files opened by process as a list of namedtuples
+        including absolute file name and file descriptor number.
+        """
+        return self._platform_impl.get_open_files()
+
+    def get_connections(self, kind='inet'):
+        """Return connections opened by process as a list of namedtuples.
+        The kind parameter filters for connections that fit the following
+        criteria:
+
+        Kind Value      Connections using
+        inet            IPv4 and IPv6
+        inet4           IPv4
+        inet6           IPv6
+        tcp             TCP
+        tcp4            TCP over IPv4
+        tcp6            TCP over IPv6
+        udp             UDP
+        udp4            UDP over IPv4
+        udp6            UDP over IPv6
+        unix            UNIX socket (both UDP and TCP protocols)
+        all             the sum of all the possible families and protocols
+        """
+        return self._platform_impl.get_connections(kind)
+
+    def is_running(self):
+        """Return whether this process is running."""
+        if self._gone:
+            return False
+        try:
+            # Checking if pid is alive is not enough as the pid might
+            # have been reused by another process.
+            # pid + creation time, on the other hand, is supposed to
+            # identify a process univocally.
+            return self.create_time == \
+                   self._platform_impl.get_process_create_time()
+        except NoSuchProcess:
+            self._gone = True
+            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 = sys.exc_info()[1]
+                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 wait(self, timeout=None):
+        """Wait for process to terminate and, if process is a children
+        of the current one also return its exit code, else None.
+        """
+        if timeout is not None and not timeout >= 0:
+            raise ValueError("timeout must be a positive integer")
+        return self._platform_impl.process_wait(timeout)
+
+    # --- deprecated API
+
+    @property
+    def nice(self):
+        """Get or set process niceness (priority).
+        Deprecated, use get_nice() instead.
+        """
+        msg = "this property is deprecated; use Process.get_nice() method instead"
+        warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
+        return self.get_nice()
+
+    @nice.setter
+    def nice(self, value):
+        # invoked on "p.nice = num"; change process niceness
+        # deprecated in favor of set_nice()
+        msg = "this property is deprecated; use Process.set_nice() method instead"
+        warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
+        return self.set_nice(value)
+
+
+class Popen(Process):
+    """A more convenient interface to stdlib subprocess module.
+    It starts a sub process and deals with it exactly as when using
+    subprocess.Popen class but in addition also provides all the
+    property and methods of psutil.Process class in a single interface:
+
+      >>> import psutil
+      >>> from subprocess import PIPE
+      >>> p = psutil.Popen(["/usr/bin/python", "-c", "print 'hi'"], stdout=PIPE)
+      >>> p.name
+      'python'
+      >>> p.uids
+      user(real=1000, effective=1000, saved=1000)
+      >>> p.username
+      'giampaolo'
+      >>> p.communicate()
+      ('hi\n', None)
+      >>> p.terminate()
+      >>> p.wait(timeout=2)
+      0
+      >>>
+
+    For method names common to both classes such as kill(), terminate()
+    and wait(), psutil.Process implementation takes precedence.
+
+    For a complete documentation refers to:
+    http://docs.python.org/library/subprocess.html
+    """
+
+    def __init__(self, *args, **kwargs):
+        self.__subproc = subprocess.Popen(*args, **kwargs)
+        self._pid = self.__subproc.pid
+        self._gone = False
+        self._platform_impl = _psplatform.Process(self._pid)
+        self._last_sys_cpu_times = None
+        self._last_proc_cpu_times = None
+        try:
+            self.create_time
+        except AccessDenied:
+            pass
+        except NoSuchProcess:
+            raise NoSuchProcess(self._pid, None,
+                                "no process found with pid %s" % pid)
+
+    def __dir__(self):
+        return list(set(dir(Popen) + dir(subprocess.Popen)))
+
+    def __getattribute__(self, name):
+        try:
+            return object.__getattribute__(self, name)
+        except AttributeError:
+            try:
+                return object.__getattribute__(self.__subproc, name)
+            except AttributeError:
+                raise AttributeError("%s instance has no attribute '%s'"
+                                      %(self.__class__.__name__, name))
+
+
+# =====================================================================
+# --- system processes related functions
+# =====================================================================
+
+get_pid_list = _psplatform.get_pid_list
+pid_exists = _psplatform.pid_exists
+
+_pmap = {}
+
+def process_iter():
+    """Return a generator yielding a Process class instance for all
+    running processes on the local machine.
+
+    Every new Process instance is only created once and then cached
+    into an internal table which is updated every time this is used.
+
+    The sorting order in which processes are yielded is based on
+    their PIDs.
+    """
+    def add(pid):
+        proc = Process(pid)
+        _pmap[proc.pid] = proc
+        return proc
+
+    def remove(pid):
+        _pmap.pop(pid, None)
+
+    a = set(get_pid_list())
+    b = set(_pmap.keys())
+    new_pids = a - b
+    gone_pids = b - a
+
+    for pid in gone_pids:
+        remove(pid)
+    for pid, proc in sorted(list(_pmap.items()) + \
+                            list(dict.fromkeys(new_pids).items())):
+        try:
+            if proc is None:  # new process
+                yield add(pid)
+            else:
+                # use is_running() to check whether PID has been reused by
+                # another process in which case yield a new Process instance
+                if proc.is_running():
+                    yield proc
+                else:
+                    yield add(pid)
+        except NoSuchProcess:
+            remove(pid)
+        except AccessDenied:
+            # Process creation time can't be determined hence there's
+            # no way to tell whether the pid of the cached process
+            # has been reused. Just return the cached version.
+            yield proc
+
+# =====================================================================
+# --- CPU related functions
+# =====================================================================
+
+def cpu_times(percpu=False):
+    """Return system-wide CPU times as a namedtuple object.
+    Every CPU time 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)
+
+    When percpu is True return a list of nameduples for each CPU.
+    First element of the list refers to first CPU, second element
+    to second CPU and so on.
+    The order of the list is consistent across calls.
+    """
+    if not percpu:
+        return _psplatform.get_system_cpu_times()
+    else:
+        return _psplatform.get_system_per_cpu_times()
+
+
+_last_cpu_times = cpu_times()
+_last_per_cpu_times = cpu_times(percpu=True)
+
+def cpu_percent(interval=0.1, percpu=False):
+    """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.
+
+    When percpu is True returns a list of floats representing the
+    utilization as a percentage for each CPU.
+    First element of the list refers to first CPU, second element
+    to second CPU and so on.
+    The order of the list is consistent across calls.
+    """
+    global _last_cpu_times
+    global _last_per_cpu_times
+    blocking = interval is not None and interval > 0.0
+
+    def calculate(t1, t2):
+        t1_all = sum(t1)
+        t1_busy = t1_all - t1.idle
+
+        t2_all = sum(t2)
+        t2_busy = t2_all - t2.idle
+
+        # 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)
+
+    # system-wide usage
+    if not percpu:
+        if blocking:
+            t1 = cpu_times()
+            time.sleep(interval)
+        else:
+            t1 = _last_cpu_times
+        _last_cpu_times = cpu_times()
+        return calculate(t1, _last_cpu_times)
+    # per-cpu usage
+    else:
+        ret = []
+        if blocking:
+            tot1 = cpu_times(percpu=True)
+            time.sleep(interval)
+        else:
+            tot1 = _last_per_cpu_times
+        _last_per_cpu_times = cpu_times(percpu=True)
+        for t1, t2 in zip(tot1, _last_per_cpu_times):
+            ret.append(calculate(t1, t2))
+        return ret
+
+# =====================================================================
+# --- system memory related functions
+# =====================================================================
+
+def virtual_memory():
+    """Return statistics about system memory usage as a namedtuple
+    including the following fields, expressed in bytes:
+
+     - total:
+       total physical memory available.
+
+     - available:
+       the actual amount of available memory that can be given
+       instantly to processes that request more memory in bytes; this
+       is calculated by summing different memory values depending on
+       the platform (e.g. free + buffers + cached on Linux) and it is
+       supposed to be used to monitor actual memory usage in a cross
+       platform fashion.
+
+     - percent:
+       the percentage usage calculated as (total - available) / total * 100
+
+     - used:
+       memory used, calculated differently depending on the platform and
+       designed for informational purposes only:
+        OSX: active + inactive + wired
+        BSD: active + wired + cached
+        LINUX: total - free
+
+     - free:
+       memory not being used at all (zeroed) that is readily available;
+       note that this doesn't reflect the actual memory available
+       (use 'available' instead)
+
+    Platform-specific fields:
+
+     - active (UNIX):
+       memory currently in use or very recently used, and so it is in RAM.
+
+     - inactive (UNIX):
+       memory that is marked as not used.
+
+     - buffers (BSD, Linux):
+       cache for things like file system metadata.
+
+     - cached (BSD, OSX):
+       cache for various things.
+
+     - wired (OSX, BSD):
+       memory that is marked to always stay in RAM. It is never moved to disk.
+
+     - shared (BSD):
+       memory that may be simultaneously accessed by multiple processes.
+
+    The sum of 'used' and 'available' does not necessarily equal total.
+    On Windows 'available' and 'free' are the same.
+    """
+    return _psplatform.virtual_memory()
+
+def swap_memory():
+    """Return system swap memory statistics as a namedtuple including
+    the following attributes:
+
+     - total:   total swap memory in bytes
+     - used:    used swap memory in bytes
+     - free:    free swap memory in bytes
+     - percent: the percentage usage
+     - sin:     no. of bytes the system has swapped in from disk (cumulative)
+     - sout:    no. of bytes the system has swapped out from disk (cumulative)
+
+    'sin' and 'sout' on Windows are meaningless and always set to 0.
+    """
+    return _psplatform.swap_memory()
+
+# =====================================================================
+# --- disks/paritions related functions
+# =====================================================================
+
+def disk_usage(path):
+    """Return disk usage statistics about the given path as a namedtuple
+    including total, used and free space expressed in bytes plus the
+    percentage usage.
+    """
+    return _psplatform.get_disk_usage(path)
+
+def disk_partitions(all=False):
+    """Return mounted partitions as a list of namedtuples including
+    device, mount point, filesystem type and mount options (a raw
+    string separated by commas which may vary depending on the platform).
+
+    If "all" parameter is False return physical devices only and ignore
+    all others.
+    """
+    return _psplatform.disk_partitions(all)
+
+def disk_io_counters(perdisk=False):
+    """Return system disk I/O statistics as a namedtuple including
+    the following attributes:
+
+     - read_count:  number of reads
+     - write_count: number of writes
+     - read_bytes:  number of bytes read
+     - write_bytes: number of bytes written
+     - read_time:   time spent reading from disk (in milliseconds)
+     - write_time:  time spent writing to disk (in milliseconds)
+
+    If perdisk is True return the same information for every
+    physical disk installed on the system as a dictionary
+    with partition names as the keys and the namedutuple
+    described above as the values.
+    """
+    rawdict = _psplatform.disk_io_counters()
+    if not rawdict:
+        raise RuntimeError("couldn't find any physical disk")
+    if perdisk:
+        for disk, fields in rawdict.items():
+            rawdict[disk] = _nt_disk_iostat(*fields)
+        return rawdict
+    else:
+        return _nt_disk_iostat(*[sum(x) for x in zip(*rawdict.values())])
+
+# =====================================================================
+# --- network related functions
+# =====================================================================
+
+def network_io_counters(pernic=False):
+    """Return network I/O statistics as a namedtuple including
+    the following attributes:
+
+     - bytes_sent:   number of bytes sent
+     - bytes_recv:   number of bytes received
+     - packets_sent: number of packets sent
+     - packets_recv: number of packets received
+     - errin:        total number of errors while receiving
+     - errout:       total number of errors while sending
+     - dropin:       total number of incoming packets which were dropped
+     - dropout:      total number of outgoing packets which were dropped
+                     (always 0 on OSX and BSD)
+
+    If pernic is True return the same information for every
+    network interface installed on the system as a dictionary
+    with network interface names as the keys and the namedtuple
+    described above as the values.
+    """
+    rawdict = _psplatform.network_io_counters()
+    if not rawdict:
+        raise RuntimeError("couldn't find any network interface")
+    if pernic:
+        for nic, fields in rawdict.items():
+            rawdict[nic] = _nt_net_iostat(*fields)
+        return rawdict
+    else:
+        return _nt_net_iostat(*[sum(x) for x in zip(*rawdict.values())])
+
+# =====================================================================
+# --- other system related functions
+# =====================================================================
+
+def get_users():
+    """Return users currently connected on the system as a list of
+    namedtuples including the following attributes.
+
+     - user: the name of the user
+     - terminal: the tty or pseudo-tty associated with the user, if any.
+     - host: the host name associated with the entry, if any.
+     - started: the creation time as a floating point number expressed in
+       seconds since the epoch.
+    """
+    return _psplatform.get_system_users()
+
+# =====================================================================
+# --- deprecated functions
+# =====================================================================
+
+@_deprecated()
+def get_process_list():
+    """Return a list of Process class instances for all running
+    processes on the local machine (deprecated).
+    """
+    return list(process_iter())
+
+@_deprecated()
+def phymem_usage():
+    """Return the amount of total, used and free physical memory
+    on the system in bytes plus the percentage usage.
+    Deprecated by psutil.virtual_memory().
+    """
+    mem = virtual_memory()
+    return _nt_sysmeminfo(mem.total, mem.used, mem.free, mem.percent)
+
+@_deprecated("psutil.swap_memory()")
+def virtmem_usage():
+    return swap_memory()
+
+@_deprecated("psutil.phymem_usage().free")
+def avail_phymem():
+    return phymem_usage().free
+
+@_deprecated("psutil.phymem_usage().used")
+def used_phymem():
+    return phymem_usage().used
+
+@_deprecated("psutil.virtmem_usage().total")
+def total_virtmem():
+    return virtmem_usage().total
+
+@_deprecated("psutil.virtmem_usage().used")
+def used_virtmem():
+    return virtmem_usage().used
+
+@_deprecated("psutil.virtmem_usage().free")
+def avail_virtmem():
+    return virtmem_usage().free
+
+def test():
+    """List info of all currently running processes emulating ps aux
+    output.
+    """
+    import datetime
+    from psutil._compat import print_
+
+    today_day = datetime.date.today()
+    templ = "%-10s %5s %4s %4s %7s %7s %-13s %5s %7s  %s"
+    attrs = ['pid', 'username', 'get_cpu_percent', 'get_memory_percent', 'name',
+             'get_cpu_times', 'create_time', 'get_memory_info']
+    if os.name == 'posix':
+        attrs.append('terminal')
+    print_(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", "START",
+                    "TIME", "COMMAND"))
+    for p in sorted(process_iter(), key=lambda p: p.pid):
+        try:
+            pinfo = p.as_dict(attrs, ad_value='')
+        except NoSuchProcess:
+            pass
+        else:
+            if pinfo['create_time']:
+                ctime = datetime.datetime.fromtimestamp(pinfo['create_time'])
+                if ctime.date() == today_day:
+                    ctime = ctime.strftime("%H:%M")
+                else:
+                    ctime = ctime.strftime("%b%d")
+            cputime = time.strftime("%M:%S", time.localtime(sum(pinfo['cpu_times'])))
+            user = pinfo['username']
+            if os.name == 'nt' and '\\' in user:
+                user = user.split('\\')[1]
+            vms = pinfo['memory_info'] and \
+                  int(pinfo['memory_info'].vms / 1024) or '?'
+            rss = pinfo['memory_info'] and \
+                  int(pinfo['memory_info'].rss / 1024) or '?'
+            memp = pinfo['memory_percent'] and \
+                   round(pinfo['memory_percent'], 1) or '?'
+            print_(templ % (user[:10],
+                            pinfo['pid'],
+                            pinfo['cpu_percent'],
+                            memp,
+                            vms,
+                            rss,
+                            pinfo.get('terminal', '') or '?',
+                            ctime,
+                            cputime,
+                            pinfo['name'].strip() or '?'))
+
+if __name__ == "__main__":
+    test()
+
+del property, cached_property, division
+if sys.version_info < (3, 0):
+    del num
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_common.py
@@ -0,0 +1,193 @@
+#/usr/bin/env python
+#
+#$Id: _common.py 1524 2012-08-16 15:06:32Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Common objects shared by all _ps* modules."""
+
+from __future__ import division
+import sys
+import os
+import stat
+import errno
+import warnings
+
+from psutil._compat import namedtuple, long, wraps
+
+# --- functions
+
+def usage_percent(used, total, _round=None):
+    """Calculate percentage usage of 'used' against 'total'."""
+    try:
+        ret = (used / total) * 100
+    except ZeroDivisionError:
+        ret = 0
+    if _round is not None:
+        return round(ret, _round)
+    else:
+        return ret
+
+class constant(int):
+    """A constant type; overrides base int to provide a useful name on str()."""
+
+    def __new__(cls, value, name, doc=None):
+        inst = super(constant, cls).__new__(cls, value)
+        inst._name = name
+        if doc is not None:
+            inst.__doc__ = doc
+        return inst
+
+    def __str__(self):
+        return self._name
+
+    def __eq__(self, other):
+        # Use both int or str values when comparing for equality
+        # (useful for serialization):
+        # >>> st = constant(0, "running")
+        # >>> st == 0
+        # True
+        # >>> st == 'running'
+        # True
+        if isinstance(other, int):
+            return int(self) == other
+        if isinstance(other, long):
+            return long(self) == other
+        if isinstance(other, str):
+            return self._name == other
+        return False
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+def memoize(f):
+    """A simple memoize decorator for functions."""
+    cache= {}
+    def memf(*x):
+        if x not in cache:
+            cache[x] = f(*x)
+        return cache[x]
+    return memf
+
+class cached_property(object):
+    """A memoize decorator for class properties."""
+    enabled = True
+
+    def __init__(self, func):
+        self.func = func
+
+    def __get__(self, instance, type):
+        ret = self.func(instance)
+        if self.enabled:
+            instance.__dict__[self.func.__name__] = ret
+        return ret
+
+# http://goo.gl/jYLvf
+def deprecated(replacement=None):
+    """A decorator which can be used to mark functions as deprecated."""
+    def outer(fun):
+        msg = "psutil.%s is deprecated" % fun.__name__
+        if replacement is not None:
+            msg += "; use %s instead" % replacement
+        if fun.__doc__ is None:
+            fun.__doc__ = msg
+
+        @wraps(fun)
+        def inner(*args, **kwargs):
+            warnings.warn(msg, category=DeprecationWarning, stacklevel=2)
+            return fun(*args, **kwargs)
+
+        return inner
+    return outer
+
+
+def isfile_strict(path):
+    """Same as os.path.isfile() but does not swallow EACCES / EPERM
+    exceptions, see:
+    http://mail.python.org/pipermail/python-dev/2012-June/120787.html
+    """
+    try:
+        st = os.stat(path)
+    except OSError:
+        err = sys.exc_info()[1]
+        if err.errno in (errno.EPERM, errno.EACCES):
+            raise
+        return False
+    else:
+        return stat.S_ISREG(st.st_mode)
+
+
+# --- constants
+
+STATUS_RUNNING = constant(0, "running")
+STATUS_SLEEPING = constant(1, "sleeping")
+STATUS_DISK_SLEEP = constant(2, "disk sleep")
+STATUS_STOPPED = constant(3, "stopped")
+STATUS_TRACING_STOP = constant(4, "tracing stop")
+STATUS_ZOMBIE = constant(5, "zombie")
+STATUS_DEAD = constant(6, "dead")
+STATUS_WAKE_KILL = constant(7, "wake kill")
+STATUS_WAKING = constant(8, "waking")
+STATUS_IDLE = constant(9, "idle")  # BSD
+STATUS_LOCKED = constant(10, "locked")  # BSD
+STATUS_WAITING = constant(11, "waiting")  # BSD
+
+# --- Process.get_connections() 'kind' parameter mapping
+
+import socket
+from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM
+AF_INET6 = getattr(socket, 'AF_INET6', None)
+AF_UNIX = getattr(socket, 'AF_UNIX', None)
+
+conn_tmap = {
+    "all"  : ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]),
+    "tcp"  : ([AF_INET, AF_INET6], [SOCK_STREAM]),
+    "tcp4" : ([AF_INET],           [SOCK_STREAM]),
+    "udp"  : ([AF_INET, AF_INET6], [SOCK_DGRAM]),
+    "udp4" : ([AF_INET],           [SOCK_DGRAM]),
+    "inet" : ([AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]),
+    "inet4": ([AF_INET],           [SOCK_STREAM, SOCK_DGRAM]),
+    "inet6": ([AF_INET6],          [SOCK_STREAM, SOCK_DGRAM]),
+}
+
+if AF_INET6 is not None:
+    conn_tmap.update({
+        "tcp6" : ([AF_INET6],          [SOCK_STREAM]),
+        "udp6" : ([AF_INET6],          [SOCK_DGRAM]),
+    })
+
+if AF_UNIX is not None:
+    conn_tmap.update({
+        "unix" : ([AF_UNIX],           [SOCK_STREAM, SOCK_DGRAM]),
+    })
+
+
+del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM, socket
+
+# --- namedtuples
+
+# system
+nt_sys_cputimes = namedtuple('cputimes', 'user nice system idle iowait irq softirq')
+nt_sysmeminfo = namedtuple('usage', 'total used free percent')
+# XXX - would 'available' be better than 'free' as for virtual_memory() nt?
+nt_swapmeminfo = namedtuple('swap', 'total used free percent sin sout')
+nt_diskinfo = namedtuple('usage', 'total used free percent')
+nt_partition = namedtuple('partition',  'device mountpoint fstype opts')
+nt_net_iostat = namedtuple('iostat',
+    'bytes_sent bytes_recv packets_sent packets_recv errin errout dropin dropout')
+nt_disk_iostat = namedtuple('iostat', 'read_count write_count read_bytes write_bytes read_time write_time')
+nt_user = namedtuple('user', 'name terminal host started')
+
+# processes
+nt_meminfo = namedtuple('meminfo', 'rss vms')
+nt_cputimes = namedtuple('cputimes', 'user system')
+nt_openfile = namedtuple('openfile', 'path fd')
+nt_connection = namedtuple('connection', 'fd family type local_address remote_address status')
+nt_thread = namedtuple('thread', 'id user_time system_time')
+nt_uids = namedtuple('user', 'real effective saved')
+nt_gids = namedtuple('group', 'real effective saved')
+nt_io = namedtuple('io', 'read_count write_count read_bytes write_bytes')
+nt_ionice = namedtuple('ionice', 'ioclass value')
+nt_ctxsw = namedtuple('amount', 'voluntary involuntary')
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_compat.py
@@ -0,0 +1,270 @@
+#!/usr/bin/env python
+#
+# $Id: _compat.py 1524 2012-08-16 15:06:32Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Module which provides compatibility with older Python versions."""
+
+__all__ = ["PY3", "int", "long", "xrange", "exec_", "callable",
+           "namedtuple", "property", "defaultdict"]
+
+import sys
+
+
+# --- python 2/3 compatibility layer
+
+PY3 = sys.version_info >= (3,)
+
+try:
+    import __builtin__
+except ImportError:
+    import builtins as __builtin__  # py3
+
+if PY3:
+    int = int
+    long = int
+    xrange = range
+    exec_ = getattr(__builtin__, "exec")
+    print_ = getattr(__builtin__, "print")
+else:
+    int = int
+    long = long
+    xrange = xrange
+
+    def exec_(code, globs=None, locs=None):
+        if globs is None:
+            frame = _sys._getframe(1)
+            globs = frame.f_globals
+            if locs is None:
+                locs = frame.f_locals
+            del frame
+        elif locs is None:
+            locs = globs
+        exec("""exec code in globs, locs""")
+
+    def print_(s):
+        sys.stdout.write(s + '\n')
+        sys.stdout.flush()
+
+
+# removed in 3.0, reintroduced in 3.2
+try:
+    callable = callable
+except Exception:
+    def callable(obj):
+        for klass in type(obj).__mro__:
+            if "__call__" in klass.__dict__:
+                return True
+        return False
+
+
+# --- stdlib additions
+
+try:
+    from collections import namedtuple
+except ImportError:
+    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:
+            sys.stdout.write(template + '\n')
+            sys.stdout.flush()
+
+        # Execute the template string in a temporary namespace
+        namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename,
+                         _property=property, _tuple=tuple)
+        try:
+            exec_(template, namespace)
+        except SyntaxError:
+            e = sys.exc_info()[1]
+            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
+
+
+# hack to support property.setter/deleter on python < 2.6
+# http://docs.python.org/library/functions.html?highlight=property#property
+if hasattr(property, 'setter'):
+    property = property
+else:
+    class property(__builtin__.property):
+        __metaclass__ = type
+
+        def __init__(self, fget, *args, **kwargs):
+            super(property, self).__init__(fget, *args, **kwargs)
+            self.__doc__ = fget.__doc__
+
+        def getter(self, method):
+            return property(method, self.fset, self.fdel)
+
+        def setter(self, method):
+            return property(self.fget, method, self.fdel)
+
+        def deleter(self, method):
+            return property(self.fget, self.fset, method)
+
+
+# py 2.5 collections.defauldict
+# Taken from:
+# http://code.activestate.com/recipes/523034-emulate-collectionsdefaultdict/
+# credits: Jason Kirtland
+try:
+    from collections import defaultdict
+except ImportError:
+    class defaultdict(dict):
+
+        def __init__(self, default_factory=None, *a, **kw):
+            if (default_factory is not None and
+                not hasattr(default_factory, '__call__')):
+                raise TypeError('first argument must be callable')
+            dict.__init__(self, *a, **kw)
+            self.default_factory = default_factory
+
+        def __getitem__(self, key):
+            try:
+                return dict.__getitem__(self, key)
+            except KeyError:
+                return self.__missing__(key)
+
+        def __missing__(self, key):
+            if self.default_factory is None:
+                raise KeyError(key)
+            self[key] = value = self.default_factory()
+            return value
+
+        def __reduce__(self):
+            if self.default_factory is None:
+                args = tuple()
+            else:
+                args = self.default_factory,
+            return type(self), args, None, None, self.items()
+
+        def copy(self):
+            return self.__copy__()
+
+        def __copy__(self):
+            return type(self)(self.default_factory, self)
+
+        def __deepcopy__(self, memo):
+            import copy
+            return type(self)(self.default_factory,
+                              copy.deepcopy(self.items()))
+
+        def __repr__(self):
+            return 'defaultdict(%s, %s)' % (self.default_factory,
+                                            dict.__repr__(self))
+
+
+# py 2.5 functools.wraps
+try:
+    from functools import wraps
+except ImportError:
+    def wraps(original):
+        def inner(fn):
+            # see functools.WRAPPER_ASSIGNMENTS
+            for attribute in ['__module__',
+                              '__name__',
+                              '__doc__'
+                              ]:
+                setattr(fn, attribute, getattr(original, attribute))
+            # see functools.WRAPPER_UPDATES
+            for attribute in ['__dict__',
+                              ]:
+                if hasattr(fn, attribute):
+                    getattr(fn, attribute).update(getattr(original, attribute))
+                else:
+                    setattr(fn, attribute,
+                            getattr(original, attribute).copy())
+            return fn
+        return inner
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psbsd.py
@@ -0,0 +1,328 @@
+#!/usr/bin/env python
+#
+# $Id: _psbsd.py 1498 2012-07-24 21:41:28Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""FreeBSD platform implementation."""
+
+import errno
+import os
+import sys
+
+import _psutil_bsd
+import _psutil_posix
+from psutil import _psposix
+from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired
+from psutil._compat import namedtuple
+from psutil._common import *
+
+__extra__all__ = []
+
+# --- constants
+
+NUM_CPUS = _psutil_bsd.get_num_cpus()
+BOOT_TIME = _psutil_bsd.get_system_boot_time()
+TOTAL_PHYMEM = _psutil_bsd.get_virtual_mem()[0]
+_TERMINAL_MAP = _psposix._get_terminal_map()
+_PAGESIZE = os.sysconf("SC_PAGE_SIZE")
+_cputimes_ntuple = namedtuple('cputimes', 'user nice system idle irq')
+
+# --- public functions
+
+nt_virtmem_info = namedtuple('vmem', ' '.join([
+    # all platforms
+    'total', 'available', 'percent', 'used', 'free',
+    # FreeBSD specific
+    'active',
+    'inactive',
+    'buffers',
+    'cached',
+    'shared',
+    'wired']))
+
+def virtual_memory():
+    """System virtual memory as a namedutple."""
+    mem =  _psutil_bsd.get_virtual_mem()
+    total, free, active, inactive, wired, cached, buffers, shared = mem
+    avail = inactive + cached + free
+    used =  active + wired + cached
+    percent = usage_percent((total - avail), total, _round=1)
+    return nt_virtmem_info(total, avail, percent, used, free,
+                           active, inactive, buffers, cached, shared, wired)
+
+def swap_memory():
+    """System swap memory as (total, used, free, sin, sout) namedtuple."""
+    total, used, free, sin, sout = \
+        [x * _PAGESIZE for x in _psutil_bsd.get_swap_mem()]
+    percent = usage_percent(used, total, _round=1)
+    return nt_swapmeminfo(total, used, free, percent, sin, sout)
+
+def get_system_cpu_times():
+    """Return system per-CPU times as a named tuple"""
+    user, nice, system, idle, irq = _psutil_bsd.get_system_cpu_times()
+    return _cputimes_ntuple(user, nice, system, idle, irq)
+
+def get_system_per_cpu_times():
+    """Return system CPU times as a named tuple"""
+    ret = []
+    for cpu_t in _psutil_bsd.get_system_per_cpu_times():
+        user, nice, system, idle, irq = cpu_t
+        item = _cputimes_ntuple(user, nice, system, idle, irq)
+        ret.append(item)
+    return ret
+
+# XXX
+# Ok, this is very dirty.
+# On FreeBSD < 8 we cannot gather per-cpu information, see:
+# http://code.google.com/p/psutil/issues/detail?id=226
+# If NUM_CPUS > 1, on first call we return single cpu times to avoid a
+# crash at psutil import time.
+# Next calls will fail with NotImplementedError
+if not hasattr(_psutil_bsd, "get_system_per_cpu_times"):
+    def get_system_per_cpu_times():
+        if NUM_CPUS == 1:
+            return [get_system_cpu_times]
+        if get_system_per_cpu_times.__called__:
+            raise NotImplementedError("supported only starting from FreeBSD 8")
+        get_system_per_cpu_times.__called__ = True
+        return [get_system_cpu_times]
+get_system_per_cpu_times.__called__ = False
+
+def disk_partitions(all=False):
+    retlist = []
+    partitions = _psutil_bsd.get_disk_partitions()
+    for partition in partitions:
+        device, mountpoint, fstype, opts = partition
+        if device == 'none':
+            device = ''
+        if not all:
+            if not os.path.isabs(device) \
+            or not os.path.exists(device):
+                continue
+        ntuple = nt_partition(device, mountpoint, fstype, opts)
+        retlist.append(ntuple)
+    return retlist
+
+def get_system_users():
+    retlist = []
+    rawlist = _psutil_bsd.get_system_users()
+    for item in rawlist:
+        user, tty, hostname, tstamp = item
+        if tty == '~':
+            continue  # reboot or shutdown
+        nt = nt_user(user, tty or None, hostname, tstamp)
+        retlist.append(nt)
+    return retlist
+
+get_pid_list = _psutil_bsd.get_pid_list
+pid_exists = _psposix.pid_exists
+get_disk_usage = _psposix.get_disk_usage
+network_io_counters = _psutil_bsd.get_network_io_counters
+disk_io_counters = _psutil_bsd.get_disk_io_counters
+
+
+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 = sys.exc_info()[1]
+            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
+
+_status_map = {
+    _psutil_bsd.SSTOP : STATUS_STOPPED,
+    _psutil_bsd.SSLEEP : STATUS_SLEEPING,
+    _psutil_bsd.SRUN : STATUS_RUNNING,
+    _psutil_bsd.SIDL : STATUS_IDLE,
+    _psutil_bsd.SWAIT : STATUS_WAITING,
+    _psutil_bsd.SLOCK : STATUS_LOCKED,
+    _psutil_bsd.SZOMB : STATUS_ZOMBIE,
+}
+
+
+class Process(object):
+    """Wrapper class around underlying C implementation."""
+
+    __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)
+
+    @wrap_exceptions
+    def get_process_exe(self):
+        """Return process executable pathname."""
+        return _psutil_bsd.get_process_exe(self.pid)
+
+    @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_terminal(self):
+        tty_nr = _psutil_bsd.get_process_tty_nr(self.pid)
+        try:
+            return _TERMINAL_MAP[tty_nr]
+        except KeyError:
+            return None
+
+    @wrap_exceptions
+    def get_process_ppid(self):
+        """Return process parent pid."""
+        return _psutil_bsd.get_process_ppid(self.pid)
+
+    # XXX - available on FreeBSD >= 8 only
+    if hasattr(_psutil_bsd, "get_process_cwd"):
+        @wrap_exceptions
+        def get_process_cwd(self):
+            """Return process current working directory."""
+            # sometimes we get an empty string, in which case we turn
+            # it into None
+            return _psutil_bsd.get_process_cwd(self.pid) or None
+
+    @wrap_exceptions
+    def get_process_uids(self):
+        """Return real, effective and saved user ids."""
+        real, effective, saved = _psutil_bsd.get_process_uids(self.pid)
+        return nt_uids(real, effective, saved)
+
+    @wrap_exceptions
+    def get_process_gids(self):
+        """Return real, effective and saved group ids."""
+        real, effective, saved = _psutil_bsd.get_process_gids(self.pid)
+        return nt_gids(real, effective, saved)
+
+    @wrap_exceptions
+    def get_cpu_times(self):
+        """return a tuple containing process user/kernel time."""
+        user, system = _psutil_bsd.get_process_cpu_times(self.pid)
+        return nt_cputimes(user, system)
+
+    @wrap_exceptions
+    def get_memory_info(self):
+        """Return a tuple with the process' RSS and VMS size."""
+        rss, vms = _psutil_bsd.get_process_memory_info(self.pid)[:2]
+        return nt_meminfo(rss, vms)
+
+    _nt_ext_mem = namedtuple('meminfo', 'rss vms text data stack')
+
+    @wrap_exceptions
+    def get_ext_memory_info(self):
+        return self._nt_ext_mem(*_psutil_bsd.get_process_memory_info(self.pid))
+
+    @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)
+
+    @wrap_exceptions
+    def get_num_ctx_switches(self):
+        return nt_ctxsw(*_psutil_bsd.get_process_num_ctx_switches(self.pid))
+
+    @wrap_exceptions
+    def get_num_fds(self):
+        """Return the number of file descriptors opened by this process."""
+        return _psutil_bsd.get_process_num_fds(self.pid)
+
+    @wrap_exceptions
+    def get_process_threads(self):
+        """Return the number of threads belonging to the process."""
+        rawlist = _psutil_bsd.get_process_threads(self.pid)
+        retlist = []
+        for thread_id, utime, stime in rawlist:
+            ntuple = nt_thread(thread_id, utime, stime)
+            retlist.append(ntuple)
+        return retlist
+
+    @wrap_exceptions
+    def get_open_files(self):
+        """Return files opened by process as a list of namedtuples."""
+        # XXX - C implementation available on FreeBSD >= 8 only
+        # else fallback on lsof parser
+        if hasattr(_psutil_bsd, "get_process_open_files"):
+            rawlist = _psutil_bsd.get_process_open_files(self.pid)
+            return [nt_openfile(path, fd) for path, fd in rawlist]
+        else:
+            lsof = _psposix.LsofParser(self.pid, self._process_name)
+            return lsof.get_process_open_files()
+
+    @wrap_exceptions
+    def get_connections(self, kind='inet'):
+        """Return etwork connections opened by a process as a list of
+        namedtuples.
+        """
+        if kind not in conn_tmap:
+            raise ValueError("invalid %r kind argument; choose between %s"
+                             % (kind, ', '.join([repr(x) for x in conn_tmap])))
+        families, types = conn_tmap[kind]
+        ret = _psutil_bsd.get_process_connections(self.pid, families, types)
+        return [nt_connection(*conn) for conn in ret]
+
+    @wrap_exceptions
+    def process_wait(self, timeout=None):
+        try:
+            return _psposix.wait_pid(self.pid, timeout)
+        except TimeoutExpired:
+            raise TimeoutExpired(self.pid, self._process_name)
+
+    @wrap_exceptions
+    def get_process_nice(self):
+        return _psutil_posix.getpriority(self.pid)
+
+    @wrap_exceptions
+    def set_process_nice(self, value):
+        return _psutil_posix.setpriority(self.pid, value)
+
+    @wrap_exceptions
+    def get_process_status(self):
+        code = _psutil_bsd.get_process_status(self.pid)
+        if code in _status_map:
+            return _status_map[code]
+        return constant(-1, "?")
+
+    @wrap_exceptions
+    def get_process_io_counters(self):
+        rc, wc, rb, wb = _psutil_bsd.get_process_io_counters(self.pid)
+        return nt_io(rc, wc, rb, wb)
+
+    nt_mmap_grouped = namedtuple('mmap',
+        'path rss, private, ref_count, shadow_count')
+    nt_mmap_ext = namedtuple('mmap',
+        'addr, perms path rss, private, ref_count, shadow_count')
+
+    @wrap_exceptions
+    def get_memory_maps(self):
+        return _psutil_bsd.get_process_memory_maps(self.pid)
+
+    # FreeBSD < 8 does not support kinfo_getfile() and kinfo_getvmmap()
+    if not hasattr(_psutil_bsd, 'get_process_open_files'):
+        def _not_implemented(self):
+            raise NotImplementedError("supported only starting from FreeBSD 8")
+        get_open_files = _not_implemented
+        get_process_cwd = _not_implemented
+        get_memory_maps = _not_implemented
+        get_num_fds = _not_implemented
new file mode 100755
--- /dev/null
+++ b/python/psutil/psutil/_pslinux.py
@@ -0,0 +1,1011 @@
+#!/usr/bin/env python
+#
+# $Id: _pslinux.py 1513 2012-08-14 11:01:37Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Linux platform implementation."""
+
+from __future__ import division
+
+import os
+import errno
+import socket
+import struct
+import sys
+import base64
+import re
+
+import _psutil_posix
+import _psutil_linux
+from psutil import _psposix
+from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired
+from psutil._common import *
+from psutil._compat import PY3, xrange, long, namedtuple
+
+__extra__all__ = [
+    "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE",
+    "IOPRIO_CLASS_IDLE",
+    "phymem_buffers", "cached_phymem"]
+
+
+def _get_boot_time():
+    """Return system boot time (epoch in seconds)"""
+    f = open('/proc/stat', 'r')
+    try:
+        for line in f:
+            if line.startswith('btime'):
+                return float(line.strip().split()[1])
+        raise RuntimeError("line not found")
+    finally:
+        f.close()
+
+def _get_num_cpus():
+    """Return the number of CPUs on the system"""
+    # we try to determine num CPUs by using different approaches.
+    # SC_NPROCESSORS_ONLN seems to be the safer and it is also
+    # used by multiprocessing module
+    try:
+        return os.sysconf("SC_NPROCESSORS_ONLN")
+    except ValueError:
+        # as a second fallback we try to parse /proc/cpuinfo
+        num = 0
+        f = open('/proc/cpuinfo', 'r')
+        try:
+            lines = f.readlines()
+        finally:
+            f.close()
+        for line in lines:
+            if line.lower().startswith('processor'):
+                num += 1
+
+    # unknown format (e.g. amrel/sparc architectures), see:
+    # http://code.google.com/p/psutil/issues/detail?id=200
+    # try to parse /proc/stat as a last resort
+    if num == 0:
+        f = open('/proc/stat', 'r')
+        try:
+            lines = f.readlines()
+        finally:
+            f.close()
+        search = re.compile('cpu\d')
+        for line in lines:
+            line = line.split(' ')[0]
+            if search.match(line):
+                num += 1
+
+    if num == 0:
+        raise RuntimeError("can't determine number of CPUs")
+    return num
+
+
+# Number of clock ticks per second
+_CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"])
+_PAGESIZE = os.sysconf("SC_PAGE_SIZE")
+_TERMINAL_MAP = _psposix._get_terminal_map()
+BOOT_TIME = _get_boot_time()
+NUM_CPUS = _get_num_cpus()
+TOTAL_PHYMEM = _psutil_linux.get_sysinfo()[0]
+# ioprio_* constants http://linux.die.net/man/2/ioprio_get
+IOPRIO_CLASS_NONE = 0
+IOPRIO_CLASS_RT = 1
+IOPRIO_CLASS_BE = 2
+IOPRIO_CLASS_IDLE = 3
+
+# 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"
+                     }
+
+# --- system memory functions
+
+nt_virtmem_info = namedtuple('vmem', ' '.join([
+    # all platforms
+    'total', 'available', 'percent', 'used', 'free',
+    # linux specific
+    'active',
+    'inactive',
+    'buffers',
+    'cached']))
+
+def virtual_memory():
+    total, free, buffers, shared, _, _ = _psutil_linux.get_sysinfo()
+    cached = active = inactive = None
+    f = open('/proc/meminfo', 'r')
+    try:
+        for line in f:
+            if line.startswith('Cached:'):
+                cached = int(line.split()[1]) * 1024
+            elif line.startswith('Active:'):
+                active = int(line.split()[1]) * 1024
+            elif line.startswith('Inactive:'):
+                inactive = int(line.split()[1]) * 1024
+            if cached is not None \
+            and active is not None \
+            and inactive is not None:
+                break
+        else:
+            raise RuntimeError("line(s) not found")
+    finally:
+        f.close()
+    avail = free + buffers + cached
+    used = total - free
+    percent = usage_percent((total - avail), total, _round=1)
+    return nt_virtmem_info(total, avail, percent, used, free,
+                           active, inactive, buffers, cached)
+
+def swap_memory():
+    _, _, _, _, total, free = _psutil_linux.get_sysinfo()
+    used = total - free
+    percent = usage_percent(used, total, _round=1)
+    # get pgin/pgouts
+    f = open("/proc/vmstat", "r")
+    sin = sout = None
+    try:
+        for line in f:
+            # values are expressed in 4 kilo bytes, we want bytes instead
+            if line.startswith('pswpin'):
+                sin = int(line.split(' ')[1]) * 4 * 1024
+            elif line.startswith('pswpout'):
+                sout = int(line.split(' ')[1])  * 4 * 1024
+            if sin is not None and sout is not None:
+                break
+        else:
+            raise RuntimeError("line(s) not found")
+    finally:
+        f.close()
+    return nt_swapmeminfo(total, used, free, percent, sin, sout)
+
+# --- XXX deprecated memory functions
+
+@deprecated('psutil.virtual_memory().cached')
+def cached_phymem():
+    return virtual_memory().cached
+
+@deprecated('psutil.virtual_memory().buffers')
+def phymem_buffers():
+    return virtual_memory().buffers
+
+
+# --- system CPU functions
+
+def get_system_cpu_times():
+    """Return a named tuple representing the following CPU times:
+    user, nice, system, idle, iowait, irq, softirq.
+    """
+    f = open('/proc/stat', 'r')
+    try:
+        values = f.readline().split()
+    finally:
+        f.close()
+
+    values = values[1:8]
+    values = tuple([float(x) / _CLOCK_TICKS for x in values])
+    return nt_sys_cputimes(*values[:7])
+
+def get_system_per_cpu_times():
+    """Return a list of namedtuple representing the CPU times
+    for every CPU available on the system.
+    """
+    cpus = []
+    f = open('/proc/stat', 'r')
+    # get rid of the first line who refers to system wide CPU stats
+    try:
+        f.readline()
+        for line in f.readlines():
+            if line.startswith('cpu'):
+                values = line.split()[1:8]
+                values = tuple([float(x) / _CLOCK_TICKS for x in values])
+                entry = nt_sys_cputimes(*values[:7])
+                cpus.append(entry)
+        return cpus
+    finally:
+        f.close()
+
+
+# --- system disk functions
+
+def disk_partitions(all=False):
+    """Return mounted disk partitions as a list of nameduples"""
+    phydevs = []
+    f = open("/proc/filesystems", "r")
+    try:
+        for line in f:
+            if not line.startswith("nodev"):
+                phydevs.append(line.strip())
+    finally:
+        f.close()
+
+    retlist = []
+    partitions = _psutil_linux.get_disk_partitions()
+    for partition in partitions:
+        device, mountpoint, fstype, opts = partition
+        if device == 'none':
+            device = ''
+        if not all:
+            if device == '' or fstype not in phydevs:
+                continue
+        ntuple = nt_partition(device, mountpoint, fstype, opts)
+        retlist.append(ntuple)
+    return retlist
+
+get_disk_usage = _psposix.get_disk_usage
+
+
+# --- other sysetm functions
+
+def get_system_users():
+    """Return currently connected users as a list of namedtuples."""
+    retlist = []
+    rawlist = _psutil_linux.get_system_users()
+    for item in rawlist:
+        user, tty, hostname, tstamp, user_process = item
+        # XXX the underlying C function includes entries about
+        # system boot, run level and others.  We might want
+        # to use them in the future.
+        if not user_process:
+            continue
+        if hostname == ':0.0':
+            hostname = 'localhost'
+        nt = nt_user(user, tty or None, hostname, tstamp)
+        retlist.append(nt)
+    return retlist
+
+# --- process functions
+
+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()]
+    return pids
+
+def pid_exists(pid):
+    """Check For the existence of a unix pid."""
+    return _psposix.pid_exists(pid)
+
+def network_io_counters():
+    """Return network I/O statistics for every network interface
+    installed on the system as a dict of raw tuples.
+    """
+    f = open("/proc/net/dev", "r")
+    try:
+        lines = f.readlines()
+    finally:
+        f.close()
+
+    retdict = {}
+    for line in lines[2:]:
+        colon = line.find(':')
+        assert colon > 0, line
+        name = line[:colon].strip()
+        fields = line[colon+1:].strip().split()
+        bytes_recv = int(fields[0])
+        packets_recv = int(fields[1])
+        errin = int(fields[2])
+        dropin = int(fields[2])
+        bytes_sent = int(fields[8])
+        packets_sent = int(fields[9])
+        errout = int(fields[10])
+        dropout = int(fields[11])
+        retdict[name] = (bytes_sent, bytes_recv, packets_sent, packets_recv,
+                         errin, errout, dropin, dropout)
+    return retdict
+
+def disk_io_counters():
+    """Return disk I/O statistics for every disk installed on the
+    system as a dict of raw tuples.
+    """
+    # man iostat states that sectors are equivalent with blocks and
+    # have a size of 512 bytes since 2.4 kernels. This value is
+    # needed to calculate the amount of disk I/O in bytes.
+    SECTOR_SIZE = 512
+
+    # determine partitions we want to look for
+    partitions = []
+    f = open("/proc/partitions", "r")
+    try:
+        lines = f.readlines()[2:]
+    finally:
+        f.close()
+    for line in lines:
+        _, _, _, name = line.split()
+        if name[-1].isdigit():
+            partitions.append(name)
+    #
+    retdict = {}
+    f = open("/proc/diskstats", "r")
+    try:
+        lines = f.readlines()
+    finally:
+        f.close()
+    for line in lines:
+        _, _, name, reads, _, rbytes, rtime, writes, _, wbytes, wtime = \
+            line.split()[:11]
+        if name in partitions:
+            rbytes = int(rbytes) * SECTOR_SIZE
+            wbytes = int(wbytes) * SECTOR_SIZE
+            reads = int(reads)
+            writes = int(writes)
+            # TODO: times are expressed in milliseconds while OSX/BSD has
+            # these expressed in nanoseconds; figure this out.
+            rtime = int(rtime)
+            wtime = int(wtime)
+            retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime)
+    return retdict
+
+
+# taken from /fs/proc/array.c
+_status_map = {"R" : STATUS_RUNNING,
+               "S" : STATUS_SLEEPING,
+               "D" : STATUS_DISK_SLEEP,
+               "T" : STATUS_STOPPED,
+               "t" : STATUS_TRACING_STOP,
+               "Z" : STATUS_ZOMBIE,
+               "X" : STATUS_DEAD,
+               "x" : STATUS_DEAD,
+               "K" : STATUS_WAKE_KILL,
+               "W" : STATUS_WAKING}
+
+# --- 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 EnvironmentError:
+            # ENOENT (no such file or directory) gets raised on open().
+            # ESRCH (no such process) can get raised on read() if
+            # process is gone in meantime.
+            err = sys.exc_info()[1]
+            if err.errno in (errno.ENOENT, 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 Process(object):
+    """Linux process implementation."""
+
+    __slots__ = ["pid", "_process_name"]
+
+    def __init__(self, pid):
+        if not isinstance(pid, int):
+            raise TypeError('pid must be an integer')
+        self.pid = pid
+        self._process_name = None
+
+    @wrap_exceptions
+    def get_process_name(self):
+        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):
+        try:
+            exe = os.readlink("/proc/%s/exe" % self.pid)
+        except (OSError, IOError):
+            err = sys.exc_info()[1]
+            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
+
+        # readlink() might return paths containing null bytes causing
+        # problems when used with other fs-related functions (os.*,
+        # open(), ...)
+        exe = exe.replace('\x00', '')
+        # Certain names have ' (deleted)' appended. Usually this is
+        # bogus as the file actually exists. Either way that's not
+        # important as we don't want to discriminate executables which
+        # have been deleted.
+        if exe.endswith(" (deleted)") and not os.path.exists(exe):
+            exe = exe[:-10]
+        return exe
+
+    @wrap_exceptions
+    def get_process_cmdline(self):
+        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_process_terminal(self):
+        f = open("/proc/%s/stat" % self.pid)
+        try:
+            tty_nr = int(f.read().split(' ')[6])
+        finally:
+            f.close()
+        try:
+            return _TERMINAL_MAP[tty_nr]
+        except KeyError:
+            return None
+
+    @wrap_exceptions
+    def get_process_io_counters(self):
+        f = open("/proc/%s/io" % self.pid)
+        try:
+            for line in f:
+                if line.startswith("rchar"):
+                    read_count = int(line.split()[1])
+                elif line.startswith("wchar"):
+                    write_count = int(line.split()[1])
+                elif line.startswith("read_bytes"):
+                    read_bytes = int(line.split()[1])
+                elif line.startswith("write_bytes"):
+                    write_bytes = int(line.split()[1])
+            return nt_io(read_count, write_count, read_bytes, write_bytes)
+        finally:
+            f.close()
+
+    if not os.path.exists('/proc/%s/io' % os.getpid()):
+        def get_process_io_counters(self):
+            raise NotImplementedError('/proc/PID/io is not available')
+
+    @wrap_exceptions
+    def get_cpu_times(self):
+        f = open("/proc/%s/stat" % self.pid)
+        try:
+            st = f.read().strip()
+        finally:
+            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 nt_cputimes(utime, stime)
+
+    @wrap_exceptions
+    def process_wait(self, timeout=None):
+        try:
+            return _psposix.wait_pid(self.pid, timeout)
+        except TimeoutExpired:
+            raise TimeoutExpired(self.pid, self._process_name)
+
+    @wrap_exceptions
+    def get_process_create_time(self):
+        f = open("/proc/%s/stat" % self.pid)
+        try:
+            st = f.read().strip()
+        finally:
+            f.close()
+        # ignore the first two values ("pid (exe)")
+        st = st[st.rfind(')') + 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) + BOOT_TIME
+        return starttime
+
+    @wrap_exceptions
+    def get_memory_info(self):
+        f = open("/proc/%s/statm" % self.pid)
+        try:
+            vms, rss = f.readline().split()[:2]
+            return nt_meminfo(int(rss) * _PAGESIZE,
+                              int(vms) * _PAGESIZE)
+        finally:
+            f.close()
+
+    _nt_ext_mem = namedtuple('meminfo', 'rss vms shared text lib data dirty')
+
+    @wrap_exceptions
+    def get_ext_memory_info(self):
+        #  ============================================================
+        # | FIELD  | DESCRIPTION                         | AKA  | TOP  |
+        #  ============================================================
+        # | rss    | resident set size                   |      | RES  |
+        # | vms    | total program size                  | size | VIRT |
+        # | shared | shared pages (from shared mappings) |      | SHR  |
+        # | text   | text ('code')                       | trs  | CODE |
+        # | lib    | library (unused in Linux 2.6)       | lrs  |      |
+        # | data   | data + stack                        | drs  | DATA |
+        # | dirty  | dirty pages (unused in Linux 2.6)   | dt   |      |
+        #  ============================================================
+        f = open("/proc/%s/statm" % self.pid)
+        try:
+            vms, rss, shared, text, lib, data, dirty = \
+              [int(x) * _PAGESIZE for x in f.readline().split()[:7]]
+        finally:
+            f.close()
+        return self._nt_ext_mem(rss, vms, shared, text, lib, data, dirty)
+
+    _mmap_base_fields = ['path', 'rss', 'size', 'pss', 'shared_clean',
+                         'shared_dirty', 'private_clean', 'private_dirty',
+                         'referenced', 'anonymous', 'swap',]
+    nt_mmap_grouped = namedtuple('mmap', ' '.join(_mmap_base_fields))
+    nt_mmap_ext = namedtuple('mmap', 'addr perms ' + ' '.join(_mmap_base_fields))
+
+    def get_memory_maps(self):
+        """Return process's mapped memory regions as a list of nameduples.
+        Fields are explained in 'man proc'; here is an updated (Apr 2012)
+        version: http://goo.gl/fmebo
+        """
+        f = None
+        try:
+            f = open("/proc/%s/smaps" % self.pid)
+            first_line = f.readline()
+            current_block = [first_line]
+
+            def get_blocks():
+                data = {}
+                for line in f:
+                    fields = line.split(None, 5)
+                    if len(fields) >= 5:
+                        yield (current_block.pop(), data)
+                        current_block.append(line)
+                    else:
+                        data[fields[0]] = int(fields[1]) * 1024
+                yield (current_block.pop(), data)
+
+            if first_line:  # smaps file can be empty
+                for header, data in get_blocks():
+                    hfields = header.split(None, 5)
+                    try:
+                        addr, perms, offset, dev, inode, path = hfields
+                    except ValueError:
+                        addr, perms, offset, dev, inode, path = hfields + ['']
+                    if not path:
+                        path = '[anon]'
+                    else:
+                        path = path.strip()
+                    yield (addr, perms, path,
+                           data['Rss:'],
+                           data['Size:'],
+                           data.get('Pss:', 0),
+                           data['Shared_Clean:'], data['Shared_Clean:'],
+                           data['Private_Clean:'], data['Private_Dirty:'],
+                           data['Referenced:'],
+                           data['Anonymous:'],
+                           data['Swap:'])
+            f.close()
+        except EnvironmentError:
+            # XXX - Can't use wrap_exceptions decorator as we're
+            # returning a generator;  this probably needs some
+            # refactoring in order to avoid this code duplication.
+            if f is not None:
+                f.close()
+            err = sys.exc_info()[1]
+            if err.errno in (errno.ENOENT, 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
+        except:
+            if f is not None:
+                f.close()
+            raise
+
+    if not os.path.exists('/proc/%s/smaps' % os.getpid()):
+        def get_shared_libs(self, ext):
+            msg = "this Linux version does not support /proc/PID/smaps " \
+                  "(kernel < 2.6.14 or CONFIG_MMU kernel configuration " \
+                  "option is not enabled)"
+            raise NotImplementedError(msg)
+
+    @wrap_exceptions
+    def get_process_cwd(self):
+        # readlink() might return paths containing null bytes causing
+        # problems when used with other fs-related functions (os.*,
+        # open(), ...)
+        path = os.readlink("/proc/%s/cwd" % self.pid)
+        return path.replace('\x00', '')
+
+    @wrap_exceptions
+    def get_num_ctx_switches(self):
+        vol = unvol = None
+        f = open("/proc/%s/status" % self.pid)
+        try:
+            for line in f:
+                if line.startswith("voluntary_ctxt_switches"):
+                    vol = int(line.split()[1])
+                elif line.startswith("nonvoluntary_ctxt_switches"):
+                    unvol = int(line.split()[1])
+                if vol is not None and unvol is not None:
+                    return nt_ctxsw(vol, unvol)
+            raise RuntimeError("line not found")
+        finally:
+            f.close()
+
+    @wrap_exceptions
+    def get_process_num_threads(self):
+        f = open("/proc/%s/status" % self.pid)
+        try:
+            for line in f:
+                if line.startswith("Threads:"):
+                    return int(line.split()[1])
+            raise RuntimeError("line not found")
+        finally:
+            f.close()
+
+    @wrap_exceptions
+    def get_process_threads(self):
+        thread_ids = os.listdir("/proc/%s/task" % self.pid)
+        thread_ids.sort()
+        retlist = []
+        hit_enoent = False
+        for thread_id in thread_ids:
+            try:
+                f = open("/proc/%s/task/%s/stat" % (self.pid, thread_id))
+            except EnvironmentError:
+                err = sys.exc_info()[1]
+                if err.errno == errno.ENOENT:
+                    # no such file or directory; it means thread
+                    # disappeared on us
+                    hit_enoent = True
+                    continue
+                raise
+            try:
+                st = f.read().strip()
+            finally:
+                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
+            ntuple = nt_thread(int(thread_id), utime, stime)
+            retlist.append(ntuple)
+        if hit_enoent:
+            # raise NSP if the process disappeared on us
+            os.stat('/proc/%s' % self.pid)
+        return retlist
+
+    @wrap_exceptions
+    def get_process_nice(self):
+        #f = open('/proc/%s/stat' % self.pid, 'r')
+        #try:
+        #   data = f.read()
+        #   return int(data.split()[18])
+        #finally:
+        #   f.close()
+
+        # Use C implementation
+        return _psutil_posix.getpriority(self.pid)
+
+    @wrap_exceptions
+    def set_process_nice(self, value):
+        return _psutil_posix.setpriority(self.pid, value)
+
+    @wrap_exceptions
+    def get_process_cpu_affinity(self):
+        from_bitmask = lambda x: [i for i in xrange(64) if (1 << i) & x]
+        bitmask = _psutil_linux.get_process_cpu_affinity(self.pid)
+        return from_bitmask(bitmask)
+
+    @wrap_exceptions
+    def set_process_cpu_affinity(self, value):
+        def to_bitmask(l):
+            if not l:
+                raise ValueError("invalid argument %r" % l)
+            out = 0
+            for b in l:
+                if not isinstance(b, (int, long)) or b < 0:
+                    raise ValueError("invalid argument %r" % b)
+                out |= 2**b
+            return out
+
+        bitmask = to_bitmask(value)
+        try:
+            _psutil_linux.set_process_cpu_affinity(self.pid, bitmask)
+        except OSError:
+            err = sys.exc_info()[1]
+            if err.errno == errno.EINVAL:
+                allcpus = list(range(len(get_system_per_cpu_times())))
+                for cpu in value:
+                    if cpu not in allcpus:
+                        raise ValueError("invalid CPU %i" % cpu)
+            raise
+
+    # only starting from kernel 2.6.13
+    if hasattr(_psutil_linux, "ioprio_get"):
+
+        @wrap_exceptions
+        def get_process_ionice(self):
+            ioclass, value = _psutil_linux.ioprio_get(self.pid)
+            return nt_ionice(ioclass, value)
+
+        @wrap_exceptions
+        def set_process_ionice(self, ioclass, value):
+            if ioclass in (IOPRIO_CLASS_NONE, None):
+                if value:
+                    raise ValueError("can't specify value with IOPRIO_CLASS_NONE")
+                ioclass = IOPRIO_CLASS_NONE
+                value = 0
+            if ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE):
+                if value is None:
+                    value = 4
+            elif ioclass == IOPRIO_CLASS_IDLE:
+                if value:
+                    raise ValueError("can't specify value with IOPRIO_CLASS_IDLE")
+                value = 0
+            else:
+                value = 0
+            if not 0 <= value <= 8:
+                raise ValueError("value argument range expected is between 0 and 8")
+            return _psutil_linux.ioprio_set(self.pid, ioclass, value)
+
+    @wrap_exceptions
+    def get_process_status(self):
+        f = open("/proc/%s/status" % self.pid)
+        try:
+            for line in f:
+                if line.startswith("State:"):
+                    letter = line.split()[1]
+                    if letter in _status_map:
+                        return _status_map[letter]
+                    return constant(-1, '?')
+        finally:
+            f.close()
+
+    @wrap_exceptions
+    def get_open_files(self):
+        retlist = []
+        files = os.listdir("/proc/%s/fd" % self.pid)
+        hit_enoent = False
+        for fd in files:
+            file = "/proc/%s/fd/%s" % (self.pid, fd)
+            if os.path.islink(file):
+                try:
+                    file = os.readlink(file)
+                except OSError:
+                    # ENOENT == file which is gone in the meantime
+                    err = sys.exc_info()[1]
+                    if err.errno == errno.ENOENT:
+                        hit_enoent = True
+                        continue
+                    raise
+                else:
+                    # If file is not an absolute path there's no way
+                    # to tell whether it's a regular file or not,
+                    # so we skip it. A regular file is always supposed
+                    # to be absolutized though.
+                    if file.startswith('/') and isfile_strict(file):
+                        ntuple = nt_openfile(file, int(fd))
+                        retlist.append(ntuple)
+        if hit_enoent:
+            # raise NSP if the process disappeared on us
+            os.stat('/proc/%s' % self.pid)
+        return retlist
+
+    @wrap_exceptions
+    def get_connections(self, kind='inet'):
+        """Return connections opened by process as a list of namedtuples.
+        The kind parameter filters for connections that fit the following
+        criteria:
+
+        Kind Value      Number of connections using
+        inet            IPv4 and IPv6
+        inet4           IPv4
+        inet6           IPv6
+        tcp             TCP
+        tcp4            TCP over IPv4
+        tcp6            TCP over IPv6
+        udp             UDP
+        udp4            UDP over IPv4
+        udp6            UDP over IPv6
+        all             the sum of all the possible families and protocols
+        """
+        # Note: in case of UNIX sockets we're only able to determine the
+        # local bound path while the remote endpoint is not retrievable:
+        # http://goo.gl/R3GHM
+        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 = []
+            try:
+                f = open(file, 'r')
+            except IOError:
+                # IPv6 not supported on this platform
+                err = sys.exc_info()[1]
+                if err.errno == errno.ENOENT and file.endswith('6'):
+                    return []
+                else:
+                    raise
+            try:
+                f.readline()  # skip the first line
+                for line in f:
+                    # IPv4 / IPv6
+                    if family in (socket.AF_INET, socket.AF_INET6):
+                        _, 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 = nt_connection(fd, family, type_, laddr,
+                                                 raddr, status)
+                            retlist.append(conn)
+                    elif family == socket.AF_UNIX:
+                        tokens = line.split()
+                        _, _, _, _, type_, _, inode = tokens[0:7]
+                        if inode in inodes:
+
+                            if len(tokens) == 8:
+                                path = tokens[-1]
+                            else:
+                                path = ""
+                            fd = int(inodes[inode])
+                            type_ = int(type_)
+                            conn = nt_connection(fd, family, type_, path,
+                                                 None, "")
+                            retlist.append(conn)
+                    else:
+                        raise ValueError(family)
+                return retlist
+            finally:
+                f.close()
+
+        tcp4 = ("tcp" , socket.AF_INET , socket.SOCK_STREAM)
+        tcp6 = ("tcp6", socket.AF_INET6, socket.SOCK_STREAM)
+        udp4 = ("udp" , socket.AF_INET , socket.SOCK_DGRAM)
+        udp6 = ("udp6", socket.AF_INET6, socket.SOCK_DGRAM)
+        unix = ("unix", socket.AF_UNIX, None)
+
+        tmap = {
+            "all"  : (tcp4, tcp6, udp4, udp6, unix),
+            "tcp"  : (tcp4, tcp6),
+            "tcp4" : (tcp4,),
+            "tcp6" : (tcp6,),
+            "udp"  : (udp4, udp6),
+            "udp4" : (udp4,),
+            "udp6" : (udp6,),
+            "unix" : (unix,),
+            "inet" : (tcp4, tcp6, udp4, udp6),
+            "inet4": (tcp4, udp4),
+            "inet6": (tcp6, udp6),
+        }
+        if kind not in tmap:
+            raise ValueError("invalid %r kind argument; choose between %s"
+                             % (kind, ', '.join([repr(x) for x in tmap])))
+        ret = []
+        for f, family, type_ in tmap[kind]:
+            ret += process("/proc/net/%s" % f, family, type_)
+        # raise NSP if the process disappeared on us
+        os.stat('/proc/%s' % self.pid)
+        return ret
+
+
+#    --- lsof implementation
+#
+#    def get_connections(self):
+#        lsof = _psposix.LsofParser(self.pid, self._process_name)
+#        return lsof.get_process_connections()
+
+    @wrap_exceptions
+    def get_num_fds(self):
+       return len(os.listdir("/proc/%s/fd" % self.pid))
+
+    @wrap_exceptions
+    def get_process_ppid(self):
+        f = open("/proc/%s/status" % self.pid)
+        try:
+            for line in f:
+                if line.startswith("PPid:"):
+                    # PPid: nnnn
+                    return int(line.split()[1])
+            raise RuntimeError("line not found")
+        finally:
+            f.close()
+
+    @wrap_exceptions
+    def get_process_uids(self):
+        f = open("/proc/%s/status" % self.pid)
+        try:
+            for line in f:
+                if line.startswith('Uid:'):
+                    _, real, effective, saved, fs = line.split()
+                    return nt_uids(int(real), int(effective), int(saved))
+            raise RuntimeError("line not found")
+        finally:
+            f.close()
+
+    @wrap_exceptions
+    def get_process_gids(self):
+        f = open("/proc/%s/status" % self.pid)
+        try:
+            for line in f:
+                if line.startswith('Gid:'):
+                    _, real, effective, saved, fs = line.split()
+                    return nt_gids(int(real), int(effective), int(saved))
+            raise RuntimeError("line not found")
+        finally:
+            f.close()
+
+    @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 IP address portion is a little or big 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 PY3:
+            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:
+            # see: http://code.google.com/p/psutil/issues/detail?id=201
+            if sys.byteorder == 'little':
+                ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1])
+            else:
+                ip = socket.inet_ntop(family, base64.b16decode(ip))
+        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)
+            # see: http://code.google.com/p/psutil/issues/detail?id=201
+            if sys.byteorder == 'little':
+                ip = socket.inet_ntop(socket.AF_INET6,
+                                struct.pack('>4I', *struct.unpack('<4I', ip)))
+            else:
+                ip = socket.inet_ntop(socket.AF_INET6,
+                                struct.pack('<4I', *struct.unpack('<4I', ip)))
+        return (ip, port)
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psmswindows.py
@@ -0,0 +1,423 @@
+#!/usr/bin/env python
+#
+# $Id: _psmswindows.py 1514 2012-08-14 11:16:56Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Windows platform implementation."""
+
+import errno
+import os
+import sys
+import platform
+
+import _psutil_mswindows
+from _psutil_mswindows import ERROR_ACCESS_DENIED
+from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired
+from psutil._common import *
+from psutil._compat import PY3, xrange, long
+
+# Windows specific extended namespace
+__extra__all__ = ["ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS",
+                  "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS",
+                  "NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS"]
+
+
+# --- module level constants (gets pushed up to psutil module)
+
+NUM_CPUS = _psutil_mswindows.get_num_cpus()
+BOOT_TIME = _psutil_mswindows.get_system_uptime()
+TOTAL_PHYMEM = _psutil_mswindows.get_virtual_mem()[0]
+WAIT_TIMEOUT = 0x00000102 # 258 in decimal
+ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES, ERROR_ACCESS_DENIED])
+
+# process priority constants:
+# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
+from _psutil_mswindows import (ABOVE_NORMAL_PRIORITY_CLASS,
+                               BELOW_NORMAL_PRIORITY_CLASS,
+                               HIGH_PRIORITY_CLASS,
+                               IDLE_PRIORITY_CLASS,
+                               NORMAL_PRIORITY_CLASS,
+                               REALTIME_PRIORITY_CLASS,
+                               INFINITE)
+
+@memoize
+def _win32_QueryDosDevice(s):
+    return _psutil_mswindows.win32_QueryDosDevice(s)
+
+def _convert_raw_path(s):
+    # convert paths using native DOS format like:
+    # "\Device\HarddiskVolume1\Windows\systemew\file.txt"
+    # into: "C:\Windows\systemew\file.txt"
+    if PY3 and not isinstance(s, str):
+        s = s.decode('utf8')
+    rawdrive = '\\'.join(s.split('\\')[:3])
+    driveletter = _win32_QueryDosDevice(rawdrive)
+    return os.path.join(driveletter, s[len(rawdrive):])
+
+
+# --- public functions
+
+nt_virtmem_info = namedtuple('vmem', ' '.join([
+    # all platforms
+    'total', 'available', 'percent', 'used', 'free']))
+
+def virtual_memory():
+    """System virtual memory as a namedtuple."""
+    mem = _psutil_mswindows.get_virtual_mem()
+    totphys, availphys, totpagef, availpagef, totvirt, freevirt = mem
+    #
+    total = totphys
+    avail = availphys
+    free = availphys
+    used = total - avail
+    percent = usage_percent((total - avail), total, _round=1)
+    return nt_virtmem_info(total, avail, percent, used, free)
+
+def swap_memory():
+    """Swap system memory as a (total, used, free, sin, sout) tuple."""
+    mem = _psutil_mswindows.get_virtual_mem()
+    total = mem[2]
+    free = mem[3]
+    used = total - free
+    percent = usage_percent(used, total, _round=1)
+    return nt_swapmeminfo(total, used, free, percent, 0, 0)
+
+def get_disk_usage(path):
+    """Return disk usage associated with path."""
+    try:
+        total, free = _psutil_mswindows.get_disk_usage(path)
+    except WindowsError:
+        err = sys.exc_info()[1]
+        if not os.path.exists(path):
+            raise OSError(errno.ENOENT, "No such file or directory: '%s'" % path)
+        raise
+    used = total - free
+    percent = usage_percent(used, total, _round=1)
+    return nt_diskinfo(total, used, free, percent)
+
+def disk_partitions(all):
+    """Return disk partitions."""
+    rawlist = _psutil_mswindows.get_disk_partitions(all)
+    return [nt_partition(*x) for x in rawlist]
+
+
+_cputimes_ntuple = namedtuple('cputimes', 'user system idle')
+
+def get_system_cpu_times():
+    """Return system CPU times as a named tuple."""
+    user, system, idle = 0, 0, 0
+    # computes system global times summing each processor value
+    for cpu_time in _psutil_mswindows.get_system_cpu_times():
+        user += cpu_time[0]
+        system += cpu_time[1]
+        idle += cpu_time[2]
+    return _cputimes_ntuple(user, system, idle)
+
+def get_system_per_cpu_times():
+    """Return system per-CPU times as a list of named tuples."""
+    ret = []
+    for cpu_t in _psutil_mswindows.get_system_cpu_times():
+        user, system, idle = cpu_t
+        item = _cputimes_ntuple(user, system, idle)
+        ret.append(item)
+    return ret
+
+def get_system_users():
+    """Return currently connected users as a list of namedtuples."""
+    retlist = []
+    rawlist = _psutil_mswindows.get_system_users()
+    for item in rawlist:
+        user, hostname, tstamp = item
+        nt = nt_user(user, None, hostname, tstamp)
+        retlist.append(nt)
+    return retlist
+
+get_pid_list = _psutil_mswindows.get_pid_list
+pid_exists = _psutil_mswindows.pid_exists
+network_io_counters = _psutil_mswindows.get_network_io_counters
+disk_io_counters = _psutil_mswindows.get_disk_io_counters
+
+# --- 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 = sys.exc_info()[1]
+            if err.errno in ACCESS_DENIED_SET:
+                raise AccessDenied(self.pid, self._process_name)
+            if err.errno == errno.ESRCH:
+                raise NoSuchProcess(self.pid, self._process_name)
+            raise
+    return wrapper
+
+
+class Process(object):
+    """Wrapper class around underlying C implementation."""
+
+    __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)
+
+    @wrap_exceptions
+    def get_process_exe(self):
+        # Note: os.path.exists(path) may return False even if the file
+        # is there, see:
+        # http://stackoverflow.com/questions/3112546/os-path-exists-lies
+        return _convert_raw_path(_psutil_mswindows.get_process_exe(self.pid))
+
+    @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_raw_meminfo(self):
+        try:
+            return _psutil_mswindows.get_process_memory_info(self.pid)
+        except OSError:
+            err = sys.exc_info()[1]
+            if err.errno in ACCESS_DENIED_SET:
+                return _psutil_mswindows.get_process_memory_info_2(self.pid)
+            raise
+
+    @wrap_exceptions
+    def get_memory_info(self):
+        """Returns a tuple or RSS/VMS memory usage in bytes."""
+        # on Windows RSS == WorkingSetSize and VSM == PagefileUsage
+        # fields of PROCESS_MEMORY_COUNTERS struct:
+        # http://msdn.microsoft.com/en-us/library/windows/desktop/ms684877(v=vs.85).aspx
+        t = self._get_raw_meminfo()
+        return nt_meminfo(t[2], t[7])
+
+    _nt_ext_mem = namedtuple('meminfo',
+        ' '.join(['num_page_faults',
+                  'peak_wset',
+                  'wset',
+                  'peak_paged_pool',
+                  'paged_pool',
+                  'peak_nonpaged_pool',
+                  'nonpaged_pool',
+                  'pagefile',
+                  'peak_pagefile',
+                  'private',]))
+
+    @wrap_exceptions
+    def get_ext_memory_info(self):
+        return self._nt_ext_mem(*self._get_raw_meminfo())
+
+    nt_mmap_grouped = namedtuple('mmap', 'path rss')
+    nt_mmap_ext = namedtuple('mmap', 'addr perms path rss')
+
+    def get_memory_maps(self):
+        try:
+            raw = _psutil_mswindows.get_process_memory_maps(self.pid)
+        except OSError:
+            # XXX - can't use wrap_exceptions decorator as we're
+            # returning a generator; probably needs refactoring.
+            err = sys.exc_info()[1]
+            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
+        else:
+            for addr, perm, path, rss in raw:
+                path = _convert_raw_path(path)
+                addr = hex(addr)
+                yield (addr, perm, path, rss)
+
+    @wrap_exceptions
+    def kill_process(self):
+        """Terminates the process with the given PID."""
+        return _psutil_mswindows.kill_process(self.pid)
+
+    @wrap_exceptions
+    def process_wait(self, timeout=None):
+        if timeout is None:
+            timeout = INFINITE
+        else:
+            # WaitForSingleObject() expects time in milliseconds
+            timeout = int(timeout * 1000)
+        ret = _psutil_mswindows.process_wait(self.pid, timeout)
+        if ret == WAIT_TIMEOUT:
+            raise TimeoutExpired(self.pid, self._process_name)
+        return ret
+
+    @wrap_exceptions
+    def get_process_username(self):
+        """Return the name of the user that owns the process"""
+        if self.pid in (0, 4):
+            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 boot time
+        if self.pid in (0, 4):
+            return BOOT_TIME
+        try:
+            return _psutil_mswindows.get_process_create_time(self.pid)
+        except OSError:
+            err = sys.exc_info()[1]
+            if err.errno in ACCESS_DENIED_SET:
+                return _psutil_mswindows.get_process_create_time_2(self.pid)
+            raise
+
+    @wrap_exceptions
+    def get_process_num_threads(self):
+        return _psutil_mswindows.get_process_num_threads(self.pid)
+
+    @wrap_exceptions
+    def get_process_threads(self):
+        rawlist = _psutil_mswindows.get_process_threads(self.pid)
+        retlist = []
+        for thread_id, utime, stime in rawlist:
+            ntuple = nt_thread(thread_id, utime, stime)
+            retlist.append(ntuple)
+        return retlist
+
+    @wrap_exceptions
+    def get_cpu_times(self):
+        try:
+            ret = _psutil_mswindows.get_process_cpu_times(self.pid)
+        except OSError:
+            err = sys.exc_info()[1]
+            if err.errno in ACCESS_DENIED_SET:
+                ret = _psutil_mswindows.get_process_cpu_times_2(self.pid)
+            else:
+                raise
+        return nt_cputimes(*ret)
+
+    @wrap_exceptions
+    def suspend_process(self):
+        return _psutil_mswindows.suspend_process(self.pid)
+
+    @wrap_exceptions
+    def resume_process(self):
+        return _psutil_mswindows.resume_process(self.pid)
+
+    @wrap_exceptions
+    def get_process_cwd(self):
+        if self.pid in (0, 4):
+            raise AccessDenied(self.pid, self._process_name)
+        # 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):
+            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:
+            file = _convert_raw_path(file)
+            if isfile_strict(file) and file not in retlist:
+                ntuple = nt_openfile(file, -1)
+                retlist.append(ntuple)
+        return retlist
+
+    @wrap_exceptions
+    def get_connections(self, kind='inet'):
+        if kind not in conn_tmap:
+            raise ValueError("invalid %r kind argument; choose between %s"
+                             % (kind, ', '.join([repr(x) for x in conn_tmap])))
+        families, types = conn_tmap[kind]
+        ret = _psutil_mswindows.get_process_connections(self.pid, families, types)
+        return [nt_connection(*conn) for conn in ret]
+
+    @wrap_exceptions
+    def get_process_nice(self):
+        return _psutil_mswindows.get_process_priority(self.pid)
+
+    @wrap_exceptions
+    def set_process_nice(self, value):
+        return _psutil_mswindows.set_process_priority(self.pid, value)
+
+    @wrap_exceptions
+    def get_process_io_counters(self):
+        try:
+            ret = _psutil_mswindows.get_process_io_counters(self.pid)
+        except OSError:
+            err = sys.exc_info()[1]
+            if err.errno in ACCESS_DENIED_SET:
+                ret = _psutil_mswindows.get_process_io_counters_2(self.pid)
+            else:
+                raise
+        return nt_io(*ret)
+
+    @wrap_exceptions
+    def get_process_status(self):
+        suspended = _psutil_mswindows.is_process_suspended(self.pid)
+        if suspended:
+            return STATUS_STOPPED
+        else:
+            return STATUS_RUNNING
+
+    @wrap_exceptions
+    def get_process_cpu_affinity(self):
+        from_bitmask = lambda x: [i for i in xrange(64) if (1 << i) & x]
+        bitmask = _psutil_mswindows.get_process_cpu_affinity(self.pid)
+        return from_bitmask(bitmask)
+
+    @wrap_exceptions
+    def set_process_cpu_affinity(self, value):
+        def to_bitmask(l):
+            if not l:
+                raise ValueError("invalid argument %r" % l)
+            out = 0
+            for b in l:
+                if not isinstance(b, (int, long)) or b < 0:
+                    raise ValueError("invalid argument %r" % b)
+                out |= 2**b
+            return out
+
+        # SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER
+        # is returned for an invalid CPU but this seems not to be true,
+        # therefore we check CPUs validy beforehand.
+        allcpus = list(range(len(get_system_per_cpu_times())))
+        for cpu in value:
+            if cpu not in allcpus:
+                raise ValueError("invalid CPU %i" % cpu)
+
+        bitmask = to_bitmask(value)
+        _psutil_mswindows.set_process_cpu_affinity(self.pid, bitmask)
+
+    @wrap_exceptions
+    def get_num_handles(self):
+        try:
+            return _psutil_mswindows.get_process_num_handles(self.pid)
+        except OSError:
+            err = sys.exc_info()[1]
+            if err.errno in ACCESS_DENIED_SET:
+                return _psutil_mswindows.get_process_num_handles_2(self.pid)
+            raise
+
+    @wrap_exceptions
+    def get_num_ctx_switches(self):
+        return nt_ctxsw(*_psutil_mswindows.get_process_num_ctx_switches(self.pid))
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psosx.py
@@ -0,0 +1,293 @@
+#!/usr/bin/env python
+#
+# $Id: _psosx.py 1498 2012-07-24 21:41:28Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""OSX platform implementation."""
+
+import errno
+import os
+import sys
+
+import _psutil_osx
+import _psutil_posix
+from psutil import _psposix
+from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired
+from psutil._compat import namedtuple
+from psutil._common import *
+
+__extra__all__ = []
+
+# --- constants
+
+NUM_CPUS = _psutil_osx.get_num_cpus()
+BOOT_TIME = _psutil_osx.get_system_boot_time()
+TOTAL_PHYMEM = _psutil_osx.get_virtual_mem()[0]
+_PAGESIZE = os.sysconf("SC_PAGE_SIZE")
+_TERMINAL_MAP = _psposix._get_terminal_map()
+_cputimes_ntuple = namedtuple('cputimes', 'user nice system idle')
+
+# --- functions
+
+nt_virtmem_info = namedtuple('vmem', ' '.join([
+    # all platforms
+    'total', 'available', 'percent', 'used', 'free',
+    # OSX specific
+    'active',
+    'inactive',
+    'wired']))
+
+def virtual_memory():
+    """System virtual memory as a namedtuple."""
+    total, active, inactive, wired, free = _psutil_osx.get_virtual_mem()
+    avail = inactive + free
+    used = active + inactive + wired
+    percent = usage_percent((total - avail), total, _round=1)
+    return nt_virtmem_info(total, avail, percent, used, free,
+                           active, inactive, wired)
+
+def swap_memory():
+    """Swap system memory as a (total, used, free, sin, sout) tuple."""
+    total, used, free, sin, sout = _psutil_osx.get_swap_mem()
+    percent = usage_percent(used, total, _round=1)
+    return nt_swapmeminfo(total, used, free, percent, sin, sout)
+
+def get_system_cpu_times():
+    """Return system CPU times as a namedtuple."""
+    user, nice, system, idle = _psutil_osx.get_system_cpu_times()
+    return _cputimes_ntuple(user, nice, system, idle)
+
+def get_system_per_cpu_times():
+    """Return system CPU times as a named tuple"""
+    ret = []
+    for cpu_t in _psutil_osx.get_system_per_cpu_times():
+        user, nice, system, idle = cpu_t
+        item = _cputimes_ntuple(user, nice, system, idle)
+        ret.append(item)
+    return ret
+
+def disk_partitions(all=False):
+    retlist = []
+    partitions = _psutil_osx.get_disk_partitions()
+    for partition in partitions:
+        device, mountpoint, fstype, opts = partition
+        if device == 'none':
+            device = ''
+        if not all:
+            if not os.path.isabs(device) \
+            or not os.path.exists(device):
+                continue
+        ntuple = nt_partition(device, mountpoint, fstype, opts)
+        retlist.append(ntuple)
+    return retlist
+
+def get_system_users():
+    retlist = []
+    rawlist = _psutil_osx.get_system_users()
+    for item in rawlist:
+        user, tty, hostname, tstamp = item
+        if tty == '~':
+            continue  # reboot or shutdown
+        if not tstamp:
+            continue
+        nt = nt_user(user, tty or None, hostname or None, tstamp)
+        retlist.append(nt)
+    return retlist
+
+
+get_pid_list = _psutil_osx.get_pid_list
+pid_exists = _psposix.pid_exists
+get_disk_usage = _psposix.get_disk_usage
+network_io_counters = _psutil_osx.get_network_io_counters
+disk_io_counters = _psutil_osx.get_disk_io_counters
+
+# --- 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 = sys.exc_info()[1]
+            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
+
+
+_status_map = {
+    _psutil_osx.SIDL : STATUS_IDLE,
+    _psutil_osx.SRUN : STATUS_RUNNING,
+    _psutil_osx.SSLEEP : STATUS_SLEEPING,
+    _psutil_osx.SSTOP : STATUS_STOPPED,
+    _psutil_osx.SZOMB : STATUS_ZOMBIE,
+}
+
+class Process(object):
+    """Wrapper class around underlying C implementation."""
+
+    __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)
+
+    @wrap_exceptions
+    def get_process_exe(self):
+        return _psutil_osx.get_process_exe(self.pid)
+
+    @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_cwd(self):
+        return _psutil_osx.get_process_cwd(self.pid)
+
+    @wrap_exceptions
+    def get_process_uids(self):
+        real, effective, saved = _psutil_osx.get_process_uids(self.pid)
+        return nt_uids(real, effective, saved)
+
+    @wrap_exceptions
+    def get_process_gids(self):
+        real, effective, saved = _psutil_osx.get_process_gids(self.pid)
+        return nt_gids(real, effective, saved)
+
+    @wrap_exceptions
+    def get_process_terminal(self):
+        tty_nr = _psutil_osx.get_process_tty_nr(self.pid)
+        try:
+            return _TERMINAL_MAP[tty_nr]
+        except KeyError:
+            return None
+
+    @wrap_exceptions
+    def get_memory_info(self):
+        """Return a tuple with the process' RSS and VMS size."""
+        rss, vms = _psutil_osx.get_process_memory_info(self.pid)[:2]
+        return nt_meminfo(rss, vms)
+
+    _nt_ext_mem = namedtuple('meminfo', 'rss vms pfaults pageins')
+
+    @wrap_exceptions
+    def get_ext_memory_info(self):
+        """Return a tuple with the process' RSS and VMS size."""
+        rss, vms, pfaults, pageins = _psutil_osx.get_process_memory_info(self.pid)
+        return self._nt_ext_mem(rss, vms,
+                                pfaults * _PAGESIZE,
+                                pageins * _PAGESIZE)
+
+    @wrap_exceptions
+    def get_cpu_times(self):
+        user, system = _psutil_osx.get_process_cpu_times(self.pid)
+        return nt_cputimes(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_num_ctx_switches(self):
+        return nt_ctxsw(*_psutil_osx.get_process_num_ctx_switches(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)
+
+    @wrap_exceptions
+    def get_open_files(self):
+        """Return files opened by process."""
+        if self.pid == 0:
+            return []
+        files = []
+        rawlist = _psutil_osx.get_process_open_files(self.pid)
+        for path, fd in rawlist:
+            if isfile_strict(path):
+                ntuple = nt_openfile(path, fd)
+                files.append(ntuple)
+        return files
+
+    @wrap_exceptions
+    def get_connections(self, kind='inet'):
+        """Return etwork connections opened by a process as a list of
+        namedtuples.
+        """
+        if kind not in conn_tmap:
+            raise ValueError("invalid %r kind argument; choose between %s"
+                             % (kind, ', '.join([repr(x) for x in conn_tmap])))
+        families, types = conn_tmap[kind]
+        ret = _psutil_osx.get_process_connections(self.pid, families, types)
+        return [nt_connection(*conn) for conn in ret]
+
+    @wrap_exceptions
+    def get_num_fds(self):
+        if self.pid == 0:
+            return 0
+        return _psutil_osx.get_process_num_fds(self.pid)
+
+    @wrap_exceptions
+    def process_wait(self, timeout=None):
+        try:
+            return _psposix.wait_pid(self.pid, timeout)
+        except TimeoutExpired:
+            raise TimeoutExpired(self.pid, self._process_name)
+
+    @wrap_exceptions
+    def get_process_nice(self):
+        return _psutil_posix.getpriority(self.pid)
+
+    @wrap_exceptions
+    def set_process_nice(self, value):
+        return _psutil_posix.setpriority(self.pid, value)
+
+    @wrap_exceptions
+    def get_process_status(self):
+        code = _psutil_osx.get_process_status(self.pid)
+        if code in _status_map:
+            return _status_map[code]
+        return constant(-1, "?")
+
+    @wrap_exceptions
+    def get_process_threads(self):
+        """Return the number of threads belonging to the process."""
+        rawlist = _psutil_osx.get_process_threads(self.pid)
+        retlist = []
+        for thread_id, utime, stime in rawlist:
+            ntuple = nt_thread(thread_id, utime, stime)
+            retlist.append(ntuple)
+        return retlist
+
+    nt_mmap_grouped = namedtuple('mmap',
+        'path rss private swapped dirtied ref_count shadow_depth')
+    nt_mmap_ext = namedtuple('mmap',
+        'addr perms path rss private swapped dirtied ref_count shadow_depth')
+
+    @wrap_exceptions
+    def get_memory_maps(self):
+        return _psutil_osx.get_process_memory_maps(self.pid)
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psposix.py
@@ -0,0 +1,118 @@
+#!/usr/bin/env python
+#
+# $Id: _psposix.py 1409 2012-07-04 08:21:06Z g.rodola $
+#
+# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Routines common to all posix systems."""
+
+import os
+import errno
+import psutil
+import sys
+import time
+import glob
+
+from psutil.error import TimeoutExpired
+from psutil._common import nt_diskinfo, usage_percent
+
+
+def pid_exists(pid):
+    """Check whether pid exists in the current process table."""
+    if not isinstance(pid, int):
+        raise TypeError('an integer is required')
+    if pid < 0:
+        return False
+    try:
+        os.kill(pid, 0)
+    except OSError:
+        e = sys.exc_info()[1]
+        return e.errno == errno.EPERM
+    else:
+        return True
+
+def wait_pid(pid, timeout=None):
+    """Wait for process with pid 'pid' to terminate and return its
+    exit status code as an integer.
+
+    If pid is not a children of os.getpid() (current process) just
+    waits until the process disappears and return None.
+
+    If pid does not exist at all return None immediately.
+
+    Raise TimeoutExpired on timeout expired.
+    """
+    def check_timeout(delay):
+        if timeout is not None:
+            if time.time() >= stop_at:
+                raise TimeoutExpired(pid)
+        time.sleep(delay)
+        return min(delay * 2, 0.04)
+
+    if timeout is not None:
+        waitcall = lambda: os.waitpid(pid, os.WNOHANG)
+        stop_at = time.time() + timeout
+    else:
+        waitcall = lambda: os.waitpid(pid, 0)
+
+    delay = 0.0001
+    while 1:
+        try:
+            retpid, status = waitcall()
+        except OSError:
+            err = sys.exc_info()[1]
+            if err.errno == errno.EINTR:
+                delay = check_timeout(delay)
+                continue
+            elif err.errno == errno.ECHILD:
+                # This has two meanings:
+                # - pid is not a child of os.getpid() in which case
+                #   we keep polling until it's gone
+                # - pid never existed in the first place
+                # In both cases we'll eventually return None as we
+                # can't determine its exit status code.
+                while 1:
+                    if pid_exists(pid):
+                        delay = check_timeout(delay)
+                    else:
+                        return
+            else:
+                raise
+        else:
+            if retpid == 0:
+                # WNOHANG was used, pid is still running
+                delay = check_timeout(delay)
+                continue
+            # process exited due to a signal; return the integer of
+            # that signal
+            if os.WIFSIGNALED(status):
+                return os.WTERMSIG(status)
+            # process exited using exit(2) system call; return the
+            # integer exit(2) system call has been called with
+            elif os.WIFEXITED(status):
+                return os.WEXITSTATUS(status)
+            else:
+                # should never happen
+                raise RuntimeError("unknown process exit status")
+
+def get_disk_usage(path):
+    """Return disk usage associated with path."""
+    st = os.statvfs(path)
+    free = (st.f_bavail * st.f_frsize)
+    total = (st.f_blocks * st.f_frsize)
+    used = (st.f_blocks - st.f_bfree) * st.f_frsize
+    percent = usage_percent(used, total, _round=1)
+    # NB: the percentage is -5% than what shown by df due to
+    # reserved blocks that we are currently not considering:
+    # http://goo.gl/sWGbH
+    return nt_diskinfo(total, used, free, percent)
+
+def _get_terminal_map():
+    ret = {}
+    ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*')
+    for name in ls:
+        assert name not in ret
+        ret[os.stat(name).st_rdev] = name
+    return ret
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psutil_bsd.c
@@ -0,0 +1,1777 @@
+/*
+ * $Id: _psutil_bsd.c 1513 2012-08-14 11:01:37Z g.rodola $
+ *
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * 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 <fcntl.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/param.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/file.h>
+#include <net/route.h>
+
+#include <sys/socket.h>
+#include <sys/socketvar.h>    /* for struct socket */
+#include <sys/protosw.h>      /* for struct proto */
+#include <sys/domain.h>       /* for struct domain */
+
+#include <sys/un.h>           /* for unpcb struct (UNIX sockets) */
+#include <sys/unpcb.h>        /* for unpcb struct (UNIX sockets) */
+#include <sys/mbuf.h>         /* for mbuf struct (UNIX sockets) */
+/* for in_pcb struct */
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/in_pcb.h>
+#include <netinet/tcp_var.h>   /* for struct tcpcb */
+#include <netinet/tcp_fsm.h>   /* for TCP connection states */
+#include <arpa/inet.h>         /* for inet_ntop() */
+
+#if __FreeBSD_version < 900000
+    #include <utmp.h>         /* system users */
+#else
+    #include <utmpx.h>
+#endif
+#include <devstat.h>      /* get io counters */
+#include <sys/vmmeter.h>  /* needed for vmtotal struct */
+#include <libutil.h>      /* process open files, shared libs (kinfo_getvmmap) */
+#include <sys/mount.h>
+
+#include <net/if.h>       /* net io counters */
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>   /* process open files/connections */
+#include <sys/un.h>
+
+#include "_psutil_bsd.h"
+#include "_psutil_common.h"
+#include "arch/bsd/process_info.h"
+
+
+// convert a timeval struct to a double
+#define TV2DOUBLE(t)    ((t).tv_sec + (t).tv_usec / 1000000.0)
+
+
+/*
+ * 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.
+     */
+    if (size == 0) {
+        NoSuchProcess();
+        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 = NULL;
+
+    if (get_proc_list(&proclist, &num_processes) != 0) {
+        PyErr_SetString(PyExc_RuntimeError, "failed to retrieve process list.");
+        goto error;
+    }
+
+    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);
+            if (!pid)
+                goto error;
+            if (PyList_Append(retlist, pid))
+                goto error;
+            Py_DECREF(pid);
+            proclist++;
+        }
+        free(orig_address);
+    }
+
+    return retlist;
+
+error:
+    Py_XDECREF(pid);
+    Py_DECREF(retlist);
+    if (orig_address != NULL) {
+        free(orig_address);
+    }
+    return NULL;
+}
+
+
+/*
+ * Return a Python float indicating the system boot time expressed in
+ * seconds since the epoch.
+ */
+static PyObject*
+get_system_boot_time(PyObject* self, PyObject* args)
+{
+    /* fetch sysctl "kern.boottime" */
+    static int request[2] = { CTL_KERN, KERN_BOOTTIME };
+    struct timeval result;
+    size_t result_len = sizeof result;
+    time_t boot_time = 0;
+
+    if (sysctl(request, 2, &result, &result_len, NULL, 0) == -1) {
+        PyErr_SetFromErrno(0);
+        return NULL;
+    }
+    boot_time = result.tv_sec;
+    return Py_BuildValue("f", (float)boot_time);
+}
+
+
+/*
+ * 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 pathname executable.
+ * Thanks to Robert N. M. Watson:
+ * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT
+ */
+static PyObject*
+get_process_exe(PyObject* self, PyObject* args)
+{
+    long pid;
+    char pathname[PATH_MAX];
+    int error;
+    int mib[4];
+    size_t size;
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        return NULL;
+    }
+    if (pid == 0) {
+        // ...otherwise we'd get '\x98\xd5\xbf\xbf\xfb\xf3\x10\x08H\x01'
+        return Py_BuildValue("s", "");
+    }
+
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_PATHNAME;
+    mib[3] = pid;
+
+    size = sizeof(pathname);
+    error = sysctl(mib, 4, pathname, &size, NULL, 0);
+    if (error == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        return NULL;
+    }
+
+    return Py_BuildValue("s", pathname);
+}
+
+
+/*
+ * 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 status as a Python integer.
+ */
+static PyObject*
+get_process_status(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("i", (int)kp.ki_stat);
+}
+
+
+/*
+ * Return process real, effective and saved user ids from kinfo_proc
+ * as a Python tuple.
+ */
+static PyObject*
+get_process_uids(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("lll", (long)kp.ki_ruid,
+                                (long)kp.ki_uid,
+                                (long)kp.ki_svuid);
+}
+
+
+/*
+ * Return process real, effective and saved group ids from kinfo_proc
+ * as a Python tuple.
+ */
+static PyObject*
+get_process_gids(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("lll", (long)kp.ki_rgid,
+                                (long)kp.ki_groups[0],
+                                (long)kp.ki_svuid);
+}
+
+
+/*
+ * Return process real, effective and saved group ids from kinfo_proc
+ * as a Python tuple.
+ */
+static PyObject*
+get_process_tty_nr(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("i", kp.ki_tdev);
+}
+
+
+/*
+ * Return the number of context switches performed by process as a tuple.
+ */
+static PyObject*
+get_process_num_ctx_switches(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)", kp.ki_rusage.ru_nvcsw,
+                                 kp.ki_rusage.ru_nivcsw);
+}
+
+
+
+/*
+ * 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);
+}
+
+
+/*
+ * Retrieves all threads used by process returning a list of tuples
+ * including thread id, user time and system time.
+ * Thanks to Robert N. M. Watson:
+ * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_threads.c?v=8-CURRENT
+ */
+static PyObject*
+get_process_threads(PyObject* self, PyObject* args)
+{
+    long pid;
+    int mib[4];
+    struct kinfo_proc *kip = NULL;
+    struct kinfo_proc *kipp;
+    int error;
+    unsigned int i;
+    size_t size;
+    PyObject* retList = PyList_New(0);
+    PyObject* pyTuple = NULL;
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        goto error;
+    }
+
+    /*
+     * We need to re-query for thread information, so don't use *kipp.
+     */
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD;
+    mib[3] = pid;
+
+    size = 0;
+    error = sysctl(mib, 4, NULL, &size, NULL, 0);
+    if (error == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    if (size == 0) {
+        NoSuchProcess();
+        goto error;
+    }
+
+    kip = malloc(size);
+    if (kip == NULL) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    error = sysctl(mib, 4, kip, &size, NULL, 0);
+    if (error == -1) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+    if (size == 0) {
+        NoSuchProcess();
+        goto error;
+    }
+
+    for (i = 0; i < size / sizeof(*kipp); i++) {
+        kipp = &kip[i];
+        pyTuple = Py_BuildValue("Idd", kipp->ki_tid,
+                                       TV2DOUBLE(kipp->ki_rusage.ru_utime),
+                                       TV2DOUBLE(kipp->ki_rusage.ru_stime)
+                                );
+        if (pyTuple == NULL)
+            goto error;
+        if (PyList_Append(retList, pyTuple))
+            goto error;
+        Py_DECREF(pyTuple);
+    }
+    free(kip);
+    return retList;
+
+error:
+    Py_XDECREF(pyTuple);
+    Py_DECREF(retList);
+    if (kip != NULL) {
+        free(kip);
+    }
+    return NULL;
+}
+
+
+/*
+ * Return a Python tuple (user_time, kernel_time)
+ */
+static PyObject*
+get_process_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 a Python float indicating the process create time expressed in
+ * seconds since the epoch.
+ */
+static PyObject*
+get_process_io_counters(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;
+    }
+    // there's apparently no way to determine bytes count, hence return -1.
+    return Py_BuildValue("(llll)", kp.ki_rusage.ru_inblock,
+                                   kp.ki_rusage.ru_oublock,
+                                   -1, -1);
+}
+
+
+/*
+ * Return extended memory info for a process as a Python tuple.
+ */
+static PyObject*
+get_process_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("(lllll)", ptoa(kp.ki_rssize),    // rss
+                                    (long)kp.ki_size,      // vms
+                                    ptoa(kp.ki_tsize),     // text
+                                    ptoa(kp.ki_dsize),     // data
+                                    ptoa(kp.ki_ssize));    // stack
+}
+
+
+/*
+ * Return virtual memory usage statistics.
+ */
+static PyObject*
+get_virtual_mem(PyObject* self, PyObject* args)
+{
+    unsigned int   total, active, inactive, wired, cached, free, buffers;
+    size_t		   size = sizeof(total);
+	struct vmtotal vm;
+	int            mib[] = {CTL_VM, VM_METER};
+	long           pagesize = getpagesize();
+
+    if (sysctlbyname("vm.stats.vm.v_page_count", &total, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_active_count", &active, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_inactive_count", &inactive, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_wire_count", &wired, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_cache_count", &cached, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vm.stats.vm.v_free_count", &free, &size, NULL, 0))
+        goto error;
+    if (sysctlbyname("vfs.bufspace", &buffers, &size, NULL, 0))
+        goto error;
+
+    size = sizeof(vm);
+    if (sysctl(mib, 2, &vm, &size, NULL, 0) != 0)
+        goto error;
+
+    return Py_BuildValue("KKKKKKKK",
+        (unsigned long long) total    * pagesize,
+        (unsigned long long) free     * pagesize,
+        (unsigned long long) active   * pagesize,
+        (unsigned long long) inactive * pagesize,
+        (unsigned long long) wired    * pagesize,
+        (unsigned long long) cached   * pagesize,
+        (unsigned long long) buffers,
+        (unsigned long long) (vm.t_vmshr + vm.t_rmshr) * pagesize  // shared
+    );
+
+error:
+    PyErr_SetFromErrno(0);
+    return NULL;
+}
+
+
+/*
+ * Return swap memory stats (see 'swapinfo' cmdline tool)
+ */
+static PyObject*
+get_swap_mem(PyObject* self, PyObject* args)
+{
+    kvm_t *kd;
+    struct kvm_swap kvmsw[1];
+    unsigned int swapin, swapout, nodein, nodeout;
+    size_t size = sizeof(unsigned int);
+
+    if (kvm_getswapinfo(kd, kvmsw, 1, 0) < 0) {
+        PyErr_SetString(PyExc_RuntimeError, "kvm_getswapinfo failed");
+        return NULL;
+    }
+
+    if (sysctlbyname("vm.stats.vm.v_swapin", &swapin, &size, NULL, 0) == -1)
+        goto sbn_error;
+    if (sysctlbyname("vm.stats.vm.v_swapout", &swapout, &size, NULL, 0) == -1)
+        goto sbn_error;
+    if (sysctlbyname("vm.stats.vm.v_vnodein", &nodein, &size, NULL, 0) == -1)
+        goto sbn_error;
+    if (sysctlbyname("vm.stats.vm.v_vnodeout", &nodeout, &size, NULL, 0) == -1)
+        goto sbn_error;
+
+    return Py_BuildValue("(iiiII)",
+                            kvmsw[0].ksw_total,                     // total
+                            kvmsw[0].ksw_used,                      // used
+                            kvmsw[0].ksw_total - kvmsw[0].ksw_used, // free
+                            swapin + swapout,                       // swap in
+                            nodein + nodeout);                      // swap out
+
+sbn_error:
+    PyErr_SetFromErrno(0);
+    return NULL;
+}
+
+
+/*
+ * 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;
+    }
+
+    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
+    );
+}
+
+/*
+ * XXX
+ * These functions are available on FreeBSD 8 only.
+ * In the upper python layer we do various tricks to avoid crashing
+ * and/or to provide alternatives where possible.
+ */
+
+
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+/*
+ * Return files opened by process as a list of (path, fd) tuples
+ */
+static PyObject*
+get_process_open_files(PyObject* self, PyObject* args)
+{
+    long pid;
+    int i, cnt;
+    PyObject *retList = PyList_New(0);
+    PyObject *tuple = NULL;
+
+    struct kinfo_file *freep = NULL;
+    struct kinfo_file *kif;
+    struct kinfo_proc kipp;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        goto error;
+    if (get_kinfo_proc(pid, &kipp) == -1)
+        goto error;
+
+    freep = kinfo_getfile(pid, &cnt);
+    if (freep == NULL) {
+        PyErr_SetFromErrno(0);
+        goto error;
+    }
+
+    for (i = 0; i < cnt; i++) {
+        kif = &freep[i];
+        if ((kif->kf_type == KF_TYPE_VNODE) &&
+            (kif->kf_vnode_type == KF_VTYPE_VREG))
+        {
+            tuple = Py_BuildValue("(si)", kif->kf_path, kif->kf_fd);
+            if (tuple == NULL)
+                goto error;
+            if (PyList_Append(retList, tuple))
+                goto error;
+            Py_DECREF(tuple);
+        }
+    }
+    free(freep);
+    return retList;
+
+error:
+    Py_XDECREF(tuple);
+    Py_DECREF(retList);
+    if (freep != NULL)
+        free(freep);
+    return NULL;
+}
+
+
+/*
+ * Return files opened by process as a list of (path, fd) tuples
+ */
+static PyObject*
+get_process_num_fds(PyObject* self, PyObject* args)
+{
+    long pid;
+    int cnt;
+
+    struct kinfo_file *freep;
+    struct kinfo_proc kipp;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        return NULL;
+    if (get_kinfo_proc(pid, &kipp) == -1)
+        return NULL;
+
+    freep = kinfo_getfile(pid, &cnt);
+    if (freep == NULL) {
+        PyErr_SetFromErrno(0);
+        return NULL;
+    }
+    free(freep);
+
+    return Py_BuildValue("i", cnt);
+}
+
+
+/*
+ * Return process current working directory.
+ */
+static PyObject*
+get_process_cwd(PyObject* self, PyObject* args)
+{
+    long pid;
+    PyObject *path = NULL;
+    struct kinfo_file *freep = NULL;
+    struct kinfo_file *kif;
+    struct kinfo_proc kipp;
+
+    int i, cnt;
+
+    if (! PyArg_ParseTuple(args, "l", &pid))
+        goto error;
+    if (get_kinfo_proc(pid, &kipp) == -1)
+        goto error;
+
+    freep = kinfo_getfile(pid, &cnt);
+    if (freep == NULL) {
+        PyErr_SetFromErrno(0);
+        goto error;
+    }
+
+    for (i = 0; i < cnt; i++) {
+        kif = &freep[i];
+        if (kif->kf_fd == KF_FD_TYPE_CWD) {
+            path = Py_BuildValue("s", kif->kf_path);
+            if (!path)
+                goto error;
+            break;
+        }
+    }
+    /*
+     * For lower pids it seems we can't retrieve any information
+     * (lsof can't do that it either).  Since this happens even
+     * as root we return an empty string instead of AccessDenied.
+     */
+    if (path == NULL) {
+        path = Py_BuildValue("s", "");
+    }
+    free(freep);
+    return path;
+
+error:
+    Py_XDECREF(path);
+    if (freep != NULL)
+        free(freep);
+    return NULL;
+}
+
+
+/*
+ * mathes Linux net/tcp_states.h:
+ * http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h
+ */
+static char *
+get_connection_status(int st) {
+    switch (st) {
+        case TCPS_CLOSED:
+            return "CLOSE";
+        case TCPS_CLOSING:
+            return "CLOSING";
+        case TCPS_CLOSE_WAIT:
+            return "CLOSE_WAIT";
+        case TCPS_LISTEN:
+            return "LISTEN";
+        case TCPS_ESTABLISHED:
+            return "ESTABLISHED";
+        case TCPS_SYN_SENT:
+            return "SYN_SENT";
+        case TCPS_SYN_RECEIVED:
+            return "SYN_RECV";
+        case TCPS_FIN_WAIT_1:
+            return "FIN_WAIT_1";
+        case TCPS_FIN_WAIT_2:
+            return "FIN_WAIT_2";
+        case TCPS_LAST_ACK:
+            return "LAST_ACK";
+        case TCPS_TIME_WAIT:
+            return "TIME_WAIT";
+        default:
+            return "?";
+    }
+}
+
+// a kvm_read that returns true if everything is read
+#define KVM_READ(kaddr, paddr, len) \
+    ((len) < SSIZE_MAX && \
+    kvm_read(kd, (u_long)(kaddr), (char *)(paddr), (len)) == (ssize_t)(len))
+
+// XXX - copied from sys/file.h to make compiler happy
+struct file {
+    void        *f_data;    /* file descriptor specific data */
+    struct fileops  *f_ops;     /* File operations */
+    struct ucred    *f_cred;    /* associated credentials. */
+    struct vnode    *f_vnode;   /* NULL or applicable vnode */
+    short       f_type;     /* descriptor type */
+    short       f_vnread_flags; /* (f) Sleep lock for f_offset */
+    volatile u_int  f_flag;     /* see fcntl.h */
+    volatile u_int  f_count;    /* reference count */
+    int     f_seqcount; /* Count of sequential accesses. */
+    off_t       f_nextoff;  /* next expected read/write offset. */
+    struct cdev_privdata *f_cdevpriv; /* (d) Private data for the cdev. */
+    off_t       f_offset;
+    void        *f_label;   /* Place-holder for MAC label. */
+};
+
+
+/*
+ * Return connections opened by process.
+ * fstat.c source code was used as an example.
+ */
+static PyObject*
+get_process_connections(PyObject* self, PyObject* args)
+{
+    long pid;
+    struct kinfo_proc *p;
+    struct file **ofiles = NULL;
+    char buf[_POSIX2_LINE_MAX];
+    char path[PATH_MAX];
+    int cnt;
+    int i;
+    kvm_t *kd = NULL;
+    struct file file;
+    struct filedesc filed;
+    struct nlist nl[] = {{ "" },};
+    struct socket   so;
+    struct protosw  proto;
+    struct domain   dom;
+    struct inpcb    inpcb;
+    struct tcpcb    tcpcb;
+    struct unpcb    unpcb;
+
+    PyObject *retList = PyList_New(0);
+    PyObject *tuple = NULL;
+    PyObject *laddr = NULL;
+    PyObject *raddr = NULL;
+    PyObject *af_filter = NULL;
+    PyObject *type_filter = NULL;
+    PyObject* _family = NULL;
+    PyObject* _type = NULL;
+
+    if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) {
+        goto error;
+    }
+    if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) {
+        PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence");
+        goto error;
+    }
+
+    kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf);
+    if (kd == NULL) {
+        AccessDenied();
+        goto error;
+    }
+
+    if (kvm_nlist(kd, nl) != 0) {
+        PyErr_SetString(PyExc_RuntimeError, "kvm_nlist() failed");
+        goto error;
+    }
+
+    p = kvm_getprocs(kd, KERN_PROC_PID, pid, &cnt);
+    if (p == NULL) {
+        NoSuchProcess();
+        goto error;
+    }
+    if (cnt != 1) {
+        NoSuchProcess();
+        goto error;
+    }
+    if (p->ki_fd == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "no usable fd found");
+        goto error;
+    }
+    if (!KVM_READ(p->ki_fd, &filed, sizeof(filed))) {
+        PyErr_SetString(PyExc_RuntimeError, "kvm_read() failed");
+        goto error;
+    }
+
+    ofiles = malloc((filed.fd_lastfile+1) * sizeof(struct file *));
+    if (ofiles == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "malloc() failed");
+        goto error;
+    }
+
+    if (!KVM_READ(filed.fd_ofiles, ofiles,
+                  (filed.fd_lastfile+1) * sizeof(struct file *))) {
+        PyErr_SetString(PyExc_RuntimeError, "kvm_read() failed");
+        goto error;
+    }
+
+    for (i = 0; i <= filed.fd_lastfile; i++) {
+        int lport, rport;
+        char lip[200], rip[200];
+        char *state;
+        int inseq;
+        tuple = NULL;
+        laddr = NULL;
+        raddr = NULL;
+
+        if (ofiles[i] == NULL) {
+            continue;
+        }
+        if (!KVM_READ(ofiles[i], &file, sizeof (struct file))) {
+            PyErr_SetString(PyExc_RuntimeError, "kvm_read() file failed");
+            goto error;
+        }
+        if (file.f_type == DTYPE_SOCKET) {
+            // fill in socket
+            if (!KVM_READ(file.f_data, &so, sizeof(struct socket))) {
+                PyErr_SetString(PyExc_RuntimeError, "kvm_read() socket failed");
+                goto error;
+            }
+            // fill in protosw entry
+            if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) {
+                PyErr_SetString(PyExc_RuntimeError, "kvm_read() proto failed");
+                goto error;
+            }
+            // fill in domain
+            if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) {
+                PyErr_SetString(PyExc_RuntimeError, "kvm_read() domain failed");
+                goto error;
+            }
+
+            // apply filters
+            _family = PyLong_FromLong((long)dom.dom_family);
+            inseq = PySequence_Contains(af_filter, _family);
+            Py_DECREF(_family);
+            if (inseq == 0) {
+                continue;
+            }
+            _type = PyLong_FromLong((long)proto.pr_type);
+            inseq = PySequence_Contains(type_filter, _type);
+            Py_DECREF(_type);
+            if (inseq == 0) {
+                continue;
+            }
+
+            // IPv4 / IPv6 socket
+            if ((dom.dom_family == AF_INET) || (dom.dom_family == AF_INET6)) {
+                // fill inpcb
+                if (kvm_read(kd, (u_long)so.so_pcb, (char *)&inpcb,
+                             sizeof(struct inpcb)) != sizeof(struct inpcb)) {
+                    PyErr_SetString(PyExc_RuntimeError, "kvm_read() addr failed");
+                    goto error;
+                }
+
+                // fill status
+                if (proto.pr_type == SOCK_STREAM) {
+                    if (kvm_read(kd, (u_long)inpcb.inp_ppcb, (char *)&tcpcb,
+                                 sizeof(struct tcpcb)) != sizeof(struct tcpcb)) {
+                        PyErr_SetString(PyExc_RuntimeError, "kvm_read() state failed");
+                        goto error;
+                    }
+                    state = get_connection_status((int)tcpcb.t_state);
+                }
+                else {
+                    state = "";
+                }
+
+                // build addr and port
+                if (dom.dom_family == AF_INET) {
+                    inet_ntop(AF_INET, &inpcb.inp_laddr.s_addr, lip, sizeof(lip));
+                    inet_ntop(AF_INET, &inpcb.inp_faddr.s_addr, rip, sizeof(rip));
+                }
+                else {
+                    inet_ntop(AF_INET6, &inpcb.in6p_laddr.s6_addr, lip, sizeof(lip));
+                    inet_ntop(AF_INET6, &inpcb.in6p_faddr.s6_addr, rip, sizeof(rip));
+                }
+                lport = ntohs(inpcb.inp_lport);
+                rport = ntohs(inpcb.inp_fport);
+
+                // contruct python tuple/list
+                laddr = Py_BuildValue("(si)", lip, lport);
+                if (!laddr)
+                    goto error;
+                if (rport != 0) {
+                    raddr = Py_BuildValue("(si)", rip, rport);
+                }
+                else {
+                    raddr = Py_BuildValue("()");
+                }
+                if (!raddr)
+                    goto error;
+                tuple = Py_BuildValue("(iiiNNs)", i,
+                                                  dom.dom_family,
+                                                  proto.pr_type,
+                                                  laddr,
+                                                  raddr,
+                                                  state);
+                if (!tuple)
+                    goto error;
+                if (PyList_Append(retList, tuple))
+                    goto error;
+                Py_DECREF(tuple);
+            }
+            // UNIX socket
+            else if (dom.dom_family == AF_UNIX) {
+                struct sockaddr_un sun;
+                path[0] = '\0';
+
+                if (kvm_read(kd, (u_long)so.so_pcb, (char *)&unpcb,
+                             sizeof(struct unpcb)) != sizeof(struct unpcb)) {
+                    PyErr_SetString(PyExc_RuntimeError, "kvm_read() unpcb failed");
+                    goto error;
+                }
+                if (unpcb.unp_addr) {
+                    if (kvm_read(kd, (u_long)unpcb.unp_addr, (char *)&sun,
+                                 sizeof(sun)) != sizeof(sun)) {
+                        PyErr_SetString(PyExc_RuntimeError,
+                                        "kvm_read() sockaddr_un failed");
+                        goto error;
+                    }
+                    sprintf(path, "%.*s",
+                            (sun.sun_len - (sizeof(sun) - sizeof(sun.sun_path))),
+                             sun.sun_path);
+                }
+
+                tuple = Py_BuildValue("(iiisOs)", i,
+                                                  dom.dom_family,
+                                                  proto.pr_type,
+                                                  path,
+                                                  Py_None,
+                                                  "");
+                if (!tuple)
+                    goto error;
+                if (PyList_Append(retList, tuple))
+                    goto error;
+                Py_DECREF(tuple);
+                Py_INCREF(Py_None);
+            }
+        }
+    }
+
+    free(ofiles);
+    kvm_close(kd);
+    return retList;
+
+error:
+    Py_XDECREF(tuple);
+    Py_XDECREF(laddr);
+    Py_XDECREF(raddr);
+    Py_DECREF(retList);
+
+    if (kd != NULL) {
+        kvm_close(kd);
+    }
+    if (ofiles != NULL) {
+        free(ofiles);
+    }
+    return NULL;
+}
+
+
+/*
+ * Return a Python list of tuple representing per-cpu times
+ */
+static PyObject*
+get_system_per_cpu_times(PyObject* self, PyObject* args)
+{
+    static int maxcpus;
+    int mib[2];
+    int ncpu;
+    size_t len;
+    size_t size;
+    int i;
+    PyObject* py_retlist = PyList_New(0);
+    PyObject* py_cputime = NULL;
+
+    // retrieve maxcpus value
+    size = sizeof(maxcpus);
+    if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) {
+        Py_DECREF(py_retlist);
+        PyErr_SetFromErrno(0);
+        return NULL;
+    }
+    long cpu_time[maxcpus][CPUSTATES];
+
+    // retrieve the number of cpus
+    mib[0] = CTL_HW;
+    mib[1] = HW_NCPU;
+    len = sizeof(ncpu);
+    if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
+        PyErr_SetFromErrno(0);
+        goto error;
+    }
+
+    // per-cpu info
+    size = sizeof(cpu_time);
+    if (sysctlbyname("kern.cp_times", &cpu_time, &size, NULL, 0) == -1) {
+        PyErr_SetFromErrno(0);
+        goto error;
+    }
+
+    for (i = 0; i < ncpu; i++) {
+        py_cputime = Py_BuildValue("(ddddd)",
+                               (double)cpu_time[i][CP_USER] / CLOCKS_PER_SEC,
+                               (double)cpu_time[i][CP_NICE] / CLOCKS_PER_SEC,
+                               (double)cpu_time[i][CP_SYS] / CLOCKS_PER_SEC,
+                               (double)cpu_time[i][CP_IDLE] / CLOCKS_PER_SEC,
+                               (double)cpu_time[i][CP_INTR] / CLOCKS_PER_SEC
+                               );
+        if (!py_cputime)
+            goto error;
+        if (PyList_Append(py_retlist, py_cputime))
+            goto error;
+        Py_DECREF(py_cputime);
+    }
+
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_cputime);
+    Py_DECREF(py_retlist);
+    return NULL;
+}
+
+
+/*
+ * Return a list of tuples for every process memory maps.
+ * 'procstat' cmdline utility has been used as an example.
+ */
+static PyObject*
+get_process_memory_maps(PyObject* self, PyObject* args)
+{
+    long pid;
+    int ptrwidth;
+    int i, cnt;
+    char addr[30];
+    char perms[10];
+    const char *path;
+    struct kinfo_proc kp;
+    struct kinfo_vmentry *freep = NULL;
+    struct kinfo_vmentry *kve;
+    PyObject* pytuple = NULL;
+    PyObject* retlist = PyList_New(0);
+
+    ptrwidth = 2*sizeof(void *);
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        goto error;
+    }
+    if (get_kinfo_proc(pid, &kp) == -1) {
+        goto error;
+    }
+
+    freep = kinfo_getvmmap(pid, &cnt);
+    if (freep == NULL) {
+        PyErr_SetString(PyExc_RuntimeError, "kinfo_getvmmap() failed");
+        goto error;
+    }
+    for (i = 0; i < cnt; i++) {
+        pytuple = NULL;
+        kve = &freep[i];
+        addr[0] = '\0';
+        perms[0] = '\0';
+        sprintf(addr, "%#*jx-%#*jx", ptrwidth, (uintmax_t)kve->kve_start,
+                                     ptrwidth, (uintmax_t)kve->kve_end);
+        strlcat(perms, kve->kve_protection & KVME_PROT_READ ? "r" : "-",
+                sizeof(perms));
+        strlcat(perms, kve->kve_protection & KVME_PROT_WRITE ? "w" : "-",
+                sizeof(perms));
+        strlcat(perms, kve->kve_protection & KVME_PROT_EXEC ? "x" : "-",
+                sizeof(perms));
+
+        if (strlen(kve->kve_path) == 0) {
+            switch (kve->kve_type) {
+            case KVME_TYPE_NONE:
+                path = "[none]";
+                break;
+            case KVME_TYPE_DEFAULT:
+                path = "[default]";
+                break;
+            case KVME_TYPE_VNODE:
+                path = "[vnode]";
+                break;
+            case KVME_TYPE_SWAP:
+                path = "[swap]";
+                break;
+            case KVME_TYPE_DEVICE:
+                path = "[device]";
+                break;
+            case KVME_TYPE_PHYS:
+                path = "[phys]";
+                break;
+            case KVME_TYPE_DEAD:
+                path = "[dead]";
+                break;
+            case KVME_TYPE_SG:
+                path = "[sg]";
+                break;
+            case KVME_TYPE_UNKNOWN:
+                path = "[unknown]";
+                break;
+            default:
+                path = "[?]";
+                break;
+            }
+        }
+        else {
+            path = kve->kve_path;
+        }
+
+        pytuple = Py_BuildValue("sssiiii",
+            addr,                       // "start-end" address
+            perms,                      // "rwx" permissions
+            path,                       // path
+            kve->kve_resident,          // rss
+            kve->kve_private_resident,  // private
+            kve->kve_ref_count,         // ref count
+            kve->kve_shadow_count       // shadow count
+        );
+        if (!pytuple)
+            goto error;
+        if (PyList_Append(retlist, pytuple))
+            goto error;
+        Py_DECREF(pytuple);
+    }
+    free(freep);
+    return retlist;
+
+error:
+    Py_XDECREF(pytuple);
+    Py_DECREF(retlist);
+    if (freep != NULL)
+        free(freep);
+    return NULL;
+}
+#endif
+
+
+/*
+ * Return a list of tuples including device, mount point and fs type
+ * for all partitions mounted on the system.
+ */
+static PyObject*
+get_disk_partitions(PyObject* self, PyObject* args)
+{
+    int num;
+    int i;
+    long len;
+    uint64_t flags;
+    char opts[200];
+    struct statfs *fs = NULL;
+    PyObject* py_retlist = PyList_New(0);
+    PyObject* py_tuple = NULL;
+
+    // get the number of mount points
+    Py_BEGIN_ALLOW_THREADS
+    num = getfsstat(NULL, 0, MNT_NOWAIT);
+    Py_END_ALLOW_THREADS
+    if (num == -1) {
+        PyErr_SetFromErrno(0);
+        goto error;
+    }
+
+    len = sizeof(*fs) * num;
+    fs = malloc(len);
+
+    Py_BEGIN_ALLOW_THREADS
+    num = getfsstat(fs, len, MNT_NOWAIT);
+    Py_END_ALLOW_THREADS
+    if (num == -1) {
+        PyErr_SetFromErrno(0);
+        goto error;
+    }
+
+    for (i = 0; i < num; i++) {
+        py_tuple = NULL;
+        opts[0] = 0;
+        flags = fs[i].f_flags;
+
+        // see sys/mount.h
+        if (flags & MNT_RDONLY)
+            strlcat(opts, "ro", sizeof(opts));
+        else
+            strlcat(opts, "rw", sizeof(opts));
+        if (flags & MNT_SYNCHRONOUS)
+            strlcat(opts, ",sync", sizeof(opts));
+        if (flags & MNT_NOEXEC)
+            strlcat(opts, ",noexec", sizeof(opts));
+        if (flags & MNT_NOSUID)
+            strlcat(opts, ",nosuid", sizeof(opts));
+        if (flags & MNT_UNION)
+            strlcat(opts, ",union", sizeof(opts));
+        if (flags & MNT_ASYNC)
+            strlcat(opts, ",async", sizeof(opts));
+        if (flags & MNT_SUIDDIR)
+            strlcat(opts, ",suiddir", sizeof(opts));
+        if (flags & MNT_SOFTDEP)
+            strlcat(opts, ",softdep", sizeof(opts));
+        if (flags & MNT_NOSYMFOLLOW)
+            strlcat(opts, ",nosymfollow", sizeof(opts));
+        if (flags & MNT_GJOURNAL)
+            strlcat(opts, ",gjournal", sizeof(opts));
+        if (flags & MNT_MULTILABEL)
+            strlcat(opts, ",multilabel", sizeof(opts));
+        if (flags & MNT_ACLS)
+            strlcat(opts, ",acls", sizeof(opts));
+        if (flags & MNT_NOATIME)
+            strlcat(opts, ",noatime", sizeof(opts));
+        if (flags & MNT_NOCLUSTERR)
+            strlcat(opts, ",noclusterr", sizeof(opts));
+        if (flags & MNT_NOCLUSTERW)
+            strlcat(opts, ",noclusterw", sizeof(opts));
+        if (flags & MNT_NFS4ACLS)
+            strlcat(opts, ",nfs4acls", sizeof(opts));
+
+        py_tuple = Py_BuildValue("(ssss)", fs[i].f_mntfromname,  // device
+                                           fs[i].f_mntonname,    // mount point
+                                           fs[i].f_fstypename,   // fs type
+                                           opts);                // options
+        if (!py_tuple)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_tuple);
+    }
+
+    free(fs);
+    return py_retlist;
+
+error:
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    if (fs != NULL)
+        free(fs);
+    return NULL;
+}
+
+
+/*
+ * Return a Python list of named tuples with overall network I/O information
+ */
+static PyObject*
+get_network_io_counters(PyObject* self, PyObject* args)
+{
+    PyObject* py_retdict = PyDict_New();
+    PyObject* py_ifc_info = NULL;
+
+    char *buf = NULL, *lim, *next;
+    struct if_msghdr *ifm;
+    int mib[6];
+    size_t len;
+
+    mib[0] = CTL_NET;          // networking subsystem
+    mib[1] = PF_ROUTE;         // type of information
+    mib[2] = 0;                // protocol (IPPROTO_xxx)
+    mib[3] = 0;                // address family
+    mib[4] = NET_RT_IFLIST;   // operation
+    mib[5] = 0;
+
+    if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+        PyErr_SetFromErrno(0);
+        goto error;
+    }
+
+
+    buf = malloc(len);
+
+    if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+        PyErr_SetFromErrno(0);
+        goto error;
+    }
+
+    lim = buf + len;
+
+    for (next = buf; next < lim; ) {
+        py_ifc_info = NULL;
+        ifm = (struct if_msghdr *)next;
+        next += ifm->ifm_msglen;
+
+        if (ifm->ifm_type == RTM_IFINFO) {
+            struct if_msghdr *if2m = (struct if_msghdr *)ifm;
+            struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1);
+            char ifc_name[32];
+
+            strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen);
+            ifc_name[sdl->sdl_nlen] = 0;
+
+            py_ifc_info = Py_BuildValue("(kkkkkkki)",
+                                        if2m->ifm_data.ifi_obytes,
+                                        if2m->ifm_data.ifi_ibytes,
+                                        if2m->ifm_data.ifi_opackets,
+                                        if2m->ifm_data.ifi_ipackets,
+                                        if2m->ifm_data.ifi_ierrors,
+                                        if2m->ifm_data.ifi_oerrors,
+                                        if2m->ifm_data.ifi_iqdrops,
+                                        0);  // dropout not supported
+            if (!py_ifc_info)
+                goto error;
+            if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info))
+                goto error;
+            Py_DECREF(py_ifc_info);
+        }
+        else {
+            continue;
+        }
+    }
+
+    free(buf);
+    return py_retdict;
+
+error:
+    Py_XDECREF(py_ifc_info);
+    Py_DECREF(py_retdict);
+    if (buf != NULL)
+        free(buf);
+    return NULL;
+}
+
+
+/*
+ * Return a Python dict of tuples for disk I/O information
+ */
+static PyObject*
+get_disk_io_counters(PyObject* self, PyObject* args)
+{
+    PyObject* py_retdict = PyDict_New();
+    PyObject* py_disk_info = NULL;
+
+    int i;
+    struct statinfo stats;
+
+    if (devstat_checkversion(NULL) < 0) {
+        PyErr_Format(PyExc_RuntimeError, "devstat_checkversion() failed");
+        goto error;
+    }
+
+    stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+    bzero(stats.dinfo, sizeof(struct devinfo));
+
+    if (devstat_getdevs(NULL, &stats) == -1) {
+        PyErr_Format(PyExc_RuntimeError, "devstat_getdevs() failed");
+        goto error;
+    }
+
+    for (i = 0; i < stats.dinfo->numdevs; i++) {
+        py_disk_info = NULL;
+        struct devstat current;
+        char disk_name[128];
+        current = stats.dinfo->devices[i];
+        snprintf(disk_name, sizeof(disk_name), "%s%d",
+                                current.device_name,
+                                current.unit_number);
+
+        py_disk_info = Py_BuildValue("(KKKKLL)",
+            current.operations[DEVSTAT_READ],   // no reads
+            current.operations[DEVSTAT_WRITE],  // no writes
+            current.bytes[DEVSTAT_READ],        // bytes read
+            current.bytes[DEVSTAT_WRITE],       // bytes written
+            (long long)devstat_compute_etime(
+                &current.duration[DEVSTAT_READ], NULL),  // r time
+            (long long)devstat_compute_etime(
+                &current.duration[DEVSTAT_WRITE], NULL)  // w time
+        );
+        if (!py_disk_info)
+            goto error;
+        if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info))
+            goto error;
+        Py_DECREF(py_disk_info);
+    }
+
+    if (stats.dinfo->mem_ptr) {
+        free(stats.dinfo->mem_ptr);
+    }
+    free(stats.dinfo);
+    return py_retdict;
+
+error:
+    Py_XDECREF(py_disk_info);
+    Py_DECREF(py_retdict);
+    if (stats.dinfo != NULL)
+        free(stats.dinfo);
+    return NULL;
+}
+
+
+/*
+ * Return currently connected users as a list of tuples.
+ */
+static PyObject*
+get_system_users(PyObject* self, PyObject* args)
+{
+    PyObject *ret_list = PyList_New(0);
+    PyObject *tuple = NULL;
+
+#if __FreeBSD_version < 900000
+    struct utmp ut;
+    FILE *fp;
+
+    fp = fopen(_PATH_UTMP, "r");
+    if (fp == NULL) {
+        PyErr_SetFromErrno(0);
+        goto error;
+    }
+
+    while (fread(&ut, sizeof(ut), 1, fp) == 1) {
+        if (*ut.ut_name == '\0')
+            continue;
+        tuple = Py_BuildValue("(sssf)",
+            ut.ut_name,              // username
+            ut.ut_line,              // tty
+            ut.ut_host,              // hostname
+            (float)ut.ut_time        // start time
+        );
+        if (!tuple) {
+            fclose(fp);
+            goto error;
+        }
+        if (PyList_Append(ret_list, tuple)) {
+            fclose(fp);
+            goto error;
+        }
+        Py_DECREF(tuple);
+    }
+
+    fclose(fp);
+#else
+    struct utmpx *utx;
+
+    while ((utx = getutxent()) != NULL) {
+        if (utx->ut_type != USER_PROCESS)
+            continue;
+        tuple = Py_BuildValue("(sssf)",
+            utx->ut_user,             // username
+            utx->ut_line,             // tty
+            utx->ut_host,             // hostname
+            (float)utx->ut_tv.tv_sec  // start time
+        );
+        if (!tuple) {
+            endutxent();
+            goto error;
+        }
+        if (PyList_Append(ret_list, tuple)) {
+            endutxent();
+            goto error;
+        }
+        Py_DECREF(tuple);
+    }
+
+    endutxent();
+#endif
+    return ret_list;
+
+error:
+    Py_XDECREF(tuple);
+    Py_DECREF(ret_list);
+    return NULL;
+}
+
+
+/*
+ * 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_connections", get_process_connections, METH_VARARGS,
+        "Return connections opened by process"},
+     {"get_process_exe", get_process_exe, METH_VARARGS,
+        "Return process pathname executable"},
+     {"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_uids", get_process_uids, METH_VARARGS,
+        "Return process real effective and saved user ids as a Python tuple"},
+     {"get_process_gids", get_process_gids, METH_VARARGS,
+        "Return process real effective and saved group ids as a Python tuple"},
+     {"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_process_memory_info", get_process_memory_info, METH_VARARGS,
+         "Return extended memory info for a process as a Python tuple."},
+     {"get_process_num_threads", get_process_num_threads, METH_VARARGS,
+         "Return number of threads used by process"},
+     {"get_process_num_ctx_switches", get_process_num_ctx_switches, METH_VARARGS,
+         "Return the number of context switches performed by process"},
+     {"get_process_threads", get_process_threads, METH_VARARGS,
+         "Return process threads"},
+     {"get_process_status", get_process_status, METH_VARARGS,
+         "Return process status as an integer"},
+     {"get_process_io_counters", get_process_io_counters, METH_VARARGS,
+         "Return process IO counters"},
+     {"get_process_tty_nr", get_process_tty_nr, METH_VARARGS,
+         "Return process tty (terminal) number"},
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+     {"get_process_open_files", get_process_open_files, METH_VARARGS,
+         "Return files opened by process as a list of (path, fd) tuples"},
+     {"get_process_cwd", get_process_cwd, METH_VARARGS,
+         "Return process current working directory."},
+     {"get_process_memory_maps", get_process_memory_maps, METH_VARARGS,
+         "Return a list of tuples for every process's memory map"},
+     {"get_process_num_fds", get_process_num_fds, METH_VARARGS,
+         "Return the number of file descriptors opened by this process"},
+#endif
+
+     // --- 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_virtual_mem", get_virtual_mem, METH_VARARGS,
+         "Return system virtual memory usage statistics"},
+     {"get_swap_mem", get_swap_mem, METH_VARARGS,
+         "Return swap mem stats"},
+     {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS,
+         "Return system cpu times as a tuple (user, system, nice, idle, irc)"},
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+     {"get_system_per_cpu_times", get_system_per_cpu_times, METH_VARARGS,
+         "Return system per-cpu times as a list of tuples"},
+#endif
+     {"get_system_boot_time", get_system_boot_time, METH_VARARGS,
+         "Return a float indicating the system boot time expressed in "
+         "seconds since the epoch"},
+     {"get_disk_partitions", get_disk_partitions, METH_VARARGS,
+         "Return a list of tuples including device, mount point and "
+         "fs type for all partitions mounted on the system."},
+     {"get_network_io_counters", get_network_io_counters, METH_VARARGS,
+         "Return dict of tuples of networks I/O information."},
+     {"get_disk_io_counters", get_disk_io_counters, METH_VARARGS,
+         "Return a Python dict of tuples for disk I/O information"},
+     {"get_system_users", get_system_users, METH_VARARGS,
+        "Return currently connected users as a list of tuples"},
+
+     {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)
+#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
+    PyModule_AddIntConstant(module, "SSTOP", SSTOP);
+    PyModule_AddIntConstant(module, "SSLEEP", SSLEEP);
+    PyModule_AddIntConstant(module, "SRUN", SRUN);
+    PyModule_AddIntConstant(module, "SIDL", SIDL);
+    PyModule_AddIntConstant(module, "SWAIT", SWAIT);
+    PyModule_AddIntConstant(module, "SLOCK", SLOCK);
+    PyModule_AddIntConstant(module, "SZOMB", SZOMB);
+
+    if (module == NULL) {
+        INITERROR;
+    }
+#if PY_MAJOR_VERSION >= 3
+    return module;
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psutil_bsd.h
@@ -0,0 +1,52 @@
+/*
+ * $Id: _psutil_bsd.h 1498 2012-07-24 21:41:28Z g.rodola $
+ *
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * BSD platform-specific module methods for _psutil_bsd
+ */
+
+#include <Python.h>
+
+// --- per-process functions
+
+static PyObject* get_process_cpu_times(PyObject* self, PyObject* args);
+static PyObject* get_process_name(PyObject* self, PyObject* args);
+static PyObject* get_process_exe(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_uids(PyObject* self, PyObject* args);
+static PyObject* get_process_gids(PyObject* self, PyObject* args);
+static PyObject* get_process_connections(PyObject* self, PyObject* args);
+static PyObject* get_process_create_time(PyObject* self, PyObject* args);
+static PyObject* get_process_memory_info(PyObject* self, PyObject* args);
+static PyObject* get_process_num_threads(PyObject* self, PyObject* args);
+static PyObject* get_process_num_fds(PyObject* self, PyObject* args);
+static PyObject* get_process_threads(PyObject* self, PyObject* args);
+static PyObject* get_process_status(PyObject* self, PyObject* args);
+static PyObject* get_process_io_counters(PyObject* self, PyObject* args);
+static PyObject* get_process_tty_nr(PyObject* self, PyObject* args);
+static PyObject* get_process_memory_maps(PyObject* self, PyObject* args);
+static PyObject* get_process_num_ctx_switches(PyObject* self, PyObject* args);
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+static PyObject* get_process_open_files(PyObject* self, PyObject* args);
+static PyObject* get_process_cwd(PyObject* self, PyObject* args);
+#endif
+
+// --- system-related functions
+
+static PyObject* get_pid_list(PyObject* self, PyObject* args);
+static PyObject* get_num_cpus(PyObject* self, PyObject* args);
+static PyObject* get_virtual_mem(PyObject* self, PyObject* args);
+static PyObject* get_swap_mem(PyObject* self, PyObject* args);
+static PyObject* get_system_cpu_times(PyObject* self, PyObject* args);
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+static PyObject* get_system_per_cpu_times(PyObject* self, PyObject* args);
+#endif
+static PyObject* get_system_boot_time(PyObject* self, PyObject* args);
+static PyObject* get_disk_partitions(PyObject* self, PyObject* args);
+static PyObject* get_network_io_counters(PyObject* self, PyObject* args);
+static PyObject* get_disk_io_counters(PyObject* self, PyObject* args);
+static PyObject* get_system_users(PyObject* self, PyObject* args);
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psutil_common.c
@@ -0,0 +1,39 @@
+/*
+ * $Id: _psutil_common.c 1142 2011-10-05 18:45:49Z g.rodola $
+ *
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Routines common to all platforms.
+ */
+
+#include <Python.h>
+
+
+/*
+ * 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="Permission denied") 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;
+}
+
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psutil_common.h
@@ -0,0 +1,13 @@
+/*
+ * $Id: _psutil_common.h 1142 2011-10-05 18:45:49Z g.rodola $
+ *
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include <Python.h>
+
+PyObject* NoSuchProcess(void);
+PyObject* AccessDenied(void);
+
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psutil_linux.c
@@ -0,0 +1,338 @@
+/*
+ * $Id: _psutil_linux.c 1498 2012-07-24 21:41:28Z g.rodola $
+ *
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Linux-specific functions.
+ */
+
+#include <Python.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <mntent.h>
+#include <utmp.h>
+#include <sched.h>
+#include <sys/syscall.h>
+#include <sys/sysinfo.h>
+#include <linux/unistd.h>
+
+#include "_psutil_linux.h"
+
+
+#define HAS_IOPRIO defined(__NR_ioprio_get) && defined(__NR_ioprio_set)
+
+#if HAS_IOPRIO
+enum {
+    IOPRIO_WHO_PROCESS = 1,
+};
+
+static inline int
+ioprio_get(int which, int who)
+{
+    return syscall(__NR_ioprio_get, which, who);
+}
+
+static inline int
+ioprio_set(int which, int who, int ioprio)
+{
+    return syscall(__NR_ioprio_set, which, who, ioprio);
+}
+
+#define IOPRIO_CLASS_SHIFT  13
+#define IOPRIO_PRIO_MASK  ((1UL << IOPRIO_CLASS_SHIFT) - 1)
+
+#define IOPRIO_PRIO_CLASS(mask)  ((mask) >> IOPRIO_CLASS_SHIFT)
+#define IOPRIO_PRIO_DATA(mask)  ((mask) & IOPRIO_PRIO_MASK)
+#define IOPRIO_PRIO_VALUE(class, data)  (((class) << IOPRIO_CLASS_SHIFT) | data)
+
+
+/*
+ * Return a (ioclass, iodata) Python tuple representing process I/O priority.
+ */
+static PyObject*
+linux_ioprio_get(PyObject* self, PyObject* args)
+{
+    long pid;
+    int ioprio, ioclass, iodata;
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        return NULL;
+    }
+    ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid);
+    if (ioprio == -1) {
+        return PyErr_SetFromErrno(PyExc_OSError);
+    }
+    ioclass = IOPRIO_PRIO_CLASS(ioprio);
+    iodata = IOPRIO_PRIO_DATA(ioprio);
+    return Py_BuildValue("ii", ioclass, iodata);
+}
+
+
+/*
+ * A wrapper around ioprio_set(); sets process I/O priority.
+ * ioclass can be either IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE
+ * or 0. iodata goes from 0 to 7 depending on ioclass specified.
+ */
+static PyObject*
+linux_ioprio_set(PyObject* self, PyObject* args)
+{
+    long pid;
+    int ioprio, ioclass, iodata;
+    int retval;
+
+    if (! PyArg_ParseTuple(args, "lii", &pid, &ioclass, &iodata)) {
+        return NULL;
+    }
+    ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata);
+    retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio);
+    if (retval == -1) {
+        return PyErr_SetFromErrno(PyExc_OSError);
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+#endif
+
+
+/*
+ * Return disk mounted partitions as a list of tuples including device,
+ * mount point and filesystem type
+ */
+static PyObject*
+get_disk_partitions(PyObject* self, PyObject* args)
+{
+    FILE *file = NULL;
+    struct mntent *entry;
+    PyObject* py_retlist = PyList_New(0);
+    PyObject* py_tuple = NULL;
+
+    // MOUNTED constant comes from mntent.h and it's == '/etc/mtab'
+    Py_BEGIN_ALLOW_THREADS
+    file = setmntent(MOUNTED, "r");
+    Py_END_ALLOW_THREADS
+    if ((file == 0) || (file == NULL)) {
+        PyErr_SetFromErrno(PyExc_OSError);
+        goto error;
+    }
+
+    while ((entry = getmntent(file))) {
+        if (entry == NULL) {
+            PyErr_Format(PyExc_RuntimeError, "getmntent() failed");
+            goto error;
+        }
+        py_tuple = Py_BuildValue("(ssss)", entry->mnt_fsname,  // device
+                                           entry->mnt_dir,     // mount point
+                                           entry->mnt_type,    // fs type
+                                           entry->mnt_opts);   // options
+        if (! py_tuple)
+            goto error;
+        if (PyList_Append(py_retlist, py_tuple))
+            goto error;
+        Py_DECREF(py_tuple);
+    }
+    endmntent(file);
+    return py_retlist;
+
+error:
+    if (file != NULL)
+        endmntent(file);
+    Py_XDECREF(py_tuple);
+    Py_DECREF(py_retlist);
+    return NULL;
+}
+
+
+/*
+ * A wrapper around sysinfo(), return system memory usage statistics.
+ */
+static PyObject*
+get_sysinfo(PyObject* self, PyObject* args)
+{
+    struct sysinfo info;
+    if (sysinfo(&info) != 0) {
+        return PyErr_SetFromErrno(PyExc_OSError);
+    }
+    return Py_BuildValue("(KKKKKK)",
+        (unsigned long long)info.totalram  * info.mem_unit,   // total
+        (unsigned long long)info.freeram   * info.mem_unit,   // free
+        (unsigned long long)info.bufferram * info.mem_unit,   // buffer
+        (unsigned long long)info.sharedram * info.mem_unit,   // shared
+        (unsigned long long)info.totalswap * info.mem_unit,   // swap tot
+        (unsigned long long)info.freeswap  * info.mem_unit);  // swap free
+    // TODO: we can also determine BOOT_TIME here
+}
+
+
+/*
+ * Return process CPU affinity as a Python long (the bitmask)
+ */
+static PyObject*
+get_process_cpu_affinity(PyObject* self, PyObject* args)
+{
+    unsigned long mask;
+    unsigned int len = sizeof(mask);
+    long pid;
+
+    if (!PyArg_ParseTuple(args, "i", &pid)) {
+        return NULL;
+    }
+    if (sched_getaffinity(pid, len, (cpu_set_t *)&mask) < 0) {
+        return PyErr_SetFromErrno(PyExc_OSError);
+    }
+    return Py_BuildValue("l", mask);
+}
+
+
+/*
+ * Set process CPU affinity; expects a bitmask
+ */
+static PyObject*
+set_process_cpu_affinity(PyObject* self, PyObject* args)
+{
+    unsigned long mask;
+    unsigned int len = sizeof(mask);
+    long pid;
+
+    if (!PyArg_ParseTuple(args, "ll", &pid, &mask)) {
+        return NULL;
+    }
+    if (sched_setaffinity(pid, len, (cpu_set_t *)&mask)) {
+        return PyErr_SetFromErrno(PyExc_OSError);
+    }
+    Py_INCREF(Py_None);
+    return Py_None;
+}
+
+
+/*
+ * Return currently connected users as a list of tuples.
+ */
+static PyObject*
+get_system_users(PyObject* self, PyObject* args)
+{
+    PyObject *ret_list = PyList_New(0);
+    PyObject *tuple = NULL;
+    PyObject *user_proc = NULL;
+    struct utmp *ut;
+
+    setutent();
+    while (NULL != (ut = getutent())) {
+        tuple = NULL;
+        user_proc = NULL;
+        if (ut->ut_type == USER_PROCESS)
+            user_proc = Py_True;
+        else
+            user_proc = Py_False;
+        tuple = Py_BuildValue("(sssfO)",
+            ut->ut_user,              // username
+            ut->ut_line,              // tty
+            ut->ut_host,              // hostname
+            (float)ut->ut_tv.tv_sec,  // tstamp
+            user_proc                 // (bool) user process
+        );
+        if (! tuple)
+            goto error;
+        if (PyList_Append(ret_list, tuple))
+            goto error;
+        Py_DECREF(tuple);
+    }
+    endutent();
+    return ret_list;
+
+error:
+    Py_XDECREF(tuple);
+    Py_XDECREF(user_proc);
+    Py_DECREF(ret_list);
+    endutent();
+    return NULL;
+}
+
+
+/*
+ * Define the psutil C module methods and initialize the module.
+ */
+static PyMethodDef
+PsutilMethods[] =
+{
+#if HAS_IOPRIO
+     {"ioprio_get", linux_ioprio_get, METH_VARARGS,
+        "Get process I/O priority"},
+     {"ioprio_set", linux_ioprio_set, METH_VARARGS,
+        "Set process I/O priority"},
+#endif
+     {"get_disk_partitions", get_disk_partitions, METH_VARARGS,
+        "Return disk mounted partitions as a list of tuples including "
+        "device, mount point and filesystem type"},
+     {"get_sysinfo", get_sysinfo, METH_VARARGS,
+        "A wrapper around sysinfo(), return system memory usage statistics"},
+     {"get_process_cpu_affinity", get_process_cpu_affinity, METH_VARARGS,
+        "Return process CPU affinity as a Python long (the bitmask)."},
+     {"set_process_cpu_affinity", set_process_cpu_affinity, METH_VARARGS,
+        "Set process CPU affinity; expects a bitmask."},
+     {"get_system_users", get_system_users, METH_VARARGS,
+        "Return currently connected users as a list of tuples"},
+
+     {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)
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+
+static int
+psutil_linux_traverse(PyObject *m, visitproc visit, void *arg) {
+    Py_VISIT(GETSTATE(m)->error);
+    return 0;
+}
+
+static int
+psutil_linux_clear(PyObject *m) {
+    Py_CLEAR(GETSTATE(m)->error);
+    return 0;
+}
+
+static struct PyModuleDef
+moduledef = {
+        PyModuleDef_HEAD_INIT,
+        "psutil_linux",
+        NULL,
+        sizeof(struct module_state),
+        PsutilMethods,
+        NULL,
+        psutil_linux_traverse,
+        psutil_linux_clear,
+        NULL
+};
+
+#define INITERROR return NULL
+
+PyObject *
+PyInit__psutil_linux(void)
+
+#else
+#define INITERROR return
+
+void init_psutil_linux(void)
+#endif
+{
+#if PY_MAJOR_VERSION >= 3
+    PyObject *module = PyModule_Create(&moduledef);
+#else
+    PyObject *module = Py_InitModule("_psutil_linux", PsutilMethods);
+#endif
+    if (module == NULL) {
+        INITERROR;
+    }
+#if PY_MAJOR_VERSION >= 3
+    return module;
+#endif
+}
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psutil_linux.h
@@ -0,0 +1,19 @@
+/*
+ * $Id: _psutil_linux.h 1498 2012-07-24 21:41:28Z g.rodola $
+ *
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * LINUX specific module methods for _psutil_linux
+ */
+
+#include <Python.h>
+
+static PyObject* linux_ioprio_get(PyObject* self, PyObject* args);
+static PyObject* linux_ioprio_set(PyObject* self, PyObject* args);
+static PyObject* get_disk_partitions(PyObject* self, PyObject* args);
+static PyObject* get_sysinfo(PyObject* self, PyObject* args);
+static PyObject* get_process_cpu_affinity(PyObject* self, PyObject* args);
+static PyObject* set_process_cpu_affinity(PyObject* self, PyObject* args);
+static PyObject* get_system_users(PyObject* self, PyObject* args);
new file mode 100644
--- /dev/null
+++ b/python/psutil/psutil/_psutil_mswindows.c
@@ -0,0 +1,2904 @@
+/*
+ * $Id: _psutil_mswindows.c 1525 2012-08-16 16:32:03Z g.rodola $
+ *
+ * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Windows platform-specific module methods for _psutil_mswindows
+ */
+
+// Fixes clash between winsock2.h and windows.h
+#define WIN32_LEAN_AND_MEAN
+
+#include <Python.h>
+#include <windows.h>
+#include <Psapi.h>
+#include <time.h>
+#include <lm.h>
+#include <WinIoCtl.h>
+#include <tchar.h>
+#include <tlhelp32.h>
+#include <winsock2.h>
+#include <iphlpapi.h>
+#include <wtsapi32.h>
+
+// Link with Iphlpapi.lib
+#pragma comment(lib, "IPHLPAPI.lib")
+
+#include "_psutil_mswindows.h"
+#include "_psutil_common.h"
+#include "arch/mswindows/security.h"
+#include "arch/mswindows/process_info.h"
+#include "arch/mswindows/process_handles.h"
+#include "arch/mswindows/ntextapi.h"
+
+
+/*
+ * 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) {
+        goto error;
+    }
+
+    for (i = 0; i < numberOfReturnedPIDs; i++) {
+        pid = Py_BuildValue("I", proclist[i]);
+        if (!pid)
+            goto error;
+        if (PyList_Append(retlist, pid))
+            goto error;
+        Py_DECREF(pid);
+    }
+
+    // free C array allocated for PIDs
+    free(proclist);
+    return retlist;
+
+error:
+    Py_XDECREF(pid);
+    Py_DECREF(retlist);
+    if (proclist != NULL)
+        free(proclist);
+    return NULL;
+}
+
+
+/*
+ * 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;
+}
+
+
+/*
+ * Wait for process to terminate and return its exit code.
+ */
+static PyObject*
+process_wait(PyObject* self, PyObject* args)
+{
+    HANDLE hProcess;
+    DWORD ExitCode;
+    DWORD retVal;
+    long pid;
+    long timeout;
+
+    if (! PyArg_ParseTuple(args, "ll", &pid, &timeout)) {
+        return NULL;
+    }
+    if (pid == 0) {
+        return AccessDenied();
+    }
+
+    hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid);
+    if (hProcess == NULL) {
+        if (GetLastError() == ERROR_INVALID_PARAMETER) {
+            // no such process; we do not want to raise NSP but
+            // return None instead.
+            Py_INCREF(Py_None);
+            return Py_None;
+        }
+        else {
+            PyErr_SetFromWindowsErr(0);
+            return NULL;
+        }
+    }
+
+    // wait until the process has terminated
+    Py_BEGIN_ALLOW_THREADS
+    retVal = WaitForSingleObject(hProcess, timeout);
+    Py_END_ALLOW_THREADS
+
+    if (retVal == WAIT_FAILED) {
+        CloseHandle(hProcess);
+        return PyErr_SetFromWindowsErr(GetLastError());
+    }
+    if (retVal == WAIT_TIMEOUT) {
+        CloseHandle(hProcess);
+        return Py_BuildValue("l", WAIT_TIMEOUT);
+    }
+
+    // get the exit code; note: subprocess module (erroneously?) uses
+    // what returned by WaitForSingleObject
+    if (GetExitCodeProcess(hProcess, &ExitCode) == 0) {
+        CloseHandle(hProcess);
+        return PyErr_SetFromWindowsErr(GetLastError());
+    }
+    CloseHandle(hProcess);
+#if PY_MAJOR_VERSION >= 3
+    return PyLong_FromLong((long) ExitCode);
+#else
+    return PyInt_FromLong((long) ExitCode);
+#endif
+}
+
+
+/*
+ * 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;
+    }
+
+    hProcess = handle_from_pid(pid);
+    if (hProcess == NULL) {
+        return NULL;
+    }
+
+    if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
+        CloseHandle(hProcess);
+        if (GetLastError() == ERROR_ACCESS_DENIED) {
+            // usually means the process has died so we throw a NoSuchProcess
+            // here
+            return NoSuchProcess();
+        }
+        else {
+            PyErr_SetFromWindowsErr(0);
+            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)
+        );
+}
+
+
+/*
+ * Alternative implementation of the one above but bypasses ACCESS DENIED.
+ */
+static PyObject*
+get_process_cpu_times_2(PyObject* self, PyObject* args)
+{
+    DWORD pid;
+    PSYSTEM_PROCESS_INFORMATION process;
+    PVOID buffer;
+    double user, kernel;
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        return NULL;
+    }
+    if (! get_process_info(pid, &process, &buffer)) {
+        return NULL;
+    }
+    user = (double)process->UserTime.HighPart * 429.4967296 + \
+           (double)process->UserTime.LowPart * 1e-7;
+    kernel = (double)process->KernelTime.HighPart * 429.4967296 + \
+             (double)process->KernelTime.LowPart * 1e-7;
+    free(buffer);
+    return Py_BuildValue("(dd)", user, kernel);
+}
+
+
+/*
+ * 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;
+    DWORD       exitCode;
+    HANDLE      hProcess;
+    BOOL        ret;
+    FILETIME    ftCreate, ftExit, ftKernel, ftUser;
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        return NULL;
+    }
+
+    // special case for PIDs 0 and 4, return BOOT_TIME
+    if (0 == pid || 4 == pid) {
+        return get_system_uptime(NULL, NULL);
+    }
+
+    hProcess = handle_from_pid(pid);
+    if (hProcess == NULL) {
+        return NULL;
+    }
+
+    if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
+        CloseHandle(hProcess);
+        if (GetLastError() == ERROR_ACCESS_DENIED) {
+            // usually means the process has died so we throw a NoSuchProcess here
+            return NoSuchProcess();
+        }
+        else {
+            PyErr_SetFromWindowsErr(0);
+            return NULL;
+        }
+    }
+
+    // Make sure the process is not gone as OpenProcess alone seems to be
+    // unreliable in doing so (it seems a previous call to p.wait() makes
+    // it unreliable).
+    // This check is important as creation time is used to make sure the
+    // process is still running.
+    ret = GetExitCodeProcess(hProcess, &exitCode);
+    CloseHandle(hProcess);
+    if (ret != 0) {
+        if (exitCode != STILL_ACTIVE) {
+            return NoSuchProcess();
+        }
+    }
+    else {
+        // Ignore access denied as it means the process is still alive.
+        // For all other errors, we want an exception.
+        if (GetLastError() != ERROR_ACCESS_DENIED) {
+            PyErr_SetFromWindowsErr(0);
+            return NULL;
+        }
+    }
+
+    /*
+    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);
+}
+
+
+/*
+ * Alternative implementation of the one above but bypasses ACCESS DENIED.
+ */
+static PyObject*
+get_process_create_time_2(PyObject* self, PyObject* args)
+{
+    DWORD pid;
+    PSYSTEM_PROCESS_INFORMATION process;
+    PVOID buffer;
+    long long   unix_time;
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        return NULL;
+    }
+    if (! get_process_info(pid, &process, &buffer)) {
+        return NULL;
+    }
+    // special case for PIDs 0 and 4, return BOOT_TIME
+    if (0 == pid || 4 == pid) {
+        return get_system_uptime(NULL, NULL);
+    }
+    /*
+    Convert the LARGE_INTEGER union 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)process->CreateTime.HighPart) << 32;
+    unix_time += process->CreateTime.LowPart - 116444736000000000LL;
+    unix_time /= 10000000;
+    free(buffer);
+    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;
+    }
+
+    // XXX the assumptio below probably needs to go away
+
+    // 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 process executable path.
+ */
+static PyObject*
+get_process_exe(PyObject* self, PyObject* args) {
+    long pid;
+    HANDLE hProcess;
+    wchar_t exe[MAX_PATH];
+    DWORD nSize = MAX_PATH;
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        return NULL;
+    }
+
+    hProcess = handle_from_pid_waccess(pid, PROCESS_QUERY_INFORMATION);
+    if (NULL == hProcess) {
+        return NULL;
+    }
+
+    if (GetProcessImageFileName(hProcess, &exe, nSize) == 0) {
+        CloseHandle(hProcess);
+        return PyErr_SetFromWindowsErr(0);
+    }
+
+    CloseHandle(hProcess);
+    return Py_BuildValue("s", exe);
+}
+
+
+/*
+ * Return process memory information as a Python tuple.
+ */
+static PyObject*
+get_process_memory_info(PyObject* self, PyObject* args)
+{
+    HANDLE hProcess;
+    DWORD pid;
+#if (_WIN32_WINNT >= 0x0501)  // Windows XP with SP2
+    PROCESS_MEMORY_COUNTERS_EX cnt;
+#else
+    PROCESS_MEMORY_COUNTERS cnt;
+#endif
+    SIZE_T private = 0;
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        return NULL;
+    }
+
+    hProcess = handle_from_pid(pid);
+    if (NULL == hProcess) {
+        return NULL;
+    }
+
+    if (! GetProcessMemoryInfo(hProcess, &cnt, sizeof(cnt)) ) {
+        CloseHandle(hProcess);
+        return PyErr_SetFromWindowsErr(0);
+    }
+
+#if (_WIN32_WINNT >= 0x0501)
+    private = cnt.PrivateUsage;
+#endif
+
+    CloseHandle(hProcess);
+
+// py 2.4
+#if (PY_MAJOR_VERSION == 2) && (PY_MINOR_VERSION <= 4)
+    return Py_BuildValue("(kIIIIIIIII)",
+        cnt.PageFaultCount,
+        (unsigned int)cnt.PeakWorkingSetSize,
+        (unsigned int)cnt.WorkingSetSize,
+        (unsigned int)cnt.QuotaPeakPagedPoolUsage,
+        (unsigned int)cnt.QuotaPagedPoolUsage,
+        (unsigned int)cnt.QuotaPeakNonPagedPoolUsage,
+        (unsigned int)cnt.QuotaNonPagedPoolUsage,
+        (unsigned int)cnt.PagefileUsage,
+        (unsigned int)cnt.PeakPagefileUsage,
+        (unsigned int)private);
+#else
+// py >= 2.5
+    return Py_BuildValue("(knnnnnnnnn)",
+        cnt.PageFaultCount,
+        cnt.PeakWorkingSetSize,
+        cnt.WorkingSetSize,
+        cnt.QuotaPeakPagedPoolUsage,
+        cnt.QuotaPagedPoolUsage,
+        cnt.QuotaPeakNonPagedPoolUsage,
+        cnt.QuotaNonPagedPoolUsage,
+        cnt.PagefileUsage,
+        cnt.PeakPagefileUsage,
+        private);
+#endif
+}
+
+
+/*
+ * Alternative implementation of the one above but bypasses ACCESS DENIED.
+ */
+static PyObject*
+get_process_memory_info_2(PyObject* self, PyObject* args)
+{
+    DWORD pid;
+    PSYSTEM_PROCESS_INFORMATION process;
+    PVOID buffer;
+    ULONG m0;
+    SIZE_T m1, m2, m3, m4, m5, m6, m7, m8, m9;
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        return NULL;
+    }
+    if (! get_process_info(pid, &process, &buffer)) {
+        return NULL;
+    }
+    m0 = process->PageFaultCount;
+    m1 = process->PeakWorkingSetSize;
+    m2 = process->WorkingSetSize;
+    m3 = process->QuotaPeakPagedPoolUsage;
+    m4 = process->QuotaPagedPoolUsage;
+    m5 = process->QuotaPeakNonPagedPoolUsage;
+    m6 = process->QuotaNonPagedPoolUsage;
+    m7 = process->PagefileUsage;
+    m8 = process->PeakPagefileUsage;
+#if (_WIN32_WINNT >= 0x0501)
+    m9 = process->PrivatePageCount;  // private me
+#else
+    m9 = 0;
+#endif
+    free(buffer);
+
+// py 2.4
+#if (PY_MAJOR_VERSION == 2) && (PY_MINOR_VERSION <= 4)
+    return Py_BuildValue("(kIIIIIIIII)",
+        (unsigned int)m0, (unsigned int)m1, (unsigned int)m2, (unsigned int)m3,
+        (unsigned int)m4, (unsigned int)m5, (unsigned int)m6, (unsigned int)m7,
+        (unsigned int)m8, (unsigned int)m9);
+#else
+    return Py_BuildValue("(knnnnnnnnn)",
+        m0, m1, m2, m3, m4, m5, m6, m7, m8, m9);
+#endif
+}
+
+
+/*
+ * Return a Python integer indicating the total amount of physical memory
+ * in bytes.
+ */
+static PyObject*
+get_virtual_mem(PyObject* self, PyObject* args)
+{
+    MEMORYSTATUSEX memInfo;
+    memInfo.dwLength = sizeof(MEMORYSTATUSEX);
+
+    if (! GlobalMemoryStatusEx(&memInfo) ) {
+        return PyErr_SetFromWindowsErr(0);
+    }
+
+    return Py_BuildValue("(LLLLLL)",
+        memInfo.ullTotalPhys,      // total
+        memInfo.ullAvailPhys,      // avail
+        memInfo.ullTotalPageFile,  // total page file
+        memInfo.ullAvailPageFile,  // avail page file
+        memInfo.ullTotalVirtual,   // total virtual
+        memInfo.ullAvailVirtual    // avail virtual
+    );
+}
+
+
+#define LO_T ((float)1e-7)
+#define HI_T (LO_T*4294967296.0)
+
+
+/*
+ * Return a Python list of tuples representing user, kernel and idle
+ * CPU times for every CPU on the system.
+ */
+static PyObject*
+get_system_cpu_times(PyObject* self, PyObject* args)
+{
+    float idle, kernel, user;
+    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;
+    PyObject *arg = NULL;
+    PyObject *retlist = PyList_New(0);
+
+    // 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++) {
+                        arg = NULL;
+                        user = (float)((HI_T * sppi[i].UserTime.HighPart) + \
+                                       (LO_T * sppi[i].UserTime.LowPart));
+                        idle = (float)((HI_T * sppi[i].IdleTime.HighPart) + \
+                                       (LO_T * sppi[i].IdleTime.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
+                        arg = Py_BuildValue("(ddd)", user,
+                                                     kernel - idle,
+                                                     idle);
+                        if (!arg)
+                            goto error;
+                        if (PyList_Append(retlist, arg))
+                            goto error;
+                        Py_DECREF(arg);
+                    }
+                    free(sppi);
+                    FreeLibrary(hNtDll);
+                    return retlist;
+
+                }  // END NtQuerySystemInformation
+            }  // END malloc SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
+        }  // END GetProcAddress
+    }  // END LoadLibrary
+    goto error;
+
+error:
+    Py_XDECREF(arg);
+    Py_DECREF(retlist);
+    if (sppi) {
+        free(sppi);
+    }
+    if (hNtDll) {
+        FreeLibrary(hNtDll);
+    }
+    PyErr_SetFromWindowsErr(0);
+    return NULL;
+}
+
+
+/*
+ * Return process current working directory as a Python string.
+ */
+
+static PyObject*
+get_process_cwd(PyObject* self, PyObject* args)
+{
+    long pid;
+    HANDLE processHandle = NULL;
+    PVOID pebAddress;
+    PVOID rtlUserProcParamsAddress;
+    UNICODE_STRING currentDirectory;
+    WCHAR *currentDirectoryContent = NULL;
+    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) {
+            // this occurs quite often with system processes
+            return AccessDenied();
+        }
+        else {
+            return PyErr_SetFromWindowsErr(0);
+        }
+    }
+
+    // Read the currentDirectory UNICODE_STRING structure.
+    // 0x24 refers to "CurrentDirectoryPath" of RTL_USER_PROCESS_PARAMETERS
+    // structure, see:
+    // http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/
+#ifdef _WIN64
+    if (!ReadProcessMemory(processHandle, (PCHAR)rtlUserProcParamsAddress + 56,
+        &currentDirectory, sizeof(currentDirectory), NULL))
+#else
+    if (!ReadProcessMemory(processHandle, (PCHAR)rtlUserProcParamsAddress + 0x24,
+        &currentDirectory, sizeof(currentDirectory), NULL))
+#endif
+    {
+        CloseHandle(processHandle);
+        if (GetLastError() == ERROR_PARTIAL_COPY) {
+            // this occurs quite often with system processes
+            return AccessDenied();
+        }
+        else {
+            return PyErr_SetFromWindowsErr(0);
+        }
+    }
+
+    // allocate memory to hold cwd
+    currentDirectoryContent = (WCHAR *)malloc(currentDirectory.Length+1);
+
+    // read cwd
+    if (!ReadProcessMemory(processHandle, currentDirectory.Buffer,
+        currentDirectoryContent, currentDirectory.Length, NULL))
+    {
+        if (GetLastError() == ERROR_PARTIAL_COPY) {
+            // this occurs quite often with system processes
+            AccessDenied();
+        }
+        else {
+            PyErr_SetFromWindowsErr(0);
+        }
+        goto error;
+    }
+
+    // null-terminate the string to prevent wcslen from returning
+    // incorrect length the length specifier is in characters, but
+    // currentDirectory.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 (cwd_from_wchar == NULL)
+        goto error;
+
+    #if PY_MAJOR_VERSION >= 3
+        cwd = PyUnicode_FromObject(cwd_from_wchar);
+    #else
+        cwd = PyUnicode_AsUTF8String(cwd_from_wchar);
+    #endif
+    if (cwd == NULL)
+        goto error;
+
+    // decrement the reference count on our temp unicode str to avoid
+    // mem leak
+    returnPyObj = Py_BuildValue("N", cwd);
+    if (!returnPyObj)
+        goto error;
+
+    Py_DECREF(cwd_from_wchar);
+
+    CloseHandle(processHandle);
+    free(currentDirectoryContent);
+    return returnPyObj;
+
+error:
+    Py_XDECREF(cwd_from_wchar);
+    Py_XDECREF(cwd);
+    Py_XDECREF(returnPyObj);
+    if (currentDirectoryContent != NULL)
+        free(currentDirectoryContent);
+    if (processHandle != NULL)
+        CloseHandle(processHandle);
+    return NULL;
+}
+
+
+/*
+ * 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};
+
+    if (pid == 0) {
+        AccessDenied();
+        return FALSE;
+    }
+
+    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));
+
+    CloseHandle(hThreadSnap);
+    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)
+{
+    DWORD pid;
+    PSYSTEM_PROCESS_INFORMATION process;
+    PVOID buffer;
+    int num;
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        return NULL;
+    }
+    if (! get_process_info(pid, &process, &buffer)) {
+        return NULL;
+    }
+    num = (int)process->NumberOfThreads;
+    free(buffer);
+    return Py_BuildValue("i", num);
+}
+
+
+static PyObject*
+get_process_threads(PyObject* self, PyObject* args)
+{
+    PyObject* retList = PyList_New(0);
+    PyObject* pyTuple = NULL;
+    HANDLE hThreadSnap = NULL;
+    HANDLE hThread;
+    THREADENTRY32 te32 = {0};
+    long pid;
+    int pid_return;
+    int rc;
+    FILETIME ftDummy, ftKernel, ftUser;
+
+    if (! PyArg_ParseTuple(args, "l", &pid)) {
+        goto error;
+    }
+    if (pid == 0) {
+        // raise AD instead of returning 0 as procexp is able to
+        // retrieve useful information somehow
+        AccessDenied();
+        goto error;
+    }
+
+    pid_return = pid_is_running(pid);
+    if (pid_return == 0) {
+        NoSuchProcess();
+        goto error;
+    }
+    if (pid_return == -1) {
+        goto error;
+    }
+
+    hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
+    if (hThreadSnap == INVALID_HANDLE_VALUE) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    // Fill in the size of the structure before using it
+    te32.dwSize = sizeof(THREADENTRY32);
+
+    if (! Thread32First(hThreadSnap, &te32)) {
+        PyErr_SetFromWindowsErr(0);
+        goto error;
+    }
+
+    // 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)
+        {
+            pyTuple = NULL;
+            hThread = NULL;
+            hThread = OpenThread(THREAD_QUERY_INFORMATION,
+                                  FALSE, te32.th32ThreadID);
+            if (hThread == NULL) {
+                // thread has disappeared on us
+                continue;
+            }
+
+            rc = GetThreadTimes(hThread, &ftDummy, &ftDummy, &ftKernel, &ftUser);
+            if (rc == 0) {
+                PyErr_SetFromWindowsErr(0);
+                goto error;
+            }
+
+            /*
+            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
+            */
+            pyTuple = Py_BuildValue("kdd",
+                            te32.th32ThreadID,
+                            (double)(ftUser.dwHighDateTime*429.4967296 + \
+                                     ftUser.dwLowDateTime*1e-7),
+                            (double)(ftKernel.dwHighDateTime*429.4967296 + \
+                                     ftKernel.dwLowDateTime*1e-7));
+            if (!pyTuple)
+                goto error;
+            if (PyList_Append(retList, pyTuple))
+                goto error;
+            Py_DECREF(pyTuple);
+
+            CloseHandle(hThread);
+        }
+    } while (Thread32Next(hThreadSnap, &te32));
+
+    CloseHandle(hThreadSnap);
+    return retList;
+
+error:
+    Py_XDECREF(pyTuple);
+    Py_DECREF(retList);
+    if (hThread != NULL)
+        CloseHandle(hThread);
+    if (hThreadSnap != NULL) {
+        CloseHandle(hThreadSnap);
+    }
+    return NULL;
+}
+
+
+
+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);
+    CloseHandle(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*
+win32_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++;
+    }
+    ret