diff -Nru python-psutil-0.1.3/CREDITS python-psutil-0.2.1/CREDITS --- python-psutil-0.1.3/CREDITS 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/CREDITS 2010-12-30 16:45:02.000000000 +0000 @@ -0,0 +1,56 @@ + +Intro +===== + +We would like to recognize some of the people who have been instrumental in the +development of psutil. +I'm sure we are forgetting some people (feel free to email us), but here is a +short list. +It's modeled after the Linux CREDITS file where the fields are: +name (N), e-mail (E), web-address (W), country (C), description (D), (I) issues +(issue tracker is at http://code.google.com/p/psutil/issues/list). +Really thanks to all of you. + + +Maintainers +=========== + +N: Giampaolo Rodola' +C: Italy +E: g.rodola@gmail.com + +N: Jay Loden +C: NJ, USA +E: jloden@gmail.com +W: http://www.jayloden.com + + +Contributors +============ + +N: wj32 +E: wj32.64@gmail.com +D: process username() and get_connections() on Windows +I: 114, 115 + +N: Yan Raber +C: Bologna, Italy +E: yanraber@gmail.com +D: help on Windows development + +N: Dave Daeschler +C: USA +E: ddaeschl@rsaisp.com + +N: cjgohlke +E: cjgohlke@gmail.com +D: Windows 64 bit support +I: 107 + +N: Jeffery Kline +E: jeffery.kline@gmail.com +I: 130 + +N: Grabriel Monnerat +E: gabrielmonnerat@gmail.com +I: 146 diff -Nru python-psutil-0.1.3/debian/changelog python-psutil-0.2.1/debian/changelog --- python-psutil-0.1.3/debian/changelog 2011-09-14 18:37:19.000000000 +0000 +++ python-psutil-0.2.1/debian/changelog 2011-09-14 18:37:20.000000000 +0000 @@ -1,9 +1,41 @@ -python-psutil (0.1.3-1ubuntu1) natty; urgency=low +python-psutil (0.2.1-1natty1) natty; urgency=low - * debian/control: - - B-D on python-all to fix FTBFS + * Rebuild for natty. - -- Michael Terry Mon, 11 Apr 2011 11:15:53 -0400 + -- Thomas Hervé Wed, 14 Sep 2011 14:22:50 -0400 + +python-psutil (0.2.1-1ubuntu1) oneiric; urgency=low + + * Switch to dh_python2. (LP: #788514) + + -- Barry Warsaw Wed, 10 Aug 2011 14:47:19 -0400 + +python-psutil (0.2.1-1) unstable; urgency=low + + * New upstream release + * debian/copyright + - extended packaging copyright years + * debian/rules + - use the correct PYTHONPATH when running tests, for all supported versions + * debian/control + - it now contains also extensions, so it's an arch:any package + - move python-support from b-d-i to b-d + - we now need python-all-dev in b-d + - added procps to b-d, needed to run tests + * debian/{control, pyversion} + - removed pyversion, replaced by XS-P-V field + + -- Sandro Tosi Mon, 04 Apr 2011 20:26:42 +0200 + +python-psutil (0.2.0-1) experimental; urgency=low + + * New upstream release + * debian/control + - bump Standards-Version to 3.9.1 (no changes needed) + * debian/python-psutil.doc-base + - added doc-base + + -- Sandro Tosi Mon, 15 Nov 2010 20:45:35 +0100 python-psutil (0.1.3-1) unstable; urgency=low diff -Nru python-psutil-0.1.3/debian/control python-psutil-0.2.1/debian/control --- python-psutil-0.1.3/debian/control 2011-09-14 18:37:19.000000000 +0000 +++ python-psutil-0.2.1/debian/control 2011-09-14 18:37:20.000000000 +0000 @@ -4,15 +4,14 @@ Maintainer: Ubuntu Developers XSBC-Original-Maintainer: Debian Python Modules Team Uploaders: Sandro Tosi -Build-Depends: debhelper (>= 7.2.18), python-all -Build-Depends-Indep: python-support (>= 1.0.0) -Standards-Version: 3.8.4 +Build-Depends: debhelper (>= 7.2.18), python-all-dev (>= 2.6.6-3~), procps +Standards-Version: 3.9.1 Homepage: http://code.google.com/p/psutil/ Vcs-Svn: svn://svn.debian.org/python-modules/packages/python-psutil/trunk/ Vcs-Browser: http://svn.debian.org/viewsvn/python-modules/packages/python-psutil/trunk/ Package: python-psutil -Architecture: all +Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends} Description: module providing convenience functions for managing processes psutil is a module providing an interface for retrieving information on diff -Nru python-psutil-0.1.3/debian/copyright python-psutil-0.2.1/debian/copyright --- python-psutil-0.1.3/debian/copyright 2011-09-14 18:37:19.000000000 +0000 +++ python-psutil-0.2.1/debian/copyright 2011-09-14 18:37:20.000000000 +0000 @@ -38,6 +38,6 @@ The Debian packaging is: - Copyright (C) 2009 Sandro Tosi + Copyright (C) 2009-2011 Sandro Tosi and is licensed under the same terms as upstream code. diff -Nru python-psutil-0.1.3/debian/python-psutil.doc-base python-psutil-0.2.1/debian/python-psutil.doc-base --- python-psutil-0.1.3/debian/python-psutil.doc-base 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/debian/python-psutil.doc-base 2011-09-14 18:37:20.000000000 +0000 @@ -0,0 +1,13 @@ +Document: psutil +Title: Psutil Summary +Author: Jay Loden, Giampaolo Rodola' +Abstract: psutil is a module providing an interface for retrieving + information on running processes and system utilization (CPU, memory) + in a portable way by using Python, implementing many functionalities + offered by command line tools like ps, top, kill and Windows task + manager. +Section: Programming/Python + +Format: HTML +Index: /usr/share/doc/python-psutil/docs/index.html +Files: /usr/share/doc/python-psutil/docs/* diff -Nru python-psutil-0.1.3/debian/pyversions python-psutil-0.2.1/debian/pyversions --- python-psutil-0.1.3/debian/pyversions 2011-09-14 18:37:19.000000000 +0000 +++ python-psutil-0.2.1/debian/pyversions 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -2.4- diff -Nru python-psutil-0.1.3/debian/rules python-psutil-0.2.1/debian/rules --- python-psutil-0.1.3/debian/rules 2011-09-14 18:37:19.000000000 +0000 +++ python-psutil-0.2.1/debian/rules 2011-09-14 18:37:20.000000000 +0000 @@ -1,6 +1,9 @@ #!/usr/bin/make -f + +PYVERS:=$(shell pyversions -s) + %: - dh $@ + dh $@ --with python2 build: dh build @@ -8,8 +11,11 @@ # run tests for test in test_memory_leaks.py test_psutil.py ;\ do \ - echo "running "test/$$test ; \ - PYTHONPATH=. python test/$$test ; \ + for python in $(PYVERS) ; do \ + echo "running "test/$$test" on "$$python ; \ + LIB=$$($$python -c "from distutils.command.build import build ; from distutils.core import Distribution ; b = build(Distribution()) ; b.finalize_options() ; print b.build_platlib") ; \ + PYTHONPATH=$$LIB $$python test/$$test ; \ + done \ done override_dh_installchangelogs: diff -Nru python-psutil-0.1.3/debian/source/format python-psutil-0.2.1/debian/source/format --- python-psutil-0.1.3/debian/source/format 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/debian/source/format 2011-09-14 18:37:20.000000000 +0000 @@ -0,0 +1 @@ +1.0 diff -Nru python-psutil-0.1.3/docs/class_diagram.dia python-psutil-0.2.1/docs/class_diagram.dia --- python-psutil-0.1.3/docs/class_diagram.dia 2009-09-13 03:42:25.000000000 +0000 +++ python-psutil-0.2.1/docs/class_diagram.dia 2010-11-13 02:19:21.000000000 +0000 @@ -62,22 +62,28 @@ - + - + - + + + + - + - + - + + + + @@ -97,19 +103,19 @@ - + - + - + - + - + #psutil (module)# @@ -118,8 +124,119 @@ ## + #main py module# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Process class# + + ## + + #higher-level public python wrapper# + @@ -136,7 +253,7 @@ - + @@ -150,6 +267,9 @@ + + + @@ -195,17 +315,110 @@ - + + + + #pid# + + + ## + + + ## + + + ## + + + + + + + + + + + + + + #name# + + + ## + + + ## + + + ## + + + + + + + + + + + + + + #cmdline# + + + ## + + + ## + + + ## + + + + + + + + + + + + + + #...# + + + ## + + + ## + + + ## + + + + + + + + + + + + - #get_process_list# + #get_cpu_times# ## - #Process[]# + ## @@ -229,13 +442,13 @@ - #iter_processes# + #terminate# ## - #Process# + ## @@ -259,13 +472,13 @@ - #find_processes# + #kill# ## - #Process[]# + ## @@ -285,91 +498,78 @@ - - - - #pid# - - - #int# - - - #0# - - - ## - - - - - - - - #name# - - - #string# - - - #""# - - - ## - - - - - - - - #path# - - - #string# - - - #""# - - - ## - - - - - + + + + + #...# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + - + + + + ## + + + ## + + + - + - + - + - + - + - + - #Process# + #_ps[platform].py module# ## - ## + #intermediate python wrapper module# @@ -381,13 +581,13 @@ - + - + @@ -401,6 +601,9 @@ + + + @@ -446,81 +649,71 @@ - - + + + - #pid# - - - #int# + #process_exists# - + ## - + ## - - - - - - - - - - #name# - - - #string# - - - ## - ## - - - + + + + + + + - + - #path# - - - #string# + #kill_process# - + ## - + ## + + ## + + + + + + + + - - - #Process# + #get_pid_list# ## @@ -546,29 +739,11 @@ - - - - #pid# - - - #int# - - - ## - - - ## - - - - - - + - #kill# + #...# ## @@ -603,30 +778,30 @@ - + - + - + - + - + - + - #PlatformImpl# + #_psutil_[platform].c# ## - ## + #C extension# @@ -644,7 +819,7 @@ - + @@ -658,6 +833,9 @@ + + + @@ -884,26 +1062,25 @@ - - + - + - + - + - + - + - #ProcessInfo# + #System-related objects# ## @@ -924,10 +1101,10 @@ - + - + @@ -941,6 +1118,9 @@ + + + @@ -989,10 +1169,10 @@ - #pid# + #TOTAL_PHYMEM# - #int# + ## ## @@ -1010,208 +1190,284 @@ - + + + - #name# - - - #string# + #get_pid_list# - + ## - + ## + + ## + + + + + + + + - + - #path# - - - #string# + #cpu_times# - + ## - + ## + + ## + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ## - - - - - - - + + + #pid_exists# + + + ## + + ## - - #1# + + + + + ## - + - - + + - - + + + + + + - - + + + #...# + + + ## + + ## - - #1# + + + + + ## - + - + - - + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + - + - + - - - - - - - - - - + + + - - + + - - + + - - + + - - ## + + - - ## + + + + + + + + + - - + + + + + + + + + + + + + + + + + + - - + + - + - + - + - - - - - - - - + + + - - + + - - + + - - + + - - ## + + - - ## + + + + + + + + + - - + + + + + + + + + + + + + + + + + + - - + + Binary files /tmp/axSshgHNa2/python-psutil-0.1.3/docs/class_diagram.png and /tmp/wQcuD3KndE/python-psutil-0.2.1/docs/class_diagram.png differ diff -Nru python-psutil-0.1.3/docs/documentation.html python-psutil-0.2.1/docs/documentation.html --- python-psutil-0.1.3/docs/documentation.html 2009-09-14 08:18:38.000000000 +0000 +++ python-psutil-0.2.1/docs/documentation.html 2011-03-19 20:43:08.000000000 +0000 @@ -1,141 +1,120 @@ - - - - - - - - psutil - Documentation - - - - - -
- - - - - -
- -
- - Documentation -   - - -
API reference
- -
-

API Reference

Exceptions

psutil.NoSuchProcess
-

Raised when no process with the given PID/parameter was - found in the current process list. -

psutil.AccessDenied
-

Exception raised when permission to perform an action -is denied. -


Classes

psutil.Process(pid) -

A class which represents an OS -process. -
New in 0.1.3 -
New in 0.1.3 - Availability Windows, Linux - -
New in 0.1.3 -
New -in 0.1.3 -

Functions

psutil.get_pid_list()
-

Return a list of current -running PIDs. -

psutil.pid_exists(pid)
-

Check whether the given PID -exists in the current process list. -

psutil.process_iter()
-

Return an iterator yielding a -Process class instances for all running processes on the local machine. -


System -related objects

psutil.cpu_percent() -

Return the current - system-wide CPU utilization as a percentage. For highest accuracy, it -is recommended that this be called at least 1/10th of a second after -importing the module or calling cpu_percent() in a previous call, to -allow for a larger time delta from which to calculate the percentage -value. -

psutil.cpu_times() -

Return system CPU -times as a CPUTimes class object. Every CPU time is accessible -in form of a CPUTimes attribute and represents the time CPU has - spent in the given mode.
The attributes availability varies -depending on the platform. Here follows a list of all available -attributes: -

- user
- -- system
-- idle
-- nice (UNIX)
-- iowait (Linux)
-- irq (Linux, FreeBSD)
-- softirq (Linux)
-

psutil.TOTAL_PHYMEM
- psutil.avail_phymem()
psutil.used_phymem()
-

Return the amount -of total, available and used physical memory on the system, in bytes. -

psutil.total_virtmem()
- psutil.avail_virtmem()
psutil.used_virtmem()
-

Return the amount -of total, available and used virtual memory on the system, in bytes.
On - Linux they match the values returned by free command line -utility. On OS X and FreeBSD they represent the same values as returned -by sysctl vm.vmtotal. On Windows they are determined by reading - the *PageFile values of
MEMORYSTATUSEX structure. -

-
-
- - - - - - \ No newline at end of file + + + + DocumentationDev + + + + + +
+ + + + + +
+ +
+
+

API Reference

Exceptions

psutil.NoSuchProcess?(pid, name=None, msg=None)

Raised when no process with the given PID is found in the current process list or when a process no longer exists (zombie). +

Changed in 0.2.0: pid, name and msg arguments were added +

psutil.AccessDenied?(pid=None, name=None, msg=None)

Raised when permission to perform an action is denied. +

Changed in 0.2.0: pid, name and msg arguments were added +

psutil.TimeoutExpired?(pid=None, name=None)

Raised on Process.wait(timeout) if timeout expires and process is still alive. +

New in 0.2.1 +

Classes

psutil.Process(pid)

A class which represents an OS process. +
  • pid
    The process pid.
  • ppid
    The process parent pid.
  • parent
    Return the parent process as a Process object. If no ppid is known then return None.
  • name
    The process name.
  • exe
    The process executable as an absolute path name.
New in 0.2.0 +
  • cmdline
    The command line process has been called with.
  • path
    The process path.
Deprecated in 0.2.0 +
  • create_time
    The process creation time as a floating point number expressed in seconds since the epoch, in UTC.
  • uids
    The real, effective and saved user ids of the current process as a nameduple. This is the same as os.getresuid() but per-process.
New in 0.2.1
Availability: UNIX +
  • gids
    The real, effective and saved group ids of the current process as a nameduple. This is the same as os.getresgid() but per-process.
New in 0.2.1
Availability: UNIX +
  • uid
    The real user id of the current process. This is the same as os.getuid() but per-process. On Windows always return -1.
Deprecated in 0.2.1: use uids property instead. +
  • gid
    The real group id of the current process. This is the same as os.getgid() but per-process. On Windows always return -1.
Deprecated in 0.2.1: use gids property instead. +
  • username
    The name of the user that owns the process. On UNIX this is calculated by using real process uid.
Changed in 2.0: Windows implementation has been rewritten in C for performace and pywin32 extension is no longer required. +
  • status
    The current process status. The return value is one of the STATUS_* constants, which is an integer that can be used in conjunction with str() to obtain a human-readable form of the current process status. Example:
  • >>> import psutil, os
    +>>> p = psutil.Process(os.getpid())
    +>>> p.status
    +0
    +>>> p.status == psutil.STATUS_RUNNING
    +True
    +>>> str(p.status)
    +'running'
    +>>> 
New in 2.1 +
  • nice
    Get or set process niceness (priority). On UNIX this is a number which usually goes from -20 to 20. The higher the nice value, the lower the priority of the process.
  • >>> p = psutil.Process(os.getpid())
    +>>> p.nice
    +0
    +>>> p.nice = 10  # set/change process priority
    +>>> p.nice
    +10
On Windows this is available as well by using GetPriorityClass and SetPriorityClass. psutil.*_PRIORITY_CLASS constants (explained here) can be used in conjunction. Example which increases process priority: +
>> p.nice = psutil.HIGH_PRIORITY_CLASS
+
New in 2.1 +
  • getcwd()
    Return a string representing the process current working directory.
Availability: Windows, Linux +
  • get_io_counters()
    Return process I/O statistics as a namedtuple including the number of read and write operations performed by the process and the amount of bytes read and written. For linux refer to /proc filesysem documentation. On BSD there's apparently no way to retrieve bytes counters, hence -1 is returned for read_bytes and write_bytes fields. OSX is not supported.
  • >>> p.get_io_counters()
    +io(read_count=454556, write_count=3456, read_bytes=110592, write_bytes=0)
New in 2.1
Availability: Linux, Windows, FreeBSD +
  • get_ionice()
    Return process I/O niceness (priority) as a namedtuple including priority class and priority data. See set_ionice below for more information.
New in 2.1
Availability: Linux +
  • set_ionice(ioclass, iodata=None)
    Change 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 iodata value, the lower the I/O priority of the process. For further information refer to ionice command line utility or ioprio_set system call. The example below sets IDLE priority class for the current process, meaning it will only get I/O time when no other process needs the disk:
  • >>> import psutil, os
    +>>> p = psutil.Process(os.getpid())
    +>>> p.set_ionice(psutil.IOPRIO_CLASS_IDLE)
    +>>> 
New in 2.1
Availability: Linux +
  • get_num_threads()
    Return the number of threads used by this process.
New in 0.2.0 +
  • get_threads()
    Return threads opened by process as a list of namedtuples including thread id and thread CPU times (user/system).
New in 0.2.1 +
  • get_cpu_times()
    Return a tuple whose values are process CPU user and system times which means the amount of time expressed in seconds that a process has spent in user/system mode.
  • get_cpu_percent(interval=0.1)
    Return a float representing the process CPU utilization as a percentage. When interval is > 0.0 compares process times to system CPU times elapsed before and after the interval (blocking). When interval is 0.0 or None compares process times to system CPU times elapsed since last call, returning immediately. In this case is recommended for accuracy that this function be called with at least 0.1 seconds between calls.
Changed in 0.2.0: interval parameter was added +
  • get_memory_info()
    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.
  • get_memory_percent()
    Compare physical system memory to process resident memory and calculate process memory utilization as a percentage.
  • get_children()
    Return the children of this process as a list of Process objects.
New in 0.2.0 +
  • get_open_files()
    Return files opened by process as a list of namedtuples including file absolute path name and file descriptor. On FreeBSD this is done by parsing lsof command output. If lsof is not installed on the system NotImplementedError exception is raised. Example:
  • >>> import psutil, os
    +>>> f = open('file.ext', 'w')
    +>>> psutil.Process(os.getpid()).get_open_files()
    +[openfile(path='/home/giampaolo/svn/psutil/file.ext', fd=3)]
New in 0.2.0
+Changed in 0.2.1: OSX implementation rewritten in C; no longer requiring lsof. +
  • get_connections()
    Return all TCP and UPD connections opened by process as a list of namedtuples. Every namedtuple provides 6 attributes:

    • fd: the socket file descriptor. This can be passed to socket.fromfd to obtain a usable socket object. This is only available on UNIX; on Windows -1 is always returned.
    • family: the address family, either AF_INET or AF_INET6
    • type: the address type, either SOCK_STREAM or SOCK_DGRAM
    • local_address: the local address as a (ip, port) tuple.
    • remote_address: the remote address as a (ip, port) tuple. When the remote endpoint is not connected the tuple is empty.
    • status: a string representing the TCP connections status. String values are supposed to match Linux's tcp_states.h header file across all platforms. For UDP sockets this is always going to be an empty string.
Example: +
>>> p = psutil.Process(1694)
+>>> p.name
+'firefox'
+>>> p.get_connections()
+[connection(fd=115, family=2, type=1, local_address=('10.0.0.1', 48776), remote_address=('93.186.135.91', 80), status='ESTABLISHED'),
+ connection(fd=117, family=2, type=1, local_address=('10.0.0.1', 43761), remote_address=('72.14.234.100', 80), status='CLOSING'),
+ connection(fd=119, family=2, type=1, local_address=('10.0.0.1', 60759), remote_address=('72.14.234.104', 80), status='ESTABLISHED'),
+ connection(fd=123, family=2, type=1, local_address=('10.0.0.1', 51314), remote_address=('72.14.234.83', 443), status='SYN_SENT')]
On FreeBSD this is implemented by parsing lsof command output. If lsof is not installed on the system NotImplementedError exception is raised and for third party processes (different than os.getpid()) results can differ depending on user privileges.
+
New in 0.2.0
+Changed in 0.2.1: OSX implementation rewritten in C; no longer requiring lsof. +
  • is_running()
    Return whether the current process is running in the current process list.
  • send_signal(signal)
    Send a signal to process (see signal module constants). On Windows only SIGTERM is valid and is treated as an alias for kill().
New in 0.2.0 +
  • suspend()
    Suspend process execution with SIGSTOP signal. On Windows this is done by suspending all process threads execution.
  • resume()
    Resume process execution with SIGCONT signal. On Windows this is done by resuming all process threads execution.
  • terminate()
    Terminate the process with SIGTERM signal. On Windows this is an alias for kill().
New in 0.2.0 +
  • kill()
    Kill the current process by using SIGKILL signal.
Changed in 0.2.0: no longer accepts sig keyword argument - use send_signal() instead. +
  • wait(timeout=None)
    Wait for process termination and if the process is a children of the current one also return the exit code, else None. On Windows there's no such limitation (exit code is always returned). If the process is already terminated does not raise NoSuchProcess exception but just return None immediately. If timeout is specified and process is still alive raises TimeoutExpired exception.
New in 0.2.1 +



psutil.Popen(*args, **kwargs)

A more convenient interface to stdlib subprocess.Popen. It starts a sub process and deals with it exactly as when using subprocess.Popen class but in addition also provides all the properties and methods of psutil.Process class in a unique interface. For method names common to both classes such as kill() and terminate(), psutil.Process implementation takes precedence. For a complete documentation refers to subprocess module documentation. +
New in 0.2.1 +

Functions

psutil.get_pid_list()

Return a list of current running PIDs. +

psutil.pid_exists(pid)

Check whether the given PID exists in the current process list. This is faster than doing pid in psutil.get_pid_list() and should be preferred. +

psutil.process_iter()

Return an iterator yielding a Process class instances for all running processes on the local machine. This should be preferred over doing for pid in psutil.get_pid_list(): psutil.Process(pid) as it safe from race conditions. +


System related objects

psutil.cpu_percent(interval=0.1)

Return a float representing the current system-wide CPU utilization as a percentage. When interval is > 0.0 compares system CPU times elapsed before and after the interval (blocking). When interval is 0.0 or None compares system CPU times elapsed since last call or module import, returning immediately. In this case is recommended for accuracy that this function be called with at least 0.1 seconds between calls. +

Changed in 0.2.0: interval parameter was added +

psutil.cpu_times()

Return system CPU times as a namedtuple. Every CPU time is accessible in form of a CPUTimes attribute and represents the time CPU has spent in the given mode.
The attributes availability varies depending on the platform. Here follows a list of all available attributes: +

- user
+- system
+- idle
+- nice (UNIX)
+- iowait (Linux)
+- irq (Linux, FreeBSD)
+- softirq (Linux)
+

psutil.avail_phymem()
psutil.used_phymem()

Return the amount of available and used physical memory on the system, in bytes. For total physical memory use psutil.BOOT_TIME constant. +

psutil.total_virtmem()
psutil.avail_virtmem()
psutil.used_virtmem()

Return the amount of total, available and used virtual memory on the system, in bytes.
On Linux they match the values returned by free command line utility. On OS X and FreeBSD they represent the same values as returned by sysctl vm.vmtotal. On Windows they are determined by reading the *PageFile values of MEMORYSTATUSEX structure. +

psutil.cached_phymem()

Return the amount of cached memory on the system, in bytes. This reflects the "cached" column of free command line utility on Linux. +

New in 0.2.0
Availability:
Linux +

psutil.phymem_buffers()

Return the amount of physical memory buffers used by the kernel in bytes. This reflects the "buffers" column of free command line utility on Linux. +

New in 0.2.0
Availability: Linux +

Constants

psutil.TOTAL_PHYMEM

The amount of total physical memory on the system, in bytes. +

psutil.NUM_CPUS

The number of CPUs on the system. This is preferable than using os.environ['NUMBER_OF_PROCESSORS'] as it is more accurate and always available. +

psutil.BOOT_TIME

A number indicating the system boot time expressed in seconds since the epoch. +

New in 0.2.1 +

psutil.ABOVE_NORMAL_PRIORITY_CLASS
psutil.BELOW_NORMAL_PRIORITY_CLASS
psutil.HIGH_PRIORITY_CLASS
psutil.IDLE_PRIORITY_CLASS
psutil.NORMAL_PRIORITY_CLASS
psutil.REALTIME_PRIORITY_CLASS

A set of integers representing the priority of a process on Windows (see MSDN documentation). They can be used in conjunction with psutil.Process.nice to get or set process priority. +

New in 0.2.1
Availability: Windows +

psutil.IOPRIO_CLASS_NONE
psutil.IOPRIO_CLASS_RT
psutil.IOPRIO_CLASS_BE
psutil.IOPRIO_CLASS_IDLE

A set of integers representing the I/O priority of a process on Linux. They can be used in conjunction with psutil.Process.get_ionice() and psutil.Process.set_ionice() to get or set process I/O priority. For further information refer to ionice command line utility or ioprio_get system call. +

New in 0.2.1
Availability: Linux +

psutil.STATUS_RUNNING
psutil.STATUS_SLEEPING
psutil.STATUS_DISK_SLEEP
psutil.STATUS_STOPPED
psutil.STATUS_TRACING_STOP
psutil.STATUS_ZOMBIE
psutil.STATUS_DEAD
psutil.STATUS_WAKE_KILL
psutil.STATUS_WAKING
psutil.STATUS_IDLE
psutil.STATUS_LOCKED
psutil.STATUS_WAITING

A set of integers representing the status of a process. To be used in conjunction with psutil.Process.status property. If used with str() return a human-readable status string. +

New in 0.2.1
+
+
+
+
+
+ + diff -Nru python-psutil-0.1.3/docs/index.html python-psutil-0.2.1/docs/index.html --- python-psutil-0.1.3/docs/index.html 2009-09-14 08:18:38.000000000 +0000 +++ python-psutil-0.2.1/docs/index.html 2010-11-13 01:37:45.000000000 +0000 @@ -1,26 +1,99 @@ - - - - -psutil - Home - - - -
-

Python process utilities (psutil)

-

Summary

-

psutil is a module providing an interface for retrieving information on running processes and system utilization (CPU, memory) in a portable way by using Python.

-

It currently supports Linux, OS X, FreeBSD and Windows.

-

Example usage

-
>>> import psutil
>>> psutil.get_pid_list()
[1, 2, 3, 4, 5, 6, 7, 46, 48, 50, 51, 178, 182, 222, 223, 224,
268, 1215, 1216, 1220, 1221, 1243, 1244, 1301, 1601, 2237, 2355,
2637, 2774, 3932, 4176, 4177, 4185, 4187, 4189, 4225, 4243, 4245,
4263, 4282, 4306, 4311, 4312, 4313, 4314, 4337, 4339, 4357, 4358,
4363, 4383, 4395, 4408, 4433, 4443, 4445, 4446, 5167, 5234, 5235,
5252, 5318, 5424, 5644, 6987, 7054, 7055, 7071]
>>> p = psutil.Process(7055)
>>> p.name
'python'
>>> p.path
'/usr/bin'
>>> p.cmdline
['/usr/bin/python', '-O']
>>> p.uid
1000
>>> p.gid
1000
>>> rss, vms =  p.get_memory_info()
>>> print "Resident memory: %s KB" %(rss / 1024)
Resident memory: 3768 KB
>>> print "Virtual memory: %s KB" %(vms / 1024)
Virtual memory: 6176 KB
>>>
>>> psutil.test()
UID       PID %CPU %MEM     VSZ     RSS START     TIME COMMAND
0           0  0.0  0.0       0       0 Mar20    00:00 [sched]
0           1  0.0  0.3    1740     600 Mar20    00:04 /sbin/init
0           2  0.0  0.0       0       0 Mar20    00:00 [kthreadd]
0           3  0.0  0.0       0       0 Mar20    00:00 [migration/0]
0           4  0.0  0.0       0       0 Mar20    00:05 [ksoftirqd/0]
0           5  0.0  0.0       0       0 Mar20    00:00 [watchdog/0]
0           6  0.0  0.0       0       0 Mar20    00:03 [events/0]
0           7  0.0  0.0       0       0 Mar20    00:00 [khelper]
0          46  0.0  0.0       0       0 Mar20    00:00 [kintegrityd/0]
0          48  0.0  0.0       0       0 Mar20    00:01 [kblockd/0]
0          50  0.0  0.0       0       0 Mar20    00:00 [kacpid]
0          51  0.0  0.0       0       0 Mar20    00:00 [kacpi_notify]
0         179  0.0  0.0       0       0 Mar20    00:00 [cqueue]
0         183  0.0  0.0       0       0 Mar20    00:00 [kseriod]
0         223  0.0  0.0       0       0 Mar20    00:00 [pdflush]
0         224  0.0  0.0       0       0 Mar20    00:04 [pdflush]
0         225  0.0  0.0       0       0 Mar20    00:01 [kswapd0]
0         269  0.0  0.0       0       0 Mar20    00:00 [aio/0]
0        1221  0.0  0.0       0       0 Mar20    00:00 [ksuspend_usbd]
0        1222  0.0  0.0       0       0 Mar20    00:00 [khubd]
0        1226  0.0  0.0       0       0 Mar20    00:00 [ata/0]
0        1227  0.0  0.0       0       0 Mar20    00:00 [ata_aux]
0        1245  0.0  0.0       0       0 Mar20    00:04 [mpt_poll_0]
0        1402  0.0  0.0       0       0 Mar20    00:00 [scsi_eh_0]
0        1843  0.0  0.0       0       0 Mar20    00:00 [scsi_eh_1]
0        1844  0.0  0.0       0       0 Mar20    00:00 [scsi_eh_2]
0        2241  0.0  0.0       0       0 Mar20    00:06 [kjournald]
0        2359  0.0  0.4    2312     740 Mar20    00:02 /sbin/udevd --daemon
0        2678  0.0  0.0       0       0 Mar20    00:00 [kgameportd]
0        2796  0.0  0.0       0       0 Mar20    00:00 [kpsmoused]
0        4209  0.0  0.3    1780     536 Mar20    00:00 /sbin/getty 38400 tty4
0        4210  0.0  0.3    1780     536 Mar20    00:00 /sbin/getty 38400 tty5
0        4215  0.0  0.3    1780     540 Mar20    00:00 /sbin/getty 38400 tty2
0        4216  0.0  0.3    1780     540 Mar20    00:00 /sbin/getty 38400 tty3
0        4217  0.0  0.3    1780     540 Mar20    00:00 /sbin/getty 38400 tty6
101      4258  0.0  0.4    2012     712 Mar20    00:01 /sbin/syslogd -u syslog
0        4276  0.0  0.4    1936     544 Mar20    00:00 /bin/dd bs 1 if /proc/kmsg of /var/run/klogd/kmsg
102      4278  0.0  0.7    3508    2248 Mar20    00:00 /sbin/klogd -P /var/run/klogd/kmsg
104      4296  0.0  0.5    2640     844 Mar20    00:02 /bin/dbus-daemon --system
0        4315  0.0  1.0    5392    1060 Mar20    00:00 /usr/sbin/sshd
0        4349  0.0  1.8    9216    1464 Mar20    00:01 /usr/sbin/winbindd
0        4363  0.0  1.8    9472    2000 Mar20    00:00 /usr/sbin/winbindd
0        4364  0.0  1.8    9216    1012 Mar20    00:00 /usr/sbin/winbindd
0        4365  0.0  0.4    2064     452 Mar20    00:00 /usr/sbin/atd
0        4390  0.0  0.7    3408    1016 Mar20    00:00 /usr/sbin/cron
0        4400  0.0  1.6    8128    3212 Mar20    00:00 /bin/login --
0        4402  0.0  1.8    9216    1108 Mar20    00:00 /usr/sbin/winbindd
1000     5213  0.0  1.1    5656    3032 Mar20    00:00 -bash
0        5232  0.0  1.6    8144    2316 Mar20    00:00 su -
0        5298  0.0  0.8    4220    1832 Mar20    00:00 -su
0        5342  0.0  0.4    2252     644 Mar20    00:00 dhclient
0       13234  0.0  1.6    8120    1580 00:38    00:02 /usr/sbin/nmbd -D
0       13236  0.0  2.6   13604    2816 00:38    00:00 /usr/sbin/smbd -D
0       13239  0.0  2.6   13604    1044 00:38    00:00 /usr/sbin/smbd -D
0       23643  0.0  2.4   12376    4280 14:43    00:00 sshd: user [priv]
1000    23648  0.0  2.4   12512    2008 14:43    00:06 sshd: user@pts/2
1000    23649  0.0  1.2    5944    3340 14:43    00:00 -bash
0       25860  0.0  1.6    8144    2300 17:55    00:00 su -
0       25926  0.0  1.1    5432    3072 17:55    00:00 -su
1000    28464  0.0  2.7   13968    4076 21:45    00:00 /usr/sbin/smbd -D
0       28655  0.0  1.0    4932    3204 21:58    00:00 python _psutil.py
>>>
-

Mailing lists

-

Users
-
http://groups.google.com/group/psutil/topics

-

Developers
- http://groups.google.com/group/psutil-dev/topics

-

SVN commits and issue tracker changes
- http://groups.google.com/group/psutil-commits/topics

-

 

-
- - + + + + + + psutil + + + + + + +
+ + + +
+ +
+ + +
+

Summary

psutil is a module providing an interface for retrieving information on running processes and system utilization (CPU, memory) in a portable way by using Python, implementing many functionalities offered by command line tools like ps, top, kill and Windows task manager.

It currently supports Linux, OS X, FreeBSD and Windows with Python versions from 2.4 to 3.1 by using a unique code base.

Example usage

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.path
+'/usr/bin'
+>>> p.cmdline
+['/usr/bin/python', '-O', 'run.py']
+>>> p.uid
+1000
+>>> p.gid
+1000
+>>> p.username
+'jake'
+>>> p.create_time
+1267551141.5019531
+>>> p.get_cpu_percent()
+12.31243
+>>> p.get_memory_percent()
+0.63423
+>>> rss, vms =  p.get_memory_info()
+>>> "Resident memory: %s KB" %(rss / 1024)
+'Resident memory: 3768 KB'
+>>> "Virtual memory: %s KB" %(vms / 1024)
+'Virtual memory: 6176 KB'
+>>>
+>>> p.suspend()
+>>> p.resume()
+>>> psutil.test()
+UID       PID %CPU %MEM     VSZ     RSS START     TIME COMMAND
+0           0  0.0  0.0       0       0 00:12    00:00 [sched]
+0           1  0.0  0.3    1740     600 00:12    00:04 /sbin/init
+0           2  0.0  0.0       0       0 00:12    00:00 [kthreadd]
+0           3  0.0  0.0       0       0 00:12    00:00 [migration/0]
+...
+0       13239  0.0  2.6    13604   1044 00:38    00:00 /usr/sbin/smbd -D
+1000    23648  0.0  2.4    12512   2008 14:43    00:06 sshd: user@pts/2
+1000    23649  0.0  1.2    5944    3340 14:43    00:00 -bash
+0       25926  0.0  1.1    5432    3072 17:55    00:00 -su
+0       28655  0.0  1.0    4932    3204 21:58    00:00 python _psutil.py
+>>>

System monitoring (CPU and memory)

>>> import psutil, time
+>>> print psutil.cpu_times()
+softirq=50.87; iowait=39.63; system=1130.67; idle=164171.41; user=965.15; irq=7.08; nice=0.0
+>>>
+>>> while 1:
+...     print round(psutil.cpu_percent(), 1)
+...     time.sleep(1)
+...
+5.4
+3.2
+7.3
+7.1
+2.5
+Traceback (most recent call last):
+  File "<stdin>", line 3, in <module>
+KeyboardInterrupt
+>>>
+>>> psutil.TOTAL_PHYMEM
+526458880
+>>> psutil.avail_phymem()
+153530368
+>>> psutil.total_virtmem()
+197365760
+>>> psutil.avail_virtmem()
+194277376

Mailing lists

Users
http://groups.google.com/group/psutil/topics

Developers
http://groups.google.com/group/psutil-dev/topics

SVN commits and issue tracker changes
http://groups.google.com/group/psutil-commits/topics

Contribute

If you want to help or just give us suggestions about the project and other related things, subscribe to the discussion mailing list. If you want to talk with project team members about psutil and other related things feel free to contact us at the following addresses:

Name Country E-mail
Jay Loden New Jersey (USA) jloden at gmail dot com
Giampaolo Rodola' Turin (Italy) g.rodola at gmail dot com

Feedbacks and suggestions are greatly appreciated as well as new testers and coders willing to join the development.
For any bug report, patch proposal or feature request, add an entry into the Issue Tracker.

Thank you.

+ + + + + diff -Nru python-psutil-0.1.3/docs/milestones.lnk.html python-psutil-0.2.1/docs/milestones.lnk.html --- python-psutil-0.1.3/docs/milestones.lnk.html 2009-09-13 03:42:25.000000000 +0000 +++ python-psutil-0.2.1/docs/milestones.lnk.html 2010-11-13 01:37:45.000000000 +0000 @@ -1,12 +1,12 @@ - - - - Redirecting to Milestones... - - - -

Redirecting to: http://code.google.com/p/psutil/wiki/Milestones

-

You'll be redirected in 2 seconds...

- - - + + + + Redirecting to Milestones... + + + +

Redirecting to: http://code.google.com/p/psutil/wiki/Milestones

+

You'll be redirected in 2 seconds...

+ + + diff -Nru python-psutil-0.1.3/HISTORY python-psutil-0.2.1/HISTORY --- python-psutil-0.1.3/HISTORY 2009-09-14 22:50:56.000000000 +0000 +++ python-psutil-0.2.1/HISTORY 2011-03-20 21:13:40.000000000 +0000 @@ -1,82 +1,193 @@ -Bug tracker at http://code.google.com/p/psutil/issues - -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. +Bug tracker at http://code.google.com/p/psutil/issues + +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. + diff -Nru python-psutil-0.1.3/LICENSE python-psutil-0.2.1/LICENSE --- python-psutil-0.1.3/LICENSE 2009-09-13 03:42:25.000000000 +0000 +++ python-psutil-0.2.1/LICENSE 2010-10-30 17:32:03.000000000 +0000 @@ -1,27 +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. \ No newline at end of file +psutil is distributed under BSD license reproduced below. + +Copyright (c) 2009, Jay Loden, Dave Daeschler, Giampaolo Rodola' +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of the psutil authors nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -Nru python-psutil-0.1.3/MANIFEST.in python-psutil-0.2.1/MANIFEST.in --- python-psutil-0.1.3/MANIFEST.in 2009-09-13 03:42:25.000000000 +0000 +++ python-psutil-0.2.1/MANIFEST.in 2010-11-13 01:38:56.000000000 +0000 @@ -1,5 +1,9 @@ -include HISTORY LICENSE MANIFEST MANIFEST.in -recursive-include docs * -recursive-include psutil * -recursive-include test * -global-exclude *.pyc *.pyo *.exe +include CREDITS +include HISTORY +include LICENSE +include MANIFEST.in +include README +include setup.py +recursive-include docs *.html *.dia *.png +recursive-include psutil *.py *.c *.h +recursive-include test *.py diff -Nru python-psutil-0.1.3/PKG-INFO python-psutil-0.2.1/PKG-INFO --- python-psutil-0.1.3/PKG-INFO 2009-09-15 00:42:33.000000000 +0000 +++ python-psutil-0.2.1/PKG-INFO 2011-03-20 21:14:26.000000000 +0000 @@ -1,21 +1,23 @@ Metadata-Version: 1.0 Name: psutil -Version: 0.1.3 +Version: 0.2.1 Summary: A process utilities module for Python Home-page: http://code.google.com/p/psutil/ Author: Giampaolo Rodola, Dave Daeschler, Jay Loden Author-email: psutil-dev@googlegroups.com License: License :: OSI Approved :: BSD License +Download-URL: http://psutil.googlecode.com/files/psutil-0.2.1.tar.gz Description: psutil is a module providing convenience functions for managing processes in a portable way by using Python. -Keywords: psutil,ps,top,process,utility +Keywords: psutil,ps,top,process,utility,lsof,top,netstat,taskmgr Platform: Platform Independent -Classifier: Development Status :: 2 - Pre-Alpha +Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console -Classifier: Operating System :: MacOS -Classifier: Operating System :: Microsoft :: Windows +Classifier: Operating System :: MacOS :: MacOS X +Classifier: Operating System :: Microsoft :: Windows :: Windows NT/2000 Classifier: Operating System :: POSIX :: Linux +Classifier: Operating System :: POSIX :: BSD :: FreeBSD Classifier: Operating System :: OS Independent Classifier: Programming Language :: C Classifier: Programming Language :: Python @@ -23,10 +25,14 @@ Classifier: Programming Language :: Python :: 2.4 Classifier: Programming Language :: Python :: 2.5 Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.0 Classifier: Programming Language :: Python :: 3.1 +Classifier: Programming Language :: Python :: 3.2 Classifier: Topic :: System :: Monitoring +Classifier: Topic :: System :: Networking +Classifier: Topic :: System :: Benchmark Classifier: Topic :: System :: Systems Administration Classifier: Topic :: Utilities Classifier: Topic :: Software Development :: Libraries :: Python Modules diff -Nru python-psutil-0.1.3/psutil/arch/bsd/process_info.c python-psutil-0.2.1/psutil/arch/bsd/process_info.c --- python-psutil-0.1.3/psutil/arch/bsd/process_info.c 2009-09-14 21:45:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/arch/bsd/process_info.c 2011-01-12 18:47:32.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $Id: process_info.c 523 2010-02-27 17:15:23Z jloden $ + * $Id: process_info.c 828 2010-11-20 20:52:07Z g.rodola $ * * Helper functions related to fetching process information. Used by _psutil_bsd * module methods. @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -27,7 +28,8 @@ * On success, the function returns 0. * On error, the function returns a BSD errno value. */ -int get_proc_list(struct kinfo_proc **procList, size_t *procCount) +int +get_proc_list(struct kinfo_proc **procList, size_t *procCount) { int err; struct kinfo_proc * result; @@ -60,34 +62,30 @@ assert(result == NULL); // Call sysctl with a NULL buffer. length = 0; - err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1, - NULL, &length, - NULL, 0); - if (err == -1) { + err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, + NULL, &length, NULL, 0); + if (err == -1) err = errno; - } // Allocate an appropriately sized buffer based on the results // from the previous call. if (err == 0) { result = malloc(length); - if (result == NULL) { + if (result == NULL) err = ENOMEM; - } } // Call sysctl again with the new buffer. If we get an ENOMEM // error, toss away our buffer and start again. if (err == 0) { - err = sysctl( (int *) name, (sizeof(name) / sizeof(*name)) - 1, - result, &length, - NULL, 0); - if (err == -1) { + err = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1, + result, &length, NULL, 0); + if (err == -1) err = errno; - } if (err == 0) { done = 1; - } else if (err == ENOMEM) { + } + else if (err == ENOMEM) { assert(result != NULL); free(result); result = NULL; @@ -105,13 +103,13 @@ *procList = result; *procCount = length / sizeof(struct kinfo_proc); - assert( (err == 0) == (*procList != NULL) ); - + assert((err == 0) == (*procList != NULL)); return err; } -char *getcmdpath(long pid, size_t *pathsize) +char +*getcmdpath(long pid, size_t *pathsize) { int mib[4]; char *path; @@ -127,19 +125,15 @@ // call with a null buffer first to determine if we need a buffer if (sysctl(mib, 4, NULL, &size, NULL, 0) == -1) { - //perror("sysctl"); return NULL; } path = malloc(size); - if (path == NULL) { - //perror("sysctl/malloc"); + if (path == NULL) return NULL; - } *pathsize = size; if (sysctl(mib, 4, path, &size, NULL, 0) == -1) { - //perror("sysctl"); free(path); return NULL; /* Insufficient privileges */ } @@ -161,7 +155,8 @@ * -1 for failure (Exception raised); * 1 for insufficient privileges. */ -char *getcmdargs(long pid, size_t *argsize) +char +*getcmdargs(long pid, size_t *argsize) { int mib[4]; size_t size, argmax; @@ -172,17 +167,13 @@ mib[1] = KERN_ARGMAX; size = sizeof(argmax); - if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) { - //perror("sysctl"); + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) return NULL; - } /* Allocate space for the arguments. */ procargs = (char *)malloc(argmax); - if (procargs == NULL) { - //printf("getcmdargs(): Cannot allocate memory for procargs\n"); + if (procargs == NULL) return NULL; - } /* * Make a sysctl() call to get the raw argument space of the process. @@ -194,8 +185,6 @@ size = argmax; if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) { - //printf("sysctl(%lu): %s\n", pid, strerror(errno)); - //perror("sysctl (raw args) for PID "); free(procargs); return NULL; /* Insufficient privileges */ } @@ -207,7 +196,8 @@ /* returns the command line as a python list object */ -PyObject* get_arg_list(long pid) +PyObject* +get_arg_list(long pid) { char *argstr = NULL; int pos = 0; @@ -219,7 +209,7 @@ return retlist; } - // this leaks memory (grrr) + // XXX - this leaks memory (grrr) argstr = getcmdargs(pid, &argsize); if (NULL == argstr) { @@ -251,3 +241,26 @@ free(argstr); return retlist; } + + +/* + * Return 1 if PID exists in the current process list, else 0. + */ +int +pid_exists(long pid) +{ + int kill_ret; + if (pid < 0) { + return 0; + } + + // if kill returns success of permission denied we know it's a valid PID + kill_ret = kill(pid , 0); + if ((0 == kill_ret) || (EPERM == errno)) { + return 1; + } + + // otherwise return 0 for PID not found + return 0; +} + diff -Nru python-psutil-0.1.3/psutil/arch/bsd/process_info.h python-psutil-0.2.1/psutil/arch/bsd/process_info.h --- python-psutil-0.1.3/psutil/arch/bsd/process_info.h 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/arch/bsd/process_info.h 2011-01-12 18:47:32.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $Id: process_info.h 213 2009-03-05 15:48:22Z jloden $ + * $Id: process_info.h 828 2010-11-20 20:52:07Z g.rodola $ * * Helper functions related to fetching process information. Used by _psutil_bsd * module methods. @@ -13,4 +13,5 @@ char *getcmdargs(long pid, size_t *argsize); char *getcmdpath(long pid, size_t *pathsize); PyObject* get_arg_list(long pid); +int pid_exists(long pid); diff -Nru python-psutil-0.1.3/psutil/arch/mswindows/ntextapi.h python-psutil-0.2.1/psutil/arch/mswindows/ntextapi.h --- python-psutil-0.1.3/psutil/arch/mswindows/ntextapi.h 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/arch/mswindows/ntextapi.h 2011-02-22 04:00:04.000000000 +0000 @@ -0,0 +1,178 @@ +/* + * $Id$ + */ + +typedef enum _KTHREAD_STATE +{ + Initialized, + Ready, + Running, + Standby, + Terminated, + Waiting, + Transition, + DeferredReady, + GateWait, + MaximumThreadState +} KTHREAD_STATE, *PKTHREAD_STATE; + +typedef enum _KWAIT_REASON +{ + Executive = 0, + FreePage = 1, + PageIn = 2, + PoolAllocation = 3, + DelayExecution = 4, + Suspended = 5, + UserRequest = 6, + WrExecutive = 7, + WrFreePage = 8, + WrPageIn = 9, + WrPoolAllocation = 10, + WrDelayExecution = 11, + WrSuspended = 12, + WrUserRequest = 13, + WrEventPair = 14, + WrQueue = 15, + WrLpcReceive = 16, + WrLpcReply = 17, + WrVirtualMemory = 18, + WrPageOut = 19, + WrRendezvous = 20, + Spare2 = 21, + Spare3 = 22, + Spare4 = 23, + Spare5 = 24, + WrCalloutStack = 25, + WrKernel = 26, + WrResource = 27, + WrPushLock = 28, + WrMutex = 29, + WrQuantumEnd = 30, + WrDispatchInt = 31, + WrPreempted = 32, + WrYieldExecution = 33, + WrFastMutex = 34, + WrGuardedMutex = 35, + WrRundown = 36, + MaximumWaitReason = 37 +} KWAIT_REASON, *PKWAIT_REASON; + + +typedef struct _CLIENT_ID +{ + HANDLE UniqueProcess; + HANDLE UniqueThread; +} CLIENT_ID, *PCLIENT_ID; + + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + + +typedef struct _SYSTEM_TIMEOFDAY_INFORMATION +{ + LARGE_INTEGER BootTime; + LARGE_INTEGER CurrentTime; + LARGE_INTEGER TimeZoneBias; + ULONG TimeZoneId; + ULONG Reserved; + ULONGLONG BootTimeBias; + ULONGLONG SleepTimeBias; +} SYSTEM_TIMEOFDAY_INFORMATION, *PSYSTEM_TIMEOFDAY_INFORMATION; + +typedef struct _SYSTEM_THREAD_INFORMATION +{ + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER CreateTime; + ULONG WaitTime; + PVOID StartAddress; + CLIENT_ID ClientId; + LONG Priority; + LONG BasePriority; + ULONG ContextSwitches; + ULONG ThreadState; + KWAIT_REASON WaitReason; +} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; + +typedef struct _TEB *PTEB; + +// private +typedef struct _SYSTEM_EXTENDED_THREAD_INFORMATION +{ + SYSTEM_THREAD_INFORMATION ThreadInfo; + PVOID StackBase; + PVOID StackLimit; + PVOID Win32StartAddress; + PTEB TebBase; + ULONG_PTR Reserved2; + ULONG_PTR Reserved3; + ULONG_PTR Reserved4; +} SYSTEM_EXTENDED_THREAD_INFORMATION, *PSYSTEM_EXTENDED_THREAD_INFORMATION; + +typedef struct _SYSTEM_PROCESS_INFORMATION +{ + ULONG NextEntryOffset; + ULONG NumberOfThreads; + LARGE_INTEGER SpareLi1; + LARGE_INTEGER SpareLi2; + LARGE_INTEGER SpareLi3; + LARGE_INTEGER CreateTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER KernelTime; + UNICODE_STRING ImageName; + LONG BasePriority; + HANDLE UniqueProcessId; + HANDLE InheritedFromUniqueProcessId; + ULONG HandleCount; + ULONG SessionId; + ULONG_PTR PageDirectoryBase; + SIZE_T PeakVirtualSize; + SIZE_T VirtualSize; + ULONG PageFaultCount; + SIZE_T PeakWorkingSetSize; + SIZE_T WorkingSetSize; + SIZE_T QuotaPeakPagedPoolUsage; + SIZE_T QuotaPagedPoolUsage; + SIZE_T QuotaPeakNonPagedPoolUsage; + SIZE_T QuotaNonPagedPoolUsage; + SIZE_T PagefileUsage; + SIZE_T PeakPagefileUsage; + SIZE_T PrivatePageCount; + LARGE_INTEGER ReadOperationCount; + LARGE_INTEGER WriteOperationCount; + LARGE_INTEGER OtherOperationCount; + LARGE_INTEGER ReadTransferCount; + LARGE_INTEGER WriteTransferCount; + LARGE_INTEGER OtherTransferCount; + SYSTEM_THREAD_INFORMATION Threads[1]; +} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION; + + +// structures and enums from winternl.h (not available under mingw) +typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { + LARGE_INTEGER IdleTime; + LARGE_INTEGER KernelTime; + LARGE_INTEGER UserTime; + LARGE_INTEGER Reserved1[2]; + ULONG Reserved2; +} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; + + +typedef enum _SYSTEM_INFORMATION_CLASS { + SystemBasicInformation = 0, + SystemPerformanceInformation = 2, + SystemTimeOfDayInformation = 3, + SystemProcessInformation = 5, + SystemProcessorPerformanceInformation = 8, + SystemInterruptInformation = 23, + SystemExceptionInformation = 33, + SystemRegistryQuotaInformation = 37, + SystemLookasideInformation = 45 +} SYSTEM_INFORMATION_CLASS; + + diff -Nru python-psutil-0.1.3/psutil/arch/mswindows/process_handles.c python-psutil-0.2.1/psutil/arch/mswindows/process_handles.c --- python-psutil-0.1.3/psutil/arch/mswindows/process_handles.c 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/arch/mswindows/process_handles.c 2011-01-12 18:47:32.000000000 +0000 @@ -0,0 +1,295 @@ +#ifndef UNICODE +#define UNICODE +#endif + +#include +#include +#include +#include "process_handles.h" + +#ifndef NT_SUCCESS + #define NT_SUCCESS(x) ((x) >= 0) +#endif +#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004 + +#define SystemHandleInformation 16 +#define ObjectBasicInformation 0 +#define ObjectNameInformation 1 +#define ObjectTypeInformation 2 + + +typedef LONG NTSTATUS; + +typedef struct _UNICODE_STRING { + USHORT Length; + USHORT MaximumLength; + PWSTR Buffer; +} UNICODE_STRING, *PUNICODE_STRING; + +typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)( + ULONG SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength + ); + +typedef NTSTATUS (NTAPI *_NtDuplicateObject)( + HANDLE SourceProcessHandle, + HANDLE SourceHandle, + HANDLE TargetProcessHandle, + PHANDLE TargetHandle, + ACCESS_MASK DesiredAccess, + ULONG Attributes, + ULONG Options + ); + +typedef NTSTATUS (NTAPI *_NtQueryObject)( + HANDLE ObjectHandle, + ULONG ObjectInformationClass, + PVOID ObjectInformation, + ULONG ObjectInformationLength, + PULONG ReturnLength + ); + +typedef struct _SYSTEM_HANDLE +{ + ULONG ProcessId; + BYTE ObjectTypeNumber; + BYTE Flags; + USHORT Handle; + PVOID Object; + ACCESS_MASK GrantedAccess; +} SYSTEM_HANDLE, *PSYSTEM_HANDLE; + +typedef struct _SYSTEM_HANDLE_INFORMATION +{ + ULONG HandleCount; + SYSTEM_HANDLE Handles[1]; +} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION; + +typedef enum _POOL_TYPE +{ + NonPagedPool, + PagedPool, + NonPagedPoolMustSucceed, + DontUseThisType, + NonPagedPoolCacheAligned, + PagedPoolCacheAligned, + NonPagedPoolCacheAlignedMustS +} POOL_TYPE, *PPOOL_TYPE; + +typedef struct _OBJECT_TYPE_INFORMATION +{ + UNICODE_STRING Name; + ULONG TotalNumberOfObjects; + ULONG TotalNumberOfHandles; + ULONG TotalPagedPoolUsage; + ULONG TotalNonPagedPoolUsage; + ULONG TotalNamePoolUsage; + ULONG TotalHandleTableUsage; + ULONG HighWaterNumberOfObjects; + ULONG HighWaterNumberOfHandles; + ULONG HighWaterPagedPoolUsage; + ULONG HighWaterNonPagedPoolUsage; + ULONG HighWaterNamePoolUsage; + ULONG HighWaterHandleTableUsage; + ULONG InvalidAttributes; + GENERIC_MAPPING GenericMapping; + ULONG ValidAccess; + BOOLEAN SecurityRequired; + BOOLEAN MaintainHandleCount; + USHORT MaintainTypeList; + POOL_TYPE PoolType; + ULONG PagedPoolUsage; + ULONG NonPagedPoolUsage; +} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION; + +PVOID GetLibraryProcAddress(PSTR LibraryName, PSTR ProcName) +{ + return GetProcAddress(GetModuleHandleA(LibraryName), ProcName); +} + + +PyObject* +get_open_files(long pid, HANDLE processHandle) +{ + _NtQuerySystemInformation NtQuerySystemInformation = + GetLibraryProcAddress("ntdll.dll", "NtQuerySystemInformation"); + _NtDuplicateObject NtDuplicateObject = + GetLibraryProcAddress("ntdll.dll", "NtDuplicateObject"); + _NtQueryObject NtQueryObject = + GetLibraryProcAddress("ntdll.dll", "NtQueryObject"); + + NTSTATUS status; + PSYSTEM_HANDLE_INFORMATION handleInfo; + ULONG handleInfoSize = 0x10000; + + ULONG i; + ULONG fileNameLength; + PyObject *filesList = Py_BuildValue("[]"); + PyObject *arg = NULL; + PyObject *fileFromWchar = NULL; + + + + handleInfo = (PSYSTEM_HANDLE_INFORMATION)malloc(handleInfoSize); + + /* NtQuerySystemInformation won't give us the correct buffer size, + so we guess by doubling the buffer size. */ + while ((status = NtQuerySystemInformation( + SystemHandleInformation, + handleInfo, + handleInfoSize, + NULL + )) == STATUS_INFO_LENGTH_MISMATCH) + handleInfo = (PSYSTEM_HANDLE_INFORMATION)realloc(handleInfo, handleInfoSize *= 2); + + /* NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH. */ + if (!NT_SUCCESS(status)) { + //printf("NtQuerySystemInformation failed!\n"); + return NULL; + } + + for (i = 0; i < handleInfo->HandleCount; i++) + { + SYSTEM_HANDLE handle = handleInfo->Handles[i]; + HANDLE dupHandle = NULL; + POBJECT_TYPE_INFORMATION objectTypeInfo; + PVOID objectNameInfo; + UNICODE_STRING objectName; + ULONG returnLength; + + /* Check if this handle belongs to the PID the user specified. */ + if (handle.ProcessId != pid) + continue; + + /* Skip handles with the following access codes as the next call + to NtDuplicateObject() or NtQueryObject() might hang forever. */ + if((handle.GrantedAccess == 0x0012019f) + || (handle.GrantedAccess == 0x001a019f) + || (handle.GrantedAccess == 0x00120189) + || (handle.GrantedAccess == 0x00100000)) { + continue; + } + + /* Duplicate the handle so we can query it. */ + if (!NT_SUCCESS(NtDuplicateObject( + processHandle, + handle.Handle, + GetCurrentProcess(), + &dupHandle, + 0, + 0, + 0 + ))) + { + //printf("[%#x] Error!\n", handle.Handle); + continue; + } + + /* Query the object type. */ + objectTypeInfo = (POBJECT_TYPE_INFORMATION)malloc(0x1000); + if (!NT_SUCCESS(NtQueryObject( + dupHandle, + ObjectTypeInformation, + objectTypeInfo, + 0x1000, + NULL + ))) + { + //printf("[%#x] Error!\n", handle.Handle); + CloseHandle(dupHandle); + continue; + } + + objectNameInfo = malloc(0x1000); + if (!NT_SUCCESS(NtQueryObject( + dupHandle, + ObjectNameInformation, + objectNameInfo, + 0x1000, + &returnLength + ))) + { + /* Reallocate the buffer and try again. */ + objectNameInfo = realloc(objectNameInfo, returnLength); + if (!NT_SUCCESS(NtQueryObject( + dupHandle, + ObjectNameInformation, + objectNameInfo, + returnLength, + NULL + ))) + { + /* We have the type name, so just display that.*/ + /* + printf( + "[%#x] %.*S: (could not get name)\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer + ); + */ + free(objectTypeInfo); + free(objectNameInfo); + CloseHandle(dupHandle); + continue; + + } + } + + /* Cast our buffer into an UNICODE_STRING. */ + objectName = *(PUNICODE_STRING)objectNameInfo; + + /* Print the information! */ + if (objectName.Length) + { + /* The object has a name. Make sure it is a file otherwise + ignore it */ + fileNameLength = objectName.Length / 2; + if (wcscmp(objectTypeInfo->Name.Buffer, L"File") == 0) { + //printf("%.*S\n", objectName.Length / 2, objectName.Buffer); + fileFromWchar = PyUnicode_FromWideChar(objectName.Buffer, + fileNameLength); + #if PY_MAJOR_VERSION >= 3 + arg = Py_BuildValue("N", PyUnicode_AsUTF8String(fileFromWchar)); + #else + arg = Py_BuildValue("N", PyUnicode_FromObject(fileFromWchar)); + #endif + Py_XDECREF(fileFromWchar); + PyList_Append(filesList, arg); + Py_XDECREF(arg); + } + /* + printf( + "[%#x] %.*S: %.*S\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer, + objectName.Length / 2, + objectName.Buffer + ); + */ + } + else + { + /* Print something else. */ + /* + printf( + "[%#x] %.*S: (unnamed)\n", + handle.Handle, + objectTypeInfo->Name.Length / 2, + objectTypeInfo->Name.Buffer + ); + */ + ;; + } + free(objectTypeInfo); + free(objectNameInfo); + CloseHandle(dupHandle); + } + free(handleInfo); + CloseHandle(processHandle); + return filesList; +} + diff -Nru python-psutil-0.1.3/psutil/arch/mswindows/process_handles.h python-psutil-0.2.1/psutil/arch/mswindows/process_handles.h --- python-psutil-0.1.3/psutil/arch/mswindows/process_handles.h 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/arch/mswindows/process_handles.h 2011-01-12 18:47:32.000000000 +0000 @@ -0,0 +1,4 @@ +#include +#include + +PyObject* get_open_files(long pid, HANDLE processHandle); diff -Nru python-psutil-0.1.3/psutil/arch/mswindows/process_info.c python-psutil-0.2.1/psutil/arch/mswindows/process_info.c --- python-psutil-0.1.3/psutil/arch/mswindows/process_info.c 2009-09-14 21:45:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/arch/mswindows/process_info.c 2011-02-22 04:21:33.000000000 +0000 @@ -1,8 +1,8 @@ /* - * $Id: process_info.c 521 2010-02-27 16:19:10Z billiejoex $ + * $Id: process_info.c 931 2011-02-22 04:10:46Z g.rodola $ * - * Helper functions related to fetching process information. Used by _psutil_mswindows - * module methods. + * Helper functions related to fetching process information. Used by + * _psutil_mswindows module methods. */ #include @@ -12,13 +12,11 @@ #include "security.h" #include "process_info.h" - +#include "ntextapi.h" /* * NtQueryInformationProcess code taken from - * * http://wj32.wordpress.com/2009/01/24/howto-get-the-command-line-of-processes/ - * * typedefs needed to compile against ntdll functions not exposted in the API */ typedef LONG NTSTATUS; @@ -31,26 +29,66 @@ PDWORD ReturnLength ); -typedef struct _UNICODE_STRING -{ - USHORT Length; - USHORT MaximumLength; - PWSTR Buffer; -} UNICODE_STRING, *PUNICODE_STRING; - typedef struct _PROCESS_BASIC_INFORMATION { - DWORD ExitStatus; + PVOID Reserved1; PVOID PebBaseAddress; - DWORD AffinityMask; - DWORD BasePriority; - DWORD UniqueProcessId; - DWORD ParentProcessId; + PVOID Reserved2[2]; + ULONG_PTR UniqueProcessId; + PVOID Reserved3; } PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; +/* + * A wrapper around OpenProcess setting NSP exception if process + * no longer exists. + * "pid" is the process pid, "dwDesiredAccess" is the first argument + * exptected by OpenProcess. + * Return a process handle or NULL. + */ +HANDLE +handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess) +{ + HANDLE hProcess; + DWORD processExitCode = 0; + + hProcess = OpenProcess(dwDesiredAccess, FALSE, pid); + if (hProcess == NULL) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + NoSuchProcess(); + } + else { + PyErr_SetFromWindowsErr(0); + } + return NULL; + } + + /* make sure the process is running */ + GetExitCodeProcess(hProcess, &processExitCode); + if (processExitCode == 0) { + NoSuchProcess(); + CloseHandle(hProcess); + return NULL; + } + return hProcess; +} + + +/* + * Same as handle_from_pid_waccess but implicitly uses + * PROCESS_QUERY_INFORMATION | PROCESS_VM_READ as dwDesiredAccess + * parameter for OpenProcess. + */ +HANDLE +handle_from_pid(DWORD pid) { + DWORD dwDesiredAccess = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ; + return handle_from_pid_waccess(pid, dwDesiredAccess); +} + + // fetch the PEB base address from NtQueryInformationProcess() -PVOID GetPebAddress(HANDLE ProcessHandle) +PVOID +GetPebAddress(HANDLE ProcessHandle) { _NtQueryInformationProcess NtQueryInformationProcess = (_NtQueryInformationProcess)GetProcAddress( @@ -62,53 +100,50 @@ } -DWORD* get_pids(DWORD *numberOfReturnedPIDs){ - int procArraySz = 1024; - - /* Win32 SDK says the only way to know if our process array - * wasn't large enough is to check the returned size and make - * sure that it doesn't match the size of the array. - * If it does we allocate a larger array and try again*/ - - /* Stores the actual array */ - DWORD *procArray = NULL; - DWORD procArrayByteSz; +DWORD* +get_pids(DWORD *numberOfReturnedPIDs) { + int procArraySz = 1024; + + /* Win32 SDK says the only way to know if our process array + * wasn't large enough is to check the returned size and make + * sure that it doesn't match the size of the array. + * If it does we allocate a larger array and try again*/ + + /* Stores the actual array */ + DWORD *procArray = NULL; + DWORD procArrayByteSz; /* Stores the byte size of the returned array from enumprocesses */ - DWORD enumReturnSz = 0; - - do { - free(procArray); + DWORD enumReturnSz = 0; + do { + free(procArray); procArrayByteSz = procArraySz * sizeof(DWORD); - procArray = malloc(procArrayByteSz); - + procArray = malloc(procArrayByteSz); - if (! EnumProcesses(procArray, procArrayByteSz, &enumReturnSz)) - { - free(procArray); - /* Throw exception to python */ - PyErr_SetString(PyExc_RuntimeError, "EnumProcesses failed"); - return NULL; - } - else if (enumReturnSz == procArrayByteSz) - { - /* Process list was too large. Allocate more space*/ - procArraySz += 1024; - } + if (! EnumProcesses(procArray, procArrayByteSz, &enumReturnSz)) { + free(procArray); + PyErr_SetFromWindowsErr(0); + return NULL; + } + else if (enumReturnSz == procArrayByteSz) { + /* Process list was too large. Allocate more space*/ + procArraySz += 1024; + } - /* else we have a good list */ + /* else we have a good list */ - } while(enumReturnSz == procArraySz * sizeof(DWORD)); + } while(enumReturnSz == procArraySz * sizeof(DWORD)); - /* The number of elements is the returned size / size of each element */ + /* The number of elements is the returned size / size of each element */ *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD); return procArray; } -int is_system_proc(DWORD pid) { +int +is_system_proc(DWORD pid) { HANDLE hProcess; // Special case for PID 0 System Idle Process @@ -116,12 +151,11 @@ if ((pid == 0) || (pid == 4)) { return 1; } - if (pid < 0) { return 0; } - hProcess = handle_from_pid(pid); + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); if (NULL == hProcess) { // invalid parameter is no such process if (GetLastError() == ERROR_INVALID_PARAMETER) { @@ -141,7 +175,8 @@ } -int pid_is_running(DWORD pid) +int +pid_is_running(DWORD pid) { HANDLE hProcess; DWORD exitCode; @@ -155,37 +190,45 @@ return 0; } - hProcess = handle_from_pid(pid); + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, pid); if (NULL == hProcess) { // invalid parameter is no such process if (GetLastError() == ERROR_INVALID_PARAMETER) { + CloseHandle(hProcess); return 0; } // access denied obviously means there's a process to deny access to... if (GetLastError() == ERROR_ACCESS_DENIED) { + CloseHandle(hProcess); return 1; } + CloseHandle(hProcess); PyErr_SetFromWindowsErr(0); return -1; } if (GetExitCodeProcess(hProcess, &exitCode)) { + CloseHandle(hProcess); return (exitCode == STILL_ACTIVE); } // access denied means there's a process there so we'll assume it's running if (GetLastError() == ERROR_ACCESS_DENIED) { + CloseHandle(hProcess); return 1; } PyErr_SetFromWindowsErr(0); + CloseHandle(hProcess); return -1; } -int pid_in_proclist(DWORD pid) +int +pid_in_proclist(DWORD pid) { DWORD *proclist = NULL; DWORD numberOfReturnedPIDs; @@ -193,8 +236,6 @@ proclist = get_pids(&numberOfReturnedPIDs); if (NULL == proclist) { - PyErr_SetString(PyExc_RuntimeError, - "get_pids() failed for pid_in_proclist()"); return -1; } @@ -210,20 +251,6 @@ } -// Get a process handle from a pid with PROCESS_QUERY_INFORMATION rights -HANDLE handle_from_pid(DWORD pid) -{ - HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, - FALSE, pid); - /* - if (NULL == hProcess) { - hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); - } - */ - return hProcess; -} - - // Check exit code from a process handle. Return FALSE on an error also BOOL is_running(HANDLE hProcess) { @@ -242,57 +269,57 @@ // Return None to represent NoSuchProcess, else return NULL for // other exception or the name as a Python string -PyObject* get_name(long pid) +PyObject* +get_name(long pid) { - HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - PROCESSENTRY32 pe = { 0 }; - pe.dwSize = sizeof(PROCESSENTRY32); - - if( Process32First(h, &pe)) { - do { - if (pe.th32ProcessID == pid) { + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe = { 0 }; + pe.dwSize = sizeof(PROCESSENTRY32); + + if( Process32First(h, &pe)) { + do { + if (pe.th32ProcessID == pid) { CloseHandle(h); return Py_BuildValue("s", pe.szExeFile); - } - } while( Process32Next(h, &pe)); + } + } while(Process32Next(h, &pe)); - // the process was never found (NoSuchProcess raised later) + // the process was never found, set NoSuchProcess exception + NoSuchProcess(); CloseHandle(h); - return Py_BuildValue(""); - } + return NULL; + } CloseHandle(h); - PyErr_SetString(PyExc_RuntimeError, - "Failed to read process name from toolhelp snapshot"); - return NULL; + return PyErr_SetFromWindowsErr(0); } /* returns parent pid (as a Python int) for given pid or None on failure */ -PyObject* get_ppid(long pid) +PyObject* +get_ppid(long pid) { - HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - PROCESSENTRY32 pe = { 0 }; - pe.dwSize = sizeof(PROCESSENTRY32); - - if( Process32First(h, &pe)) { - do { - if (pe.th32ProcessID == pid) { - //printf("PID: %i; PPID: %i\n", pid, pe.th32ParentProcessID); + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe = { 0 }; + pe.dwSize = sizeof(PROCESSENTRY32); + + if( Process32First(h, &pe)) { + do { + if (pe.th32ProcessID == pid) { + //printf("PID: %i; PPID: %i\n", pid, pe.th32ParentProcessID); CloseHandle(h); - return Py_BuildValue("l", pe.th32ParentProcessID); - } - } while( Process32Next(h, &pe)); + return Py_BuildValue("I", pe.th32ParentProcessID); + } + } while(Process32Next(h, &pe)); - // the process was never found (NoSuchProcess raised later) + // the process was never found, set NoSuchProcess exception + NoSuchProcess(); CloseHandle(h); - return Py_BuildValue(""); - } + return NULL; + } CloseHandle(h); - PyErr_SetString(PyExc_RuntimeError, - "Failed to read process ppid from toolhelp snapshot"); - return NULL; + return PyErr_SetFromWindowsErr(0); } @@ -301,7 +328,8 @@ * returns a Python list representing the arguments for the process * with given pid or NULL on error. */ -PyObject* get_arg_list(long pid) +PyObject* +get_arg_list(long pid) { int nArgs, i; LPWSTR *szArglist; @@ -316,29 +344,40 @@ hProcess = handle_from_pid(pid); - if(NULL == hProcess) { - //printf("Could not open process!: %lu\n", GetLastError()); - return PyErr_SetFromWindowsErr(0); + if(hProcess == NULL) { + return NULL; } pebAddress = GetPebAddress(hProcess); /* get the address of ProcessParameters */ +#ifdef _WIN64 + if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32, + &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) +#else if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10, &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) +#endif { //printf("Could not read the address of ProcessParameters!\n"); + PyErr_SetFromWindowsErr(0); CloseHandle(hProcess); - return PyErr_SetFromWindowsErr(0); + return NULL; } /* read the CommandLine UNICODE_STRING structure */ +#ifdef _WIN64 + if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 112, + &commandLine, sizeof(commandLine), NULL)) +#else if (!ReadProcessMemory(hProcess, (PCHAR)rtlUserProcParamsAddress + 0x40, &commandLine, sizeof(commandLine), NULL)) +#endif { //printf("Could not read CommandLine!\n"); + PyErr_SetFromWindowsErr(0); CloseHandle(hProcess); - return PyErr_SetFromWindowsErr(0); + return NULL; } @@ -350,9 +389,10 @@ commandLineContents, commandLine.Length, NULL)) { //printf("Could not read the command line string!\n"); + PyErr_SetFromWindowsErr(0); CloseHandle(hProcess); free(commandLineContents); - return PyErr_SetFromWindowsErr(0); + return NULL; } /* print the commandline */ @@ -380,7 +420,7 @@ // arglist parsed as array of UNICODE_STRING, so convert each to Python // string object and add to arg list argList = Py_BuildValue("[]"); - for( i=0; iNextEntryOffset ? \ + (PSYSTEM_PROCESS_INFORMATION)((PCHAR)(Process) + \ + ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset) : \ + NULL \ + ) + +const STATUS_INFO_LENGTH_MISMATCH = 0xC0000004; +const STATUS_BUFFER_TOO_SMALL = 0xC0000023L; + +/* + * Given a process PID and a PSYSTEM_PROCESS_INFORMATION structure + * fills the structure with process information. + * On success return 1, else 0 with Python exception already set. + */ +int +get_process_info(DWORD pid, PSYSTEM_PROCESS_INFORMATION *retProcess) +{ + static ULONG initialBufferSize = 0x4000; + NTSTATUS status; + PVOID buffer; + ULONG bufferSize; + PSYSTEM_PROCESS_INFORMATION process; + + // get NtQuerySystemInformation + typedef DWORD (_stdcall *NTQSI_PROC) (int, PVOID, ULONG, PULONG); + NTQSI_PROC NtQuerySystemInformation; + HINSTANCE hNtDll; + hNtDll = LoadLibrary(TEXT("ntdll.dll")); + NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress( + hNtDll, "NtQuerySystemInformation"); + + bufferSize = initialBufferSize; + buffer = malloc(bufferSize); + + while (TRUE) { + status = NtQuerySystemInformation(SystemProcessInformation, buffer, + bufferSize, &bufferSize); + + if (status == STATUS_BUFFER_TOO_SMALL || status == STATUS_INFO_LENGTH_MISMATCH) + { + free(buffer); + buffer = malloc(bufferSize); + } + else { + break; + } + } + + if (status != 0) { + free(buffer); + PyErr_Format(PyExc_RuntimeError, "NtQuerySystemInformation() failed"); + return 0; + } + + + if (bufferSize <= 0x20000) + initialBufferSize = bufferSize; + + process = PH_FIRST_PROCESS(buffer); + do { + if (process->UniqueProcessId == (HANDLE)pid) { + free(buffer); + *retProcess = process; + return 1; + } + } while (process = PH_NEXT_PROCESS(process)); + + free(buffer); + NoSuchProcess(); + return 0; +} + + diff -Nru python-psutil-0.1.3/psutil/arch/mswindows/process_info.h python-psutil-0.2.1/psutil/arch/mswindows/process_info.h --- python-psutil-0.1.3/psutil/arch/mswindows/process_info.h 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/arch/mswindows/process_info.h 2011-02-22 04:00:04.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $Id: process_info.h 435 2009-09-19 09:32:29Z jloden $ + * $Id: process_info.h 930 2011-02-22 03:45:38Z g.rodola $ * * Helper functions related to fetching process information. Used by _psutil_mswindows * module methods. @@ -8,7 +8,10 @@ #include #include - +PyObject * NoSuchProcess(void); +PyObject * AccessDenied(void); +HANDLE handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess); +HANDLE handle_from_pid(DWORD pid); PVOID GetPebAddress(HANDLE ProcessHandle); HANDLE handle_from_pid(DWORD pid); BOOL is_running(HANDLE hProcess); @@ -19,3 +22,4 @@ PyObject* get_ppid(long pid); PyObject* get_name(long pid); DWORD* get_pids(DWORD *numberOfReturnedPIDs); + diff -Nru python-psutil-0.1.3/psutil/arch/mswindows/security.c python-psutil-0.2.1/psutil/arch/mswindows/security.c --- python-psutil-0.1.3/psutil/arch/mswindows/security.c 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/arch/mswindows/security.c 2011-01-12 18:47:32.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $Id: security.c 435 2009-09-19 09:32:29Z jloden $ + * $Id: security.c 772 2010-11-03 13:51:11Z g.rodola $ * * Security related functions for Windows platform (Set privileges such as * SeDebug), as well as security helper functions. @@ -11,7 +11,8 @@ /* * Convert a process handle to a process token handle. */ -HANDLE token_from_handle(HANDLE hProcess) { +HANDLE +token_from_handle(HANDLE hProcess) { HANDLE hToken = NULL; if (! OpenProcessToken(hProcess, TOKEN_QUERY, &hToken) ) { diff -Nru python-psutil-0.1.3/psutil/arch/osx/process_info.c python-psutil-0.2.1/psutil/arch/osx/process_info.c --- python-psutil-0.1.3/psutil/arch/osx/process_info.c 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/arch/osx/process_info.c 2011-01-12 18:47:32.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $Id: process_info.c 399 2009-05-12 23:24:21Z jloden $ + * $Id: process_info.c 843 2010-11-27 01:26:41Z jloden $ * * Helper functions related to fetching process information. Used by _psutil_osx * module methods. @@ -12,11 +12,35 @@ #include #include #include +#include #include #include "process_info.h" +#include "../../_psutil_common.h" -#define ARGS_ACCESS_DENIED -2 + +/* + * Return 1 if PID exists in the current process list, else 0. + */ +int +pid_exists(long pid) +{ + int kill_ret; + + // save some time if it's an invalid PID + if (pid < 0) { + return 0; + } + + // if kill returns success of permission denied we know it's a valid PID + kill_ret = kill(pid , 0); + if ( (0 == kill_ret) || (EPERM == errno) ) { + return 1; + } + + // otherwise return 0 for PID not found + return 0; +} @@ -28,7 +52,8 @@ * On success, the function returns 0. * On error, the function returns a BSD errno value. */ -int get_proc_list(kinfo_proc **procList, size_t *procCount) +int +get_proc_list(kinfo_proc **procList, size_t *procCount) { /* Declaring mib as const requires use of a cast since the * sysctl prototype doesn't include the const modifier. */ @@ -68,7 +93,8 @@ } else { size = size2; } - } else { + } + else { ptr = malloc(size); } if (ptr == NULL) { @@ -92,174 +118,133 @@ } -/* - * Modified from psi Python System Information project - * - * Get command path, arguments and environment variables. - * - * Based on code from ps. - * - * Returns: - * 0 for success - * 1 for sysctl error, errno exception raised - * -1 for failure, system or memory exception raised - * -2 rather ARGS_ACCESS_DENIED, for insufficient privileges - */ -int getcmdargs(long pid, PyObject **exec_path, PyObject **envlist, PyObject **arglist) +/* Read the maximum argument size for processes */ +size_t +get_argmax() { - int nargs, mib[3]; - size_t size, argmax; - char *curr_arg, *start_args, *iter_args, *end_args; - char *procargs = NULL; - char *err = NULL; - PyObject *arg; - - *arglist = Py_BuildValue("[]"); /* empty list */ - if (*arglist == NULL) { - err = "getcmdargs(): arglist exception"; - goto ERROR_RETURN; + size_t argmax; + int mib[] = { CTL_KERN, KERN_ARGMAX }; + size_t size = sizeof(argmax); + + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == 0) { + return argmax; } + return 0; +} - /* Get the maximum process arguments size. */ - mib[0] = CTL_KERN; - mib[1] = KERN_ARGMAX; - size = sizeof(argmax); - if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) { - PyErr_SetFromErrno(NULL); - return errno; +/* return process args as a python list */ +PyObject* +get_arg_list(long pid) +{ + int mib[3]; + int nargs; + int len; + char *procargs; + char *arg_ptr; + char *arg_end; + char *curr_arg; + size_t argmax; + PyObject *arg = NULL; + PyObject *arglist = NULL; + + //special case for PID 0 (kernel_task) where cmdline cannot be fetched + if (pid == 0) { + return Py_BuildValue("[]"); } - /* Allocate space for the arguments. */ + /* read argmax and allocate memory for argument space. */ + argmax = get_argmax(); + if (! argmax) { return PyErr_SetFromErrno(PyExc_OSError); } + procargs = (char *)malloc(argmax); - if (procargs == NULL) { - PyErr_SetString(PyExc_MemoryError, - "getcmdargs(): insufficient memory for procargs"); - return ENOMEM; + if (NULL == procargs) { + return PyErr_SetFromErrno(PyExc_OSError); } - /* - * Make a sysctl() call to get the raw argument space of the process. - */ + /* read argument space */ mib[0] = CTL_KERN; mib[1] = KERN_PROCARGS2; - mib[2] = (int)pid; - - size = argmax; - if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) { - if (EINVAL == errno) { // invalid == access denied for some reason - free(procargs); - return ARGS_ACCESS_DENIED; /* Insufficient privileges */ + mib[2] = pid; + if (sysctl(mib, 3, procargs, &argmax, NULL, 0) < 0) { + if (EINVAL == errno) { // invalid == access denied OR nonexistent PID + if ( pid_exists(pid) ) { + AccessDenied(); + } else { + NoSuchProcess(); + } } - - PyErr_SetFromErrno(PyExc_OSError); free(procargs); - return errno; + return NULL; } - // copy the number of argument to nargs + arg_end = &procargs[argmax]; + /* copy the number of arguments to nargs */ memcpy(&nargs, procargs, sizeof(nargs)); - iter_args = procargs + sizeof(nargs); - end_args = &procargs[size]; // end of the argument space - if (iter_args >= end_args) { - err = "getcmdargs(): argument length mismatch"; - goto ERROR_RETURN; + + arg_ptr = procargs + sizeof(nargs); + len = strlen(arg_ptr); + arg_ptr += len + 1; + + if (arg_ptr == arg_end) { + free(procargs); + return Py_BuildValue("[]"); } - // Save the path - if (NULL != exec_path) { - *exec_path = Py_BuildValue("s", iter_args); - if (*exec_path == NULL) { - err = "getcmdargs(): exec_path exception"; - goto ERROR_RETURN; + // skip ahead to the first argument + for (; arg_ptr < arg_end; arg_ptr++) { + if (*arg_ptr != '\0') { + break; } } - //TODO: save the environment variables to envlist as well - // Skip over the exec_path and '\0' characters. - while (iter_args < end_args && *iter_args != '\0') { iter_args++; } - while (iter_args < end_args && *iter_args == '\0') { iter_args++; } - - /* Iterate through the '\0'-terminated strings and add each string - * to the Python List arglist as a Python string. - * Stop when nargs strings have been extracted. That should be all - * the arguments. The rest of the strings will be environment - * strings for the command. - */ - curr_arg = iter_args; - start_args = iter_args; //reset start position to beginning of cmdline - while (iter_args < end_args && nargs > 0) { - if (*iter_args++ == '\0') { - /* Fetch next argument */ + /* iterate through arguments */ + curr_arg = arg_ptr; + arglist = Py_BuildValue("[]"); + while (arg_ptr < arg_end && nargs > 0) { + if (*arg_ptr++ == '\0') { arg = Py_BuildValue("s", curr_arg); - if (arg == NULL) { - err = "getcmdargs(): exception building argument string"; - goto ERROR_RETURN; + if (NULL == arg) { + return NULL; } - PyList_Append(*arglist, arg); + PyList_Append(arglist, arg); Py_DECREF(arg); - - curr_arg = iter_args; + // iterate to next arg and decrement # of args + curr_arg = arg_ptr; nargs--; } } - /* - * curr_arg position should be further than the start of the argspace - * and number of arguments should be 0 after iterating above. Otherwise - * we had an empty argument space or a missing terminating \0 etc. - */ - if (curr_arg == start_args || nargs > 0) { - err = "getcmdargs(): argument parsing failed"; - goto ERROR_RETURN; - } - -ERROR_RETURN: - // Clean up. - if (NULL != procargs) { - free(procargs); - } - if (NULL != err) { - PyErr_SetString(PyExc_SystemError, err); - return -1; - } - return 0; + free(procargs); + return arglist; } -/* return process args as a python list */ -PyObject* get_arg_list(long pid) +int +get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) { - int r; - PyObject *argList; - PyObject *env = NULL; - PyObject *args = NULL; - PyObject *exec_path = NULL; - - //special case for PID 0 (kernel_task) where cmdline cannot be fetched - if (pid == 0) { - return Py_BuildValue("[]"); + int mib[4]; + size_t len; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + + // fetch the info with sysctl() + len = sizeof(struct kinfo_proc); + + // now read the data from sysctl + if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) { + // raise an exception and throw errno as the error + PyErr_SetFromErrno(PyExc_OSError); } - /* Fetch the command-line arguments and environment variables */ - //printf("pid: %ld\n", pid); - if (pid < 0 || pid > (long)INT_MAX) { - return Py_BuildValue(""); - } - - r = getcmdargs(pid, &exec_path, &env, &args); - if (r == 0) { - //PySequence_Tuple(args); - argList = PySequence_List(args); - } else if (r == ARGS_ACCESS_DENIED) { //-2 - argList = Py_BuildValue("[]"); - } else { - argList = Py_BuildValue(""); - } - - Py_XDECREF(args); - Py_XDECREF(exec_path); - Py_XDECREF(env); - return argList; + /* + * sysctl succeeds but len is zero, happens when process has gone away + */ + if (len == 0) { + NoSuchProcess(); + return -1; + } + return 0; } - diff -Nru python-psutil-0.1.3/psutil/arch/osx/process_info.h python-psutil-0.2.1/psutil/arch/osx/process_info.h --- python-psutil-0.1.3/psutil/arch/osx/process_info.h 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/arch/osx/process_info.h 2011-01-12 18:47:32.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $Id: process_info.h 399 2009-05-12 23:24:21Z jloden $ + * $Id: process_info.h 823 2010-11-16 22:34:06Z jloden $ * * Helper functions related to fetching process information. Used by _psutil_osx * module methods. @@ -11,6 +11,7 @@ typedef struct kinfo_proc kinfo_proc; int get_proc_list(kinfo_proc **procList, size_t *procCount); -int getcmdargs(long pid, PyObject **exec_path, PyObject **envlist, PyObject **arglist); +int get_kinfo_proc(pid_t pid, struct kinfo_proc *kp); +size_t get_argmax(void); +int pid_exists(long pid); PyObject* get_arg_list(long pid); - diff -Nru python-psutil-0.1.3/psutil/_common.py python-psutil-0.2.1/psutil/_common.py --- python-psutil-0.1.3/psutil/_common.py 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/_common.py 2011-03-20 20:29:49.000000000 +0000 @@ -0,0 +1,61 @@ +#/usr/bin/env python +# +#$Id: _common.py 953 2011-03-20 20:29:45Z g.rodola $ +# + +"""Common objects shared by all _ps* modules.""" + +from psutil._compat import namedtuple + +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 + +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 + + +# system +ntuple_sys_cputimes = namedtuple('cputimes', 'user nice system idle iowait irq softirq') + +# processes +ntuple_meminfo = namedtuple('meminfo', 'rss vms') +ntuple_cputimes = namedtuple('cputimes', 'user system') +ntuple_openfile = namedtuple('openfile', 'path fd') +ntuple_connection = namedtuple('connection', 'fd family type local_address remote_address status') +ntuple_thread = namedtuple('thread', 'id user_time system_time') +ntuple_uids = namedtuple('user', 'real effective saved') +ntuple_gids = namedtuple('group', 'real effective saved') +ntuple_io = namedtuple('io', 'read_count write_count read_bytes write_bytes') +ntuple_ionice = namedtuple('ionice', 'ioclass value') + +# the __all__ namespace common to all _ps*.py platform modules +base_module_namespace = [ + # constants + "NUM_CPUS", "TOTAL_PHYMEM", "BOOT_TIME", + # classes + "PlatformProcess", + # functions + "avail_phymem", "used_phymem", "total_virtmem", "avail_virtmem", + "used_virtmem", "get_system_cpu_times", "pid_exists", "get_pid_list", + ] + diff -Nru python-psutil-0.1.3/psutil/_compat.py python-psutil-0.2.1/psutil/_compat.py --- python-psutil-0.1.3/psutil/_compat.py 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/_compat.py 2011-02-15 13:33:12.000000000 +0000 @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# +# $Id: _compat.py 910 2011-02-15 13:33:06Z g.rodola $ +# + +"""Module which provides compatibility with older Python versions.""" + +__all__ = ["namedtuple", "property"] + +from operator import itemgetter as _itemgetter +from keyword import iskeyword as _iskeyword +import sys as _sys + +try: + from collections import namedtuple +except ImportError: + def namedtuple(typename, field_names, verbose=False, rename=False): + """A collections.namedtuple implementation written in Python + to support Python versions < 2.6. + + Taken from: http://code.activestate.com/recipes/500261/ + """ + # Parse and validate the field names. Validation serves two + # purposes, generating informative error messages and preventing + # template injection attacks. + if isinstance(field_names, basestring): + # names separated by whitespace and/or commas + field_names = field_names.replace(',', ' ').split() + field_names = tuple(map(str, field_names)) + if rename: + names = list(field_names) + seen = set() + for i, name in enumerate(names): + if (not min(c.isalnum() or c=='_' for c in name) or _iskeyword(name) + or not name or name[0].isdigit() or name.startswith('_') + or name in seen): + names[i] = '_%d' % i + seen.add(name) + field_names = tuple(names) + for name in (typename,) + field_names: + if not min(c.isalnum() or c=='_' for c in name): + raise ValueError('Type names and field names can only contain ' \ + 'alphanumeric characters and underscores: %r' + % name) + if _iskeyword(name): + raise ValueError('Type names and field names cannot be a keyword: %r' \ + % name) + if name[0].isdigit(): + raise ValueError('Type names and field names cannot start with a ' \ + 'number: %r' % name) + seen_names = set() + for name in field_names: + if name.startswith('_') and not rename: + raise ValueError('Field names cannot start with an underscore: %r' + % name) + if name in seen_names: + raise ValueError('Encountered duplicate field name: %r' % name) + seen_names.add(name) + + # Create and fill-in the class template + numfields = len(field_names) + # tuple repr without parens or quotes + argtxt = repr(field_names).replace("'", "")[1:-1] + reprtxt = ', '.join('%s=%%r' % name for name in field_names) + template = '''class %(typename)s(tuple): + '%(typename)s(%(argtxt)s)' \n + __slots__ = () \n + _fields = %(field_names)r \n + def __new__(_cls, %(argtxt)s): + return _tuple.__new__(_cls, (%(argtxt)s)) \n + @classmethod + def _make(cls, iterable, new=tuple.__new__, len=len): + 'Make a new %(typename)s object from a sequence or iterable' + result = new(cls, iterable) + if len(result) != %(numfields)d: + raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) + return result \n + def __repr__(self): + return '%(typename)s(%(reprtxt)s)' %% self \n + def _asdict(self): + 'Return a new dict which maps field names to their values' + return dict(zip(self._fields, self)) \n + def _replace(_self, **kwds): + 'Return a new %(typename)s object replacing specified fields with new values' + result = _self._make(map(kwds.pop, %(field_names)r, _self)) + if kwds: + raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) + return result \n + def __getnewargs__(self): + return tuple(self) \n\n''' % locals() + for i, name in enumerate(field_names): + template += ' %s = _property(_itemgetter(%d))\n' % (name, i) + if verbose: + print template + + # Execute the template string in a temporary namespace + namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, + _property=property, _tuple=tuple) + try: + exec template in namespace + except SyntaxError, e: + raise SyntaxError(e.message + ':\n' + template) + result = namespace[typename] + + # For pickling to work, the __module__ variable needs to be set + # to the frame where the named tuple is created. Bypass this + # step in enviroments where sys._getframe is not defined (Jython + # for example) or sys._getframe is not defined for arguments + # greater than 0 (IronPython). + try: + result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + + return result + +# dirty hack to support property.setter on python < 2.6 +property = property + +if not hasattr(property, "setter"): + class property(property): + def setter(self, value): + cls_ns = _sys._getframe(1).f_locals + for k, v in cls_ns.iteritems(): + if v == self: + name = k + break + cls_ns[name] = property(self.fget, value, self.fdel, self.__doc__) + return cls_ns[name] + diff -Nru python-psutil-0.1.3/psutil/error.py python-psutil-0.2.1/psutil/error.py --- python-psutil-0.1.3/psutil/error.py 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/error.py 2011-02-22 04:21:33.000000000 +0000 @@ -1,7 +1,70 @@ -# this exception get overriden by the platform specific modules if necessary -class NoSuchProcess(Exception): - """No process was found for the given parameters.""" +#!/usr/bin/env python +# +# $Id: error.py 931 2011-02-22 04:10:46Z g.rodola $ +# -class AccessDenied(Exception): +"""psutil exception classes""" + + +class Error(Exception): + """Base exception class. All other psutil exceptions inherit + from this one. + """ + +class NoSuchProcess(Error): + """Exception raised when a process with a certain PID doesn't + or no longer exists (zombie). + """ + + def __init__(self, pid, name=None, msg=None): + self.pid = pid + self.name = name + self.msg = msg + if msg is None: + if name: + details = "(pid=%s, name=%s)" % (self.pid, repr(self.name)) + else: + details = "(pid=%s)" % self.pid + self.msg = "process no longer exists " + details + + def __str__(self): + return self.msg + + +class AccessDenied(Error): """Exception raised when permission to perform an action is denied.""" + def __init__(self, pid=None, name=None, msg=None): + self.pid = pid + self.name = name + self.msg = msg + if msg is None: + if (pid is not None) and (name is not None): + self.msg = "(pid=%s, name=%s)" % (pid, repr(name)) + elif (pid is not None): + self.msg = "(pid=%s)" % self.pid + else: + self.msg = "" + + def __str__(self): + return self.msg + + +class TimeoutExpired(Error): + """Raised on Process.wait(timeout) if timeout expires and process + is still alive. + """ + + def __init__(self, pid=None, name=None): + self.pid = pid + self.name = name + if (pid is not None) and (name is not None): + self.msg = "(pid=%s, name=%s)" % (pid, repr(name)) + elif (pid is not None): + self.msg = "(pid=%s)" % self.pid + else: + self.msg = "" + + def __str__(self): + return self.msg + diff -Nru python-psutil-0.1.3/psutil/__init__.py python-psutil-0.2.1/psutil/__init__.py --- python-psutil-0.1.3/psutil/__init__.py 2009-09-15 00:42:26.000000000 +0000 +++ python-psutil-0.2.1/psutil/__init__.py 2011-03-20 20:42:29.000000000 +0000 @@ -1,126 +1,70 @@ #!/usr/bin/env python # -# $Id: __init__.py 531 2010-03-02 20:54:22Z billiejoex $ +# $Id: __init__.py 954 2011-03-20 20:42:26Z g.rodola $ # """psutil is a module providing convenience functions for managing processes in a portable way by using Python. """ +__version__ = "0.2.1" +version_info = tuple([int(num) for num in __version__.split('.')]) + __all__ = [ - "NoSuchProcess", - "AccessDenied", - "NUM_CPUS", - "ProcessInfo", - "Process", - "test", - "CPUTimes", - "pid_exists", - "get_pid_list", - "process_iter", - "get_process_list", - "TOTAL_PHYMEM", - "avail_phymem", - "used_phymem", - "total_virtmem", - "avail_virtmem", - "used_virtmem", - "cpu_times", - "cpu_percent", + # 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", + "avail_phymem", "used_phymem", "total_virtmem", "avail_virtmem", + "used_virtmem", "cpu_times", "cpu_percent", ] -__version__ = '0.1.3' - - import sys import os import time +import signal +import warnings +import errno +import subprocess try: - import pwd, grp + import pwd except ImportError: - pwd = grp = None + pwd = None -# exceptions are imported here, but may be overriden by platform -# module implementation later -from error import * +from psutil.error import Error, NoSuchProcess, AccessDenied, TimeoutExpired +from psutil._compat import property +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.lower().startswith("linux"): - from _pslinux import * + from psutil._pslinux import * + __all__.extend(["cached_phymem", "phymem_buffers"]) elif sys.platform.lower().startswith("win32"): - from _psmswindows import * - __all__.append("wmi") + from psutil._psmswindows import * + __all__.extend(["ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS", + "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", + "NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS"]) elif sys.platform.lower().startswith("darwin"): - from _psosx import * + from psutil._psosx import * elif sys.platform.lower().startswith("freebsd"): - from _psbsd import * -else: - raise ImportError('no os specific module found') - -# platform-specific modules define an Impl implementation class -_platform_impl = Impl() - - -class CPUTimes: - """This class contains information about CPU times. - It is not used directly but it's returned as an instance by - psutil.cpu_times() function. - - Every CPU time is accessible in form of an attribute and represents - the time CPU has spent in the given mode. - - The attributes availability varies depending on the platform. - Here follows a list of all available attributes: - - - user - - system - - idle - - nice (UNIX) - - iowait (Linux) - - irq (Linux, FreeBSD) - - softirq (Linux) - """ - - def __init__(self, **kwargs): - self.__attrs = [] - for name in kwargs: - setattr(self, name, kwargs[name]) - self.__attrs.append(name) - - def __str__(self): - string = [] - for attr in self.__attrs: - value = getattr(self, attr) - string.append("%s=%s" %(attr, value)) - return '; '.join(string) - - def __iter__(self): - for attr in self.__attrs: - yield getattr(self, attr) - + from psutil._psbsd import * -class ProcessInfo(object): - """Class that allows the process information to be passed between - external code and psutil. Used directly by the Process class. - """ - - def __init__(self, pid, ppid=None, name=None, path=None, cmdline=None, - uid=None, gid=None): - self.pid = pid - self.ppid = ppid - self.name = name - self.path = path - self.cmdline = cmdline - # if we have the cmdline but not the path, figure it out from argv[0] - if cmdline and not path: - self.path = os.path.dirname(cmdline[0]) - self.uid = uid - self.gid = gid - self.create = None - self.username = None +else: + raise NotImplementedError('platform %s is not supported' % sys.platform) class Process(object): @@ -133,196 +77,294 @@ if not isinstance(pid, int): raise ValueError("An integer is required") if not pid_exists(pid): - raise NoSuchProcess("No process found with PID %s" % pid) - self._procinfo = ProcessInfo(pid) - self.is_proxy = True - # try to init CPU times, if it raises AccessDenied then suppress - # it so it won't interrupt the constructor. First call to - # get_cpu_percent() will trigger the AccessDenied exception - # instead. + raise NoSuchProcess(pid, None, "no process found with PID %s" % pid) + self._pid = pid + # platform-specific modules define an PlatformProcess + # implementation class + self._platform_impl = PlatformProcess(pid) + self._last_sys_cpu_times = None + self._last_proc_cpu_times = None + + def __str__(self): try: - self._last_sys_time = time.time() - self._last_user_time, self._last_kern_time = self.get_cpu_times() + pid = self.pid + name = repr(self.name) + cmdline = self.cmdline and repr(' '.join(self.cmdline)) + except NoSuchProcess: + details = "(pid=%s (terminated))" % self.pid except AccessDenied: - self._last_user_time, self._last_kern_time = None, None + details = "(pid=%s)" % (self.pid) + else: + if cmdline: + details = "(pid=%s, name=%s, cmdline=%s)" % (pid, name, cmdline) + else: + details = "(pid=%s, name=%s)" % (pid, name) + return "%s.%s%s" % (self.__class__.__module__, + self.__class__.__name__, details) - def __str__(self): - return "psutil.Process " \ - %(self.pid, self.ppid, self.name, self.path, self.cmdline, \ - self.uid, self.gid) + def __repr__(self): + return "<%s at %s>" % (self.__str__(), id(self)) def __eq__(self, other): - """Test for equality with another Process object based on PID - and creation time.""" - # Avoid to use self.create_time directly to avoid caching in - # case kill() gets called before create_time resulting in - # NoSuchProcess not being raised (see issue 77): - h1 = (self.pid, self._procinfo.create or \ - _platform_impl.get_process_create_time(self.pid)) - h2 = (other.pid, other.create_time) - return h1 == h2 - - def deproxy(self): - """Used internally by Process properties. The first call to - deproxy() initializes the ProcessInfo object in self._procinfo - with process data read from platform-specific module's - get_process_info() method. - - This method becomes a NO-OP after the first property is accessed. - Property data is filled in from the ProcessInfo object created, - and further calls to deproxy() simply return immediately without - calling get_process_info(). - """ - if self.is_proxy: - # get_process_info returns a tuple we use as the arguments - # to the ProcessInfo constructor - self._procinfo = ProcessInfo(*_platform_impl.get_process_info(self._procinfo.pid)) - self.is_proxy = False + """Test for equality with another Process object based on pid + and creation time. + """ + h1 = (self.pid, self.create_time) + try: + h2 = (other.pid, other.create_time) + except AttributeError: + return False + else: + return h1 == h2 @property def pid(self): """The process pid.""" - return self._procinfo.pid + return self._pid @property def ppid(self): """The process parent pid.""" - self.deproxy() - return self._procinfo.ppid + return self._platform_impl.get_process_ppid() @property def parent(self): - """Return the parent process as a Process object. If no ppid is - known then return None.""" - if self.ppid is not None: - return Process(self.ppid) - return None + """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 @property def name(self): """The process name.""" - self.deproxy() - return self._procinfo.name + name = self._platform_impl.get_process_name() + if os.name == 'posix': + # On UNIX the name gets truncated to the first 15 characters. + # If it matches the first part of the cmdline we return that + # one instead because it's usually more explicative. + # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon". + cmdline = self.cmdline + if cmdline: + extended_name = os.path.basename(cmdline[0]) + if extended_name.startswith(name): + name = extended_name + # XXX - perhaps needs refactoring + self._platform_impl._process_name = name + return name + + @property + def exe(self): + """The process executable as an absolute path name.""" + exe = self._platform_impl.get_process_exe() + # if we have the cmdline but not the exe, figure it out from argv[0] + if not exe: + cmdline = self.cmdline + if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'): + _exe = os.path.realpath(cmdline[0]) + if os.path.isfile(_exe) and os.access(_exe, os.X_OK): + return _exe + if not exe: + raise AccessDenied(self.pid, self._platform_impl._process_name) + return exe @property def path(self): - """The process path.""" - self.deproxy() - return self._procinfo.path + msg = "'path' property is deprecated; use 'os.path.dirname(exe)' instead" + warnings.warn(msg, DeprecationWarning) + return os.path.dirname(self.exe) @property def cmdline(self): """The command line process has been called with.""" - self.deproxy() - return self._procinfo.cmdline + 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() + + @property + def nice(self): + """Get or set process niceness (priority).""" + return self._platform_impl.get_process_nice() + + @nice.setter + def nice(self, value): + # invoked on "p.nice = num"; change process niceness + return self._platform_impl.set_process_nice(value) + + 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 uid(self): - """The real user id of the current process.""" - self.deproxy() - return self._procinfo.uid + """The real user id of the current process (deprecated).""" + warnings.warn("'uid' property is deprecated; use 'uids.real' instead", + DeprecationWarning) + if os.name != 'posix': + return -1 + return self.uids.real @property def gid(self): - """The real group id of the current process.""" - self.deproxy() - return self._procinfo.gid + """The real group id of the current process (deprecated).""" + warnings.warn("'gid' property is deprecated; use 'uids.real' instead", + DeprecationWarning) + if os.name != 'posix': + return -1 + return self.gids.real @property def username(self): - """The name of the user that owns the process.""" - if self._procinfo.username is not None: - return self._procinfo.username - if pwd is not None: - self._procinfo.username = pwd.getpwuid(self.uid).pw_name + """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: - self._procinfo.username = _platform_impl.get_process_username(self.pid) - return self._procinfo.username + return self._platform_impl.get_process_username() @property def create_time(self): """The process creation time as a floating point number expressed in seconds since the epoch, in UTC. """ - if self._procinfo.create is None: - self._procinfo.create = _platform_impl.get_process_create_time(self.pid) - return self._procinfo.create + return self._platform_impl.get_process_create_time() # available for Windows and Linux only - if hasattr(_platform_impl, "get_process_cwd"): + if hasattr(PlatformProcess, "get_process_cwd"): + def getcwd(self): """Return a string representing the process current working directory. """ - return _platform_impl.get_process_cwd(self.pid) + return self._platform_impl.get_process_cwd() - 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(): - raise NoSuchProcess - # windows - if hasattr(_platform_impl, "suspend_process"): - _platform_impl.suspend_process(self.pid) - else: - # posix - try: - os.kill(self.pid, signal.SIGSTOP) - except OSError, err: - if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid) - raise + # Linux, BSD and Windows only + if hasattr(PlatformProcess, "get_process_io_counters"): - def resume(self): - """Resume process execution.""" - # safety measure in case the current process has been killed in - # meantime and the kernel reused its PID + 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() + + # available only on Linux + if hasattr(PlatformProcess, "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) + + 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): + """Return the children of this process as a list of Process + objects. + """ if not self.is_running(): - raise NoSuchProcess - # windows - if hasattr(_platform_impl, "resume_process"): - _platform_impl.resume_process(self.pid) - else: - # posix + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + retlist = [] + for proc in process_iter(): try: - os.kill(self.pid, signal.SIGCONT) - except OSError, err: - if err.errno == errno.ESRCH: - raise NoSuchProcess(self.pid) - raise - - def get_cpu_percent(self): - """Compare process times to system time elapsed since last call - and calculate CPU utilization as a percentage. It is recommended - for accuracy that this function be called with at least 1 second - between calls. The initial delta is calculated from the - instantiation of the Process object. - """ - now = time.time() - # will raise AccessDenied on OS X if not root or in procmod group - user_t, kern_t = _platform_impl.get_cpu_times(self.pid) + if proc.ppid == self.pid: + retlist.append(proc) + except NoSuchProcess: + pass + return retlist + + def get_cpu_percent(self, interval=0.1): + """Return a float representing the current process CPU + utilization as a percentage. + + When interval is > 0.0 compares process times to system CPU + times elapsed before and after the interval (blocking). + + When interval is 0.0 or None compares process times to system CPU + times elapsed since last call, returning immediately. + In this case is recommended for accuracy that this function be + called with at least 0.1 seconds between calls. + """ + blocking = interval is not None and interval > 0.0 + if blocking: + st1 = sum(cpu_times()) + pt1 = self._platform_impl.get_cpu_times() + time.sleep(interval) + st2 = sum(cpu_times()) + pt2 = self._platform_impl.get_cpu_times() + else: + st1 = self._last_sys_cpu_times + pt1 = self._last_proc_cpu_times + st2 = sum(cpu_times()) + pt2 = self._platform_impl.get_cpu_times() + if st1 is None or pt1 is None: + self._last_sys_cpu_times = st2 + self._last_proc_cpu_times = pt2 + return 0.0 + + delta_proc = (pt2.user - pt1.user) + (pt2.system - pt1.system) + delta_time = st2 - st1 + # reset values for next call in case of interval == None + self._last_sys_cpu_times = st2 + self._last_proc_cpu_times = pt2 - total_proc_time = float((user_t - self._last_user_time) + \ - (kern_t - self._last_kern_time)) try: - percent = total_proc_time / float((now - self._last_sys_time)) + # the utilization split between all CPUs + overall_percent = (delta_proc / delta_time) * 100 except ZeroDivisionError: - percent = 0.000 - - # reset the values - self._last_sys_time = time.time() - self._last_user_time, self._last_kern_time = self.get_cpu_times() - - return (percent * 100.0) + # interval was too low + return 0.0 + # the utilization of a single CPU + single_cpu_percent = overall_percent * NUM_CPUS + # ugly hack to avoid troubles with float precision issues + if single_cpu_percent > 100.0: + return 100.0 + return round(single_cpu_percent, 1) def get_cpu_times(self): """Return a tuple whose values are process CPU user and system - time. These are the same first two values that os.times() - returns for the current process. + times. The same as os.times() but per-process. """ - return _platform_impl.get_cpu_times(self.pid) + return self._platform_impl.get_cpu_times() def get_memory_info(self): """Return a tuple representing RSS (Resident Set Size) and VMS @@ -333,52 +375,173 @@ On Windows RSS and VMS refer to "Mem Usage" and "VM Size" columns of taskmgr.exe. """ - return _platform_impl.get_memory_info(self.pid) + return self._platform_impl.get_memory_info() def get_memory_percent(self): """Compare physical system memory to process resident memory and calculate process memory utilization as a percentage. """ - rss = _platform_impl.get_memory_info(self.pid)[0] + rss = self._platform_impl.get_memory_info()[0] try: return (rss / float(TOTAL_PHYMEM)) * 100 except ZeroDivisionError: return 0.0 + def get_open_files(self): + """Return files opened by process as a list of namedtuples + including absolute file name and file descriptor number. + """ + return self._platform_impl.get_open_files() + + def get_connections(self): + """Return TCP and UPD connections opened by process as a list + of namedtuples. + On BSD and OSX results for third party processes (!= os.getpid()) + can differ depending on user privileges. + """ + return self._platform_impl.get_connections() + def is_running(self): - """Return whether the current process is running in the current process - list.""" + """Return whether the current process is running in the current + process list. + """ try: newproc = Process(self.pid) return self == newproc except NoSuchProcess: return False - def kill(self, sig=None): - """Kill the current process by using signal sig (defaults to SIGKILL). + 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(): - raise NoSuchProcess - _platform_impl.kill_process(self.pid, sig) + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + if os.name == 'posix': + try: + os.kill(self.pid, sig) + except OSError, err: + name = self._platform_impl._process_name + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, name) + if err.errno == errno.EPERM: + raise AccessDenied(self.pid, name) + raise + else: + if sig == signal.SIGTERM: + self._platform_impl.kill_process() + else: + raise ValueError("only SIGTERM is supported on Windows") + def suspend(self): + """Suspend process execution.""" + # safety measure in case the current process has been killed in + # meantime and the kernel reused its PID + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + # windows + if hasattr(self._platform_impl, "suspend_process"): + self._platform_impl.suspend_process() + else: + # posix + self.send_signal(signal.SIGSTOP) -def pid_exists(pid): - """Check whether the given PID exists in the current process list.""" - return _platform_impl.pid_exists(pid) + 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. + """ + return self._platform_impl.process_wait(timeout) + + +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 unique 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) + Process.__init__(self, self.__subproc.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)) -def get_pid_list(): - """Return a list of current running PIDs.""" - return _platform_impl.get_pid_list() def process_iter(): """Return an iterator yielding a Process class instances for all running processes on the local machine. """ - pids = _platform_impl.get_pid_list() - # for each PID, create a proxyied Process object - # it will lazy init it's name and path later if required + pids = get_pid_list() for pid in pids: try: yield Process(pid) @@ -392,48 +555,60 @@ return list(process_iter()) def cpu_times(): - """Return system CPU times as a CPUTimes object.""" - values = get_system_cpu_times() - return CPUTimes(**values) - -_last_idle_time = cpu_times().idle -_last_time = time.time() - -def cpu_percent(): - """Return the current system-wide CPU utilization as a percentage. For - highest accuracy, it is recommended that this be called at least 1/10th - of a second after importing the module or calling cpu_percent() in a - previous call, to allow for a larger time delta from which to calculate - the percentage value. + """Return system 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) """ - global _last_idle_time - global _last_time + return get_system_cpu_times() - idle_time = cpu_times().idle - curr_time = time.time() - time_delta = curr_time - _last_time - idle_delta = idle_time - _last_idle_time +_last_cpu_times = cpu_times() - # reset values for next run - _last_idle_time = idle_time - _last_time = curr_time - - # Removed; see Issue #67: http://code.google.com/p/psutil/issues/detail?id=67 - # invalid data, will not be accurate so return 0.0 to avoid an overflow - #if time_delta < idle_delta: - # return 0.0 - - try : - idle_percent = (idle_delta / time_delta) * 100.0 - util_percent = ((100 * NUM_CPUS) - idle_percent) / NUM_CPUS - except ZeroDivisionError: - return 0.0 +def cpu_percent(interval=0.1): + """Return a float representing the current system-wide CPU + utilization as a percentage. - if util_percent < 0: - return 0.0 + When interval is > 0.0 compares system CPU times elapsed before + and after the interval (blocking). + + When interval is 0.0 or None compares system CPU times elapsed + since last call or module import, returning immediately. + In this case is recommended for accuracy that this function be + called with at least 0.1 seconds between calls. + """ + global _last_cpu_times + + blocking = interval is not None and interval > 0.0 + if blocking: + t1 = cpu_times() + time.sleep(interval) else: - return util_percent + t1 = _last_cpu_times + + t1_all = sum(t1) + t1_busy = t1_all - t1.idle + + t2 = cpu_times() + t2_all = sum(t2) + t2_busy = t2_all - t2.idle + + _last_cpu_times = t1 + # this usually indicates a float precision issue + if t2_busy <= t1_busy: + return 0.0 + + busy_delta = t2_busy - t1_busy + all_delta = t2_all - t1_all + busy_perc = (busy_delta / all_delta) * 100 + return round(busy_perc, 1) def test(): @@ -449,7 +624,7 @@ if os.name == 'nt' and '\\' in user: user = user.split('\\')[1] pid = proc.pid - cpu = round(proc.get_cpu_percent(), 1) + cpu = round(proc.get_cpu_percent(interval=None), 1) mem = round(proc.get_memory_percent(), 1) rss, vsz = [x / 1024 for x in proc.get_memory_info()] @@ -465,12 +640,12 @@ # where cmdline is not available UNIX shows process name between # [] parentheses if not cmd: - cmd = "[%s]" %proc.name + cmd = "[%s]" % proc.name return "%-9s %-5s %-4s %4s %7s %7s %5s %8s %s" \ - %(user, pid, cpu, mem, vsz, rss, start, cputime, cmd) + % (user, pid, cpu, mem, vsz, rss, start, cputime, cmd) print "%-9s %-5s %-4s %4s %7s %7s %5s %7s %s" \ - %("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "START", "TIME", "COMMAND") + % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "START", "TIME", "COMMAND") pids = get_pid_list() pids.sort() for pid in pids: @@ -483,3 +658,4 @@ if __name__ == "__main__": test() + Binary files /tmp/axSshgHNa2/python-psutil-0.1.3/psutil/.__init__.py.swp and /tmp/wQcuD3KndE/python-psutil-0.2.1/psutil/.__init__.py.swp differ diff -Nru python-psutil-0.1.3/psutil/_psbsd.py python-psutil-0.2.1/psutil/_psbsd.py --- python-psutil-0.1.3/psutil/_psbsd.py 2009-09-14 21:45:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psbsd.py 2011-03-04 17:05:27.000000000 +0000 @@ -1,23 +1,27 @@ #!/usr/bin/env python # -# $Id: _psbsd.py 519 2010-02-23 23:18:51Z billiejoex@gmail.com $ +# $Id: _psbsd.py 944 2011-03-04 17:05:24Z g.rodola $ # -import os -import signal import errno -import pwd -import grp +import os import _psutil_bsd +import _psutil_posix +import _psposix +from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._compat import namedtuple +from psutil._common import * + +__all__ = base_module_namespace[:] -# import psutil exceptions we can override with our own -from error import * +# --- constants -# module level constants (gets pushed up to psutil module) -NoSuchProcess = _psutil_bsd.NoSuchProcess NUM_CPUS = _psutil_bsd.get_num_cpus() TOTAL_PHYMEM = _psutil_bsd.get_total_phymem() +BOOT_TIME = _psutil_bsd.get_system_boot_time() + +# --- public functions def avail_phymem(): "Return the amount of physical memory available on the system, in bytes." @@ -39,93 +43,162 @@ """Return the amount of used memory currently in use on the system, in bytes.""" return _psutil_bsd.get_total_virtmem() - _psutil_bsd.get_avail_virtmem() +_cputimes_ntuple = namedtuple('cputimes', 'user nice system idle irq') def get_system_cpu_times(): - """Return a dict representing the following CPU times: - user, nice, system, idle, interrupt.""" - values = _psutil_bsd.get_system_cpu_times() - return dict(user=values[0], nice=values[1], system=values[2], - idle=values[3], irq=values[4]) + """Return system 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_pid_list(): + """Returns a list of PIDs currently running on the system.""" + return _psutil_bsd.get_pid_list() + +def pid_exists(pid): + """Check For the existence of a unix pid.""" + return _psposix.pid_exists(pid) -def wrap_privileges(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(*args, **kwargs): - try: - return callable(*args, **kwargs) - except OSError, err: - if err.errno == errno.EPERM: - raise AccessDenied - raise - return wrapper -def prevent_zombie(method): +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, pid, *args, **kwargs): + def wrapper(self, *args, **kwargs): try: - return method(self, pid, *args, **kwargs) + return method(self, *args, **kwargs) except OSError, err: if err.errno == errno.ESRCH: - raise NoSuchProcess(pid) + 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 BSDProcess(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_ppid(self): + """Return process parent pid.""" + return _psutil_bsd.get_process_ppid(self.pid) + + @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 ntuple_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 ntuple_gids(real, effective, saved) -class Impl(object): - - @wrap_privileges - def get_process_info(self, pid): - """Returns a tuple that can be passed to the psutil.ProcessInfo class - constructor. - """ - infoTuple = _psutil_bsd.get_process_info(pid) - return infoTuple - - @wrap_privileges - def kill_process(self, pid, sig=signal.SIGKILL): - """Terminates the process with the given PID.""" - if sig is None: - sig = signal.SIGKILL - try: - os.kill(pid, sig) - except OSError, err: - if err.errno == errno.ESRCH: - raise NoSuchProcess(pid) - raise - - @wrap_privileges - def get_cpu_times(self, pid): + @wrap_exceptions + def get_cpu_times(self): """return a tuple containing process user/kernel time.""" - return _psutil_bsd.get_cpu_times(pid) + user, system = _psutil_bsd.get_cpu_times(self.pid) + return ntuple_cputimes(user, system) - @prevent_zombie - def get_memory_info(self, pid): + @wrap_exceptions + def get_memory_info(self): """Return a tuple with the process' RSS and VMS size.""" - return _psutil_bsd.get_memory_info(pid) + rss, vms = _psutil_bsd.get_memory_info(self.pid) + return ntuple_meminfo(rss, vms) - @prevent_zombie - def get_process_create_time(self, pid): - return _psutil_bsd.get_process_create_time(pid) - - def get_pid_list(self): - """Returns a list of PIDs currently running on the system.""" - return _psutil_bsd.get_pid_list() - - def pid_exists(self, pid): - """Check For the existence of a unix pid.""" - if pid < 0: - return False + @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_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 = ntuple_thread(thread_id, utime, stime) + retlist.append(ntuple) + return retlist + + + def get_open_files(self): + """Return files opened by process by parsing lsof output.""" + lsof = _psposix.LsofParser(self.pid, self._process_name) + return lsof.get_process_open_files() + + def get_connections(self): + """Return network connections opened by a process as a list of + namedtuples by parsing lsof output. + """ + lsof = _psposix.LsofParser(self.pid, self._process_name) + return lsof.get_process_connections() + @wrap_exceptions + def process_wait(self, timeout=None): try: - os.kill(pid, 0) - except OSError, e: - return e.errno == errno.EPERM - else: - return True + 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 ntuple_io(rc, wc, rb, wb) + +PlatformProcess = BSDProcess diff -Nru python-psutil-0.1.3/psutil/_pslinux.py python-psutil-0.2.1/psutil/_pslinux.py --- python-psutil-0.1.3/psutil/_pslinux.py 2009-09-14 17:57:44.000000000 +0000 +++ python-psutil-0.2.1/psutil/_pslinux.py 2011-03-20 20:29:49.000000000 +0000 @@ -1,19 +1,31 @@ #!/usr/bin/env python # -# $Id: _pslinux.py 515 2010-02-23 19:57:14Z billiejoex@gmail.com $ +# $Id: _pslinux.py 953 2011-03-20 20:29:45Z g.rodola $ # import os -import signal import errno -import pwd -import grp +import socket +import struct +import sys +import base64 + +import _psutil_posix +import _psutil_linux +from psutil import _psposix +from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._compat import namedtuple +from psutil._common import * + +# Linux specific extended namespace +__all__ = base_module_namespace[:] +__all__.extend([ + "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE", "IOPRIO_CLASS_IDLE", + "phymem_buffers", "cached_phymem", + ]) -# import psutil exceptions we can override with our own -from error import * - -def _get_uptime(): +def _get_boot_time(): """Return system boot time (epoch in seconds)""" f = open('/proc/stat', 'r') for line in f: @@ -42,10 +54,30 @@ # Number of clock ticks per second _CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"]) -_UPTIME = _get_uptime() +BOOT_TIME = _get_boot_time() NUM_CPUS = _get_num_cpus() TOTAL_PHYMEM = _get_total_phymem() - +# 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 + +del _get_boot_time, _get_num_cpus, _get_total_phymem + +# http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h +_TCP_STATES_TABLE = {"01" : "ESTABLISHED", + "02" : "SYN_SENT", + "03" : "SYN_RECV", + "04" : "FIN_WAIT1", + "05" : "FIN_WAIT2", + "06" : "TIME_WAIT", + "07" : "CLOSE", + "08" : "CLOSE_WAIT", + "09" : "LAST_ACK", + "0A" : "LISTEN", + "0B" : "CLOSING" + } def avail_phymem(): """Return the amount of physical memory available, in bytes.""" @@ -72,7 +104,9 @@ return int(line.split()[1]) * 1024 def avail_virtmem(): - "Return the amount of virtual memory currently in use on the system, in bytes." + """Return the amount of virtual memory currently in use on the + system, in bytes. + """ f = open('/proc/meminfo', 'r') for line in f: if line.startswith('SwapFree:'): @@ -80,9 +114,32 @@ return int(line.split()[1]) * 1024 def used_virtmem(): - """Return the amount of used memory currently in use on the system, in bytes.""" + """Return the amount of used memory currently in use on the system, + in bytes. + """ return total_virtmem() - avail_virtmem() +def cached_phymem(): + """Return the amount of cached memory on the system, in bytes. + This reflects the "cached" column of free command line utility. + """ + f = open('/proc/meminfo', 'r') + for line in f: + if line.startswith('Cached:'): + f.close() + return int(line.split()[1]) * 1024 + +def phymem_buffers(): + """Return the amount of physical memory buffers used by the + kernel in bytes. + This reflects the "buffers" column of free command line utility. + """ + f = open('/proc/meminfo', 'r') + for line in f: + if line.startswith('Buffers:'): + f.close() + return int(line.split()[1]) * 1024 + def get_system_cpu_times(): """Return a dict representing the following CPU times: user, nice, system, idle, iowait, irq, softirq. @@ -90,121 +147,143 @@ f = open('/proc/stat', 'r') values = f.readline().split() f.close() + values = values[1:8] values = tuple([float(x) / _CLOCK_TICKS for x in values]) - return dict(user=values[0], nice=values[1], system=values[2], - idle=values[3], iowait=values[4], irq=values[5], softirq=values[6]) + return ntuple_sys_cputimes(*values[:7]) +def get_pid_list(): + """Returns a list of PIDs currently running on the system.""" + pids = [int(x) for x in os.listdir('/proc') if x.isdigit()] + # special case for 0 (kernel process) PID + pids.insert(0, 0) + return pids + +def pid_exists(pid): + """Check For the existence of a unix pid.""" + return _psposix.pid_exists(pid) + + +# 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 prevent_zombie(method): - """Call method(self, pid) into a try/except clause so that if an - IOError "No such file" exception is raised we assume the process - has died and raise psutil.NoSuchProcess instead. - """ - def wrapper(self, pid, *args, **kwargs): - try: - return method(self, pid, *args, **kwargs) - except (OSError, IOError), err: - if err.errno == errno.ENOENT: # no such file or directory - raise NoSuchProcess(pid) - raise - return wrapper - - -def wrap_privileges(callable): - """Call callable into a try/except clause so that if an - OSError EPERM exception is raised we translate it into - psutil.AccessDenied. +def wrap_exceptions(callable): + """Call callable into a try/except clause and translate ENOENT, + EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. """ - def wrapper(*args, **kwargs): + def wrapper(self, *args, **kwargs): try: - return callable(*args, **kwargs) + return callable(self, *args, **kwargs) except (OSError, IOError), err: + # 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. + 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 + raise AccessDenied(self.pid, self._process_name) raise return wrapper -class Impl(object): +class LinuxProcess(object): + """Linux process implementation.""" - @prevent_zombie - @wrap_privileges - def get_process_info(self, pid): - """Returns a process info class.""" - if pid == 0: - # special case for 0 (kernel process) PID - return (pid, 0, 'sched', '', [], 0, 0) + __slots__ = ["pid", "_process_name"] - # determine executable + def __init__(self, pid): + self.pid = pid + self._process_name = None + + @wrap_exceptions + def get_process_name(self): + if self.pid == 0: + return 'sched' # special case for kernel process + f = open("/proc/%s/stat" % self.pid) try: - _exe = os.readlink("/proc/%s/exe" %pid) - except OSError: - f = open("/proc/%s/stat" %pid) - try: - _exe = f.read().split(' ')[1].replace('(', '').replace(')', '') - finally: - f.close() + name = f.read().split(' ')[1].replace('(', '').replace(')', '') + finally: + f.close() + # XXX - gets changed later and probably needs refactoring + return name - # determine name and path - if os.path.isabs(_exe): - path, name = os.path.split(_exe) - else: - path = '' - name = _exe + def get_process_exe(self): + if self.pid in (0, 2): + raise AccessDenied(self.pid, self._process_name) + try: + exe = os.readlink("/proc/%s/exe" % self.pid) + except (OSError, IOError), err: + if err.errno == errno.ENOENT: + # no such file error; might be raised also if the + # path actually exists for system processes with + # low pids (about 0-20) + if os.path.lexists("/proc/%s/exe" % self.pid): + return "" + else: + # ok, it is a process which has gone away + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) + raise - # determine cmdline - f = open("/proc/%s/cmdline" %pid) + # readlink() might return paths containing null bytes causing + # problems when used with other fs-related functions (os.*, + # open(), ...) + exe = exe.replace('\x00', '') + # It seems symlinks can point to a deleted/invalid location + # (this usually happens with "pulseaudio" process). + # However, if we had permissions to execute readlink() it's + # likely that we'll be able to figure out exe from argv[0] + # later on. + if exe.endswith(" (deleted)") and not os.path.isfile(exe): + return "" + return exe + + @wrap_exceptions + def get_process_cmdline(self): + if self.pid == 0: + return [] # special case for kernel process + f = open("/proc/%s/cmdline" % self.pid) try: # return the args as a list - cmdline = [x for x in f.read().split('\x00') if x] + return [x for x in f.read().split('\x00') if x] finally: f.close() - return (pid, self._get_ppid(pid), name, path, cmdline, - self._get_process_uid(pid), - self._get_process_gid(pid)) - - @wrap_privileges - def kill_process(self, pid, sig=signal.SIGKILL): - """Terminates the process with the given PID.""" - if sig is None: - sig = signal.SIGKILL - try: - os.kill(pid, sig) - except OSError, err: - if err.errno == errno.ESRCH: - raise NoSuchProcess(pid) - raise - - def get_pid_list(self): - """Returns a list of PIDs currently running on the system.""" - pids = [int(x) for x in os.listdir('/proc') if x.isdigit()] + @wrap_exceptions + def get_process_io_counters(self): # special case for 0 (kernel process) PID - pids.insert(0, 0) - return pids - - def pid_exists(self, pid): - """Check For the existence of a unix pid.""" - if pid < 0: - return False + if self.pid == 0: + return ntuple_io(0, 0, 0, 0) + f = open("/proc/%s/io" % self.pid) + 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 ntuple_io(read_count, write_count, read_bytes, write_bytes) - try: - os.kill(pid, 0) - except OSError, e: - return e.errno == errno.EPERM - else: - return True - - @prevent_zombie - @wrap_privileges - def get_cpu_times(self, pid): + @wrap_exceptions + def get_cpu_times(self): # special case for 0 (kernel process) PID - if pid == 0: - return (0.0, 0.0) - f = open("/proc/%s/stat" %pid) + if self.pid == 0: + return ntuple_cputimes(0.0, 0.0) + f = open("/proc/%s/stat" % self.pid) st = f.read().strip() f.close() # ignore the first two values ("pid (exe)") @@ -212,15 +291,21 @@ values = st.split(' ') utime = float(values[11]) / _CLOCK_TICKS stime = float(values[12]) / _CLOCK_TICKS - return (utime, stime) + return ntuple_cputimes(utime, stime) - @prevent_zombie - @wrap_privileges - def get_process_create_time(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_create_time(self): # special case for 0 (kernel processes) PID; return system uptime - if pid == 0: - return _UPTIME - f = open("/proc/%s/stat" %pid) + if self.pid == 0: + return BOOT_TIME + f = open("/proc/%s/stat" % self.pid) st = f.read().strip() f.close() # ignore the first two values ("pid (exe)") @@ -230,79 +315,272 @@ # unit is jiffies (clock ticks). # We first divide it for clock ticks and then add uptime returning # seconds since the epoch, in UTC. - starttime = (float(values[19]) / _CLOCK_TICKS) + _UPTIME + starttime = (float(values[19]) / _CLOCK_TICKS) + BOOT_TIME return starttime - @prevent_zombie - @wrap_privileges - def get_memory_info(self, pid): + @wrap_exceptions + def get_memory_info(self): # special case for 0 (kernel processes) PID - if pid == 0: - return (0, 0) - f = open("/proc/%s/status" % pid) + if self.pid == 0: + return ntuple_meminfo(0, 0) + f = open("/proc/%s/status" % self.pid) virtual_size = 0 resident_size = 0 _flag = False for line in f: if (not _flag) and line.startswith("VmSize:"): - virtual_size = int(line.split()[1]) + virtual_size = int(line.split()[1]) * 1024 _flag = True elif line.startswith("VmRSS"): - resident_size = int(line.split()[1]) + resident_size = int(line.split()[1]) * 1024 break f.close() - return (resident_size * 1024, virtual_size * 1024) + return ntuple_meminfo(resident_size, virtual_size) + + @wrap_exceptions + def get_process_cwd(self): + if self.pid == 0: + raise AccessDenied(self.pid, self._process_name) + # 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_process_num_threads(self): + if self.pid == 0: + return 0 + f = open("/proc/%s/status" % self.pid) + for line in f: + if line.startswith("Threads:"): + f.close() + return int(line.split()[1]) + + @wrap_exceptions + def get_process_threads(self): + if self.pid == 0: + return [] + thread_ids = os.listdir("/proc/%s/task" % self.pid) + thread_ids.sort() + retlist = [] + for thread_id in thread_ids: + try: + f = open("/proc/%s/task/%s/stat" % (self.pid, thread_id)) + except (OSError, IOError), err: + if err.errno == errno.ENOENT: + # no such file or directory; it means thread + # disappeared on us + continue + raise + st = f.read().strip() + f.close() + # ignore the first two values ("pid (exe)") + st = st[st.find(')') + 2:] + values = st.split(' ') + utime = float(values[11]) / _CLOCK_TICKS + stime = float(values[12]) / _CLOCK_TICKS + ntuple = ntuple_thread(int(thread_id), utime, stime) + retlist.append(ntuple) + return retlist + + @wrap_exceptions + def get_process_nice(self): + #f = open('/proc/%s/stat' % self.pid, 'r') + #data = f.read() + #f.close() + #return int(data.split()[18]) + + # Use C implementation + return _psutil_posix.getpriority(self.pid) + + @wrap_exceptions + def set_process_nice(self, value): + return _psutil_posix.setpriority(self.pid, value) + + # 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 ntuple_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): + if self.pid == 0: + return 0 + f = open("/proc/%s/status" % self.pid) + for line in f: + if line.startswith("State:"): + f.close() + letter = line.split()[1] + if letter in _status_map: + return _status_map[letter] + return constant(-1, '?') + + @wrap_exceptions + def get_open_files(self): + retlist = [] + files = os.listdir("/proc/%s/fd" % self.pid) + for fd in files: + file = "/proc/%s/fd/%s" % (self.pid, fd) + if os.path.islink(file): + file = os.readlink(file) + if file.startswith("socket:["): + continue + if file.startswith("pipe:["): + continue + if file == "[]": + continue + if os.path.isfile(file) and not file in retlist: + ntuple = ntuple_openfile(file, int(fd)) + retlist.append(ntuple) + return retlist + + @wrap_exceptions + def get_connections(self): + if self.pid == 0: + return [] + inodes = {} + # os.listdir() is gonna raise a lot of access denied + # exceptions in case of unprivileged user; that's fine: + # lsof does the same so it's unlikely that we can to better. + for fd in os.listdir("/proc/%s/fd" % self.pid): + try: + inode = os.readlink("/proc/%s/fd/%s" % (self.pid, fd)) + except OSError: + continue + if inode.startswith('socket:['): + # the process is using a socket + inode = inode[8:][:-1] + inodes[inode] = fd + + if not inodes: + # no connections for this process + return [] + + def process(file, family, _type): + retlist = [] + f = open(file) + f.readline() # skip the first line + for line in f: + _, laddr, raddr, status, _, _, _, _, _, inode = line.split()[:10] + if inode in inodes: + laddr = self._decode_address(laddr, family) + raddr = self._decode_address(raddr, family) + if _type == socket.SOCK_STREAM: + status = _TCP_STATES_TABLE[status] + else: + status = "" + fd = int(inodes[inode]) + conn = ntuple_connection(fd, family, _type, laddr, + raddr, status) + retlist.append(conn) + f.close() + return retlist -# XXX - commented as it's still not implemented on all platforms. -# Leaving it here for the future. -## @prevent_zombie -## @wrap_privileges -## def get_process_environ(self, pid): -## """Return process environment variables for the process with the -## given PID as a dictionary.""" -## if pid == 0: -## return {} -## f = open("/proc/%s/environ" % pid) -## envs = f.read().strip('\0').split('\0') -## f.close() -## dict = {} -## for env in envs: -## key, value = env.split('=', 1) -## dict[key] = value -## return dict - - @prevent_zombie - @wrap_privileges - def get_process_cwd(self, pid): - if pid == 0: - return '' - return os.readlink("/proc/%s/cwd" %pid) + tcp4 = process("/proc/net/tcp", socket.AF_INET, socket.SOCK_STREAM) + tcp6 = process("/proc/net/tcp6", socket.AF_INET6, socket.SOCK_STREAM) + udp4 = process("/proc/net/udp", socket.AF_INET, socket.SOCK_DGRAM) + udp6 = process("/proc/net/udp6", socket.AF_INET6, socket.SOCK_DGRAM) + return tcp4 + tcp6 + udp4 + udp6 - def _get_ppid(self, pid): - f = open("/proc/%s/status" % pid) +# --- lsof implementation +# +# def get_connections(self): +# lsof = _psposix.LsofParser(self.pid, self._process_name) +# return lsof.get_process_connections() + + @wrap_exceptions + def get_process_ppid(self): + if self.pid == 0: + return 0 + f = open("/proc/%s/status" % self.pid) for line in f: if line.startswith("PPid:"): # PPid: nnnn f.close() return int(line.split()[1]) - def _get_process_uid(self, pid): - f = open("/proc/%s/status" %pid) + @wrap_exceptions + def get_process_uids(self): + if self.pid == 0: + return ntuple_uids(0, 0, 0) + f = open("/proc/%s/status" % self.pid) for line in f: if line.startswith('Uid:'): - # Uid line provides 4 values which stand for real, - # effective, saved set, and file system UIDs. - # We want to provide real UID only. f.close() - return int(line.split()[1]) + _, real, effective, saved, fs = line.split() + return ntuple_uids(int(real), int(effective), int(saved)) - def _get_process_gid(self, pid): - f = open("/proc/%s/status" %pid) + @wrap_exceptions + def get_process_gids(self): + if self.pid == 0: + return ntuple_uids(0, 0, 0) + f = open("/proc/%s/status" % self.pid) for line in f: if line.startswith('Gid:'): - # Uid line provides 4 values which stand for real, - # effective, saved set, and file system GIDs. - # We want to provide real GID only. f.close() - return int(line.split()[1]) + _, real, effective, saved, fs = line.split() + return ntuple_gids(int(real), int(effective), int(saved)) + + @staticmethod + def _decode_address(addr, family): + """Accept an "ip:port" address as displayed in /proc/net/* + and convert it into a human readable form, like: + + "0500000A:0016" -> ("10.0.0.5", 22) + "0000000000000000FFFF00000100007F:9E49" -> ("::ffff:127.0.0.1", 40521) + + The IPv4 address portion is a little-endian four-byte hexadecimal + number; that is, the least significant byte is listed first, + so we need to reverse the order of the bytes to convert it + to an IP address. + The port is represented as a two-byte hexadecimal number. + + Reference: + http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html + """ + ip, port = addr.split(':') + port = int(port, 16) + if sys.version_info >= (3,): + ip = ip.encode('ascii') + # this usually refers to a local socket in listen mode with + # no end-points connected + if not port: + return () + if family == socket.AF_INET: + ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1]) + else: # IPv6 + # old version - let's keep it, just in case... + #ip = ip.decode('hex') + #return socket.inet_ntop(socket.AF_INET6, + # ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4))) + ip = base64.b16decode(ip) + ip = socket.inet_ntop(socket.AF_INET6, + struct.pack('>4I', *struct.unpack('<4I', ip))) + return (ip, port) + +PlatformProcess = LinuxProcess diff -Nru python-psutil-0.1.3/psutil/_psmswindows.py python-psutil-0.2.1/psutil/_psmswindows.py --- python-psutil-0.1.3/psutil/_psmswindows.py 2009-09-15 00:42:26.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psmswindows.py 2011-02-22 05:52:41.000000000 +0000 @@ -1,30 +1,46 @@ #!/usr/bin/env python # -# $Id: _psmswindows.py 534 2010-03-02 21:21:55Z billiejoex $ +# $Id: _psmswindows.py 934 2011-02-22 05:52:06Z g.rodola $ # import errno import os -import _psutil_mswindows - -# import psutil exceptions we can override with our own -from error import * +import subprocess +import socket +import re +import sys +import platform -try: - import wmi -except ImportError: - wmi = None +import _psutil_mswindows +from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._compat import namedtuple +from psutil._common import * + +# Windows specific extended namespace +__all__ = base_module_namespace[:] +__all__.extend(["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) -NoSuchProcess = _psutil_mswindows.NoSuchProcess NUM_CPUS = _psutil_mswindows.get_num_cpus() -_UPTIME = _psutil_mswindows.get_system_uptime() TOTAL_PHYMEM = _psutil_mswindows.get_total_phymem() - +BOOT_TIME = _psutil_mswindows.get_system_uptime() +_WIN2000 = platform.win32_ver()[0] == '2000' ERROR_ACCESS_DENIED = 5 -ERROR_INVALID_PARAMETER = 87 + +# 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) + # --- public functions @@ -48,101 +64,207 @@ """Return the amount of used memory currently in use on the system, in bytes.""" return _psutil_mswindows.get_total_virtmem() - _psutil_mswindows.get_avail_virtmem() +_cputimes_ntuple = namedtuple('cputimes', 'user system idle') def get_system_cpu_times(): - """Return a dict representing the following CPU times: user, system, idle.""" - times = _psutil_mswindows.get_system_cpu_times() - return dict(user=times[0], system=times[1], idle=times[2]) + """Return system CPU times as a named tuple.""" + user, system, idle = _psutil_mswindows.get_system_cpu_times() + return _cputimes_ntuple(user, system, idle) + +def get_pid_list(): + """Returns a list of PIDs currently running on the system.""" + return _psutil_mswindows.get_pid_list() + +def pid_exists(pid): + return _psutil_mswindows.pid_exists(pid) # --- decorator -def wrap_privileges(callable): +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(*args, **kwargs): + def wrapper(self, *args, **kwargs): try: - return callable(*args, **kwargs) + return callable(self, *args, **kwargs) except OSError, err: - if err.errno in (errno.EACCES, ERROR_ACCESS_DENIED): - raise AccessDenied + if err.errno in (errno.EPERM, errno.EACCES, ERROR_ACCESS_DENIED): + raise AccessDenied(self.pid, self._process_name) + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, self._process_name) raise return wrapper -class Impl(object): +class WindowsProcess(object): + """Wrapper class around underlying C implementation.""" - @wrap_privileges - def get_process_info(self, pid): - """Returns a tuple that can be passed to the psutil.ProcessInfo class - constructor. - """ - infoTuple = _psutil_mswindows.get_process_info(pid) - return infoTuple + __slots__ = ["pid", "_process_name"] - @wrap_privileges - def get_memory_info(self, pid): + def __init__(self, pid): + self.pid = pid + self._process_name = None + + + @wrap_exceptions + def get_process_name(self): + """Return process name as a string of limited len (15).""" + return _psutil_mswindows.get_process_name(self.pid) + + def get_process_exe(self): + # no such thing as "exe" on Windows; it will maybe be determined + # later from cmdline[0] + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + if self.pid in (0, 4): + raise AccessDenied(self.pid, self._process_name) + return "" + + @wrap_exceptions + def get_process_cmdline(self): + """Return process cmdline as a list of arguments.""" + return _psutil_mswindows.get_process_cmdline(self.pid) + + @wrap_exceptions + def get_process_ppid(self): + """Return process parent pid.""" + return _psutil_mswindows.get_process_ppid(self.pid) + + def get_process_uid(self): + # no such thing as uid on Windows + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + return -1 + + def get_process_gid(self): + # no such thing as gid on Windows + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + return -1 + + @wrap_exceptions + def get_memory_info(self): """Returns a tuple or RSS/VMS memory usage in bytes.""" # special case for 0 (kernel processes) PID - if pid == 0: - return (0, 0) - return _psutil_mswindows.get_memory_info(pid) + if self.pid == 0: + return ntuple_meminfo(0, 0) + rss, vms = _psutil_mswindows.get_memory_info(self.pid) + return ntuple_meminfo(rss, vms) - @wrap_privileges - def kill_process(self, pid, sig=None): + @wrap_exceptions + def kill_process(self): """Terminates the process with the given PID.""" - try: - return _psutil_mswindows.kill_process(pid) - except OSError, err: - # work around issue #24 - if (pid == 0) and err.errno in (errno.EINVAL, ERROR_INVALID_PARAMETER): - raise AccessDenied - raise + return _psutil_mswindows.kill_process(self.pid) - def get_process_username(self, pid): - """Return the name of the user that owns the process""" - if wmi is None: - raise NotImplementedError("This functionnality requires pywin32 " \ - "extension to be installed") + @wrap_exceptions + def process_wait(self, timeout=None): + if not timeout: + timeout = 0 else: - if pid in (0, 4): - return 'NT AUTHORITY\\SYSTEM' - w = wmi.WMI().Win32_Process(ProcessId=pid) - if not w: - raise NoSuchProcess - domain, _, username = w[0].GetOwner() - return "%s\\%s" %(domain, username) - - def get_pid_list(self): - """Returns a list of PIDs currently running on the system.""" - return _psutil_mswindows.get_pid_list() - - def pid_exists(self, pid): - return _psutil_mswindows.pid_exists(pid) - - @wrap_privileges - def get_process_create_time(self, pid): - # special case for kernel process PIDs; return system uptime - if pid in (0, 4): - return _UPTIME - return _psutil_mswindows.get_process_create_time(pid) - - @wrap_privileges - def get_cpu_times(self, pid): - return _psutil_mswindows.get_process_cpu_times(pid) - - def suspend_process(self, pid): - return _psutil_mswindows.suspend_process(pid) - - def resume_process(self, pid): - return _psutil_mswindows.resume_process(pid) - - @wrap_privileges - def get_process_cwd(self, pid): - if pid in (0, 4): - return '' + # WaitForSingleObject() expects time in milliseconds + timeout = int(timeout * 1000) + ret = _psutil_mswindows.process_wait(self.pid, timeout) + if ret == -1: + 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) or self.pid == 8 and _WIN2000: + return 'NT AUTHORITY\\SYSTEM' + return _psutil_mswindows.get_process_username(self.pid); + + @wrap_exceptions + def get_process_create_time(self): + # special case for kernel process PIDs; return system boot time + if self.pid in (0, 4) or self.pid == 8 and _WIN2000: + return BOOT_TIME + return _psutil_mswindows.get_process_create_time(self.pid) + + @wrap_exceptions + def get_process_num_threads(self): + return _psutil_mswindows.get_process_num_threads(self.pid) + + @wrap_exceptions + def get_process_threads(self): + rawlist = _psutil_mswindows.get_process_threads(self.pid) + retlist = [] + for thread_id, utime, stime in rawlist: + ntuple = ntuple_thread(thread_id, utime, stime) + retlist.append(ntuple) + return retlist + + @wrap_exceptions + def get_cpu_times(self): + user, system = _psutil_mswindows.get_process_cpu_times(self.pid) + return ntuple_cputimes(user, system) + + @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) or self.pid == 8 and _WIN2000: + 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(pid) + path = _psutil_mswindows.get_process_cwd(self.pid) return os.path.normpath(path) + + @wrap_exceptions + def get_open_files(self): + if self.pid in (0, 4) or self.pid == 8 and _WIN2000: + return [] + retlist = [] + # Filenames come in in native format like: + # "\Device\HarddiskVolume1\Windows\systemew\file.txt" + # Convert the first part in the corresponding drive letter + # (e.g. "C:\") by using Windows's QueryDosDevice() + raw_file_names = _psutil_mswindows.get_process_open_files(self.pid) + for file in raw_file_names: + if sys.version_info >= (3,): + file = file.decode('utf8') + if file.startswith('\\Device\\'): + rawdrive = '\\'.join(file.split('\\')[:3]) + driveletter = _psutil_mswindows._QueryDosDevice(rawdrive) + file = file.replace(rawdrive, driveletter) + if os.path.isfile(file) and file not in retlist: + ntuple = ntuple_openfile(file, -1) + retlist.append(ntuple) + return retlist + + @wrap_exceptions + def get_connections(self): + retlist = _psutil_mswindows.get_process_connections(self.pid) + return [ntuple_connection(*conn) for conn in retlist] + + @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): + rc, wc, rb, wb =_psutil_mswindows.get_process_io_counters(self.pid) + return ntuple_io(rc, wc, rb, wb) + + @wrap_exceptions + def get_process_status(self): + suspended = _psutil_mswindows.is_process_suspended(self.pid) + if suspended: + return STATUS_STOPPED + else: + return STATUS_RUNNING + +PlatformProcess = WindowsProcess + diff -Nru python-psutil-0.1.3/psutil/_psosx.py python-psutil-0.2.1/psutil/_psosx.py --- python-psutil-0.1.3/psutil/_psosx.py 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psosx.py 2011-02-28 21:40:50.000000000 +0000 @@ -1,22 +1,27 @@ #!/usr/bin/env python # -# $Id: _psosx.py 366 2009-05-06 01:25:11Z g.rodola $ +# $Id: _psosx.py 940 2011-02-24 19:19:58Z g.rodola $ # -import os -import signal import errno +import os import _psutil_osx +import _psutil_posix +import _psposix +from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._compat import namedtuple +from psutil._common import * -# import psutil exceptions we can override with our own -from error import * +__all__ = base_module_namespace[:] + +# --- constants -# module level constants (gets pushed up to psutil module) -NoSuchProcess = _psutil_osx.NoSuchProcess -AccessDenied = _psutil_osx.AccessDenied NUM_CPUS = _psutil_osx.get_num_cpus() TOTAL_PHYMEM = _psutil_osx.get_total_phymem() +BOOT_TIME = _psutil_osx.get_system_boot_time() + +# --- functions def avail_phymem(): "Return the amount of physical memory available on the system, in bytes." @@ -38,76 +43,166 @@ """Return the amount of used memory currently in use on the system, in bytes.""" return _psutil_osx.get_total_virtmem() - _psutil_osx.get_avail_virtmem() +_cputimes_ntuple = namedtuple('cputimes', 'user nice system idle') def get_system_cpu_times(): - """Return a dict representing the following CPU times: - user, nice, system, idle.""" - values = _psutil_osx.get_system_cpu_times() - return dict(user=values[0], nice=values[1], system=values[2], idle=values[3]) + """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 wrap_privileges(callable): +def get_pid_list(): + """Returns a list of PIDs currently running on the system.""" + return _psutil_osx.get_pid_list() + +def pid_exists(pid): + """Check For the existence of a unix pid.""" + return _psposix.pid_exists(pid) + +# --- decorator + +def wrap_exceptions(callable): """Call callable into a try/except clause so that if an OSError EPERM exception is raised we translate it into psutil.AccessDenied. """ - def wrapper(*args, **kwargs): + def wrapper(self, *args, **kwargs): try: - return callable(*args, **kwargs) + return callable(self, *args, **kwargs) except OSError, err: - if err.errno == errno.EPERM: - raise AccessDenied + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) raise return wrapper -class Impl(object): +_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 OSXProcess(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) + + def get_process_exe(self): + # no such thing as "exe" on OS X; it will maybe be determined + # later from cmdline[0] + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + return "" + + @wrap_exceptions + def get_process_cmdline(self): + """Return process cmdline as a list of arguments.""" + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + return _psutil_osx.get_process_cmdline(self.pid) + + @wrap_exceptions + def get_process_ppid(self): + """Return process parent pid.""" + return _psutil_osx.get_process_ppid(self.pid) + + @wrap_exceptions + def get_process_uids(self): + real, effective, saved = _psutil_osx.get_process_uids(self.pid) + return ntuple_uids(real, effective, saved) + + + @wrap_exceptions + def get_process_gids(self): + real, effective, saved = _psutil_osx.get_process_gids(self.pid) + return ntuple_gids(real, effective, saved) - @wrap_privileges - def get_process_info(self, pid): - """Returns a tuple that can be passed to the psutil.ProcessInfo class - constructor. - """ - infoTuple = _psutil_osx.get_process_info(pid) - return infoTuple - - @wrap_privileges - def kill_process(self, pid, sig=signal.SIGKILL): - """Terminates the process with the given PID.""" - if sig is None: - sig = signal.SIGKILL - try: - os.kill(pid, sig) - except OSError, err: - if err.errno == errno.ESRCH: - raise NoSuchProcess(pid) - raise - - @wrap_privileges - def get_memory_info(self, pid): + @wrap_exceptions + def get_memory_info(self): """Return a tuple with the process' RSS and VMS size.""" - return _psutil_osx.get_memory_info(pid) + rss, vms = _psutil_osx.get_memory_info(self.pid) + return ntuple_meminfo(rss, vms) - def get_cpu_times(self, pid): - return _psutil_osx.get_process_cpu_times(pid) + @wrap_exceptions + def get_cpu_times(self): + user, system = _psutil_osx.get_cpu_times(self.pid) + return ntuple_cputimes(user, system) - def get_process_create_time(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_osx.get_process_create_time(pid) + return _psutil_osx.get_process_create_time(self.pid) + + @wrap_exceptions + def get_process_num_threads(self): + """Return the number of threads belonging to the process.""" + return _psutil_osx.get_process_num_threads(self.pid) + + @wrap_exceptions + def get_open_files(self): + """Return files opened by process.""" + if self.pid == 0: + raise AccessDenied(self.pid, self._process_name) + files = [] + rawlist = _psutil_osx.get_process_open_files(self.pid) + for path, fd in rawlist: + if os.path.isfile(path): + ntuple = ntuple_openfile(path, fd) + files.append(ntuple) + return files + + @wrap_exceptions + def get_connections(self): + """Return etwork connections opened by a process as a list of + namedtuples.""" + retlist = _psutil_osx.get_process_connections(self.pid) + return [ntuple_connection(*conn) for conn in retlist] + @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 = ntuple_thread(thread_id, utime, stime) + retlist.append(ntuple) + return retlist - def get_pid_list(self): - """Returns a list of PIDs currently running on the system.""" - return _psutil_osx.get_pid_list() - def pid_exists(self, pid): - """Check For the existence of a unix pid.""" - if pid < 0: - return False - try: - os.kill(pid, 0) - except OSError, e: - return e.errno == errno.EPERM - else: - return True +PlatformProcess = OSXProcess diff -Nru python-psutil-0.1.3/psutil/_psposix.py python-psutil-0.2.1/psutil/_psposix.py --- python-psutil-0.1.3/psutil/_psposix.py 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psposix.py 2011-02-18 00:17:59.000000000 +0000 @@ -0,0 +1,293 @@ +#!/usr/bin/env python +# +# $Id: _psposix.py 924 2011-02-18 00:17:55Z g.rodola $ +# + +"""Routines common to all posix systems.""" + +import os +import errno +import subprocess +import psutil +import socket +import re +import sys +import warnings +import time + +from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._compat import namedtuple + + +def pid_exists(pid): + """Check whether pid exists in the current process table.""" + if pid < 0: + return False + try: + os.kill(pid, 0) + except OSError, e: + return e.errno == errno.EPERM + else: + return True + +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(): + if timeout: + if time.time() >= stop_at: + raise TimeoutExpired + time.sleep(0.001) + + if timeout: + waitcall = lambda: os.waitpid(pid, os.WNOHANG) + stop_at = time.time() + timeout + else: + waitcall = lambda: os.waitpid(pid, 0) + + while 1: + try: + retpid, status = waitcall() + except OSError, err: + if err.errno == errno.EINTR: + check_timeout() + continue + elif err.errno == errno.ECHILD: + # not a child of os.getpid(): poll until pid has + # disappeared and return None instead + while 1: + if pid_exists(pid): + check_timeout() + else: + return + else: + raise + else: + if retpid == 0: + check_timeout() + 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") + + +class LsofParser: + """A wrapper for lsof command line utility. + Executes lsof in subprocess and parses its output. + """ + socket_table = {'TCP' : socket.SOCK_STREAM, + 'UDP' : socket.SOCK_DGRAM, + 'IPv4' : socket.AF_INET, + 'IPv6' : socket.AF_INET6} + _openfile_ntuple = namedtuple('openfile', 'path fd') + _connection_ntuple = namedtuple('connection', 'fd family type local_address ' + 'remote_address status') + + def __init__(self, pid, name): + self.pid = pid + self.process_name = name + + def get_process_open_files(self): + """Return files opened by process by parsing lsof output.""" + # Options: + # -i == network files only + # -a == ANDing of all options + # -p == process with given PID only + # -n == do not resolve IP addresses + # -P == do not resolve port numbers + # -w == suppresses warnings + # -F0nPt == (0) separate lines with "\x00" + # (n) file name + # (t) file type + # (f) file descriptr + cmd = "lsof -a -p %s -n -P -F0ftn" % self.pid + stdout = self.runcmd(cmd) + if not stdout: + return [] + files = [] + lines = stdout.split("\n") + del lines[0] # first line contains the PID + for line in lines: + if not line: + continue + line = line.strip("\x00") + fields = {} + for field in line.split("\x00"): + key, value = field[0], field[1:] + fields[key] = value + if not 't' in fields: + continue + _type = fields['t'] + fd = fields['f'] + name = fields['n'] + if 'REG' in _type and fd.isdigit(): + if not os.path.isfile(os.path.realpath(name)): + continue + ntuple = self._openfile_ntuple(name, int(fd)) + files.append(ntuple) + return files + + def get_process_connections(self): + """Return connections opened by a process by parsing lsof output.""" + # Options: + # -i == network files only + # -a == ANDing of all options + # -p == process with given PID only + # -n == do not resolve IP addresses + # -P == do not resolve port numbers + # -w == suppresses warnings + # -F0nPt == (0) separate lines with "\x00" + # (n) and show internet addresses only + # (P) protocol type (TCP, UPD, Unix) + # (t) socket family (IPv4, IPv6) + # (T) connection status + # (f) file descriptors + cmd = "lsof -p %s -i -a -F0nPtTf -n -P" % self.pid + stdout = self.runcmd(cmd) + if not stdout: + return [] + connections = [] + lines = stdout.split() + del lines[0] # first line contains the PID + for line in lines: + line = line.strip("\x00") + fields = {} + for field in line.split("\x00"): + if field.startswith('T'): + key, value = field.split('=') + else: + key, value = field[0], field[1:] + fields[key] = value + + # XXX - might trow execption; needs "continue on unsupported + # family or type" (e.g. unix sockets) + # we consider TCP and UDP sockets only + stype = fields['P'] + if stype not in self.socket_table: + continue + else: + _type = self.socket_table[fields['P']] + family = self.socket_table[fields['t']] + peers = fields['n'] + fd = int(fields['f']) + if _type == socket.SOCK_STREAM: + status = fields['TST'] + # OS X shows "CLOSED" instead of "CLOSE" so translate them + if status == "CLOSED": + status = "CLOSE" + else: + status = "" + if not '->' in peers: + local_addr = self._normaddress(peers, family) + remote_addr = () + # OS X processes e.g. SystemUIServer can return *:* for local + # address, so we return 0 and move on + if local_addr == 0: + continue + else: + local_addr, remote_addr = peers.split("->") + local_addr = self._normaddress(local_addr, family) + remote_addr = self._normaddress(remote_addr, family) + + conn = self._connection_ntuple(fd, family, _type, local_addr, + remote_addr, status) + connections.append(conn) + + return connections + + def runcmd(self, cmd): + """Expects an lsof-related command line, execute it in a + subprocess and return its output. + If something goes bad stderr is parsed and proper exceptions + raised as necessary. + """ + p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + if sys.version_info >= (3,): + stdout, stderr = map(lambda x: x.decode(sys.stdout.encoding), + (stdout, stderr)) + if stderr: + utility = cmd.split(' ')[0] + if self._which(utility) is None: + msg = "this functionnality requires %s command line utility " \ + "to be installed on the system" % utility + raise NotImplementedError(msg) + elif "permission denied" in stderr.lower(): + # "permission denied" can be found also in case of zombie + # processes; + p = psutil.Process(self.pid) + if not p.is_running(): + raise NoSuchProcess(self.pid, self.process_name) + raise AccessDenied(self.pid, self.process_name) + elif "lsof: warning:" in stderr.lower(): + # usually appears when lsof is run for the first time and + # complains about missing cache file in user home + warnings.warn(stderr, RuntimeWarning) + else: + # this must be considered an application bug + raise RuntimeError(stderr) + if not stdout: + p = psutil.Process(self.pid) + if not p.is_running(): + raise NoSuchProcess(self.pid, self.process_name) + return "" + return stdout + + @staticmethod + def _which(program): + """Same as UNIX which command. Return None on command not found.""" + def is_exe(fpath): + return os.path.isfile(fpath) and os.access(fpath, os.X_OK) + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + if is_exe(exe_file): + return exe_file + return None + + @staticmethod + def _normaddress(addr, family): + """Normalize an IP address.""" + assert family in (socket.AF_INET, socket.AF_INET6), "unsupported family" + if family == socket.AF_INET: + ip, port = addr.split(':') + else: + if "]" in addr: + ip, port = re.findall('\[([^]]+)\]:([0-9]+)', addr)[0] + else: + ip, port = addr.split(':') + if ip == '*': + if family == socket.AF_INET: + ip = "0.0.0.0" + elif family == socket.AF_INET6: + ip = "::" + # OS X can have some procs e.g. SystemUIServer listening on *:* + else: + raise ValueError("invalid IP %s" %addr) + if port == "*": + return 0 + return (ip, int(port)) + + diff -Nru python-psutil-0.1.3/psutil/_psutil_bsd.c python-psutil-0.2.1/psutil/_psutil_bsd.c --- python-psutil-0.1.3/psutil/_psutil_bsd.c 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_bsd.c 2011-03-04 17:05:27.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $Id: _psutil_bsd.c 502 2010-02-21 12:33:41Z billiejoex@gmail.com $ + * $Id: _psutil_bsd.c 944 2011-03-04 17:05:24Z g.rodola $ * * FreeBSD platform-specific module methods for _psutil_bsd */ @@ -12,129 +12,56 @@ #include #include #include +#include #include #include #include /* needed for vmtotal struct */ #include "_psutil_bsd.h" +#include "_psutil_common.h" #include "arch/bsd/process_info.h" -static PyObject *NoSuchProcessException; +// convert a timeval struct to a double +#define TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) + /* - * define the psutil C module methods and initialize the module. + * Utility function which fills a kinfo_proc struct based on process pid */ -static PyMethodDef PsutilMethods[] = +static int +get_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) { - {"get_pid_list", get_pid_list, METH_VARARGS, - "Returns a list of PIDs currently running on the system"}, - {"get_process_info", get_process_info, METH_VARARGS, - "Return a tuple containing a set of information about the " - "process (pid, ppid, name, path, cmdline)"}, - {"get_cpu_times", get_cpu_times, METH_VARARGS, - "Return tuple of user/kern time for the given PID"}, - {"get_num_cpus", get_num_cpus, METH_VARARGS, - "Return number of CPUs on the system"}, - {"get_process_create_time", get_process_create_time, METH_VARARGS, - "Return a float indicating the process create time expressed in " - "seconds since the epoch"}, - {"get_memory_info", get_memory_info, METH_VARARGS, - "Return a tuple of RSS/VMS memory information"}, - {"get_total_phymem", get_total_phymem, METH_VARARGS, - "Return the total amount of physical memory, in bytes"}, - {"get_avail_phymem", get_avail_phymem, METH_VARARGS, - "Return the amount of available physical memory, in bytes"}, - {"get_total_virtmem", get_total_virtmem, METH_VARARGS, - "Return the total amount of virtual memory, in bytes"}, - {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, - "Return the amount of available virtual memory, in bytes"}, - {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, - "Return system cpu times as a tuple (user, system, nice, idle, irc)"}, - {NULL, NULL, 0, NULL} -}; - - -struct module_state { - PyObject *error; -}; - -#if PY_MAJOR_VERSION >= 3 -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -#else -#define GETSTATE(m) (&_state) -static struct module_state _state; -#endif - -#if PY_MAJOR_VERSION >= 3 - -static int psutil_bsd_traverse(PyObject *m, visitproc visit, void *arg) { - Py_VISIT(GETSTATE(m)->error); - return 0; -} - -static int psutil_bsd_clear(PyObject *m) { - Py_CLEAR(GETSTATE(m)->error); - return 0; -} - - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "psutil_bsd", - NULL, - sizeof(struct module_state), - PsutilMethods, - NULL, - psutil_bsd_traverse, - psutil_bsd_clear, - NULL -}; - -#define INITERROR return NULL - -PyObject * -PyInit__psutil_bsd(void) - -#else -#define INITERROR return + int mib[4]; + size_t size; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; -void -init_psutil_bsd(void) -#endif -{ -#if PY_MAJOR_VERSION >= 3 - PyObject *module = PyModule_Create(&moduledef); -#else - PyObject *module = Py_InitModule("_psutil_bsd", PsutilMethods); -#endif - if (module == NULL) - INITERROR; - struct module_state *st = GETSTATE(module); + size = sizeof(struct kinfo_proc); - st->error = PyErr_NewException("_psutil_bsd.Error", NULL, NULL); - if (st->error == NULL) { - Py_DECREF(module); - INITERROR; + if (sysctl((int*)mib, 4, proc, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; } - NoSuchProcessException = PyErr_NewException("_psutil_bsd.NoSuchProcess", - NULL, NULL); - Py_INCREF(NoSuchProcessException); - PyModule_AddObject(module, "NoSuchProcess", NoSuchProcessException); - -#if PY_MAJOR_VERSION >= 3 - return module; -#endif + /* + * 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) +static PyObject* +get_pid_list(PyObject* self, PyObject* args) { kinfo_proc *proclist = NULL; kinfo_proc *orig_address = NULL; @@ -165,144 +92,305 @@ /* - * Return a Python tuple containing a set of information about the process: - * (pid, ppid, name, path, cmdline). + * Return a Python float indicating the system boot time expressed in + * seconds since the epoch. */ -static PyObject* get_process_info(PyObject* self, PyObject* args) +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; - int mib[4]; - size_t len; + 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; - long pid; - PyObject* infoTuple = NULL; - PyObject* arglist = NULL; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("s", kp.ki_comm); +} - // the argument passed should be a process id - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, "Invalid argument - no PID provided."); - } - if (0 == pid) { - // USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND - // root 0 0.0 0.0 0 0 ?? DLs 12:22AM 0:00.13 [swapper] - return Py_BuildValue("llssNll", pid, 0, "swapper", "", Py_BuildValue("[]"), 0, 0); +/* + * 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; } - // build the mib to pass to sysctl to tell it what PID and what info we want - len = 4; - sysctlnametomib("kern.proc.pid", mib, &len); mib[0] = CTL_KERN; mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; + mib[2] = KERN_PROC_PATHNAME; mib[3] = pid; - // fetch the info with sysctl() - len = sizeof(kp); - if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) { - // raise an exception if it failed, since we won't get any data - if (ESRCH == errno) { - return PyErr_Format(NoSuchProcessException, "No process found with pid %lu", pid); + size = sizeof(pathname); + error = sysctl(mib, 4, pathname, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if (size == 0 || strlen(pathname) == 0) { + if (pid_exists(pid) == 0) { + return NoSuchProcess(); + } + else { + strcpy(pathname, ""); } + } + 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); +} - if (len > 0) { // if 0 then no data was retrieved - // get the commandline, since we got everything else - arglist = get_arg_list(pid); +/* + * 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); +} - // get_arg_list() returns NULL only if getcmdargs failed with ESRCH - // (no process with that PID) - if (NULL == arglist) { - return PyErr_Format(NoSuchProcessException, - "No such process found with pid %lu", pid); - } - // hooray, we got all the data, so return it as a tuple to be passed to - // ProcessInfo() constructor - infoTuple = Py_BuildValue("llssNll", pid, (long)kp.ki_ppid, kp.ki_comm, "", - arglist, (long)kp.ki_ruid, (long)kp.ki_rgid); - - if (NULL == infoTuple) { - PyErr_SetString(PyExc_RuntimeError, - "Failed to build process information tuple!"); - } - return infoTuple; +/* + * 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); +} + - // something went wrong, throw an error - return PyErr_Format(PyExc_RuntimeError, - "Failed to retrieve process information."); +/* + * 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); } -// convert a timeval struct to a double -#define TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) + +/* + * 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 a Python tuple (user_time, kernel_time) + * Return number of threads used by process as a Python integer. */ -static PyObject* get_cpu_times(PyObject* self, PyObject* args) +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]; - size_t len; - struct kinfo_proc kp; - long pid; - double user_t, sys_t; - PyObject* timeTuple = NULL; + struct kinfo_proc *kip; + struct kinfo_proc *kipp; + int error; + unsigned int i; + size_t size; + PyObject* retList = PyList_New(0); + PyObject* pyTuple = NULL; - // the argument passed should be a process id - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, - "Invalid argument - no PID provided."); - } + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } - // build the mib to pass to sysctl to tell it what PID and what info we want - len = 4; - sysctlnametomib("kern.proc.pid", mib, &len); + /* + * 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; + mib[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD; mib[3] = pid; - // fetch the info with sysctl() - len = sizeof(kp); - if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) { - // raise an exception if it failed, since we won't get any data - if (ESRCH == errno) { - return PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); - } - return PyErr_SetFromErrno(PyExc_OSError); + size = 0; + error = sysctl(mib, 4, NULL, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if (size == 0) { + return NoSuchProcess(); } - if (len > 0) { // if 0 then no data was retrieved - user_t = TV2DOUBLE(kp.ki_rusage.ru_utime); - sys_t = TV2DOUBLE(kp.ki_rusage.ru_stime); - - // convert from microseconds to seconds - timeTuple = Py_BuildValue("(dd)", user_t, sys_t); - if (NULL == timeTuple) { - PyErr_SetString(PyExc_RuntimeError, - "Failed to build process CPU times tuple!"); - } - return timeTuple; + kip = malloc(size); + if (kip == NULL) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; } - // something went wrong, throw an error - return PyErr_Format(PyExc_RuntimeError, - "Failed to retrieve process CPU times."); + error = sysctl(mib, 4, kip, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + if (size == 0) { + return NoSuchProcess(); + } + + 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) + ); + PyList_Append(retList, pyTuple); + Py_XDECREF(pyTuple); + } + free(kip); + return retList; } /* - * Return a Python integer indicating the number of CPUs on the system + * Return a Python tuple (user_time, kernel_time) */ -static PyObject* get_num_cpus(PyObject* self, PyObject* args) +static PyObject* +get_cpu_times(PyObject* self, PyObject* args) { + long pid; + double user_t, sys_t; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + // convert from microseconds to seconds + user_t = TV2DOUBLE(kp.ki_rusage.ru_utime); + sys_t = TV2DOUBLE(kp.ki_rusage.ru_stime); + return Py_BuildValue("(dd)", user_t, sys_t); +} + +/* + * Return a Python integer indicating the number of CPUs on the system + */ +static PyObject* +get_num_cpus(PyObject* self, PyObject* args) +{ int mib[2]; int ncpu; size_t len; @@ -324,68 +412,59 @@ * 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) +static PyObject* +get_process_create_time(PyObject* self, PyObject* args) { - int mib[4]; long pid; - size_t len; 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)); +} - // the argument passed should be a process id - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, - "Invalid argument - no PID provided."); - } - len = 4; - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = pid; - len = sizeof(kp); - if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) { - return PyErr_SetFromErrno(PyExc_OSError); +/* + * 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 (len > 0) { - return Py_BuildValue("d", TV2DOUBLE(kp.ki_start)); + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; } - - return PyErr_Format(PyExc_RuntimeError, "Unable to read process start time."); + // 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 the RSS and VMS as a Python tuple. */ -static PyObject* get_memory_info(PyObject* self, PyObject* args) +static PyObject* +get_memory_info(PyObject* self, PyObject* args) { - int mib[4]; long pid; - size_t len; struct kinfo_proc kp; - - // the argument passed should be a process id - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, - "Invalid argument - no PID provided."); - } - - len = 4; - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = pid; - len = sizeof(kp); - if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) { - return PyErr_SetFromErrno(PyExc_OSError); + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; } - - if (len > 0) { - return Py_BuildValue("(ll)", ptoa(kp.ki_rssize), (long)kp.ki_size); + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; } - - return PyErr_Format(PyExc_RuntimeError, "Unable to read process start time."); + return Py_BuildValue("(ll)", ptoa(kp.ki_rssize), (long)kp.ki_size); } @@ -393,7 +472,8 @@ * Return a Python integer indicating the total amount of physical memory * in bytes. */ -static PyObject* get_total_phymem(PyObject* self, PyObject* args) +static PyObject* +get_total_phymem(PyObject* self, PyObject* args) { long total_phymem; int mib[2]; @@ -416,7 +496,8 @@ * Return a Python long indicating the amount of available physical memory in * bytes. */ -static PyObject* get_avail_phymem(PyObject* self, PyObject* args) +static PyObject* +get_avail_phymem(PyObject* self, PyObject* args) { unsigned long v_inactive_count = 0; unsigned long v_cache_count = 0; @@ -461,7 +542,8 @@ * Return a Python long indicating the total amount of virtual memory * in bytes. */ -static PyObject* get_total_virtmem(PyObject* self, PyObject* args) +static PyObject* +get_total_virtmem(PyObject* self, PyObject* args) { int mib[2]; struct vmtotal vm; @@ -485,7 +567,8 @@ * Return a Python long indicating the avail amount of virtual memory * in bytes. */ -static PyObject* get_avail_virtmem(PyObject* self, PyObject* args) +static PyObject* +get_avail_virtmem(PyObject* self, PyObject* args) { int mib[2]; struct vmtotal vm; @@ -510,7 +593,8 @@ /* * Return a Python tuple representing user, kernel and idle CPU times */ -static PyObject* get_system_cpu_times(PyObject* self, PyObject* args) +static PyObject* +get_system_cpu_times(PyObject* self, PyObject* args) { long cpu_time[CPUSTATES]; size_t size; @@ -539,3 +623,142 @@ (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC ); } + + +/* + * 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_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_cpu_times", get_cpu_times, METH_VARARGS, + "Return tuple of user/kern time for the given PID"}, + {"get_process_create_time", get_process_create_time, METH_VARARGS, + "Return a float indicating the process create time expressed in " + "seconds since the epoch"}, + {"get_memory_info", get_memory_info, METH_VARARGS, + "Return a tuple of RSS/VMS memory information"}, + {"get_process_num_threads", get_process_num_threads, METH_VARARGS, + "Return number of threads used by process"}, + {"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"}, + + + // --- system-related functions + + {"get_pid_list", get_pid_list, METH_VARARGS, + "Returns a list of PIDs currently running on the system"}, + {"get_num_cpus", get_num_cpus, METH_VARARGS, + "Return number of CPUs on the system"}, + {"get_total_phymem", get_total_phymem, METH_VARARGS, + "Return the total amount of physical memory, in bytes"}, + {"get_avail_phymem", get_avail_phymem, METH_VARARGS, + "Return the amount of available physical memory, in bytes"}, + {"get_total_virtmem", get_total_virtmem, METH_VARARGS, + "Return the total amount of virtual memory, in bytes"}, + {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, + "Return the amount of available virtual memory, in bytes"}, + {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, + "Return system cpu times as a tuple (user, system, nice, idle, irc)"}, + {"get_system_boot_time", get_system_boot_time, METH_VARARGS, + "Return a float indicating the system boot time expressed in " + "seconds since the epoch"}, + + {NULL, NULL, 0, NULL} +}; + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +static struct module_state _state; +#endif + +#if PY_MAJOR_VERSION >= 3 + +static int +psutil_bsd_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +static int +psutil_bsd_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + +static struct PyModuleDef +moduledef = { + PyModuleDef_HEAD_INIT, + "psutil_bsd", + NULL, + sizeof(struct module_state), + PsutilMethods, + NULL, + psutil_bsd_traverse, + psutil_bsd_clear, + NULL +}; + +#define INITERROR return NULL + +PyObject * +PyInit__psutil_bsd(void) + +#else +#define INITERROR return + +void init_psutil_bsd(void) +#endif +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); +#else + PyObject *module = Py_InitModule("_psutil_bsd", PsutilMethods); +#endif + 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; + } + struct module_state *st = GETSTATE(module); + + st->error = PyErr_NewException("_psutil_bsd.Error", NULL, NULL); + if (st->error == NULL) { + Py_DECREF(module); + INITERROR; + } +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} + diff -Nru python-psutil-0.1.3/psutil/_psutil_bsd.h python-psutil-0.2.1/psutil/_psutil_bsd.h --- python-psutil-0.1.3/psutil/_psutil_bsd.h 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_bsd.h 2011-03-04 17:05:27.000000000 +0000 @@ -1,19 +1,35 @@ /* - * $Id: _psutil_bsd.h 342 2009-04-17 14:03:19Z g.rodola $ + * $Id: _psutil_bsd.h 944 2011-03-04 17:05:24Z g.rodola $ * * BSD platform-specific module methods for _psutil_bsd */ #include -static PyObject* get_pid_list(PyObject* self, PyObject* args); -static PyObject* get_process_info(PyObject* self, PyObject* args); +// --- per-process functions + static PyObject* get_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_process_name(PyObject* self, PyObject* args); +static PyObject* get_process_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_create_time(PyObject* self, PyObject* args); -static PyObject* get_num_cpus(PyObject* self, PyObject* args); static PyObject* get_memory_info(PyObject* self, PyObject* args); +static PyObject* get_process_num_threads(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); + +// --- system-related functions + +static PyObject* get_pid_list(PyObject* self, PyObject* args); +static PyObject* get_num_cpus(PyObject* self, PyObject* args); static PyObject* get_total_phymem(PyObject* self, PyObject* args); static PyObject* get_avail_phymem(PyObject* self, PyObject* args); static PyObject* get_total_virtmem(PyObject* self, PyObject* args); static PyObject* get_avail_virtmem(PyObject* self, PyObject* args); static PyObject* get_system_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_system_boot_time(PyObject* self, PyObject* args); + diff -Nru python-psutil-0.1.3/psutil/_psutil_common.c python-psutil-0.2.1/psutil/_psutil_common.c --- python-psutil-0.1.3/psutil/_psutil_common.c 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_common.c 2011-01-12 18:47:31.000000000 +0000 @@ -0,0 +1,35 @@ +/* + * $Id: _psutil_common.c 831 2010-11-20 21:53:59Z g.rodola $ + * + * Routines common to all platforms. + */ + +#include + + +/* + * 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; +} + diff -Nru python-psutil-0.1.3/psutil/_psutil_common.h python-psutil-0.2.1/psutil/_psutil_common.h --- python-psutil-0.1.3/psutil/_psutil_common.h 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_common.h 2011-01-12 18:47:31.000000000 +0000 @@ -0,0 +1,9 @@ +/* + * $Id: _psutil_common.h 831 2010-11-20 21:53:59Z g.rodola $ + */ + +#include + +PyObject* NoSuchProcess(void); +PyObject* AccessDenied(void); + diff -Nru python-psutil-0.1.3/psutil/_psutil_linux.c python-psutil-0.2.1/psutil/_psutil_linux.c --- python-psutil-0.1.3/psutil/_psutil_linux.c 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_linux.c 2011-01-13 21:42:59.000000000 +0000 @@ -0,0 +1,166 @@ +/* + * $Id: _psutil_linux.c 892 2011-01-13 21:42:53Z g.rodola $ + * + * Linux-specific functions. + */ + +#include +#include +#include +#include +#include + +#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 + +/* + * 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 + {NULL, NULL, 0, NULL} +}; + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +static struct module_state _state; +#endif + +#if PY_MAJOR_VERSION >= 3 + +static int +psutil_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; + } + struct module_state *st = GETSTATE(module); +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} + diff -Nru python-psutil-0.1.3/psutil/_psutil_linux.h python-psutil-0.2.1/psutil/_psutil_linux.h --- python-psutil-0.1.3/psutil/_psutil_linux.h 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_linux.h 2011-01-13 21:42:59.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * $Id: _psutil_linux.h 892 2011-01-13 21:42:53Z g.rodola $ + * + * LINUX specific module methods for _psutil_linux + */ + +#include + +static PyObject* linux_ioprio_get(PyObject* self, PyObject* args); +static PyObject* linux_ioprio_set(PyObject* self, PyObject* args); + diff -Nru python-psutil-0.1.3/psutil/_psutil_mswindows.c python-psutil-0.2.1/psutil/_psutil_mswindows.c --- python-psutil-0.1.3/psutil/_psutil_mswindows.c 2009-09-14 21:45:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_mswindows.c 2011-03-20 20:21:47.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $Id: _psutil_mswindows.c 527 2010-03-01 01:12:58Z jloden $ + * $Id: _psutil_mswindows.c 952 2011-03-20 20:06:20Z g.rodola $ * * Windows platform-specific module methods for _psutil_mswindows */ @@ -9,138 +9,26 @@ #include #include #include +#include #include +#include #include "_psutil_mswindows.h" +#include "_psutil_common.h" #include "arch/mswindows/security.h" #include "arch/mswindows/process_info.h" - - -static PyObject *NoSuchProcessException; - -static PyMethodDef PsutilMethods[] = -{ - {"get_pid_list", get_pid_list, METH_VARARGS, - "Returns a list of PIDs currently running on the system"}, - {"get_process_info", get_process_info, METH_VARARGS, - "Return a tuple containing a set of information about the " - "process (pid, ppid, name, path, cmdline)"}, - {"kill_process", kill_process, METH_VARARGS, - "Kill the process identified by the given PID"}, - {"pid_exists", pid_exists, METH_VARARGS, - "Determine if the process exists in the current process list."}, - {"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_num_cpus", get_num_cpus, METH_VARARGS, - "Returns the number of CPUs on the system"}, - {"get_system_uptime", get_system_uptime, METH_VARARGS, - "Return system uptime"}, - {"get_memory_info", get_memory_info, METH_VARARGS, - "Return a tuple of RSS/VMS memory information"}, - {"get_total_phymem", get_total_phymem, METH_VARARGS, - "Return the total amount of physical memory, in bytes"}, - {"get_total_virtmem", get_total_virtmem, METH_VARARGS, - "Return the total amount of virtual memory, in bytes"}, - {"get_avail_phymem", get_avail_phymem, METH_VARARGS, - "Return the amount of available physical memory, in bytes"}, - {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, - "Return the amount of available virtual memory, in bytes"}, - {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, - "Return system cpu times as a tuple (user, system, idle)"}, - {"get_process_cwd", get_process_cwd, METH_VARARGS, - "Return process current working directory"}, - {"suspend_process", suspend_process, METH_VARARGS, - "Suspend a process"}, - {"resume_process", resume_process, METH_VARARGS, - "Resume a process"}, - {NULL, NULL, 0, NULL} -}; - -struct module_state { - PyObject *error; -}; - -#if PY_MAJOR_VERSION >= 3 -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -#else -#define GETSTATE(m) (&_state) -static struct module_state _state; -#endif - -#if PY_MAJOR_VERSION >= 3 - -static int psutil_mswindows_traverse(PyObject *m, visitproc visit, void *arg) { - Py_VISIT(GETSTATE(m)->error); - return 0; -} - -static int psutil_mswindows_clear(PyObject *m) { - Py_CLEAR(GETSTATE(m)->error); - return 0; -} - - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "psutil_mswindows", - NULL, - sizeof(struct module_state), - PsutilMethods, - NULL, - psutil_mswindows_traverse, - psutil_mswindows_clear, - NULL -}; - -#define INITERROR return NULL - -PyObject * -PyInit__psutil_mswindows(void) - -#else -#define INITERROR return - -void -init_psutil_mswindows(void) -#endif -{ -#if PY_MAJOR_VERSION >= 3 - PyObject *module = PyModule_Create(&moduledef); -#else - PyObject *module = Py_InitModule("_psutil_mswindows", PsutilMethods); -#endif - if (module == NULL) - INITERROR; - struct module_state *st = GETSTATE(module); - - st->error = PyErr_NewException("_psutil_mswindow.Error", NULL, NULL); - if (st->error == NULL) { - Py_DECREF(module); - INITERROR; - } - - NoSuchProcessException = PyErr_NewException("_psutil_mswindows.NoSuchProcess", - NULL, NULL); - Py_INCREF(NoSuchProcessException); - PyModule_AddObject(module, "NoSuchProcess", NoSuchProcessException); - SetSeDebug(); - -#if PY_MAJOR_VERSION >= 3 - return module; -#endif -} +#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) +static PyObject* +get_system_uptime(PyObject* self, PyObject* args) { - float uptime; + double uptime; time_t pt; FILETIME fileTime; long long ll; @@ -165,29 +53,28 @@ // XXX - By using GetTickCount() time will wrap around to zero if the // system is run continuously for 49.7 days. - uptime = GetTickCount() / 1000.00; - return Py_BuildValue("f", (float)pt - uptime); + 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) +static PyObject* +pid_exists(PyObject* self, PyObject* args) { long pid; int status; - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, - "Invalid argument - no PID provided."); - } + 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); } @@ -195,23 +82,23 @@ /* * Return a Python list of all the PIDs running on the system. */ -static PyObject* get_pid_list(PyObject* self, PyObject* args) +static PyObject* +get_pid_list(PyObject* self, PyObject* args) { DWORD *proclist = NULL; DWORD numberOfReturnedPIDs; DWORD i; PyObject* pid = NULL; - PyObject* retlist = PyList_New(0); + PyObject* retlist = PyList_New(0); proclist = get_pids(&numberOfReturnedPIDs); if (NULL == proclist) { Py_DECREF(retlist); - PyErr_SetString(PyExc_RuntimeError, "get_pids() failed in get_pid_list()"); return NULL; } for (i = 0; i < numberOfReturnedPIDs; i++) { - pid = Py_BuildValue("i", proclist[i]); + pid = Py_BuildValue("I", proclist[i]); PyList_Append(retlist, pid); Py_XDECREF(pid); } @@ -226,97 +113,136 @@ /* * Kill a process given its PID. */ -static PyObject* kill_process(PyObject* self, PyObject* args) +static PyObject* +kill_process(PyObject* self, PyObject* args) { HANDLE hProcess; long pid; - int pid_return; - PyObject* ret; - ret = PyLong_FromLong(0); - if (! PyArg_ParseTuple(args, "l", &pid)) { - PyErr_SetString(PyExc_RuntimeError, "Invalid argument"); + if (! PyArg_ParseTuple(args, "l", &pid)) return NULL; - } + if (pid == 0) + return AccessDenied(); - if (pid < 0) { + 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; } - pid_return = pid_is_running(pid); - if (pid_return == 0) { - return PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); + // kill the process + if (! TerminateProcess(hProcess, 0)) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hProcess); + return NULL; } - if (pid_return == -1) { - return NULL; // exception raised from within pid_is_running() - } + CloseHandle(hProcess); + Py_INCREF(Py_None); + return Py_None; +} - // get a process handle - hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); - if (hProcess == NULL) { - PyErr_SetFromWindowsErr(0); + +/* + * 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(); + if (timeout == 0) + timeout = INFINITE; + + 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; + } } - // kill the process - if (! TerminateProcess(hProcess, 0) ){ - PyErr_SetFromWindowsErr(0); + // 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 NULL; + return PyErr_SetFromWindowsErr(GetLastError()); + } + if (retVal == WAIT_TIMEOUT) { + CloseHandle(hProcess); + return Py_BuildValue("i", -1); } + // 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); - return PyLong_FromLong(1); +#if PY_MAJOR_VERSION >= 3 + return PyLong_FromLong((int) ExitCode); +#else + return PyInt_FromLong((int) ExitCode); +#endif } /* * Return a Python tuple (user_time, kernel_time) */ -static PyObject* get_process_cpu_times(PyObject* self, PyObject* args) +static PyObject* +get_process_cpu_times(PyObject* self, PyObject* args) { long pid; HANDLE hProcess; FILETIME ftCreate, ftExit, ftKernel, ftUser; - DWORD ProcessExitCode = 0; if (! PyArg_ParseTuple(args, "l", &pid)) { - PyErr_SetString(PyExc_RuntimeError, "Invalid argument"); return NULL; } // special case for PID 0 if (0 == pid){ - return Py_BuildValue("(dd)", 0.0, 0.0); + return Py_BuildValue("(dd)", 0.0, 0.0); } - hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); - if (hProcess == NULL){ - PyErr_SetFromWindowsErr(0); - if (GetLastError() == ERROR_INVALID_PARAMETER) { - // bad PID so no such process - PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); - } + hProcess = handle_from_pid(pid); + if (hProcess == NULL) { return NULL; } - /* make sure the process is running */ - GetExitCodeProcess(hProcess, &ProcessExitCode); - if (ProcessExitCode == 0) { - return PyErr_Format(NoSuchProcessException, "No Process found with pid %lu", pid); - } - if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { - PyErr_SetFromWindowsErr(0); if (GetLastError() == ERROR_ACCESS_DENIED) { // usually means the process has died so we throw a NoSuchProcess // here - PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); + CloseHandle(hProcess); + return NoSuchProcess(); } + PyErr_SetFromWindowsErr(0); CloseHandle(hProcess); return NULL; } @@ -334,12 +260,12 @@ Modules/posixmodule.c */ - return Py_BuildValue( - "(dd)", - (double)(ftUser.dwHighDateTime*429.4967296 + \ - ftUser.dwLowDateTime*1e-7), - (double)(ftKernel.dwHighDateTime*429.4967296 + \ - ftKernel.dwLowDateTime*1e-7) + return Py_BuildValue( + "(dd)", + (double)(ftUser.dwHighDateTime*429.4967296 + \ + ftUser.dwLowDateTime*1e-7), + (double)(ftKernel.dwHighDateTime*429.4967296 + \ + ftKernel.dwLowDateTime*1e-7) ); } @@ -348,47 +274,35 @@ * 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) +static PyObject* +get_process_create_time(PyObject* self, PyObject* args) { long pid; long long unix_time; HANDLE hProcess; FILETIME ftCreate, ftExit, ftKernel, ftUser; - DWORD ProcessExitCode = 0; if (! PyArg_ParseTuple(args, "l", &pid)) { - PyErr_SetString(PyExc_RuntimeError, "Invalid argument"); return NULL; } // special case for PIDs 0 and 4 if ( (0 == pid) || (4 == pid) ){ - return Py_BuildValue("d", 0.0); + return Py_BuildValue("d", 0.0); } - hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); - if (hProcess == NULL){ - PyErr_SetFromWindowsErr(0); - if (GetLastError() == ERROR_INVALID_PARAMETER) { - // bad PID so no such process - PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); - } + hProcess = handle_from_pid(pid); + if (hProcess == NULL) { return NULL; } - /* make sure the process is running */ - GetExitCodeProcess(hProcess, &ProcessExitCode); - if (ProcessExitCode == 0) { - return PyErr_Format(NoSuchProcessException, "No Process found with pid %lu", pid); - } - if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { - PyErr_SetFromWindowsErr(0); if (GetLastError() == ERROR_ACCESS_DENIED) { // usually means the process has died so we throw a NoSuchProcess here - PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); + return NoSuchProcess(); + } + else { + PyErr_SetFromWindowsErr(0); } CloseHandle(hProcess); return NULL; @@ -404,14 +318,15 @@ unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32; unix_time += ftCreate.dwLowDateTime - 116444736000000000LL; unix_time /= 10000000; - return Py_BuildValue("f", (float)unix_time); + 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) +static PyObject* +get_num_cpus(PyObject* self, PyObject* args) { SYSTEM_INFO system_info; system_info.dwNumberOfProcessors = 0; @@ -419,78 +334,93 @@ 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", 1); } - return Py_BuildValue("i", system_info.dwNumberOfProcessors); + return Py_BuildValue("I", system_info.dwNumberOfProcessors); } - /* - * Return a Python tuple containing a set of information about the process: - * (pid, ppid, name, path, cmdline). + * Return process name as a Python string. */ -static PyObject* get_process_info(PyObject* self, PyObject* args) -{ - // the argument passed should be a process id - long pid; +static PyObject* +get_process_name(PyObject* self, PyObject* args) { + long pid; int pid_return; - PyObject* infoTuple; - PyObject* ppid; - PyObject* arglist; PyObject* name; - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, - "get_process_info(): Invalid argument - no PID provided."); - } - - // special case for PID 0 (System Idle Process) - if (0 == pid) { - arglist = Py_BuildValue("[]"); - infoTuple = Py_BuildValue("llssNll", pid, 0, "System Idle Process", "", - arglist, -1, -1); - return infoTuple; - } - - // special case for PID 4 (System) - if (4 == pid) { - arglist = Py_BuildValue("[]"); - infoTuple = Py_BuildValue("llssNll", pid, 0, "System", "", arglist, -1, -1); - return infoTuple; + 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"); } - // check if the process exists before we waste time trying to read info pid_return = pid_is_running(pid); if (pid_return == 0) { - return PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); + return NoSuchProcess(); } - if (pid_return == -1) { - return NULL; // exception raised from within pid_is_running() + return NULL; } + name = get_name(pid); + if (name == NULL) + return NULL; // exception set in get_name() + return name; +} - // Now fetch the actual properties of the process - ppid = get_ppid(pid); - if ( NULL == ppid ) { - return NULL; // exception string set in get_ppid() - } - name = get_name(pid); - if ( NULL == name ) { - return NULL; //exception string set in get_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 get_name or get_pid returns None that means the process is dead - if (Py_None == name) { - return PyErr_Format(NoSuchProcessException, - "Process with pid %lu has disappeared", pid); + 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 (Py_None == ppid) { - return PyErr_Format(NoSuchProcessException, - "Process with pid %lu has disappeared", pid); + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if ((pid == 0) || (pid == 4)) + return Py_BuildValue("[]"); + + pid_return = pid_is_running(pid); + if (pid_return == 0) { + return NoSuchProcess(); + } + if (pid_return == -1) { + return NULL; } // May fail any of several ReadProcessMemory calls etc. and not indicate @@ -499,48 +429,37 @@ if ( NULL == arglist ) { // carry on anyway, clear any exceptions too PyErr_Clear(); - arglist = Py_BuildValue("[]"); + return Py_BuildValue("[]"); } - infoTuple = Py_BuildValue("lNNsNll", pid, ppid, name, "", arglist, -1, -1); - if (infoTuple == NULL) { - PyErr_SetString(PyExc_RuntimeError, "error building info tuple"); - } - return infoTuple; + return arglist; } /* * Return the RSS and VMS as a Python tuple. */ -static PyObject* get_memory_info(PyObject* self, PyObject* args) +static PyObject* +get_memory_info(PyObject* self, PyObject* args) { HANDLE hProcess; PROCESS_MEMORY_COUNTERS counters; DWORD pid; - DWORD ProcessExitCode = 0; - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, - "Invalid argument - no PID provided."); - } + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } hProcess = handle_from_pid(pid); if (NULL == hProcess) { - return PyErr_SetFromWindowsErr(0); - } - - /* make sure the process is running */ - GetExitCodeProcess(hProcess, &ProcessExitCode); - if (ProcessExitCode == 0) { - return PyErr_Format(NoSuchProcessException, "No Process found with pid %lu", pid); + return NULL; } if (! GetProcessMemoryInfo(hProcess, &counters, sizeof(counters)) ) { return PyErr_SetFromWindowsErr(0); } - return Py_BuildValue("(ll)", counters.WorkingSetSize, counters.PagefileUsage); + return Py_BuildValue("(nn)", counters.WorkingSetSize, counters.PagefileUsage); } @@ -548,7 +467,8 @@ * Return a Python integer indicating the total amount of physical memory * in bytes. */ -static PyObject* get_total_phymem(PyObject* self, PyObject* args) +static PyObject* +get_total_phymem(PyObject* self, PyObject* args) { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); @@ -565,7 +485,8 @@ * Return a Python integer indicating the total amount of virtual memory * in bytes. */ -static PyObject* get_total_virtmem(PyObject* self, PyObject* args) +static PyObject* +get_total_virtmem(PyObject* self, PyObject* args) { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); @@ -582,7 +503,8 @@ * Return a Python integer indicating the amount of available physical memory * in bytes. */ -static PyObject* get_avail_phymem(PyObject* self, PyObject* args) +static PyObject* +get_avail_phymem(PyObject* self, PyObject* args) { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); @@ -597,7 +519,8 @@ * Return a Python integer indicating the amount of available virtual memory * in bytes. */ -static PyObject* get_avail_virtmem(PyObject* self, PyObject* args) +static PyObject* +get_avail_virtmem(PyObject* self, PyObject* args) { MEMORYSTATUSEX memInfo; memInfo.dwLength = sizeof(MEMORYSTATUSEX); @@ -612,157 +535,131 @@ #define LO_T ((float)1e-7) #define HI_T (LO_T*4294967296.0) -// structures and enums from winternl.h (not available under mingw) -typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { - LARGE_INTEGER IdleTime; - LARGE_INTEGER KernelTime; - LARGE_INTEGER UserTime; - LARGE_INTEGER Reserved1[2]; - ULONG Reserved2; -} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; - - -typedef enum _SYSTEM_INFORMATION_CLASS { - SystemBasicInformation = 0, - SystemPerformanceInformation = 2, - SystemTimeOfDayInformation = 3, - SystemProcessInformation = 5, - SystemProcessorPerformanceInformation = 8, - SystemInterruptInformation = 23, - SystemExceptionInformation = 33, - SystemRegistryQuotaInformation = 37, - SystemLookasideInformation = 45 -} SYSTEM_INFORMATION_CLASS; - /* * Return a Python tuple representing user, kernel and idle CPU times */ -static PyObject* get_system_cpu_times(PyObject* self, PyObject* args) +static PyObject* +get_system_cpu_times(PyObject* self, PyObject* args) { - typedef BOOL (_stdcall *GST_PROC) (LPFILETIME, LPFILETIME, LPFILETIME); - static GST_PROC GetSystemTimes; - static BOOL bFirstCall = TRUE; - float idle, kernel, user; - - // Improves performance calling GetProcAddress only the first time - if (bFirstCall) - { + typedef BOOL (_stdcall *GST_PROC) (LPFILETIME, LPFILETIME, LPFILETIME); + static GST_PROC GetSystemTimes; + static BOOL bFirstCall = TRUE; + float idle, kernel, user; + + // Improves performance calling GetProcAddress only the first time + if (bFirstCall) { // retrieves GetSystemTimes address in Kernel32 - GetSystemTimes=(GST_PROC)GetProcAddress(GetModuleHandle + GetSystemTimes=(GST_PROC)GetProcAddress(GetModuleHandle (TEXT("Kernel32.dll")), "GetSystemTimes"); - bFirstCall = FALSE; - } + bFirstCall = FALSE; + } // Uses GetSystemTimes if supported (winXP sp1+) - if (NULL!=GetSystemTimes) - { - // GetSystemTimes supported - - FILETIME idle_time; - FILETIME kernel_time; - FILETIME user_time; - - if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) - { - return PyErr_SetFromWindowsErr(0); - } + if (NULL!=GetSystemTimes) { + // GetSystemTimes supported + + FILETIME idle_time; + FILETIME kernel_time; + FILETIME user_time; + + if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) { + return PyErr_SetFromWindowsErr(0); + } - idle = (float)((HI_T * idle_time.dwHighDateTime) + \ + idle = (float)((HI_T * idle_time.dwHighDateTime) + \ (LO_T * idle_time.dwLowDateTime)); - user = (float)((HI_T * user_time.dwHighDateTime) + \ + user = (float)((HI_T * user_time.dwHighDateTime) + \ (LO_T * user_time.dwLowDateTime)); - kernel = (float)((HI_T * kernel_time.dwHighDateTime) + \ + kernel = (float)((HI_T * kernel_time.dwHighDateTime) + \ (LO_T * kernel_time.dwLowDateTime)); // kernel time includes idle time on windows // we return only busy kernel time subtracting idle time from kernel time - return Py_BuildValue("(ddd)", user, + return Py_BuildValue("(fff)", user, kernel - idle, idle); - } - else - { + } + + else { // GetSystemTimes NOT supported, use NtQuerySystemInformation instead - typedef DWORD (_stdcall *NTQSI_PROC) (int, PVOID, ULONG, PULONG); - NTQSI_PROC NtQuerySystemInformation; - HINSTANCE hNtDll; - SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; - SYSTEM_INFO si; - UINT i; - - // dynamic linking is mandatory to use NtQuerySystemInformation - hNtDll = LoadLibrary(TEXT("ntdll.dll")); - if (hNtDll != NULL) - { - // gets NtQuerySystemInformation address - NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress( + typedef DWORD (_stdcall *NTQSI_PROC) (int, PVOID, ULONG, PULONG); + NTQSI_PROC NtQuerySystemInformation; + HINSTANCE hNtDll; + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; + SYSTEM_INFO si; + UINT i; + + // dynamic linking is mandatory to use NtQuerySystemInformation + hNtDll = LoadLibrary(TEXT("ntdll.dll")); + if (hNtDll != NULL) { + // gets NtQuerySystemInformation address + NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress( hNtDll, "NtQuerySystemInformation"); - if (NtQuerySystemInformation != NULL) - { - // retrives number of processors - GetSystemInfo(&si); + if (NtQuerySystemInformation != NULL) + { + // retrives number of processors + GetSystemInfo(&si); - // allocates an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION + // allocates an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION // structures, one per processor - sppi=(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ - malloc(si.dwNumberOfProcessors * \ + 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 %s\n"), d, szTarget); + if(_tcscmp(lpDevicePath, szTarget) == 0) { + _stprintf(szBuff, TEXT("%c:"), d); + return Py_BuildValue("s", szBuff); + } + } + d++; + } + return Py_BuildValue("s", ""); +} + +/* + * Return process username as a "DOMAIN//USERNAME" string. + */ +static PyObject* +get_process_username(PyObject* self, PyObject* args) +{ + long pid; + HANDLE processHandle; + HANDLE tokenHandle; + PTOKEN_USER user; + ULONG bufferSize; + PTSTR name; + ULONG nameSize; + PTSTR domainName; + ULONG domainNameSize; + SID_NAME_USE nameUse; + PTSTR fullName; + PyObject* returnObject; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + processHandle = handle_from_pid_waccess(pid, PROCESS_QUERY_INFORMATION); + if (processHandle == NULL) { + return NULL; + } + + if (!OpenProcessToken(processHandle, TOKEN_QUERY, &tokenHandle)) { + CloseHandle(processHandle); + return PyErr_SetFromWindowsErr(0); + } + + CloseHandle(processHandle); + + /* Get the user SID. */ + + bufferSize = 0x100; + user = malloc(bufferSize); + + if (!GetTokenInformation(tokenHandle, + TokenUser, + user, + bufferSize, + &bufferSize)) + { + free(user); + user = malloc(bufferSize); + + if (!GetTokenInformation(tokenHandle, + TokenUser, + user, + bufferSize, + &bufferSize)) + { + free(user); + CloseHandle(tokenHandle); + return PyErr_SetFromWindowsErr(0); + } + } + + CloseHandle(tokenHandle); + + /* Resolve the SID to a name. */ + + nameSize = 0x100; + domainNameSize = 0x100; + + name = malloc(nameSize * sizeof(TCHAR)); + domainName = malloc(domainNameSize * sizeof(TCHAR)); + + if (!LookupAccountSid(NULL, user->User.Sid, name, &nameSize, domainName, + &domainNameSize, &nameUse)) + { + free(name); + free(domainName); + name = malloc(nameSize * sizeof(TCHAR)); + domainName = malloc(domainNameSize * sizeof(TCHAR)); + + if (!LookupAccountSid(NULL, user->User.Sid, name, &nameSize, domainName, + &domainNameSize, &nameUse)) + { + free(name); + free(domainName); + free(user); + + return PyErr_SetFromWindowsErr(0); + } + } + + nameSize = _tcslen(name); + domainNameSize = _tcslen(domainName); + + /* Build the full username string. */ + fullName = malloc((domainNameSize + 1 + nameSize + 1) * sizeof(TCHAR)); + memcpy(fullName, domainName, domainNameSize); + fullName[domainNameSize] = '\\'; + memcpy(&fullName[domainNameSize + 1], name, nameSize); + fullName[domainNameSize + 1 + nameSize] = '\0'; + + returnObject = Py_BuildValue("s", fullName); + + free(fullName); + free(name); + free(domainName); + free(user); + + return returnObject; +} + +#define BYTESWAP_USHORT(x) ((((USHORT)(x) << 8) | ((USHORT)(x) >> 8)) & 0xffff) + +#ifndef AF_INET6 +#define AF_INET6 23 +#endif + +static char *state_to_string(ULONG state) +{ + switch (state) + { + case MIB_TCP_STATE_CLOSED: + return "CLOSE"; + case MIB_TCP_STATE_LISTEN: + return "LISTEN"; + case MIB_TCP_STATE_SYN_SENT: + return "SYN_SENT"; + case MIB_TCP_STATE_SYN_RCVD: + return "SYN_RECV"; + case MIB_TCP_STATE_ESTAB: + return "ESTABLISHED"; + case MIB_TCP_STATE_FIN_WAIT1: + return "FIN_WAIT1"; + case MIB_TCP_STATE_FIN_WAIT2: + return "FIN_WAIT2"; + case MIB_TCP_STATE_CLOSE_WAIT: + return "CLOSE_WAIT"; + case MIB_TCP_STATE_CLOSING: + return "CLOSING"; + case MIB_TCP_STATE_LAST_ACK: + return "LAST_ACK"; + case MIB_TCP_STATE_TIME_WAIT: + return "TIME_WAIT"; + case MIB_TCP_STATE_DELETE_TCB: + return "DELETE_TCB"; + default: + return ""; + } +} + +/* mingw support */ +#ifndef _IPRTRMIB_H +typedef struct _MIB_TCP6ROW_OWNER_PID +{ + UCHAR ucLocalAddr[16]; + DWORD dwLocalScopeId; + DWORD dwLocalPort; + UCHAR ucRemoteAddr[16]; + DWORD dwRemoteScopeId; + DWORD dwRemotePort; + DWORD dwState; + DWORD dwOwningPid; +} MIB_TCP6ROW_OWNER_PID, *PMIB_TCP6ROW_OWNER_PID; + +typedef struct _MIB_TCP6TABLE_OWNER_PID +{ + DWORD dwNumEntries; + MIB_TCP6ROW_OWNER_PID table[ANY_SIZE]; +} MIB_TCP6TABLE_OWNER_PID, *PMIB_TCP6TABLE_OWNER_PID; +#endif + +#ifndef __IPHLPAPI_H__ +typedef struct in6_addr { + union { + UCHAR Byte[16]; + USHORT Word[8]; + } u; +} IN6_ADDR, *PIN6_ADDR, FAR *LPIN6_ADDR; + + +typedef enum _UDP_TABLE_CLASS { + UDP_TABLE_BASIC, + UDP_TABLE_OWNER_PID, + UDP_TABLE_OWNER_MODULE +} UDP_TABLE_CLASS, *PUDP_TABLE_CLASS; + + +typedef struct _MIB_UDPROW_OWNER_PID { + DWORD dwLocalAddr; + DWORD dwLocalPort; + DWORD dwOwningPid; +} MIB_UDPROW_OWNER_PID, *PMIB_UDPROW_OWNER_PID; + +typedef struct _MIB_UDPTABLE_OWNER_PID +{ + DWORD dwNumEntries; + MIB_UDPROW_OWNER_PID table[ANY_SIZE]; +} MIB_UDPTABLE_OWNER_PID, *PMIB_UDPTABLE_OWNER_PID; +#endif +/* end of mingw support */ + +typedef struct _MIB_UDP6ROW_OWNER_PID { + UCHAR ucLocalAddr[16]; + DWORD dwLocalScopeId; + DWORD dwLocalPort; + DWORD dwOwningPid; +} MIB_UDP6ROW_OWNER_PID, *PMIB_UDP6ROW_OWNER_PID; + +typedef struct _MIB_UDP6TABLE_OWNER_PID +{ + DWORD dwNumEntries; + MIB_UDP6ROW_OWNER_PID table[ANY_SIZE]; +} MIB_UDP6TABLE_OWNER_PID, *PMIB_UDP6TABLE_OWNER_PID; + + +/* + * Return a list of network connections opened by a process + */ +static PyObject* +get_process_connections(PyObject* self, PyObject* args) +{ + static long null_address[4] = { 0, 0, 0, 0 }; + + unsigned long pid; + PyObject* connectionsList; + PyObject* connectionTuple; + typedef PSTR (NTAPI *_RtlIpv4AddressToStringA)(struct in_addr *, + PSTR /* __out_ecount(16) */); + _RtlIpv4AddressToStringA rtlIpv4AddressToStringA; + typedef PSTR (NTAPI *_RtlIpv6AddressToStringA)(struct in6_addr *, + PSTR /* __out_ecount(65) */); + _RtlIpv6AddressToStringA rtlIpv6AddressToStringA; + typedef DWORD (WINAPI *_GetExtendedTcpTable)(PVOID, PDWORD, BOOL, ULONG, + TCP_TABLE_CLASS, ULONG); + _GetExtendedTcpTable getExtendedTcpTable; + typedef DWORD (WINAPI *_GetExtendedUdpTable)(PVOID, PDWORD, BOOL, ULONG, + UDP_TABLE_CLASS, ULONG); + _GetExtendedUdpTable getExtendedUdpTable; + PVOID table; + DWORD tableSize; + PMIB_TCPTABLE_OWNER_PID tcp4Table; + PMIB_UDPTABLE_OWNER_PID udp4Table; + PMIB_TCP6TABLE_OWNER_PID tcp6Table; + PMIB_UDP6TABLE_OWNER_PID udp6Table; + ULONG i; + CHAR addressBufferLocal[65]; + PyObject* addressTupleLocal; + CHAR addressBufferRemote[65]; + PyObject* addressTupleRemote; + + if (!PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + if (pid_is_running(pid) == 0) { + return NoSuchProcess(); + } + + /* Import some functions. */ + { + HMODULE ntdll; + HMODULE iphlpapi; + + ntdll = LoadLibrary(TEXT("ntdll.dll")); + rtlIpv4AddressToStringA = (_RtlIpv4AddressToStringA)GetProcAddress(ntdll, + "RtlIpv4AddressToStringA"); + rtlIpv6AddressToStringA = (_RtlIpv6AddressToStringA)GetProcAddress(ntdll, + "RtlIpv6AddressToStringA"); + /* TODO: Check these two function pointers */ + + iphlpapi = LoadLibrary(TEXT("iphlpapi.dll")); + getExtendedTcpTable = (_GetExtendedTcpTable)GetProcAddress(iphlpapi, + "GetExtendedTcpTable"); + getExtendedUdpTable = (_GetExtendedUdpTable)GetProcAddress(iphlpapi, + "GetExtendedUdpTable"); + } + + if ((getExtendedTcpTable == NULL) || (getExtendedUdpTable == NULL)) { + PyErr_SetString(PyExc_NotImplementedError, + "feature not supported on this Windows version"); + return NULL; + } + + connectionsList = PyList_New(0); + + /* TCP IPv4 */ + + tableSize = 0; + getExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET, + TCP_TABLE_OWNER_PID_ALL, 0); + + table = malloc(tableSize); + + if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET, + TCP_TABLE_OWNER_PID_ALL, 0) == 0) + { + tcp4Table = table; + + for (i = 0; i < tcp4Table->dwNumEntries; i++) + { + if (tcp4Table->table[i].dwOwningPid != pid) + continue; + + if (tcp4Table->table[i].dwLocalAddr != 0 || + tcp4Table->table[i].dwLocalPort != 0) + { + struct in_addr addr; + + addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr; + rtlIpv4AddressToStringA(&addr, addressBufferLocal); + addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, + BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort)); + } + else + { + addressTupleLocal = PyTuple_New(0); + } + + // On Windows <= XP, remote addr is filled even if socket + // is in LISTEN mode in which case we just ignore it. + if ((tcp4Table->table[i].dwRemoteAddr != 0 || + tcp4Table->table[i].dwRemotePort != 0) && + (tcp4Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) + { + struct in_addr addr; + + addr.S_un.S_addr = tcp4Table->table[i].dwRemoteAddr; + rtlIpv4AddressToStringA(&addr, addressBufferRemote); + addressTupleRemote = Py_BuildValue("(si)", addressBufferRemote, + BYTESWAP_USHORT(tcp4Table->table[i].dwRemotePort)); + } + else + { + addressTupleRemote = PyTuple_New(0); + } + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET, + SOCK_STREAM, + addressTupleLocal, + addressTupleRemote, + state_to_string(tcp4Table->table[i].dwState) + ); + PyList_Append(connectionsList, connectionTuple); + } + } + + free(table); + + /* TCP IPv6 */ + + tableSize = 0; + getExtendedTcpTable(NULL, &tableSize, FALSE, AF_INET6, + TCP_TABLE_OWNER_PID_ALL, 0); + + table = malloc(tableSize); + + if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET6, + TCP_TABLE_OWNER_PID_ALL, 0) == 0) + { + tcp6Table = table; + + for (i = 0; i < tcp6Table->dwNumEntries; i++) + { + if (tcp6Table->table[i].dwOwningPid != pid) { + continue; + } + + if (memcmp(tcp6Table->table[i].ucLocalAddr, null_address, 16) != 0 || + tcp6Table->table[i].dwLocalPort != 0) + { + struct in6_addr addr; + + memcpy(&addr, tcp6Table->table[i].ucLocalAddr, 16); + rtlIpv6AddressToStringA(&addr, addressBufferLocal); + addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, + BYTESWAP_USHORT(tcp6Table->table[i].dwLocalPort)); + } + else + { + addressTupleLocal = PyTuple_New(0); + } + + // On Windows <= XP, remote addr is filled even if socket + // is in LISTEN mode in which case we just ignore it. + if ((memcmp(tcp6Table->table[i].ucRemoteAddr, null_address, 16) != 0 || + tcp6Table->table[i].dwRemotePort != 0) && + (tcp6Table->table[i].dwState != MIB_TCP_STATE_LISTEN)) + { + struct in6_addr addr; + + memcpy(&addr, tcp6Table->table[i].ucRemoteAddr, 16); + rtlIpv6AddressToStringA(&addr, addressBufferRemote); + addressTupleRemote = Py_BuildValue("(si)", addressBufferRemote, + BYTESWAP_USHORT(tcp6Table->table[i].dwRemotePort)); + } + else + { + addressTupleRemote = PyTuple_New(0); + } + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET6, + SOCK_STREAM, + addressTupleLocal, + addressTupleRemote, + state_to_string(tcp6Table->table[i].dwState) + ); + PyList_Append(connectionsList, connectionTuple); + } + } + + free(table); + + /* UDP IPv4 */ + + tableSize = 0; + getExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET, + UDP_TABLE_OWNER_PID, 0); + + table = malloc(tableSize); + + if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET, + UDP_TABLE_OWNER_PID, 0) == 0) + { + udp4Table = table; + + for (i = 0; i < udp4Table->dwNumEntries; i++) + { + if (udp4Table->table[i].dwOwningPid != pid) + continue; + + if (udp4Table->table[i].dwLocalAddr != 0 || + udp4Table->table[i].dwLocalPort != 0) + { + struct in_addr addr; + + addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr; + rtlIpv4AddressToStringA(&addr, addressBufferLocal); + addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, + BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort)); + } + else + { + addressTupleLocal = PyTuple_New(0); + } + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET, + SOCK_DGRAM, + addressTupleLocal, + PyTuple_New(0), + "" + ); + PyList_Append(connectionsList, connectionTuple); + } + } + + free(table); + + /* UDP IPv6 */ + + tableSize = 0; + getExtendedUdpTable(NULL, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_PID, 0); + + table = malloc(tableSize); + + if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET6, UDP_TABLE_OWNER_PID, 0) == 0) + { + udp6Table = table; + + for (i = 0; i < udp6Table->dwNumEntries; i++) + { + if (udp6Table->table[i].dwOwningPid != pid) { + continue; + } + + if (memcmp(udp6Table->table[i].ucLocalAddr, null_address, 16) != 0 || + udp6Table->table[i].dwLocalPort != 0) + { + struct in6_addr addr; + + memcpy(&addr, udp6Table->table[i].ucLocalAddr, 16); + rtlIpv6AddressToStringA(&addr, addressBufferLocal); + addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, + BYTESWAP_USHORT(udp6Table->table[i].dwLocalPort)); + } + else + { + addressTupleLocal = PyTuple_New(0); + } + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET6, + SOCK_DGRAM, + addressTupleLocal, + PyTuple_New(0), + "" + ); + PyList_Append(connectionsList, connectionTuple); + } + } + + free(table); + + return connectionsList; +} + + +/* + * Get process priority as a Python integer. + */ +static PyObject* +get_process_priority(PyObject* self, PyObject* args) +{ + long pid; + DWORD priority; + HANDLE hProcess; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + hProcess = handle_from_pid(pid); + if (hProcess == NULL) { + return NULL; + } + + priority = GetPriorityClass(hProcess); + if (priority == 0) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + return Py_BuildValue("i", priority); +} + + +/* + * Set process priority. + */ +static PyObject* +set_process_priority(PyObject* self, PyObject* args) +{ + long pid; + int priority; + int retval; + HANDLE hProcess; + DWORD dwDesiredAccess = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION; + if (! PyArg_ParseTuple(args, "li", &pid, &priority)) { + return NULL; + } + + hProcess = handle_from_pid_waccess(pid, dwDesiredAccess); + if (hProcess == NULL) { + return NULL; + } + + retval = SetPriorityClass(hProcess, priority); + if (retval == 0) { PyErr_SetFromWindowsErr(0); return NULL; } Py_INCREF(Py_None); return Py_None; } + + +/* + * Return a Python tuple referencing process I/O counters. + */ +static PyObject* +get_process_io_counters(PyObject* self, PyObject* args) +{ + DWORD pid; + HANDLE hProcess; + IO_COUNTERS IoCounters; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (pid == 0) { + return AccessDenied(); + } + hProcess = handle_from_pid(pid); + if (NULL == hProcess) { + return NULL; + } + if (! GetProcessIoCounters(hProcess, &IoCounters)) { + return PyErr_SetFromWindowsErr(0); + } + return Py_BuildValue("(KKKK)", IoCounters.ReadOperationCount, + IoCounters.WriteOperationCount, + IoCounters.ReadTransferCount, + IoCounters.WriteTransferCount); +} + + +/* + * Return True if one of the process threads is in a waiting or + * suspended status. + */ +static PyObject* +is_process_suspended(PyObject* self, PyObject* args) +{ + DWORD pid; + ULONG i; + PSYSTEM_PROCESS_INFORMATION process; + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if (get_process_info(pid, &process) != 1) + return NULL; + if (pid_is_running(pid) == 0) + return NoSuchProcess(); + + for (i = 0; i < process->NumberOfThreads; i++) { + if (process->Threads[i].ThreadState != Waiting || + process->Threads[i].WaitReason != Suspended) + { + Py_RETURN_FALSE; + } + } + Py_RETURN_TRUE; +} + + + +// ------------------------ Python init --------------------------- + +static PyMethodDef +PsutilMethods[] = +{ + // --- per-process functions + + {"get_process_name", get_process_name, METH_VARARGS, + "Return process name"}, + {"get_process_cmdline", get_process_cmdline, METH_VARARGS, + "Return process cmdline as a list of cmdline arguments"}, + {"get_process_ppid", get_process_ppid, METH_VARARGS, + "Return process ppid as an integer"}, + {"kill_process", kill_process, METH_VARARGS, + "Kill the process identified by the given PID"}, + {"get_process_cpu_times", get_process_cpu_times, METH_VARARGS, + "Return tuple of user/kern time for the given PID"}, + {"get_process_create_time", get_process_create_time, METH_VARARGS, + "Return a float indicating the process create time expressed in " + "seconds since the epoch"}, + {"get_memory_info", get_memory_info, METH_VARARGS, + "Return a tuple of RSS/VMS memory information"}, + {"get_process_cwd", get_process_cwd, METH_VARARGS, + "Return process current working directory"}, + {"suspend_process", suspend_process, METH_VARARGS, + "Suspend a process"}, + {"resume_process", resume_process, METH_VARARGS, + "Resume a process"}, + {"get_process_open_files", get_process_open_files, METH_VARARGS, + "Return files opened by process"}, + {"_QueryDosDevice", _QueryDosDevice, METH_VARARGS, + "QueryDosDevice binding"}, + {"get_process_username", get_process_username, METH_VARARGS, + "Return the username of a process"}, + {"get_process_connections", get_process_connections, METH_VARARGS, + "Return the network connections of a process"}, + {"get_process_num_threads", get_process_num_threads, METH_VARARGS, + "Return the network connections of a process"}, + {"get_process_threads", get_process_threads, METH_VARARGS, + "Return process threads information as a list of tuple"}, + {"process_wait", process_wait, METH_VARARGS, + "Wait for process to terminate and return its exit code."}, + {"get_process_priority", get_process_priority, METH_VARARGS, + "Return process priority."}, + {"set_process_priority", set_process_priority, METH_VARARGS, + "Set process priority."}, + {"get_process_io_counters", get_process_io_counters, METH_VARARGS, + "Get process I/O counters."}, + {"is_process_suspended", is_process_suspended, METH_VARARGS, + "Return True if one of the process threads is in a suspended state"}, + + + // --- system-related functions + + {"get_pid_list", get_pid_list, METH_VARARGS, + "Returns a list of PIDs currently running on the system"}, + {"pid_exists", pid_exists, METH_VARARGS, + "Determine if the process exists in the current process list."}, + {"get_num_cpus", get_num_cpus, METH_VARARGS, + "Returns the number of CPUs on the system"}, + {"get_system_uptime", get_system_uptime, METH_VARARGS, + "Return system uptime"}, + {"get_total_phymem", get_total_phymem, METH_VARARGS, + "Return the total amount of physical memory, in bytes"}, + {"get_total_virtmem", get_total_virtmem, METH_VARARGS, + "Return the total amount of virtual memory, in bytes"}, + {"get_avail_phymem", get_avail_phymem, METH_VARARGS, + "Return the amount of available physical memory, in bytes"}, + {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, + "Return the amount of available virtual memory, in bytes"}, + {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, + "Return system cpu times as a tuple (user, system, idle)"}, + + {NULL, NULL, 0, NULL} +}; + + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 + #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else + #define GETSTATE(m) (&_state) + static struct module_state _state; +#endif + +#if PY_MAJOR_VERSION >= 3 + + static int psutil_mswindows_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; + } + + static int psutil_mswindows_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; + } + + static struct PyModuleDef moduledef = { + PyModuleDef_HEAD_INIT, + "psutil_mswindows", + NULL, + sizeof(struct module_state), + PsutilMethods, + NULL, + psutil_mswindows_traverse, + psutil_mswindows_clear, + NULL + }; + +#define INITERROR return NULL + + PyObject* PyInit__psutil_mswindows(void) + +#else + #define INITERROR return + void init_psutil_mswindows(void) +#endif +{ + struct module_state *st = NULL; +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); +#else + PyObject *module = Py_InitModule("_psutil_mswindows", PsutilMethods); +#endif + + if (module == NULL) { + INITERROR; + } + + st = GETSTATE(module); + st->error = PyErr_NewException("_psutil_mswindow.Error", NULL, NULL); + if (st->error == NULL) { + Py_DECREF(module); + INITERROR; + } + + // Public constants + // http://msdn.microsoft.com/en-us/library/ms683211(v=vs.85).aspx + PyModule_AddIntConstant(module, "ABOVE_NORMAL_PRIORITY_CLASS", + ABOVE_NORMAL_PRIORITY_CLASS); + PyModule_AddIntConstant(module, "BELOW_NORMAL_PRIORITY_CLASS", + BELOW_NORMAL_PRIORITY_CLASS); + PyModule_AddIntConstant(module, "HIGH_PRIORITY_CLASS", + HIGH_PRIORITY_CLASS); + PyModule_AddIntConstant(module, "IDLE_PRIORITY_CLASS", + IDLE_PRIORITY_CLASS); + PyModule_AddIntConstant(module, "NORMAL_PRIORITY_CLASS", + NORMAL_PRIORITY_CLASS); + PyModule_AddIntConstant(module, "REALTIME_PRIORITY_CLASS", + REALTIME_PRIORITY_CLASS); + SetSeDebug(); + +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} + + + diff -Nru python-psutil-0.1.3/psutil/_psutil_mswindows.h python-psutil-0.2.1/psutil/_psutil_mswindows.h --- python-psutil-0.1.3/psutil/_psutil_mswindows.h 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_mswindows.h 2011-02-22 04:00:05.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $Id: _psutil_mswindows.h 496 2010-02-17 20:59:27Z billiejoex $ + * $Id: _psutil_mswindows.h 930 2011-02-22 03:45:38Z g.rodola $ * * Windows platform-specific module methods for _psutil_mswindows * @@ -8,22 +8,42 @@ #include #include -static PyObject* get_pid_list(PyObject* self, PyObject* args); +// --- per-process functions + static PyObject* kill_process(PyObject* self, PyObject* args); -static PyObject* get_process_info(PyObject* self, PyObject* args); -static PyObject* pid_exists(PyObject* self, PyObject* args); +static PyObject* get_process_name(PyObject* self, PyObject* args); +static PyObject* get_process_cmdline(PyObject* self, PyObject* args); +static PyObject* get_process_ppid(PyObject* self, PyObject* args); static PyObject* get_process_cpu_times(PyObject* self, PyObject* args); static PyObject* get_process_create_time(PyObject* self, PyObject* args); +static PyObject* get_memory_info(PyObject* self, PyObject* args); +static PyObject* get_process_cwd(PyObject* self, PyObject* args); +static PyObject* suspend_process(PyObject* self, PyObject* args); +static PyObject* resume_process(PyObject* self, PyObject* args); +static PyObject* get_process_open_files(PyObject* self, PyObject* args); +static PyObject* get_process_username(PyObject* self, PyObject* args); +static PyObject* get_process_connections(PyObject* self, PyObject* args); +static PyObject* get_process_num_threads(PyObject* self, PyObject* args); +static PyObject* get_process_threads(PyObject* self, PyObject* args); +static PyObject* process_wait(PyObject* self, PyObject* args); +static PyObject* get_process_priority(PyObject* self, PyObject* args); +static PyObject* set_process_priority(PyObject* self, PyObject* args); +static PyObject* get_process_io_counters(PyObject* self, PyObject* args); +static PyObject* is_process_suspended(PyObject* self, PyObject* args); + +// --- system-related functions + +static PyObject* get_pid_list(PyObject* self, PyObject* args); static PyObject* get_num_cpus(PyObject* self, PyObject* args); static PyObject* get_system_uptime(PyObject* self, PyObject* args); -static PyObject* get_memory_info(PyObject* self, PyObject* args); static PyObject* get_total_phymem(PyObject* self, PyObject* args); static PyObject* get_total_virtmem(PyObject* self, PyObject* args); static PyObject* get_avail_phymem(PyObject* self, PyObject* args); static PyObject* get_avail_virtmem(PyObject* self, PyObject* args); static PyObject* get_system_cpu_times(PyObject* self, PyObject* args); -static PyObject* get_process_cwd(PyObject* self, PyObject* args); -static PyObject* suspend_process(PyObject* self, PyObject* args); -static PyObject* resume_process(PyObject* self, PyObject* args); +static PyObject* _QueryDosDevice(PyObject* self, PyObject* args); +static PyObject* pid_exists(PyObject* self, PyObject* args); + +// --- others int suspend_resume_process(DWORD pid, int suspend); diff -Nru python-psutil-0.1.3/psutil/_psutil_osx.c python-psutil-0.2.1/psutil/_psutil_osx.c --- python-psutil-0.1.3/psutil/_psutil_osx.c 2009-09-14 21:45:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_osx.c 2011-02-28 21:40:50.000000000 +0000 @@ -1,5 +1,5 @@ /* - * $Id: _psutil_osx.c 524 2010-02-28 01:07:26Z jloden $ + * $Id: _psutil_osx.c 940 2011-02-24 19:19:58Z g.rodola $ * * OS X platform-specific module methods for _psutil_osx */ @@ -10,140 +10,31 @@ #include #include #include -#include #include #include +#include +#include +#include +#include +#include #include #include #include +#include #include #include #include "_psutil_osx.h" +#include "_psutil_common.h" #include "arch/osx/process_info.h" -static PyObject *NoSuchProcessException; -static PyObject *AccessDeniedException; - -/* - * define the psutil C module methods and initialize the module. - */ -static PyMethodDef PsutilMethods[] = -{ - {"get_pid_list", get_pid_list, METH_VARARGS, - "Returns a list of PIDs currently running on the system"}, - {"get_process_info", get_process_info, METH_VARARGS, - "Return a tuple containing a set of information about the " - "process (pid, ppid, name, path, cmdline)"}, - {"get_num_cpus", get_num_cpus, METH_VARARGS, - "Returns the number of CPUs on the system"}, - {"get_process_cpu_times", get_process_cpu_times, METH_VARARGS, - "Return process CPU kernel/user times."}, - {"get_process_create_time", get_process_create_time, METH_VARARGS, - "Return process creation time."}, - {"get_memory_info", get_memory_info, METH_VARARGS, - "Return a tuple of RSS/VMS memory information"}, - {"get_total_phymem", get_total_phymem, METH_VARARGS, - "Return the total amount of physical memory, in bytes"}, - {"get_avail_phymem", get_avail_phymem, METH_VARARGS, - "Return the amount of available physical memory, in bytes"}, - {"get_total_virtmem", get_total_virtmem, METH_VARARGS, - "Return the total amount of virtual memory, in bytes"}, - {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, - "Return the amount of available virtual memory, in bytes"}, - {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, - "Return system cpu times as a tuple (user, nice, system, idle)."}, - {NULL, NULL, 0, NULL} -}; - - - - -struct module_state { - PyObject *error; -}; - -#if PY_MAJOR_VERSION >= 3 -#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) -#else -#define GETSTATE(m) (&_state) -static struct module_state _state; -#endif - -#if PY_MAJOR_VERSION >= 3 - -static int psutil_osx_traverse(PyObject *m, visitproc visit, void *arg) { - Py_VISIT(GETSTATE(m)->error); - return 0; -} - -static int psutil_osx_clear(PyObject *m) { - Py_CLEAR(GETSTATE(m)->error); - return 0; -} - - -static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "psutil_osx", - NULL, - sizeof(struct module_state), - PsutilMethods, - NULL, - psutil_osx_traverse, - psutil_osx_clear, - NULL -}; - -#define INITERROR return NULL - -PyObject * -PyInit__psutil_osx(void) - -#else -#define INITERROR return - -void -init_psutil_osx(void) -#endif -{ -#if PY_MAJOR_VERSION >= 3 - PyObject *module = PyModule_Create(&moduledef); -#else - PyObject *module = Py_InitModule("_psutil_osx", PsutilMethods); -#endif - if (module == NULL) - INITERROR; - struct module_state *st = GETSTATE(module); - - st->error = PyErr_NewException("_psutil_osx.Error", NULL, NULL); - if (st->error == NULL) { - Py_DECREF(module); - INITERROR; - } - - NoSuchProcessException = PyErr_NewException("_psutil_osx.NoSuchProcess", - NULL, NULL); - Py_INCREF(NoSuchProcessException); - PyModule_AddObject(module, "NoSuchProcess", NoSuchProcessException); - AccessDeniedException = PyErr_NewException("_psutil_osx.AccessDenied", - NULL, NULL); - Py_INCREF(AccessDeniedException); - PyModule_AddObject(module, "AccessDenied", AccessDeniedException); - -#if PY_MAJOR_VERSION >= 3 - return module; -#endif -} - - - /* * Return a Python list of all the PIDs running on the system. */ -static PyObject* get_pid_list(PyObject* self, PyObject* args) +static PyObject* +get_pid_list(PyObject* self, PyObject* args) { kinfo_proc *proclist = NULL; kinfo_proc *orig_address = NULL; @@ -174,102 +65,105 @@ /* - * Return 1 if PID exists in the current process list, else 0. + * Return process name from kinfo_proc as a Python string. */ -static int pid_exists(long pid) { - kinfo_proc *procList = NULL; - size_t num_processes; - size_t idx; - int kill_ret; - - // save some time if it's an invalid PID - if (pid < 0) { - return 0; +static PyObject* +get_process_name(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("s", kp.kp_proc.p_comm); +} + + +/* + * Return process cmdline as a Python list of cmdline arguments. + */ +static PyObject* +get_process_cmdline(PyObject* self, PyObject* args) +{ + long pid; + PyObject* arglist = NULL; - // if kill returns success of permission denied we know it's a valid PID - kill_ret = kill(pid , 0); - if ( (0 == kill_ret) || (EPERM == errno) ) { - return 1; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; } - return 0; + // get the commandline, defined in arch/osx/process_info.c + arglist = get_arg_list(pid); + return arglist; } /* - * Return a Python tuple containing a set of information about the process: - * (pid, ppid, name, path, cmdline). + * Return process parent pid from kinfo_proc as a Python integer. */ -static PyObject* get_process_info(PyObject* self, PyObject* args) +static PyObject* +get_process_ppid(PyObject* self, PyObject* args) { - int mib[4]; - size_t len; + long pid; struct kinfo_proc kp; - long pid; - PyObject* arglist = NULL; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.kp_eproc.e_ppid); +} - // the argument passed should be a process id - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, - "Invalid argument - no PID provided."); - } - - // Fill out the first three components of the mib - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - // now the PID we want - mib[3] = pid; - - // fetch the info with sysctl() - len = sizeof(kp); - - // now read the data from sysctl - if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) { - // raise an exception and throw errno as the error - if (ESRCH == errno) { //no such process - return PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); - } - return PyErr_SetFromErrno(NULL); - } - - if (len > 0) { - arglist = get_arg_list(pid); - if (Py_None == arglist) { - return NULL; // exception should already be set from getcmdargs() - } - - return Py_BuildValue("llssNll", - pid, - kp.kp_eproc.e_ppid, - kp.kp_proc.p_comm, - "", - arglist, - kp.kp_eproc.e_pcred.p_ruid, - kp.kp_eproc.e_pcred.p_rgid); - } - - /* - * if sysctl succeeds but len is zero, raise NoSuchProcess as this only - * appears to happen when the process has gone away. - */ - else { - return PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); + +/* + * Return process real uid from kinfo_proc as a Python integer. + */ +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.kp_eproc.e_pcred.p_ruid, + (long)kp.kp_eproc.e_ucred.cr_uid, + (long)kp.kp_eproc.e_pcred.p_svuid); +} + - // something went wrong if we get to this, so throw an exception - return PyErr_Format(PyExc_RuntimeError, - "Failed to retrieve process information."); +/* + * Return process real group id from ki_comm as a Python integer. + */ +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.kp_eproc.e_pcred.p_rgid, + (long)kp.kp_eproc.e_ucred.cr_groups[0], + (long)kp.kp_eproc.e_pcred.p_svgid); } /* * Return a Python integer indicating the number of CPUs on the system. */ -static PyObject* get_num_cpus(PyObject* self, PyObject* args) +static PyObject* +get_num_cpus(PyObject* self, PyObject* args) { int mib[2]; @@ -294,58 +188,59 @@ /* * Return a Python tuple (user_time, kernel_time) */ -static PyObject* get_process_cpu_times(PyObject* self, PyObject* args) +static PyObject* +get_cpu_times(PyObject* self, PyObject* args) { long pid; int err; - int t_utime, t_utime_ms; - int t_stime, t_stime_ms; - int t_cpu; - unsigned int thread_count; - thread_array_t thread_list = NULL; unsigned int info_count = TASK_BASIC_INFO_COUNT; - task_port_t task = (task_port_t)NULL; + task_port_t task; // = (task_port_t)NULL; time_value_t user_time, system_time; struct task_basic_info tasks_info; struct task_thread_times_info task_times; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } - // the argument passed should be a process id - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, - "Invalid argument - no PID provided."); - } - - - /* task_for_pid() requires special privileges + /* task_for_pid() requires special privileges * "This function can be called only if the process is owned by the * procmod group or if the caller is root." - * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html - */ + * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html */ err = task_for_pid(mach_task_self(), pid, &task); if ( err == KERN_SUCCESS) { info_count = TASK_BASIC_INFO_COUNT; - if (task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, - &info_count) != KERN_SUCCESS) { - return PyErr_Format(PyExc_RuntimeError, - "task_info(TASK_BASIC_INFO) failed for pid %lu", pid); + err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); + if (err != KERN_SUCCESS) { + // errcode 4 is "invalid argument" (access denied) + if (err == 4) { + return AccessDenied(); + } + + // otherwise throw a runtime error with appropriate error code + return PyErr_Format(PyExc_RuntimeError, + "task_info(TASK_BASIC_INFO) failed"); } info_count = TASK_THREAD_TIMES_INFO_COUNT; - if (task_info(task, TASK_THREAD_TIMES_INFO, (task_info_t)&task_times, - &info_count) != KERN_SUCCESS) { + err = task_info(task, TASK_THREAD_TIMES_INFO, + (task_info_t)&task_times, &info_count); + if (err != KERN_SUCCESS) { + // errcode 4 is "invalid argument" (access denied) + if (err == 4) { + return AccessDenied(); + } return PyErr_Format(PyExc_RuntimeError, - "task_info(TASK_THREAD_TIMES_INFO) failed for pid %lu", pid); + "task_info(TASK_BASIC_INFO) failed"); } } else { // task_for_pid failed if (! pid_exists(pid) ) { - return PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); + return NoSuchProcess(); } - return PyErr_Format(AccessDeniedException, - "task_for_pid() failed for pid %lu with error %i", pid, err); + // pid exists, so return AccessDenied error since task_for_pid() failed + return AccessDenied(); } float user_t = -1.0; @@ -366,56 +261,31 @@ * 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) +static PyObject* +get_process_create_time(PyObject* self, PyObject* args) { - - int mib[4]; long pid; - size_t len; struct kinfo_proc kp; - - // the argument passed should be a process id - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, - "Invalid argument - no PID provided."); - } - - mib[0] = CTL_KERN; - mib[1] = KERN_PROC; - mib[2] = KERN_PROC_PID; - mib[3] = pid; - len = sizeof(kp); - if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) { - return PyErr_SetFromErrno(PyExc_OSError); - } - - if (len > 0) { - return Py_BuildValue("d", TV2DOUBLE(kp.kp_proc.p_starttime)); + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; } - - /* - * if sysctl succeeds but len is zero, raise NoSuchProcess as this only - * appears to happen when the process has gone away. - */ - else { - return PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; } - - return PyErr_Format(PyExc_RuntimeError, "Unable to read process start time."); + return Py_BuildValue("d", TV2DOUBLE(kp.kp_proc.p_starttime)); } /* * Return a tuple of RSS and VMS memory usage. */ -static PyObject* get_memory_info(PyObject* self, PyObject* args) +static PyObject* +get_memory_info(PyObject* self, PyObject* args) { long pid; int err; unsigned int info_count = TASK_BASIC_INFO_COUNT; - task_port_t task = (task_port_t)NULL; - time_value_t user_time, system_time; + mach_port_t task; struct task_basic_info tasks_info; vm_region_basic_info_data_64_t b_info; vm_address_t address = GLOBAL_SHARED_TEXT_SEGMENT; @@ -423,24 +293,26 @@ mach_port_t object_name; // the argument passed should be a process id - if (! PyArg_ParseTuple(args, "l", &pid)) { - return PyErr_Format(PyExc_RuntimeError, - "Invalid argument - no PID provided."); - } - + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } /* task_for_pid() requires special privileges * "This function can be called only if the process is owned by the * procmod group or if the caller is root." - * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html - */ + * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html */ err = task_for_pid(mach_task_self(), pid, &task); if ( err == KERN_SUCCESS) { info_count = TASK_BASIC_INFO_COUNT; - if (task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, - &info_count) != KERN_SUCCESS) { + err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); + if (err != KERN_SUCCESS) { + if (err == 4) { + // errcode 4 is "invalid argument" (access denied) + return AccessDenied(); + } + // otherwise throw a runtime error with appropriate error code return PyErr_Format(PyExc_RuntimeError, - "task_info(TASK_BASIC_INFO) failed for pid %lu", pid); + "task_info(TASK_BASIC_INFO) failed"); } /* Issue #73 http://code.google.com/p/psutil/issues/detail?id=73 @@ -452,20 +324,20 @@ (vm_region_info_t)&b_info, &info_count, &object_name); if (err == KERN_SUCCESS) { if (b_info.reserved && size == (SHARED_TEXT_REGION_SIZE) && - tasks_info.virtual_size > (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE)) { - tasks_info.virtual_size -= (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE); + tasks_info.virtual_size > (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE)) + { + tasks_info.virtual_size -= (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE); } } - } else { - if (! pid_exists(pid)) { - return PyErr_Format(NoSuchProcessException, - "No process found with pid %lu", pid); + if (! pid_exists(pid) ) { + return NoSuchProcess(); } - return PyErr_Format(AccessDeniedException, - "task_for_pid() failed for pid %lu with error %i", pid, err); + + // pid exists, so return AccessDenied error since task_for_pid() failed + return AccessDenied(); } return Py_BuildValue("(ll)", tasks_info.resident_size, tasks_info.virtual_size); @@ -473,10 +345,69 @@ /* + * Return number of threads used by process as a Python integer. + */ +static PyObject* +get_process_num_threads(PyObject* self, PyObject* args) +{ + long pid; + int err; + unsigned int info_count = TASK_BASIC_INFO_COUNT; + mach_port_t task; + struct task_basic_info tasks_info; + thread_act_port_array_t thread_list; + mach_msg_type_number_t thread_count; + + // the argument passed should be a process id + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + /* task_for_pid() requires special privileges + * "This function can be called only if the process is owned by the + * procmod group or if the caller is root." + * - http://developer.apple.com/documentation/MacOSX/Conceptual/universal_binary/universal_binary_tips/chapter_5_section_19.html + */ + err = task_for_pid(mach_task_self(), pid, &task); + if ( err == KERN_SUCCESS) { + info_count = TASK_BASIC_INFO_COUNT; + err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); + if (err != KERN_SUCCESS) { + // errcode 4 is "invalid argument" (access denied) + if (err == 4) { + return AccessDenied(); + } + + // otherwise throw a runtime error with appropriate error code + return PyErr_Format(PyExc_RuntimeError, + "task_info(TASK_BASIC_INFO) failed"); + } + + err = task_threads(task, &thread_list, &thread_count); + if (err == KERN_SUCCESS) { + return Py_BuildValue("l", (long)thread_count); + } + } + + + else { + if (! pid_exists(pid) ) { + return NoSuchProcess(); + } + + // pid exists, so return AccessDenied error since task_for_pid() failed + return AccessDenied(); + } + return NULL; +} + + +/* * Return a Python integer indicating the total amount of physical memory * in bytes. */ -static PyObject* get_total_phymem(PyObject* self, PyObject* args) +static PyObject* +get_total_phymem(PyObject* self, PyObject* args) { int mib[2]; uint64_t total_phymem; @@ -490,7 +421,6 @@ PyErr_SetFromErrno(0); return NULL; } - return Py_BuildValue("L", total_phymem); } @@ -499,24 +429,25 @@ * Return a Python long indicating the amount of available physical memory in * bytes. */ -static PyObject* get_avail_phymem(PyObject* self, PyObject* args) +static PyObject* +get_avail_phymem(PyObject* self, PyObject* args) { vm_statistics_data_t vm_stat; - mach_msg_type_number_t count; - kern_return_t error; - unsigned long long mem_free; + mach_msg_type_number_t count; + kern_return_t error; + unsigned long long mem_free; int pagesize = getpagesize(); mach_port_t mport = mach_host_self(); - count = sizeof(vm_stat) / sizeof(natural_t); - error = host_statistics(mport, HOST_VM_INFO, (host_info_t)&vm_stat, &count); + count = sizeof(vm_stat) / sizeof(natural_t); + error = host_statistics(mport, HOST_VM_INFO, (host_info_t)&vm_stat, &count); - if (error != KERN_SUCCESS) { - return PyErr_Format(PyExc_RuntimeError, + if (error != KERN_SUCCESS) { + return PyErr_Format(PyExc_RuntimeError, "Error in host_statistics(): %s", mach_error_string(error)); - } + } - mem_free = (unsigned long long) vm_stat.free_count * pagesize; + mem_free = (unsigned long long) vm_stat.free_count * pagesize; return Py_BuildValue("L", mem_free); } @@ -525,7 +456,8 @@ * Return a Python integer indicating the total amount of virtual memory * in bytes. */ -static PyObject* get_total_virtmem(PyObject* self, PyObject* args) +static PyObject* +get_total_virtmem(PyObject* self, PyObject* args) { int mib[2]; size_t size; @@ -547,7 +479,8 @@ * Return a Python integer indicating the avail amount of virtual memory * in bytes. */ -static PyObject* get_avail_virtmem(PyObject* self, PyObject* args) +static PyObject* +get_avail_virtmem(PyObject* self, PyObject* args) { int mib[2]; size_t size; @@ -568,18 +501,570 @@ /* * Return a Python tuple representing user, kernel and idle CPU times */ -static PyObject* get_system_cpu_times(PyObject* self, PyObject* args) +static PyObject* +get_system_cpu_times(PyObject* self, PyObject* args) +{ + mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; + kern_return_t error; + host_cpu_load_info_data_t r_load; + + error = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&r_load, &count); + if (error != KERN_SUCCESS) { + return PyErr_Format(PyExc_RuntimeError, + "Error in host_statistics(): %s", mach_error_string(error)); + } + + return Py_BuildValue("(dddd)", + (double)r_load.cpu_ticks[CPU_STATE_USER] / CLK_TCK, + (double)r_load.cpu_ticks[CPU_STATE_NICE] / CLK_TCK, + (double)r_load.cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK, + (double)r_load.cpu_ticks[CPU_STATE_IDLE] / CLK_TCK + ); +} + + +/* + * 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 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.kp_proc.p_stat); +} + + +/* + * Return process threads + */ +static PyObject* +get_process_threads(PyObject* self, PyObject* args) { - mach_msg_type_number_t count; - struct host_cpu_load_info r_load; + long pid; + int err, j; + kern_return_t kr; + unsigned int info_count = TASK_BASIC_INFO_COUNT; + mach_port_t task; + struct task_basic_info tasks_info; + thread_act_port_array_t thread_list; + thread_info_data_t thinfo; + thread_basic_info_t basic_info_th; + mach_msg_type_number_t thread_count, thread_info_count; + + PyObject* retList = PyList_New(0); + PyObject* pyTuple = NULL; + + // the argument passed should be a process id + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + // task_for_pid() requires special privileges + err = task_for_pid(mach_task_self(), pid, &task); + if (err != KERN_SUCCESS) { + if (! pid_exists(pid) ) { + return NoSuchProcess(); + } + return AccessDenied(); + } - host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, &r_load, &count); + info_count = TASK_BASIC_INFO_COUNT; + err = task_info(task, TASK_BASIC_INFO, (task_info_t)&tasks_info, &info_count); + if (err != KERN_SUCCESS) { + // errcode 4 is "invalid argument" (access denied) + if (err == 4) { + return AccessDenied(); + } + // otherwise throw a runtime error with appropriate error code + return PyErr_Format(PyExc_RuntimeError, + "task_info(TASK_BASIC_INFO) failed"); + } - //user, nice, system, idle, iowait, irqm, softirq - return Py_BuildValue("(dddd)", - (double)r_load.cpu_ticks[CPU_STATE_USER] / CLOCKS_PER_SEC, - (double)r_load.cpu_ticks[CPU_STATE_NICE] / CLOCKS_PER_SEC, - (double)r_load.cpu_ticks[CPU_STATE_SYSTEM] / CLOCKS_PER_SEC, - (double)r_load.cpu_ticks[CPU_STATE_IDLE] / CLOCKS_PER_SEC - ); + err = task_threads(task, &thread_list, &thread_count); + if (err != KERN_SUCCESS) { + return PyErr_Format(PyExc_RuntimeError, "task_threads() failed"); + } + + for (j = 0; j < thread_count; j++) { + thread_info_count = THREAD_INFO_MAX; + kr = thread_info(thread_list[j], THREAD_BASIC_INFO, + (thread_info_t)thinfo, &thread_info_count); + if (kr != KERN_SUCCESS) { + return PyErr_Format(PyExc_RuntimeError, "thread_info() failed"); + } + basic_info_th = (thread_basic_info_t)thinfo; + // XXX - thread_info structure does not provide any process id; + // the best we can do is assigning an incremental bogus value + pyTuple = Py_BuildValue("Iff", j + 1, + (float)basic_info_th->user_time.microseconds / 1000000.0, + (float)basic_info_th->system_time.microseconds / 1000000.0 + ); + PyList_Append(retList, pyTuple); + Py_XDECREF(pyTuple); + } + + return retList; } + + +/* + * Return process open files as a Python tuple. + * References: + * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/m78fd + * - /usr/include/sys/proc_info.h + */ +static PyObject* +get_process_open_files(PyObject* self, PyObject* args) +{ + long pid; + int pidinfo_result; + int iterations; + int i; + int nb; + + struct proc_fdinfo *fds_pointer; + struct proc_fdinfo *fdp_pointer; + struct vnode_fdinfowithpath vi; + + PyObject *retList = PyList_New(0); + PyObject *tuple = NULL; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); + if (pidinfo_result <= 0) { + goto error; + } + + fds_pointer = malloc(pidinfo_result); + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, + pidinfo_result); + free(fds_pointer); + + if (pidinfo_result <= 0) { + goto error; + } + + iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE); + + for (i = 0; i < iterations; i++) { + fdp_pointer = &fds_pointer[i]; + + // + if (fdp_pointer->proc_fdtype == PROX_FDTYPE_VNODE) + { + nb = proc_pidfdinfo(pid, + fdp_pointer->proc_fd, + PROC_PIDFDVNODEPATHINFO, + &vi, + sizeof(vi)); + + // --- errors checking + if (nb <= 0) { + if ((errno == ENOENT) || (errno == EBADF)) { + // no such file or directory or bad file descriptor; + // let's assume the file has been closed or removed + continue; + } + if (errno != 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + else + return PyErr_Format(PyExc_RuntimeError, + "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed"); + } + if (nb < sizeof(vi)) { + return PyErr_Format(PyExc_RuntimeError, + "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed (buffer mismatch)"); + } + // --- /errors checking + + // --- construct python list + tuple = Py_BuildValue("(si)", vi.pvip.vip_path, + (int)fdp_pointer->proc_fd); + PyList_Append(retList, tuple); + Py_DECREF(tuple); + // --- /construct python list + } + } + + return retList; + +error: + if (errno != 0) + return PyErr_SetFromErrno(PyExc_OSError); + else if (! pid_exists(pid) ) + return NoSuchProcess(); + else + return PyErr_Format(PyExc_RuntimeError, + "proc_pidinfo(PROC_PIDLISTFDS) failed"); +} + + +/* + * 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 ""; + } +} + + +/* + * Return process TCP and UDP connections as a list of tuples. + * References: + * - lsof source code: http://goo.gl/SYW79 and http://goo.gl/wNrC0 + * - /usr/include/sys/proc_info.h + */ +static PyObject* +get_process_connections(PyObject* self, PyObject* args) +{ + long pid; + int pidinfo_result; + int iterations; + int i; + int nb; + + struct proc_fdinfo *fds_pointer; + struct proc_fdinfo *fdp_pointer; + struct socket_fdinfo si; + + + PyObject *retList = PyList_New(0); + PyObject *tuple = NULL; + PyObject *laddr = NULL; + PyObject *raddr = NULL; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); + if (pidinfo_result <= 0) { + goto error; + } + + fds_pointer = malloc(pidinfo_result); + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, + pidinfo_result); + free(fds_pointer); + + if (pidinfo_result <= 0) { + goto error; + } + + iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE); + + for (i = 0; i < iterations; i++) { + errno = 0; + fdp_pointer = &fds_pointer[i]; + + // + if (fdp_pointer->proc_fdtype == PROX_FDTYPE_SOCKET) + { + nb = proc_pidfdinfo(pid, fdp_pointer->proc_fd, PROC_PIDFDSOCKETINFO, + &si, sizeof(si)); + + // --- errors checking + if (nb <= 0) { + if (errno == EBADF) { + // let's assume socket has been closed + continue; + } + if (errno != 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + else { + return PyErr_Format(PyExc_RuntimeError, + "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed"); + } + } + if (nb < sizeof(si)) { + return PyErr_Format(PyExc_RuntimeError, + "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed (buffer mismatch)"); + } + // --- /errors checking + + // + int fd, family, type, lport, rport; + char lip[200], rip[200]; + char *state; + + fd = (int)fdp_pointer->proc_fd; + family = si.psi.soi_family; + type = si.psi.soi_kind; + + if ((family != AF_INET) && (family != AF_INET6)) { + continue; + } + + if (type == 2) + type = SOCK_STREAM; + else if (type == 1) + type = SOCK_DGRAM; + else + continue; + + if (errno != 0) { + printf("errno 1 = %i\n", errno); + return PyErr_SetFromErrno(PyExc_OSError); + } + + + if (family == AF_INET) { + inet_ntop(AF_INET, + &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_46.i46a_addr4, + lip, + sizeof(lip)); + inet_ntop(AF_INET, + &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_46.i46a_addr4, + rip, + sizeof(lip)); + } + else { + inet_ntop(AF_INET6, + &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_laddr.ina_6, + lip, sizeof(lip)); + inet_ntop(AF_INET6, + &si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_faddr.ina_6, + lip, sizeof(rip)); + } + + // check for inet_ntop failures + if (errno != 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + + lport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_lport); + rport = ntohs(si.psi.soi_proto.pri_tcp.tcpsi_ini.insi_fport); + if (type == SOCK_STREAM) + state = get_connection_status((int)si.psi.soi_proto.pri_tcp.tcpsi_state); + else + state = ""; + + laddr = Py_BuildValue("(si)", lip, lport); + if (rport != 0) + raddr = Py_BuildValue("(si)", rip, rport); + else + raddr = PyTuple_New(0); + + // --- construct python list + tuple = Py_BuildValue("(iiiNNs)", fd, family, type, laddr, raddr, + state); + PyList_Append(retList, tuple); + Py_DECREF(tuple); + // --- /construct python list + } + } + + return retList; + +error: + if (errno != 0) + return PyErr_SetFromErrno(PyExc_OSError); + else if (! pid_exists(pid) ) + return NoSuchProcess(); + else + return PyErr_Format(PyExc_RuntimeError, + "proc_pidinfo(PROC_PIDLISTFDS) failed"); +} + + +/* + * define the psutil C module methods and initialize the module. + */ +static PyMethodDef +PsutilMethods[] = +{ + // --- per-process functions + + {"get_process_name", get_process_name, METH_VARARGS, + "Return process name"}, + {"get_process_cmdline", get_process_cmdline, METH_VARARGS, + "Return process cmdline as a list of cmdline arguments"}, + {"get_process_ppid", get_process_ppid, METH_VARARGS, + "Return process ppid as an integer"}, + {"get_process_uids", get_process_uids, METH_VARARGS, + "Return process real user id as an integer"}, + {"get_process_gids", get_process_gids, METH_VARARGS, + "Return process real group id as an integer"}, + {"get_cpu_times", get_cpu_times, METH_VARARGS, + "Return tuple of user/kern time for the given PID"}, + {"get_process_create_time", get_process_create_time, METH_VARARGS, + "Return a float indicating the process create time expressed in " + "seconds since the epoch"}, + {"get_memory_info", get_memory_info, METH_VARARGS, + "Return a tuple of RSS/VMS memory information"}, + {"get_process_num_threads", get_process_num_threads, METH_VARARGS, + "Return number of threads used by process"}, + {"get_process_status", get_process_status, METH_VARARGS, + "Return process status as an integer"}, + {"get_process_threads", get_process_threads, METH_VARARGS, + "Return process threads as a list of tuples"}, + {"get_process_open_files", get_process_open_files, METH_VARARGS, + "Return files opened by process as a list of tuples"}, + {"get_process_connections", get_process_connections, METH_VARARGS, + "Get process TCP and UDP connections as a list of tuples"}, + + + // --- system-related functions + + {"get_pid_list", get_pid_list, METH_VARARGS, + "Returns a list of PIDs currently running on the system"}, + {"get_num_cpus", get_num_cpus, METH_VARARGS, + "Return number of CPUs on the system"}, + {"get_total_phymem", get_total_phymem, METH_VARARGS, + "Return the total amount of physical memory, in bytes"}, + {"get_avail_phymem", get_avail_phymem, METH_VARARGS, + "Return the amount of available physical memory, in bytes"}, + {"get_total_virtmem", get_total_virtmem, METH_VARARGS, + "Return the total amount of virtual memory, in bytes"}, + {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, + "Return the amount of available virtual memory, in bytes"}, + {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, + "Return system cpu times as a tuple (user, system, nice, idle, irc)"}, + {"get_system_boot_time", get_system_boot_time, METH_VARARGS, + "Return a float indicating the system boot time expressed in " + "seconds since the epoch"}, + + {NULL, NULL, 0, NULL} +}; + + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +static struct module_state _state; +#endif + +#if PY_MAJOR_VERSION >= 3 + +static int +psutil_osx_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +static int +psutil_osx_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + + +static struct PyModuleDef +moduledef = { + PyModuleDef_HEAD_INIT, + "psutil_osx", + NULL, + sizeof(struct module_state), + PsutilMethods, + NULL, + psutil_osx_traverse, + psutil_osx_clear, + NULL +}; + +#define INITERROR return NULL + +PyObject * +PyInit__psutil_osx(void) + +#else +#define INITERROR return + +void +init_psutil_osx(void) +#endif +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); +#else + PyObject *module = Py_InitModule("_psutil_osx", PsutilMethods); +#endif + // process status constants, defined in: + // http://fxr.watson.org/fxr/source/bsd/sys/proc.h?v=xnu-792.6.70#L149 + PyModule_AddIntConstant(module, "SIDL", SIDL); + PyModule_AddIntConstant(module, "SRUN", SRUN); + PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); + PyModule_AddIntConstant(module, "SSTOP", SSTOP); + PyModule_AddIntConstant(module, "SZOMB", SZOMB); + + if (module == NULL) { + INITERROR; + } + struct module_state *st = GETSTATE(module); + + st->error = PyErr_NewException("_psutil_osx.Error", NULL, NULL); + if (st->error == NULL) { + Py_DECREF(module); + INITERROR; + } +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} + + diff -Nru python-psutil-0.1.3/psutil/_psutil_osx.h python-psutil-0.2.1/psutil/_psutil_osx.h --- python-psutil-0.1.3/psutil/_psutil_osx.h 2009-09-14 13:36:29.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_osx.h 2011-02-28 21:40:50.000000000 +0000 @@ -1,22 +1,33 @@ /* - * $Id: _psutil_osx.h 347 2009-04-24 17:02:36Z jloden $ + * $Id: _psutil_osx.h 940 2011-02-24 19:19:58Z g.rodola $ * * OS X platform-specific module methods for _psutil_osx */ #include +// --- per-process functions +static PyObject* get_process_name(PyObject* self, PyObject* args); +static PyObject* get_process_cmdline(PyObject* self, PyObject* args); +static PyObject* get_process_ppid(PyObject* self, PyObject* args); +static PyObject* get_process_uids(PyObject* self, PyObject* args); +static PyObject* get_process_gids(PyObject* self, PyObject* args); +static PyObject* get_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_process_create_time(PyObject* self, PyObject* args); +static PyObject* get_memory_info(PyObject* self, PyObject* args); +static PyObject* get_process_num_threads(PyObject* self, PyObject* args); +static PyObject* get_process_status(PyObject* self, PyObject* args); +static PyObject* get_process_threads(PyObject* self, PyObject* args); +static PyObject* get_process_open_files(PyObject* self, PyObject* args); +static PyObject* get_process_connections(PyObject* self, PyObject* args); +// --- system-related functions static PyObject* get_pid_list(PyObject* self, PyObject* args); -static PyObject* get_process_info(PyObject* self, PyObject* args); -static int pid_exists(long pid); -static PyObject* get_process_cpu_times(PyObject* self, PyObject* args); -static PyObject* get_process_create_time(PyObject* self, PyObject* args); static PyObject* get_num_cpus(PyObject* self, PyObject* args); -static PyObject* get_memory_info(PyObject* self, PyObject* args); static PyObject* get_total_phymem(PyObject* self, PyObject* args); static PyObject* get_avail_phymem(PyObject* self, PyObject* args); static PyObject* get_total_virtmem(PyObject* self, PyObject* args); static PyObject* get_avail_virtmem(PyObject* self, PyObject* args); static PyObject* get_system_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_system_boot_time(PyObject* self, PyObject* args); diff -Nru python-psutil-0.1.3/psutil/_psutil_posix.c python-psutil-0.2.1/psutil/_psutil_posix.c --- python-psutil-0.1.3/psutil/_psutil_posix.c 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_posix.c 2011-01-12 18:47:31.000000000 +0000 @@ -0,0 +1,138 @@ +/* + * $Id: _psutil_posix.c 857 2010-12-25 21:21:07Z g.rodola $ + * + * Functions specific to all POSIX compliant platforms. + */ + +#include +#include +#include +#include + +#include "_psutil_posix.h" + + +/* + * Given a PID return process priority as a Python integer. + */ +static PyObject* +posix_getpriority(PyObject* self, PyObject* args) +{ + long pid; + int priority; + errno = 0; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + priority = getpriority(PRIO_PROCESS, pid); + if (errno != 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return Py_BuildValue("i", priority); +} + +/* + * Given a PID and a value change process priority. + */ +static PyObject* +posix_setpriority(PyObject* self, PyObject* args) +{ + long pid; + int priority; + int retval; + if (! PyArg_ParseTuple(args, "li", &pid, &priority)) { + return NULL; + } + retval = setpriority(PRIO_PROCESS, pid, priority); + if (retval == -1) { + return PyErr_SetFromErrno(PyExc_OSError); + } + Py_INCREF(Py_None); + return Py_None; +} + + +/* + * define the psutil C module methods and initialize the module. + */ +static PyMethodDef +PsutilMethods[] = +{ + {"getpriority", posix_getpriority, METH_VARARGS, + "Return process priority"}, + {"setpriority", posix_setpriority, METH_VARARGS, + "Set process priority"}, + {NULL, NULL, 0, NULL} +}; + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +static struct module_state _state; +#endif + +#if PY_MAJOR_VERSION >= 3 + +static int +psutil_posix_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +static int +psutil_posix_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + +static struct PyModuleDef +moduledef = { + PyModuleDef_HEAD_INIT, + "psutil_posix", + NULL, + sizeof(struct module_state), + PsutilMethods, + NULL, + psutil_posix_traverse, + psutil_posix_clear, + NULL +}; + +#define INITERROR return NULL + +PyObject * +PyInit__psutil_posix(void) + +#else +#define INITERROR return + +void init_psutil_posix(void) +#endif +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); +#else + PyObject *module = Py_InitModule("_psutil_posix", PsutilMethods); +#endif + if (module == NULL) { + INITERROR; + } + struct module_state *st = GETSTATE(module); + + st->error = PyErr_NewException("_psutil_posix.Error", NULL, NULL); + if (st->error == NULL) { + Py_DECREF(module); + INITERROR; + } +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} + + + diff -Nru python-psutil-0.1.3/psutil/_psutil_posix.h python-psutil-0.2.1/psutil/_psutil_posix.h --- python-psutil-0.1.3/psutil/_psutil_posix.h 1970-01-01 00:00:00.000000000 +0000 +++ python-psutil-0.2.1/psutil/_psutil_posix.h 2011-01-12 18:47:31.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * $Id: _psutil_posix.h 857 2010-12-25 21:21:07Z g.rodola $ + * + * POSIX specific module methods for _psutil_posix + */ + +#include + +static PyObject* posix_getpriority(PyObject* self, PyObject* args); +static PyObject* posix_setpriority(PyObject* self, PyObject* args); + diff -Nru python-psutil-0.1.3/psutil/wmi.py python-psutil-0.2.1/psutil/wmi.py --- python-psutil-0.1.3/psutil/wmi.py 2009-09-15 00:42:26.000000000 +0000 +++ python-psutil-0.2.1/psutil/wmi.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1418 +0,0 @@ -""" -Windows Management Instrumentation (WMI) is Microsoft's answer to -the DMTF's Common Information Model. It allows you to query just -about any conceivable piece of information from any computer which -is running the necessary agent and over which have you the -necessary authority. - -Since the COM implementation doesn't give much away to Python -programmers, I've wrapped it in some lightweight classes with -some getattr / setattr magic to ease the way. In particular: - -* The :class:`_wmi_namespace` object itself will determine its classes - and allow you to return all instances of any of them by - using its name as an attribute:: - - disks = wmi.WMI ().Win32_LogicalDisk () - -* In addition, you can specify what would become the WHERE clause - as keyword parameters:: - - fixed_disks = wmi.WMI ().Win32_LogicalDisk (DriveType=3) - -* The objects returned by a WMI lookup are wrapped in a Python - class which determines their methods and classes and allows - you to access them as though they were Python classes. The - methods only allow named parameters:: - - for p in wmi.WMI ().Win32_Process (Name="notepad.exe"): - p.Terminate (Result=1) - -* Doing a print on one of the WMI objects will result in its - `GetObjectText\_` method being called, which usually produces - a meaningful printout of current values. - The repr of the object will include its full WMI path, - which lets you get directly to it if you need to. - -* You can get the associators and references of an object as - a list of python objects by calling the associators () and - references () methods on a WMI Python object:: - - for p in wmi.WMI ().Win32_Process (Name="notepad.exe"): - for r in p.references (): - print r - - .. note:: - Don't do this on a Win32_ComputerSystem object; it will - take all day and kill your machine! - - -* WMI classes (as opposed to instances) are first-class - objects, so you can get hold of a class, and call - its methods or set up a watch against it:: - - process = wmi.WMI ().Win32_Process - process.Create (CommandLine="notepad.exe") - -* To make it easier to use in embedded systems and py2exe-style - executable wrappers, the module will not force early Dispatch. - To do this, it uses a handy hack by Thomas Heller for easy access - to constants. - -Typical usage will be:: - - import wmi - - vodev1 = wmi.WMI ("vodev1") - for disk in vodev1.Win32_LogicalDisk (): - if disk.DriveType == 3: - space = 100 * long (disk.FreeSpace) / long (disk.Size) - print "%s has %d%% free" % (disk.Name, space) - -Many thanks, obviously to Mark Hammond for creating the win32all -extensions, but also to Alex Martelli and Roger Upole, whose -c.l.py postings pointed me in the right direction. -Thanks especially in release 1.2 to Paul Tiemann for his code -contributions and robust testing. -""" -__VERSION__ = __version__ = "1.4.6" - -_DEBUG = False - -import sys -import datetime -import re -import struct -import warnings - -from win32com.client import GetObject, Dispatch -import pywintypes - -def signed_to_unsigned (signed): - """Convert a (possibly signed) long to unsigned hex. Useful - when converting a COM error code to the more conventional - 8-digit hex:: - - print "%08X" % signed_to_unsigned (-2147023174) - """ - unsigned, = struct.unpack ("L", struct.pack ("l", signed)) - return unsigned - -class SelfDeprecatingDict (object): - """Provides for graceful degradation of objects which - are currently dictionaries (and therefore accessed via - `.keys`, `.items`, etc.) into lists. Wraps an existing - `dict` and allows it to be addressed as a `dict` or as a - `list` during an interregnum, issuing a `DeprecationWarning` - if accessed as a `dict`. - """ - - dict_only = set (dir (dict)).difference (dir (list)) - - def __init__ (self, dictlike): - self.dict = dict (dictlike) - self.list = list (self.dict) - - def __getattr__ (self, attribute): - if attribute in self.dict_only: - warnings.warn ("In future this will be a list and not a dictionary", DeprecationWarning) - return getattr (self.dict, attribute) - else: - return getattr (self.list, attribute) - - def __iter__ (self): - return iter (self.list) - - def __str__ (self): - return str (self.list) - - def __repr__ (self): - return repr (self.list) - - def __getitem__ (self, item): - try: - return self.list[item] - except TypeError: - return self.dict[item] - -class ProvideConstants (object): - """When called on a ``win32com.client.Dispatch`` object, - provides lazy access to constants defined in the typelib. - They can then be accessed as attributes of the :attr:`_constants` - property. (From Thomas Heller on c.l.py). - """ - def __init__(self, comobj): - comobj.__dict__["_constants"] = self - self.__typecomp = \ - comobj._oleobj_.GetTypeInfo().GetContainingTypeLib()[0].GetTypeComp() - - def __getattr__(self, name): - if name.startswith("__") and name.endswith("__"): - raise AttributeError (name) - result = self.__typecomp.Bind(name) - if not result[0]: - raise AttributeError (name) - return result[1].value - -obj = GetObject ("winmgmts:") -ProvideConstants (obj) - -wbemErrInvalidQuery = obj._constants.wbemErrInvalidQuery -wbemErrTimedout = obj._constants.wbemErrTimedout -wbemFlagReturnImmediately = obj._constants.wbemFlagReturnImmediately -wbemFlagForwardOnly = obj._constants.wbemFlagForwardOnly - -# -# Exceptions -# -class x_wmi (Exception): - """Ancestor of all wmi-related exceptions. Keeps track of - an info message and the underlying COM error if any, exposed - as the :attr:`com_error` attribute. - """ - def __init__ (self, info="", com_error=None): - self.info = info - self.com_error = com_error - - def __str__ (self): - return "" % ( - self.info or "Unexpected COM Error", - self.com_error or "(no underlying exception)" - ) - -class x_wmi_invalid_query (x_wmi): - "Raised when a WMI returns `wbemErrInvalidQuery`" - pass - -class x_wmi_timed_out (x_wmi): - "Raised when a watcher times out" - pass - -class x_wmi_no_namespace (x_wmi): - """Raised when an attempt is made to query or watch - from a class without a namespace. - """ - pass - -class x_access_denied (x_wmi): - "Raised when WMI raises 80070005" - pass - -class x_wmi_authentication (x_wmi): - "Raised when an invalid combination of authentication properties is attempted when connecting" - pass - -class x_wmi_uninitialised_thread (x_wmi): - """Raised when WMI returns 800401E4 on connection, usually - indicating that no COM threading model has been initialised - """ - pass - -WMI_EXCEPTIONS = { - signed_to_unsigned (wbemErrInvalidQuery) : x_wmi_invalid_query, - signed_to_unsigned (wbemErrTimedout) : x_wmi_timed_out, - 0x80070005 : x_access_denied, - 0x80041003 : x_access_denied, - 0x800401E4 : x_wmi_uninitialised_thread, -} - -def handle_com_error (err=None): - """Convenience wrapper for displaying all manner of COM errors. - Raises a :exc:`x_wmi` exception with more useful information attached - - :param err: The structure attached to a `pywintypes.com_error` - """ - if err is None: - _, err, _ = sys.exc_info () - hresult_code, hresult_name, additional_info, parameter_in_error = err.args - hresult_code = signed_to_unsigned (hresult_code) - exception_string = ["%s - %s" % (hex (hresult_code), hresult_name)] - scode = None - if additional_info: - wcode, source_of_error, error_description, whlp_file, whlp_context, scode = additional_info - scode = signed_to_unsigned (scode) - exception_string.append (" Error in: %s" % source_of_error) - exception_string.append (" %s - %s" % (hex (scode), (error_description or "").strip ())) - for error_code, klass in WMI_EXCEPTIONS.items (): - if error_code in (hresult_code, scode): - break - else: - klass = x_wmi - raise klass (com_error=err) - - -BASE = datetime.datetime (1601, 1, 1) -def from_1601 (ns100): - return BASE + datetime.timedelta (microseconds=int (ns100) / 10) - -def from_time (year=None, month=None, day=None, hours=None, minutes=None, seconds=None, microseconds=None, timezone=None): - """Convenience wrapper to take a series of date/time elements and return a WMI time - of the form `yyyymmddHHMMSS.mmmmmm+UUU`. All elements may be int, string or - omitted altogether. If omitted, they will be replaced in the output string - by a series of stars of the appropriate length. - - :param year: The year element of the date/time - :param month: The month element of the date/time - :param day: The day element of the date/time - :param hours: The hours element of the date/time - :param minutes: The minutes element of the date/time - :param seconds: The seconds element of the date/time - :param microseconds: The microseconds element of the date/time - :param timezone: The timeezone element of the date/time - - :returns: A WMI datetime string of the form: `yyyymmddHHMMSS.mmmmmm+UUU` - """ - def str_or_stars (i, length): - if i is None: - return "*" * length - else: - return str (i).rjust (length, "0") - - wmi_time = "" - wmi_time += str_or_stars (year, 4) - wmi_time += str_or_stars (month, 2) - wmi_time += str_or_stars (day, 2) - wmi_time += str_or_stars (hours, 2) - wmi_time += str_or_stars (minutes, 2) - wmi_time += str_or_stars (seconds, 2) - wmi_time += "." - wmi_time += str_or_stars (microseconds, 6) - wmi_time += "+" - wmi_time += str_or_stars (timezone, 3) - - return wmi_time - -def to_time (wmi_time): - """Convenience wrapper to take a WMI datetime string of the form - yyyymmddHHMMSS.mmmmmm+UUU and return a 9-tuple containing the - individual elements, or None where string contains placeholder - stars. - - :param wmi_time: The WMI datetime string in `yyyymmddHHMMSS.mmmmmm+UUU` format - - :returns: A 9-tuple of (year, month, day, hours, minutes, seconds, microseconds, timezone) - """ - def int_or_none (s, start, end): - try: - return int (s[start:end]) - except ValueError: - return None - - year = int_or_none (wmi_time, 0, 4) - month = int_or_none (wmi_time, 4, 6) - day = int_or_none (wmi_time, 6, 8) - hours = int_or_none (wmi_time, 8, 10) - minutes = int_or_none (wmi_time, 10, 12) - seconds = int_or_none (wmi_time, 12, 14) - microseconds = int_or_none (wmi_time, 15, 21) - timezone = wmi_time[22:] - if timezone == "***": - timezone = None - - return year, month, day, hours, minutes, seconds, microseconds, timezone - -def _set (obj, attribute, value): - """Helper function to add an attribute directly into the instance - dictionary, bypassing possible `__getattr__` calls - - :param obj: Any python object - :param attribute: String containing attribute name - :param value: Any python object - """ - obj.__dict__[attribute] = value - -class _wmi_method: - """A currying sort of wrapper around a WMI method name. It - abstract's the method's parameters and can be called like - a normal Python object passing in the parameter values. - - Output parameters are returned from the call as a tuple. - In addition, the docstring is set up as the method's - signature, including an indication as to whether any - given parameter is expecting an array, and what - special privileges are required to call the method. - """ - - def __init__ (self, ole_object, method_name): - """ - :param ole_object: The WMI class/instance whose method is to be called - :param method_name: The name of the method to be called - """ - try: - self.ole_object = Dispatch (ole_object) - self.method = ole_object.Methods_ (method_name) - self.qualifiers = {} - for q in self.method.Qualifiers_: - self.qualifiers[q.Name] = q.Value - self.provenance = "\n".join (self.qualifiers.get ("MappingStrings", [])) - - self.in_parameters = self.method.InParameters - self.out_parameters = self.method.OutParameters - if self.in_parameters is None: - self.in_parameter_names = [] - else: - self.in_parameter_names = [(i.Name, i.IsArray) for i in self.in_parameters.Properties_] - if self.out_parameters is None: - self.out_parameter_names = [] - else: - self.out_parameter_names = [(i.Name, i.IsArray) for i in self.out_parameters.Properties_] - - doc = "%s (%s) => (%s)" % ( - method_name, - ", ".join ([name + ("", "[]")[is_array] for (name, is_array) in self.in_parameter_names]), - ", ".join ([name + ("", "[]")[is_array] for (name, is_array) in self.out_parameter_names]) - ) - privileges = self.qualifiers.get ("Privileges", []) - if privileges: - doc += " | Needs: " + ", ".join (privileges) - self.__doc__ = doc - except pywintypes.com_error: - handle_com_error () - - def __call__ (self, *args, **kwargs): - """Execute the call to a WMI method, returning - a tuple (even if is of only one value) containing - the out and return parameters. - """ - try: - if self.in_parameters: - parameter_names = {} - for name, is_array in self.in_parameter_names: - parameter_names[name] = is_array - - parameters = self.in_parameters - - # - # Check positional parameters first - # - for n_arg in range (len (args)): - arg = args[n_arg] - parameter = parameters.Properties_[n_arg] - if parameter.IsArray: - try: list (arg) - except TypeError: raise TypeError ("parameter %d must be iterable" % n_arg) - parameter.Value = arg - - # - # If any keyword param supersedes a positional one, - # it'll simply overwrite it. - # - for k, v in kwargs.items (): - is_array = parameter_names.get (k) - if is_array is None: - raise AttributeError ("%s is not a valid parameter for %s" % (k, self.__doc__)) - else: - if is_array: - try: list (v) - except TypeError: raise TypeError ("%s must be iterable" % k) - parameters.Properties_ (k).Value = v - - result = self.ole_object.ExecMethod_ (self.method.Name, self.in_parameters) - else: - result = self.ole_object.ExecMethod_ (self.method.Name) - - results = [] - for name, is_array in self.out_parameter_names: - value = result.Properties_ (name).Value - if is_array: - # - # Thanks to Jonas Bjering for bug report and patch - # - results.append (list (value or [])) - else: - results.append (value) - return tuple (results) - - except pywintypes.com_error: - handle_com_error () - - def __repr__ (self): - return "" % self.__doc__ - -class _wmi_property (object): - - def __init__ (self, property): - self.property = property - self.name = property.Name - self.value = property.Value - self.qualifiers = dict ((q.Name, q.Value) for q in property.Qualifiers_) - self.type = self.qualifiers.get ("CIMTYPE", None) - - def set (self, value): - self.property.Value = value - - def __getattr__ (self, attr): - return getattr (self.property, attr) - -# -# class _wmi_object -# -class _wmi_object: - """The heart of the WMI module: wraps the objects returned by COM - ISWbemObject interface and provide readier access to their properties - and methods resulting in a more Pythonic interface. Not usually - instantiated directly, rather as a result of calling a :class:`_wmi_class` - on the parent :class:`_wmi_namespace`. - - If you get hold of a WMI-related COM object from some other - source than this module, you can wrap it in one of these objects - to get the benefits of the module:: - - import win32com.client - import wmi - - wmiobj = win32com.client.GetObject ("winmgmts:Win32_LogicalDisk.DeviceID='C:'") - c_drive = wmi._wmi_object (wmiobj) - print c_drive - """ - - def __init__ (self, ole_object, instance_of=None, fields=[], property_map={}): - try: - _set (self, "ole_object", ole_object) - _set (self, "id", ole_object.Path_.DisplayName.lower ()) - _set (self, "_instance_of", instance_of) - _set (self, "properties", {}) - _set (self, "methods", {}) - _set (self, "property_map", property_map) - _set (self, "_associated_classes", None) - _set (self, "_keys", None) - - if fields: - for field in fields: - self.properties[field] = None - else: - for p in ole_object.Properties_: - self.properties[p.Name] = None - - for m in ole_object.Methods_: - self.methods[m.Name] = None - - _set (self, "_properties", self.properties.keys ()) - _set (self, "_methods", self.methods.keys ()) - _set (self, "qualifiers", dict ((q.Name, q.Value) for q in self.ole_object.Qualifiers_)) - - except pywintypes.com_error: - handle_com_error () - - def __lt__ (self, other): - return self.id < other.id - - def __str__ (self): - """For a call to print [object] return the OLE description - of the properties / values of the object - """ - try: - return self.ole_object.GetObjectText_ () - except pywintypes.com_error: - handle_com_error () - - def __repr__ (self): - """ - Indicate both the fact that this is a wrapped WMI object - and the WMI object's own identifying class. - """ - try: - return "<%s: %s>" % (self.__class__.__name__, str (self.Path_.Path)) - except pywintypes.com_error: - handle_com_error () - - def _cached_properties (self, attribute): - if self.properties[attribute] is None: - self.properties[attribute] = _wmi_property (self.ole_object.Properties_ (attribute)) - return self.properties[attribute] - - def _cached_methods (self, attribute): - if self.methods[attribute] is None: - self.methods[attribute] = _wmi_method (self.ole_object, attribute) - return self.methods[attribute] - - def __getattr__ (self, attribute): - """ - Attempt to pass attribute calls to the proxied COM object. - If the attribute is recognised as a property, return its value; - if it is recognised as a method, return a method wrapper which - can then be called with parameters; otherwise pass the lookup - on to the underlying object. - """ - try: - if attribute in self.properties: - property = self._cached_properties (attribute) - factory = self.property_map.get (attribute, self.property_map.get (property.type, lambda x: x)) - value = factory (property.value) - # - # If this is an association, certain of its properties - # are actually the paths to the aspects of the association, - # so translate them automatically into WMI objects. - # - if property.type.startswith ("ref:"): - return WMI (moniker=value) - else: - return value - elif attribute in self.methods: - return self._cached_methods (attribute) - else: - return getattr (self.ole_object, attribute) - except pywintypes.com_error: - handle_com_error () - - def __setattr__ (self, attribute, value): - """If the attribute to be set is valid for the proxied - COM object, set that objects's parameter value; if not, - raise an exception. - """ - try: - if attribute in self.properties: - self._cached_properties (attribute).set (value) - if self.ole_object.Path_.Path: - self.ole_object.Put_ () - else: - raise AttributeError (attribute) - except pywintypes.com_error: - handle_com_error () - - def __eq__ (self, other): - return self.id == other.id - - def __hash__ (self): - return hash (self.id) - - def _getAttributeNames (self): - """Return list of methods/properties for IPython completion""" - attribs = [str (x) for x in self.methods.keys ()] - attribs.extend ([str (x) for x in self.properties.keys ()]) - return attribs - - def _get_keys (self): - """A WMI object is uniquely defined by a set of properties - which constitute its keys. Lazily retrieves the keys for this - instance or class. - - :returns: list of key property names - """ - # NB You can get the keys of an instance more directly, via - # Path\_.Keys but this doesn't apply to classes. The technique - # here appears to work for both. - if self._keys is None: - _set (self, "_keys", []) - for property in self.ole_object.Properties_: - for qualifier in property.Qualifiers_: - if qualifier.Name == "key" and qualifier.Value: - self._keys.append (property.Name) - return self._keys - keys = property (_get_keys) - - def put (self): - """Push all outstanding property updates back to the - WMI database. - """ - self.ole_object.Put_ () - - def set (self, **kwargs): - """Set several properties of the underlying object - at one go. This is particularly useful in combination - with the new () method below. However, an instance - which has been spawned in this way won't have enough - information to write pack, so only try if the - instance has a path. - """ - if kwargs: - try: - for attribute, value in kwargs.items (): - if attribute in self.properties: - self._cached_properties (attribute).set (value) - else: - raise AttributeError (attribute) - # - # Only try to write the attributes - # back if the object exists. - # - if self.ole_object.Path_.Path: - self.ole_object.Put_ () - except pywintypes.com_error: - handle_com_error () - - def path (self): - """Return the WMI URI to this object. Can be used to - determine the path relative to the parent namespace:: - - pp0 = wmi.WMI ().Win32_ParallelPort ()[0] - print pp0.path ().RelPath - - .. Do more with this - """ - try: - return self.ole_object.Path_ - except pywintypes.com_error: - handle_com_error () - - def derivation (self): - """Return a tuple representing the object derivation for - this object, with the most specific object first:: - - pp0 = wmi.WMI ().Win32_ParallelPort ()[0] - print ' <- '.join (pp0.derivation ()) - """ - try: - return self.ole_object.Derivation_ - except pywintypes.com_error: - handle_com_error () - - def _cached_associated_classes (self): - if self._associated_classes is None: - if isinstance (self, _wmi_class): - params = {'bSchemaOnly' : True} - else: - params = {'bClassesOnly' : True} - try: - associated_classes = dict ( - (assoc.Path_.Class, _wmi_class (self._namespace, assoc)) for - assoc in self.ole_object.Associators_ (**params) - ) - _set (self, "_associated_classes", associated_classes) - except pywintypes.com_error: - handle_com_error () - - return self._associated_classes - associated_classes = property (_cached_associated_classes) - - def associators (self, wmi_association_class="", wmi_result_class=""): - """Return a list of objects related to this one, optionally limited - either by association class (ie the name of the class which relates - them) or by result class (ie the name of the class which would be - retrieved):: - - c = wmi.WMI () - pp = c.Win32_ParallelPort ()[0] - - for i in pp.associators (wmi_association_class="Win32_PortResource"): - print i - - for i in pp.associators (wmi_result_class="Win32_PnPEntity"): - print i - """ - try: - return [ - _wmi_object (i) for i in \ - self.ole_object.Associators_ ( - strAssocClass=wmi_association_class, - strResultClass=wmi_result_class - ) - ] - except pywintypes.com_error: - handle_com_error () - - def references (self, wmi_class=""): - """Return a list of associations involving this object, optionally - limited by the result class (the name of the association class). - - NB Associations are treated specially; although WMI only returns - the string corresponding to the instance of each associated object, - this module will automatically convert that to the object itself:: - - c = wmi.WMI () - sp = c.Win32_SerialPort ()[0] - - for i in sp.references (): - print i - - for i in sp.references (wmi_class="Win32_SerialPortSetting"): - print i - """ - # - # FIXME: Allow an actual class to be passed in, using - # its .Path_.RelPath property to determine the string - # - try: - return [_wmi_object (i) for i in self.ole_object.References_ (strResultClass=wmi_class)] - except pywintypes.com_error: - handle_com_error () - -# -# class _wmi_event -# -class _wmi_event (_wmi_object): - """Slight extension of the _wmi_object class to allow - objects which are the result of events firing to return - extra information such as the type of event. - """ - event_type_re = re.compile ("__Instance(Creation|Modification|Deletion)Event") - def __init__ (self, event, event_info, fields=[]): - _wmi_object.__init__ (self, event, fields=fields) - _set (self, "event_type", None) - _set (self, "timestamp", None) - _set (self, "previous", None) - - if event_info: - event_type = self.event_type_re.match (event_info.Path_.Class).group (1).lower () - _set (self, "event_type", event_type) - if hasattr (event_info, "TIME_CREATED"): - _set (self, "timestamp", from_1601 (event_info.TIME_CREATED)) - if hasattr (event_info, "PreviousInstance"): - _set (self, "previous", event_info.PreviousInstance) - -# -# class _wmi_class -# -class _wmi_class (_wmi_object): - """Currying class to assist in issuing queries against - a WMI namespace. The idea is that when someone issues - an otherwise unknown method against the WMI object, if - it matches a known WMI class a query object will be - returned which may then be called with one or more params - which will form the WHERE clause:: - - c = wmi.WMI () - c_drives = c.Win32_LogicalDisk (Name='C:') - """ - def __init__ (self, namespace, wmi_class): - _wmi_object.__init__ (self, wmi_class) - _set (self, "_class_name", wmi_class.Path_.Class) - if namespace: - _set (self, "_namespace", namespace) - else: - class_moniker = wmi_class.Path_.DisplayName - winmgmts, namespace_moniker, class_name = class_moniker.split (":") - namespace = _wmi_namespace (GetObject (winmgmts + ":" + namespace_moniker), False) - _set (self, "_namespace", namespace) - - def query (self, fields=[], **where_clause): - """Make it slightly easier to query against the class, - by calling the namespace's query with the class preset. - Won't work if the class has been instantiated directly. - """ - # - # FIXME: Not clear if this can ever happen - # - if self._namespace is None: - raise x_wmi_no_namespace ("You cannot query directly from a WMI class") - - try: - field_list = ", ".join (fields) or "*" - wql = "SELECT " + field_list + " FROM " + self._class_name - if where_clause: - wql += " WHERE " + " AND ". join (["%s = '%s'" % (k, v) for k, v in where_clause.items ()]) - return self._namespace.query (wql, self, fields) - except pywintypes.com_error: - handle_com_error () - - __call__ = query - - def watch_for ( - self, - notification_type="operation", - delay_secs=1, - fields=[], - **where_clause - ): - if self._namespace is None: - raise x_wmi_no_namespace ("You cannot watch directly from a WMI class") - - valid_notification_types = ("operation", "creation", "deletion", "modification") - if notification_type.lower () not in valid_notification_types: - raise x_wmi ("notification_type must be one of %s" % ", ".join (valid_notification_types)) - - return self._namespace.watch_for ( - notification_type=notification_type, - wmi_class=self, - delay_secs=delay_secs, - fields=fields, - **where_clause - ) - - def instances (self): - """Return a list of instances of the WMI class - """ - try: - return [_wmi_object (instance, self) for instance in self.Instances_ ()] - except pywintypes.com_error: - handle_com_error () - - def new (self, **kwargs): - """This is the equivalent to the raw-WMI SpawnInstance\_ - method. Note that there are relatively few uses for - this, certainly fewer than you might imagine. Most - classes which need to create a new *real* instance - of themselves, eg Win32_Process, offer a .Create - method. SpawnInstance\_ is generally reserved for - instances which are passed as parameters to such - `.Create` methods, a common example being the - `Win32_SecurityDescriptor`, passed to `Win32_Share.Create` - and other instances which need security. - - The example here is `Win32_ProcessStartup`, which - controls the shown/hidden state etc. of a new - `Win32_Process` instance:: - - import win32con - import wmi - c = wmi.WMI () - startup = c.Win32_ProcessStartup.new (ShowWindow=win32con.SW_SHOWMINIMIZED) - pid, retval = c.Win32_Process.Create ( - CommandLine="notepad.exe", - ProcessStartupInformation=startup - ) - - .. warning:: - previous versions of this docstring illustrated using this function - to create a new process. This is *not* a good example of its use; - it is better handled with something like the example above. - """ - try: - obj = _wmi_object (self.SpawnInstance_ (), self) - obj.set (**kwargs) - return obj - except pywintypes.com_error: - handle_com_error () - -# -# class _wmi_result -# -class _wmi_result: - """Simple, data only result for targeted WMI queries which request - data only result classes via fetch_as_classes. - """ - def __init__(self, obj, attributes): - if attributes: - for attr in attributes: - self.__dict__[attr] = obj.Properties_ (attr).Value - else: - for p in obj.Properties_: - attr = p.Name - self.__dict__[attr] = obj.Properties_(attr).Value - -# -# class WMI -# -class _wmi_namespace: - """A WMI root of a computer system. The classes attribute holds a list - of the classes on offer. This means you can explore a bit with - things like this:: - - c = wmi.WMI () - for i in c.classes: - if "user" in i.lower (): - print i - """ - def __init__ (self, namespace, find_classes): - _set (self, "_namespace", namespace) - # - # wmi attribute preserved for backwards compatibility - # - _set (self, "wmi", namespace) - - self._classes = None - self._classes_map = {} - # - # Pick up the list of classes under this namespace - # so that they can be queried, and used as though - # properties of the namespace by means of the __getattr__ - # hook below. - # If the namespace does not support SubclassesOf, carry on - # regardless - # - if find_classes: - _ = self.classes - - def __repr__ (self): - return "<_wmi_namespace: %s>" % self.wmi - - def __str__ (self): - return repr (self) - - def _get_classes (self): - if self._classes is None: - self._classes = self.subclasses_of () - return SelfDeprecatingDict (dict.fromkeys (self._classes)) - classes = property (_get_classes) - - def get (self, moniker): - try: - return _wmi_object (self.wmi.Get (moniker)) - except pywintypes.com_error: - handle_com_error () - - def handle (self): - """The raw OLE object representing the WMI namespace""" - return self._namespace - - def subclasses_of (self, root="", regex=r".*"): - try: - SubclassesOf = self._namespace.SubclassesOf - except AttributeError: - return set () - else: - return set ( - c.Path_.Class - for c in SubclassesOf (root) - if re.match (regex, c.Path_.Class) - ) - - def instances (self, class_name): - """Return a list of instances of the WMI class. This is - (probably) equivalent to querying with no qualifiers:: - - wmi.WMI ().instances ("Win32_LogicalDisk") - # should be the same as - wmi.WMI ().Win32_LogicalDisk () - """ - try: - return [_wmi_object (obj) for obj in self._namespace.InstancesOf (class_name)] - except pywintypes.com_error: - handle_com_error () - - def new (self, wmi_class, **kwargs): - """This is now implemented by a call to :meth:`_wmi_class.new`""" - return getattr (self, wmi_class).new (**kwargs) - - new_instance_of = new - - def _raw_query (self, wql): - """Execute a WQL query and return its raw results. Use the flags - recommended by Microsoft to achieve a read-only, semi-synchronous - query where the time is taken while looping through. - NB Backslashes need to be doubled up. - """ - flags = wbemFlagReturnImmediately | wbemFlagForwardOnly - wql = wql.replace ("\\", "\\\\") - try: - return self._namespace.ExecQuery (strQuery=wql, iFlags=flags) - except pywintypes.com_error: - handle_com_error () - - def query (self, wql, instance_of=None, fields=[]): - """Perform an arbitrary query against a WMI object, and return - a list of _wmi_object representations of the results. - """ - return [ _wmi_object (obj, instance_of, fields) for obj in self._raw_query(wql) ] - - def fetch_as_classes (self, wmi_classname, fields=(), **where_clause): - """Build and execute a wql query to fetch the specified list of fields from - the specified wmi_classname + where_clause, then return the results as - a list of simple class instances with attributes matching field_list. - - If fields is left empty, select * and pre-load all class attributes for - each class returned. - """ - wql = "SELECT %s FROM %s" % (fields and ", ".join (fields) or "*", wmi_classname) - if where_clause: - wql += " WHERE " + " AND ".join (["%s = '%s'" % (k, v) for k, v in where_clause.items()]) - return [_wmi_result (obj, fields) for obj in self._raw_query(wql)] - - def fetch_as_lists (self, wmi_classname, fields, **where_clause): - """Build and execute a wql query to fetch the specified list of fields from - the specified wmi_classname + where_clause, then return the results as - a list of lists whose values correspond to field_list. - """ - wql = "SELECT %s FROM %s" % (", ".join (fields), wmi_classname) - if where_clause: - wql += " WHERE " + " AND ".join (["%s = '%s'" % (k, v) for k, v in where_clause.items()]) - results = [] - for obj in self._raw_query(wql): - results.append ([obj.Properties_ (field).Value for field in fields]) - return results - - def watch_for ( - self, - raw_wql=None, - notification_type="operation", - wmi_class=None, - delay_secs=1, - fields=[], - **where_clause - ): - """Set up an event tracker on a WMI event. This function - returns an wmi_watcher which can be called to get the - next event:: - - c = wmi.WMI () - - raw_wql = "SELECT * FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'" - watcher = c.watch_for (raw_wql=raw_wql) - while 1: - process_created = watcher () - print process_created.Name - - # or - - watcher = c.watch_for ( - notification_type="Creation", - wmi_class="Win32_Process", - delay_secs=2, - Name='calc.exe' - ) - calc_created = watcher () - - Now supports timeout on the call to watcher:: - - import pythoncom - import wmi - c = wmi.WMI (privileges=["Security"]) - watcher1 = c.watch_for ( - notification_type="Creation", - wmi_class="Win32_NTLogEvent", - Type="error" - ) - watcher2 = c.watch_for ( - notification_type="Creation", - wmi_class="Win32_NTLogEvent", - Type="warning" - ) - - while 1: - try: - error_log = watcher1 (500) - except wmi.x_wmi_timed_out: - pythoncom.PumpWaitingMessages () - else: - print error_log - - try: - warning_log = watcher2 (500) - except wmi.x_wmi_timed_out: - pythoncom.PumpWaitingMessages () - else: - print warning_log - """ - if isinstance (wmi_class, _wmi_class): - class_name = wmi_class._class_name - else: - class_name = wmi_class - wmi_class = getattr (self, class_name) - is_extrinsic = "__ExtrinsicEvent" in wmi_class.derivation () - if raw_wql: - wql = raw_wql - else: - fields = set (['TargetInstance'] + fields) - field_list = ", ".join (fields) or "*" - if is_extrinsic: - if where_clause: - where = " WHERE " + " AND ".join (["%s = '%s'" % (k, v) for k, v in where_clause.items ()]) - else: - where = "" - wql = "SELECT " + field_list + " FROM " + class_name + where - else: - if where_clause: - where = " AND " + " AND ".join (["TargetInstance.%s = '%s'" % (k, v) for k, v in where_clause.items ()]) - else: - where = "" - wql = \ - "SELECT %s FROM __Instance%sEvent WITHIN %d WHERE TargetInstance ISA '%s' %s" % \ - (field_list, notification_type, delay_secs, class_name, where) - - try: - return _wmi_watcher ( - self._namespace.ExecNotificationQuery (wql), - is_extrinsic=is_extrinsic, - fields=fields - ) - except pywintypes.com_error: - handle_com_error () - - def __getattr__ (self, attribute): - """Offer WMI classes as simple attributes. Pass through any untrapped - unattribute to the underlying OLE object. This means that new or - unmapped functionality is still available to the module user. - """ - # - # Don't try to match against known classes as was previously - # done since the list may not have been requested - # (find_classes=False). - # - try: - return self._cached_classes (attribute) - except pywintypes.com_error: - return getattr (self._namespace, attribute) - - def _cached_classes (self, class_name): - """Standard caching helper which keeps track of classes - already retrieved by name and returns the existing object - if found. If this is the first retrieval, store it and - pass it back - """ - if class_name not in self._classes_map: - self._classes_map[class_name] = _wmi_class (self, self._namespace.Get (class_name)) - return self._classes_map[class_name] - - def _getAttributeNames (self): - """Return list of classes for IPython completion engine""" - return [x for x in self.classes if not x.startswith ('__')] - -# -# class _wmi_watcher -# -class _wmi_watcher: - """Helper class for WMI.watch_for below (qv)""" - - _event_property_map = { - "TargetInstance" : _wmi_object, - "PreviousInstance" : _wmi_object - } - def __init__ (self, wmi_event, is_extrinsic, fields=[]): - self.wmi_event = wmi_event - self.is_extrinsic = is_extrinsic - self.fields = fields - - def __call__ (self, timeout_ms=-1): - """When called, return the instance which caused the event. Supports - timeout in milliseconds (defaulting to infinite). If the watcher - times out, :exc:`x_wmi_timed_out` is raised. This makes it easy to support - watching for multiple objects. - """ - try: - event = self.wmi_event.NextEvent (timeout_ms) - if self.is_extrinsic: - return _wmi_event (event, None, self.fields) - else: - return _wmi_event ( - event.Properties_ ("TargetInstance").Value, - _wmi_object (event, property_map=self._event_property_map), - self.fields - ) - except pywintypes.com_error: - handle_com_error () - -PROTOCOL = "winmgmts:" -def connect ( - computer="", - impersonation_level="", - authentication_level="", - authority="", - privileges="", - moniker="", - wmi=None, - namespace="", - suffix="", - user="", - password="", - find_classes=False, - debug=False -): - """The WMI constructor can either take a ready-made moniker or as many - parts of one as are necessary. Eg:: - - c = wmi.WMI (moniker="winmgmts:{impersonationLevel=Delegate}//remote") - # or - c = wmi.WMI (computer="remote", privileges=["!RemoteShutdown", "Security"]) - - I daren't link to a Microsoft URL; they change so often. Try Googling for - WMI construct moniker and see what it comes back with. - - For complete control, a named argument "wmi" can be supplied, which - should be a SWbemServices object, which you create yourself. Eg:: - - loc = win32com.client.Dispatch("WbemScripting.SWbemLocator") - svc = loc.ConnectServer(...) - c = wmi.WMI(wmi=svc) - - This is the only way of connecting to a remote computer with a different - username, as the moniker syntax does not allow specification of a user - name. - - If the `wmi` parameter is supplied, all other parameters are ignored. - """ - global _DEBUG - _DEBUG = debug - - try: - try: - if wmi: - obj = wmi - - elif moniker: - if not moniker.startswith (PROTOCOL): - moniker = PROTOCOL + moniker - obj = GetObject (moniker) - - else: - if user: - if privileges or suffix: - raise x_wmi_authentication ("You can't specify privileges or a suffix as well as a username") - elif computer in (None, '', '.'): - raise x_wmi_authentication ("You can only specify user/password for a remote connection") - else: - obj = connect_server ( - server=computer, - namespace=namespace, - user=user, - password=password, - authority=authority, - impersonation_level=impersonation_level, - authentication_level=authentication_level - ) - - else: - moniker = construct_moniker ( - computer=computer, - impersonation_level=impersonation_level, - authentication_level=authentication_level, - authority=authority, - privileges=privileges, - namespace=namespace, - suffix=suffix - ) - obj = GetObject (moniker) - - wmi_type = get_wmi_type (obj) - - if wmi_type == "namespace": - return _wmi_namespace (obj, find_classes) - elif wmi_type == "class": - return _wmi_class (None, obj) - elif wmi_type == "instance": - return _wmi_object (obj) - else: - raise x_wmi ("Unknown moniker type") - - except pywintypes.com_error: - handle_com_error () - - except x_wmi_uninitialised_thread: - raise x_wmi_uninitialised_thread ("WMI returned a syntax error: you're probably running inside a thread without first calling pythoncom.CoInitialize[Ex]") - -WMI = connect - -def construct_moniker ( - computer=None, - impersonation_level=None, - authentication_level=None, - authority=None, - privileges=None, - namespace=None, - suffix=None -): - security = [] - if impersonation_level: security.append ("impersonationLevel=%s" % impersonation_level) - if authentication_level: security.append ("authenticationLevel=%s" % authentication_level) - # - # Use of the authority descriptor is invalid on the local machine - # - if authority and computer: security.append ("authority=%s" % authority) - if privileges: security.append ("(%s)" % ", ".join (privileges)) - - moniker = [PROTOCOL] - if security: moniker.append ("{%s}!" % ",".join (security)) - if computer: moniker.append ("//%s/" % computer) - if namespace: - parts = re.split (r"[/\\]", namespace) - if parts[0] != 'root': - parts.insert (0, "root") - moniker.append ("/".join (parts)) - if suffix: moniker.append (":%s" % suffix) - return "".join (moniker) - -def get_wmi_type (obj): - try: - path = obj.Path_ - except AttributeError: - return "namespace" - else: - if path.IsClass: - return "class" - else: - return "instance" - -def connect_server ( - server, - namespace = "", - user = "", - password = "", - locale = "", - authority = "", - impersonation_level="", - authentication_level="", - security_flags = 0x80, - named_value_set = None -): - """Return a remote server running WMI - - :param server: name of the server - :param namespace: namespace to connect to - defaults to whatever's defined as default - :param user: username to connect as, either local or domain (dom\\name or user@domain for XP) - :param password: leave blank to use current context - :param locale: desired locale in form MS_XXXX (eg MS_409 for Am En) - :param authority: either "Kerberos:" or an NT domain. Not needed if included in user - :param impersonation_level: valid WMI impersonation level - :param security_flags: if 0, connect will wait forever; if 0x80, connect will timeout at 2 mins - :param named_value_set: typically empty, otherwise a context-specific `SWbemNamedValueSet` - - Example:: - - remote_connetion = wmi.connect_server ( - server="remote_machine", user="myname", password="mypassword" - ) - c = wmi.WMI (wmi=remote_connection) - """ - # - # Thanks to Matt Mercer for example code to set - # impersonation & authentication on ConnectServer - # - if impersonation_level: - try: - impersonation = getattr (obj._constants, "wbemImpersonationLevel%s" % impersonation_level.title ()) - except AttributeError: - raise x_wmi_authentication ("No such impersonation level: %s" % impersonation_level) - else: - impersonation = None - - if authentication_level: - try: - authentication = getattr (obj._constants, "wbemAuthenticationLevel%s" % authentication_level.title ()) - except AttributeError: - raise x_wmi_authentication ("No such impersonation level: %s" % impersonation_level) - else: - authentication = None - - server = Dispatch ("WbemScripting.SWbemLocator").\ - ConnectServer ( - server, - namespace, - user, - password, - locale, - authority, - security_flags, - named_value_set - ) - if impersonation: - server.Security_.ImpersonationLevel = impersonation - if authentication: - server.Security_.AuthenticationLevel = authentication - return server - -def Registry ( - computer=None, - impersonation_level="Impersonate", - authentication_level="Default", - authority=None, - privileges=None, - moniker=None -): - - warnings.warn ("This function can be implemented using wmi.WMI (namespace='DEFAULT').StdRegProv", DeprecationWarning) - if not moniker: - moniker = construct_moniker ( - computer=computer, - impersonation_level=impersonation_level, - authentication_level=authentication_level, - authority=authority, - privileges=privileges, - namespace="default", - suffix="StdRegProv" - ) - - try: - return _wmi_object (GetObject (moniker)) - - except pywintypes.com_error: - handle_com_error () - -# -# Typical use test -# -if __name__ == '__main__': - system = WMI () - for my_computer in system.Win32_ComputerSystem (): - print ("Disks on", my_computer.Name) - for disk in system.Win32_LogicalDisk (): - print (disk.Caption, disk.Description, disk.ProviderName or "") - diff -Nru python-psutil-0.1.3/README python-psutil-0.2.1/README --- python-psutil-0.1.3/README 2009-09-14 08:18:38.000000000 +0000 +++ python-psutil-0.2.1/README 2011-03-20 20:51:13.000000000 +0000 @@ -1,123 +1,120 @@ - -Introduction -============ - -psutil is a module providing an interface for retrieving information on running -processes in a portable way by using Python. -It currently supports Linux, OS X, FreeBSD and Windows. - -psutil website is at http://code.google.com/p/psutil/ - -The following document describes how to compile and install psutil from sources -on different platforms. - - -Using easy_install -================== - -The easiest way to install psutil from sources is using easy_install. -Get the latest easy_install version from http://pypi.python.org/pypi/setuptools -and just run: - - > python easy_install psutil - -This should get the most updated psutil version from the Python pypi repository, -unpack it, compile it and install it automatically. - - -Installing on Windows using mingw32 -=================================== - -After the mingw [1] environment is properly set up on your system you can -compile Windows sources by entering: - - > setup.py build -c mingw32 - -To compile and install just append the "install" keyword at the end of the -command line above, like this: - - > setup.py build -c mingw32 install - -It might be possible that distutils will complain about missing gcc executable. -That means you have to add mingw bin PATH variable first. -Entering this line in the command prompt should do the work: - - > SET PATH=C:\MinGW\bin;%PATH% - -NOTE: this assumes MinGW is installed in C:\MinGW, if not simply replace the -path in the command above with an appropriate location. - -[1] http://www.mingw.org/ - -NOTE #2: as of right now all Windows functionnalities are written in pure C -and no third party modules are required to be installed with the exception of -the "username" functionnality which requires pywin32 extension to be installed -separately. - - -Installing on Windows using Visual Studio -========================================= - -To use Visual Studio to install psutil, you must have the same version of -Visual Studio used to compile your installation of Python. For older versions -of Python that will be Visual Studio 2003. For 2.6 and later it should be -Visual Studio 2008. If you do not have the requisite version of Visual Studio -available then it is recommended to use MinGW to compile psutil instead. - -If you do have Visual Studio installed, you can use the basic distutils -commands: - - > setup.py build - -or to install and build: - - > setup.py install - -distutils should take care of any necessary magic to compile from there. - - -Installing on OS X -================== - -OS X installation from source will require gcc which you can obtain as part of -the 'XcodeTools' installer from Apple. Then you can run the standard distutils -commands: - -to build only: - - > ./setup.py build - -to install and build: - - > ./setup.py install - -NOTE: due to developer's hardware limitations psutil has only been compiled and -tested on OS X 10.4.11 so may or may not work on other versions. - - -Installing on FreeBSD -===================== - -The same compiler used to install Python must be present on the system in order -to build modules using distutils. Assuming it is installed, you can build using -the standard distutils commands: - -build only: - - > ./setup.py build - -install and build: - - > ./setup.py install - - -Installing on Linux -=================== - -Standard distutils installation steps should apply here. At the current time -the Linux port of psutil does not require any C modules, so can be installed -without need for a compiler using disutils: - -install/build: - - > ./setup.py install + +Introduction +============ + +psutil is a module providing an interface for retrieving information on running +processes in a portable way by using Python. +It currently supports Linux, OS X, FreeBSD and Windows. + +psutil website is at http://code.google.com/p/psutil/ + +The following document describes how to compile and install psutil from sources +on different platforms. + + +Using easy_install +================== + +The easiest way to install psutil from sources is using easy_install. +Get the latest easy_install version from http://pypi.python.org/pypi/setuptools +and just run: + + > 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 + diff -Nru python-psutil-0.1.3/setup.py python-psutil-0.2.1/setup.py --- python-psutil-0.1.3/setup.py 2009-09-15 00:42:26.000000000 +0000 +++ python-psutil-0.2.1/setup.py 2011-03-20 20:46:59.000000000 +0000 @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# $Id: setup.py 531 2010-03-02 20:54:22Z billiejoex $ +# $Id: setup.py 955 2011-03-20 20:46:58Z g.rodola $ # import sys @@ -8,6 +8,8 @@ import shutil from distutils.core import setup, Extension +__ver__ = "0.2.1" + # Hack for Python 3 to tell distutils to run 2to3 against the files # copied in the build directory before installing. # Reference: http://osdir.com/ml/python.python-3000.cvs/2008-03/msg00127.html @@ -17,6 +19,11 @@ from distutils.command.build_py import build_py +if os.name == 'posix': + posix_extension = Extension('_psutil_posix', + sources = ['psutil/_psutil_posix.c']) + + # Windows if sys.platform.lower().startswith("win"): @@ -24,41 +31,56 @@ maj,min = sys.getwindowsversion()[0:2] return '0x0%s' % ((maj * 100) + min) - extensions = Extension('_psutil_mswindows', - sources=['psutil/_psutil_mswindows.c', - 'psutil/arch/mswindows/process_info.c', - 'psutil/arch/mswindows/security.c'], - define_macros=[('_WIN32_WINNT', get_winver()), - ('_AVAIL_WINVER_', get_winver())], - libraries=["psapi", "kernel32", "advapi32", "shell32", - "netapi32"] - ) + extensions = [Extension('_psutil_mswindows', + sources=['psutil/_psutil_mswindows.c', + 'psutil/_psutil_common.c', + 'psutil/arch/mswindows/process_info.c', + 'psutil/arch/mswindows/process_handles.c', + 'psutil/arch/mswindows/security.c'], + define_macros=[('_WIN32_WINNT', get_winver()), + ('_AVAIL_WINVER_', get_winver())], + libraries=["psapi", "kernel32", "advapi32", "shell32", + "netapi32"] + )] # OS X elif sys.platform.lower().startswith("darwin"): - extensions = Extension('_psutil_osx', - sources = ['psutil/_psutil_osx.c', - 'psutil/arch/osx/process_info.c'] - ) + extensions = [Extension('_psutil_osx', + sources = ['psutil/_psutil_osx.c', + 'psutil/_psutil_common.c', + 'psutil/arch/osx/process_info.c'] + ), + posix_extension] # FreeBSD elif sys.platform.lower().startswith("freebsd"): - extensions = Extension('_psutil_bsd', - sources = ['psutil/_psutil_bsd.c', - 'psutil/arch/bsd/process_info.c'] - ) -# Others + extensions = [Extension('_psutil_bsd', + sources = ['psutil/_psutil_bsd.c', + 'psutil/_psutil_common.c', + 'psutil/arch/bsd/process_info.c'] + ), + posix_extension] +# Linux +elif sys.platform.lower().startswith("linux"): + extensions = [Extension('_psutil_linux', + sources=['psutil/_psutil_linux.c'], + libraries=[] + ), + posix_extension] + else: - extensions = None + raise NotImplementedError('platform %s is not supported' % sys.platform) def main(): setup_args = dict( name='psutil', - version='0.1.3', + version=__ver__, + download_url="http://psutil.googlecode.com/files/psutil-%s.tar.gz" % __ver__, description='A process utilities module for Python', long_description=""" psutil is a module providing convenience functions for managing processes in a portable way by using Python.""", - keywords=['psutil', 'ps', 'top', 'process', 'utility'], + keywords=['psutil', 'ps', 'top', 'process', 'utility', 'lsof', 'top', + 'netstat', 'taskmgr'], author='Giampaolo Rodola, Dave Daeschler, Jay Loden', author_email='psutil-dev@googlegroups.com', url='http://code.google.com/p/psutil/', @@ -67,11 +89,12 @@ packages=['psutil'], cmdclass={'build_py':build_py}, # Python 3.X classifiers=[ - 'Development Status :: 2 - Pre-Alpha', + 'Development Status :: 5 - Production/Stable', 'Environment :: Console', - 'Operating System :: MacOS', - 'Operating System :: Microsoft :: Windows', + 'Operating System :: MacOS :: MacOS X', + 'Operating System :: Microsoft :: Windows :: Windows NT/2000', 'Operating System :: POSIX :: Linux', + 'Operating System :: POSIX :: BSD :: FreeBSD', 'Operating System :: OS Independent', 'Programming Language :: C', 'Programming Language :: Python', @@ -79,10 +102,14 @@ 'Programming Language :: Python :: 2.4', 'Programming Language :: Python :: 2.5', 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.0', 'Programming Language :: Python :: 3.1', + 'Programming Language :: Python :: 3.2', 'Topic :: System :: Monitoring', + 'Topic :: System :: Networking', + 'Topic :: System :: Benchmark', 'Topic :: System :: Systems Administration', 'Topic :: Utilities', 'Topic :: Software Development :: Libraries :: Python Modules', @@ -92,10 +119,11 @@ ], ) if extensions is not None: - setup_args["ext_modules"] = [extensions] + setup_args["ext_modules"] = extensions setup(**setup_args) if __name__ == '__main__': main() + diff -Nru python-psutil-0.1.3/test/_bsd.py python-psutil-0.2.1/test/_bsd.py --- python-psutil-0.1.3/test/_bsd.py 2009-09-14 22:50:56.000000000 +0000 +++ python-psutil-0.2.1/test/_bsd.py 2011-03-20 20:23:06.000000000 +0000 @@ -1,134 +1,145 @@ -#!/usr/bin/env python - -import unittest -import subprocess -import time -import re -import sys - -import psutil - -from test_psutil import kill, PYTHON, DEVNULL -from _posix import ps - - -def sysctl(cmdline): - """Expects a sysctl command with an argument and parse the result - returning only the value of interest. - """ - p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) - result = p.communicate()[0].strip().split()[1] - if sys.version_info >= (3,): - result = str(result, sys.stdout.encoding) - try: - return int(result) - except ValueError: - return result - -def parse_sysctl_vmtotal(output): - """Parse sysctl vm.vmtotal output returning total and free memory - values. - """ - line = output.split('\n')[4] # our line of interest - mobj = re.match(r'Virtual\s+Memory.*Total:\s+(\d+)K,\s+Active\s+(\d+)K.*', line) - total, active = mobj.groups() - # values are represented in kilo bytes - total = int(total) * 1024 - active = int(active) * 1024 - free = total - active - return total, free - - -class BSDSpecificTestCase(unittest.TestCase): - - def setUp(self): - self.pid = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL).pid - - def tearDown(self): - kill(self.pid) - - def test_TOTAL_PHYMEM(self): - sysctl_hwphymem = sysctl('sysctl hw.physmem') - self.assertEqual(sysctl_hwphymem, psutil.TOTAL_PHYMEM) - - def test_avail_phymem(self): - # This test is not particularly accurate and may fail if the OS is - # consuming memory for other applications. - # We just want to test that the difference between psutil result - # and sysctl's is not too high. - _sum = sum((sysctl("sysctl vm.stats.vm.v_inactive_count"), - sysctl("sysctl vm.stats.vm.v_cache_count"), - sysctl("sysctl vm.stats.vm.v_free_count") - )) - _pagesize = sysctl("sysctl hw.pagesize") - sysctl_avail_phymem = _sum * _pagesize - psutil_avail_phymem = psutil.avail_phymem() - difference = abs(psutil_avail_phymem - sysctl_avail_phymem) - # On my system both sysctl and psutil report the same values. - # Let's use a tollerance of 0.5 MB and consider the test as failed - # if we go over it. - if difference > (0.5 * 2**20): - self.fail("sysctl=%s; psutil=%s; difference=%s;" %( - sysctl_avail_phymem, psutil_avail_phymem, difference)) - - def test_total_virtmem(self): - # This test is not particularly accurate and may fail if the OS is - # consuming memory for other applications. - # We just want to test that the difference between psutil result - # and sysctl's is not too high. - p = subprocess.Popen("sysctl vm.vmtotal", shell=1, stdout=subprocess.PIPE) - result = p.communicate()[0].strip() - if sys.version_info >= (3,): - result = str(result, sys.stdout.encoding) - sysctl_total_virtmem, _ = parse_sysctl_vmtotal(result) - psutil_total_virtmem = psutil.total_virtmem() - difference = abs(sysctl_total_virtmem - psutil_total_virtmem) - - # On my system I get a difference of 4657152 bytes, probably because - # the system is consuming memory for this same test. - # Assuming psutil is right, let's use a tollerance of 10 MB and consider - # the test as failed if we go over it. - if difference > (10 * 2**20): - self.fail("sysctl=%s; psutil=%s; difference=%s;" %( - sysctl_total_virtmem, psutil_total_virtmem, difference) - ) - - def test_avail_virtmem(self): - # This test is not particularly accurate and may fail if the OS is - # consuming memory for other applications. - # We just want to test that the difference between psutil result - # and sysctl's is not too high. - p = subprocess.Popen("sysctl vm.vmtotal", shell=1, stdout=subprocess.PIPE) - result = p.communicate()[0].strip() - if sys.version_info >= (3,): - result = str(result, sys.stdout.encoding) - _, sysctl_avail_virtmem = parse_sysctl_vmtotal(result) - psutil_avail_virtmem = psutil.avail_virtmem() - difference = abs(sysctl_avail_virtmem - psutil_avail_virtmem) - # let's assume the test is failed if difference is > 0.5 MB - if difference > (0.5 * 2**20): - self.fail("sysctl=%s; psutil=%s; difference=%s;" %( - sysctl_avail_virtmem, psutil_avail_virtmem, difference) - ) - - def test_process_create_time(self): - cmdline = "ps -o lstart -p %s" %self.pid - p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0] - if sys.version_info >= (3,): - output = str(output, sys.stdout.encoding) - start_ps = output.replace('STARTED', '').strip() - start_psutil = psutil.Process(self.pid).create_time - start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", - time.localtime(start_psutil)) - self.assertEqual(start_ps, start_psutil) - - -if __name__ == '__main__': - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase)) - unittest.TextTestRunner(verbosity=2).run(test_suite) - - - - +#!/usr/bin/env python +# +# $Id: _bsd.py 950 2011-03-19 21:37:09Z g.rodola $ +# + +import unittest +import subprocess +import time +import re +import sys + +import psutil + +from test_psutil import reap_children, get_test_subprocess +from _posix import ps + + +def sysctl(cmdline): + """Expects a sysctl command with an argument and parse the result + returning only the value of interest. + """ + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + result = p.communicate()[0].strip() + if sys.version_info >= (3,): + result = result[result.find(b": ") + 2:] + result = str(result, sys.stdout.encoding) + else: + result = result[result.find(": ") + 2:] + try: + return int(result) + except ValueError: + return result + +def parse_sysctl_vmtotal(output): + """Parse sysctl vm.vmtotal output returning total and free memory + values. + """ + line = output.split('\n')[4] # our line of interest + mobj = re.match(r'Virtual\s+Memory.*Total:\s+(\d+)K,\s+Active\s+(\d+)K.*', line) + total, active = mobj.groups() + # values are represented in kilo bytes + total = int(total) * 1024 + active = int(active) * 1024 + free = total - active + return total, free + + +class BSDSpecificTestCase(unittest.TestCase): + + def setUp(self): + self.pid = get_test_subprocess().pid + + def tearDown(self): + reap_children() + + def test_TOTAL_PHYMEM(self): + sysctl_hwphymem = sysctl('sysctl hw.physmem') + self.assertEqual(sysctl_hwphymem, psutil.TOTAL_PHYMEM) + + def test_BOOT_TIME(self): + s = sysctl('sysctl kern.boottime') + s = s[s.find(" sec = ") + 7:] + s = s[:s.find(',')] + btime = int(s) + self.assertEqual(btime, psutil.BOOT_TIME) + + def test_avail_phymem(self): + # This test is not particularly accurate and may fail if the OS is + # consuming memory for other applications. + # We just want to test that the difference between psutil result + # and sysctl's is not too high. + _sum = sum((sysctl("sysctl vm.stats.vm.v_inactive_count"), + sysctl("sysctl vm.stats.vm.v_cache_count"), + sysctl("sysctl vm.stats.vm.v_free_count") + )) + _pagesize = sysctl("sysctl hw.pagesize") + sysctl_avail_phymem = _sum * _pagesize + psutil_avail_phymem = psutil.avail_phymem() + difference = abs(psutil_avail_phymem - sysctl_avail_phymem) + # On my system both sysctl and psutil report the same values. + # Let's use a tollerance of 0.5 MB and consider the test as failed + # if we go over it. + if difference > (0.5 * 2**20): + self.fail("sysctl=%s; psutil=%s; difference=%s;" %( + sysctl_avail_phymem, psutil_avail_phymem, difference)) + + def test_total_virtmem(self): + # This test is not particularly accurate and may fail if the OS is + # consuming memory for other applications. + # We just want to test that the difference between psutil result + # and sysctl's is not too high. + p = subprocess.Popen("sysctl vm.vmtotal", shell=1, stdout=subprocess.PIPE) + result = p.communicate()[0].strip() + if sys.version_info >= (3,): + result = str(result, sys.stdout.encoding) + sysctl_total_virtmem, _ = parse_sysctl_vmtotal(result) + psutil_total_virtmem = psutil.total_virtmem() + difference = abs(sysctl_total_virtmem - psutil_total_virtmem) + + # On my system I get a difference of 4657152 bytes, probably because + # the system is consuming memory for this same test. + # Assuming psutil is right, let's use a tollerance of 10 MB and consider + # the test as failed if we go over it. + if difference > (10 * 2**20): + self.fail("sysctl=%s; psutil=%s; difference=%s;" %( + sysctl_total_virtmem, psutil_total_virtmem, difference)) + + def test_avail_virtmem(self): + # This test is not particularly accurate and may fail if the OS is + # consuming memory for other applications. + # We just want to test that the difference between psutil result + # and sysctl's is not too high. + p = subprocess.Popen("sysctl vm.vmtotal", shell=1, stdout=subprocess.PIPE) + result = p.communicate()[0].strip() + if sys.version_info >= (3,): + result = str(result, sys.stdout.encoding) + _, sysctl_avail_virtmem = parse_sysctl_vmtotal(result) + psutil_avail_virtmem = psutil.avail_virtmem() + difference = abs(sysctl_avail_virtmem - psutil_avail_virtmem) + # let's assume the test is failed if difference is > 0.5 MB + if difference > (0.5 * 2**20): + self.fail("sysctl=%s; psutil=%s; difference=%s;" %( + sysctl_avail_virtmem, psutil_avail_virtmem, difference)) + + def test_process_create_time(self): + cmdline = "ps -o lstart -p %s" %self.pid + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0] + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + start_ps = output.replace('STARTED', '').strip() + start_psutil = psutil.Process(self.pid).create_time + start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", + time.localtime(start_psutil)) + self.assertEqual(start_ps, start_psutil) + + +if __name__ == '__main__': + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(BSDSpecificTestCase)) + unittest.TextTestRunner(verbosity=2).run(test_suite) + + + + diff -Nru python-psutil-0.1.3/test/_linux.py python-psutil-0.2.1/test/_linux.py --- python-psutil-0.1.3/test/_linux.py 2009-09-14 22:50:56.000000000 +0000 +++ python-psutil-0.2.1/test/_linux.py 2011-03-20 20:23:06.000000000 +0000 @@ -1,21 +1,45 @@ -#!/usr/bin/env python - -import unittest -import subprocess -import time - -import psutil - -from test_psutil import PYTHON, DEVNULL, kill -from _posix import ps - - -class LinuxSpecificTestCase(unittest.TestCase): - - def setUp(self): - self.pid = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL).pid - - def tearDown(self): - kill(self.pid) - - +#!/usr/bin/env python +# +# $Id: _linux.py 707 2010-10-19 18:16:08Z g.rodola $ +# + +import unittest +import subprocess +import sys + +import psutil + + +class LinuxSpecificTestCase(unittest.TestCase): + + def test_cached_phymem(self): + # test psutil.cached_phymem against "cached" column of free + # command line utility + p = subprocess.Popen("free", shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + free_cmem = int(output.split('\n')[1].split()[6]) + psutil_cmem = psutil.cached_phymem() / 1024 + self.assertEqual(free_cmem, psutil_cmem) + + def test_phymem_buffers(self): + # test psutil.phymem_buffers against "buffers" column of free + # command line utility + p = subprocess.Popen("free", shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + free_cmem = int(output.split('\n')[1].split()[5]) + psutil_cmem = psutil.phymem_buffers() / 1024 + self.assertEqual(free_cmem, psutil_cmem) + + +if __name__ == '__main__': + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(LinuxSpecificTestCase)) + unittest.TextTestRunner(verbosity=2).run(test_suite) + + + + diff -Nru python-psutil-0.1.3/test/_osx.py python-psutil-0.2.1/test/_osx.py --- python-psutil-0.1.3/test/_osx.py 2009-09-14 22:50:56.000000000 +0000 +++ python-psutil-0.2.1/test/_osx.py 2011-03-20 20:23:06.000000000 +0000 @@ -1,59 +1,64 @@ -import unittest -import subprocess -import time -import re -import sys - -import psutil - -from test_psutil import kill, PYTHON, DEVNULL -#from _posix import ps - - -def sysctl(cmdline): - """Expects a sysctl command with an argument and parse the result - returning only the value of interest. - """ - p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) - result = p.communicate()[0].strip().split()[1] - if sys.version_info >= (3,): - result = str(result, sys.stdout.encoding) - try: - return int(result) - except ValueError: - return result - - -class OSXSpecificTestCase(unittest.TestCase): - - def setUp(self): - self.pid = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL).pid - - def tearDown(self): - kill(self.pid) - - def test_TOTAL_PHYMEM(self): - sysctl_hwphymem = sysctl('sysctl hw.physmem') - self.assertEqual(sysctl_hwphymem, psutil.TOTAL_PHYMEM) - - def test_process_create_time(self): - cmdline = "ps -o lstart -p %s" %self.pid - p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0] - if sys.version_info >= (3,): - output = str(output, sys.stdout.encoding) - start_ps = output.replace('STARTED', '').strip() - start_psutil = psutil.Process(self.pid).create_time - start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", - time.localtime(start_psutil)) - self.assertEqual(start_ps, start_psutil) - - -if __name__ == '__main__': - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(OSXSpecificTestCase)) - unittest.TextTestRunner(verbosity=2).run(test_suite) - - - - +#!/usr/bin/env python +# +# $Id: _osx.py 818 2010-11-13 19:52:38Z jloden $ +# + +import unittest +import subprocess +import time +import re +import sys + +import psutil + +from test_psutil import reap_children, get_test_subprocess +#from _posix import ps + + +def sysctl(cmdline): + """Expects a sysctl command with an argument and parse the result + returning only the value of interest. + """ + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + result = p.communicate()[0].strip().split()[1] + if sys.version_info >= (3,): + result = str(result, sys.stdout.encoding) + try: + return int(result) + except ValueError: + return result + + +class OSXSpecificTestCase(unittest.TestCase): + + def setUp(self): + self.pid = get_test_subprocess().pid + + def tearDown(self): + reap_children() + + def test_TOTAL_PHYMEM(self): + sysctl_hwphymem = sysctl('sysctl hw.memsize') + self.assertEqual(sysctl_hwphymem, psutil.TOTAL_PHYMEM) + + def test_process_create_time(self): + cmdline = "ps -o lstart -p %s" %self.pid + p = subprocess.Popen(cmdline, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0] + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + start_ps = output.replace('STARTED', '').strip() + start_psutil = psutil.Process(self.pid).create_time + start_psutil = time.strftime("%a %b %e %H:%M:%S %Y", + time.localtime(start_psutil)) + self.assertEqual(start_ps, start_psutil) + + +if __name__ == '__main__': + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(OSXSpecificTestCase)) + unittest.TextTestRunner(verbosity=2).run(test_suite) + + + + diff -Nru python-psutil-0.1.3/test/_posix.py python-psutil-0.2.1/test/_posix.py --- python-psutil-0.1.3/test/_posix.py 2009-09-14 22:50:56.000000000 +0000 +++ python-psutil-0.2.1/test/_posix.py 2011-03-20 20:28:37.000000000 +0000 @@ -1,133 +1,152 @@ -#!/usr/bin/env python - -import unittest -import subprocess -import time -import sys -import os - -import psutil - -from test_psutil import kill, PYTHON, DEVNULL - - -def ps(cmd): - """Expects a ps command with a -o argument and parse the result - returning only the value of interest. - """ - if not sys.platform.lower().startswith("linux"): - cmd = cmd.replace(" --no-headers ", " ") - p = subprocess.Popen(cmd, shell=1, stdout=subprocess.PIPE) - output = p.communicate()[0].strip() - if sys.version_info >= (3,): - output = str(output, sys.stdout.encoding) - if not sys.platform.lower().startswith("linux"): - output = output.split('\n')[1] - try: - return int(output) - except ValueError: - return output - - -class PosixSpecificTestCase(unittest.TestCase): - """Compare psutil results against 'ps' command line utility.""" - - # for ps -o arguments see: http://unixhelp.ed.ac.uk/CGI/man-cgi?ps - - def setUp(self): - self.pid = subprocess.Popen([PYTHON, "-E", "-O"], stdout=DEVNULL, stderr=DEVNULL).pid - - def tearDown(self): - kill(self.pid) - - def test_process_parent_pid(self): - ppid_ps = ps("ps --no-headers -o ppid -p %s" %self.pid) - ppid_psutil = psutil.Process(self.pid).ppid - self.assertEqual(ppid_ps, ppid_psutil) - - def test_process_uid(self): - uid_ps = ps("ps --no-headers -o uid -p %s" %self.pid) - uid_psutil = psutil.Process(self.pid).uid - self.assertEqual(uid_ps, uid_psutil) - - def test_process_gid(self): - gid_ps = ps("ps --no-headers -o rgid -p %s" %self.pid) - gid_psutil = psutil.Process(self.pid).gid - self.assertEqual(gid_ps, gid_psutil) - - def test_process_username(self): - username_ps = ps("ps --no-headers -o user -p %s" %self.pid) - username_psutil = psutil.Process(self.pid).username - self.assertEqual(username_ps, username_psutil) - - def test_process_rss_memory(self): - # give python interpreter some time to properly initialize - # so that the results are the same - time.sleep(0.1) - rss_ps = ps("ps --no-headers -o rss -p %s" %self.pid) - rss_psutil = psutil.Process(self.pid).get_memory_info()[0] / 1024 - self.assertEqual(rss_ps, rss_psutil) - - def test_process_vsz_memory(self): - # give python interpreter some time to properly initialize - # so that the results are the same - time.sleep(0.1) - vsz_ps = ps("ps --no-headers -o vsz -p %s" %self.pid) - vsz_psutil = psutil.Process(self.pid).get_memory_info()[1] / 1024 - self.assertEqual(vsz_ps, vsz_psutil) - - def test_process_name(self): - # use command + arg since "comm" keyword not supported on all platforms - name_ps = ps("ps --no-headers -o command -p %s" %self.pid).split(' ')[0] - # remove path if there is any, from the command - name_ps = os.path.basename(name_ps) - name_psutil = psutil.Process(self.pid).name - self.assertEqual(name_ps, name_psutil) - - def test_process_pathname(self): - ps_pathname = ps("ps --no-headers -o command -p %s" %self.pid).split(' ')[0] - psutil_pathname = os.path.join(psutil.Process(self.pid).path, - psutil.Process(self.pid).name) - self.assertEqual(ps_pathname, psutil_pathname) - - def test_process_cmdline(self): - ps_cmdline = ps("ps --no-headers -o command -p %s" %self.pid) - psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline) - self.assertEqual(ps_cmdline, psutil_cmdline) - - def test_get_pids(self): - # Note: this test might fail if the OS is starting/killing - # other processes in the meantime - p = subprocess.Popen(["ps", "ax", "-o", "pid"], stdout=subprocess.PIPE) - output = p.communicate()[0].strip() - if sys.version_info >= (3,): - output = str(output, sys.stdout.encoding) - output = output.replace('PID', '') - p.wait() - pids_ps = [] - for pid in output.split('\n'): - if pid: - pids_ps.append(int(pid.strip())) - # remove ps subprocess pid which is supposed to be dead in meantime - pids_ps.remove(p.pid) - # not all systems include pid 0 in their list but psutil does so - # we force it - if 0 not in pids_ps: - pids_ps.append(0) - - pids_psutil = psutil.get_pid_list() - pids_ps.sort() - pids_psutil.sort() - - if pids_ps != pids_psutil: - difference = filter(lambda x:x not in pids_ps, pids_psutil) + \ - filter(lambda x:x not in pids_psutil, pids_ps) - self.fail("difference: " + str(difference)) - - -if __name__ == '__main__': - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(PosixSpecificTestCase)) - unittest.TextTestRunner(verbosity=2).run(test_suite) - - +#!/usr/bin/env python +# +# $Id: _posix.py 935 2011-02-22 22:19:33Z g.rodola $ +# + +import unittest +import subprocess +import time +import sys +import os + +import psutil + +from test_psutil import (get_test_subprocess, reap_children, PYTHON, LINUX, OSX, + ignore_access_denied) + + +def ps(cmd): + """Expects a ps command with a -o argument and parse the result + returning only the value of interest. + """ + if not LINUX: + cmd = cmd.replace(" --no-headers ", " ") + p = subprocess.Popen(cmd, shell=1, stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + if not LINUX: + output = output.split('\n')[1] + try: + return int(output) + except ValueError: + return output + + +class PosixSpecificTestCase(unittest.TestCase): + """Compare psutil results against 'ps' command line utility.""" + + # for ps -o arguments see: http://unixhelp.ed.ac.uk/CGI/man-cgi?ps + + def setUp(self): + self.pid = get_test_subprocess([PYTHON, "-E", "-O"]).pid + + def tearDown(self): + reap_children() + + def test_process_parent_pid(self): + ppid_ps = ps("ps --no-headers -o ppid -p %s" %self.pid) + ppid_psutil = psutil.Process(self.pid).ppid + self.assertEqual(ppid_ps, ppid_psutil) + + def test_process_uid(self): + uid_ps = ps("ps --no-headers -o uid -p %s" %self.pid) + uid_psutil = psutil.Process(self.pid).uids.real + self.assertEqual(uid_ps, uid_psutil) + + def test_process_gid(self): + gid_ps = ps("ps --no-headers -o rgid -p %s" %self.pid) + gid_psutil = psutil.Process(self.pid).gids.real + self.assertEqual(gid_ps, gid_psutil) + + def test_process_username(self): + username_ps = ps("ps --no-headers -o user -p %s" %self.pid) + username_psutil = psutil.Process(self.pid).username + self.assertEqual(username_ps, username_psutil) + + @ignore_access_denied + def test_process_rss_memory(self): + # give python interpreter some time to properly initialize + # so that the results are the same + time.sleep(0.1) + rss_ps = ps("ps --no-headers -o rss -p %s" %self.pid) + rss_psutil = psutil.Process(self.pid).get_memory_info()[0] / 1024 + self.assertEqual(rss_ps, rss_psutil) + + @ignore_access_denied + def test_process_vsz_memory(self): + # give python interpreter some time to properly initialize + # so that the results are the same + time.sleep(0.1) + vsz_ps = ps("ps --no-headers -o vsz -p %s" %self.pid) + vsz_psutil = psutil.Process(self.pid).get_memory_info()[1] / 1024 + self.assertEqual(vsz_ps, vsz_psutil) + + def test_process_name(self): + # use command + arg since "comm" keyword not supported on all platforms + name_ps = ps("ps --no-headers -o command -p %s" %self.pid).split(' ')[0] + # remove path if there is any, from the command + name_ps = os.path.basename(name_ps) + name_psutil = psutil.Process(self.pid).name + if OSX: + self.assertEqual(name_psutil, "Python") + else: + self.assertEqual(name_ps, name_psutil) + + def test_process_exe(self): + ps_pathname = ps("ps --no-headers -o command -p %s" %self.pid).split(' ')[0] + psutil_pathname = psutil.Process(self.pid).exe + try: + self.assertEqual(ps_pathname, psutil_pathname) + except AssertionError: + # certain platforms such as BSD are more accurate returning: + # "/usr/local/bin/python2.7" + # ...instead of: + # "/usr/local/bin/python" + # We do not want to consider this difference in accuracy + # an error. + ps_extended_pathname = PYTHON + "%s.%s" % (sys.version_info.major, + sys.version_info.minor) + self.assertEqual(ps_extended_pathname, psutil_pathname) + + def test_process_cmdline(self): + ps_cmdline = ps("ps --no-headers -o command -p %s" %self.pid) + psutil_cmdline = " ".join(psutil.Process(self.pid).cmdline) + self.assertEqual(ps_cmdline, psutil_cmdline) + + def test_get_pids(self): + # Note: this test might fail if the OS is starting/killing + # other processes in the meantime + p = get_test_subprocess(["ps", "ax", "-o", "pid"], stdout=subprocess.PIPE) + output = p.communicate()[0].strip() + if sys.version_info >= (3,): + output = str(output, sys.stdout.encoding) + output = output.replace('PID', '') + p.wait() + pids_ps = [] + for pid in output.split('\n'): + if pid: + pids_ps.append(int(pid.strip())) + # remove ps subprocess pid which is supposed to be dead in meantime + pids_ps.remove(p.pid) + # not all systems include pid 0 in their list but psutil does so + # we force it + if 0 not in pids_ps: + pids_ps.append(0) + + pids_psutil = psutil.get_pid_list() + pids_ps.sort() + pids_psutil.sort() + + if pids_ps != pids_psutil: + difference = filter(lambda x:x not in pids_ps, pids_psutil) + \ + filter(lambda x:x not in pids_psutil, pids_ps) + self.fail("difference: " + str(difference)) + + +if __name__ == '__main__': + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(PosixSpecificTestCase)) + unittest.TextTestRunner(verbosity=2).run(test_suite) + + diff -Nru python-psutil-0.1.3/test/test_memory_leaks.py python-psutil-0.2.1/test/test_memory_leaks.py --- python-psutil-0.1.3/test/test_memory_leaks.py 2009-09-14 22:50:56.000000000 +0000 +++ python-psutil-0.2.1/test/test_memory_leaks.py 2011-03-20 20:28:37.000000000 +0000 @@ -1,17 +1,27 @@ #!/usr/bin/env python # -# $Id: test_memory_leaks.py 510 2010-02-22 21:43:18Z billiejoex $ +# $Id: test_memory_leaks.py 828 2010-11-20 20:52:07Z g.rodola $ # +""" +Note: this is targeted for python 2.x. +To run it under python 3.x you need to use 2to3 tool first: + +$ 2to3 -w test/test_memory_leaks.py +""" + + import os import gc import sys import unittest import psutil +from test_psutil import reap_children, skipUnless, skipIf, \ + POSIX, LINUX, WINDOWS, OSX, BSD LOOPS = 1000 -MARGIN = 4096 +TOLERANCE = 4096 class TestProcessObjectLeaks(unittest.TestCase): @@ -20,10 +30,13 @@ def setUp(self): gc.collect() + def tearDown(self): + reap_children() + def execute(self, method, *args, **kwarks): # step 1 + p = psutil.Process(os.getpid()) for x in xrange(LOOPS): - p = psutil.Process(os.getpid()) obj = getattr(p, method) if callable(obj): retvalue = obj(*args, **kwarks) @@ -34,8 +47,8 @@ rss1 = psutil.Process(os.getpid()).get_memory_info()[0] # step 2 + p = psutil.Process(os.getpid()) for x in xrange(LOOPS): - p = psutil.Process(os.getpid()) obj = getattr(p, method) if callable(obj): retvalue = obj(*args, **kwarks) @@ -47,45 +60,61 @@ # comparison difference = rss2 - rss1 - if difference > MARGIN: + if difference > TOLERANCE: self.fail("rss1=%s, rss2=%s, difference=%s" %(rss1, rss2, difference)) - def test_pid(self): - self.execute('pid') + def test_name(self): + self.execute('name') + + def test_cmdline(self): + self.execute('cmdline') + + def test_ppid(self): + self.execute('ppid') + + def test_uid(self): + self.execute('uid') - def test__str__(self): - # includes name, ppid, path, cmdline, uid, gid properties - self.execute('__str__') - - # XXX - disabled for Windows as it uses WMI which takes ages to complete - if not sys.platform.lower().startswith("win32"): - def test_username(self): - self.execute('username') + def test_uid(self): + self.execute('gid') + + @skipIf(POSIX) + def test_username(self): + self.execute('username') def test_create_time(self): self.execute('create_time') + def test_get_num_threads(self): + self.execute('get_num_threads') + + def test_get_threads(self): + self.execute('get_num_threads') + def test_get_cpu_times(self): self.execute('get_cpu_times') - def test_get_cpu_percent(self): - self.execute('get_cpu_percent') - def test_get_memory_info(self): self.execute('get_memory_info') - def test_get_memory_percent(self): - self.execute('get_memory_percent') - def test_is_running(self): self.execute('is_running') + @skipUnless(WINDOWS) def test_resume(self): self.execute('resume') - if hasattr(psutil.Process, "getcwd"): - def test_getcwd(self): - self.execute('getcwd') + @skipUnless(WINDOWS) + def test_getcwd(self): + self.execute('getcwd') + + @skipUnless(WINDOWS) + def test_get_open_files(self): + self.execute('get_open_files') + + @skipUnless(WINDOWS) + def test_get_connections(self): + self.execute('get_connections') class TestModuleFunctionsLeaks(unittest.TestCase): @@ -119,12 +148,13 @@ # comparison difference = rss2 - rss1 - if difference > MARGIN: + if difference > TOLERANCE: self.fail("rss1=%s, rss2=%s, difference=%s" %(rss1, rss2, difference)) def test_get_pid_list(self): self.execute('get_pid_list') + @skipIf(POSIX) def test_pid_exists(self): self.execute('pid_exists', os.getpid()) @@ -149,9 +179,6 @@ def test_cpu_times(self): self.execute('cpu_times') - def test_cpu_percent(self): - self.execute('cpu_percent') - def test_main(): test_suite = unittest.TestSuite() diff -Nru python-psutil-0.1.3/test/test_psutil.py python-psutil-0.2.1/test/test_psutil.py --- python-psutil-0.1.3/test/test_psutil.py 2009-09-14 22:50:56.000000000 +0000 +++ python-psutil-0.2.1/test/test_psutil.py 2011-03-20 20:29:49.000000000 +0000 @@ -1,8 +1,15 @@ #!/usr/bin/env python # -# $Id: test_psutil.py 521 2010-02-27 16:19:10Z billiejoex $ +# $Id: test_psutil.py 953 2011-03-20 20:29:45Z g.rodola $ # +"""psutil test suite. +Note: this is targeted for python 2.x. +To run it under python 3.x you need to use 2to3 tool first: + +$ 2to3 -w test/*.py +""" + import unittest import os import sys @@ -10,15 +17,61 @@ import time import signal import types +import traceback +import socket +import warnings +import atexit import errno -import platform +import threading +import tempfile import psutil PYTHON = os.path.realpath(sys.executable) DEVNULL = open(os.devnull, 'r+') +TESTFN = os.path.join(os.getcwd(), "$testfile") +POSIX = os.name == 'posix' +LINUX = sys.platform.lower().startswith("linux") +WINDOWS = sys.platform.lower().startswith("win32") +OSX = sys.platform.lower().startswith("darwin") +BSD = sys.platform.lower().startswith("freebsd") + +try: + psutil.Process(os.getpid()).get_connections() +except NotImplementedError, err: + SUPPORT_CONNECTIONS = False + atexit.register(warnings.warn, "get_connections() not supported on this platform", + RuntimeWarning) +else: + SUPPORT_CONNECTIONS = True + + +_subprocesses_started = set() + +def get_test_subprocess(cmd=None, stdout=DEVNULL, stderr=DEVNULL, stdin=None): + """Return a subprocess.Popen object to use in tests. + By default stdout and stderr are redirected to /dev/null and the + python interpreter is used as test process. + """ + if cmd is None: + cmd = [PYTHON, "-c", "import time; time.sleep(3600);"] + sproc = subprocess.Popen(cmd, stdout=stdout, stderr=stderr, stdin=stdin) + _subprocesses_started.add(sproc.pid) + return sproc + +def sh(cmdline): + """run cmd in a subprocess and return its output. + raises RuntimeError on error. + """ + p = subprocess.Popen(cmdline, shell=True, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + if p.returncode != 0: + raise RuntimeError(stderr) + if stderr: + warnings.warn(stderr, RuntimeWarning) def wait_for_pid(pid, timeout=1): """Wait for pid to show up in the process list then return. @@ -27,133 +80,349 @@ raise_at = time.time() + timeout while 1: if pid in psutil.get_pid_list(): - # give it one more iteration to allow full initialization + # give it one more iteration to allow full initialization time.sleep(0.01) return time.sleep(0.0001) if time.time() >= raise_at: raise RuntimeError("Timed out") -def kill(pid): - """Kill a process given its PID.""" - if hasattr(os, 'kill'): - os.kill(pid, signal.SIGKILL) +def reap_children(search_all=False): + """Kill any subprocess started by this test suite and ensure that + no zombies stick around to hog resources and create problems when + looking for refleaks. + """ + if search_all: + this_process = psutil.Process(os.getpid()) + pids = [x.pid for x in this_process.get_children()] else: - psutil.Process(pid).kill() + pids =_subprocesses_started + while pids: + pid = pids.pop() + try: + child = psutil.Process(pid) + child.kill() + except psutil.NoSuchProcess: + pass + else: + child.wait() -class TestCase(unittest.TestCase): +# we want to search through all processes before exiting +atexit.register(reap_children, search_all=True) + +def skipIf(condition, reason="", warn=False): + """Decorator which skip a test under if condition is satisfied. + This is a substitute of unittest.skipIf which is available + only in python 2.7 and 3.2. + If 'reason' argument is provided this will be printed during + tests execution. + If 'warn' is provided a RuntimeWarning will be shown when all + tests are run. + """ + def outer(fun, *args, **kwargs): + def inner(self): + if condition: + sys.stdout.write("skipped-") + sys.stdout.flush() + if warn: + objname = "%s.%s" % (self.__class__.__name__, fun.__name__) + msg = "%s was skipped" % objname + if reason: + msg += "; reason: " + repr(reason) + atexit.register(warnings.warn, msg, RuntimeWarning) + return + else: + return fun(self, *args, **kwargs) + return inner + return outer + +def skipUnless(condition, reason="", warn=False): + """Contrary of skipIf.""" + if not condition: + return skipIf(True, reason, warn) + return skipIf(False) + +def ignore_access_denied(fun): + """Decorator to Ignore AccessDenied exceptions.""" + def outer(fun, *args, **kwargs): + def inner(self): + try: + return fun(self, *args, **kwargs) + except psutil.AccessDenied: + pass + return inner + return outer + + +class ThreadTask(threading.Thread): + """A thread object used for running process thread tests.""" + + def __init__(self): + threading.Thread.__init__(self) + self._running = False + self._interval = None + self._flag = threading.Event() + + def __repr__(self): + name = self.__class__.__name__ + return '<%s running=%s at %#x>' % (name, self._running, id(self)) + + def start(self, interval=0.001): + """Start thread and keep it running until an explicit + stop() request. Polls for shutdown every 'timeout' seconds. + """ + if self._running: + raise ValueError("already started") + self._interval = interval + threading.Thread.start(self) + self._flag.wait() + + def run(self): + self._running = True + self._flag.set() + while self._running: + time.sleep(self._interval) + + def stop(self): + """Stop thread execution and and waits until it is stopped.""" + if not self._running: + raise ValueError("already stopped") + self._running = False + self.join() - def setUp(self): - self.proc = None + +class TestCase(unittest.TestCase): def tearDown(self): - if self.proc is not None: - kill(self.proc.pid) + reap_children() def test_get_process_list(self): pids = [x.pid for x in psutil.get_process_list()] - if hasattr(os, 'getpid'): - self.assertTrue(os.getpid() in pids) + self.assertTrue(os.getpid() in pids) + self.assertTrue(0 in pids) def test_process_iter(self): pids = [x.pid for x in psutil.process_iter()] - if hasattr(os, 'getpid'): - self.assertTrue(os.getpid() in pids) + self.assertTrue(os.getpid() in pids) + self.assertTrue(0 in pids) def test_kill(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - test_pid = self.proc.pid + sproc = get_test_subprocess() + test_pid = sproc.pid wait_for_pid(test_pid) p = psutil.Process(test_pid) name = p.name p.kill() - self.proc.wait() - self.proc = None + p.wait() + self.assertFalse(psutil.pid_exists(test_pid) and name == PYTHON) + + def test_terminate(self): + sproc = get_test_subprocess() + test_pid = sproc.pid + wait_for_pid(test_pid) + p = psutil.Process(test_pid) + name = p.name + p.terminate() + p.wait() self.assertFalse(psutil.pid_exists(test_pid) and name == PYTHON) + def test_send_signal(self): + if POSIX: + sig = signal.SIGKILL + else: + sig = signal.SIGTERM + sproc = get_test_subprocess() + test_pid = sproc.pid + p = psutil.Process(test_pid) + name = p.name + p.send_signal(sig) + p.wait() + self.assertFalse(psutil.pid_exists(test_pid) and name == PYTHON) + + def test_wait(self): + # check exit code signal + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.kill() + code = p.wait() + if os.name == 'posix': + self.assertEqual(code, signal.SIGKILL) + else: + self.assertEqual(code, 0) + self.assertFalse(p.is_running()) + + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.terminate() + code = p.wait() + if os.name == 'posix': + self.assertEqual(code, signal.SIGTERM) + else: + self.assertEqual(code, 0) + self.assertFalse(p.is_running()) + + # check sys.exit() code + code = "import time, sys; time.sleep(0.01); sys.exit(5);" + sproc = get_test_subprocess([PYTHON, "-c", code]) + p = psutil.Process(sproc.pid) + self.assertEqual(p.wait(), 5) + self.assertFalse(p.is_running()) + + # Test wait() issued twice. + # It is not supposed to raise NSP when the process is gone. + # On UNIX this should return None, on Windows it should keep + # returning the exit code. + sproc = get_test_subprocess([PYTHON, "-c", code]) + p = psutil.Process(sproc.pid) + self.assertEqual(p.wait(), 5) + self.assertTrue(p.wait() in (5, None)) + + # test timeout + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.name + self.assertRaises(psutil.TimeoutExpired, p.wait, 0.01) + + @skipUnless(POSIX) + def test_wait_non_children(self): + # test wait() against processes which are not our children + code = "import sys;" + code += "from subprocess import Popen, PIPE;" + code += "cmd = ['%s', '-c', 'import time; time.sleep(10)'];" %PYTHON + code += "sp = Popen(cmd, stdout=PIPE);" + code += "sys.stdout.write(str(sp.pid));" + sproc = get_test_subprocess([PYTHON, "-c", code], stdout=subprocess.PIPE) + + grandson_pid = int(sproc.stdout.read()) + grandson_proc = psutil.Process(grandson_pid) + try: + self.assertRaises(psutil.TimeoutExpired, grandson_proc.wait, 0.01) + grandson_proc.kill() + ret = grandson_proc.wait() + self.assertEqual(ret, None) + finally: + if grandson_proc.is_running(): + grandson_proc.kill() + grandson_proc.wait() + def test_TOTAL_PHYMEM(self): x = psutil.TOTAL_PHYMEM - self.assertTrue(isinstance(x, int) or isinstance(x, long)) + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x > 0) + + def test_BOOT_TIME(self): + x = psutil.BOOT_TIME + self.assertTrue(isinstance(x, float)) self.assertTrue(x > 0) def test_used_phymem(self): x = psutil.used_phymem() - self.assertTrue(isinstance(x, int) or isinstance(x, long)) + self.assertTrue(isinstance(x, (int, long))) self.assertTrue(x > 0) def test_avail_phymem(self): x = psutil.avail_phymem() - self.assertTrue(isinstance(x, int) or isinstance(x, long)) + self.assertTrue(isinstance(x, (int, long))) self.assertTrue(x > 0) def test_total_virtmem(self): x = psutil.total_virtmem() - self.assertTrue(isinstance(x, int) or isinstance(x, long)) + self.assertTrue(isinstance(x, (int, long))) self.assertTrue(x >= 0) def test_used_virtmem(self): x = psutil.used_virtmem() - self.assertTrue(isinstance(x, int) or isinstance(x, long)) + self.assertTrue(isinstance(x, (int, long))) self.assertTrue(x >= 0) def test_avail_virtmem(self): x = psutil.avail_virtmem() - self.assertTrue(isinstance(x, int) or isinstance(x, long)) + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x >= 0) + + @skipUnless(LINUX) + def test_cached_phymem(self): + x = psutil.cached_phymem() + self.assertTrue(isinstance(x, (int, long))) + self.assertTrue(x >= 0) + + @skipUnless(LINUX) + def test_phymem_buffers(self): + x = psutil.phymem_buffers() + self.assertTrue(isinstance(x, (int, long))) self.assertTrue(x >= 0) def test_system_cpu_times(self): total = 0 times = psutil.cpu_times() - self.assertTrue(isinstance(times, psutil.CPUTimes)) + sum(times) for cp_time in times: self.assertTrue(isinstance(cp_time, float)) self.assertTrue(cp_time >= 0.0) total += cp_time - # test CPUTimes's __iter__ and __str__ implementation self.assertEqual(total, sum(times)) str(times) + def test_system_cpu_times2(self): + t1 = sum(psutil.cpu_times()) + time.sleep(0.1) + t2 = sum(psutil.cpu_times()) + difference = t2 - t1 + if not difference >= 0.05: + self.fail("difference %s" % difference) + def test_system_cpu_percent(self): - percent = psutil.cpu_percent() - self.assertTrue(isinstance(percent, float)) + psutil.cpu_percent(interval=0.001) + psutil.cpu_percent(interval=0.001) for x in xrange(1000): - percent = psutil.cpu_percent() + percent = psutil.cpu_percent(interval=None) + self.assertTrue(isinstance(percent, float)) + self.assertTrue(percent >= 0.0) + self.assertTrue(percent <= 100.0) + + def test_process_cpu_percent(self): + p = psutil.Process(os.getpid()) + p.get_cpu_percent(interval=0.001) + p.get_cpu_percent(interval=0.001) + for x in xrange(100): + percent = p.get_cpu_percent(interval=None) self.assertTrue(isinstance(percent, float)) self.assertTrue(percent >= 0.0) self.assertTrue(percent <= 100.0) - # os.times() is broken on OS X and *BSD because, see: + def test_get_process_cpu_times(self): + times = psutil.Process(os.getpid()).get_cpu_times() + self.assertTrue((times.user > 0.0) or (times.system > 0.0)) + # make sure returned values can be pretty printed with strftime + time.strftime("%H:%M:%S", time.localtime(times.user)) + time.strftime("%H:%M:%S", time.localtime(times.system)) + + # Test Process.cpu_times() against os.times() + # os.times() is broken on Python 2.6 # http://bugs.python.org/issue1040026 - # It's also broken on Windows on Python 2.5 (not 2.6) - # Disabled since we can't rely on it + # XXX fails on OSX: not sure if it's for os.times(). We should + # try this with Python 2.7 and re-enable the test. -## def test_get_cpu_times(self): -## user_time, kernel_time = psutil.Process(os.getpid()).get_cpu_times() -## utime, ktime = os.times()[:2] -## -## # Use os.times()[:2] as base values to compare our results -## # using a tolerance of +/- 0.1 seconds. -## # It will fail if the difference between the values is > 0.1s. -## if (max([user_time, utime]) - min([user_time, utime])) > 0.1: -## self.fail("expected: %s, found: %s" %(utime, user_time)) -## -## if (max([kernel_time, ktime]) - min([kernel_time, ktime])) > 0.1: -## self.fail("expected: %s, found: %s" %(ktime, kernel_time)) -## -## # make sure returned values can be pretty printed with strftime -## time.strftime("%H:%M:%S", time.localtime(user_time)) -## time.strftime("%H:%M:%S", time.localtime(kernel_time)) - - def test_get_cpu_times(self): - user, kernel = psutil.Process(os.getpid()).get_cpu_times() - self.assertTrue((user > 0.0) or (kernel > 0.0)) + @skipUnless(sys.version_info > (2, 6, 1) and not OSX) + def test_get_process_cpu_times2(self): + user_time, kernel_time = psutil.Process(os.getpid()).get_cpu_times() + utime, ktime = os.times()[:2] + + # Use os.times()[:2] as base values to compare our results + # using a tolerance of +/- 0.1 seconds. + # It will fail if the difference between the values is > 0.1s. + if (max([user_time, utime]) - min([user_time, utime])) > 0.1: + self.fail("expected: %s, found: %s" %(utime, user_time)) + + if (max([kernel_time, ktime]) - min([kernel_time, ktime])) > 0.1: + self.fail("expected: %s, found: %s" %(ktime, kernel_time)) def test_create_time(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) + sproc = get_test_subprocess() now = time.time() - wait_for_pid(self.proc.pid) - p = psutil.Process(self.proc.pid) + wait_for_pid(sproc.pid) + p = psutil.Process(sproc.pid) create_time = p.create_time # Use time.time() as base value to compare our result using a @@ -161,11 +430,114 @@ # It will fail if the difference between the values is > 2s. difference = abs(create_time - now) if difference > 2: - self.fail("expected: %s, found: %s, difference: %s" %(now, create_time, difference)) + self.fail("expected: %s, found: %s, difference: %s" + % (now, create_time, difference)) # make sure returned value can be pretty printed with strftime time.strftime("%Y %m %d %H:%M:%S", time.localtime(p.create_time)) + @skipIf(OSX, warn=False) + def test_get_io_counters(self): + p = psutil.Process(os.getpid()) + # test reads + io1 = p.get_io_counters() + f = open(PYTHON, 'rb') + f.read() + f.close() + io2 = p.get_io_counters() + if not BSD: + self.assertTrue(io2.read_count > io1.read_count) + self.assertTrue(io2.write_count == io1.write_count) + self.assertTrue(io2.read_bytes >= io1.read_bytes) + self.assertTrue(io2.write_bytes >= io1.write_bytes) + # test writes + io1 = p.get_io_counters() + f = tempfile.TemporaryFile() + if sys.version_info >= (3,): + f.write(bytes("x" * 1000000, 'ascii')) + else: + f.write("x" * 1000000) + f.close() + io2 = p.get_io_counters() + if not BSD: + self.assertTrue(io2.write_count > io1.write_count) + self.assertTrue(io2.write_bytes > io1.write_bytes) + self.assertTrue(io2.read_count >= io1.read_count) + self.assertTrue(io2.read_bytes >= io1.read_bytes) + + @skipUnless(LINUX) + def test_get_set_ionice(self): + from psutil import (IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, + IOPRIO_CLASS_IDLE) + self.assertEqual(IOPRIO_CLASS_NONE, 0) + self.assertEqual(IOPRIO_CLASS_RT, 1) + self.assertEqual(IOPRIO_CLASS_BE, 2) + self.assertEqual(IOPRIO_CLASS_IDLE, 3) + p = psutil.Process(os.getpid()) + try: + p.set_ionice(2) + ioclass, value = p.get_ionice() + self.assertEqual(ioclass, 2) + self.assertEqual(value, 4) + # + p.set_ionice(3) + ioclass, value = p.get_ionice() + self.assertEqual(ioclass, 3) + self.assertEqual(value, 0) + # + p.set_ionice(2, 0) + ioclass, value = p.get_ionice() + self.assertEqual(ioclass, 2) + self.assertEqual(value, 0) + p.set_ionice(2, 7) + ioclass, value = p.get_ionice() + self.assertEqual(ioclass, 2) + self.assertEqual(value, 7) + self.assertRaises(ValueError, p.set_ionice, 2, 10) + finally: + p.set_ionice(IOPRIO_CLASS_NONE) + + def test_get_num_threads(self): + # on certain platforms such as Linux we might test for exact + # thread number, since we always have with 1 thread per process, + # but this does not apply across all platforms (OSX, Windows) + p = psutil.Process(os.getpid()) + step1 = p.get_num_threads() + + thread = ThreadTask() + thread.start() + try: + step2 = p.get_num_threads() + self.assertEqual(step2, step1 + 1) + thread.stop() + finally: + if thread._running: + thread.stop() + + def test_get_threads(self): + p = psutil.Process(os.getpid()) + step1 = p.get_threads() + + thread = ThreadTask() + thread.start() + + try: + step2 = p.get_threads() + self.assertEqual(len(step2), len(step1) + 1) + # on Linux, first thread id is supposed to be this process + if LINUX: + self.assertEqual(step2[0].id, os.getpid()) + athread = step2[0] + # test named tuple + self.assertEqual(athread.id, athread[0]) + self.assertEqual(athread.user_time, athread[1]) + self.assertEqual(athread.system_time, athread[2]) + # test num threads + thread.stop() + finally: + if thread._running: + thread.stop() + def test_get_memory_info(self): p = psutil.Process(os.getpid()) @@ -191,116 +563,456 @@ self.assertTrue(p.get_memory_percent() > 0.0) def test_pid(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - self.assertEqual(psutil.Process(self.proc.pid).pid, self.proc.pid) + sproc = get_test_subprocess() + self.assertEqual(psutil.Process(sproc.pid).pid, sproc.pid) def test_eq(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - wait_for_pid(self.proc.pid) - self.assertTrue(psutil.Process(self.proc.pid) == psutil.Process(self.proc.pid)) + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + self.assertTrue(psutil.Process(sproc.pid) == psutil.Process(sproc.pid)) def test_is_running(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - wait_for_pid(self.proc.pid) - p = psutil.Process(self.proc.pid) + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + p = psutil.Process(sproc.pid) self.assertTrue(p.is_running()) - psutil.Process(self.proc.pid).kill() - self.proc.wait() - self.proc = None + p.kill() + p.wait() self.assertFalse(p.is_running()) def test_pid_exists(self): - if hasattr(os, 'getpid'): - self.assertTrue(psutil.pid_exists(os.getpid())) + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + self.assertTrue(psutil.pid_exists(sproc.pid)) + p = psutil.Process(sproc.pid) + p.kill() + p.wait() + self.assertFalse(psutil.pid_exists(sproc.pid)) self.assertFalse(psutil.pid_exists(-1)) + def test_exe(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + try: + self.assertEqual(psutil.Process(sproc.pid).exe, PYTHON) + except AssertionError: + # certain platforms such as BSD are more accurate returning: + # "/usr/local/bin/python2.7" + # ...instead of: + # "/usr/local/bin/python" + # We do not want to consider this difference in accuracy + # an error. + extended_py_name = PYTHON + "%s.%s" % (sys.version_info.major, + sys.version_info.minor) + self.assertEqual(psutil.Process(sproc.pid).exe, extended_py_name) + for p in psutil.process_iter(): + try: + exe = p.exe + except psutil.Error: + continue + if not exe: + continue + if not os.path.exists(exe): + self.fail("%s does not exist (pid=%s, name=%s, cmdline=%s)" \ + % (repr(exe), p.pid, p.name, p.cmdline)) + if hasattr(os, 'access') and hasattr(os, "X_OK"): + if not os.access(p.exe, os.X_OK): + self.fail("%s is not executable (pid=%s, name=%s, cmdline=%s)" \ + % (repr(p.exe), p.pid, p.name, p.cmdline)) + def test_path(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - wait_for_pid(self.proc.pid) - self.assertEqual(psutil.Process(self.proc.pid).path, os.path.dirname(PYTHON)) + proc = psutil.Process(os.getpid()) + warnings.filterwarnings("error") + try: + self.assertRaises(DeprecationWarning, getattr, proc, 'path') + finally: + warnings.resetwarnings() def test_cmdline(self): - self.proc = subprocess.Popen([PYTHON, "-E"], stdout=DEVNULL, stderr=DEVNULL) - wait_for_pid(self.proc.pid) - self.assertEqual(psutil.Process(self.proc.pid).cmdline, [PYTHON, "-E"]) + sproc = get_test_subprocess([PYTHON, "-E"]) + wait_for_pid(sproc.pid) + self.assertEqual(psutil.Process(sproc.pid).cmdline, [PYTHON, "-E"]) def test_name(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - wait_for_pid(self.proc.pid) - self.assertEqual(psutil.Process(self.proc.pid).name, os.path.basename(PYTHON)) - - def test_uid(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - wait_for_pid(self.proc.pid) - uid = psutil.Process(self.proc.pid).uid - if hasattr(os, 'getuid'): - self.assertEqual(uid, os.getuid()) - else: - # On those platforms where UID doesn't make sense (Windows) - # we expect it to be -1 - self.assertEqual(uid, -1) - - def test_gid(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - wait_for_pid(self.proc.pid) - gid = psutil.Process(self.proc.pid).gid - if hasattr(os, 'getgid'): - self.assertEqual(gid, os.getgid()) - else: - # On those platforms where GID doesn't make sense (Windows) - # we expect it to be -1 - self.assertEqual(gid, -1) + sproc = get_test_subprocess(PYTHON) + wait_for_pid(sproc.pid) + if OSX: + self.assertEqual(psutil.Process(sproc.pid).name, "Python") + else: + self.assertEqual(psutil.Process(sproc.pid).name, os.path.basename(PYTHON)) - def test_username(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - p = psutil.Process(self.proc.pid) - # generic test, only access the attribute (twice for testing the - # caching code) - p.username - p.username + if os.name == 'posix': - # UNIX tests - if os.name == 'posix': - import pwd - user = pwd.getpwuid(p.uid).pw_name - self.assertEqual(p.username, user) + def test_uids(self): + p = psutil.Process(os.getpid()) + real, effective, saved = p.uids + # os.getuid() refers to "real" uid + self.assertEqual(real, os.getuid()) + # os.geteuid() refers to "effective" uid + self.assertEqual(effective, os.geteuid()) + # no such thing as os.getsuid() ("saved" uid), but starting + # from python 2.7 we have os.getresuid()[2] + if hasattr(os, "getresuid"): + self.assertEqual(saved, os.getresuid()[2]) + + def test_gids(self): + p = psutil.Process(os.getpid()) + real, effective, saved = p.gids + # os.getuid() refers to "real" uid + self.assertEqual(real, os.getgid()) + # os.geteuid() refers to "effective" uid + self.assertEqual(effective, os.getegid()) + # no such thing as os.getsuid() ("saved" uid), but starting + # from python 2.7 we have os.getresgid()[2] + if hasattr(os, "getresuid"): + self.assertEqual(saved, os.getresgid()[2]) + + def test_nice(self): + p = psutil.Process(os.getpid()) + self.assertRaises(TypeError, setattr, p, "nice", "str") + first_nice = p.nice + try: + p.nice = 1 + self.assertEqual(p.nice, 1) + # going back to previous nice value raises AccessDenied on OSX + if not OSX: + p.nice = 0 + self.assertEqual(p.nice, 0) + finally: + # going back to previous nice value raises AccessDenied on OSX + if not OSX: + p.nice = first_nice + + if os.name == 'nt': + + def test_nice(self): + p = psutil.Process(os.getpid()) + self.assertRaises(TypeError, setattr, p, "nice", "str") + try: + self.assertEqual(p.nice, psutil.NORMAL_PRIORITY_CLASS) + p.nice = psutil.HIGH_PRIORITY_CLASS + self.assertEqual(p.nice, psutil.HIGH_PRIORITY_CLASS) + p.nice = psutil.NORMAL_PRIORITY_CLASS + self.assertEqual(p.nice, psutil.NORMAL_PRIORITY_CLASS) + finally: + p.nice = psutil.NORMAL_PRIORITY_CLASS + + def test_status(self): + p = psutil.Process(os.getpid()) + self.assertEqual(p.status, psutil.STATUS_RUNNING) + self.assertEqual(str(p.status), "running") + for p in psutil.process_iter(): + if str(p.status) == '?': + self.fail("invalid status for pid %d" % p.pid) - # Windows tests - elif sys.platform.lower().startswith("win32"): + def test_username(self): + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + if POSIX: + import pwd + self.assertEqual(p.username, pwd.getpwuid(os.getuid()).pw_name) + elif WINDOWS: expected_username = os.environ['USERNAME'] expected_domain = os.environ['USERDOMAIN'] domain, username = p.username.split('\\') self.assertEqual(domain, expected_domain) self.assertEqual(username, expected_username) + else: + p.username - if hasattr(psutil.Process, "getcwd"): - def test_getcwd(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - wait_for_pid(self.proc.pid) - p = psutil.Process(self.proc.pid) - self.assertEqual(p.getcwd(), os.getcwd()) - - def test_getcwd_2(self): - cmd = [PYTHON, "-c", "import os; os.chdir('..'); input()"] - self.proc = subprocess.Popen(cmd, stdout=DEVNULL) - wait_for_pid(self.proc.pid) - p = psutil.Process(self.proc.pid) - time.sleep(0.1) - expected_dir = os.path.dirname(os.getcwd()) - self.assertEqual(p.getcwd(), expected_dir) + @skipUnless(WINDOWS or LINUX) + def test_getcwd(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + p = psutil.Process(sproc.pid) + self.assertEqual(p.getcwd(), os.getcwd()) + + @skipUnless(WINDOWS or LINUX) + def test_getcwd_2(self): + cmd = [PYTHON, "-c", "import os, time; os.chdir('..'); time.sleep(10)"] + sproc = get_test_subprocess(cmd) + wait_for_pid(sproc.pid) + p = psutil.Process(sproc.pid) + time.sleep(0.1) + expected_dir = os.path.dirname(os.getcwd()) + self.assertEqual(p.getcwd(), expected_dir) + + def test_get_open_files(self): + # current process + p = psutil.Process(os.getpid()) + files = p.get_open_files() + self.assertFalse(TESTFN in files) + f = open(TESTFN, 'r') + time.sleep(.1) + filenames = [x.path for x in p.get_open_files()] + self.assertTrue(TESTFN in filenames) + f.close() + for file in filenames: + self.assertTrue(os.path.isfile(file)) + + # another process + cmdline = "import time; f = open(r'%s', 'r'); time.sleep(100);" % TESTFN + sproc = get_test_subprocess([PYTHON, "-c", cmdline]) + wait_for_pid(sproc.pid) + time.sleep(0.1) + p = psutil.Process(sproc.pid) + filenames = [x.path for x in p.get_open_files()] + self.assertTrue(TESTFN in filenames) + for file in filenames: + self.assertTrue(os.path.isfile(file)) + # all processes + for proc in psutil.process_iter(): + try: + files = proc.get_open_files() + except psutil.Error: + pass + else: + for file in filenames: + self.assertTrue(os.path.isfile(file)) + + def test_get_open_files2(self): + # test fd and path fields + fileobj = open(TESTFN, 'r') + p = psutil.Process(os.getpid()) + for path, fd in p.get_open_files(): + if path == fileobj.name or fd == fileobj.fileno(): + break + else: + self.fail("no file found; files=%s" % repr(p.get_open_files())) + self.assertEqual(path, fileobj.name) + if WINDOWS: + self.assertEqual(fd, -1) + else: + self.assertEqual(fd, fileobj.fileno()) + # test positions + ntuple = p.get_open_files()[0] + self.assertEqual(ntuple[0], ntuple.path) + self.assertEqual(ntuple[1], ntuple.fd) + # test file is gone + fileobj.close() + self.assertTrue(fileobj.name not in p.get_open_files()) + + @skipUnless(SUPPORT_CONNECTIONS, warn=1) + def test_get_connections(self): + arg = "import socket, time;" \ + "s = socket.socket();" \ + "s.bind(('127.0.0.1', 0));" \ + "s.listen(1);" \ + "conn, addr = s.accept();" \ + "time.sleep(100);" + sproc = get_test_subprocess([PYTHON, "-c", arg]) + time.sleep(0.1) + p = psutil.Process(sproc.pid) + cons = p.get_connections() + self.assertTrue(len(cons) == 1) + con = cons[0] + self.assertEqual(con.family, socket.AF_INET) + self.assertEqual(con.type, socket.SOCK_STREAM) + self.assertEqual(con.status, "LISTEN") + ip, port = con.local_address + self.assertEqual(ip, '127.0.0.1') + self.assertEqual(con.remote_address, ()) + if WINDOWS: + self.assertEqual(con.fd, -1) + else: + self.assertTrue(con.fd > 0) + # test positions + self.assertEqual(con[0], con.fd) + self.assertEqual(con[1], con.family) + self.assertEqual(con[2], con.type) + self.assertEqual(con[3], con.local_address) + self.assertEqual(con[4], con.remote_address) + self.assertEqual(con[5], con.status) + + @skipUnless(hasattr(socket, "fromfd") and not WINDOWS) + def test_connection_fromfd(self): + sock = socket.socket() + sock.bind(('127.0.0.1', 0)) + sock.listen(1) + p = psutil.Process(os.getpid()) + for conn in p.get_connections(): + if conn.fd == sock.fileno(): + break + else: + sock.close() + self.fail("couldn't find socket fd") + dupsock = socket.fromfd(conn.fd, conn.family, conn.type) + try: + self.assertEqual(dupsock.getsockname(), conn.local_address) + self.assertNotEqual(sock.fileno(), dupsock.fileno()) + finally: + sock.close() + dupsock.close() + + @skipUnless(SUPPORT_CONNECTIONS, warn=1) + def test_get_connections_all(self): + + def check_address(addr, family): + if not addr: + return + ip, port = addr + self.assertTrue(isinstance(port, int)) + if family == socket.AF_INET: + ip = map(int, ip.split('.')) + self.assertTrue(len(ip) == 4) + for num in ip: + self.assertTrue(0 <= num <= 255) + self.assertTrue(0 <= port <= 65535) + + def supports_ipv6(): + if not socket.has_ipv6 or not hasattr(socket, "AF_INET6"): + return False + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.bind(("::1", 0)) + except (socket.error, socket.gaierror): + return False + else: + sock.close() + return True + + # all values are supposed to match Linux's tcp_states.h states + # table across all platforms. + valid_states = ["ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1", + "FIN_WAIT2", "TIME_WAIT", "CLOSE", "CLOSE_WAIT", + "LAST_ACK", "LISTEN", "CLOSING", ""] + + tcp_template = "import socket;" \ + "s = socket.socket($family, socket.SOCK_STREAM);" \ + "s.bind(('$addr', 0));" \ + "s.listen(1);" \ + "conn, addr = s.accept();" + + udp_template = "import socket, time;" \ + "s = socket.socket($family, socket.SOCK_DGRAM);" \ + "s.bind(('$addr', 0));" \ + "time.sleep(100);" + + from string import Template + tcp4_template = Template(tcp_template).substitute(family=socket.AF_INET, + addr="127.0.0.1") + udp4_template = Template(udp_template).substitute(family=socket.AF_INET, + addr="127.0.0.1") + tcp6_template = Template(tcp_template).substitute(family=socket.AF_INET6, + addr="::1") + udp6_template = Template(udp_template).substitute(family=socket.AF_INET6, + addr="::1") + + # launch various subprocess instantiating a socket of various + # families and tupes to enrich psutil results + tcp4_proc = get_test_subprocess([PYTHON, "-c", tcp4_template]) + udp4_proc = get_test_subprocess([PYTHON, "-c", udp4_template]) + if supports_ipv6(): + tcp6_proc = get_test_subprocess([PYTHON, "-c", tcp6_template]) + udp6_proc = get_test_subprocess([PYTHON, "-c", udp6_template]) + else: + tcp6_proc = None + udp6_proc = None + + # --- check connections of all processes + + time.sleep(0.1) + for p in psutil.process_iter(): + try: + cons = p.get_connections() + except (psutil.NoSuchProcess, psutil.AccessDenied): + pass + else: + for conn in cons: + self.assertTrue(conn.type in (socket.SOCK_STREAM, + socket.SOCK_DGRAM)) + self.assertTrue(conn.family in (socket.AF_INET, + socket.AF_INET6)) + check_address(conn.local_address, conn.family) + check_address(conn.remote_address, conn.family) + if conn.status not in valid_states: + self.fail("%s is not a valid status" %conn.status) + # actually try to bind the local socket; ignore IPv6 + # sockets as their address might be represented as + # an IPv4-mapped-address (e.g. "::127.0.0.1") + # and that's rejected by bind() + if conn.family == socket.AF_INET: + s = socket.socket(conn.family, conn.type) + s.bind((conn.local_address[0], 0)) + s.close() + + if not WINDOWS and hasattr(socket, 'fromfd'): + try: + dupsock = socket.fromfd(conn.fd, conn.family, + conn.type) + except (socket.error, OSError), err: + if err.args[0] == errno.EBADF: + continue + else: + raise + # python >= 2.5 + if hasattr(dupsock, "family"): + self.assertEqual(dupsock.family, conn.family) + self.assertEqual(dupsock.type, conn.type) + + # --- check matches against subprocesses + + for p in psutil.Process(os.getpid()).get_children(): + for conn in p.get_connections(): + # TCP v4 + if p.pid == tcp4_proc.pid: + self.assertEqual(conn.family, socket.AF_INET) + self.assertEqual(conn.type, socket.SOCK_STREAM) + self.assertEqual(conn.local_address[0], "127.0.0.1") + self.assertEqual(conn.remote_address, ()) + self.assertEqual(conn.status, "LISTEN") + # UDP v4 + elif p.pid == udp4_proc.pid: + self.assertEqual(conn.family, socket.AF_INET) + self.assertEqual(conn.type, socket.SOCK_DGRAM) + self.assertEqual(conn.local_address[0], "127.0.0.1") + self.assertEqual(conn.remote_address, ()) + self.assertEqual(conn.status, "") + # TCP v6 + elif p.pid == getattr(tcp6_proc, "pid", None): + self.assertEqual(conn.family, socket.AF_INET6) + self.assertEqual(conn.type, socket.SOCK_STREAM) + self.assertEqual(conn.local_address[0], "::1") + self.assertEqual(conn.remote_address, ()) + self.assertEqual(conn.status, "LISTEN") + # UDP v6 + elif p.pid == getattr(udp6_proc, "pid", None): + self.assertEqual(conn.family, socket.AF_INET6) + self.assertEqual(conn.type, socket.SOCK_DGRAM) + self.assertEqual(conn.local_address[0], "::1") + self.assertEqual(conn.remote_address, ()) + self.assertEqual(conn.status, "") def test_parent_ppid(self): this_parent = os.getpid() - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - p = psutil.Process(self.proc.pid) + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) self.assertEqual(p.ppid, this_parent) self.assertEqual(p.parent.pid, this_parent) + # no other process is supposed to have us as parent + for p in psutil.process_iter(): + if p.pid == sproc.pid: + continue + self.assertTrue(p.ppid != this_parent) + + def test_get_children(self): + p = psutil.Process(os.getpid()) + self.assertEqual(p.get_children(), []) + sproc = get_test_subprocess() + children = p.get_children() + self.assertEqual(len(children), 1) + self.assertEqual(children[0].pid, sproc.pid) + self.assertEqual(children[0].ppid, os.getpid()) def test_suspend_resume(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - p = psutil.Process(self.proc.pid) + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) p.suspend() + time.sleep(0.1) + self.assertEqual(p.status, psutil.STATUS_STOPPED) + self.assertEqual(str(p.status), "stopped") p.resume() + self.assertTrue(p.status != psutil.STATUS_STOPPED) def test_get_pid_list(self): plist = [x.pid for x in psutil.get_process_list()] @@ -318,94 +1030,129 @@ finally: sys.stdout = stdout - def test_types(self): - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - p = psutil.Process(self.proc.pid) - self.assert_(isinstance(p.pid, int)) - self.assert_(isinstance(p.ppid, int)) - self.assert_(isinstance(p.parent, psutil.Process)) - self.assert_(isinstance(p.name, str)) - self.assert_(isinstance(p.path, str)) - self.assert_(isinstance(p.cmdline, list)) - self.assert_(isinstance(p.uid, int)) - self.assert_(isinstance(p.gid, int)) - self.assert_(isinstance(p.create_time, float)) - self.assert_(isinstance(p.username, str) or \ - isinstance(p.username, type(u''))) - if hasattr(p, 'getpwd'): - self.assert_(isinstance(p.getpwd(), str)) - self.assert_(isinstance(p.is_running(), bool)) - self.assert_(isinstance(p.get_cpu_times(), tuple)) - self.assert_(isinstance(p.get_cpu_times()[0], float)) - self.assert_(isinstance(p.get_cpu_times()[1], float)) - self.assert_(isinstance(p.get_cpu_percent(), float)) - self.assert_(isinstance(p.get_memory_info(), tuple)) - self.assert_(isinstance(p.get_memory_info()[0], int)) - self.assert_(isinstance(p.get_memory_info()[1], int)) - self.assert_(isinstance(p.get_memory_percent(), float)) - self.assert_(isinstance(psutil.get_process_list(), list)) - self.assert_(isinstance(psutil.get_process_list()[0], psutil.Process)) - self.assert_(isinstance(psutil.process_iter(), types.GeneratorType)) - self.assert_(isinstance(psutil.process_iter().next(), psutil.Process)) - self.assert_(isinstance(psutil.get_pid_list(), list)) - self.assert_(isinstance(psutil.get_pid_list()[0], int)) - self.assert_(isinstance(psutil.pid_exists(1), bool)) - - def test_no_such_process(self): - # Refers to Issue #12 - self.assertRaises(psutil.NoSuchProcess, psutil.Process, -1) - def test_invalid_pid(self): self.assertRaises(ValueError, psutil.Process, "1") + self.assertRaises(ValueError, psutil.Process, None) + # Refers to Issue #12 + self.assertRaises(psutil.NoSuchProcess, psutil.Process, -1) def test_zombie_process(self): - # Test that NoSuchProcess exception gets raised in the event the + # Test that NoSuchProcess exception gets raised in case the # process dies after we create the Process object. # Example: # >>> proc = Process(1234) # >>> time.sleep(5) # time-consuming task, process dies in meantime # >>> proc.name # Refers to Issue #15 - self.proc = subprocess.Popen(PYTHON, stdout=DEVNULL, stderr=DEVNULL) - p = psutil.Process(self.proc.pid) + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) p.kill() - self.proc.wait() - self.proc = None + p.wait() self.assertRaises(psutil.NoSuchProcess, getattr, p, "ppid") self.assertRaises(psutil.NoSuchProcess, getattr, p, "parent") self.assertRaises(psutil.NoSuchProcess, getattr, p, "name") - self.assertRaises(psutil.NoSuchProcess, getattr, p, "path") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "exe") self.assertRaises(psutil.NoSuchProcess, getattr, p, "cmdline") - self.assertRaises(psutil.NoSuchProcess, getattr, p, "uid") - self.assertRaises(psutil.NoSuchProcess, getattr, p, "gid") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "status") self.assertRaises(psutil.NoSuchProcess, getattr, p, "create_time") self.assertRaises(psutil.NoSuchProcess, getattr, p, "username") if hasattr(p, 'getcwd'): self.assertRaises(psutil.NoSuchProcess, p.getcwd) + if POSIX: + self.assertRaises(psutil.NoSuchProcess, getattr, p, "uids") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "gids") + self.assertRaises(psutil.NoSuchProcess, getattr, p, "nice") + try: + if os.name == 'posix': + p.nice = 1 + else: + p.nice = psutil.NORMAL_PRIORITY_CLASS + except psutil.NoSuchProcess: + pass + else: + self.fail("exception not raised") + if hasattr(p, 'get_ionice'): + self.assertRaises(psutil.NoSuchProcess, p.get_ionice) + self.assertRaises(psutil.NoSuchProcess, p.set_ionice, 2) + if hasattr(p, 'get_io_counters'): + self.assertRaises(psutil.NoSuchProcess, p.get_io_counters) + self.assertRaises(psutil.NoSuchProcess, p.get_open_files) + self.assertRaises(psutil.NoSuchProcess, p.get_connections) self.assertRaises(psutil.NoSuchProcess, p.suspend) self.assertRaises(psutil.NoSuchProcess, p.resume) self.assertRaises(psutil.NoSuchProcess, p.kill) + self.assertRaises(psutil.NoSuchProcess, p.terminate) + self.assertRaises(psutil.NoSuchProcess, p.send_signal, signal.SIGTERM) self.assertRaises(psutil.NoSuchProcess, p.get_cpu_times) - self.assertRaises(psutil.NoSuchProcess, p.get_cpu_percent) + self.assertRaises(psutil.NoSuchProcess, p.get_cpu_percent, 0) self.assertRaises(psutil.NoSuchProcess, p.get_memory_info) self.assertRaises(psutil.NoSuchProcess, p.get_memory_percent) + self.assertRaises(psutil.NoSuchProcess, p.get_children) + self.assertRaises(psutil.NoSuchProcess, p.get_num_threads) + self.assertRaises(psutil.NoSuchProcess, p.get_threads) self.assertFalse(p.is_running()) + def test__str__(self): + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + self.assertTrue(str(sproc.pid) in str(p)) + self.assertTrue(os.path.basename(PYTHON) in str(p)) + sproc = get_test_subprocess() + p = psutil.Process(sproc.pid) + p.kill() + p.wait() + self.assertTrue(str(sproc.pid) in str(p)) + self.assertTrue("terminated" in str(p)) + def test_fetch_all(self): valid_procs = 0 - attrs = ['__str__', 'create_time', 'username', 'getcwd', 'get_cpu_times', - 'get_cpu_percent', 'get_memory_info', 'get_memory_percent'] + excluded_names = ['send_signal', 'suspend', 'resume', 'terminate', + 'kill', 'wait'] + excluded_names += ['get_cpu_percent', 'path', 'uid', 'gid', 'get_children'] + # XXX - skip slow lsof implementation; + if BSD: + excluded_names += ['get_open_files', 'get_connections'] + if OSX: + excluded_names += ['get_connections'] + attrs = [] + for name in dir(psutil.Process): + if name.startswith("_"): + continue + if name.startswith("set_"): + continue + if name in excluded_names: + continue + attrs.append(name) + for p in psutil.process_iter(): - for attr in attrs: + for name in attrs: try: - attr = getattr(p, attr, None) - if attr is not None and callable(attr): - attr() - valid_procs += 1 - - except (psutil.NoSuchProcess, psutil.AccessDenied): - continue + try: + attr = getattr(p, name, None) + if attr is not None and callable(attr): + ret = attr() + else: + ret = attr + valid_procs += 1 + except (psutil.NoSuchProcess, psutil.AccessDenied), err: + self.assertEqual(err.pid, p.pid) + if err.name: + self.assertEqual(err.name, p.name) + self.assertTrue(str(err)) + self.assertTrue(err.msg) + else: + if name == 'parent' or ret in (0, 0.0, []): + continue + self.assertTrue(ret) + if name == "exe": + self.assertTrue(os.path.isfile(ret)) + elif name == "getcwd": + self.assertTrue(os.path.isdir(ret)) + except Exception, err: + trace = traceback.format_exc() + self.fail('%s\nmethod=%s, pid=%s, retvalue=%s' + %(trace, name, p.pid, repr(ret))) # we should always have a non-empty list, not including PID 0 etc. # special cases. @@ -415,31 +1162,30 @@ # Process(0) is supposed to work on all platforms even if with # some differences p = psutil.Process(0) - if sys.platform.lower().startswith("win32"): - self.assertEqual(p.name, 'System Idle Process') - elif sys.platform.lower().startswith("linux"): - self.assertEqual(p.name, 'sched') - elif sys.platform.lower().startswith("freebsd"): - self.assertEqual(p.name, 'swapper') - elif sys.platform.lower().startswith("darwin"): - self.assertEqual(p.name, 'kernel_task') - - # use __str__ to access all common Process properties to check - # that nothing strange happens - str(p) + self.assertTrue(p.name) + + if os.name == 'posix': + self.assertEqual(p.uids.real, 0) + self.assertEqual(p.gids.real, 0) + + self.assertTrue(p.ppid in (0, 1)) + #self.assertEqual(p.exe, "") + self.assertEqual(p.cmdline, []) + try: + p.get_num_threads() + except psutil.AccessDenied: + pass - if sys.platform.lower().startswith("darwin") and os.geteuid() != 0: + if OSX : #and os.geteuid() != 0: self.assertRaises(psutil.AccessDenied, p.get_memory_info) + self.assertRaises(psutil.AccessDenied, p.get_cpu_times) else: p.get_memory_info() # username property - if sys.platform.lower().startswith("linux"): + if POSIX: self.assertEqual(p.username, 'root') - elif sys.platform.lower().startswith('freebsd') or \ - sys.platform.lower().startswith('darwin'): - self.assertEqual(p.username, 'root') - elif sys.platform.lower().startswith("win32"): + elif WINDOWS: self.assertEqual(p.username, 'NT AUTHORITY\\SYSTEM') else: p.username @@ -448,16 +1194,19 @@ self.assertTrue(0 in psutil.get_pid_list()) self.assertTrue(psutil.pid_exists(0)) - - # --- OS specific tests - - # UNIX specific tests - - if os.name == 'posix': - if hasattr(os, 'getuid') and os.getuid() > 0: - def test_unix_access_denied(self): - p = psutil.Process(1) - self.assertRaises(psutil.AccessDenied, p.kill) + def test_Popen(self): + # Popen class test + cmd = [PYTHON, "-c", "import time; time.sleep(3600);"] + proc = psutil.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + try: + proc.name + proc.stdin + self.assertTrue(hasattr(proc, 'name')) + self.assertTrue(hasattr(proc, 'stdin')) + self.assertRaises(AttributeError, getattr, proc, 'foo') + finally: + proc.kill() + proc.wait() if hasattr(os, 'getuid'): @@ -480,61 +1229,98 @@ os.seteuid(self.PROCESS_GID) TestCase.tearDown(self) - if sys.platform.lower().startswith("linux"): + def test_path(self): + # DeprecationWarning is only raised once + pass + + def test_nice(self): + try: + psutil.Process(os.getpid()).nice = -1 + except psutil.AccessDenied: + pass + else: + self.fail("exception not raised") + + # overridden tests known to raise AccessDenied when run + # as limited user on different platforms + + if LINUX: - # disable these tests on Linux as they always raises AccessDenied def test_getcwd(self): - return + self.assertRaises(psutil.AccessDenied, TestCase.test_getcwd, self) + def test_getcwd_2(self): - return + self.assertRaises(psutil.AccessDenied, TestCase.test_getcwd_2, self) + + def test_get_open_files(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_get_open_files, self) + + def test_get_connections(self): + pass + + def test_exe(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_exe, self) + + if BSD: + + def test_get_open_files(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_get_open_files, self) + + def test_get_open_files2(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_get_open_files, self) + + def test_get_connections(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_get_connections, self) + + def test_get_connections_all(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_get_connections_all, self) + + def test_connection_fromfd(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_connection_fromfd, self) + + if OSX: + def test_nice(self): + self.assertRaises(psutil.AccessDenied, TestCase.test_nice, self) def test_main(): tests = [] test_suite = unittest.TestSuite() - tests.append(TestCase) - if hasattr(os, 'getuid') and os.getuid() == 0: - tests.append(LimitedUserTestCase) + if hasattr(os, 'getuid'): + if os.getuid() == 0: + tests.append(LimitedUserTestCase) + else: + atexit.register(warnings.warn, "Couldn't run limited user tests (" + "super-user privileges are required)", RuntimeWarning) - if os.name == 'posix': + if POSIX: from _posix import PosixSpecificTestCase tests.append(PosixSpecificTestCase) # import the specific platform test suite - if sys.platform.lower().startswith("linux"): + if LINUX: from _linux import LinuxSpecificTestCase as stc - elif sys.platform.lower().startswith("win32"): + elif WINDOWS: from _windows import WindowsSpecificTestCase as stc - elif sys.platform.lower().startswith("darwin"): + elif OSX: from _osx import OSXSpecificTestCase as stc - elif sys.platform.lower().startswith("freebsd"): + elif BSD: from _bsd import BSDSpecificTestCase as stc tests.append(stc) for test_class in tests: test_suite.addTest(unittest.makeSuite(test_class)) - unittest.TextTestRunner(verbosity=2).run(test_suite) - - # reap_children() function should help ensure that no extra children - # (zombies) stick around to hog resources and create problems when - # looking for refleaks. - # We check for ImportError as on most Unixen python tests are not - # installed by default (e.g. on Ubuntu they get installed with: - # "apt-get install python-dev") - try: - from test import test_support - except ImportError: - pass - else: - # available since python 2.5 - if hasattr(test_support, "reap_children"): - test_support.reap_children() + f = open(TESTFN, 'w') + f.close() + atexit.register(lambda: os.remove(TESTFN)) + unittest.TextTestRunner(verbosity=2).run(test_suite) DEVNULL.close() if __name__ == '__main__': test_main() + diff -Nru python-psutil-0.1.3/test/_windows.py python-psutil-0.2.1/test/_windows.py --- python-psutil-0.1.3/test/_windows.py 2009-09-15 00:42:26.000000000 +0000 +++ python-psutil-0.2.1/test/_windows.py 2011-03-20 20:28:37.000000000 +0000 @@ -1,133 +1,162 @@ -import os -import unittest -import platform -import subprocess -import time -import warnings - -import psutil -from test_psutil import kill, PYTHON, DEVNULL -try: - from psutil import wmi -except ImportError: - warnings.warn("Can't import WMI module; Windows specific tests disabled", - RuntimeWarning) - wmi = None - - -class WindowsSpecificTestCase(unittest.TestCase): - - def setUp(self): - self.pid = subprocess.Popen([PYTHON, "-E", "-O"], stdout=DEVNULL, stderr=DEVNULL).pid - - def tearDown(self): - kill(self.pid) - - def test_issue_24(self): - p = psutil.Process(0) - self.assertRaises(psutil.AccessDenied, p.kill) - - def test_pid_4(self): - p = psutil.Process(4) - self.assertEqual(p.name, 'System') - # use __str__ to access all common Process properties to check - # that nothing strange happens - str(p) - p.username - self.assertTrue(p.create_time >= 0.0) - try: - rss, vms = p.get_memory_info() - except psutil.AccessDenied: - # expected on Windows Vista and Windows 7 - if not platform.uname()[1] in ('vista', 'win-7'): - raise - else: - self.assertTrue(rss > 0) - self.assertEqual(vms, 0) - - if wmi is not None: - - # --- Process class tests - - def test_process_name(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - self.assertEqual(p.name, w.Caption) - - def test_process_path(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - self.assertEqual(os.path.join(p.path, p.name), w.ExecutablePath) - - def test_process_cmdline(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - self.assertEqual(' '.join(p.cmdline), w.CommandLine) - - def test_process_username(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - domain, _, username = w.GetOwner() - username = "%s\\%s" %(domain, username) - self.assertEqual(p.username, username) - - def test_process_rss_memory(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - rss = p.get_memory_info()[0] - self.assertEqual(rss, int(w.WorkingSetSize)) - - def test_process_vsz_memory(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - vsz = p.get_memory_info()[1] - self.assertEqual(vsz, int(w.PageFileUsage) * 1024) - - def test_process_create_time(self): - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(self.pid) - wmic_create = str(w.CreationDate.split('.')[0]) - psutil_create = time.strftime("%Y%m%d%H%M%S", - time.localtime(p.create_time)) - self.assertEqual(wmic_create, psutil_create) - - - # --- psutil namespace functions and constants tests - - def test_NUM_CPUS(self): - num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) - self.assertEqual(num_cpus, psutil.NUM_CPUS) - - def test_TOTAL_PHYMEM(self): - w = wmi.WMI().Win32_ComputerSystem()[0] - self.assertEqual(int(w.TotalPhysicalMemory), psutil.TOTAL_PHYMEM) - - def test__UPTIME(self): - # _UPTIME constant is not public but it is used internally - # as value to return for pid 0 creation time. - # WMI behaves the same. - w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] - p = psutil.Process(0) - wmic_create = str(w.CreationDate.split('.')[0]) - psutil_create = time.strftime("%Y%m%d%H%M%S", - time.localtime(p.create_time)) - - def test_get_pids(self): - # Note: this test might fail if the OS is starting/killing - # other processes in the meantime - w = wmi.WMI().Win32_Process() - wmi_pids = [x.ProcessId for x in w] - wmi_pids.sort() - psutil_pids = psutil.get_pid_list() - psutil_pids.sort() - if wmi_pids != psutil_pids: - difference = filter(lambda x:x not in wmi_pids, psutil_pids) + \ - filter(lambda x:x not in psutil_pids, wmi_pids) - self.fail("difference: " + str(difference)) - - -if __name__ == '__main__': - test_suite = unittest.TestSuite() - test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase)) - unittest.TextTestRunner(verbosity=2).run(test_suite) - +#!/usr/bin/env python +# +# $Id: _windows.py 798 2010-11-12 20:52:57Z g.rodola $ +# + + +import os +import unittest +import platform +import subprocess +import signal +import time +import warnings +import atexit + +import psutil +from test_psutil import reap_children, get_test_subprocess, wait_for_pid +try: + import wmi +except ImportError, err: + atexit.register(warnings.warn, "Couldn't run wmi tests: %s" % str(err), + RuntimeWarning) + wmi = None + +WIN2000 = platform.win32_ver()[0] == '2000' + + +class WindowsSpecificTestCase(unittest.TestCase): + + def setUp(self): + sproc = get_test_subprocess() + wait_for_pid(sproc.pid) + self.pid = sproc.pid + + def tearDown(self): + reap_children() + + def test_issue_24(self): + p = psutil.Process(0) + self.assertRaises(psutil.AccessDenied, p.kill) + + def test_special_pid(self): + if not WIN2000: + p = psutil.Process(4) + else: + p = psutil.Process(8) + self.assertEqual(p.name, 'System') + # use __str__ to access all common Process properties to check + # that nothing strange happens + str(p) + p.username + self.assertTrue(p.create_time >= 0.0) + try: + rss, vms = p.get_memory_info() + except psutil.AccessDenied: + # expected on Windows Vista and Windows 7 + if not platform.uname()[1] in ('vista', 'win-7', 'win7'): + raise + else: + self.assertTrue(rss > 0) + if not WIN2000: + self.assertEqual(vms, 0) + + def test_signal(self): + p = psutil.Process(self.pid) + self.assertRaises(ValueError, p.send_signal, signal.SIGINT) + + if wmi is not None: + + # --- Process class tests + + def test_process_name(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + self.assertEqual(p.name, w.Caption) + + def test_process_path(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + self.assertEqual(p.exe, w.ExecutablePath) + + if not WIN2000: + def test_process_cmdline(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + self.assertEqual(' '.join(p.cmdline), w.CommandLine.replace('"', '')) + + def test_process_username(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + domain, _, username = w.GetOwner() + username = "%s\\%s" %(domain, username) + self.assertEqual(p.username, username) + + def test_process_rss_memory(self): + time.sleep(0.1) + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + rss = p.get_memory_info().rss + self.assertEqual(rss, int(w.WorkingSetSize)) + + def test_process_vms_memory(self): + time.sleep(0.1) + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + vms = p.get_memory_info().vms + # http://msdn.microsoft.com/en-us/library/aa394372(VS.85).aspx + # ...claims that PageFileUsage is represented in Kilo + # bytes but funnily enough on certain platforms bytes are + # returned instead. + wmi_usage = int(w.PageFileUsage) + if (vms != wmi_usage) and (vms != wmi_usage * 1024): + self.fail("wmi=%s, psutil=%s" % (wmi_usage, vms)) + + def test_process_create_time(self): + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(self.pid) + wmic_create = str(w.CreationDate.split('.')[0]) + psutil_create = time.strftime("%Y%m%d%H%M%S", + time.localtime(p.create_time)) + self.assertEqual(wmic_create, psutil_create) + + + # --- psutil namespace functions and constants tests + + def test_NUM_CPUS(self): + num_cpus = int(os.environ['NUMBER_OF_PROCESSORS']) + self.assertEqual(num_cpus, psutil.NUM_CPUS) + + def test_TOTAL_PHYMEM(self): + w = wmi.WMI().Win32_ComputerSystem()[0] + self.assertEqual(int(w.TotalPhysicalMemory), psutil.TOTAL_PHYMEM) + + def test__UPTIME(self): + # _UPTIME constant is not public but it is used internally + # as value to return for pid 0 creation time. + # WMI behaves the same. + w = wmi.WMI().Win32_Process(ProcessId=self.pid)[0] + p = psutil.Process(0) + wmic_create = str(w.CreationDate.split('.')[0]) + psutil_create = time.strftime("%Y%m%d%H%M%S", + time.localtime(p.create_time)) + + def test_get_pids(self): + # Note: this test might fail if the OS is starting/killing + # other processes in the meantime + w = wmi.WMI().Win32_Process() + wmi_pids = [x.ProcessId for x in w] + wmi_pids.sort() + psutil_pids = psutil.get_pid_list() + psutil_pids.sort() + if wmi_pids != psutil_pids: + difference = filter(lambda x:x not in wmi_pids, psutil_pids) + \ + filter(lambda x:x not in psutil_pids, wmi_pids) + self.fail("difference: " + str(difference)) + + +if __name__ == '__main__': + test_suite = unittest.TestSuite() + test_suite.addTest(unittest.makeSuite(WindowsSpecificTestCase)) + unittest.TextTestRunner(verbosity=2).run(test_suite) +