diff --git a/config/gunicorn_conf.py b/config/gunicorn_conf.py index 53462a5b6..f423e05c6 100644 --- a/config/gunicorn_conf.py +++ b/config/gunicorn_conf.py @@ -1,4 +1,4 @@ -import psutil +from vendor import psutil import math GIGS_OF_MEMORY = psutil.TOTAL_PHYMEM/1024/1024/1024. diff --git a/config/requirements.txt b/config/requirements.txt index 360ead3c1..f786653dd 100644 --- a/config/requirements.txt +++ b/config/requirements.txt @@ -22,7 +22,6 @@ lxml==3.1.0 mongoengine==0.8.2 nltk==2.0.4 oauth2==1.5.211 -psutil==0.6.1 pyes==0.19.1 pyelasticsearch==0.5 pyflakes==0.6.1 diff --git a/vendor/psutil/__init__.py b/vendor/psutil/__init__.py new file mode 100644 index 000000000..1215b8126 --- /dev/null +++ b/vendor/psutil/__init__.py @@ -0,0 +1,1195 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# $Id: __init__.py 1525 2012-08-16 16:32:03Z g.rodola $ +# +# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""psutil is a module providing convenience functions for managing +processes and gather system information in a portable way by using +Python. +""" + +from __future__ import division + +__version__ = "0.6.1" +version_info = tuple([int(num) for num in __version__.split('.')]) + +__all__ = [ + # exceptions + "Error", "NoSuchProcess", "AccessDenied", "TimeoutExpired", + # constants + "NUM_CPUS", "TOTAL_PHYMEM", "BOOT_TIME", + "version_info", "__version__", + "STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP", + "STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD", + "STATUS_WAKING", "STATUS_LOCKED", + # classes + "Process", "Popen", + # functions + "test", "pid_exists", "get_pid_list", "process_iter", "get_process_list", + "virtual_memory", "swap_memory", + "cpu_times", "cpu_percent", "per_cpu_percent", + "network_io_counters", "disk_io_counters", + ] + +import sys +import os +import time +import signal +import warnings +import errno +import subprocess +try: + import pwd +except ImportError: + pwd = None + +from psutil.error import Error, NoSuchProcess, AccessDenied, TimeoutExpired +from psutil._compat import property, callable, defaultdict +from psutil._common import cached_property +from psutil._common import (deprecated as _deprecated, + nt_disk_iostat as _nt_disk_iostat, + nt_net_iostat as _nt_net_iostat, + nt_sysmeminfo as _nt_sysmeminfo, + isfile_strict as _isfile_strict) +from psutil._common import (STATUS_RUNNING, STATUS_IDLE, STATUS_SLEEPING, + STATUS_DISK_SLEEP, STATUS_STOPPED, + STATUS_TRACING_STOP, STATUS_ZOMBIE, STATUS_DEAD, + STATUS_WAKING, STATUS_LOCKED) + +# import the appropriate module for our platform only +if sys.platform.startswith("linux"): + import psutil._pslinux as _psplatform + from psutil._pslinux import (phymem_buffers, + cached_phymem, + IOPRIO_CLASS_NONE, + IOPRIO_CLASS_RT, + IOPRIO_CLASS_BE, + IOPRIO_CLASS_IDLE) + phymem_buffers = _psplatform.phymem_buffers + cached_phymem = _psplatform.cached_phymem + +elif sys.platform.startswith("win32"): + import psutil._psmswindows as _psplatform + from psutil._psmswindows import (ABOVE_NORMAL_PRIORITY_CLASS, + BELOW_NORMAL_PRIORITY_CLASS, + HIGH_PRIORITY_CLASS, + IDLE_PRIORITY_CLASS, + NORMAL_PRIORITY_CLASS, + REALTIME_PRIORITY_CLASS) + +elif sys.platform.startswith("darwin"): + import psutil._psosx as _psplatform + +elif sys.platform.startswith("freebsd"): + import psutil._psbsd as _psplatform + +else: + raise NotImplementedError('platform %s is not supported' % sys.platform) + +__all__.extend(_psplatform.__extra__all__) + +NUM_CPUS = _psplatform.NUM_CPUS +BOOT_TIME = _psplatform.BOOT_TIME +TOTAL_PHYMEM = _psplatform.TOTAL_PHYMEM + + +class Process(object): + """Represents an OS process.""" + + def __init__(self, pid): + """Create a new Process object for the given pid. + Raises NoSuchProcess if pid does not exist. + """ + self._pid = pid + self._gone = False + # platform-specific modules define an _psplatform.Process + # implementation class + self._platform_impl = _psplatform.Process(pid) + self._last_sys_cpu_times = None + self._last_proc_cpu_times = None + # cache creation time for later use in is_running() method + try: + self.create_time + except AccessDenied: + pass + except NoSuchProcess: + raise NoSuchProcess(pid, None, 'no process found with pid %s' % pid) + + def __str__(self): + try: + pid = self.pid + name = repr(self.name) + except NoSuchProcess: + details = "(pid=%s (terminated))" % self.pid + except AccessDenied: + details = "(pid=%s)" % (self.pid) + else: + details = "(pid=%s, name=%s)" % (pid, name) + return "%s.%s%s" % (self.__class__.__module__, + self.__class__.__name__, details) + + def __repr__(self): + return "<%s at %s>" % (self.__str__(), id(self)) + + def as_dict(self, attrs=[], ad_value=None): + """Utility method returning process information as a hashable + dictionary. + + If 'attrs' is specified it must be a list of strings reflecting + available Process class's attribute names (e.g. ['get_cpu_times', + 'name']) else all public (read only) attributes are assumed. + + 'ad_value' is the value which gets assigned to a dict key in case + AccessDenied exception is raised when retrieving that particular + process information. + """ + excluded_names = set(['send_signal', 'suspend', 'resume', 'terminate', + 'kill', 'wait', 'is_running', 'as_dict', 'parent', + 'get_children', 'nice']) + retdict = dict() + for name in set(attrs or dir(self)): + if name.startswith('_'): + continue + if name.startswith('set_'): + continue + if name in excluded_names: + continue + try: + attr = getattr(self, name) + if callable(attr): + if name == 'get_cpu_percent': + ret = attr(interval=0) + else: + ret = attr() + else: + ret = attr + except AccessDenied: + ret = ad_value + except NotImplementedError: + # in case of not implemented functionality (may happen + # on old or exotic systems) we want to crash only if + # the user explicitly asked for that particular attr + if attrs: + raise + continue + if name.startswith('get'): + if name[3] == '_': + name = name[4:] + elif name == 'getcwd': + name = 'cwd' + retdict[name] = ret + return retdict + + @property + def pid(self): + """The process pid.""" + return self._pid + + @cached_property + def ppid(self): + """The process parent pid.""" + return self._platform_impl.get_process_ppid() + + @property + def parent(self): + """Return the parent process as a Process object. If no parent + pid is known return None. + """ + ppid = self.ppid + if ppid is not None: + try: + return Process(ppid) + except NoSuchProcess: + pass + + @cached_property + def name(self): + """The process name.""" + name = self._platform_impl.get_process_name() + if os.name == 'posix': + # On UNIX the name gets truncated to the first 15 characters. + # If it matches the first part of the cmdline we return that + # one instead because it's usually more explicative. + # Examples are "gnome-keyring-d" vs. "gnome-keyring-daemon". + try: + cmdline = self.cmdline + except AccessDenied: + pass + else: + if cmdline: + extended_name = os.path.basename(cmdline[0]) + if extended_name.startswith(name): + name = extended_name + # XXX - perhaps needs refactoring + self._platform_impl._process_name = name + return name + + @cached_property + def exe(self): + """The process executable path. May also be an empty string.""" + def guess_it(fallback): + # try to guess exe from cmdline[0] in absence of a native + # exe representation + cmdline = self.cmdline + if cmdline and hasattr(os, 'access') and hasattr(os, 'X_OK'): + exe = cmdline[0] # the possible exe + rexe = os.path.realpath(exe) # ...in case it's a symlink + if os.path.isabs(rexe) and os.path.isfile(rexe) \ + and os.access(rexe, os.X_OK): + return exe + if isinstance(fallback, AccessDenied): + raise fallback + return fallback + + try: + exe = self._platform_impl.get_process_exe() + except AccessDenied: + err = sys.exc_info()[1] + return guess_it(fallback=err) + else: + if not exe: + # underlying implementation can legitimately return an + # empty string; if that's the case we don't want to + # raise AD while guessing from the cmdline + try: + exe = guess_it(fallback=exe) + except AccessDenied: + pass + return exe + + @cached_property + def cmdline(self): + """The command line process has been called with.""" + return self._platform_impl.get_process_cmdline() + + @property + def status(self): + """The process current status as a STATUS_* constant.""" + return self._platform_impl.get_process_status() + + if os.name == 'posix': + + @property + def uids(self): + """Return a named tuple denoting the process real, + effective, and saved user ids. + """ + return self._platform_impl.get_process_uids() + + @property + def gids(self): + """Return a named tuple denoting the process real, + effective, and saved group ids. + """ + return self._platform_impl.get_process_gids() + + @property + def terminal(self): + """The terminal associated with this process, if any, + else None. + """ + return self._platform_impl.get_process_terminal() + + @property + def username(self): + """The name of the user that owns the process. + On UNIX this is calculated by using *real* process uid. + """ + if os.name == 'posix': + if pwd is None: + # might happen if python was installed from sources + raise ImportError("requires pwd module shipped with standard python") + return pwd.getpwuid(self.uids.real).pw_name + else: + return self._platform_impl.get_process_username() + + @cached_property + def create_time(self): + """The process creation time as a floating point number + expressed in seconds since the epoch, in UTC. + """ + return self._platform_impl.get_process_create_time() + + def getcwd(self): + """Return a string representing the process current working + directory. + """ + return self._platform_impl.get_process_cwd() + + # Linux, BSD and Windows only + if hasattr(_psplatform.Process, "get_process_io_counters"): + + def get_io_counters(self): + """Return process I/O statistics as a namedtuple including + the number of read/write calls performed and the amount of + bytes read and written by the process. + """ + return self._platform_impl.get_process_io_counters() + + def get_nice(self): + """Get process niceness (priority).""" + return self._platform_impl.get_process_nice() + + def set_nice(self, value): + """Set process niceness (priority).""" + return self._platform_impl.set_process_nice(value) + + # available only on Linux + if hasattr(_psplatform.Process, "get_process_ionice"): + + def get_ionice(self): + """Return process I/O niceness (priority) as a namedtuple.""" + return self._platform_impl.get_process_ionice() + + def set_ionice(self, ioclass, value=None): + """Set process I/O niceness (priority). + ioclass is one of the IOPRIO_CLASS_* constants. + iodata is a number which goes from 0 to 7. The higher the + value, the lower the I/O priority of the process. + """ + return self._platform_impl.set_process_ionice(ioclass, value) + + # available on Windows and Linux only + if hasattr(_psplatform.Process, "get_process_cpu_affinity"): + + def get_cpu_affinity(self): + """Get process current CPU affinity.""" + return self._platform_impl.get_process_cpu_affinity() + + def set_cpu_affinity(self, cpus): + """Set process current CPU affinity. + 'cpus' is a list of CPUs for which you want to set the + affinity (e.g. [0, 1]). + """ + return self._platform_impl.set_process_cpu_affinity(cpus) + + if os.name == 'nt': + + def get_num_handles(self): + """Return the number of handles opened by this process + (Windows only). + """ + return self._platform_impl.get_num_handles() + + if os.name == 'posix': + + def get_num_fds(self): + """Return the number of file descriptors opened by this + process (POSIX only). + """ + return self._platform_impl.get_num_fds() + + def get_num_ctx_switches(self): + """Return the number voluntary and involuntary context switches + performed by this process. + """ + return self._platform_impl.get_num_ctx_switches() + + def get_num_threads(self): + """Return the number of threads used by this process.""" + return self._platform_impl.get_process_num_threads() + + def get_threads(self): + """Return threads opened by process as a list of namedtuples + including thread id and thread CPU times (user/system). + """ + return self._platform_impl.get_process_threads() + + def get_children(self, recursive=False): + """Return the children of this process as a list of Process + objects. + If recursive is True return all the parent descendants. + + Example (A == this process): + + A ─┐ + │ + ├─ B (child) ─┐ + │ └─ X (grandchild) ─┐ + │ └─ Y (great grandchild) + ├─ C (child) + └─ D (child) + + >>> p.get_children() + B, C, D + >>> p.get_children(recursive=True) + B, X, Y, C, D + + Note that in the example above if process X disappears + process Y won't be returned either as the reference to + process A is lost. + """ + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + + ret = [] + if not recursive: + for p in process_iter(): + try: + if p.ppid == self.pid: + # if child happens to be older than its parent + # (self) it means child's PID has been reused + if self.create_time <= p.create_time: + ret.append(p) + except NoSuchProcess: + pass + else: + # construct a dict where 'values' are all the processes + # having 'key' as their parent + table = defaultdict(list) + for p in process_iter(): + try: + table[p.ppid].append(p) + except NoSuchProcess: + pass + # At this point we have a mapping table where table[self.pid] + # are the current process's children. + # Below, we look for all descendants recursively, similarly + # to a recursive function call. + checkpids = [self.pid] + for pid in checkpids: + for child in table[pid]: + try: + # if child happens to be older than its parent + # (self) it means child's PID has been reused + intime = self.create_time <= child.create_time + except NoSuchProcess: + pass + else: + if intime: + ret.append(child) + if child.pid not in checkpids: + checkpids.append(child.pid) + return ret + + def get_cpu_percent(self, interval=0.1): + """Return a float representing the current process CPU + utilization as a percentage. + + When interval is > 0.0 compares process times to system CPU + times elapsed before and after the interval (blocking). + + When interval is 0.0 or None compares process times to system CPU + times elapsed since last call, returning immediately. + In this case is recommended for accuracy that this function be + called with at least 0.1 seconds between calls. + """ + blocking = interval is not None and interval > 0.0 + if blocking: + st1 = sum(cpu_times()) + pt1 = self._platform_impl.get_cpu_times() + time.sleep(interval) + st2 = sum(cpu_times()) + pt2 = self._platform_impl.get_cpu_times() + else: + st1 = self._last_sys_cpu_times + pt1 = self._last_proc_cpu_times + st2 = sum(cpu_times()) + pt2 = self._platform_impl.get_cpu_times() + if st1 is None or pt1 is None: + self._last_sys_cpu_times = st2 + self._last_proc_cpu_times = pt2 + return 0.0 + + delta_proc = (pt2.user - pt1.user) + (pt2.system - pt1.system) + delta_time = st2 - st1 + # reset values for next call in case of interval == None + self._last_sys_cpu_times = st2 + self._last_proc_cpu_times = pt2 + + try: + # the utilization split between all CPUs + overall_percent = (delta_proc / delta_time) * 100 + except ZeroDivisionError: + # interval was too low + return 0.0 + # the utilization of a single CPU + single_cpu_percent = overall_percent * NUM_CPUS + # on posix a percentage > 100 is legitimate + # http://stackoverflow.com/questions/1032357/comprehending-top-cpu-usage + # on windows we use this ugly hack to avoid troubles with float + # precision issues + if os.name != 'posix': + if single_cpu_percent > 100.0: + return 100.0 + return round(single_cpu_percent, 1) + + def get_cpu_times(self): + """Return a tuple whose values are process CPU user and system + times. The same as os.times() but per-process. + """ + return self._platform_impl.get_cpu_times() + + def get_memory_info(self): + """Return a tuple representing RSS (Resident Set Size) and VMS + (Virtual Memory Size) in bytes. + + On UNIX RSS and VMS are the same values shown by ps. + + On Windows RSS and VMS refer to "Mem Usage" and "VM Size" columns + of taskmgr.exe. + """ + return self._platform_impl.get_memory_info() + + def get_ext_memory_info(self): + """Return a namedtuple with variable fields depending on the + platform representing extended memory information about + the process. All numbers are expressed in bytes. + """ + return self._platform_impl.get_ext_memory_info() + + def get_memory_percent(self): + """Compare physical system memory to process resident memory and + calculate process memory utilization as a percentage. + """ + rss = self._platform_impl.get_memory_info()[0] + try: + return (rss / float(TOTAL_PHYMEM)) * 100 + except ZeroDivisionError: + return 0.0 + + def get_memory_maps(self, grouped=True): + """Return process's mapped memory regions as a list of nameduples + whose fields are variable depending on the platform. + + If 'grouped' is True the mapped regions with the same 'path' + are grouped together and the different memory fields are summed. + + If 'grouped' is False every mapped region is shown as a single + entity and the namedtuple will also include the mapped region's + address space ('addr') and permission set ('perms'). + """ + it = self._platform_impl.get_memory_maps() + if grouped: + d = {} + for tupl in it: + path = tupl[2] + nums = tupl[3:] + try: + d[path] = map(lambda x, y: x+y, d[path], nums) + except KeyError: + d[path] = nums + nt = self._platform_impl.nt_mmap_grouped + return [nt(path, *d[path]) for path in d] + else: + nt = self._platform_impl.nt_mmap_ext + return [nt(*x) for x in it] + + def get_open_files(self): + """Return files opened by process as a list of namedtuples + including absolute file name and file descriptor number. + """ + return self._platform_impl.get_open_files() + + def get_connections(self, kind='inet'): + """Return connections opened by process as a list of namedtuples. + The kind parameter filters for connections that fit the following + criteria: + + Kind Value Connections using + inet IPv4 and IPv6 + inet4 IPv4 + inet6 IPv6 + tcp TCP + tcp4 TCP over IPv4 + tcp6 TCP over IPv6 + udp UDP + udp4 UDP over IPv4 + udp6 UDP over IPv6 + unix UNIX socket (both UDP and TCP protocols) + all the sum of all the possible families and protocols + """ + return self._platform_impl.get_connections(kind) + + def is_running(self): + """Return whether this process is running.""" + if self._gone: + return False + try: + # Checking if pid is alive is not enough as the pid might + # have been reused by another process. + # pid + creation time, on the other hand, is supposed to + # identify a process univocally. + return self.create_time == \ + self._platform_impl.get_process_create_time() + except NoSuchProcess: + self._gone = True + return False + + def send_signal(self, sig): + """Send a signal to process (see signal module constants). + On Windows only SIGTERM is valid and is treated as an alias + for kill(). + """ + # safety measure in case the current process has been killed in + # meantime and the kernel reused its PID + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + if os.name == 'posix': + try: + os.kill(self.pid, sig) + except OSError: + err = sys.exc_info()[1] + name = self._platform_impl._process_name + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, name) + if err.errno == errno.EPERM: + raise AccessDenied(self.pid, name) + raise + else: + if sig == signal.SIGTERM: + self._platform_impl.kill_process() + else: + raise ValueError("only SIGTERM is supported on Windows") + + def suspend(self): + """Suspend process execution.""" + # safety measure in case the current process has been killed in + # meantime and the kernel reused its PID + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + # windows + if hasattr(self._platform_impl, "suspend_process"): + self._platform_impl.suspend_process() + else: + # posix + self.send_signal(signal.SIGSTOP) + + def resume(self): + """Resume process execution.""" + # safety measure in case the current process has been killed in + # meantime and the kernel reused its PID + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + # windows + if hasattr(self._platform_impl, "resume_process"): + self._platform_impl.resume_process() + else: + # posix + self.send_signal(signal.SIGCONT) + + def terminate(self): + """Terminate the process with SIGTERM. + On Windows this is an alias for kill(). + """ + self.send_signal(signal.SIGTERM) + + def kill(self): + """Kill the current process.""" + # safety measure in case the current process has been killed in + # meantime and the kernel reused its PID + if not self.is_running(): + name = self._platform_impl._process_name + raise NoSuchProcess(self.pid, name) + if os.name == 'posix': + self.send_signal(signal.SIGKILL) + else: + self._platform_impl.kill_process() + + def wait(self, timeout=None): + """Wait for process to terminate and, if process is a children + of the current one also return its exit code, else None. + """ + if timeout is not None and not timeout >= 0: + raise ValueError("timeout must be a positive integer") + return self._platform_impl.process_wait(timeout) + + # --- deprecated API + + @property + def nice(self): + """Get or set process niceness (priority). + Deprecated, use get_nice() instead. + """ + msg = "this property is deprecated; use Process.get_nice() method instead" + warnings.warn(msg, category=DeprecationWarning, stacklevel=2) + return self.get_nice() + + @nice.setter + def nice(self, value): + # invoked on "p.nice = num"; change process niceness + # deprecated in favor of set_nice() + msg = "this property is deprecated; use Process.set_nice() method instead" + warnings.warn(msg, category=DeprecationWarning, stacklevel=2) + return self.set_nice(value) + + +class Popen(Process): + """A more convenient interface to stdlib subprocess module. + It starts a sub process and deals with it exactly as when using + subprocess.Popen class but in addition also provides all the + property and methods of psutil.Process class in a single interface: + + >>> import psutil + >>> from subprocess import PIPE + >>> p = psutil.Popen(["/usr/bin/python", "-c", "print 'hi'"], stdout=PIPE) + >>> p.name + 'python' + >>> p.uids + user(real=1000, effective=1000, saved=1000) + >>> p.username + 'giampaolo' + >>> p.communicate() + ('hi\n', None) + >>> p.terminate() + >>> p.wait(timeout=2) + 0 + >>> + + For method names common to both classes such as kill(), terminate() + and wait(), psutil.Process implementation takes precedence. + + For a complete documentation refers to: + http://docs.python.org/library/subprocess.html + """ + + def __init__(self, *args, **kwargs): + self.__subproc = subprocess.Popen(*args, **kwargs) + self._pid = self.__subproc.pid + self._gone = False + self._platform_impl = _psplatform.Process(self._pid) + self._last_sys_cpu_times = None + self._last_proc_cpu_times = None + try: + self.create_time + except AccessDenied: + pass + except NoSuchProcess: + raise NoSuchProcess(self._pid, None, + "no process found with pid %s" % pid) + + def __dir__(self): + return list(set(dir(Popen) + dir(subprocess.Popen))) + + def __getattribute__(self, name): + try: + return object.__getattribute__(self, name) + except AttributeError: + try: + return object.__getattribute__(self.__subproc, name) + except AttributeError: + raise AttributeError("%s instance has no attribute '%s'" + %(self.__class__.__name__, name)) + + +# ===================================================================== +# --- system processes related functions +# ===================================================================== + +get_pid_list = _psplatform.get_pid_list +pid_exists = _psplatform.pid_exists + +_pmap = {} + +def process_iter(): + """Return a generator yielding a Process class instance for all + running processes on the local machine. + + Every new Process instance is only created once and then cached + into an internal table which is updated every time this is used. + + The sorting order in which processes are yielded is based on + their PIDs. + """ + def add(pid): + proc = Process(pid) + _pmap[proc.pid] = proc + return proc + + def remove(pid): + _pmap.pop(pid, None) + + a = set(get_pid_list()) + b = set(_pmap.keys()) + new_pids = a - b + gone_pids = b - a + + for pid in gone_pids: + remove(pid) + for pid, proc in sorted(list(_pmap.items()) + \ + list(dict.fromkeys(new_pids).items())): + try: + if proc is None: # new process + yield add(pid) + else: + # use is_running() to check whether PID has been reused by + # another process in which case yield a new Process instance + if proc.is_running(): + yield proc + else: + yield add(pid) + except NoSuchProcess: + remove(pid) + except AccessDenied: + # Process creation time can't be determined hence there's + # no way to tell whether the pid of the cached process + # has been reused. Just return the cached version. + yield proc + +# ===================================================================== +# --- CPU related functions +# ===================================================================== + +def cpu_times(percpu=False): + """Return system-wide CPU times as a namedtuple object. + Every CPU time represents the time CPU has spent in the given mode. + The attributes availability varies depending on the platform. + Here follows a list of all available attributes: + - user + - system + - idle + - nice (UNIX) + - iowait (Linux) + - irq (Linux, FreeBSD) + - softirq (Linux) + + When percpu is True return a list of nameduples for each CPU. + First element of the list refers to first CPU, second element + to second CPU and so on. + The order of the list is consistent across calls. + """ + if not percpu: + return _psplatform.get_system_cpu_times() + else: + return _psplatform.get_system_per_cpu_times() + + +_last_cpu_times = cpu_times() +_last_per_cpu_times = cpu_times(percpu=True) + +def cpu_percent(interval=0.1, percpu=False): + """Return a float representing the current system-wide CPU + utilization as a percentage. + + When interval is > 0.0 compares system CPU times elapsed before + and after the interval (blocking). + + When interval is 0.0 or None compares system CPU times elapsed + since last call or module import, returning immediately. + In this case is recommended for accuracy that this function be + called with at least 0.1 seconds between calls. + + When percpu is True returns a list of floats representing the + utilization as a percentage for each CPU. + First element of the list refers to first CPU, second element + to second CPU and so on. + The order of the list is consistent across calls. + """ + global _last_cpu_times + global _last_per_cpu_times + blocking = interval is not None and interval > 0.0 + + def calculate(t1, t2): + t1_all = sum(t1) + t1_busy = t1_all - t1.idle + + t2_all = sum(t2) + t2_busy = t2_all - t2.idle + + # this usually indicates a float precision issue + if t2_busy <= t1_busy: + return 0.0 + + busy_delta = t2_busy - t1_busy + all_delta = t2_all - t1_all + busy_perc = (busy_delta / all_delta) * 100 + return round(busy_perc, 1) + + # system-wide usage + if not percpu: + if blocking: + t1 = cpu_times() + time.sleep(interval) + else: + t1 = _last_cpu_times + _last_cpu_times = cpu_times() + return calculate(t1, _last_cpu_times) + # per-cpu usage + else: + ret = [] + if blocking: + tot1 = cpu_times(percpu=True) + time.sleep(interval) + else: + tot1 = _last_per_cpu_times + _last_per_cpu_times = cpu_times(percpu=True) + for t1, t2 in zip(tot1, _last_per_cpu_times): + ret.append(calculate(t1, t2)) + return ret + +# ===================================================================== +# --- system memory related functions +# ===================================================================== + +def virtual_memory(): + """Return statistics about system memory usage as a namedtuple + including the following fields, expressed in bytes: + + - total: + total physical memory available. + + - available: + the actual amount of available memory that can be given + instantly to processes that request more memory in bytes; this + is calculated by summing different memory values depending on + the platform (e.g. free + buffers + cached on Linux) and it is + supposed to be used to monitor actual memory usage in a cross + platform fashion. + + - percent: + the percentage usage calculated as (total - available) / total * 100 + + - used: + memory used, calculated differently depending on the platform and + designed for informational purposes only: + OSX: active + inactive + wired + BSD: active + wired + cached + LINUX: total - free + + - free: + memory not being used at all (zeroed) that is readily available; + note that this doesn't reflect the actual memory available + (use 'available' instead) + + Platform-specific fields: + + - active (UNIX): + memory currently in use or very recently used, and so it is in RAM. + + - inactive (UNIX): + memory that is marked as not used. + + - buffers (BSD, Linux): + cache for things like file system metadata. + + - cached (BSD, OSX): + cache for various things. + + - wired (OSX, BSD): + memory that is marked to always stay in RAM. It is never moved to disk. + + - shared (BSD): + memory that may be simultaneously accessed by multiple processes. + + The sum of 'used' and 'available' does not necessarily equal total. + On Windows 'available' and 'free' are the same. + """ + return _psplatform.virtual_memory() + +def swap_memory(): + """Return system swap memory statistics as a namedtuple including + the following attributes: + + - total: total swap memory in bytes + - used: used swap memory in bytes + - free: free swap memory in bytes + - percent: the percentage usage + - sin: no. of bytes the system has swapped in from disk (cumulative) + - sout: no. of bytes the system has swapped out from disk (cumulative) + + 'sin' and 'sout' on Windows are meaningless and always set to 0. + """ + return _psplatform.swap_memory() + +# ===================================================================== +# --- disks/paritions related functions +# ===================================================================== + +def disk_usage(path): + """Return disk usage statistics about the given path as a namedtuple + including total, used and free space expressed in bytes plus the + percentage usage. + """ + return _psplatform.get_disk_usage(path) + +def disk_partitions(all=False): + """Return mounted partitions as a list of namedtuples including + device, mount point, filesystem type and mount options (a raw + string separated by commas which may vary depending on the platform). + + If "all" parameter is False return physical devices only and ignore + all others. + """ + return _psplatform.disk_partitions(all) + +def disk_io_counters(perdisk=False): + """Return system disk I/O statistics as a namedtuple including + the following attributes: + + - read_count: number of reads + - write_count: number of writes + - read_bytes: number of bytes read + - write_bytes: number of bytes written + - read_time: time spent reading from disk (in milliseconds) + - write_time: time spent writing to disk (in milliseconds) + + If perdisk is True return the same information for every + physical disk installed on the system as a dictionary + with partition names as the keys and the namedutuple + described above as the values. + """ + rawdict = _psplatform.disk_io_counters() + if not rawdict: + raise RuntimeError("couldn't find any physical disk") + if perdisk: + for disk, fields in rawdict.items(): + rawdict[disk] = _nt_disk_iostat(*fields) + return rawdict + else: + return _nt_disk_iostat(*[sum(x) for x in zip(*rawdict.values())]) + +# ===================================================================== +# --- network related functions +# ===================================================================== + +def network_io_counters(pernic=False): + """Return network I/O statistics as a namedtuple including + the following attributes: + + - bytes_sent: number of bytes sent + - bytes_recv: number of bytes received + - packets_sent: number of packets sent + - packets_recv: number of packets received + - errin: total number of errors while receiving + - errout: total number of errors while sending + - dropin: total number of incoming packets which were dropped + - dropout: total number of outgoing packets which were dropped + (always 0 on OSX and BSD) + + If pernic is True return the same information for every + network interface installed on the system as a dictionary + with network interface names as the keys and the namedtuple + described above as the values. + """ + rawdict = _psplatform.network_io_counters() + if not rawdict: + raise RuntimeError("couldn't find any network interface") + if pernic: + for nic, fields in rawdict.items(): + rawdict[nic] = _nt_net_iostat(*fields) + return rawdict + else: + return _nt_net_iostat(*[sum(x) for x in zip(*rawdict.values())]) + +# ===================================================================== +# --- other system related functions +# ===================================================================== + +def get_users(): + """Return users currently connected on the system as a list of + namedtuples including the following attributes. + + - user: the name of the user + - terminal: the tty or pseudo-tty associated with the user, if any. + - host: the host name associated with the entry, if any. + - started: the creation time as a floating point number expressed in + seconds since the epoch. + """ + return _psplatform.get_system_users() + +# ===================================================================== +# --- deprecated functions +# ===================================================================== + +@_deprecated() +def get_process_list(): + """Return a list of Process class instances for all running + processes on the local machine (deprecated). + """ + return list(process_iter()) + +@_deprecated() +def phymem_usage(): + """Return the amount of total, used and free physical memory + on the system in bytes plus the percentage usage. + Deprecated by psutil.virtual_memory(). + """ + mem = virtual_memory() + return _nt_sysmeminfo(mem.total, mem.used, mem.free, mem.percent) + +@_deprecated("psutil.swap_memory()") +def virtmem_usage(): + return swap_memory() + +@_deprecated("psutil.phymem_usage().free") +def avail_phymem(): + return phymem_usage().free + +@_deprecated("psutil.phymem_usage().used") +def used_phymem(): + return phymem_usage().used + +@_deprecated("psutil.virtmem_usage().total") +def total_virtmem(): + return virtmem_usage().total + +@_deprecated("psutil.virtmem_usage().used") +def used_virtmem(): + return virtmem_usage().used + +@_deprecated("psutil.virtmem_usage().free") +def avail_virtmem(): + return virtmem_usage().free + +def test(): + """List info of all currently running processes emulating ps aux + output. + """ + import datetime + from psutil._compat import print_ + + today_day = datetime.date.today() + templ = "%-10s %5s %4s %4s %7s %7s %-13s %5s %7s %s" + attrs = ['pid', 'username', 'get_cpu_percent', 'get_memory_percent', 'name', + 'get_cpu_times', 'create_time', 'get_memory_info'] + if os.name == 'posix': + attrs.append('terminal') + print_(templ % ("USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", "START", + "TIME", "COMMAND")) + for p in sorted(process_iter(), key=lambda p: p.pid): + try: + pinfo = p.as_dict(attrs, ad_value='') + except NoSuchProcess: + pass + else: + if pinfo['create_time']: + ctime = datetime.datetime.fromtimestamp(pinfo['create_time']) + if ctime.date() == today_day: + ctime = ctime.strftime("%H:%M") + else: + ctime = ctime.strftime("%b%d") + cputime = time.strftime("%M:%S", time.localtime(sum(pinfo['cpu_times']))) + user = pinfo['username'] + if os.name == 'nt' and '\\' in user: + user = user.split('\\')[1] + vms = pinfo['memory_info'] and \ + int(pinfo['memory_info'].vms / 1024) or '?' + rss = pinfo['memory_info'] and \ + int(pinfo['memory_info'].rss / 1024) or '?' + memp = pinfo['memory_percent'] and \ + round(pinfo['memory_percent'], 1) or '?' + print_(templ % (user[:10], + pinfo['pid'], + pinfo['cpu_percent'], + memp, + vms, + rss, + pinfo.get('terminal', '') or '?', + ctime, + cputime, + pinfo['name'].strip() or '?')) + +if __name__ == "__main__": + test() + +del property, cached_property, division +if sys.version_info < (3, 0): + del num diff --git a/vendor/psutil/_common.py b/vendor/psutil/_common.py new file mode 100644 index 000000000..f0431bca9 --- /dev/null +++ b/vendor/psutil/_common.py @@ -0,0 +1,193 @@ +#/usr/bin/env python +# +#$Id: _common.py 1524 2012-08-16 15:06:32Z g.rodola $ +# +# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Common objects shared by all _ps* modules.""" + +from __future__ import division +import sys +import os +import stat +import errno +import warnings + +from psutil._compat import namedtuple, long, wraps + +# --- functions + +def usage_percent(used, total, _round=None): + """Calculate percentage usage of 'used' against 'total'.""" + try: + ret = (used / total) * 100 + except ZeroDivisionError: + ret = 0 + if _round is not None: + return round(ret, _round) + else: + return ret + +class constant(int): + """A constant type; overrides base int to provide a useful name on str().""" + + def __new__(cls, value, name, doc=None): + inst = super(constant, cls).__new__(cls, value) + inst._name = name + if doc is not None: + inst.__doc__ = doc + return inst + + def __str__(self): + return self._name + + def __eq__(self, other): + # Use both int or str values when comparing for equality + # (useful for serialization): + # >>> st = constant(0, "running") + # >>> st == 0 + # True + # >>> st == 'running' + # True + if isinstance(other, int): + return int(self) == other + if isinstance(other, long): + return long(self) == other + if isinstance(other, str): + return self._name == other + return False + + def __ne__(self, other): + return not self.__eq__(other) + +def memoize(f): + """A simple memoize decorator for functions.""" + cache= {} + def memf(*x): + if x not in cache: + cache[x] = f(*x) + return cache[x] + return memf + +class cached_property(object): + """A memoize decorator for class properties.""" + enabled = True + + def __init__(self, func): + self.func = func + + def __get__(self, instance, type): + ret = self.func(instance) + if self.enabled: + instance.__dict__[self.func.__name__] = ret + return ret + +# http://goo.gl/jYLvf +def deprecated(replacement=None): + """A decorator which can be used to mark functions as deprecated.""" + def outer(fun): + msg = "psutil.%s is deprecated" % fun.__name__ + if replacement is not None: + msg += "; use %s instead" % replacement + if fun.__doc__ is None: + fun.__doc__ = msg + + @wraps(fun) + def inner(*args, **kwargs): + warnings.warn(msg, category=DeprecationWarning, stacklevel=2) + return fun(*args, **kwargs) + + return inner + return outer + + +def isfile_strict(path): + """Same as os.path.isfile() but does not swallow EACCES / EPERM + exceptions, see: + http://mail.python.org/pipermail/python-dev/2012-June/120787.html + """ + try: + st = os.stat(path) + except OSError: + err = sys.exc_info()[1] + if err.errno in (errno.EPERM, errno.EACCES): + raise + return False + else: + return stat.S_ISREG(st.st_mode) + + +# --- constants + +STATUS_RUNNING = constant(0, "running") +STATUS_SLEEPING = constant(1, "sleeping") +STATUS_DISK_SLEEP = constant(2, "disk sleep") +STATUS_STOPPED = constant(3, "stopped") +STATUS_TRACING_STOP = constant(4, "tracing stop") +STATUS_ZOMBIE = constant(5, "zombie") +STATUS_DEAD = constant(6, "dead") +STATUS_WAKE_KILL = constant(7, "wake kill") +STATUS_WAKING = constant(8, "waking") +STATUS_IDLE = constant(9, "idle") # BSD +STATUS_LOCKED = constant(10, "locked") # BSD +STATUS_WAITING = constant(11, "waiting") # BSD + +# --- Process.get_connections() 'kind' parameter mapping + +import socket +from socket import AF_INET, SOCK_STREAM, SOCK_DGRAM +AF_INET6 = getattr(socket, 'AF_INET6', None) +AF_UNIX = getattr(socket, 'AF_UNIX', None) + +conn_tmap = { + "all" : ([AF_INET, AF_INET6, AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), + "tcp" : ([AF_INET, AF_INET6], [SOCK_STREAM]), + "tcp4" : ([AF_INET], [SOCK_STREAM]), + "udp" : ([AF_INET, AF_INET6], [SOCK_DGRAM]), + "udp4" : ([AF_INET], [SOCK_DGRAM]), + "inet" : ([AF_INET, AF_INET6], [SOCK_STREAM, SOCK_DGRAM]), + "inet4": ([AF_INET], [SOCK_STREAM, SOCK_DGRAM]), + "inet6": ([AF_INET6], [SOCK_STREAM, SOCK_DGRAM]), +} + +if AF_INET6 is not None: + conn_tmap.update({ + "tcp6" : ([AF_INET6], [SOCK_STREAM]), + "udp6" : ([AF_INET6], [SOCK_DGRAM]), + }) + +if AF_UNIX is not None: + conn_tmap.update({ + "unix" : ([AF_UNIX], [SOCK_STREAM, SOCK_DGRAM]), + }) + + +del AF_INET, AF_INET6, AF_UNIX, SOCK_STREAM, SOCK_DGRAM, socket + +# --- namedtuples + +# system +nt_sys_cputimes = namedtuple('cputimes', 'user nice system idle iowait irq softirq') +nt_sysmeminfo = namedtuple('usage', 'total used free percent') +# XXX - would 'available' be better than 'free' as for virtual_memory() nt? +nt_swapmeminfo = namedtuple('swap', 'total used free percent sin sout') +nt_diskinfo = namedtuple('usage', 'total used free percent') +nt_partition = namedtuple('partition', 'device mountpoint fstype opts') +nt_net_iostat = namedtuple('iostat', + 'bytes_sent bytes_recv packets_sent packets_recv errin errout dropin dropout') +nt_disk_iostat = namedtuple('iostat', 'read_count write_count read_bytes write_bytes read_time write_time') +nt_user = namedtuple('user', 'name terminal host started') + +# processes +nt_meminfo = namedtuple('meminfo', 'rss vms') +nt_cputimes = namedtuple('cputimes', 'user system') +nt_openfile = namedtuple('openfile', 'path fd') +nt_connection = namedtuple('connection', 'fd family type local_address remote_address status') +nt_thread = namedtuple('thread', 'id user_time system_time') +nt_uids = namedtuple('user', 'real effective saved') +nt_gids = namedtuple('group', 'real effective saved') +nt_io = namedtuple('io', 'read_count write_count read_bytes write_bytes') +nt_ionice = namedtuple('ionice', 'ioclass value') +nt_ctxsw = namedtuple('amount', 'voluntary involuntary') diff --git a/vendor/psutil/_compat.py b/vendor/psutil/_compat.py new file mode 100644 index 000000000..dee9e058d --- /dev/null +++ b/vendor/psutil/_compat.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python +# +# $Id: _compat.py 1524 2012-08-16 15:06:32Z g.rodola $ +# +# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Module which provides compatibility with older Python versions.""" + +__all__ = ["PY3", "int", "long", "xrange", "exec_", "callable", + "namedtuple", "property", "defaultdict"] + +import sys + + +# --- python 2/3 compatibility layer + +PY3 = sys.version_info >= (3,) + +try: + import __builtin__ +except ImportError: + import builtins as __builtin__ # py3 + +if PY3: + int = int + long = int + xrange = range + exec_ = getattr(__builtin__, "exec") + print_ = getattr(__builtin__, "print") +else: + int = int + long = long + xrange = xrange + + def exec_(code, globs=None, locs=None): + if globs is None: + frame = _sys._getframe(1) + globs = frame.f_globals + if locs is None: + locs = frame.f_locals + del frame + elif locs is None: + locs = globs + exec("""exec code in globs, locs""") + + def print_(s): + sys.stdout.write(s + '\n') + sys.stdout.flush() + + +# removed in 3.0, reintroduced in 3.2 +try: + callable = callable +except Exception: + def callable(obj): + for klass in type(obj).__mro__: + if "__call__" in klass.__dict__: + return True + return False + + +# --- stdlib additions + +try: + from collections import namedtuple +except ImportError: + from operator import itemgetter as _itemgetter + from keyword import iskeyword as _iskeyword + import sys as _sys + + def namedtuple(typename, field_names, verbose=False, rename=False): + """A collections.namedtuple implementation written in Python + to support Python versions < 2.6. + + Taken from: http://code.activestate.com/recipes/500261/ + """ + # Parse and validate the field names. Validation serves two + # purposes, generating informative error messages and preventing + # template injection attacks. + if isinstance(field_names, basestring): + # names separated by whitespace and/or commas + field_names = field_names.replace(',', ' ').split() + field_names = tuple(map(str, field_names)) + if rename: + names = list(field_names) + seen = set() + for i, name in enumerate(names): + if (not min(c.isalnum() or c=='_' for c in name) or _iskeyword(name) + or not name or name[0].isdigit() or name.startswith('_') + or name in seen): + names[i] = '_%d' % i + seen.add(name) + field_names = tuple(names) + for name in (typename,) + field_names: + if not min(c.isalnum() or c=='_' for c in name): + raise ValueError('Type names and field names can only contain ' \ + 'alphanumeric characters and underscores: %r' + % name) + if _iskeyword(name): + raise ValueError('Type names and field names cannot be a keyword: %r' \ + % name) + if name[0].isdigit(): + raise ValueError('Type names and field names cannot start with a ' \ + 'number: %r' % name) + seen_names = set() + for name in field_names: + if name.startswith('_') and not rename: + raise ValueError('Field names cannot start with an underscore: %r' + % name) + if name in seen_names: + raise ValueError('Encountered duplicate field name: %r' % name) + seen_names.add(name) + + # Create and fill-in the class template + numfields = len(field_names) + # tuple repr without parens or quotes + argtxt = repr(field_names).replace("'", "")[1:-1] + reprtxt = ', '.join('%s=%%r' % name for name in field_names) + template = '''class %(typename)s(tuple): + '%(typename)s(%(argtxt)s)' \n + __slots__ = () \n + _fields = %(field_names)r \n + def __new__(_cls, %(argtxt)s): + return _tuple.__new__(_cls, (%(argtxt)s)) \n + @classmethod + def _make(cls, iterable, new=tuple.__new__, len=len): + 'Make a new %(typename)s object from a sequence or iterable' + result = new(cls, iterable) + if len(result) != %(numfields)d: + raise TypeError('Expected %(numfields)d arguments, got %%d' %% len(result)) + return result \n + def __repr__(self): + return '%(typename)s(%(reprtxt)s)' %% self \n + def _asdict(self): + 'Return a new dict which maps field names to their values' + return dict(zip(self._fields, self)) \n + def _replace(_self, **kwds): + 'Return a new %(typename)s object replacing specified fields with new values' + result = _self._make(map(kwds.pop, %(field_names)r, _self)) + if kwds: + raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) + return result \n + def __getnewargs__(self): + return tuple(self) \n\n''' % locals() + for i, name in enumerate(field_names): + template += ' %s = _property(_itemgetter(%d))\n' % (name, i) + if verbose: + sys.stdout.write(template + '\n') + sys.stdout.flush() + + # Execute the template string in a temporary namespace + namespace = dict(_itemgetter=_itemgetter, __name__='namedtuple_%s' % typename, + _property=property, _tuple=tuple) + try: + exec_(template, namespace) + except SyntaxError: + e = sys.exc_info()[1] + raise SyntaxError(e.message + ':\n' + template) + result = namespace[typename] + + # For pickling to work, the __module__ variable needs to be set + # to the frame where the named tuple is created. Bypass this + # step in enviroments where sys._getframe is not defined (Jython + # for example) or sys._getframe is not defined for arguments + # greater than 0 (IronPython). + try: + result.__module__ = _sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + + return result + + +# hack to support property.setter/deleter on python < 2.6 +# http://docs.python.org/library/functions.html?highlight=property#property +if hasattr(property, 'setter'): + property = property +else: + class property(__builtin__.property): + __metaclass__ = type + + def __init__(self, fget, *args, **kwargs): + super(property, self).__init__(fget, *args, **kwargs) + self.__doc__ = fget.__doc__ + + def getter(self, method): + return property(method, self.fset, self.fdel) + + def setter(self, method): + return property(self.fget, method, self.fdel) + + def deleter(self, method): + return property(self.fget, self.fset, method) + + +# py 2.5 collections.defauldict +# Taken from: +# http://code.activestate.com/recipes/523034-emulate-collectionsdefaultdict/ +# credits: Jason Kirtland +try: + from collections import defaultdict +except ImportError: + class defaultdict(dict): + + def __init__(self, default_factory=None, *a, **kw): + if (default_factory is not None and + not hasattr(default_factory, '__call__')): + raise TypeError('first argument must be callable') + dict.__init__(self, *a, **kw) + self.default_factory = default_factory + + def __getitem__(self, key): + try: + return dict.__getitem__(self, key) + except KeyError: + return self.__missing__(key) + + def __missing__(self, key): + if self.default_factory is None: + raise KeyError(key) + self[key] = value = self.default_factory() + return value + + def __reduce__(self): + if self.default_factory is None: + args = tuple() + else: + args = self.default_factory, + return type(self), args, None, None, self.items() + + def copy(self): + return self.__copy__() + + def __copy__(self): + return type(self)(self.default_factory, self) + + def __deepcopy__(self, memo): + import copy + return type(self)(self.default_factory, + copy.deepcopy(self.items())) + + def __repr__(self): + return 'defaultdict(%s, %s)' % (self.default_factory, + dict.__repr__(self)) + + +# py 2.5 functools.wraps +try: + from functools import wraps +except ImportError: + def wraps(original): + def inner(fn): + # see functools.WRAPPER_ASSIGNMENTS + for attribute in ['__module__', + '__name__', + '__doc__' + ]: + setattr(fn, attribute, getattr(original, attribute)) + # see functools.WRAPPER_UPDATES + for attribute in ['__dict__', + ]: + if hasattr(fn, attribute): + getattr(fn, attribute).update(getattr(original, attribute)) + else: + setattr(fn, attribute, + getattr(original, attribute).copy()) + return fn + return inner diff --git a/vendor/psutil/_psbsd.py b/vendor/psutil/_psbsd.py new file mode 100644 index 000000000..282765a27 --- /dev/null +++ b/vendor/psutil/_psbsd.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python +# +# $Id: _psbsd.py 1498 2012-07-24 21:41:28Z g.rodola $ +# +# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""FreeBSD platform implementation.""" + +import errno +import os +import sys + +import _psutil_bsd +import _psutil_posix +from psutil import _psposix +from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._compat import namedtuple +from psutil._common import * + +__extra__all__ = [] + +# --- constants + +NUM_CPUS = _psutil_bsd.get_num_cpus() +BOOT_TIME = _psutil_bsd.get_system_boot_time() +TOTAL_PHYMEM = _psutil_bsd.get_virtual_mem()[0] +_TERMINAL_MAP = _psposix._get_terminal_map() +_PAGESIZE = os.sysconf("SC_PAGE_SIZE") +_cputimes_ntuple = namedtuple('cputimes', 'user nice system idle irq') + +# --- public functions + +nt_virtmem_info = namedtuple('vmem', ' '.join([ + # all platforms + 'total', 'available', 'percent', 'used', 'free', + # FreeBSD specific + 'active', + 'inactive', + 'buffers', + 'cached', + 'shared', + 'wired'])) + +def virtual_memory(): + """System virtual memory as a namedutple.""" + mem = _psutil_bsd.get_virtual_mem() + total, free, active, inactive, wired, cached, buffers, shared = mem + avail = inactive + cached + free + used = active + wired + cached + percent = usage_percent((total - avail), total, _round=1) + return nt_virtmem_info(total, avail, percent, used, free, + active, inactive, buffers, cached, shared, wired) + +def swap_memory(): + """System swap memory as (total, used, free, sin, sout) namedtuple.""" + total, used, free, sin, sout = \ + [x * _PAGESIZE for x in _psutil_bsd.get_swap_mem()] + percent = usage_percent(used, total, _round=1) + return nt_swapmeminfo(total, used, free, percent, sin, sout) + +def get_system_cpu_times(): + """Return system per-CPU times as a named tuple""" + user, nice, system, idle, irq = _psutil_bsd.get_system_cpu_times() + return _cputimes_ntuple(user, nice, system, idle, irq) + +def get_system_per_cpu_times(): + """Return system CPU times as a named tuple""" + ret = [] + for cpu_t in _psutil_bsd.get_system_per_cpu_times(): + user, nice, system, idle, irq = cpu_t + item = _cputimes_ntuple(user, nice, system, idle, irq) + ret.append(item) + return ret + +# XXX +# Ok, this is very dirty. +# On FreeBSD < 8 we cannot gather per-cpu information, see: +# http://code.google.com/p/psutil/issues/detail?id=226 +# If NUM_CPUS > 1, on first call we return single cpu times to avoid a +# crash at psutil import time. +# Next calls will fail with NotImplementedError +if not hasattr(_psutil_bsd, "get_system_per_cpu_times"): + def get_system_per_cpu_times(): + if NUM_CPUS == 1: + return [get_system_cpu_times] + if get_system_per_cpu_times.__called__: + raise NotImplementedError("supported only starting from FreeBSD 8") + get_system_per_cpu_times.__called__ = True + return [get_system_cpu_times] +get_system_per_cpu_times.__called__ = False + +def disk_partitions(all=False): + retlist = [] + partitions = _psutil_bsd.get_disk_partitions() + for partition in partitions: + device, mountpoint, fstype, opts = partition + if device == 'none': + device = '' + if not all: + if not os.path.isabs(device) \ + or not os.path.exists(device): + continue + ntuple = nt_partition(device, mountpoint, fstype, opts) + retlist.append(ntuple) + return retlist + +def get_system_users(): + retlist = [] + rawlist = _psutil_bsd.get_system_users() + for item in rawlist: + user, tty, hostname, tstamp = item + if tty == '~': + continue # reboot or shutdown + nt = nt_user(user, tty or None, hostname, tstamp) + retlist.append(nt) + return retlist + +get_pid_list = _psutil_bsd.get_pid_list +pid_exists = _psposix.pid_exists +get_disk_usage = _psposix.get_disk_usage +network_io_counters = _psutil_bsd.get_network_io_counters +disk_io_counters = _psutil_bsd.get_disk_io_counters + + +def wrap_exceptions(method): + """Call method(self, pid) into a try/except clause so that if an + OSError "No such process" exception is raised we assume the process + has died and raise psutil.NoSuchProcess instead. + """ + def wrapper(self, *args, **kwargs): + try: + return method(self, *args, **kwargs) + except OSError: + err = sys.exc_info()[1] + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) + raise + return wrapper + +_status_map = { + _psutil_bsd.SSTOP : STATUS_STOPPED, + _psutil_bsd.SSLEEP : STATUS_SLEEPING, + _psutil_bsd.SRUN : STATUS_RUNNING, + _psutil_bsd.SIDL : STATUS_IDLE, + _psutil_bsd.SWAIT : STATUS_WAITING, + _psutil_bsd.SLOCK : STATUS_LOCKED, + _psutil_bsd.SZOMB : STATUS_ZOMBIE, +} + + +class Process(object): + """Wrapper class around underlying C implementation.""" + + __slots__ = ["pid", "_process_name"] + + def __init__(self, pid): + self.pid = pid + self._process_name = None + + @wrap_exceptions + def get_process_name(self): + """Return process name as a string of limited len (15).""" + return _psutil_bsd.get_process_name(self.pid) + + @wrap_exceptions + def get_process_exe(self): + """Return process executable pathname.""" + return _psutil_bsd.get_process_exe(self.pid) + + @wrap_exceptions + def get_process_cmdline(self): + """Return process cmdline as a list of arguments.""" + return _psutil_bsd.get_process_cmdline(self.pid) + + @wrap_exceptions + def get_process_terminal(self): + tty_nr = _psutil_bsd.get_process_tty_nr(self.pid) + try: + return _TERMINAL_MAP[tty_nr] + except KeyError: + return None + + @wrap_exceptions + def get_process_ppid(self): + """Return process parent pid.""" + return _psutil_bsd.get_process_ppid(self.pid) + + # XXX - available on FreeBSD >= 8 only + if hasattr(_psutil_bsd, "get_process_cwd"): + @wrap_exceptions + def get_process_cwd(self): + """Return process current working directory.""" + # sometimes we get an empty string, in which case we turn + # it into None + return _psutil_bsd.get_process_cwd(self.pid) or None + + @wrap_exceptions + def get_process_uids(self): + """Return real, effective and saved user ids.""" + real, effective, saved = _psutil_bsd.get_process_uids(self.pid) + return nt_uids(real, effective, saved) + + @wrap_exceptions + def get_process_gids(self): + """Return real, effective and saved group ids.""" + real, effective, saved = _psutil_bsd.get_process_gids(self.pid) + return nt_gids(real, effective, saved) + + @wrap_exceptions + def get_cpu_times(self): + """return a tuple containing process user/kernel time.""" + user, system = _psutil_bsd.get_process_cpu_times(self.pid) + return nt_cputimes(user, system) + + @wrap_exceptions + def get_memory_info(self): + """Return a tuple with the process' RSS and VMS size.""" + rss, vms = _psutil_bsd.get_process_memory_info(self.pid)[:2] + return nt_meminfo(rss, vms) + + _nt_ext_mem = namedtuple('meminfo', 'rss vms text data stack') + + @wrap_exceptions + def get_ext_memory_info(self): + return self._nt_ext_mem(*_psutil_bsd.get_process_memory_info(self.pid)) + + @wrap_exceptions + def get_process_create_time(self): + """Return the start time of the process as a number of seconds since + the epoch.""" + return _psutil_bsd.get_process_create_time(self.pid) + + @wrap_exceptions + def get_process_num_threads(self): + """Return the number of threads belonging to the process.""" + return _psutil_bsd.get_process_num_threads(self.pid) + + @wrap_exceptions + def get_num_ctx_switches(self): + return nt_ctxsw(*_psutil_bsd.get_process_num_ctx_switches(self.pid)) + + @wrap_exceptions + def get_num_fds(self): + """Return the number of file descriptors opened by this process.""" + return _psutil_bsd.get_process_num_fds(self.pid) + + @wrap_exceptions + def get_process_threads(self): + """Return the number of threads belonging to the process.""" + rawlist = _psutil_bsd.get_process_threads(self.pid) + retlist = [] + for thread_id, utime, stime in rawlist: + ntuple = nt_thread(thread_id, utime, stime) + retlist.append(ntuple) + return retlist + + @wrap_exceptions + def get_open_files(self): + """Return files opened by process as a list of namedtuples.""" + # XXX - C implementation available on FreeBSD >= 8 only + # else fallback on lsof parser + if hasattr(_psutil_bsd, "get_process_open_files"): + rawlist = _psutil_bsd.get_process_open_files(self.pid) + return [nt_openfile(path, fd) for path, fd in rawlist] + else: + lsof = _psposix.LsofParser(self.pid, self._process_name) + return lsof.get_process_open_files() + + @wrap_exceptions + def get_connections(self, kind='inet'): + """Return etwork connections opened by a process as a list of + namedtuples. + """ + if kind not in conn_tmap: + raise ValueError("invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in conn_tmap]))) + families, types = conn_tmap[kind] + ret = _psutil_bsd.get_process_connections(self.pid, families, types) + return [nt_connection(*conn) for conn in ret] + + @wrap_exceptions + def process_wait(self, timeout=None): + try: + return _psposix.wait_pid(self.pid, timeout) + except TimeoutExpired: + raise TimeoutExpired(self.pid, self._process_name) + + @wrap_exceptions + def get_process_nice(self): + return _psutil_posix.getpriority(self.pid) + + @wrap_exceptions + def set_process_nice(self, value): + return _psutil_posix.setpriority(self.pid, value) + + @wrap_exceptions + def get_process_status(self): + code = _psutil_bsd.get_process_status(self.pid) + if code in _status_map: + return _status_map[code] + return constant(-1, "?") + + @wrap_exceptions + def get_process_io_counters(self): + rc, wc, rb, wb = _psutil_bsd.get_process_io_counters(self.pid) + return nt_io(rc, wc, rb, wb) + + nt_mmap_grouped = namedtuple('mmap', + 'path rss, private, ref_count, shadow_count') + nt_mmap_ext = namedtuple('mmap', + 'addr, perms path rss, private, ref_count, shadow_count') + + @wrap_exceptions + def get_memory_maps(self): + return _psutil_bsd.get_process_memory_maps(self.pid) + + # FreeBSD < 8 does not support kinfo_getfile() and kinfo_getvmmap() + if not hasattr(_psutil_bsd, 'get_process_open_files'): + def _not_implemented(self): + raise NotImplementedError("supported only starting from FreeBSD 8") + get_open_files = _not_implemented + get_process_cwd = _not_implemented + get_memory_maps = _not_implemented + get_num_fds = _not_implemented diff --git a/vendor/psutil/_pslinux.py b/vendor/psutil/_pslinux.py new file mode 100755 index 000000000..70fb8d38e --- /dev/null +++ b/vendor/psutil/_pslinux.py @@ -0,0 +1,1011 @@ +#!/usr/bin/env python +# +# $Id: _pslinux.py 1513 2012-08-14 11:01:37Z g.rodola $ +# +# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Linux platform implementation.""" + +from __future__ import division + +import os +import errno +import socket +import struct +import sys +import base64 +import re + +import _psutil_posix +import _psutil_linux +from psutil import _psposix +from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._common import * +from psutil._compat import PY3, xrange, long, namedtuple + +__extra__all__ = [ + "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE", + "IOPRIO_CLASS_IDLE", + "phymem_buffers", "cached_phymem"] + + +def _get_boot_time(): + """Return system boot time (epoch in seconds)""" + f = open('/proc/stat', 'r') + try: + for line in f: + if line.startswith('btime'): + return float(line.strip().split()[1]) + raise RuntimeError("line not found") + finally: + f.close() + +def _get_num_cpus(): + """Return the number of CPUs on the system""" + # we try to determine num CPUs by using different approaches. + # SC_NPROCESSORS_ONLN seems to be the safer and it is also + # used by multiprocessing module + try: + return os.sysconf("SC_NPROCESSORS_ONLN") + except ValueError: + # as a second fallback we try to parse /proc/cpuinfo + num = 0 + f = open('/proc/cpuinfo', 'r') + try: + lines = f.readlines() + finally: + f.close() + for line in lines: + if line.lower().startswith('processor'): + num += 1 + + # unknown format (e.g. amrel/sparc architectures), see: + # http://code.google.com/p/psutil/issues/detail?id=200 + # try to parse /proc/stat as a last resort + if num == 0: + f = open('/proc/stat', 'r') + try: + lines = f.readlines() + finally: + f.close() + search = re.compile('cpu\d') + for line in lines: + line = line.split(' ')[0] + if search.match(line): + num += 1 + + if num == 0: + raise RuntimeError("can't determine number of CPUs") + return num + + +# Number of clock ticks per second +_CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"]) +_PAGESIZE = os.sysconf("SC_PAGE_SIZE") +_TERMINAL_MAP = _psposix._get_terminal_map() +BOOT_TIME = _get_boot_time() +NUM_CPUS = _get_num_cpus() +TOTAL_PHYMEM = _psutil_linux.get_sysinfo()[0] +# ioprio_* constants http://linux.die.net/man/2/ioprio_get +IOPRIO_CLASS_NONE = 0 +IOPRIO_CLASS_RT = 1 +IOPRIO_CLASS_BE = 2 +IOPRIO_CLASS_IDLE = 3 + +# http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h +_TCP_STATES_TABLE = {"01" : "ESTABLISHED", + "02" : "SYN_SENT", + "03" : "SYN_RECV", + "04" : "FIN_WAIT1", + "05" : "FIN_WAIT2", + "06" : "TIME_WAIT", + "07" : "CLOSE", + "08" : "CLOSE_WAIT", + "09" : "LAST_ACK", + "0A" : "LISTEN", + "0B" : "CLOSING" + } + +# --- system memory functions + +nt_virtmem_info = namedtuple('vmem', ' '.join([ + # all platforms + 'total', 'available', 'percent', 'used', 'free', + # linux specific + 'active', + 'inactive', + 'buffers', + 'cached'])) + +def virtual_memory(): + total, free, buffers, shared, _, _ = _psutil_linux.get_sysinfo() + cached = active = inactive = None + f = open('/proc/meminfo', 'r') + try: + for line in f: + if line.startswith('Cached:'): + cached = int(line.split()[1]) * 1024 + elif line.startswith('Active:'): + active = int(line.split()[1]) * 1024 + elif line.startswith('Inactive:'): + inactive = int(line.split()[1]) * 1024 + if cached is not None \ + and active is not None \ + and inactive is not None: + break + else: + raise RuntimeError("line(s) not found") + finally: + f.close() + avail = free + buffers + cached + used = total - free + percent = usage_percent((total - avail), total, _round=1) + return nt_virtmem_info(total, avail, percent, used, free, + active, inactive, buffers, cached) + +def swap_memory(): + _, _, _, _, total, free = _psutil_linux.get_sysinfo() + used = total - free + percent = usage_percent(used, total, _round=1) + # get pgin/pgouts + f = open("/proc/vmstat", "r") + sin = sout = None + try: + for line in f: + # values are expressed in 4 kilo bytes, we want bytes instead + if line.startswith('pswpin'): + sin = int(line.split(' ')[1]) * 4 * 1024 + elif line.startswith('pswpout'): + sout = int(line.split(' ')[1]) * 4 * 1024 + if sin is not None and sout is not None: + break + else: + raise RuntimeError("line(s) not found") + finally: + f.close() + return nt_swapmeminfo(total, used, free, percent, sin, sout) + +# --- XXX deprecated memory functions + +@deprecated('psutil.virtual_memory().cached') +def cached_phymem(): + return virtual_memory().cached + +@deprecated('psutil.virtual_memory().buffers') +def phymem_buffers(): + return virtual_memory().buffers + + +# --- system CPU functions + +def get_system_cpu_times(): + """Return a named tuple representing the following CPU times: + user, nice, system, idle, iowait, irq, softirq. + """ + f = open('/proc/stat', 'r') + try: + values = f.readline().split() + finally: + f.close() + + values = values[1:8] + values = tuple([float(x) / _CLOCK_TICKS for x in values]) + return nt_sys_cputimes(*values[:7]) + +def get_system_per_cpu_times(): + """Return a list of namedtuple representing the CPU times + for every CPU available on the system. + """ + cpus = [] + f = open('/proc/stat', 'r') + # get rid of the first line who refers to system wide CPU stats + try: + f.readline() + for line in f.readlines(): + if line.startswith('cpu'): + values = line.split()[1:8] + values = tuple([float(x) / _CLOCK_TICKS for x in values]) + entry = nt_sys_cputimes(*values[:7]) + cpus.append(entry) + return cpus + finally: + f.close() + + +# --- system disk functions + +def disk_partitions(all=False): + """Return mounted disk partitions as a list of nameduples""" + phydevs = [] + f = open("/proc/filesystems", "r") + try: + for line in f: + if not line.startswith("nodev"): + phydevs.append(line.strip()) + finally: + f.close() + + retlist = [] + partitions = _psutil_linux.get_disk_partitions() + for partition in partitions: + device, mountpoint, fstype, opts = partition + if device == 'none': + device = '' + if not all: + if device == '' or fstype not in phydevs: + continue + ntuple = nt_partition(device, mountpoint, fstype, opts) + retlist.append(ntuple) + return retlist + +get_disk_usage = _psposix.get_disk_usage + + +# --- other sysetm functions + +def get_system_users(): + """Return currently connected users as a list of namedtuples.""" + retlist = [] + rawlist = _psutil_linux.get_system_users() + for item in rawlist: + user, tty, hostname, tstamp, user_process = item + # XXX the underlying C function includes entries about + # system boot, run level and others. We might want + # to use them in the future. + if not user_process: + continue + if hostname == ':0.0': + hostname = 'localhost' + nt = nt_user(user, tty or None, hostname, tstamp) + retlist.append(nt) + return retlist + +# --- process functions + +def get_pid_list(): + """Returns a list of PIDs currently running on the system.""" + pids = [int(x) for x in os.listdir('/proc') if x.isdigit()] + return pids + +def pid_exists(pid): + """Check For the existence of a unix pid.""" + return _psposix.pid_exists(pid) + +def network_io_counters(): + """Return network I/O statistics for every network interface + installed on the system as a dict of raw tuples. + """ + f = open("/proc/net/dev", "r") + try: + lines = f.readlines() + finally: + f.close() + + retdict = {} + for line in lines[2:]: + colon = line.find(':') + assert colon > 0, line + name = line[:colon].strip() + fields = line[colon+1:].strip().split() + bytes_recv = int(fields[0]) + packets_recv = int(fields[1]) + errin = int(fields[2]) + dropin = int(fields[2]) + bytes_sent = int(fields[8]) + packets_sent = int(fields[9]) + errout = int(fields[10]) + dropout = int(fields[11]) + retdict[name] = (bytes_sent, bytes_recv, packets_sent, packets_recv, + errin, errout, dropin, dropout) + return retdict + +def disk_io_counters(): + """Return disk I/O statistics for every disk installed on the + system as a dict of raw tuples. + """ + # man iostat states that sectors are equivalent with blocks and + # have a size of 512 bytes since 2.4 kernels. This value is + # needed to calculate the amount of disk I/O in bytes. + SECTOR_SIZE = 512 + + # determine partitions we want to look for + partitions = [] + f = open("/proc/partitions", "r") + try: + lines = f.readlines()[2:] + finally: + f.close() + for line in lines: + _, _, _, name = line.split() + if name[-1].isdigit(): + partitions.append(name) + # + retdict = {} + f = open("/proc/diskstats", "r") + try: + lines = f.readlines() + finally: + f.close() + for line in lines: + _, _, name, reads, _, rbytes, rtime, writes, _, wbytes, wtime = \ + line.split()[:11] + if name in partitions: + rbytes = int(rbytes) * SECTOR_SIZE + wbytes = int(wbytes) * SECTOR_SIZE + reads = int(reads) + writes = int(writes) + # TODO: times are expressed in milliseconds while OSX/BSD has + # these expressed in nanoseconds; figure this out. + rtime = int(rtime) + wtime = int(wtime) + retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime) + return retdict + + +# taken from /fs/proc/array.c +_status_map = {"R" : STATUS_RUNNING, + "S" : STATUS_SLEEPING, + "D" : STATUS_DISK_SLEEP, + "T" : STATUS_STOPPED, + "t" : STATUS_TRACING_STOP, + "Z" : STATUS_ZOMBIE, + "X" : STATUS_DEAD, + "x" : STATUS_DEAD, + "K" : STATUS_WAKE_KILL, + "W" : STATUS_WAKING} + +# --- decorators + +def wrap_exceptions(callable): + """Call callable into a try/except clause and translate ENOENT, + EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. + """ + def wrapper(self, *args, **kwargs): + try: + return callable(self, *args, **kwargs) + except EnvironmentError: + # ENOENT (no such file or directory) gets raised on open(). + # ESRCH (no such process) can get raised on read() if + # process is gone in meantime. + err = sys.exc_info()[1] + if err.errno in (errno.ENOENT, errno.ESRCH): + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) + raise + return wrapper + + +class Process(object): + """Linux process implementation.""" + + __slots__ = ["pid", "_process_name"] + + def __init__(self, pid): + if not isinstance(pid, int): + raise TypeError('pid must be an integer') + self.pid = pid + self._process_name = None + + @wrap_exceptions + def get_process_name(self): + f = open("/proc/%s/stat" % self.pid) + try: + name = f.read().split(' ')[1].replace('(', '').replace(')', '') + finally: + f.close() + # XXX - gets changed later and probably needs refactoring + return name + + def get_process_exe(self): + try: + exe = os.readlink("/proc/%s/exe" % self.pid) + except (OSError, IOError): + err = sys.exc_info()[1] + if err.errno == errno.ENOENT: + # no such file error; might be raised also if the + # path actually exists for system processes with + # low pids (about 0-20) + if os.path.lexists("/proc/%s/exe" % self.pid): + return "" + else: + # ok, it is a process which has gone away + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) + raise + + # readlink() might return paths containing null bytes causing + # problems when used with other fs-related functions (os.*, + # open(), ...) + exe = exe.replace('\x00', '') + # Certain names have ' (deleted)' appended. Usually this is + # bogus as the file actually exists. Either way that's not + # important as we don't want to discriminate executables which + # have been deleted. + if exe.endswith(" (deleted)") and not os.path.exists(exe): + exe = exe[:-10] + return exe + + @wrap_exceptions + def get_process_cmdline(self): + f = open("/proc/%s/cmdline" % self.pid) + try: + # return the args as a list + return [x for x in f.read().split('\x00') if x] + finally: + f.close() + + @wrap_exceptions + def get_process_terminal(self): + f = open("/proc/%s/stat" % self.pid) + try: + tty_nr = int(f.read().split(' ')[6]) + finally: + f.close() + try: + return _TERMINAL_MAP[tty_nr] + except KeyError: + return None + + @wrap_exceptions + def get_process_io_counters(self): + f = open("/proc/%s/io" % self.pid) + try: + for line in f: + if line.startswith("rchar"): + read_count = int(line.split()[1]) + elif line.startswith("wchar"): + write_count = int(line.split()[1]) + elif line.startswith("read_bytes"): + read_bytes = int(line.split()[1]) + elif line.startswith("write_bytes"): + write_bytes = int(line.split()[1]) + return nt_io(read_count, write_count, read_bytes, write_bytes) + finally: + f.close() + + if not os.path.exists('/proc/%s/io' % os.getpid()): + def get_process_io_counters(self): + raise NotImplementedError('/proc/PID/io is not available') + + @wrap_exceptions + def get_cpu_times(self): + f = open("/proc/%s/stat" % self.pid) + try: + st = f.read().strip() + finally: + f.close() + # ignore the first two values ("pid (exe)") + st = st[st.find(')') + 2:] + values = st.split(' ') + utime = float(values[11]) / _CLOCK_TICKS + stime = float(values[12]) / _CLOCK_TICKS + return nt_cputimes(utime, stime) + + @wrap_exceptions + def process_wait(self, timeout=None): + try: + return _psposix.wait_pid(self.pid, timeout) + except TimeoutExpired: + raise TimeoutExpired(self.pid, self._process_name) + + @wrap_exceptions + def get_process_create_time(self): + f = open("/proc/%s/stat" % self.pid) + try: + st = f.read().strip() + finally: + f.close() + # ignore the first two values ("pid (exe)") + st = st[st.rfind(')') + 2:] + values = st.split(' ') + # According to documentation, starttime is in field 21 and the + # unit is jiffies (clock ticks). + # We first divide it for clock ticks and then add uptime returning + # seconds since the epoch, in UTC. + starttime = (float(values[19]) / _CLOCK_TICKS) + BOOT_TIME + return starttime + + @wrap_exceptions + def get_memory_info(self): + f = open("/proc/%s/statm" % self.pid) + try: + vms, rss = f.readline().split()[:2] + return nt_meminfo(int(rss) * _PAGESIZE, + int(vms) * _PAGESIZE) + finally: + f.close() + + _nt_ext_mem = namedtuple('meminfo', 'rss vms shared text lib data dirty') + + @wrap_exceptions + def get_ext_memory_info(self): + # ============================================================ + # | FIELD | DESCRIPTION | AKA | TOP | + # ============================================================ + # | rss | resident set size | | RES | + # | vms | total program size | size | VIRT | + # | shared | shared pages (from shared mappings) | | SHR | + # | text | text ('code') | trs | CODE | + # | lib | library (unused in Linux 2.6) | lrs | | + # | data | data + stack | drs | DATA | + # | dirty | dirty pages (unused in Linux 2.6) | dt | | + # ============================================================ + f = open("/proc/%s/statm" % self.pid) + try: + vms, rss, shared, text, lib, data, dirty = \ + [int(x) * _PAGESIZE for x in f.readline().split()[:7]] + finally: + f.close() + return self._nt_ext_mem(rss, vms, shared, text, lib, data, dirty) + + _mmap_base_fields = ['path', 'rss', 'size', 'pss', 'shared_clean', + 'shared_dirty', 'private_clean', 'private_dirty', + 'referenced', 'anonymous', 'swap',] + nt_mmap_grouped = namedtuple('mmap', ' '.join(_mmap_base_fields)) + nt_mmap_ext = namedtuple('mmap', 'addr perms ' + ' '.join(_mmap_base_fields)) + + def get_memory_maps(self): + """Return process's mapped memory regions as a list of nameduples. + Fields are explained in 'man proc'; here is an updated (Apr 2012) + version: http://goo.gl/fmebo + """ + f = None + try: + f = open("/proc/%s/smaps" % self.pid) + first_line = f.readline() + current_block = [first_line] + + def get_blocks(): + data = {} + for line in f: + fields = line.split(None, 5) + if len(fields) >= 5: + yield (current_block.pop(), data) + current_block.append(line) + else: + data[fields[0]] = int(fields[1]) * 1024 + yield (current_block.pop(), data) + + if first_line: # smaps file can be empty + for header, data in get_blocks(): + hfields = header.split(None, 5) + try: + addr, perms, offset, dev, inode, path = hfields + except ValueError: + addr, perms, offset, dev, inode, path = hfields + [''] + if not path: + path = '[anon]' + else: + path = path.strip() + yield (addr, perms, path, + data['Rss:'], + data['Size:'], + data.get('Pss:', 0), + data['Shared_Clean:'], data['Shared_Clean:'], + data['Private_Clean:'], data['Private_Dirty:'], + data['Referenced:'], + data['Anonymous:'], + data['Swap:']) + f.close() + except EnvironmentError: + # XXX - Can't use wrap_exceptions decorator as we're + # returning a generator; this probably needs some + # refactoring in order to avoid this code duplication. + if f is not None: + f.close() + err = sys.exc_info()[1] + if err.errno in (errno.ENOENT, errno.ESRCH): + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) + raise + except: + if f is not None: + f.close() + raise + + if not os.path.exists('/proc/%s/smaps' % os.getpid()): + def get_shared_libs(self, ext): + msg = "this Linux version does not support /proc/PID/smaps " \ + "(kernel < 2.6.14 or CONFIG_MMU kernel configuration " \ + "option is not enabled)" + raise NotImplementedError(msg) + + @wrap_exceptions + def get_process_cwd(self): + # readlink() might return paths containing null bytes causing + # problems when used with other fs-related functions (os.*, + # open(), ...) + path = os.readlink("/proc/%s/cwd" % self.pid) + return path.replace('\x00', '') + + @wrap_exceptions + def get_num_ctx_switches(self): + vol = unvol = None + f = open("/proc/%s/status" % self.pid) + try: + for line in f: + if line.startswith("voluntary_ctxt_switches"): + vol = int(line.split()[1]) + elif line.startswith("nonvoluntary_ctxt_switches"): + unvol = int(line.split()[1]) + if vol is not None and unvol is not None: + return nt_ctxsw(vol, unvol) + raise RuntimeError("line not found") + finally: + f.close() + + @wrap_exceptions + def get_process_num_threads(self): + f = open("/proc/%s/status" % self.pid) + try: + for line in f: + if line.startswith("Threads:"): + return int(line.split()[1]) + raise RuntimeError("line not found") + finally: + f.close() + + @wrap_exceptions + def get_process_threads(self): + thread_ids = os.listdir("/proc/%s/task" % self.pid) + thread_ids.sort() + retlist = [] + hit_enoent = False + for thread_id in thread_ids: + try: + f = open("/proc/%s/task/%s/stat" % (self.pid, thread_id)) + except EnvironmentError: + err = sys.exc_info()[1] + if err.errno == errno.ENOENT: + # no such file or directory; it means thread + # disappeared on us + hit_enoent = True + continue + raise + try: + st = f.read().strip() + finally: + f.close() + # ignore the first two values ("pid (exe)") + st = st[st.find(')') + 2:] + values = st.split(' ') + utime = float(values[11]) / _CLOCK_TICKS + stime = float(values[12]) / _CLOCK_TICKS + ntuple = nt_thread(int(thread_id), utime, stime) + retlist.append(ntuple) + if hit_enoent: + # raise NSP if the process disappeared on us + os.stat('/proc/%s' % self.pid) + return retlist + + @wrap_exceptions + def get_process_nice(self): + #f = open('/proc/%s/stat' % self.pid, 'r') + #try: + # data = f.read() + # return int(data.split()[18]) + #finally: + # f.close() + + # Use C implementation + return _psutil_posix.getpriority(self.pid) + + @wrap_exceptions + def set_process_nice(self, value): + return _psutil_posix.setpriority(self.pid, value) + + @wrap_exceptions + def get_process_cpu_affinity(self): + from_bitmask = lambda x: [i for i in xrange(64) if (1 << i) & x] + bitmask = _psutil_linux.get_process_cpu_affinity(self.pid) + return from_bitmask(bitmask) + + @wrap_exceptions + def set_process_cpu_affinity(self, value): + def to_bitmask(l): + if not l: + raise ValueError("invalid argument %r" % l) + out = 0 + for b in l: + if not isinstance(b, (int, long)) or b < 0: + raise ValueError("invalid argument %r" % b) + out |= 2**b + return out + + bitmask = to_bitmask(value) + try: + _psutil_linux.set_process_cpu_affinity(self.pid, bitmask) + except OSError: + err = sys.exc_info()[1] + if err.errno == errno.EINVAL: + allcpus = list(range(len(get_system_per_cpu_times()))) + for cpu in value: + if cpu not in allcpus: + raise ValueError("invalid CPU %i" % cpu) + raise + + # only starting from kernel 2.6.13 + if hasattr(_psutil_linux, "ioprio_get"): + + @wrap_exceptions + def get_process_ionice(self): + ioclass, value = _psutil_linux.ioprio_get(self.pid) + return nt_ionice(ioclass, value) + + @wrap_exceptions + def set_process_ionice(self, ioclass, value): + if ioclass in (IOPRIO_CLASS_NONE, None): + if value: + raise ValueError("can't specify value with IOPRIO_CLASS_NONE") + ioclass = IOPRIO_CLASS_NONE + value = 0 + if ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE): + if value is None: + value = 4 + elif ioclass == IOPRIO_CLASS_IDLE: + if value: + raise ValueError("can't specify value with IOPRIO_CLASS_IDLE") + value = 0 + else: + value = 0 + if not 0 <= value <= 8: + raise ValueError("value argument range expected is between 0 and 8") + return _psutil_linux.ioprio_set(self.pid, ioclass, value) + + @wrap_exceptions + def get_process_status(self): + f = open("/proc/%s/status" % self.pid) + try: + for line in f: + if line.startswith("State:"): + letter = line.split()[1] + if letter in _status_map: + return _status_map[letter] + return constant(-1, '?') + finally: + f.close() + + @wrap_exceptions + def get_open_files(self): + retlist = [] + files = os.listdir("/proc/%s/fd" % self.pid) + hit_enoent = False + for fd in files: + file = "/proc/%s/fd/%s" % (self.pid, fd) + if os.path.islink(file): + try: + file = os.readlink(file) + except OSError: + # ENOENT == file which is gone in the meantime + err = sys.exc_info()[1] + if err.errno == errno.ENOENT: + hit_enoent = True + continue + raise + else: + # If file is not an absolute path there's no way + # to tell whether it's a regular file or not, + # so we skip it. A regular file is always supposed + # to be absolutized though. + if file.startswith('/') and isfile_strict(file): + ntuple = nt_openfile(file, int(fd)) + retlist.append(ntuple) + if hit_enoent: + # raise NSP if the process disappeared on us + os.stat('/proc/%s' % self.pid) + return retlist + + @wrap_exceptions + def get_connections(self, kind='inet'): + """Return connections opened by process as a list of namedtuples. + The kind parameter filters for connections that fit the following + criteria: + + Kind Value Number of connections using + inet IPv4 and IPv6 + inet4 IPv4 + inet6 IPv6 + tcp TCP + tcp4 TCP over IPv4 + tcp6 TCP over IPv6 + udp UDP + udp4 UDP over IPv4 + udp6 UDP over IPv6 + all the sum of all the possible families and protocols + """ + # Note: in case of UNIX sockets we're only able to determine the + # local bound path while the remote endpoint is not retrievable: + # http://goo.gl/R3GHM + inodes = {} + # os.listdir() is gonna raise a lot of access denied + # exceptions in case of unprivileged user; that's fine: + # lsof does the same so it's unlikely that we can to better. + for fd in os.listdir("/proc/%s/fd" % self.pid): + try: + inode = os.readlink("/proc/%s/fd/%s" % (self.pid, fd)) + except OSError: + continue + if inode.startswith('socket:['): + # the process is using a socket + inode = inode[8:][:-1] + inodes[inode] = fd + + if not inodes: + # no connections for this process + return [] + + def process(file, family, type_): + retlist = [] + try: + f = open(file, 'r') + except IOError: + # IPv6 not supported on this platform + err = sys.exc_info()[1] + if err.errno == errno.ENOENT and file.endswith('6'): + return [] + else: + raise + try: + f.readline() # skip the first line + for line in f: + # IPv4 / IPv6 + if family in (socket.AF_INET, socket.AF_INET6): + _, laddr, raddr, status, _, _, _, _, _, inode = \ + line.split()[:10] + if inode in inodes: + laddr = self._decode_address(laddr, family) + raddr = self._decode_address(raddr, family) + if type_ == socket.SOCK_STREAM: + status = _TCP_STATES_TABLE[status] + else: + status = "" + fd = int(inodes[inode]) + conn = nt_connection(fd, family, type_, laddr, + raddr, status) + retlist.append(conn) + elif family == socket.AF_UNIX: + tokens = line.split() + _, _, _, _, type_, _, inode = tokens[0:7] + if inode in inodes: + + if len(tokens) == 8: + path = tokens[-1] + else: + path = "" + fd = int(inodes[inode]) + type_ = int(type_) + conn = nt_connection(fd, family, type_, path, + None, "") + retlist.append(conn) + else: + raise ValueError(family) + return retlist + finally: + f.close() + + tcp4 = ("tcp" , socket.AF_INET , socket.SOCK_STREAM) + tcp6 = ("tcp6", socket.AF_INET6, socket.SOCK_STREAM) + udp4 = ("udp" , socket.AF_INET , socket.SOCK_DGRAM) + udp6 = ("udp6", socket.AF_INET6, socket.SOCK_DGRAM) + unix = ("unix", socket.AF_UNIX, None) + + tmap = { + "all" : (tcp4, tcp6, udp4, udp6, unix), + "tcp" : (tcp4, tcp6), + "tcp4" : (tcp4,), + "tcp6" : (tcp6,), + "udp" : (udp4, udp6), + "udp4" : (udp4,), + "udp6" : (udp6,), + "unix" : (unix,), + "inet" : (tcp4, tcp6, udp4, udp6), + "inet4": (tcp4, udp4), + "inet6": (tcp6, udp6), + } + if kind not in tmap: + raise ValueError("invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in tmap]))) + ret = [] + for f, family, type_ in tmap[kind]: + ret += process("/proc/net/%s" % f, family, type_) + # raise NSP if the process disappeared on us + os.stat('/proc/%s' % self.pid) + return ret + + +# --- lsof implementation +# +# def get_connections(self): +# lsof = _psposix.LsofParser(self.pid, self._process_name) +# return lsof.get_process_connections() + + @wrap_exceptions + def get_num_fds(self): + return len(os.listdir("/proc/%s/fd" % self.pid)) + + @wrap_exceptions + def get_process_ppid(self): + f = open("/proc/%s/status" % self.pid) + try: + for line in f: + if line.startswith("PPid:"): + # PPid: nnnn + return int(line.split()[1]) + raise RuntimeError("line not found") + finally: + f.close() + + @wrap_exceptions + def get_process_uids(self): + f = open("/proc/%s/status" % self.pid) + try: + for line in f: + if line.startswith('Uid:'): + _, real, effective, saved, fs = line.split() + return nt_uids(int(real), int(effective), int(saved)) + raise RuntimeError("line not found") + finally: + f.close() + + @wrap_exceptions + def get_process_gids(self): + f = open("/proc/%s/status" % self.pid) + try: + for line in f: + if line.startswith('Gid:'): + _, real, effective, saved, fs = line.split() + return nt_gids(int(real), int(effective), int(saved)) + raise RuntimeError("line not found") + finally: + f.close() + + @staticmethod + def _decode_address(addr, family): + """Accept an "ip:port" address as displayed in /proc/net/* + and convert it into a human readable form, like: + + "0500000A:0016" -> ("10.0.0.5", 22) + "0000000000000000FFFF00000100007F:9E49" -> ("::ffff:127.0.0.1", 40521) + + The IP address portion is a little or big endian four-byte + hexadecimal number; that is, the least significant byte is listed + first, so we need to reverse the order of the bytes to convert it + to an IP address. + The port is represented as a two-byte hexadecimal number. + + Reference: + http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html + """ + ip, port = addr.split(':') + port = int(port, 16) + if PY3: + ip = ip.encode('ascii') + # this usually refers to a local socket in listen mode with + # no end-points connected + if not port: + return () + if family == socket.AF_INET: + # see: http://code.google.com/p/psutil/issues/detail?id=201 + if sys.byteorder == 'little': + ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1]) + else: + ip = socket.inet_ntop(family, base64.b16decode(ip)) + else: # IPv6 + # old version - let's keep it, just in case... + #ip = ip.decode('hex') + #return socket.inet_ntop(socket.AF_INET6, + # ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4))) + ip = base64.b16decode(ip) + # see: http://code.google.com/p/psutil/issues/detail?id=201 + if sys.byteorder == 'little': + ip = socket.inet_ntop(socket.AF_INET6, + struct.pack('>4I', *struct.unpack('<4I', ip))) + else: + ip = socket.inet_ntop(socket.AF_INET6, + struct.pack('<4I', *struct.unpack('<4I', ip))) + return (ip, port) diff --git a/vendor/psutil/_psmswindows.py b/vendor/psutil/_psmswindows.py new file mode 100644 index 000000000..aa57f87ef --- /dev/null +++ b/vendor/psutil/_psmswindows.py @@ -0,0 +1,423 @@ +#!/usr/bin/env python +# +# $Id: _psmswindows.py 1514 2012-08-14 11:16:56Z g.rodola $ +# +# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Windows platform implementation.""" + +import errno +import os +import sys +import platform + +import _psutil_mswindows +from _psutil_mswindows import ERROR_ACCESS_DENIED +from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._common import * +from psutil._compat import PY3, xrange, long + +# Windows specific extended namespace +__extra__all__ = ["ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS", + "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", + "NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS"] + + +# --- module level constants (gets pushed up to psutil module) + +NUM_CPUS = _psutil_mswindows.get_num_cpus() +BOOT_TIME = _psutil_mswindows.get_system_uptime() +TOTAL_PHYMEM = _psutil_mswindows.get_virtual_mem()[0] +WAIT_TIMEOUT = 0x00000102 # 258 in decimal +ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES, ERROR_ACCESS_DENIED]) + +# process priority constants: +# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx +from _psutil_mswindows import (ABOVE_NORMAL_PRIORITY_CLASS, + BELOW_NORMAL_PRIORITY_CLASS, + HIGH_PRIORITY_CLASS, + IDLE_PRIORITY_CLASS, + NORMAL_PRIORITY_CLASS, + REALTIME_PRIORITY_CLASS, + INFINITE) + +@memoize +def _win32_QueryDosDevice(s): + return _psutil_mswindows.win32_QueryDosDevice(s) + +def _convert_raw_path(s): + # convert paths using native DOS format like: + # "\Device\HarddiskVolume1\Windows\systemew\file.txt" + # into: "C:\Windows\systemew\file.txt" + if PY3 and not isinstance(s, str): + s = s.decode('utf8') + rawdrive = '\\'.join(s.split('\\')[:3]) + driveletter = _win32_QueryDosDevice(rawdrive) + return os.path.join(driveletter, s[len(rawdrive):]) + + +# --- public functions + +nt_virtmem_info = namedtuple('vmem', ' '.join([ + # all platforms + 'total', 'available', 'percent', 'used', 'free'])) + +def virtual_memory(): + """System virtual memory as a namedtuple.""" + mem = _psutil_mswindows.get_virtual_mem() + totphys, availphys, totpagef, availpagef, totvirt, freevirt = mem + # + total = totphys + avail = availphys + free = availphys + used = total - avail + percent = usage_percent((total - avail), total, _round=1) + return nt_virtmem_info(total, avail, percent, used, free) + +def swap_memory(): + """Swap system memory as a (total, used, free, sin, sout) tuple.""" + mem = _psutil_mswindows.get_virtual_mem() + total = mem[2] + free = mem[3] + used = total - free + percent = usage_percent(used, total, _round=1) + return nt_swapmeminfo(total, used, free, percent, 0, 0) + +def get_disk_usage(path): + """Return disk usage associated with path.""" + try: + total, free = _psutil_mswindows.get_disk_usage(path) + except WindowsError: + err = sys.exc_info()[1] + if not os.path.exists(path): + raise OSError(errno.ENOENT, "No such file or directory: '%s'" % path) + raise + used = total - free + percent = usage_percent(used, total, _round=1) + return nt_diskinfo(total, used, free, percent) + +def disk_partitions(all): + """Return disk partitions.""" + rawlist = _psutil_mswindows.get_disk_partitions(all) + return [nt_partition(*x) for x in rawlist] + + +_cputimes_ntuple = namedtuple('cputimes', 'user system idle') + +def get_system_cpu_times(): + """Return system CPU times as a named tuple.""" + user, system, idle = 0, 0, 0 + # computes system global times summing each processor value + for cpu_time in _psutil_mswindows.get_system_cpu_times(): + user += cpu_time[0] + system += cpu_time[1] + idle += cpu_time[2] + return _cputimes_ntuple(user, system, idle) + +def get_system_per_cpu_times(): + """Return system per-CPU times as a list of named tuples.""" + ret = [] + for cpu_t in _psutil_mswindows.get_system_cpu_times(): + user, system, idle = cpu_t + item = _cputimes_ntuple(user, system, idle) + ret.append(item) + return ret + +def get_system_users(): + """Return currently connected users as a list of namedtuples.""" + retlist = [] + rawlist = _psutil_mswindows.get_system_users() + for item in rawlist: + user, hostname, tstamp = item + nt = nt_user(user, None, hostname, tstamp) + retlist.append(nt) + return retlist + +get_pid_list = _psutil_mswindows.get_pid_list +pid_exists = _psutil_mswindows.pid_exists +network_io_counters = _psutil_mswindows.get_network_io_counters +disk_io_counters = _psutil_mswindows.get_disk_io_counters + +# --- decorator + +def wrap_exceptions(callable): + """Call callable into a try/except clause so that if a + WindowsError 5 AccessDenied exception is raised we translate it + into psutil.AccessDenied + """ + def wrapper(self, *args, **kwargs): + try: + return callable(self, *args, **kwargs) + except OSError: + err = sys.exc_info()[1] + if err.errno in ACCESS_DENIED_SET: + raise AccessDenied(self.pid, self._process_name) + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, self._process_name) + raise + return wrapper + + +class Process(object): + """Wrapper class around underlying C implementation.""" + + __slots__ = ["pid", "_process_name"] + + def __init__(self, pid): + self.pid = pid + self._process_name = None + + @wrap_exceptions + def get_process_name(self): + """Return process name as a string of limited len (15).""" + return _psutil_mswindows.get_process_name(self.pid) + + @wrap_exceptions + def get_process_exe(self): + # Note: os.path.exists(path) may return False even if the file + # is there, see: + # http://stackoverflow.com/questions/3112546/os-path-exists-lies + return _convert_raw_path(_psutil_mswindows.get_process_exe(self.pid)) + + @wrap_exceptions + def get_process_cmdline(self): + """Return process cmdline as a list of arguments.""" + return _psutil_mswindows.get_process_cmdline(self.pid) + + @wrap_exceptions + def get_process_ppid(self): + """Return process parent pid.""" + return _psutil_mswindows.get_process_ppid(self.pid) + + def _get_raw_meminfo(self): + try: + return _psutil_mswindows.get_process_memory_info(self.pid) + except OSError: + err = sys.exc_info()[1] + if err.errno in ACCESS_DENIED_SET: + return _psutil_mswindows.get_process_memory_info_2(self.pid) + raise + + @wrap_exceptions + def get_memory_info(self): + """Returns a tuple or RSS/VMS memory usage in bytes.""" + # on Windows RSS == WorkingSetSize and VSM == PagefileUsage + # fields of PROCESS_MEMORY_COUNTERS struct: + # http://msdn.microsoft.com/en-us/library/windows/desktop/ms684877(v=vs.85).aspx + t = self._get_raw_meminfo() + return nt_meminfo(t[2], t[7]) + + _nt_ext_mem = namedtuple('meminfo', + ' '.join(['num_page_faults', + 'peak_wset', + 'wset', + 'peak_paged_pool', + 'paged_pool', + 'peak_nonpaged_pool', + 'nonpaged_pool', + 'pagefile', + 'peak_pagefile', + 'private',])) + + @wrap_exceptions + def get_ext_memory_info(self): + return self._nt_ext_mem(*self._get_raw_meminfo()) + + nt_mmap_grouped = namedtuple('mmap', 'path rss') + nt_mmap_ext = namedtuple('mmap', 'addr perms path rss') + + def get_memory_maps(self): + try: + raw = _psutil_mswindows.get_process_memory_maps(self.pid) + except OSError: + # XXX - can't use wrap_exceptions decorator as we're + # returning a generator; probably needs refactoring. + err = sys.exc_info()[1] + if err.errno in (errno.EPERM, errno.EACCES, ERROR_ACCESS_DENIED): + raise AccessDenied(self.pid, self._process_name) + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, self._process_name) + raise + else: + for addr, perm, path, rss in raw: + path = _convert_raw_path(path) + addr = hex(addr) + yield (addr, perm, path, rss) + + @wrap_exceptions + def kill_process(self): + """Terminates the process with the given PID.""" + return _psutil_mswindows.kill_process(self.pid) + + @wrap_exceptions + def process_wait(self, timeout=None): + if timeout is None: + timeout = INFINITE + else: + # WaitForSingleObject() expects time in milliseconds + timeout = int(timeout * 1000) + ret = _psutil_mswindows.process_wait(self.pid, timeout) + if ret == WAIT_TIMEOUT: + raise TimeoutExpired(self.pid, self._process_name) + return ret + + @wrap_exceptions + def get_process_username(self): + """Return the name of the user that owns the process""" + if self.pid in (0, 4): + return 'NT AUTHORITY\\SYSTEM' + return _psutil_mswindows.get_process_username(self.pid) + + @wrap_exceptions + def get_process_create_time(self): + # special case for kernel process PIDs; return system boot time + if self.pid in (0, 4): + return BOOT_TIME + try: + return _psutil_mswindows.get_process_create_time(self.pid) + except OSError: + err = sys.exc_info()[1] + if err.errno in ACCESS_DENIED_SET: + return _psutil_mswindows.get_process_create_time_2(self.pid) + raise + + @wrap_exceptions + def get_process_num_threads(self): + return _psutil_mswindows.get_process_num_threads(self.pid) + + @wrap_exceptions + def get_process_threads(self): + rawlist = _psutil_mswindows.get_process_threads(self.pid) + retlist = [] + for thread_id, utime, stime in rawlist: + ntuple = nt_thread(thread_id, utime, stime) + retlist.append(ntuple) + return retlist + + @wrap_exceptions + def get_cpu_times(self): + try: + ret = _psutil_mswindows.get_process_cpu_times(self.pid) + except OSError: + err = sys.exc_info()[1] + if err.errno in ACCESS_DENIED_SET: + ret = _psutil_mswindows.get_process_cpu_times_2(self.pid) + else: + raise + return nt_cputimes(*ret) + + @wrap_exceptions + def suspend_process(self): + return _psutil_mswindows.suspend_process(self.pid) + + @wrap_exceptions + def resume_process(self): + return _psutil_mswindows.resume_process(self.pid) + + @wrap_exceptions + def get_process_cwd(self): + if self.pid in (0, 4): + raise AccessDenied(self.pid, self._process_name) + # return a normalized pathname since the native C function appends + # "\\" at the and of the path + path = _psutil_mswindows.get_process_cwd(self.pid) + return os.path.normpath(path) + + @wrap_exceptions + def get_open_files(self): + if self.pid in (0, 4): + return [] + retlist = [] + # Filenames come in in native format like: + # "\Device\HarddiskVolume1\Windows\systemew\file.txt" + # Convert the first part in the corresponding drive letter + # (e.g. "C:\") by using Windows's QueryDosDevice() + raw_file_names = _psutil_mswindows.get_process_open_files(self.pid) + for file in raw_file_names: + file = _convert_raw_path(file) + if isfile_strict(file) and file not in retlist: + ntuple = nt_openfile(file, -1) + retlist.append(ntuple) + return retlist + + @wrap_exceptions + def get_connections(self, kind='inet'): + if kind not in conn_tmap: + raise ValueError("invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in conn_tmap]))) + families, types = conn_tmap[kind] + ret = _psutil_mswindows.get_process_connections(self.pid, families, types) + return [nt_connection(*conn) for conn in ret] + + @wrap_exceptions + def get_process_nice(self): + return _psutil_mswindows.get_process_priority(self.pid) + + @wrap_exceptions + def set_process_nice(self, value): + return _psutil_mswindows.set_process_priority(self.pid, value) + + @wrap_exceptions + def get_process_io_counters(self): + try: + ret = _psutil_mswindows.get_process_io_counters(self.pid) + except OSError: + err = sys.exc_info()[1] + if err.errno in ACCESS_DENIED_SET: + ret = _psutil_mswindows.get_process_io_counters_2(self.pid) + else: + raise + return nt_io(*ret) + + @wrap_exceptions + def get_process_status(self): + suspended = _psutil_mswindows.is_process_suspended(self.pid) + if suspended: + return STATUS_STOPPED + else: + return STATUS_RUNNING + + @wrap_exceptions + def get_process_cpu_affinity(self): + from_bitmask = lambda x: [i for i in xrange(64) if (1 << i) & x] + bitmask = _psutil_mswindows.get_process_cpu_affinity(self.pid) + return from_bitmask(bitmask) + + @wrap_exceptions + def set_process_cpu_affinity(self, value): + def to_bitmask(l): + if not l: + raise ValueError("invalid argument %r" % l) + out = 0 + for b in l: + if not isinstance(b, (int, long)) or b < 0: + raise ValueError("invalid argument %r" % b) + out |= 2**b + return out + + # SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER + # is returned for an invalid CPU but this seems not to be true, + # therefore we check CPUs validy beforehand. + allcpus = list(range(len(get_system_per_cpu_times()))) + for cpu in value: + if cpu not in allcpus: + raise ValueError("invalid CPU %i" % cpu) + + bitmask = to_bitmask(value) + _psutil_mswindows.set_process_cpu_affinity(self.pid, bitmask) + + @wrap_exceptions + def get_num_handles(self): + try: + return _psutil_mswindows.get_process_num_handles(self.pid) + except OSError: + err = sys.exc_info()[1] + if err.errno in ACCESS_DENIED_SET: + return _psutil_mswindows.get_process_num_handles_2(self.pid) + raise + + @wrap_exceptions + def get_num_ctx_switches(self): + return nt_ctxsw(*_psutil_mswindows.get_process_num_ctx_switches(self.pid)) diff --git a/vendor/psutil/_psosx.py b/vendor/psutil/_psosx.py new file mode 100644 index 000000000..90cead2bf --- /dev/null +++ b/vendor/psutil/_psosx.py @@ -0,0 +1,293 @@ +#!/usr/bin/env python +# +# $Id: _psosx.py 1498 2012-07-24 21:41:28Z g.rodola $ +# +# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""OSX platform implementation.""" + +import errno +import os +import sys + +import _psutil_osx +import _psutil_posix +from psutil import _psposix +from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired +from psutil._compat import namedtuple +from psutil._common import * + +__extra__all__ = [] + +# --- constants + +NUM_CPUS = _psutil_osx.get_num_cpus() +BOOT_TIME = _psutil_osx.get_system_boot_time() +TOTAL_PHYMEM = _psutil_osx.get_virtual_mem()[0] +_PAGESIZE = os.sysconf("SC_PAGE_SIZE") +_TERMINAL_MAP = _psposix._get_terminal_map() +_cputimes_ntuple = namedtuple('cputimes', 'user nice system idle') + +# --- functions + +nt_virtmem_info = namedtuple('vmem', ' '.join([ + # all platforms + 'total', 'available', 'percent', 'used', 'free', + # OSX specific + 'active', + 'inactive', + 'wired'])) + +def virtual_memory(): + """System virtual memory as a namedtuple.""" + total, active, inactive, wired, free = _psutil_osx.get_virtual_mem() + avail = inactive + free + used = active + inactive + wired + percent = usage_percent((total - avail), total, _round=1) + return nt_virtmem_info(total, avail, percent, used, free, + active, inactive, wired) + +def swap_memory(): + """Swap system memory as a (total, used, free, sin, sout) tuple.""" + total, used, free, sin, sout = _psutil_osx.get_swap_mem() + percent = usage_percent(used, total, _round=1) + return nt_swapmeminfo(total, used, free, percent, sin, sout) + +def get_system_cpu_times(): + """Return system CPU times as a namedtuple.""" + user, nice, system, idle = _psutil_osx.get_system_cpu_times() + return _cputimes_ntuple(user, nice, system, idle) + +def get_system_per_cpu_times(): + """Return system CPU times as a named tuple""" + ret = [] + for cpu_t in _psutil_osx.get_system_per_cpu_times(): + user, nice, system, idle = cpu_t + item = _cputimes_ntuple(user, nice, system, idle) + ret.append(item) + return ret + +def disk_partitions(all=False): + retlist = [] + partitions = _psutil_osx.get_disk_partitions() + for partition in partitions: + device, mountpoint, fstype, opts = partition + if device == 'none': + device = '' + if not all: + if not os.path.isabs(device) \ + or not os.path.exists(device): + continue + ntuple = nt_partition(device, mountpoint, fstype, opts) + retlist.append(ntuple) + return retlist + +def get_system_users(): + retlist = [] + rawlist = _psutil_osx.get_system_users() + for item in rawlist: + user, tty, hostname, tstamp = item + if tty == '~': + continue # reboot or shutdown + if not tstamp: + continue + nt = nt_user(user, tty or None, hostname or None, tstamp) + retlist.append(nt) + return retlist + + +get_pid_list = _psutil_osx.get_pid_list +pid_exists = _psposix.pid_exists +get_disk_usage = _psposix.get_disk_usage +network_io_counters = _psutil_osx.get_network_io_counters +disk_io_counters = _psutil_osx.get_disk_io_counters + +# --- decorator + +def wrap_exceptions(callable): + """Call callable into a try/except clause so that if an + OSError EPERM exception is raised we translate it into + psutil.AccessDenied. + """ + def wrapper(self, *args, **kwargs): + try: + return callable(self, *args, **kwargs) + except OSError: + err = sys.exc_info()[1] + if err.errno == errno.ESRCH: + raise NoSuchProcess(self.pid, self._process_name) + if err.errno in (errno.EPERM, errno.EACCES): + raise AccessDenied(self.pid, self._process_name) + raise + return wrapper + + +_status_map = { + _psutil_osx.SIDL : STATUS_IDLE, + _psutil_osx.SRUN : STATUS_RUNNING, + _psutil_osx.SSLEEP : STATUS_SLEEPING, + _psutil_osx.SSTOP : STATUS_STOPPED, + _psutil_osx.SZOMB : STATUS_ZOMBIE, +} + +class Process(object): + """Wrapper class around underlying C implementation.""" + + __slots__ = ["pid", "_process_name"] + + def __init__(self, pid): + self.pid = pid + self._process_name = None + + @wrap_exceptions + def get_process_name(self): + """Return process name as a string of limited len (15).""" + return _psutil_osx.get_process_name(self.pid) + + @wrap_exceptions + def get_process_exe(self): + return _psutil_osx.get_process_exe(self.pid) + + @wrap_exceptions + def get_process_cmdline(self): + """Return process cmdline as a list of arguments.""" + if not pid_exists(self.pid): + raise NoSuchProcess(self.pid, self._process_name) + return _psutil_osx.get_process_cmdline(self.pid) + + @wrap_exceptions + def get_process_ppid(self): + """Return process parent pid.""" + return _psutil_osx.get_process_ppid(self.pid) + + @wrap_exceptions + def get_process_cwd(self): + return _psutil_osx.get_process_cwd(self.pid) + + @wrap_exceptions + def get_process_uids(self): + real, effective, saved = _psutil_osx.get_process_uids(self.pid) + return nt_uids(real, effective, saved) + + @wrap_exceptions + def get_process_gids(self): + real, effective, saved = _psutil_osx.get_process_gids(self.pid) + return nt_gids(real, effective, saved) + + @wrap_exceptions + def get_process_terminal(self): + tty_nr = _psutil_osx.get_process_tty_nr(self.pid) + try: + return _TERMINAL_MAP[tty_nr] + except KeyError: + return None + + @wrap_exceptions + def get_memory_info(self): + """Return a tuple with the process' RSS and VMS size.""" + rss, vms = _psutil_osx.get_process_memory_info(self.pid)[:2] + return nt_meminfo(rss, vms) + + _nt_ext_mem = namedtuple('meminfo', 'rss vms pfaults pageins') + + @wrap_exceptions + def get_ext_memory_info(self): + """Return a tuple with the process' RSS and VMS size.""" + rss, vms, pfaults, pageins = _psutil_osx.get_process_memory_info(self.pid) + return self._nt_ext_mem(rss, vms, + pfaults * _PAGESIZE, + pageins * _PAGESIZE) + + @wrap_exceptions + def get_cpu_times(self): + user, system = _psutil_osx.get_process_cpu_times(self.pid) + return nt_cputimes(user, system) + + @wrap_exceptions + def get_process_create_time(self): + """Return the start time of the process as a number of seconds since + the epoch.""" + return _psutil_osx.get_process_create_time(self.pid) + + @wrap_exceptions + def get_num_ctx_switches(self): + return nt_ctxsw(*_psutil_osx.get_process_num_ctx_switches(self.pid)) + + @wrap_exceptions + def get_process_num_threads(self): + """Return the number of threads belonging to the process.""" + return _psutil_osx.get_process_num_threads(self.pid) + + @wrap_exceptions + def get_open_files(self): + """Return files opened by process.""" + if self.pid == 0: + return [] + files = [] + rawlist = _psutil_osx.get_process_open_files(self.pid) + for path, fd in rawlist: + if isfile_strict(path): + ntuple = nt_openfile(path, fd) + files.append(ntuple) + return files + + @wrap_exceptions + def get_connections(self, kind='inet'): + """Return etwork connections opened by a process as a list of + namedtuples. + """ + if kind not in conn_tmap: + raise ValueError("invalid %r kind argument; choose between %s" + % (kind, ', '.join([repr(x) for x in conn_tmap]))) + families, types = conn_tmap[kind] + ret = _psutil_osx.get_process_connections(self.pid, families, types) + return [nt_connection(*conn) for conn in ret] + + @wrap_exceptions + def get_num_fds(self): + if self.pid == 0: + return 0 + return _psutil_osx.get_process_num_fds(self.pid) + + @wrap_exceptions + def process_wait(self, timeout=None): + try: + return _psposix.wait_pid(self.pid, timeout) + except TimeoutExpired: + raise TimeoutExpired(self.pid, self._process_name) + + @wrap_exceptions + def get_process_nice(self): + return _psutil_posix.getpriority(self.pid) + + @wrap_exceptions + def set_process_nice(self, value): + return _psutil_posix.setpriority(self.pid, value) + + @wrap_exceptions + def get_process_status(self): + code = _psutil_osx.get_process_status(self.pid) + if code in _status_map: + return _status_map[code] + return constant(-1, "?") + + @wrap_exceptions + def get_process_threads(self): + """Return the number of threads belonging to the process.""" + rawlist = _psutil_osx.get_process_threads(self.pid) + retlist = [] + for thread_id, utime, stime in rawlist: + ntuple = nt_thread(thread_id, utime, stime) + retlist.append(ntuple) + return retlist + + nt_mmap_grouped = namedtuple('mmap', + 'path rss private swapped dirtied ref_count shadow_depth') + nt_mmap_ext = namedtuple('mmap', + 'addr perms path rss private swapped dirtied ref_count shadow_depth') + + @wrap_exceptions + def get_memory_maps(self): + return _psutil_osx.get_process_memory_maps(self.pid) diff --git a/vendor/psutil/_psposix.py b/vendor/psutil/_psposix.py new file mode 100644 index 000000000..899f77ae6 --- /dev/null +++ b/vendor/psutil/_psposix.py @@ -0,0 +1,118 @@ +#!/usr/bin/env python +# +# $Id: _psposix.py 1409 2012-07-04 08:21:06Z g.rodola $ +# +# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Routines common to all posix systems.""" + +import os +import errno +import psutil +import sys +import time +import glob + +from psutil.error import TimeoutExpired +from psutil._common import nt_diskinfo, usage_percent + + +def pid_exists(pid): + """Check whether pid exists in the current process table.""" + if not isinstance(pid, int): + raise TypeError('an integer is required') + if pid < 0: + return False + try: + os.kill(pid, 0) + except OSError: + e = sys.exc_info()[1] + return e.errno == errno.EPERM + else: + return True + +def wait_pid(pid, timeout=None): + """Wait for process with pid 'pid' to terminate and return its + exit status code as an integer. + + If pid is not a children of os.getpid() (current process) just + waits until the process disappears and return None. + + If pid does not exist at all return None immediately. + + Raise TimeoutExpired on timeout expired. + """ + def check_timeout(delay): + if timeout is not None: + if time.time() >= stop_at: + raise TimeoutExpired(pid) + time.sleep(delay) + return min(delay * 2, 0.04) + + if timeout is not None: + waitcall = lambda: os.waitpid(pid, os.WNOHANG) + stop_at = time.time() + timeout + else: + waitcall = lambda: os.waitpid(pid, 0) + + delay = 0.0001 + while 1: + try: + retpid, status = waitcall() + except OSError: + err = sys.exc_info()[1] + if err.errno == errno.EINTR: + delay = check_timeout(delay) + continue + elif err.errno == errno.ECHILD: + # This has two meanings: + # - pid is not a child of os.getpid() in which case + # we keep polling until it's gone + # - pid never existed in the first place + # In both cases we'll eventually return None as we + # can't determine its exit status code. + while 1: + if pid_exists(pid): + delay = check_timeout(delay) + else: + return + else: + raise + else: + if retpid == 0: + # WNOHANG was used, pid is still running + delay = check_timeout(delay) + continue + # process exited due to a signal; return the integer of + # that signal + if os.WIFSIGNALED(status): + return os.WTERMSIG(status) + # process exited using exit(2) system call; return the + # integer exit(2) system call has been called with + elif os.WIFEXITED(status): + return os.WEXITSTATUS(status) + else: + # should never happen + raise RuntimeError("unknown process exit status") + +def get_disk_usage(path): + """Return disk usage associated with path.""" + st = os.statvfs(path) + free = (st.f_bavail * st.f_frsize) + total = (st.f_blocks * st.f_frsize) + used = (st.f_blocks - st.f_bfree) * st.f_frsize + percent = usage_percent(used, total, _round=1) + # NB: the percentage is -5% than what shown by df due to + # reserved blocks that we are currently not considering: + # http://goo.gl/sWGbH + return nt_diskinfo(total, used, free, percent) + +def _get_terminal_map(): + ret = {} + ls = glob.glob('/dev/tty*') + glob.glob('/dev/pts/*') + for name in ls: + assert name not in ret + ret[os.stat(name).st_rdev] = name + return ret diff --git a/vendor/psutil/_psutil_bsd.c b/vendor/psutil/_psutil_bsd.c new file mode 100644 index 000000000..91bf6584b --- /dev/null +++ b/vendor/psutil/_psutil_bsd.c @@ -0,0 +1,1777 @@ +/* + * $Id: _psutil_bsd.c 1513 2012-08-14 11:01:37Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * FreeBSD platform-specific module methods for _psutil_bsd + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include /* for struct socket */ +#include /* for struct proto */ +#include /* for struct domain */ + +#include /* for unpcb struct (UNIX sockets) */ +#include /* for unpcb struct (UNIX sockets) */ +#include /* for mbuf struct (UNIX sockets) */ +/* for in_pcb struct */ +#include +#include +#include +#include +#include /* for struct tcpcb */ +#include /* for TCP connection states */ +#include /* for inet_ntop() */ + +#if __FreeBSD_version < 900000 + #include /* system users */ +#else + #include +#endif +#include /* get io counters */ +#include /* needed for vmtotal struct */ +#include /* process open files, shared libs (kinfo_getvmmap) */ +#include + +#include /* net io counters */ +#include +#include + +#include /* process open files/connections */ +#include + +#include "_psutil_bsd.h" +#include "_psutil_common.h" +#include "arch/bsd/process_info.h" + + +// convert a timeval struct to a double +#define TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) + + +/* + * Utility function which fills a kinfo_proc struct based on process pid + */ +static int +get_kinfo_proc(const pid_t pid, struct kinfo_proc *proc) +{ + int mib[4]; + size_t size; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + + size = sizeof(struct kinfo_proc); + + if (sysctl((int*)mib, 4, proc, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + /* + * sysctl stores 0 in the size if we can't find the process information. + */ + if (size == 0) { + NoSuchProcess(); + return -1; + } + return 0; +} + + +/* + * Return a Python list of all the PIDs running on the system. + */ +static PyObject* +get_pid_list(PyObject* self, PyObject* args) +{ + kinfo_proc *proclist = NULL; + kinfo_proc *orig_address = NULL; + size_t num_processes; + size_t idx; + PyObject* retlist = PyList_New(0); + PyObject* pid = NULL; + + if (get_proc_list(&proclist, &num_processes) != 0) { + PyErr_SetString(PyExc_RuntimeError, "failed to retrieve process list."); + goto error; + } + + if (num_processes > 0) { + orig_address = proclist; // save so we can free it after we're done + for (idx=0; idx < num_processes; idx++) { + pid = Py_BuildValue("i", proclist->ki_pid); + if (!pid) + goto error; + if (PyList_Append(retlist, pid)) + goto error; + Py_DECREF(pid); + proclist++; + } + free(orig_address); + } + + return retlist; + +error: + Py_XDECREF(pid); + Py_DECREF(retlist); + if (orig_address != NULL) { + free(orig_address); + } + return NULL; +} + + +/* + * Return a Python float indicating the system boot time expressed in + * seconds since the epoch. + */ +static PyObject* +get_system_boot_time(PyObject* self, PyObject* args) +{ + /* fetch sysctl "kern.boottime" */ + static int request[2] = { CTL_KERN, KERN_BOOTTIME }; + struct timeval result; + size_t result_len = sizeof result; + time_t boot_time = 0; + + if (sysctl(request, 2, &result, &result_len, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + boot_time = result.tv_sec; + return Py_BuildValue("f", (float)boot_time); +} + + +/* + * Return process name from kinfo_proc as a Python string. + */ +static PyObject* +get_process_name(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("s", kp.ki_comm); +} + + +/* + * Return process pathname executable. + * Thanks to Robert N. M. Watson: + * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_bin.c?v=8-CURRENT + */ +static PyObject* +get_process_exe(PyObject* self, PyObject* args) +{ + long pid; + char pathname[PATH_MAX]; + int error; + int mib[4]; + size_t size; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (pid == 0) { + // ...otherwise we'd get '\x98\xd5\xbf\xbf\xfb\xf3\x10\x08H\x01' + return Py_BuildValue("s", ""); + } + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = pid; + + size = sizeof(pathname); + error = sysctl(mib, 4, pathname, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + return NULL; + } + + return Py_BuildValue("s", pathname); +} + + +/* + * Return process cmdline as a Python list of cmdline arguments. + */ +static PyObject* +get_process_cmdline(PyObject* self, PyObject* args) +{ + long pid; + PyObject* arglist = NULL; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + // get the commandline, defined in arch/bsd/process_info.c + arglist = get_arg_list(pid); + + // get_arg_list() returns NULL only if getcmdargs failed with ESRCH + // (no process with that PID) + if (NULL == arglist) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return Py_BuildValue("N", arglist); +} + + +/* + * Return process parent pid from kinfo_proc as a Python integer. + */ +static PyObject* +get_process_ppid(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.ki_ppid); +} + + +/* + * Return process status as a Python integer. + */ +static PyObject* +get_process_status(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("i", (int)kp.ki_stat); +} + + +/* + * Return process real, effective and saved user ids from kinfo_proc + * as a Python tuple. + */ +static PyObject* +get_process_uids(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("lll", (long)kp.ki_ruid, + (long)kp.ki_uid, + (long)kp.ki_svuid); +} + + +/* + * Return process real, effective and saved group ids from kinfo_proc + * as a Python tuple. + */ +static PyObject* +get_process_gids(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("lll", (long)kp.ki_rgid, + (long)kp.ki_groups[0], + (long)kp.ki_svuid); +} + + +/* + * Return process real, effective and saved group ids from kinfo_proc + * as a Python tuple. + */ +static PyObject* +get_process_tty_nr(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("i", kp.ki_tdev); +} + + +/* + * Return the number of context switches performed by process as a tuple. + */ +static PyObject* +get_process_num_ctx_switches(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("(ll)", kp.ki_rusage.ru_nvcsw, + kp.ki_rusage.ru_nivcsw); +} + + + +/* + * Return number of threads used by process as a Python integer. + */ +static PyObject* +get_process_num_threads(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.ki_numthreads); +} + + +/* + * Retrieves all threads used by process returning a list of tuples + * including thread id, user time and system time. + * Thanks to Robert N. M. Watson: + * http://fxr.googlebit.com/source/usr.bin/procstat/procstat_threads.c?v=8-CURRENT + */ +static PyObject* +get_process_threads(PyObject* self, PyObject* args) +{ + long pid; + int mib[4]; + struct kinfo_proc *kip = NULL; + struct kinfo_proc *kipp; + int error; + unsigned int i; + size_t size; + PyObject* retList = PyList_New(0); + PyObject* pyTuple = NULL; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + goto error; + } + + /* + * We need to re-query for thread information, so don't use *kipp. + */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID | KERN_PROC_INC_THREAD; + mib[3] = pid; + + size = 0; + error = sysctl(mib, 4, NULL, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (size == 0) { + NoSuchProcess(); + goto error; + } + + kip = malloc(size); + if (kip == NULL) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + error = sysctl(mib, 4, kip, &size, NULL, 0); + if (error == -1) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (size == 0) { + NoSuchProcess(); + goto error; + } + + for (i = 0; i < size / sizeof(*kipp); i++) { + kipp = &kip[i]; + pyTuple = Py_BuildValue("Idd", kipp->ki_tid, + TV2DOUBLE(kipp->ki_rusage.ru_utime), + TV2DOUBLE(kipp->ki_rusage.ru_stime) + ); + if (pyTuple == NULL) + goto error; + if (PyList_Append(retList, pyTuple)) + goto error; + Py_DECREF(pyTuple); + } + free(kip); + return retList; + +error: + Py_XDECREF(pyTuple); + Py_DECREF(retList); + if (kip != NULL) { + free(kip); + } + return NULL; +} + + +/* + * Return a Python tuple (user_time, kernel_time) + */ +static PyObject* +get_process_cpu_times(PyObject* self, PyObject* args) +{ + long pid; + double user_t, sys_t; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + // convert from microseconds to seconds + user_t = TV2DOUBLE(kp.ki_rusage.ru_utime); + sys_t = TV2DOUBLE(kp.ki_rusage.ru_stime); + return Py_BuildValue("(dd)", user_t, sys_t); +} + + +/* + * Return a Python integer indicating the number of CPUs on the system + */ +static PyObject* +get_num_cpus(PyObject* self, PyObject* args) +{ + int mib[2]; + int ncpu; + size_t len; + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + len = sizeof(ncpu); + + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + return Py_BuildValue("i", ncpu); +} + + +/* + * Return a Python float indicating the process create time expressed in + * seconds since the epoch. + */ +static PyObject* +get_process_create_time(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("d", TV2DOUBLE(kp.ki_start)); +} + + +/* + * Return a Python float indicating the process create time expressed in + * seconds since the epoch. + */ +static PyObject* +get_process_io_counters(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + // there's apparently no way to determine bytes count, hence return -1. + return Py_BuildValue("(llll)", kp.ki_rusage.ru_inblock, + kp.ki_rusage.ru_oublock, + -1, -1); +} + + +/* + * Return extended memory info for a process as a Python tuple. + */ +static PyObject* +get_process_memory_info(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("(lllll)", ptoa(kp.ki_rssize), // rss + (long)kp.ki_size, // vms + ptoa(kp.ki_tsize), // text + ptoa(kp.ki_dsize), // data + ptoa(kp.ki_ssize)); // stack +} + + +/* + * Return virtual memory usage statistics. + */ +static PyObject* +get_virtual_mem(PyObject* self, PyObject* args) +{ + unsigned int total, active, inactive, wired, cached, free, buffers; + size_t size = sizeof(total); + struct vmtotal vm; + int mib[] = {CTL_VM, VM_METER}; + long pagesize = getpagesize(); + + if (sysctlbyname("vm.stats.vm.v_page_count", &total, &size, NULL, 0)) + goto error; + if (sysctlbyname("vm.stats.vm.v_active_count", &active, &size, NULL, 0)) + goto error; + if (sysctlbyname("vm.stats.vm.v_inactive_count", &inactive, &size, NULL, 0)) + goto error; + if (sysctlbyname("vm.stats.vm.v_wire_count", &wired, &size, NULL, 0)) + goto error; + if (sysctlbyname("vm.stats.vm.v_cache_count", &cached, &size, NULL, 0)) + goto error; + if (sysctlbyname("vm.stats.vm.v_free_count", &free, &size, NULL, 0)) + goto error; + if (sysctlbyname("vfs.bufspace", &buffers, &size, NULL, 0)) + goto error; + + size = sizeof(vm); + if (sysctl(mib, 2, &vm, &size, NULL, 0) != 0) + goto error; + + return Py_BuildValue("KKKKKKKK", + (unsigned long long) total * pagesize, + (unsigned long long) free * pagesize, + (unsigned long long) active * pagesize, + (unsigned long long) inactive * pagesize, + (unsigned long long) wired * pagesize, + (unsigned long long) cached * pagesize, + (unsigned long long) buffers, + (unsigned long long) (vm.t_vmshr + vm.t_rmshr) * pagesize // shared + ); + +error: + PyErr_SetFromErrno(0); + return NULL; +} + + +/* + * Return swap memory stats (see 'swapinfo' cmdline tool) + */ +static PyObject* +get_swap_mem(PyObject* self, PyObject* args) +{ + kvm_t *kd; + struct kvm_swap kvmsw[1]; + unsigned int swapin, swapout, nodein, nodeout; + size_t size = sizeof(unsigned int); + + if (kvm_getswapinfo(kd, kvmsw, 1, 0) < 0) { + PyErr_SetString(PyExc_RuntimeError, "kvm_getswapinfo failed"); + return NULL; + } + + if (sysctlbyname("vm.stats.vm.v_swapin", &swapin, &size, NULL, 0) == -1) + goto sbn_error; + if (sysctlbyname("vm.stats.vm.v_swapout", &swapout, &size, NULL, 0) == -1) + goto sbn_error; + if (sysctlbyname("vm.stats.vm.v_vnodein", &nodein, &size, NULL, 0) == -1) + goto sbn_error; + if (sysctlbyname("vm.stats.vm.v_vnodeout", &nodeout, &size, NULL, 0) == -1) + goto sbn_error; + + return Py_BuildValue("(iiiII)", + kvmsw[0].ksw_total, // total + kvmsw[0].ksw_used, // used + kvmsw[0].ksw_total - kvmsw[0].ksw_used, // free + swapin + swapout, // swap in + nodein + nodeout); // swap out + +sbn_error: + PyErr_SetFromErrno(0); + return NULL; +} + + +/* + * Return a Python tuple representing user, kernel and idle CPU times + */ +static PyObject* +get_system_cpu_times(PyObject* self, PyObject* args) +{ + long cpu_time[CPUSTATES]; + size_t size; + + size = sizeof(cpu_time); + + if (sysctlbyname("kern.cp_time", &cpu_time, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + return Py_BuildValue("(ddddd)", + (double)cpu_time[CP_USER] / CLOCKS_PER_SEC, + (double)cpu_time[CP_NICE] / CLOCKS_PER_SEC, + (double)cpu_time[CP_SYS] / CLOCKS_PER_SEC, + (double)cpu_time[CP_IDLE] / CLOCKS_PER_SEC, + (double)cpu_time[CP_INTR] / CLOCKS_PER_SEC + ); +} + +/* + * XXX + * These functions are available on FreeBSD 8 only. + * In the upper python layer we do various tricks to avoid crashing + * and/or to provide alternatives where possible. + */ + + +#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 +/* + * Return files opened by process as a list of (path, fd) tuples + */ +static PyObject* +get_process_open_files(PyObject* self, PyObject* args) +{ + long pid; + int i, cnt; + PyObject *retList = PyList_New(0); + PyObject *tuple = NULL; + + struct kinfo_file *freep = NULL; + struct kinfo_file *kif; + struct kinfo_proc kipp; + + if (! PyArg_ParseTuple(args, "l", &pid)) + goto error; + if (get_kinfo_proc(pid, &kipp) == -1) + goto error; + + freep = kinfo_getfile(pid, &cnt); + if (freep == NULL) { + PyErr_SetFromErrno(0); + goto error; + } + + for (i = 0; i < cnt; i++) { + kif = &freep[i]; + if ((kif->kf_type == KF_TYPE_VNODE) && + (kif->kf_vnode_type == KF_VTYPE_VREG)) + { + tuple = Py_BuildValue("(si)", kif->kf_path, kif->kf_fd); + if (tuple == NULL) + goto error; + if (PyList_Append(retList, tuple)) + goto error; + Py_DECREF(tuple); + } + } + free(freep); + return retList; + +error: + Py_XDECREF(tuple); + Py_DECREF(retList); + if (freep != NULL) + free(freep); + return NULL; +} + + +/* + * Return files opened by process as a list of (path, fd) tuples + */ +static PyObject* +get_process_num_fds(PyObject* self, PyObject* args) +{ + long pid; + int cnt; + + struct kinfo_file *freep; + struct kinfo_proc kipp; + + if (! PyArg_ParseTuple(args, "l", &pid)) + return NULL; + if (get_kinfo_proc(pid, &kipp) == -1) + return NULL; + + freep = kinfo_getfile(pid, &cnt); + if (freep == NULL) { + PyErr_SetFromErrno(0); + return NULL; + } + free(freep); + + return Py_BuildValue("i", cnt); +} + + +/* + * Return process current working directory. + */ +static PyObject* +get_process_cwd(PyObject* self, PyObject* args) +{ + long pid; + PyObject *path = NULL; + struct kinfo_file *freep = NULL; + struct kinfo_file *kif; + struct kinfo_proc kipp; + + int i, cnt; + + if (! PyArg_ParseTuple(args, "l", &pid)) + goto error; + if (get_kinfo_proc(pid, &kipp) == -1) + goto error; + + freep = kinfo_getfile(pid, &cnt); + if (freep == NULL) { + PyErr_SetFromErrno(0); + goto error; + } + + for (i = 0; i < cnt; i++) { + kif = &freep[i]; + if (kif->kf_fd == KF_FD_TYPE_CWD) { + path = Py_BuildValue("s", kif->kf_path); + if (!path) + goto error; + break; + } + } + /* + * For lower pids it seems we can't retrieve any information + * (lsof can't do that it either). Since this happens even + * as root we return an empty string instead of AccessDenied. + */ + if (path == NULL) { + path = Py_BuildValue("s", ""); + } + free(freep); + return path; + +error: + Py_XDECREF(path); + if (freep != NULL) + free(freep); + return NULL; +} + + +/* + * mathes Linux net/tcp_states.h: + * http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h + */ +static char * +get_connection_status(int st) { + switch (st) { + case TCPS_CLOSED: + return "CLOSE"; + case TCPS_CLOSING: + return "CLOSING"; + case TCPS_CLOSE_WAIT: + return "CLOSE_WAIT"; + case TCPS_LISTEN: + return "LISTEN"; + case TCPS_ESTABLISHED: + return "ESTABLISHED"; + case TCPS_SYN_SENT: + return "SYN_SENT"; + case TCPS_SYN_RECEIVED: + return "SYN_RECV"; + case TCPS_FIN_WAIT_1: + return "FIN_WAIT_1"; + case TCPS_FIN_WAIT_2: + return "FIN_WAIT_2"; + case TCPS_LAST_ACK: + return "LAST_ACK"; + case TCPS_TIME_WAIT: + return "TIME_WAIT"; + default: + return "?"; + } +} + +// a kvm_read that returns true if everything is read +#define KVM_READ(kaddr, paddr, len) \ + ((len) < SSIZE_MAX && \ + kvm_read(kd, (u_long)(kaddr), (char *)(paddr), (len)) == (ssize_t)(len)) + +// XXX - copied from sys/file.h to make compiler happy +struct file { + void *f_data; /* file descriptor specific data */ + struct fileops *f_ops; /* File operations */ + struct ucred *f_cred; /* associated credentials. */ + struct vnode *f_vnode; /* NULL or applicable vnode */ + short f_type; /* descriptor type */ + short f_vnread_flags; /* (f) Sleep lock for f_offset */ + volatile u_int f_flag; /* see fcntl.h */ + volatile u_int f_count; /* reference count */ + int f_seqcount; /* Count of sequential accesses. */ + off_t f_nextoff; /* next expected read/write offset. */ + struct cdev_privdata *f_cdevpriv; /* (d) Private data for the cdev. */ + off_t f_offset; + void *f_label; /* Place-holder for MAC label. */ +}; + + +/* + * Return connections opened by process. + * fstat.c source code was used as an example. + */ +static PyObject* +get_process_connections(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc *p; + struct file **ofiles = NULL; + char buf[_POSIX2_LINE_MAX]; + char path[PATH_MAX]; + int cnt; + int i; + kvm_t *kd = NULL; + struct file file; + struct filedesc filed; + struct nlist nl[] = {{ "" },}; + struct socket so; + struct protosw proto; + struct domain dom; + struct inpcb inpcb; + struct tcpcb tcpcb; + struct unpcb unpcb; + + PyObject *retList = PyList_New(0); + PyObject *tuple = NULL; + PyObject *laddr = NULL; + PyObject *raddr = NULL; + PyObject *af_filter = NULL; + PyObject *type_filter = NULL; + PyObject* _family = NULL; + PyObject* _type = NULL; + + if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) { + goto error; + } + if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) { + PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); + goto error; + } + + kd = kvm_openfiles(NULL, NULL, NULL, O_RDONLY, buf); + if (kd == NULL) { + AccessDenied(); + goto error; + } + + if (kvm_nlist(kd, nl) != 0) { + PyErr_SetString(PyExc_RuntimeError, "kvm_nlist() failed"); + goto error; + } + + p = kvm_getprocs(kd, KERN_PROC_PID, pid, &cnt); + if (p == NULL) { + NoSuchProcess(); + goto error; + } + if (cnt != 1) { + NoSuchProcess(); + goto error; + } + if (p->ki_fd == NULL) { + PyErr_SetString(PyExc_RuntimeError, "no usable fd found"); + goto error; + } + if (!KVM_READ(p->ki_fd, &filed, sizeof(filed))) { + PyErr_SetString(PyExc_RuntimeError, "kvm_read() failed"); + goto error; + } + + ofiles = malloc((filed.fd_lastfile+1) * sizeof(struct file *)); + if (ofiles == NULL) { + PyErr_SetString(PyExc_RuntimeError, "malloc() failed"); + goto error; + } + + if (!KVM_READ(filed.fd_ofiles, ofiles, + (filed.fd_lastfile+1) * sizeof(struct file *))) { + PyErr_SetString(PyExc_RuntimeError, "kvm_read() failed"); + goto error; + } + + for (i = 0; i <= filed.fd_lastfile; i++) { + int lport, rport; + char lip[200], rip[200]; + char *state; + int inseq; + tuple = NULL; + laddr = NULL; + raddr = NULL; + + if (ofiles[i] == NULL) { + continue; + } + if (!KVM_READ(ofiles[i], &file, sizeof (struct file))) { + PyErr_SetString(PyExc_RuntimeError, "kvm_read() file failed"); + goto error; + } + if (file.f_type == DTYPE_SOCKET) { + // fill in socket + if (!KVM_READ(file.f_data, &so, sizeof(struct socket))) { + PyErr_SetString(PyExc_RuntimeError, "kvm_read() socket failed"); + goto error; + } + // fill in protosw entry + if (!KVM_READ(so.so_proto, &proto, sizeof(struct protosw))) { + PyErr_SetString(PyExc_RuntimeError, "kvm_read() proto failed"); + goto error; + } + // fill in domain + if (!KVM_READ(proto.pr_domain, &dom, sizeof(struct domain))) { + PyErr_SetString(PyExc_RuntimeError, "kvm_read() domain failed"); + goto error; + } + + // apply filters + _family = PyLong_FromLong((long)dom.dom_family); + inseq = PySequence_Contains(af_filter, _family); + Py_DECREF(_family); + if (inseq == 0) { + continue; + } + _type = PyLong_FromLong((long)proto.pr_type); + inseq = PySequence_Contains(type_filter, _type); + Py_DECREF(_type); + if (inseq == 0) { + continue; + } + + // IPv4 / IPv6 socket + if ((dom.dom_family == AF_INET) || (dom.dom_family == AF_INET6)) { + // fill inpcb + if (kvm_read(kd, (u_long)so.so_pcb, (char *)&inpcb, + sizeof(struct inpcb)) != sizeof(struct inpcb)) { + PyErr_SetString(PyExc_RuntimeError, "kvm_read() addr failed"); + goto error; + } + + // fill status + if (proto.pr_type == SOCK_STREAM) { + if (kvm_read(kd, (u_long)inpcb.inp_ppcb, (char *)&tcpcb, + sizeof(struct tcpcb)) != sizeof(struct tcpcb)) { + PyErr_SetString(PyExc_RuntimeError, "kvm_read() state failed"); + goto error; + } + state = get_connection_status((int)tcpcb.t_state); + } + else { + state = ""; + } + + // build addr and port + if (dom.dom_family == AF_INET) { + inet_ntop(AF_INET, &inpcb.inp_laddr.s_addr, lip, sizeof(lip)); + inet_ntop(AF_INET, &inpcb.inp_faddr.s_addr, rip, sizeof(rip)); + } + else { + inet_ntop(AF_INET6, &inpcb.in6p_laddr.s6_addr, lip, sizeof(lip)); + inet_ntop(AF_INET6, &inpcb.in6p_faddr.s6_addr, rip, sizeof(rip)); + } + lport = ntohs(inpcb.inp_lport); + rport = ntohs(inpcb.inp_fport); + + // contruct python tuple/list + laddr = Py_BuildValue("(si)", lip, lport); + if (!laddr) + goto error; + if (rport != 0) { + raddr = Py_BuildValue("(si)", rip, rport); + } + else { + raddr = Py_BuildValue("()"); + } + if (!raddr) + goto error; + tuple = Py_BuildValue("(iiiNNs)", i, + dom.dom_family, + proto.pr_type, + laddr, + raddr, + state); + if (!tuple) + goto error; + if (PyList_Append(retList, tuple)) + goto error; + Py_DECREF(tuple); + } + // UNIX socket + else if (dom.dom_family == AF_UNIX) { + struct sockaddr_un sun; + path[0] = '\0'; + + if (kvm_read(kd, (u_long)so.so_pcb, (char *)&unpcb, + sizeof(struct unpcb)) != sizeof(struct unpcb)) { + PyErr_SetString(PyExc_RuntimeError, "kvm_read() unpcb failed"); + goto error; + } + if (unpcb.unp_addr) { + if (kvm_read(kd, (u_long)unpcb.unp_addr, (char *)&sun, + sizeof(sun)) != sizeof(sun)) { + PyErr_SetString(PyExc_RuntimeError, + "kvm_read() sockaddr_un failed"); + goto error; + } + sprintf(path, "%.*s", + (sun.sun_len - (sizeof(sun) - sizeof(sun.sun_path))), + sun.sun_path); + } + + tuple = Py_BuildValue("(iiisOs)", i, + dom.dom_family, + proto.pr_type, + path, + Py_None, + ""); + if (!tuple) + goto error; + if (PyList_Append(retList, tuple)) + goto error; + Py_DECREF(tuple); + Py_INCREF(Py_None); + } + } + } + + free(ofiles); + kvm_close(kd); + return retList; + +error: + Py_XDECREF(tuple); + Py_XDECREF(laddr); + Py_XDECREF(raddr); + Py_DECREF(retList); + + if (kd != NULL) { + kvm_close(kd); + } + if (ofiles != NULL) { + free(ofiles); + } + return NULL; +} + + +/* + * Return a Python list of tuple representing per-cpu times + */ +static PyObject* +get_system_per_cpu_times(PyObject* self, PyObject* args) +{ + static int maxcpus; + int mib[2]; + int ncpu; + size_t len; + size_t size; + int i; + PyObject* py_retlist = PyList_New(0); + PyObject* py_cputime = NULL; + + // retrieve maxcpus value + size = sizeof(maxcpus); + if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) { + Py_DECREF(py_retlist); + PyErr_SetFromErrno(0); + return NULL; + } + long cpu_time[maxcpus][CPUSTATES]; + + // retrieve the number of cpus + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + len = sizeof(ncpu); + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + goto error; + } + + // per-cpu info + size = sizeof(cpu_time); + if (sysctlbyname("kern.cp_times", &cpu_time, &size, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + goto error; + } + + for (i = 0; i < ncpu; i++) { + py_cputime = Py_BuildValue("(ddddd)", + (double)cpu_time[i][CP_USER] / CLOCKS_PER_SEC, + (double)cpu_time[i][CP_NICE] / CLOCKS_PER_SEC, + (double)cpu_time[i][CP_SYS] / CLOCKS_PER_SEC, + (double)cpu_time[i][CP_IDLE] / CLOCKS_PER_SEC, + (double)cpu_time[i][CP_INTR] / CLOCKS_PER_SEC + ); + if (!py_cputime) + goto error; + if (PyList_Append(py_retlist, py_cputime)) + goto error; + Py_DECREF(py_cputime); + } + + return py_retlist; + +error: + Py_XDECREF(py_cputime); + Py_DECREF(py_retlist); + return NULL; +} + + +/* + * Return a list of tuples for every process memory maps. + * 'procstat' cmdline utility has been used as an example. + */ +static PyObject* +get_process_memory_maps(PyObject* self, PyObject* args) +{ + long pid; + int ptrwidth; + int i, cnt; + char addr[30]; + char perms[10]; + const char *path; + struct kinfo_proc kp; + struct kinfo_vmentry *freep = NULL; + struct kinfo_vmentry *kve; + PyObject* pytuple = NULL; + PyObject* retlist = PyList_New(0); + + ptrwidth = 2*sizeof(void *); + + if (! PyArg_ParseTuple(args, "l", &pid)) { + goto error; + } + if (get_kinfo_proc(pid, &kp) == -1) { + goto error; + } + + freep = kinfo_getvmmap(pid, &cnt); + if (freep == NULL) { + PyErr_SetString(PyExc_RuntimeError, "kinfo_getvmmap() failed"); + goto error; + } + for (i = 0; i < cnt; i++) { + pytuple = NULL; + kve = &freep[i]; + addr[0] = '\0'; + perms[0] = '\0'; + sprintf(addr, "%#*jx-%#*jx", ptrwidth, (uintmax_t)kve->kve_start, + ptrwidth, (uintmax_t)kve->kve_end); + strlcat(perms, kve->kve_protection & KVME_PROT_READ ? "r" : "-", + sizeof(perms)); + strlcat(perms, kve->kve_protection & KVME_PROT_WRITE ? "w" : "-", + sizeof(perms)); + strlcat(perms, kve->kve_protection & KVME_PROT_EXEC ? "x" : "-", + sizeof(perms)); + + if (strlen(kve->kve_path) == 0) { + switch (kve->kve_type) { + case KVME_TYPE_NONE: + path = "[none]"; + break; + case KVME_TYPE_DEFAULT: + path = "[default]"; + break; + case KVME_TYPE_VNODE: + path = "[vnode]"; + break; + case KVME_TYPE_SWAP: + path = "[swap]"; + break; + case KVME_TYPE_DEVICE: + path = "[device]"; + break; + case KVME_TYPE_PHYS: + path = "[phys]"; + break; + case KVME_TYPE_DEAD: + path = "[dead]"; + break; + case KVME_TYPE_SG: + path = "[sg]"; + break; + case KVME_TYPE_UNKNOWN: + path = "[unknown]"; + break; + default: + path = "[?]"; + break; + } + } + else { + path = kve->kve_path; + } + + pytuple = Py_BuildValue("sssiiii", + addr, // "start-end" address + perms, // "rwx" permissions + path, // path + kve->kve_resident, // rss + kve->kve_private_resident, // private + kve->kve_ref_count, // ref count + kve->kve_shadow_count // shadow count + ); + if (!pytuple) + goto error; + if (PyList_Append(retlist, pytuple)) + goto error; + Py_DECREF(pytuple); + } + free(freep); + return retlist; + +error: + Py_XDECREF(pytuple); + Py_DECREF(retlist); + if (freep != NULL) + free(freep); + return NULL; +} +#endif + + +/* + * Return a list of tuples including device, mount point and fs type + * for all partitions mounted on the system. + */ +static PyObject* +get_disk_partitions(PyObject* self, PyObject* args) +{ + int num; + int i; + long len; + uint64_t flags; + char opts[200]; + struct statfs *fs = NULL; + PyObject* py_retlist = PyList_New(0); + PyObject* py_tuple = NULL; + + // get the number of mount points + Py_BEGIN_ALLOW_THREADS + num = getfsstat(NULL, 0, MNT_NOWAIT); + Py_END_ALLOW_THREADS + if (num == -1) { + PyErr_SetFromErrno(0); + goto error; + } + + len = sizeof(*fs) * num; + fs = malloc(len); + + Py_BEGIN_ALLOW_THREADS + num = getfsstat(fs, len, MNT_NOWAIT); + Py_END_ALLOW_THREADS + if (num == -1) { + PyErr_SetFromErrno(0); + goto error; + } + + for (i = 0; i < num; i++) { + py_tuple = NULL; + opts[0] = 0; + flags = fs[i].f_flags; + + // see sys/mount.h + if (flags & MNT_RDONLY) + strlcat(opts, "ro", sizeof(opts)); + else + strlcat(opts, "rw", sizeof(opts)); + if (flags & MNT_SYNCHRONOUS) + strlcat(opts, ",sync", sizeof(opts)); + if (flags & MNT_NOEXEC) + strlcat(opts, ",noexec", sizeof(opts)); + if (flags & MNT_NOSUID) + strlcat(opts, ",nosuid", sizeof(opts)); + if (flags & MNT_UNION) + strlcat(opts, ",union", sizeof(opts)); + if (flags & MNT_ASYNC) + strlcat(opts, ",async", sizeof(opts)); + if (flags & MNT_SUIDDIR) + strlcat(opts, ",suiddir", sizeof(opts)); + if (flags & MNT_SOFTDEP) + strlcat(opts, ",softdep", sizeof(opts)); + if (flags & MNT_NOSYMFOLLOW) + strlcat(opts, ",nosymfollow", sizeof(opts)); + if (flags & MNT_GJOURNAL) + strlcat(opts, ",gjournal", sizeof(opts)); + if (flags & MNT_MULTILABEL) + strlcat(opts, ",multilabel", sizeof(opts)); + if (flags & MNT_ACLS) + strlcat(opts, ",acls", sizeof(opts)); + if (flags & MNT_NOATIME) + strlcat(opts, ",noatime", sizeof(opts)); + if (flags & MNT_NOCLUSTERR) + strlcat(opts, ",noclusterr", sizeof(opts)); + if (flags & MNT_NOCLUSTERW) + strlcat(opts, ",noclusterw", sizeof(opts)); + if (flags & MNT_NFS4ACLS) + strlcat(opts, ",nfs4acls", sizeof(opts)); + + py_tuple = Py_BuildValue("(ssss)", fs[i].f_mntfromname, // device + fs[i].f_mntonname, // mount point + fs[i].f_fstypename, // fs type + opts); // options + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_DECREF(py_tuple); + } + + free(fs); + return py_retlist; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + if (fs != NULL) + free(fs); + return NULL; +} + + +/* + * Return a Python list of named tuples with overall network I/O information + */ +static PyObject* +get_network_io_counters(PyObject* self, PyObject* args) +{ + PyObject* py_retdict = PyDict_New(); + PyObject* py_ifc_info = NULL; + + char *buf = NULL, *lim, *next; + struct if_msghdr *ifm; + int mib[6]; + size_t len; + + mib[0] = CTL_NET; // networking subsystem + mib[1] = PF_ROUTE; // type of information + mib[2] = 0; // protocol (IPPROTO_xxx) + mib[3] = 0; // address family + mib[4] = NET_RT_IFLIST; // operation + mib[5] = 0; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { + PyErr_SetFromErrno(0); + goto error; + } + + + buf = malloc(len); + + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + PyErr_SetFromErrno(0); + goto error; + } + + lim = buf + len; + + for (next = buf; next < lim; ) { + py_ifc_info = NULL; + ifm = (struct if_msghdr *)next; + next += ifm->ifm_msglen; + + if (ifm->ifm_type == RTM_IFINFO) { + struct if_msghdr *if2m = (struct if_msghdr *)ifm; + struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1); + char ifc_name[32]; + + strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen); + ifc_name[sdl->sdl_nlen] = 0; + + py_ifc_info = Py_BuildValue("(kkkkkkki)", + if2m->ifm_data.ifi_obytes, + if2m->ifm_data.ifi_ibytes, + if2m->ifm_data.ifi_opackets, + if2m->ifm_data.ifi_ipackets, + if2m->ifm_data.ifi_ierrors, + if2m->ifm_data.ifi_oerrors, + if2m->ifm_data.ifi_iqdrops, + 0); // dropout not supported + if (!py_ifc_info) + goto error; + if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info)) + goto error; + Py_DECREF(py_ifc_info); + } + else { + continue; + } + } + + free(buf); + return py_retdict; + +error: + Py_XDECREF(py_ifc_info); + Py_DECREF(py_retdict); + if (buf != NULL) + free(buf); + return NULL; +} + + +/* + * Return a Python dict of tuples for disk I/O information + */ +static PyObject* +get_disk_io_counters(PyObject* self, PyObject* args) +{ + PyObject* py_retdict = PyDict_New(); + PyObject* py_disk_info = NULL; + + int i; + struct statinfo stats; + + if (devstat_checkversion(NULL) < 0) { + PyErr_Format(PyExc_RuntimeError, "devstat_checkversion() failed"); + goto error; + } + + stats.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo)); + bzero(stats.dinfo, sizeof(struct devinfo)); + + if (devstat_getdevs(NULL, &stats) == -1) { + PyErr_Format(PyExc_RuntimeError, "devstat_getdevs() failed"); + goto error; + } + + for (i = 0; i < stats.dinfo->numdevs; i++) { + py_disk_info = NULL; + struct devstat current; + char disk_name[128]; + current = stats.dinfo->devices[i]; + snprintf(disk_name, sizeof(disk_name), "%s%d", + current.device_name, + current.unit_number); + + py_disk_info = Py_BuildValue("(KKKKLL)", + current.operations[DEVSTAT_READ], // no reads + current.operations[DEVSTAT_WRITE], // no writes + current.bytes[DEVSTAT_READ], // bytes read + current.bytes[DEVSTAT_WRITE], // bytes written + (long long)devstat_compute_etime( + ¤t.duration[DEVSTAT_READ], NULL), // r time + (long long)devstat_compute_etime( + ¤t.duration[DEVSTAT_WRITE], NULL) // w time + ); + if (!py_disk_info) + goto error; + if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info)) + goto error; + Py_DECREF(py_disk_info); + } + + if (stats.dinfo->mem_ptr) { + free(stats.dinfo->mem_ptr); + } + free(stats.dinfo); + return py_retdict; + +error: + Py_XDECREF(py_disk_info); + Py_DECREF(py_retdict); + if (stats.dinfo != NULL) + free(stats.dinfo); + return NULL; +} + + +/* + * Return currently connected users as a list of tuples. + */ +static PyObject* +get_system_users(PyObject* self, PyObject* args) +{ + PyObject *ret_list = PyList_New(0); + PyObject *tuple = NULL; + +#if __FreeBSD_version < 900000 + struct utmp ut; + FILE *fp; + + fp = fopen(_PATH_UTMP, "r"); + if (fp == NULL) { + PyErr_SetFromErrno(0); + goto error; + } + + while (fread(&ut, sizeof(ut), 1, fp) == 1) { + if (*ut.ut_name == '\0') + continue; + tuple = Py_BuildValue("(sssf)", + ut.ut_name, // username + ut.ut_line, // tty + ut.ut_host, // hostname + (float)ut.ut_time // start time + ); + if (!tuple) { + fclose(fp); + goto error; + } + if (PyList_Append(ret_list, tuple)) { + fclose(fp); + goto error; + } + Py_DECREF(tuple); + } + + fclose(fp); +#else + struct utmpx *utx; + + while ((utx = getutxent()) != NULL) { + if (utx->ut_type != USER_PROCESS) + continue; + tuple = Py_BuildValue("(sssf)", + utx->ut_user, // username + utx->ut_line, // tty + utx->ut_host, // hostname + (float)utx->ut_tv.tv_sec // start time + ); + if (!tuple) { + endutxent(); + goto error; + } + if (PyList_Append(ret_list, tuple)) { + endutxent(); + goto error; + } + Py_DECREF(tuple); + } + + endutxent(); +#endif + return ret_list; + +error: + Py_XDECREF(tuple); + Py_DECREF(ret_list); + return NULL; +} + + +/* + * define the psutil C module methods and initialize the module. + */ +static PyMethodDef +PsutilMethods[] = +{ + // --- per-process functions + + {"get_process_name", get_process_name, METH_VARARGS, + "Return process name"}, + {"get_process_connections", get_process_connections, METH_VARARGS, + "Return connections opened by process"}, + {"get_process_exe", get_process_exe, METH_VARARGS, + "Return process pathname executable"}, + {"get_process_cmdline", get_process_cmdline, METH_VARARGS, + "Return process cmdline as a list of cmdline arguments"}, + {"get_process_ppid", get_process_ppid, METH_VARARGS, + "Return process ppid as an integer"}, + {"get_process_uids", get_process_uids, METH_VARARGS, + "Return process real effective and saved user ids as a Python tuple"}, + {"get_process_gids", get_process_gids, METH_VARARGS, + "Return process real effective and saved group ids as a Python tuple"}, + {"get_process_cpu_times", get_process_cpu_times, METH_VARARGS, + "Return tuple of user/kern time for the given PID"}, + {"get_process_create_time", get_process_create_time, METH_VARARGS, + "Return a float indicating the process create time expressed in " + "seconds since the epoch"}, + {"get_process_memory_info", get_process_memory_info, METH_VARARGS, + "Return extended memory info for a process as a Python tuple."}, + {"get_process_num_threads", get_process_num_threads, METH_VARARGS, + "Return number of threads used by process"}, + {"get_process_num_ctx_switches", get_process_num_ctx_switches, METH_VARARGS, + "Return the number of context switches performed by process"}, + {"get_process_threads", get_process_threads, METH_VARARGS, + "Return process threads"}, + {"get_process_status", get_process_status, METH_VARARGS, + "Return process status as an integer"}, + {"get_process_io_counters", get_process_io_counters, METH_VARARGS, + "Return process IO counters"}, + {"get_process_tty_nr", get_process_tty_nr, METH_VARARGS, + "Return process tty (terminal) number"}, +#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 + {"get_process_open_files", get_process_open_files, METH_VARARGS, + "Return files opened by process as a list of (path, fd) tuples"}, + {"get_process_cwd", get_process_cwd, METH_VARARGS, + "Return process current working directory."}, + {"get_process_memory_maps", get_process_memory_maps, METH_VARARGS, + "Return a list of tuples for every process's memory map"}, + {"get_process_num_fds", get_process_num_fds, METH_VARARGS, + "Return the number of file descriptors opened by this process"}, +#endif + + // --- system-related functions + + {"get_pid_list", get_pid_list, METH_VARARGS, + "Returns a list of PIDs currently running on the system"}, + {"get_num_cpus", get_num_cpus, METH_VARARGS, + "Return number of CPUs on the system"}, + {"get_virtual_mem", get_virtual_mem, METH_VARARGS, + "Return system virtual memory usage statistics"}, + {"get_swap_mem", get_swap_mem, METH_VARARGS, + "Return swap mem stats"}, + {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, + "Return system cpu times as a tuple (user, system, nice, idle, irc)"}, +#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 + {"get_system_per_cpu_times", get_system_per_cpu_times, METH_VARARGS, + "Return system per-cpu times as a list of tuples"}, +#endif + {"get_system_boot_time", get_system_boot_time, METH_VARARGS, + "Return a float indicating the system boot time expressed in " + "seconds since the epoch"}, + {"get_disk_partitions", get_disk_partitions, METH_VARARGS, + "Return a list of tuples including device, mount point and " + "fs type for all partitions mounted on the system."}, + {"get_network_io_counters", get_network_io_counters, METH_VARARGS, + "Return dict of tuples of networks I/O information."}, + {"get_disk_io_counters", get_disk_io_counters, METH_VARARGS, + "Return a Python dict of tuples for disk I/O information"}, + {"get_system_users", get_system_users, METH_VARARGS, + "Return currently connected users as a list of tuples"}, + + {NULL, NULL, 0, NULL} +}; + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +#endif + +#if PY_MAJOR_VERSION >= 3 + +static int +psutil_bsd_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +static int +psutil_bsd_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + +static struct PyModuleDef +moduledef = { + PyModuleDef_HEAD_INIT, + "psutil_bsd", + NULL, + sizeof(struct module_state), + PsutilMethods, + NULL, + psutil_bsd_traverse, + psutil_bsd_clear, + NULL +}; + +#define INITERROR return NULL + +PyObject * +PyInit__psutil_bsd(void) + +#else +#define INITERROR return + +void init_psutil_bsd(void) +#endif +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); +#else + PyObject *module = Py_InitModule("_psutil_bsd", PsutilMethods); +#endif + PyModule_AddIntConstant(module, "SSTOP", SSTOP); + PyModule_AddIntConstant(module, "SSLEEP", SSLEEP); + PyModule_AddIntConstant(module, "SRUN", SRUN); + PyModule_AddIntConstant(module, "SIDL", SIDL); + PyModule_AddIntConstant(module, "SWAIT", SWAIT); + PyModule_AddIntConstant(module, "SLOCK", SLOCK); + PyModule_AddIntConstant(module, "SZOMB", SZOMB); + + if (module == NULL) { + INITERROR; + } +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} diff --git a/vendor/psutil/_psutil_bsd.h b/vendor/psutil/_psutil_bsd.h new file mode 100644 index 000000000..3c87a86c7 --- /dev/null +++ b/vendor/psutil/_psutil_bsd.h @@ -0,0 +1,52 @@ +/* + * $Id: _psutil_bsd.h 1498 2012-07-24 21:41:28Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * BSD platform-specific module methods for _psutil_bsd + */ + +#include + +// --- per-process functions + +static PyObject* get_process_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_process_name(PyObject* self, PyObject* args); +static PyObject* get_process_exe(PyObject* self, PyObject* args); +static PyObject* get_process_cmdline(PyObject* self, PyObject* args); +static PyObject* get_process_ppid(PyObject* self, PyObject* args); +static PyObject* get_process_uids(PyObject* self, PyObject* args); +static PyObject* get_process_gids(PyObject* self, PyObject* args); +static PyObject* get_process_connections(PyObject* self, PyObject* args); +static PyObject* get_process_create_time(PyObject* self, PyObject* args); +static PyObject* get_process_memory_info(PyObject* self, PyObject* args); +static PyObject* get_process_num_threads(PyObject* self, PyObject* args); +static PyObject* get_process_num_fds(PyObject* self, PyObject* args); +static PyObject* get_process_threads(PyObject* self, PyObject* args); +static PyObject* get_process_status(PyObject* self, PyObject* args); +static PyObject* get_process_io_counters(PyObject* self, PyObject* args); +static PyObject* get_process_tty_nr(PyObject* self, PyObject* args); +static PyObject* get_process_memory_maps(PyObject* self, PyObject* args); +static PyObject* get_process_num_ctx_switches(PyObject* self, PyObject* args); +#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 +static PyObject* get_process_open_files(PyObject* self, PyObject* args); +static PyObject* get_process_cwd(PyObject* self, PyObject* args); +#endif + +// --- system-related functions + +static PyObject* get_pid_list(PyObject* self, PyObject* args); +static PyObject* get_num_cpus(PyObject* self, PyObject* args); +static PyObject* get_virtual_mem(PyObject* self, PyObject* args); +static PyObject* get_swap_mem(PyObject* self, PyObject* args); +static PyObject* get_system_cpu_times(PyObject* self, PyObject* args); +#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000 +static PyObject* get_system_per_cpu_times(PyObject* self, PyObject* args); +#endif +static PyObject* get_system_boot_time(PyObject* self, PyObject* args); +static PyObject* get_disk_partitions(PyObject* self, PyObject* args); +static PyObject* get_network_io_counters(PyObject* self, PyObject* args); +static PyObject* get_disk_io_counters(PyObject* self, PyObject* args); +static PyObject* get_system_users(PyObject* self, PyObject* args); diff --git a/vendor/psutil/_psutil_common.c b/vendor/psutil/_psutil_common.c new file mode 100644 index 000000000..79aef970f --- /dev/null +++ b/vendor/psutil/_psutil_common.c @@ -0,0 +1,39 @@ +/* + * $Id: _psutil_common.c 1142 2011-10-05 18:45:49Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Routines common to all platforms. + */ + +#include + + +/* + * 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 --git a/vendor/psutil/_psutil_common.h b/vendor/psutil/_psutil_common.h new file mode 100644 index 000000000..cfe94938a --- /dev/null +++ b/vendor/psutil/_psutil_common.h @@ -0,0 +1,13 @@ +/* + * $Id: _psutil_common.h 1142 2011-10-05 18:45:49Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include + +PyObject* NoSuchProcess(void); +PyObject* AccessDenied(void); + diff --git a/vendor/psutil/_psutil_linux.c b/vendor/psutil/_psutil_linux.c new file mode 100644 index 000000000..5093e61d4 --- /dev/null +++ b/vendor/psutil/_psutil_linux.c @@ -0,0 +1,338 @@ +/* + * $Id: _psutil_linux.c 1498 2012-07-24 21:41:28Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Linux-specific functions. + */ + +#include +#include +#include +#include +#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 + + +/* + * Return disk mounted partitions as a list of tuples including device, + * mount point and filesystem type + */ +static PyObject* +get_disk_partitions(PyObject* self, PyObject* args) +{ + FILE *file = NULL; + struct mntent *entry; + PyObject* py_retlist = PyList_New(0); + PyObject* py_tuple = NULL; + + // MOUNTED constant comes from mntent.h and it's == '/etc/mtab' + Py_BEGIN_ALLOW_THREADS + file = setmntent(MOUNTED, "r"); + Py_END_ALLOW_THREADS + if ((file == 0) || (file == NULL)) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + while ((entry = getmntent(file))) { + if (entry == NULL) { + PyErr_Format(PyExc_RuntimeError, "getmntent() failed"); + goto error; + } + py_tuple = Py_BuildValue("(ssss)", entry->mnt_fsname, // device + entry->mnt_dir, // mount point + entry->mnt_type, // fs type + entry->mnt_opts); // options + if (! py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_DECREF(py_tuple); + } + endmntent(file); + return py_retlist; + +error: + if (file != NULL) + endmntent(file); + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + return NULL; +} + + +/* + * A wrapper around sysinfo(), return system memory usage statistics. + */ +static PyObject* +get_sysinfo(PyObject* self, PyObject* args) +{ + struct sysinfo info; + if (sysinfo(&info) != 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return Py_BuildValue("(KKKKKK)", + (unsigned long long)info.totalram * info.mem_unit, // total + (unsigned long long)info.freeram * info.mem_unit, // free + (unsigned long long)info.bufferram * info.mem_unit, // buffer + (unsigned long long)info.sharedram * info.mem_unit, // shared + (unsigned long long)info.totalswap * info.mem_unit, // swap tot + (unsigned long long)info.freeswap * info.mem_unit); // swap free + // TODO: we can also determine BOOT_TIME here +} + + +/* + * Return process CPU affinity as a Python long (the bitmask) + */ +static PyObject* +get_process_cpu_affinity(PyObject* self, PyObject* args) +{ + unsigned long mask; + unsigned int len = sizeof(mask); + long pid; + + if (!PyArg_ParseTuple(args, "i", &pid)) { + return NULL; + } + if (sched_getaffinity(pid, len, (cpu_set_t *)&mask) < 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + return Py_BuildValue("l", mask); +} + + +/* + * Set process CPU affinity; expects a bitmask + */ +static PyObject* +set_process_cpu_affinity(PyObject* self, PyObject* args) +{ + unsigned long mask; + unsigned int len = sizeof(mask); + long pid; + + if (!PyArg_ParseTuple(args, "ll", &pid, &mask)) { + return NULL; + } + if (sched_setaffinity(pid, len, (cpu_set_t *)&mask)) { + return PyErr_SetFromErrno(PyExc_OSError); + } + Py_INCREF(Py_None); + return Py_None; +} + + +/* + * Return currently connected users as a list of tuples. + */ +static PyObject* +get_system_users(PyObject* self, PyObject* args) +{ + PyObject *ret_list = PyList_New(0); + PyObject *tuple = NULL; + PyObject *user_proc = NULL; + struct utmp *ut; + + setutent(); + while (NULL != (ut = getutent())) { + tuple = NULL; + user_proc = NULL; + if (ut->ut_type == USER_PROCESS) + user_proc = Py_True; + else + user_proc = Py_False; + tuple = Py_BuildValue("(sssfO)", + ut->ut_user, // username + ut->ut_line, // tty + ut->ut_host, // hostname + (float)ut->ut_tv.tv_sec, // tstamp + user_proc // (bool) user process + ); + if (! tuple) + goto error; + if (PyList_Append(ret_list, tuple)) + goto error; + Py_DECREF(tuple); + } + endutent(); + return ret_list; + +error: + Py_XDECREF(tuple); + Py_XDECREF(user_proc); + Py_DECREF(ret_list); + endutent(); + return NULL; +} + + +/* + * Define the psutil C module methods and initialize the module. + */ +static PyMethodDef +PsutilMethods[] = +{ +#if HAS_IOPRIO + {"ioprio_get", linux_ioprio_get, METH_VARARGS, + "Get process I/O priority"}, + {"ioprio_set", linux_ioprio_set, METH_VARARGS, + "Set process I/O priority"}, +#endif + {"get_disk_partitions", get_disk_partitions, METH_VARARGS, + "Return disk mounted partitions as a list of tuples including " + "device, mount point and filesystem type"}, + {"get_sysinfo", get_sysinfo, METH_VARARGS, + "A wrapper around sysinfo(), return system memory usage statistics"}, + {"get_process_cpu_affinity", get_process_cpu_affinity, METH_VARARGS, + "Return process CPU affinity as a Python long (the bitmask)."}, + {"set_process_cpu_affinity", set_process_cpu_affinity, METH_VARARGS, + "Set process CPU affinity; expects a bitmask."}, + {"get_system_users", get_system_users, METH_VARARGS, + "Return currently connected users as a list of tuples"}, + + {NULL, NULL, 0, NULL} +}; + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +#endif + +#if PY_MAJOR_VERSION >= 3 + +static int +psutil_linux_traverse(PyObject *m, visitproc visit, void *arg) { + Py_VISIT(GETSTATE(m)->error); + return 0; +} + +static int +psutil_linux_clear(PyObject *m) { + Py_CLEAR(GETSTATE(m)->error); + return 0; +} + +static struct PyModuleDef +moduledef = { + PyModuleDef_HEAD_INIT, + "psutil_linux", + NULL, + sizeof(struct module_state), + PsutilMethods, + NULL, + psutil_linux_traverse, + psutil_linux_clear, + NULL +}; + +#define INITERROR return NULL + +PyObject * +PyInit__psutil_linux(void) + +#else +#define INITERROR return + +void init_psutil_linux(void) +#endif +{ +#if PY_MAJOR_VERSION >= 3 + PyObject *module = PyModule_Create(&moduledef); +#else + PyObject *module = Py_InitModule("_psutil_linux", PsutilMethods); +#endif + if (module == NULL) { + INITERROR; + } +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} diff --git a/vendor/psutil/_psutil_linux.h b/vendor/psutil/_psutil_linux.h new file mode 100644 index 000000000..b62906abf --- /dev/null +++ b/vendor/psutil/_psutil_linux.h @@ -0,0 +1,19 @@ +/* + * $Id: _psutil_linux.h 1498 2012-07-24 21:41:28Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * LINUX specific module methods for _psutil_linux + */ + +#include + +static PyObject* linux_ioprio_get(PyObject* self, PyObject* args); +static PyObject* linux_ioprio_set(PyObject* self, PyObject* args); +static PyObject* get_disk_partitions(PyObject* self, PyObject* args); +static PyObject* get_sysinfo(PyObject* self, PyObject* args); +static PyObject* get_process_cpu_affinity(PyObject* self, PyObject* args); +static PyObject* set_process_cpu_affinity(PyObject* self, PyObject* args); +static PyObject* get_system_users(PyObject* self, PyObject* args); diff --git a/vendor/psutil/_psutil_mswindows.c b/vendor/psutil/_psutil_mswindows.c new file mode 100644 index 000000000..50c5d246a --- /dev/null +++ b/vendor/psutil/_psutil_mswindows.c @@ -0,0 +1,2904 @@ +/* + * $Id: _psutil_mswindows.c 1525 2012-08-16 16:32:03Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Windows platform-specific module methods for _psutil_mswindows + */ + +// Fixes clash between winsock2.h and windows.h +#define WIN32_LEAN_AND_MEAN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Link with Iphlpapi.lib +#pragma comment(lib, "IPHLPAPI.lib") + +#include "_psutil_mswindows.h" +#include "_psutil_common.h" +#include "arch/mswindows/security.h" +#include "arch/mswindows/process_info.h" +#include "arch/mswindows/process_handles.h" +#include "arch/mswindows/ntextapi.h" + + +/* + * Return a Python float representing the system uptime expressed in seconds + * since the epoch. + */ +static PyObject* +get_system_uptime(PyObject* self, PyObject* args) +{ + double uptime; + time_t pt; + FILETIME fileTime; + long long ll; + + GetSystemTimeAsFileTime(&fileTime); + + /* + HUGE thanks to: + http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry + + This function converts the FILETIME structure to the 32 bit + Unix time structure. + The time_t is a 32-bit value for the number of seconds since + January 1, 1970. A FILETIME is a 64-bit for the number of + 100-nanosecond periods since January 1, 1601. Convert by + subtracting the number of 100-nanosecond period betwee 01-01-1970 + and 01-01-1601, from time_t the divide by 1e+7 to get to the same + base granularity. + */ + ll = (((LONGLONG)(fileTime.dwHighDateTime)) << 32) + fileTime.dwLowDateTime; + pt = (time_t)((ll - 116444736000000000ull) / 10000000ull); + + // XXX - By using GetTickCount() time will wrap around to zero if the + // system is run continuously for 49.7 days. + uptime = GetTickCount() / 1000.00f; + return Py_BuildValue("d", (double)pt - uptime); +} + + +/* + * Return 1 if PID exists in the current process list, else 0. + */ +static PyObject* +pid_exists(PyObject* self, PyObject* args) +{ + long pid; + int status; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + status = pid_is_running(pid); + if (-1 == status) { + return NULL; // exception raised in pid_is_running() + } + return PyBool_FromLong(status); +} + + +/* + * Return a Python list of all the PIDs running on the system. + */ +static PyObject* +get_pid_list(PyObject* self, PyObject* args) +{ + DWORD *proclist = NULL; + DWORD numberOfReturnedPIDs; + DWORD i; + PyObject* pid = NULL; + PyObject* retlist = PyList_New(0); + + proclist = get_pids(&numberOfReturnedPIDs); + if (NULL == proclist) { + goto error; + } + + for (i = 0; i < numberOfReturnedPIDs; i++) { + pid = Py_BuildValue("I", proclist[i]); + if (!pid) + goto error; + if (PyList_Append(retlist, pid)) + goto error; + Py_DECREF(pid); + } + + // free C array allocated for PIDs + free(proclist); + return retlist; + +error: + Py_XDECREF(pid); + Py_DECREF(retlist); + if (proclist != NULL) + free(proclist); + return NULL; +} + + +/* + * Kill a process given its PID. + */ +static PyObject* +kill_process(PyObject* self, PyObject* args) +{ + HANDLE hProcess; + long pid; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (pid == 0) { + return AccessDenied(); + } + + hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); + if (hProcess == NULL) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + // see http://code.google.com/p/psutil/issues/detail?id=24 + NoSuchProcess(); + } + else { + PyErr_SetFromWindowsErr(0); + } + return NULL; + } + + // kill the process + if (! TerminateProcess(hProcess, 0)) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hProcess); + return NULL; + } + + CloseHandle(hProcess); + Py_INCREF(Py_None); + return Py_None; +} + + +/* + * Wait for process to terminate and return its exit code. + */ +static PyObject* +process_wait(PyObject* self, PyObject* args) +{ + HANDLE hProcess; + DWORD ExitCode; + DWORD retVal; + long pid; + long timeout; + + if (! PyArg_ParseTuple(args, "ll", &pid, &timeout)) { + return NULL; + } + if (pid == 0) { + return AccessDenied(); + } + + hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid); + if (hProcess == NULL) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + // no such process; we do not want to raise NSP but + // return None instead. + Py_INCREF(Py_None); + return Py_None; + } + else { + PyErr_SetFromWindowsErr(0); + return NULL; + } + } + + // wait until the process has terminated + Py_BEGIN_ALLOW_THREADS + retVal = WaitForSingleObject(hProcess, timeout); + Py_END_ALLOW_THREADS + + if (retVal == WAIT_FAILED) { + CloseHandle(hProcess); + return PyErr_SetFromWindowsErr(GetLastError()); + } + if (retVal == WAIT_TIMEOUT) { + CloseHandle(hProcess); + return Py_BuildValue("l", WAIT_TIMEOUT); + } + + // get the exit code; note: subprocess module (erroneously?) uses + // what returned by WaitForSingleObject + if (GetExitCodeProcess(hProcess, &ExitCode) == 0) { + CloseHandle(hProcess); + return PyErr_SetFromWindowsErr(GetLastError()); + } + CloseHandle(hProcess); +#if PY_MAJOR_VERSION >= 3 + return PyLong_FromLong((long) ExitCode); +#else + return PyInt_FromLong((long) ExitCode); +#endif +} + + +/* + * Return a Python tuple (user_time, kernel_time) + */ +static PyObject* +get_process_cpu_times(PyObject* self, PyObject* args) +{ + long pid; + HANDLE hProcess; + FILETIME ftCreate, ftExit, ftKernel, ftUser; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + hProcess = handle_from_pid(pid); + if (hProcess == NULL) { + return NULL; + } + + if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { + CloseHandle(hProcess); + if (GetLastError() == ERROR_ACCESS_DENIED) { + // usually means the process has died so we throw a NoSuchProcess + // here + return NoSuchProcess(); + } + else { + PyErr_SetFromWindowsErr(0); + return NULL; + } + } + + CloseHandle(hProcess); + + /* + user and kernel times are represented as a FILETIME structure wich contains + a 64-bit value representing the number of 100-nanosecond intervals since + January 1, 1601 (UTC). + http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx + + To convert it into a float representing the seconds that the process has + executed in user/kernel mode I borrowed the code below from Python's + Modules/posixmodule.c + */ + + return Py_BuildValue( + "(dd)", + (double)(ftUser.dwHighDateTime*429.4967296 + \ + ftUser.dwLowDateTime*1e-7), + (double)(ftKernel.dwHighDateTime*429.4967296 + \ + ftKernel.dwLowDateTime*1e-7) + ); +} + + +/* + * Alternative implementation of the one above but bypasses ACCESS DENIED. + */ +static PyObject* +get_process_cpu_times_2(PyObject* self, PyObject* args) +{ + DWORD pid; + PSYSTEM_PROCESS_INFORMATION process; + PVOID buffer; + double user, kernel; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! get_process_info(pid, &process, &buffer)) { + return NULL; + } + user = (double)process->UserTime.HighPart * 429.4967296 + \ + (double)process->UserTime.LowPart * 1e-7; + kernel = (double)process->KernelTime.HighPart * 429.4967296 + \ + (double)process->KernelTime.LowPart * 1e-7; + free(buffer); + return Py_BuildValue("(dd)", user, kernel); +} + + +/* + * Return a Python float indicating the process create time expressed in + * seconds since the epoch. + */ +static PyObject* +get_process_create_time(PyObject* self, PyObject* args) +{ + long pid; + long long unix_time; + DWORD exitCode; + HANDLE hProcess; + BOOL ret; + FILETIME ftCreate, ftExit, ftKernel, ftUser; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + // special case for PIDs 0 and 4, return BOOT_TIME + if (0 == pid || 4 == pid) { + return get_system_uptime(NULL, NULL); + } + + hProcess = handle_from_pid(pid); + if (hProcess == NULL) { + return NULL; + } + + if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { + CloseHandle(hProcess); + if (GetLastError() == ERROR_ACCESS_DENIED) { + // usually means the process has died so we throw a NoSuchProcess here + return NoSuchProcess(); + } + else { + PyErr_SetFromWindowsErr(0); + return NULL; + } + } + + // Make sure the process is not gone as OpenProcess alone seems to be + // unreliable in doing so (it seems a previous call to p.wait() makes + // it unreliable). + // This check is important as creation time is used to make sure the + // process is still running. + ret = GetExitCodeProcess(hProcess, &exitCode); + CloseHandle(hProcess); + if (ret != 0) { + if (exitCode != STILL_ACTIVE) { + return NoSuchProcess(); + } + } + else { + // Ignore access denied as it means the process is still alive. + // For all other errors, we want an exception. + if (GetLastError() != ERROR_ACCESS_DENIED) { + PyErr_SetFromWindowsErr(0); + return NULL; + } + } + + /* + Convert the FILETIME structure to a Unix time. + It's the best I could find by googling and borrowing code here and there. + The time returned has a precision of 1 second. + */ + unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32; + unix_time += ftCreate.dwLowDateTime - 116444736000000000LL; + unix_time /= 10000000; + return Py_BuildValue("d", (double)unix_time); +} + + +/* + * Alternative implementation of the one above but bypasses ACCESS DENIED. + */ +static PyObject* +get_process_create_time_2(PyObject* self, PyObject* args) +{ + DWORD pid; + PSYSTEM_PROCESS_INFORMATION process; + PVOID buffer; + long long unix_time; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! get_process_info(pid, &process, &buffer)) { + return NULL; + } + // special case for PIDs 0 and 4, return BOOT_TIME + if (0 == pid || 4 == pid) { + return get_system_uptime(NULL, NULL); + } + /* + Convert the LARGE_INTEGER union to a Unix time. + It's the best I could find by googling and borrowing code here and there. + The time returned has a precision of 1 second. + */ + unix_time = ((LONGLONG)process->CreateTime.HighPart) << 32; + unix_time += process->CreateTime.LowPart - 116444736000000000LL; + unix_time /= 10000000; + free(buffer); + return Py_BuildValue("d", (double)unix_time); +} + + +/* + * Return a Python integer indicating the number of CPUs on the system. + */ +static PyObject* +get_num_cpus(PyObject* self, PyObject* args) +{ + SYSTEM_INFO system_info; + system_info.dwNumberOfProcessors = 0; + + GetSystemInfo(&system_info); + if (system_info.dwNumberOfProcessors == 0){ + // GetSystemInfo failed for some reason; return 1 as default + return Py_BuildValue("I", 1); + } + return Py_BuildValue("I", system_info.dwNumberOfProcessors); +} + +/* + * Return process name as a Python string. + */ +static PyObject* +get_process_name(PyObject* self, PyObject* args) { + long pid; + int pid_return; + PyObject* name; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + if (pid == 0) { + return Py_BuildValue("s", "System Idle Process"); + } + else if (pid == 4) { + return Py_BuildValue("s", "System"); + } + + pid_return = pid_is_running(pid); + if (pid_return == 0) { + return NoSuchProcess(); + } + if (pid_return == -1) { + return NULL; + } + + name = get_name(pid); + if (name == NULL) { + return NULL; // exception set in get_name() + } + return name; +} + + +/* + * Return process parent pid as a Python integer. + */ +static PyObject* +get_process_ppid(PyObject* self, PyObject* args) { + long pid; + int pid_return; + PyObject* ppid; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if ((pid == 0) || (pid == 4)) { + return Py_BuildValue("l", 0); + } + + pid_return = pid_is_running(pid); + if (pid_return == 0) { + return NoSuchProcess(); + } + if (pid_return == -1) { + return NULL; + } + + ppid = get_ppid(pid); + if (ppid == NULL) { + return NULL; // exception set in get_ppid() + } + return ppid; +} + +/* + * Return process cmdline as a Python list of cmdline arguments. + */ +static PyObject* +get_process_cmdline(PyObject* self, PyObject* args) { + long pid; + int pid_return; + PyObject* arglist; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if ((pid == 0) || (pid == 4)) { + return Py_BuildValue("[]"); + } + + pid_return = pid_is_running(pid); + if (pid_return == 0) { + return NoSuchProcess(); + } + if (pid_return == -1) { + return NULL; + } + + // XXX the assumptio below probably needs to go away + + // May fail any of several ReadProcessMemory calls etc. and not indicate + // a real problem so we ignore any errors and just live without commandline + arglist = get_arg_list(pid); + if ( NULL == arglist ) { + // carry on anyway, clear any exceptions too + PyErr_Clear(); + return Py_BuildValue("[]"); + } + + return arglist; +} + + +/* + * Return process executable path. + */ +static PyObject* +get_process_exe(PyObject* self, PyObject* args) { + long pid; + HANDLE hProcess; + wchar_t exe[MAX_PATH]; + DWORD nSize = MAX_PATH; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + hProcess = handle_from_pid_waccess(pid, PROCESS_QUERY_INFORMATION); + if (NULL == hProcess) { + return NULL; + } + + if (GetProcessImageFileName(hProcess, &exe, nSize) == 0) { + CloseHandle(hProcess); + return PyErr_SetFromWindowsErr(0); + } + + CloseHandle(hProcess); + return Py_BuildValue("s", exe); +} + + +/* + * Return process memory information as a Python tuple. + */ +static PyObject* +get_process_memory_info(PyObject* self, PyObject* args) +{ + HANDLE hProcess; + DWORD pid; +#if (_WIN32_WINNT >= 0x0501) // Windows XP with SP2 + PROCESS_MEMORY_COUNTERS_EX cnt; +#else + PROCESS_MEMORY_COUNTERS cnt; +#endif + SIZE_T private = 0; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + hProcess = handle_from_pid(pid); + if (NULL == hProcess) { + return NULL; + } + + if (! GetProcessMemoryInfo(hProcess, &cnt, sizeof(cnt)) ) { + CloseHandle(hProcess); + return PyErr_SetFromWindowsErr(0); + } + +#if (_WIN32_WINNT >= 0x0501) + private = cnt.PrivateUsage; +#endif + + CloseHandle(hProcess); + +// py 2.4 +#if (PY_MAJOR_VERSION == 2) && (PY_MINOR_VERSION <= 4) + return Py_BuildValue("(kIIIIIIIII)", + cnt.PageFaultCount, + (unsigned int)cnt.PeakWorkingSetSize, + (unsigned int)cnt.WorkingSetSize, + (unsigned int)cnt.QuotaPeakPagedPoolUsage, + (unsigned int)cnt.QuotaPagedPoolUsage, + (unsigned int)cnt.QuotaPeakNonPagedPoolUsage, + (unsigned int)cnt.QuotaNonPagedPoolUsage, + (unsigned int)cnt.PagefileUsage, + (unsigned int)cnt.PeakPagefileUsage, + (unsigned int)private); +#else +// py >= 2.5 + return Py_BuildValue("(knnnnnnnnn)", + cnt.PageFaultCount, + cnt.PeakWorkingSetSize, + cnt.WorkingSetSize, + cnt.QuotaPeakPagedPoolUsage, + cnt.QuotaPagedPoolUsage, + cnt.QuotaPeakNonPagedPoolUsage, + cnt.QuotaNonPagedPoolUsage, + cnt.PagefileUsage, + cnt.PeakPagefileUsage, + private); +#endif +} + + +/* + * Alternative implementation of the one above but bypasses ACCESS DENIED. + */ +static PyObject* +get_process_memory_info_2(PyObject* self, PyObject* args) +{ + DWORD pid; + PSYSTEM_PROCESS_INFORMATION process; + PVOID buffer; + ULONG m0; + SIZE_T m1, m2, m3, m4, m5, m6, m7, m8, m9; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! get_process_info(pid, &process, &buffer)) { + return NULL; + } + m0 = process->PageFaultCount; + m1 = process->PeakWorkingSetSize; + m2 = process->WorkingSetSize; + m3 = process->QuotaPeakPagedPoolUsage; + m4 = process->QuotaPagedPoolUsage; + m5 = process->QuotaPeakNonPagedPoolUsage; + m6 = process->QuotaNonPagedPoolUsage; + m7 = process->PagefileUsage; + m8 = process->PeakPagefileUsage; +#if (_WIN32_WINNT >= 0x0501) + m9 = process->PrivatePageCount; // private me +#else + m9 = 0; +#endif + free(buffer); + +// py 2.4 +#if (PY_MAJOR_VERSION == 2) && (PY_MINOR_VERSION <= 4) + return Py_BuildValue("(kIIIIIIIII)", + (unsigned int)m0, (unsigned int)m1, (unsigned int)m2, (unsigned int)m3, + (unsigned int)m4, (unsigned int)m5, (unsigned int)m6, (unsigned int)m7, + (unsigned int)m8, (unsigned int)m9); +#else + return Py_BuildValue("(knnnnnnnnn)", + m0, m1, m2, m3, m4, m5, m6, m7, m8, m9); +#endif +} + + +/* + * Return a Python integer indicating the total amount of physical memory + * in bytes. + */ +static PyObject* +get_virtual_mem(PyObject* self, PyObject* args) +{ + MEMORYSTATUSEX memInfo; + memInfo.dwLength = sizeof(MEMORYSTATUSEX); + + if (! GlobalMemoryStatusEx(&memInfo) ) { + return PyErr_SetFromWindowsErr(0); + } + + return Py_BuildValue("(LLLLLL)", + memInfo.ullTotalPhys, // total + memInfo.ullAvailPhys, // avail + memInfo.ullTotalPageFile, // total page file + memInfo.ullAvailPageFile, // avail page file + memInfo.ullTotalVirtual, // total virtual + memInfo.ullAvailVirtual // avail virtual + ); +} + + +#define LO_T ((float)1e-7) +#define HI_T (LO_T*4294967296.0) + + +/* + * Return a Python list of tuples representing user, kernel and idle + * CPU times for every CPU on the system. + */ +static PyObject* +get_system_cpu_times(PyObject* self, PyObject* args) +{ + float idle, kernel, user; + typedef DWORD (_stdcall *NTQSI_PROC) (int, PVOID, ULONG, PULONG); + NTQSI_PROC NtQuerySystemInformation; + HINSTANCE hNtDll; + SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; + SYSTEM_INFO si; + UINT i; + PyObject *arg = NULL; + PyObject *retlist = PyList_New(0); + + // dynamic linking is mandatory to use NtQuerySystemInformation + hNtDll = LoadLibrary(TEXT("ntdll.dll")); + if (hNtDll != NULL) { + // gets NtQuerySystemInformation address + NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress( + hNtDll, "NtQuerySystemInformation"); + + if (NtQuerySystemInformation != NULL) + { + // retrives number of processors + GetSystemInfo(&si); + + // allocates an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION + // structures, one per processor + sppi = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ + malloc(si.dwNumberOfProcessors * \ + sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); + if (sppi != NULL) + { + // gets cpu time informations + if (0 == NtQuerySystemInformation( + SystemProcessorPerformanceInformation, + sppi, + si.dwNumberOfProcessors * sizeof + (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), + NULL) + ) + { + // computes system global times summing each processor value + idle = user = kernel = 0; + for (i=0; i= 3 + cwd = PyUnicode_FromObject(cwd_from_wchar); + #else + cwd = PyUnicode_AsUTF8String(cwd_from_wchar); + #endif + if (cwd == NULL) + goto error; + + // decrement the reference count on our temp unicode str to avoid + // mem leak + returnPyObj = Py_BuildValue("N", cwd); + if (!returnPyObj) + goto error; + + Py_DECREF(cwd_from_wchar); + + CloseHandle(processHandle); + free(currentDirectoryContent); + return returnPyObj; + +error: + Py_XDECREF(cwd_from_wchar); + Py_XDECREF(cwd); + Py_XDECREF(returnPyObj); + if (currentDirectoryContent != NULL) + free(currentDirectoryContent); + if (processHandle != NULL) + CloseHandle(processHandle); + return NULL; +} + + +/* + * Resume or suspends a process + */ +int +suspend_resume_process(DWORD pid, int suspend) +{ + // a huge thanks to http://www.codeproject.com/KB/threads/pausep.aspx + HANDLE hThreadSnap = NULL; + THREADENTRY32 te32 = {0}; + + if (pid == 0) { + AccessDenied(); + return FALSE; + } + + hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hThreadSnap == INVALID_HANDLE_VALUE) { + PyErr_SetFromWindowsErr(0); + return FALSE; + } + + // Fill in the size of the structure before using it + te32.dwSize = sizeof(THREADENTRY32); + + if (! Thread32First(hThreadSnap, &te32)) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hThreadSnap); + return FALSE; + } + + // Walk the thread snapshot to find all threads of the process. + // If the thread belongs to the process, add its information + // to the display list. + do + { + if (te32.th32OwnerProcessID == pid) + { + HANDLE hThread = OpenThread(THREAD_SUSPEND_RESUME, FALSE, + te32.th32ThreadID); + if (hThread == NULL) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hThread); + CloseHandle(hThreadSnap); + return FALSE; + } + if (suspend == 1) + { + if (SuspendThread(hThread) == (DWORD)-1) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hThread); + CloseHandle(hThreadSnap); + return FALSE; + } + } + else + { + if (ResumeThread(hThread) == (DWORD)-1) { + PyErr_SetFromWindowsErr(0); + CloseHandle(hThread); + CloseHandle(hThreadSnap); + return FALSE; + } + } + CloseHandle(hThread); + } + } while (Thread32Next(hThreadSnap, &te32)); + + CloseHandle(hThreadSnap); + return TRUE; +} + + +static PyObject* +suspend_process(PyObject* self, PyObject* args) +{ + long pid; + int suspend = 1; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + if (! suspend_resume_process(pid, suspend)) { + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject* +resume_process(PyObject* self, PyObject* args) +{ + long pid; + int suspend = 0; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + if (! suspend_resume_process(pid, suspend)) { + return NULL; + } + Py_INCREF(Py_None); + return Py_None; +} + + +static PyObject* +get_process_num_threads(PyObject* self, PyObject* args) +{ + DWORD pid; + PSYSTEM_PROCESS_INFORMATION process; + PVOID buffer; + int num; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! get_process_info(pid, &process, &buffer)) { + return NULL; + } + num = (int)process->NumberOfThreads; + free(buffer); + return Py_BuildValue("i", num); +} + + +static PyObject* +get_process_threads(PyObject* self, PyObject* args) +{ + PyObject* retList = PyList_New(0); + PyObject* pyTuple = NULL; + HANDLE hThreadSnap = NULL; + HANDLE hThread; + THREADENTRY32 te32 = {0}; + long pid; + int pid_return; + int rc; + FILETIME ftDummy, ftKernel, ftUser; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + goto error; + } + if (pid == 0) { + // raise AD instead of returning 0 as procexp is able to + // retrieve useful information somehow + AccessDenied(); + goto error; + } + + pid_return = pid_is_running(pid); + if (pid_return == 0) { + NoSuchProcess(); + goto error; + } + if (pid_return == -1) { + goto error; + } + + hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hThreadSnap == INVALID_HANDLE_VALUE) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + // Fill in the size of the structure before using it + te32.dwSize = sizeof(THREADENTRY32); + + if (! Thread32First(hThreadSnap, &te32)) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + // Walk the thread snapshot to find all threads of the process. + // If the thread belongs to the process, increase the counter. + do + { + if (te32.th32OwnerProcessID == pid) + { + pyTuple = NULL; + hThread = NULL; + hThread = OpenThread(THREAD_QUERY_INFORMATION, + FALSE, te32.th32ThreadID); + if (hThread == NULL) { + // thread has disappeared on us + continue; + } + + rc = GetThreadTimes(hThread, &ftDummy, &ftDummy, &ftKernel, &ftUser); + if (rc == 0) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + /* + user and kernel times are represented as a FILETIME structure + wich contains a 64-bit value representing the number of + 100-nanosecond intervals since January 1, 1601 (UTC). + http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx + + To convert it into a float representing the seconds that the + process has executed in user/kernel mode I borrowed the code + below from Python's Modules/posixmodule.c + */ + pyTuple = Py_BuildValue("kdd", + te32.th32ThreadID, + (double)(ftUser.dwHighDateTime*429.4967296 + \ + ftUser.dwLowDateTime*1e-7), + (double)(ftKernel.dwHighDateTime*429.4967296 + \ + ftKernel.dwLowDateTime*1e-7)); + if (!pyTuple) + goto error; + if (PyList_Append(retList, pyTuple)) + goto error; + Py_DECREF(pyTuple); + + CloseHandle(hThread); + } + } while (Thread32Next(hThreadSnap, &te32)); + + CloseHandle(hThreadSnap); + return retList; + +error: + Py_XDECREF(pyTuple); + Py_DECREF(retList); + if (hThread != NULL) + CloseHandle(hThread); + if (hThreadSnap != NULL) { + CloseHandle(hThreadSnap); + } + return NULL; +} + + + +static PyObject* +get_process_open_files(PyObject* self, PyObject* args) +{ + long pid; + HANDLE processHandle; + DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; + PyObject* filesList; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + processHandle = handle_from_pid_waccess(pid, access); + if (processHandle == NULL) { + return NULL; + } + + filesList = get_open_files(pid, processHandle); + CloseHandle(processHandle); + if (filesList == NULL) { + return PyErr_SetFromWindowsErr(0); + } + return filesList; +} + + +/* + Accept a filename's drive in native format like "\Device\HarddiskVolume1\" + and return the corresponding drive letter (e.g. "C:\\"). + If no match is found return an empty string. +*/ +static PyObject* +win32_QueryDosDevice(PyObject* self, PyObject* args) +{ + LPCTSTR lpDevicePath; + TCHAR d = TEXT('A'); + TCHAR szBuff[5]; + + if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) { + return NULL; + } + + while(d <= TEXT('Z')) + { + TCHAR szDeviceName[3] = {d,TEXT(':'),TEXT('\0')}; + TCHAR szTarget[512] = {0}; + if (QueryDosDevice(szDeviceName, szTarget, 511) != 0){ + //_tprintf (TEXT("%c:\\ => %s\n"), d, szTarget); + if(_tcscmp(lpDevicePath, szTarget) == 0) { + _stprintf(szBuff, TEXT("%c:"), d); + return Py_BuildValue("s", szBuff); + } + } + d++; + } + 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; + + +#define ConnDecrefPyObjs() Py_DECREF(_AF_INET); \ + Py_DECREF(_AF_INET6);\ + Py_DECREF(_SOCK_STREAM);\ + Py_DECREF(_SOCK_DGRAM); + +/* + * 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 = NULL; + PyObject *af_filter = NULL; + PyObject *type_filter = NULL; + + PyObject *_AF_INET = PyLong_FromLong((long)AF_INET); + PyObject *_AF_INET6 = PyLong_FromLong((long)AF_INET6); + PyObject *_SOCK_STREAM = PyLong_FromLong((long)SOCK_STREAM); + PyObject *_SOCK_DGRAM = PyLong_FromLong((long)SOCK_DGRAM); + + 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 = NULL; + 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 = NULL; + CHAR addressBufferRemote[65]; + PyObject* addressTupleRemote = NULL; + + if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) { + ConnDecrefPyObjs(); + return NULL; + } + + if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) { + ConnDecrefPyObjs(); + PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); + return NULL; + } + + if (pid_is_running(pid) == 0) { + ConnDecrefPyObjs(); + 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"); + FreeLibrary(ntdll); + FreeLibrary(iphlpapi); + } + + if ((getExtendedTcpTable == NULL) || (getExtendedUdpTable == NULL)) { + PyErr_SetString(PyExc_NotImplementedError, + "feature not supported on this Windows version"); + ConnDecrefPyObjs(); + return NULL; + } + + connectionsList = PyList_New(0); + + /* TCP IPv4 */ + + if ((PySequence_Contains(af_filter, _AF_INET) == 1) && + (PySequence_Contains(type_filter, _SOCK_STREAM) == 1)) + { + table = NULL; + connectionTuple = NULL; + addressTupleLocal = NULL; + addressTupleRemote = NULL; + 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); + } + + if (addressTupleLocal == NULL) + goto error; + + // 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); + } + + if (addressTupleRemote == NULL) + goto error; + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET, + SOCK_STREAM, + addressTupleLocal, + addressTupleRemote, + state_to_string(tcp4Table->table[i].dwState) + ); + if (!connectionTuple) + goto error; + if (PyList_Append(connectionsList, connectionTuple)) + goto error; + Py_DECREF(connectionTuple); + } + } + + free(table); + } + + /* TCP IPv6 */ + + if ((PySequence_Contains(af_filter, _AF_INET6) == 1) && + (PySequence_Contains(type_filter, _SOCK_STREAM) == 1)) + { + table = NULL; + connectionTuple = NULL; + addressTupleLocal = NULL; + addressTupleRemote = NULL; + 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); + } + + if (addressTupleLocal == NULL) + goto error; + + // 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); + } + + if (addressTupleRemote == NULL) + goto error; + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET6, + SOCK_STREAM, + addressTupleLocal, + addressTupleRemote, + state_to_string(tcp6Table->table[i].dwState) + ); + if (!connectionTuple) + goto error; + if (PyList_Append(connectionsList, connectionTuple)) + goto error; + Py_DECREF(connectionTuple); + } + } + + free(table); + } + + /* UDP IPv4 */ + + if ((PySequence_Contains(af_filter, _AF_INET) == 1) && + (PySequence_Contains(type_filter, _SOCK_DGRAM) == 1)) + { + table = NULL; + connectionTuple = NULL; + addressTupleLocal = NULL; + addressTupleRemote = NULL; + 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); + } + + if (addressTupleLocal == NULL) + goto error; + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET, + SOCK_DGRAM, + addressTupleLocal, + PyTuple_New(0), + "" + ); + if (!connectionTuple) + goto error; + if (PyList_Append(connectionsList, connectionTuple)) + goto error; + Py_DECREF(connectionTuple); + } + } + + free(table); + } + + /* UDP IPv6 */ + + if ((PySequence_Contains(af_filter, _AF_INET6) == 1) && + (PySequence_Contains(type_filter, _SOCK_DGRAM) == 1)) + { + table = NULL; + connectionTuple = NULL; + addressTupleLocal = NULL; + addressTupleRemote = NULL; + 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); + } + + if (addressTupleLocal == NULL) + goto error; + + connectionTuple = Py_BuildValue("(iiiNNs)", + -1, + AF_INET6, + SOCK_DGRAM, + addressTupleLocal, + PyTuple_New(0), + "" + ); + if (!connectionTuple) + goto error; + if (PyList_Append(connectionsList, connectionTuple)) + goto error; + Py_DECREF(connectionTuple); + } + } + + free(table); + } + + ConnDecrefPyObjs(); + return connectionsList; + +error: + ConnDecrefPyObjs(); + Py_XDECREF(connectionTuple); + Py_XDECREF(addressTupleLocal); + Py_XDECREF(addressTupleRemote); + Py_DECREF(connectionsList); + if (table != NULL) + free(table); + return NULL; +} + + +/* + * 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); + CloseHandle(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); + CloseHandle(hProcess); + 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; + } + hProcess = handle_from_pid(pid); + if (NULL == hProcess) { + return NULL; + } + if (! GetProcessIoCounters(hProcess, &IoCounters)) { + CloseHandle(hProcess); + return PyErr_SetFromWindowsErr(0); + } + CloseHandle(hProcess); + return Py_BuildValue("(KKKK)", IoCounters.ReadOperationCount, + IoCounters.WriteOperationCount, + IoCounters.ReadTransferCount, + IoCounters.WriteTransferCount); +} + + +/* + * Alternative implementation of the one above but bypasses ACCESS DENIED. + */ +static PyObject* +get_process_io_counters_2(PyObject* self, PyObject* args) +{ + DWORD pid; + PSYSTEM_PROCESS_INFORMATION process; + PVOID buffer; + LONGLONG rcount, wcount, rbytes, wbytes; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! get_process_info(pid, &process, &buffer)) { + return NULL; + } + rcount = process->ReadOperationCount.QuadPart; + wcount = process->WriteOperationCount.QuadPart; + rbytes = process->ReadTransferCount.QuadPart; + wbytes = process->WriteTransferCount.QuadPart; + free(buffer); + return Py_BuildValue("KKKK", rcount, wcount, rbytes, wbytes); +} + + +/* + * Return process CPU affinity as a bitmask + */ +static PyObject* +get_process_cpu_affinity(PyObject* self, PyObject* args) +{ + DWORD pid; + HANDLE hProcess; + PDWORD_PTR proc_mask; + PDWORD_PTR system_mask; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + hProcess = handle_from_pid(pid); + if (hProcess == NULL) { + return NULL; + } + if (GetProcessAffinityMask(hProcess, &proc_mask, &system_mask) == 0) { + CloseHandle(hProcess); + return PyErr_SetFromWindowsErr(0); + } + + CloseHandle(hProcess); + return Py_BuildValue("k", (unsigned long)proc_mask); +} + + +/* + * Set process CPU affinity + */ +static PyObject* +set_process_cpu_affinity(PyObject* self, PyObject* args) +{ + DWORD pid; + HANDLE hProcess; + DWORD dwDesiredAccess = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION; + DWORD_PTR mask; + + if (! PyArg_ParseTuple(args, "lk", &pid, &mask)) { + return NULL; + } + hProcess = handle_from_pid_waccess(pid, dwDesiredAccess); + if (hProcess == NULL) { + return NULL; + } + + if (SetProcessAffinityMask(hProcess, mask) == 0) { + CloseHandle(hProcess); + return PyErr_SetFromWindowsErr(0); + } + + CloseHandle(hProcess); + Py_INCREF(Py_None); + return Py_None; +} + + +/* + * 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; + PVOID buffer; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! get_process_info(pid, &process, &buffer)) { + return NULL; + } + for (i = 0; i < process->NumberOfThreads; i++) { + if (process->Threads[i].ThreadState != Waiting || + process->Threads[i].WaitReason != Suspended) + { + free(buffer); + Py_RETURN_FALSE; + } + } + free(buffer); + Py_RETURN_TRUE; +} + + +/* + * Return path's disk total and free as a Python tuple. + */ +static PyObject* +get_disk_usage(PyObject* self, PyObject* args) +{ + BOOL retval; + ULARGE_INTEGER _, total, free; + LPCTSTR path; + + if (! PyArg_ParseTuple(args, "s", &path)) { + return NULL; + } + + Py_BEGIN_ALLOW_THREADS + retval = GetDiskFreeSpaceEx(path, &_, &total, &free); + Py_END_ALLOW_THREADS + if (retval == 0) { + return PyErr_SetFromWindowsErr(0); + } + + return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart); +} + + +/* + * Return a Python list of named tuples with overall network I/O information + */ +static PyObject* +get_network_io_counters(PyObject* self, PyObject* args) +{ + PyObject* py_retdict = PyDict_New(); + PyObject* py_nic_info = NULL; + PyObject* py_pre_nic_name = NULL; + PyObject* py_nic_name = NULL; + + int attempts = 0; + int outBufLen = 15000; + DWORD dwRetVal = 0; + MIB_IFROW *pIfRow = NULL; + ULONG flags = 0; + ULONG family = AF_UNSPEC; + PIP_ADAPTER_ADDRESSES pAddresses = NULL; + PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL; + + do { + pAddresses = (IP_ADAPTER_ADDRESSES *) malloc(outBufLen); + if (pAddresses == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "memory allocation failed for IP_ADAPTER_ADDRESSES struct."); + goto error; + } + + dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, + &outBufLen); + if (dwRetVal == ERROR_BUFFER_OVERFLOW) { + free(pAddresses); + pAddresses = NULL; + } + else { + break; + } + + attempts++; + } while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (attempts < 3)); + + if (dwRetVal != NO_ERROR) { + PyErr_SetString(PyExc_RuntimeError, "GetAdaptersAddresses() failed."); + goto error; + } + + pCurrAddresses = pAddresses; + while (pCurrAddresses) { + py_pre_nic_name = NULL; + py_nic_name = NULL; + py_nic_info = NULL; + pIfRow = (MIB_IFROW *) malloc(sizeof(MIB_IFROW)); + + if (pIfRow == NULL) { + PyErr_SetString(PyExc_RuntimeError, + "memory allocation failed for MIB_IFROW struct."); + goto error; + } + + pIfRow->dwIndex = pCurrAddresses->IfIndex; + dwRetVal = GetIfEntry(pIfRow); + if (dwRetVal != NO_ERROR) { + PyErr_SetString(PyExc_RuntimeError, "GetIfEntry() failed."); + goto error; + } + + py_nic_info = Py_BuildValue("(IIIIIIII)", + pIfRow->dwOutOctets, + pIfRow->dwInOctets, + pIfRow->dwOutUcastPkts, + pIfRow->dwInUcastPkts, + pIfRow->dwInErrors, + pIfRow->dwOutErrors, + pIfRow->dwInDiscards, + pIfRow->dwOutDiscards); + if (!py_nic_info) + goto error; + + py_pre_nic_name = PyUnicode_FromWideChar( + pCurrAddresses->FriendlyName, + wcslen(pCurrAddresses->FriendlyName)); + if (py_pre_nic_name == NULL) + goto error; + py_nic_name = PyUnicode_FromObject(py_pre_nic_name); + if (py_nic_name == NULL) + goto error; + if (PyDict_SetItem(py_retdict, py_nic_name, py_nic_info)) + goto error; + Py_XDECREF(py_pre_nic_name); + Py_XDECREF(py_nic_name); + Py_XDECREF(py_nic_info); + + free(pIfRow); + pCurrAddresses = pCurrAddresses->Next; + } + + free(pAddresses); + return py_retdict; + +error: + Py_XDECREF(py_pre_nic_name); + Py_XDECREF(py_nic_name); + Py_XDECREF(py_nic_info); + Py_DECREF(py_retdict); + if (pAddresses != NULL) + free(pAddresses); + if (pIfRow != NULL) + free(pIfRow); + return NULL; +} + + +/* + * Return a Python dict of tuples for disk I/O information + */ +static PyObject* +get_disk_io_counters(PyObject* self, PyObject* args) +{ + PyObject* py_retdict = PyDict_New(); + PyObject* py_disk_info = NULL; + + DISK_PERFORMANCE diskPerformance; + DWORD dwSize; + HANDLE hDevice = NULL; + char szDevice[MAX_PATH]; + char szDeviceDisplay[MAX_PATH]; + int devNum; + + for (devNum = 0;; devNum++) { + py_disk_info = NULL; + sprintf (szDevice, "\\\\.\\PhysicalDrive%d", devNum); + hDevice = CreateFile (szDevice, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, 0, NULL); + + if (hDevice == INVALID_HANDLE_VALUE) { + // what happens if we get an invalid handle on the first disk? + // we might end up with an empty dict incorrectly in some cases + break; + } + + if (DeviceIoControl(hDevice, IOCTL_DISK_PERFORMANCE, NULL, 0, + &diskPerformance, sizeof(DISK_PERFORMANCE), + &dwSize, NULL)) + { + sprintf (szDeviceDisplay, "PhysicalDrive%d", devNum); + py_disk_info = Py_BuildValue("(IILLLL)", + diskPerformance.ReadCount, + diskPerformance.WriteCount, + diskPerformance.BytesRead, + diskPerformance.BytesWritten, + (diskPerformance.ReadTime.QuadPart + * 10) / 1000, + (diskPerformance.WriteTime.QuadPart + * 10) / 1000); + if (!py_disk_info) + goto error; + if (PyDict_SetItemString(py_retdict, szDeviceDisplay, py_disk_info)) + goto error; + Py_XDECREF(py_disk_info); + } + else { + // XXX we might get here with ERROR_INSUFFICIENT_BUFFER when + // compiling with mingw32; not sure what to do. + //return PyErr_SetFromWindowsErr(0); + ;; + } + + CloseHandle(hDevice); + } + + return py_retdict; + +error: + Py_XDECREF(py_disk_info); + Py_DECREF(py_retdict); + if (hDevice != NULL) + CloseHandle(hDevice); + return NULL; +} + + +static char *get_drive_type(int type) +{ + switch (type) { + case DRIVE_FIXED: + return "fixed"; + case DRIVE_CDROM: + return "cdrom"; + case DRIVE_REMOVABLE: + return "removable"; + case DRIVE_UNKNOWN: + return "unknown"; + case DRIVE_NO_ROOT_DIR: + return "unmounted"; + case DRIVE_REMOTE: + return "remote"; + case DRIVE_RAMDISK: + return "ramdisk"; + default: + return "?"; + } +} + + +#define _ARRAYSIZE(a) (sizeof(a)/sizeof(a[0])) + +/* + * Return disk partitions as a list of tuples such as + * (drive_letter, drive_letter, type, "") + */ +static PyObject* +get_disk_partitions(PyObject* self, PyObject* args) +{ + DWORD num_bytes; + char drive_strings[255]; + char* drive_letter = drive_strings; + int all; + int type; + int ret; + char opts[20]; + LPTSTR fs_type[MAX_PATH + 1] = { 0 }; + DWORD pflags = 0; + PyObject* py_all; + PyObject* py_retlist = PyList_New(0); + PyObject* py_tuple = NULL; + + if (! PyArg_ParseTuple(args, "O", &py_all)) { + goto error; + } + all = PyObject_IsTrue(py_all); + + Py_BEGIN_ALLOW_THREADS + num_bytes = GetLogicalDriveStrings(254, drive_letter); + Py_END_ALLOW_THREADS + + if (num_bytes == 0) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + while (*drive_letter != 0) { + py_tuple = NULL; + opts[0] = 0; + fs_type[0] = 0; + + Py_BEGIN_ALLOW_THREADS + type = GetDriveType(drive_letter); + Py_END_ALLOW_THREADS + + // by default we only show hard drives and cd-roms + if (all == 0) { + if ((type == DRIVE_UNKNOWN) || + (type == DRIVE_NO_ROOT_DIR) || + (type == DRIVE_REMOTE) || + (type == DRIVE_RAMDISK)) { + goto next; + } + // floppy disk: skip it by default as it introduces a + // considerable slowdown. + if ((type == DRIVE_REMOVABLE) && (strcmp(drive_letter, "A:\\") == 0)) { + goto next; + } + } + + ret = GetVolumeInformation(drive_letter, NULL, _ARRAYSIZE(drive_letter), + NULL, NULL, &pflags, fs_type, + _ARRAYSIZE(fs_type)); + if (ret == 0) { + // We might get here in case of a floppy hard drive, in + // which case the error is (21, "device not ready"). + // Let's pretend it didn't happen as we already have + // the drive name and type ('removable'). + strcat(opts, ""); + SetLastError(0); + } + else { + if (pflags & FILE_READ_ONLY_VOLUME) { + strcat(opts, "ro"); + } + else { + strcat(opts, "rw"); + } + if (pflags & FILE_VOLUME_IS_COMPRESSED) { + strcat(opts, ",compressed"); + } + } + + if (strlen(opts) > 0) { + strcat(opts, ","); + } + strcat(opts, get_drive_type(type)); + + py_tuple = Py_BuildValue("(ssss)", + drive_letter, + drive_letter, + fs_type, // either FAT, FAT32, NTFS, HPFS, CDFS, UDF or NWFS + opts); + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_DECREF(py_tuple); + goto next; + + next: + drive_letter = strchr(drive_letter, 0) + 1; + } + + return py_retlist; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + return NULL; +} + + +#ifdef UNICODE +#define WTSOpenServer WTSOpenServerW +#else +#define WTSOpenServer WTSOpenServerA +#endif + + +/* + * Return a Python dict of tuples for disk I/O information + */ +static PyObject* +get_system_users(PyObject* self, PyObject* args) +{ + PyObject* py_retlist = PyList_New(0); + PyObject* py_tuple = NULL; + PyObject* py_address = NULL; + + HANDLE hServer = NULL; + LPTSTR buffer_user = NULL; + LPTSTR buffer_addr = NULL; + PWTS_SESSION_INFO sessions = NULL; + DWORD count; + DWORD i; + DWORD sessionId; + DWORD bytes; + PWTS_CLIENT_ADDRESS address; + char address_str[50]; + long long unix_time; + + PWINSTATIONQUERYINFORMATIONW WinStationQueryInformationW; + WINSTATION_INFO station_info; + HINSTANCE hInstWinSta = NULL; + ULONG returnLen; + + hInstWinSta = LoadLibraryA("winsta.dll"); + WinStationQueryInformationW = (PWINSTATIONQUERYINFORMATIONW) + GetProcAddress(hInstWinSta, "WinStationQueryInformationW"); + + hServer = WTSOpenServer('\0'); + if (hServer == NULL) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + if (WTSEnumerateSessions(hServer, 0, 1, &sessions, &count) == 0) { + PyErr_SetFromWindowsErr(0); + goto error; + } + + for (i=0; iAddressFamily == 0) { // AF_INET + sprintf(address_str, "%u.%u.%u.%u", address->Address[0], + address->Address[1], + address->Address[2], + address->Address[3]); + py_address = Py_BuildValue("s", address_str); + if (!py_address) + goto error; + } + else { + py_address = Py_None; + } + + // login time + if (!WinStationQueryInformationW(hServer, + sessionId, + WinStationInformation, + &station_info, + sizeof(station_info), + &returnLen)) + { + goto error; + } + + unix_time = ((LONGLONG)station_info.ConnectTime.dwHighDateTime) << 32; + unix_time += station_info.ConnectTime.dwLowDateTime - 116444736000000000LL; + unix_time /= 10000000; + + py_tuple = Py_BuildValue("sOd", buffer_user, + py_address, + (double)unix_time); + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_XDECREF(py_address); + Py_XDECREF(py_tuple); + } + + WTSCloseServer(hServer); + WTSFreeMemory(sessions); + WTSFreeMemory(buffer_user); + WTSFreeMemory(buffer_addr); + FreeLibrary(hInstWinSta); + return py_retlist; + +error: + Py_XDECREF(py_tuple); + Py_XDECREF(py_address); + Py_DECREF(py_retlist); + + if (hInstWinSta != NULL) { + FreeLibrary(hInstWinSta); + } + if (hServer != NULL) { + WTSCloseServer(hServer); + } + if (sessions != NULL) { + WTSFreeMemory(sessions); + } + if (buffer_user != NULL) { + WTSFreeMemory(buffer_user); + } + if (buffer_addr != NULL) { + WTSFreeMemory(buffer_addr); + } + return NULL; +} + + +/* + * Return the number of handles opened by process. + */ +static PyObject* +get_process_num_handles(PyObject* self, PyObject* args) +{ + DWORD pid; + HANDLE hProcess; + DWORD handleCount; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + hProcess = handle_from_pid(pid); + if (NULL == hProcess) { + return NULL; + } + if (! GetProcessHandleCount(hProcess, &handleCount)) { + CloseHandle(hProcess); + return PyErr_SetFromWindowsErr(0); + } + CloseHandle(hProcess); + return Py_BuildValue("k", handleCount); +} + + +/* + * Alternative implementation of the one above but bypasses ACCESS DENIED. + */ +static PyObject* +get_process_num_handles_2(PyObject* self, PyObject* args) +{ + DWORD pid; + PSYSTEM_PROCESS_INFORMATION process; + PVOID buffer; + ULONG count; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! get_process_info(pid, &process, &buffer)) { + return NULL; + } + count = process->HandleCount; + free(buffer); + return Py_BuildValue("k", count); +} + + +/* + * Return the number of context switches executed by process. + */ +static PyObject* +get_process_num_ctx_switches(PyObject* self, PyObject* args) +{ + DWORD pid; + PSYSTEM_PROCESS_INFORMATION process; + PVOID buffer; + ULONG i; + ULONG total = 0; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! get_process_info(pid, &process, &buffer)) { + return NULL; + } + for (i=0; i < process->NumberOfThreads; i++) { + total += process->Threads[i].ContextSwitches; + } + free(buffer); + return Py_BuildValue("ki", total, 0); +} + + +static char *get_region_protection_string(ULONG protection) +{ + switch (protection & 0xff) { + case PAGE_NOACCESS: + return ""; + case PAGE_READONLY: + return "r"; + case PAGE_READWRITE: + return "rw"; + case PAGE_WRITECOPY: + return "wc"; + case PAGE_EXECUTE: + return "x"; + case PAGE_EXECUTE_READ: + return "xr"; + case PAGE_EXECUTE_READWRITE: + return "xrw"; + case PAGE_EXECUTE_WRITECOPY: + return "xwc"; + default: + return "?"; + } +} + +/* + * Return a list of process's memory mappings. + */ +static PyObject* +get_process_memory_maps(PyObject* self, PyObject* args) +{ + DWORD pid; + HANDLE hProcess = NULL; + MEMORY_BASIC_INFORMATION basicInfo; + PVOID baseAddress; + PVOID previousAllocationBase; + CHAR mappedFileName[MAX_PATH]; + SYSTEM_INFO system_info; + LPVOID maxAddr; + PyObject* py_list = PyList_New(0); + PyObject* py_tuple = NULL; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + goto error; + } + hProcess = handle_from_pid(pid); + if (NULL == hProcess) { + goto error; + } + + GetSystemInfo(&system_info); + maxAddr = system_info.lpMaximumApplicationAddress; + baseAddress = NULL; + previousAllocationBase = NULL; + + while (VirtualQueryEx(hProcess, baseAddress, &basicInfo, + sizeof(MEMORY_BASIC_INFORMATION))) + { + py_tuple = NULL; + if (baseAddress > maxAddr) { + break; + } + if (GetMappedFileNameA(hProcess, baseAddress, mappedFileName, + sizeof(mappedFileName))) + { + py_tuple = Py_BuildValue("(kssI)", + (unsigned long)baseAddress, + get_region_protection_string(basicInfo.Protect), + mappedFileName, + basicInfo.RegionSize + ); + if (!py_tuple) + goto error; + if (PyList_Append(py_list, py_tuple)) + goto error; + Py_DECREF(py_tuple); + } + previousAllocationBase = basicInfo.AllocationBase; + baseAddress = (PCHAR)baseAddress + basicInfo.RegionSize; + } + + CloseHandle(hProcess); + return py_list; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_list); + if (hProcess != NULL) + CloseHandle(hProcess); + return NULL; +} + +// ------------------------ 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_exe", get_process_exe, METH_VARARGS, + "Return path of the process executable"}, + {"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_process_memory_info", get_process_memory_info, METH_VARARGS, + "Return a tuple of process 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"}, + {"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_cpu_affinity", get_process_cpu_affinity, METH_VARARGS, + "Return process CPU affinity as a bitmask."}, + {"set_process_cpu_affinity", set_process_cpu_affinity, METH_VARARGS, + "Set process CPU affinity."}, + {"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"}, + {"get_process_num_handles", get_process_num_handles, METH_VARARGS, + "Return the number of handles opened by process."}, + {"get_process_num_ctx_switches", get_process_num_ctx_switches, METH_VARARGS, + "Return the number of context switches performed by process."}, + {"get_process_memory_maps", get_process_memory_maps, METH_VARARGS, + "Return a list of process's memory mappings"}, + + // --- alternative pinfo interface + {"get_process_cpu_times_2", get_process_cpu_times_2, METH_VARARGS, + "Alternative implementation"}, + {"get_process_create_time_2", get_process_create_time_2, METH_VARARGS, + "Alternative implementation"}, + {"get_process_num_handles_2", get_process_num_handles_2, METH_VARARGS, + "Alternative implementation"}, + {"get_process_io_counters_2", get_process_io_counters_2, METH_VARARGS, + "Alternative implementation"}, + {"get_process_memory_info_2", get_process_memory_info_2, METH_VARARGS, + "Alternative implementation"}, + + // --- 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_virtual_mem", get_virtual_mem, METH_VARARGS, + "Return the total amount of physical memory, in bytes"}, + {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, + "Return system per-cpu times as a list of tuples"}, + {"get_disk_usage", get_disk_usage, METH_VARARGS, + "Return path's disk total and free as a Python tuple."}, + {"get_network_io_counters", get_network_io_counters, METH_VARARGS, + "Return dict of tuples of networks I/O information."}, + {"get_disk_io_counters", get_disk_io_counters, METH_VARARGS, + "Return dict of tuples of disks I/O information."}, + {"get_system_users", get_system_users, METH_VARARGS, + "Return a list of currently connected users."}, + {"get_disk_partitions", get_disk_partitions, METH_VARARGS, + "Return disk partitions."}, + + + // --- windows API bindings + {"win32_QueryDosDevice", win32_QueryDosDevice, METH_VARARGS, + "QueryDosDevice binding"}, + + {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); + // private constants + PyModule_AddIntConstant(module, "INFINITE", INFINITE); + PyModule_AddIntConstant(module, "ERROR_ACCESS_DENIED", ERROR_ACCESS_DENIED); + SetSeDebug(); + +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} diff --git a/vendor/psutil/_psutil_mswindows.h b/vendor/psutil/_psutil_mswindows.h new file mode 100644 index 000000000..19881a7c9 --- /dev/null +++ b/vendor/psutil/_psutil_mswindows.h @@ -0,0 +1,69 @@ +/* + * $Id: _psutil_mswindows.h 1498 2012-07-24 21:41:28Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Windows platform-specific module methods for _psutil_mswindows + */ + +#include +#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_exe(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_process_memory_info(PyObject* self, PyObject* args); +static PyObject* get_process_cwd(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* 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* get_process_cpu_affinity(PyObject* self, PyObject* args); +static PyObject* set_process_cpu_affinity(PyObject* self, PyObject* args); +static PyObject* get_process_num_handles(PyObject* self, PyObject* args); +static PyObject* get_process_num_ctx_switches(PyObject* self, PyObject* args); +static PyObject* get_process_memory_maps(PyObject* self, PyObject* args); + +static PyObject* get_process_cpu_times_2(PyObject* self, PyObject* args); +static PyObject* get_process_create_time_2(PyObject* self, PyObject* args); +static PyObject* get_process_num_handles_2(PyObject* self, PyObject* args); +static PyObject* get_process_io_counters_2(PyObject* self, PyObject* args); +static PyObject* get_process_memory_info_2(PyObject* self, PyObject* args); + +static PyObject* suspend_process(PyObject* self, PyObject* args); +static PyObject* resume_process(PyObject* self, PyObject* args); +static PyObject* is_process_suspended(PyObject* self, PyObject* args); +static PyObject* process_wait(PyObject* self, PyObject* args); +static PyObject* kill_process(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_virtual_mem(PyObject* self, PyObject* args); +static PyObject* get_system_cpu_times(PyObject* self, PyObject* args); +static PyObject* pid_exists(PyObject* self, PyObject* args); +static PyObject* get_disk_usage(PyObject* self, PyObject* args); +static PyObject* get_disk_partitions(PyObject* self, PyObject* args); +static PyObject* get_network_io_counters(PyObject* self, PyObject* args); +static PyObject* get_disk_io_counters(PyObject* self, PyObject* args); +static PyObject* get_system_users(PyObject* self, PyObject* args); + +// --- windows API bindings + +static PyObject* win32_QueryDosDevice(PyObject* self, PyObject* args); + +// --- internal +int suspend_resume_process(DWORD pid, int suspend); diff --git a/vendor/psutil/_psutil_osx.c b/vendor/psutil/_psutil_osx.c new file mode 100644 index 000000000..5b4da1100 --- /dev/null +++ b/vendor/psutil/_psutil_osx.c @@ -0,0 +1,1792 @@ +/* + * $Id: _psutil_osx.c 1501 2012-07-25 12:57:34Z g.rodola@gmail.com $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * OS X platform-specific module methods for _psutil_osx + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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" + + +/* + * A wrapper around host_statistics() invoked with HOST_VM_INFO. + */ +int +psutil_sys_vminfo(vm_statistics_data_t *vmstat) +{ + kern_return_t ret; + mach_msg_type_number_t count = sizeof(*vmstat) / sizeof(integer_t); + mach_port_t mport = mach_host_self(); + + ret = host_statistics(mport, HOST_VM_INFO, (host_info_t)vmstat, &count); + if (ret != KERN_SUCCESS) { + PyErr_Format(PyExc_RuntimeError, + "host_statistics() failed: %s", mach_error_string(ret)); + return 0; + } + return 1; +} + + +/* + * Return a Python list of all the PIDs running on the system. + */ +static PyObject* +get_pid_list(PyObject* self, PyObject* args) +{ + kinfo_proc *proclist = NULL; + kinfo_proc *orig_address = NULL; + size_t num_processes; + size_t idx; + PyObject *pid = NULL; + PyObject *retlist = PyList_New(0); + + if (get_proc_list(&proclist, &num_processes) != 0) { + PyErr_SetString(PyExc_RuntimeError, "failed to retrieve process list."); + goto error; + } + + if (num_processes > 0) { + // save the address of proclist so we can free it later + orig_address = proclist; + for (idx=0; idx < num_processes; idx++) { + pid = Py_BuildValue("i", proclist->kp_proc.p_pid); + if (!pid) + goto error; + if (PyList_Append(retlist, pid)) + goto error; + Py_DECREF(pid); + proclist++; + } + free(orig_address); + } + return retlist; + +error: + Py_XDECREF(pid); + Py_DECREF(retlist); + if (orig_address != NULL) + free(orig_address); + return NULL; +} + + +/* + * Return process name from kinfo_proc as a Python string. + */ +static PyObject* +get_process_name(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("s", kp.kp_proc.p_comm); +} + + +/* + * Return process current working directory. + */ +static PyObject* +get_process_cwd(PyObject* self, PyObject* args) +{ + long pid; + struct proc_vnodepathinfo pathinfo; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + if (! psutil_proc_pidinfo(pid, PROC_PIDVNODEPATHINFO, &pathinfo, + sizeof(pathinfo))) + { + return NULL; + } + return Py_BuildValue("s", pathinfo.pvi_cdir.vip_path); +} + + +/* + * Return path of the process executable. + */ +static PyObject* +get_process_exe(PyObject* self, PyObject* args) +{ + long pid; + char buf[PATH_MAX]; + int ret; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + ret = proc_pidpath(pid, &buf, sizeof(buf)); + if (ret == 0) { + if (! pid_exists(pid)) { + return NoSuchProcess(); + } + else { + return AccessDenied(); + } + } + return Py_BuildValue("s", buf); +} + +/* + * Return process cmdline as a Python list of cmdline arguments. + */ +static PyObject* +get_process_cmdline(PyObject* self, PyObject* args) +{ + long pid; + PyObject* arglist = NULL; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + // get the commandline, defined in arch/osx/process_info.c + arglist = get_arg_list(pid); + return arglist; +} + + +/* + * Return process parent pid from kinfo_proc as a Python integer. + */ +static PyObject* +get_process_ppid(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("l", (long)kp.kp_eproc.e_ppid); +} + + +/* + * Return process real uid from kinfo_proc as a Python integer. + */ +static PyObject* +get_process_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); +} + + +/* + * 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 process controlling terminal number as an integer. + */ +static PyObject* +get_process_tty_nr(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("i", kp.kp_eproc.e_tdev); +} + + +/* + * Return a list of tuples for every process memory maps. + * 'procstat' cmdline utility has been used as an example. + */ +static PyObject* +get_process_memory_maps(PyObject* self, PyObject* args) +{ + char buf[PATH_MAX]; + char addr_str[34]; + char perms[8]; + int pagesize = getpagesize(); + long pid; + kern_return_t err = KERN_SUCCESS; + mach_port_t task; + uint32_t depth = 1; + vm_address_t address = 0; + vm_size_t size = 0; + + PyObject* py_tuple = NULL; + PyObject* py_list = PyList_New(0); + + if (! PyArg_ParseTuple(args, "l", &pid)) { + goto error; + } + + err = task_for_pid(mach_task_self(), pid, &task); + + if (err != KERN_SUCCESS) { + if (! pid_exists(pid)) { + NoSuchProcess(); + } + else { + // pid exists, so return AccessDenied error since task_for_pid() + // failed + AccessDenied(); + } + goto error; + } + + while (1) { + py_tuple = NULL; + struct vm_region_submap_info_64 info; + mach_msg_type_number_t count = VM_REGION_SUBMAP_INFO_COUNT_64; + + err = vm_region_recurse_64(task, &address, &size, &depth, + (vm_region_info_64_t)&info, &count); + + if (err == KERN_INVALID_ADDRESS) { + break; + } + + if (info.is_submap) { + depth++; + } + else { + // Free/Reset the char[]s to avoid weird paths + memset(buf, 0, sizeof(buf)); + memset(addr_str, 0, sizeof(addr_str)); + memset(perms, 0, sizeof(perms)); + + sprintf(addr_str, "%016x-%016x", address, address + size); + sprintf(perms, "%c%c%c/%c%c%c", + (info.protection & VM_PROT_READ) ? 'r' : '-', + (info.protection & VM_PROT_WRITE) ? 'w' : '-', + (info.protection & VM_PROT_EXECUTE) ? 'x' : '-', + (info.max_protection & VM_PROT_READ) ? 'r' : '-', + (info.max_protection & VM_PROT_WRITE) ? 'w' : '-', + (info.max_protection & VM_PROT_EXECUTE) ? 'x' : '-'); + + address += size; + + err = proc_regionfilename(pid, address, buf, sizeof(buf)); + + if (info.share_mode == SM_COW && info.ref_count == 1) { + // Treat single reference SM_COW as SM_PRIVATE + info.share_mode = SM_PRIVATE; + } + + if (strlen(buf) == 0) { + switch(info.share_mode) { + /* + case SM_LARGE_PAGE: + // Treat SM_LARGE_PAGE the same as SM_PRIVATE + // since they are not shareable and are wired. + */ + case SM_COW: + strcpy(buf, "[cow]"); + break; + case SM_PRIVATE: + strcpy(buf, "[prv]"); + break; + case SM_EMPTY: + strcpy(buf, "[nul]"); + break; + case SM_SHARED: + case SM_TRUESHARED: + strcpy(buf, "[shm]"); + break; + case SM_PRIVATE_ALIASED: + strcpy(buf, "[ali]"); + break; + case SM_SHARED_ALIASED: + strcpy(buf, "[s/a]"); + break; + default: + strcpy(buf, "[???]"); + } + } + + py_tuple = Py_BuildValue("sssIIIIIH", + addr_str, // "start-end" address + perms, // "rwx" permissions + buf, // path + info.pages_resident * pagesize, // rss + info.pages_shared_now_private * pagesize, // private + info.pages_swapped_out * pagesize, // swapped + info.pages_dirtied * pagesize, // dirtied + info.ref_count, // ref count + info.shadow_depth // shadow depth + ); + if (!py_tuple) + goto error; + if (PyList_Append(py_list, py_tuple)) + goto error; + Py_DECREF(py_tuple); + } + } + + return py_list; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_list); + return NULL; +} + + +/* + * Return a Python integer indicating the number of CPUs on the system. + */ +static PyObject* +get_num_cpus(PyObject* self, PyObject* args) +{ + int mib[2]; + int ncpu; + size_t len; + + mib[0] = CTL_HW; + mib[1] = HW_NCPU; + len = sizeof(ncpu); + + if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + + return Py_BuildValue("i", ncpu); +} + + +#define TV2DOUBLE(t) ((t).tv_sec + (t).tv_usec / 1000000.0) + +/* + * Return a Python tuple (user_time, kernel_time) + */ +static PyObject* +get_process_cpu_times(PyObject* self, PyObject* args) +{ + long pid; + struct proc_taskinfo pti; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) { + return NULL; + } + return Py_BuildValue("(dd)", + (float)pti.pti_total_user / 1000000000.0, + (float)pti.pti_total_system / 1000000000.0); +} + + +/* + * Return a Python float indicating the process create time expressed in + * seconds since the epoch. + */ +static PyObject* +get_process_create_time(PyObject* self, PyObject* args) +{ + long pid; + struct kinfo_proc kp; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (get_kinfo_proc(pid, &kp) == -1) { + return NULL; + } + return Py_BuildValue("d", TV2DOUBLE(kp.kp_proc.p_starttime)); +} + + +/* + * Return extended memory info about a process. + */ +static PyObject* +get_process_memory_info(PyObject* self, PyObject* args) +{ + long pid; + struct proc_taskinfo pti; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) { + return NULL; + } + + // Note: determining other memory stats on OSX is a mess: + // http://www.opensource.apple.com/source/top/top-67/libtop.c?txt + // I just give up... + //struct proc_regioninfo pri; + //psutil_proc_pidinfo(pid, PROC_PIDREGIONINFO, &pri, sizeof(pri)) + + return Py_BuildValue("(KKkk)", + pti.pti_resident_size, // resident memory size (rss) + pti.pti_virtual_size, // virtual memory size (vms) + pti.pti_faults, // number of page faults (pages) + pti.pti_pageins // number of actual pageins (pages) + ); +} + + +/* + * Return number of threads used by process as a Python integer. + */ +static PyObject* +get_process_num_threads(PyObject* self, PyObject* args) +{ + long pid; + struct proc_taskinfo pti; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) { + return NULL; + } + return Py_BuildValue("k", pti.pti_threadnum); +} + + +/* + * Return the number of context switches performed by process. + */ +static PyObject* +get_process_num_ctx_switches(PyObject* self, PyObject* args) +{ + long pid; + struct proc_taskinfo pti; + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + if (! psutil_proc_pidinfo(pid, PROC_PIDTASKINFO, &pti, sizeof(pti))) { + return NULL; + } + // unvoluntary value seems not to be available; + // pti.pti_csw probably refers to the sum of the two (getrusage() + // numbers seems to confirm this theory). + return Py_BuildValue("ki", pti.pti_csw, 0); +} + + +/* + * Return system virtual memory stats + */ +static PyObject* +get_virtual_mem(PyObject* self, PyObject* args) +{ + + int mib[2]; + uint64_t total; + size_t len = sizeof(total); + vm_statistics_data_t vm; + int pagesize = getpagesize(); + + // physical mem + mib[0] = CTL_HW; + mib[1] = HW_MEMSIZE; + if (sysctl(mib, 2, &total, &len, NULL, 0)) { + if (errno != 0) + PyErr_SetFromErrno(0); + else + PyErr_Format(PyExc_RuntimeError, "sysctl(HW_MEMSIZE) failed"); + return NULL; + } + + // vm + if (!psutil_sys_vminfo(&vm)) { + return NULL; + } + + return Py_BuildValue("KKKKK", + total, + (unsigned long long) vm.active_count * pagesize, + (unsigned long long) vm.inactive_count * pagesize, + (unsigned long long) vm.wire_count * pagesize, + (unsigned long long) vm.free_count * pagesize + ); +} + + +/* + * Return stats about swap memory. + */ +static PyObject* +get_swap_mem(PyObject* self, PyObject* args) +{ + int mib[2]; + size_t size; + struct xsw_usage totals; + vm_statistics_data_t vmstat; + int pagesize = getpagesize(); + + mib[0] = CTL_VM; + mib[1] = VM_SWAPUSAGE; + size = sizeof(totals); + if (sysctl(mib, 2, &totals, &size, NULL, 0) == -1) { + if (errno != 0) + PyErr_SetFromErrno(0); + else + PyErr_Format(PyExc_RuntimeError, "sysctl(VM_SWAPUSAGE) failed"); + return NULL; + } + if (!psutil_sys_vminfo(&vmstat)) { + return NULL; + } + + return Py_BuildValue("LLLKK", + totals.xsu_total, + totals.xsu_used, + totals.xsu_avail, + (unsigned long long)vmstat.pageins * pagesize, + (unsigned long long)vmstat.pageouts * pagesize); +} + + +/* + * Return a Python tuple representing user, kernel and idle CPU times + */ +static PyObject* +get_system_cpu_times(PyObject* self, PyObject* args) +{ + mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT; + kern_return_t error; + host_cpu_load_info_data_t r_load; + + error = host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&r_load, &count); + if (error != KERN_SUCCESS) { + return PyErr_Format(PyExc_RuntimeError, + "Error in host_statistics(): %s", mach_error_string(error)); + } + + 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 list of tuple representing per-cpu times + */ +static PyObject* +get_system_per_cpu_times(PyObject* self, PyObject* args) +{ + natural_t cpu_count; + processor_info_array_t info_array; + mach_msg_type_number_t info_count; + kern_return_t error; + processor_cpu_load_info_data_t* cpu_load_info = NULL; + PyObject* py_retlist = PyList_New(0); + PyObject* py_cputime = NULL; + int i, ret; + + error = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, + &cpu_count, &info_array, &info_count); + if (error != KERN_SUCCESS) { + PyErr_Format(PyExc_RuntimeError, "Error in host_processor_info(): %s", + mach_error_string(error)); + goto error; + } + + cpu_load_info = (processor_cpu_load_info_data_t*) info_array; + + for (i = 0; i < cpu_count; i++) { + py_cputime = Py_BuildValue("(dddd)", + (double)cpu_load_info[i].cpu_ticks[CPU_STATE_USER] / CLK_TCK, + (double)cpu_load_info[i].cpu_ticks[CPU_STATE_NICE] / CLK_TCK, + (double)cpu_load_info[i].cpu_ticks[CPU_STATE_SYSTEM] / CLK_TCK, + (double)cpu_load_info[i].cpu_ticks[CPU_STATE_IDLE] / CLK_TCK + ); + if (!py_cputime) + goto error; + if (PyList_Append(py_retlist, py_cputime)) + goto error; + Py_DECREF(py_cputime); + } + + ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array, + info_count * sizeof(int)); + if (ret != KERN_SUCCESS) { + PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2); + } + return py_retlist; + +error: + Py_XDECREF(py_cputime); + Py_DECREF(py_retlist); + if (cpu_load_info != NULL) { + ret = vm_deallocate(mach_task_self(), (vm_address_t)info_array, + info_count * sizeof(int)); + if (ret != KERN_SUCCESS) { + PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2); + } + } + return NULL; +} + + +/* + * Return a Python float indicating the system boot time expressed in + * seconds since the epoch. + */ +static PyObject* +get_system_boot_time(PyObject* self, PyObject* args) +{ + /* fetch sysctl "kern.boottime" */ + static int request[2] = { CTL_KERN, KERN_BOOTTIME }; + struct timeval result; + size_t result_len = sizeof result; + time_t boot_time = 0; + + if (sysctl(request, 2, &result, &result_len, NULL, 0) == -1) { + PyErr_SetFromErrno(0); + return NULL; + } + boot_time = result.tv_sec; + return Py_BuildValue("f", (float)boot_time); +} + + +/* + * Return a list of tuples including device, mount point and fs type + * for all partitions mounted on the system. + */ +static PyObject* +get_disk_partitions(PyObject* self, PyObject* args) +{ + int num; + int i; + long len; + uint64_t flags; + char opts[400]; + struct statfs *fs = NULL; + PyObject* py_retlist = PyList_New(0); + PyObject* py_tuple = NULL; + + // get the number of mount points + Py_BEGIN_ALLOW_THREADS + num = getfsstat(NULL, 0, MNT_NOWAIT); + Py_END_ALLOW_THREADS + if (num == -1) { + PyErr_SetFromErrno(0); + goto error; + } + + len = sizeof(*fs) * num; + fs = malloc(len); + + Py_BEGIN_ALLOW_THREADS + num = getfsstat(fs, len, MNT_NOWAIT); + Py_END_ALLOW_THREADS + if (num == -1) { + PyErr_SetFromErrno(0); + goto error; + } + + for (i = 0; i < num; i++) { + opts[0] = 0; + flags = fs[i].f_flags; + + // see sys/mount.h + if (flags & MNT_RDONLY) + strlcat(opts, "ro", sizeof(opts)); + else + strlcat(opts, "rw", sizeof(opts)); + if (flags & MNT_SYNCHRONOUS) + strlcat(opts, ",sync", sizeof(opts)); + if (flags & MNT_NOEXEC) + strlcat(opts, ",noexec", sizeof(opts)); + if (flags & MNT_NOSUID) + strlcat(opts, ",nosuid", sizeof(opts)); + if (flags & MNT_UNION) + strlcat(opts, ",union", sizeof(opts)); + if (flags & MNT_ASYNC) + strlcat(opts, ",async", sizeof(opts)); + if (flags & MNT_EXPORTED) + strlcat(opts, ",exported", sizeof(opts)); + if (flags & MNT_QUARANTINE) + strlcat(opts, ",quarantine", sizeof(opts)); + if (flags & MNT_LOCAL) + strlcat(opts, ",local", sizeof(opts)); + if (flags & MNT_QUOTA) + strlcat(opts, ",quota", sizeof(opts)); + if (flags & MNT_ROOTFS) + strlcat(opts, ",rootfs", sizeof(opts)); + if (flags & MNT_DOVOLFS) + strlcat(opts, ",dovolfs", sizeof(opts)); + if (flags & MNT_DONTBROWSE) + strlcat(opts, ",dontbrowse", sizeof(opts)); + if (flags & MNT_IGNORE_OWNERSHIP) + strlcat(opts, ",ignore-ownership", sizeof(opts)); + if (flags & MNT_AUTOMOUNTED) + strlcat(opts, ",automounted", sizeof(opts)); + if (flags & MNT_JOURNALED) + strlcat(opts, ",journaled", sizeof(opts)); + if (flags & MNT_NOUSERXATTR) + strlcat(opts, ",nouserxattr", sizeof(opts)); + if (flags & MNT_DEFWRITE) + strlcat(opts, ",defwrite", sizeof(opts)); + if (flags & MNT_MULTILABEL) + strlcat(opts, ",multilabel", sizeof(opts)); + if (flags & MNT_NOATIME) + strlcat(opts, ",noatime", sizeof(opts)); + if (flags & MNT_UPDATE) + strlcat(opts, ",update", sizeof(opts)); + if (flags & MNT_RELOAD) + strlcat(opts, ",reload", sizeof(opts)); + if (flags & MNT_FORCE) + strlcat(opts, ",force", sizeof(opts)); + if (flags & MNT_CMDFLAGS) + strlcat(opts, ",cmdflags", sizeof(opts)); + + py_tuple = Py_BuildValue("(ssss)", fs[i].f_mntfromname, // device + fs[i].f_mntonname, // mount point + fs[i].f_fstypename, // fs type + opts); // options + if (!py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_DECREF(py_tuple); + } + + free(fs); + return py_retlist; + +error: + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + if (fs != NULL) + free(fs); + return NULL; +} + + +/* + * Return 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) +{ + long pid; + int err, j, ret; + 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 = NULL; + 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)) { + goto error; + } + + // task_for_pid() requires special privileges + err = task_for_pid(mach_task_self(), pid, &task); + if (err != KERN_SUCCESS) { + if (! pid_exists(pid)) { + NoSuchProcess(); + } + else { + AccessDenied(); + } + goto error; + } + + 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) { + AccessDenied(); + } + else { + // otherwise throw a runtime error with appropriate error code + PyErr_Format(PyExc_RuntimeError, "task_info(TASK_BASIC_INFO) failed"); + } + goto error; + } + + err = task_threads(task, &thread_list, &thread_count); + if (err != KERN_SUCCESS) { + PyErr_Format(PyExc_RuntimeError, "task_threads() failed"); + goto error; + } + + for (j = 0; j < thread_count; j++) { + pyTuple = NULL; + 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) { + PyErr_Format(PyExc_RuntimeError, "thread_info() failed"); + goto error; + } + 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 + ); + if (!pyTuple) + goto error; + if (PyList_Append(retList, pyTuple)) + goto error; + Py_DECREF(pyTuple); + } + + ret = vm_deallocate(task, (vm_address_t)thread_list, + thread_count * sizeof(int)); + if (ret != KERN_SUCCESS) { + PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2); + } + + return retList; + +error: + Py_XDECREF(pyTuple); + Py_DECREF(retList); + if (thread_list != NULL) { + ret = vm_deallocate(task, (vm_address_t)thread_list, + thread_count * sizeof(int)); + if (ret != KERN_SUCCESS) { + PyErr_WarnEx(PyExc_RuntimeWarning, "vm_deallocate() failed", 2); + } + } + return NULL; +} + + +/* + * 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 = NULL; + struct proc_fdinfo *fdp_pointer; + struct vnode_fdinfowithpath vi; + + PyObject *retList = PyList_New(0); + PyObject *tuple = NULL; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + goto error; + } + + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); + if (pidinfo_result <= 0) { + // may be be ignored later if errno != 0 + PyErr_Format(PyExc_RuntimeError, "proc_pidinfo(PROC_PIDLISTFDS) failed"); + goto error; + } + + fds_pointer = malloc(pidinfo_result); + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, + pidinfo_result); + if (pidinfo_result <= 0) { + // may be be ignored later if errno != 0 + PyErr_Format(PyExc_RuntimeError, "proc_pidinfo(PROC_PIDLISTFDS) failed"); + goto error; + } + + iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE); + + for (i = 0; i < iterations; i++) { + tuple = NULL; + 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; + } + // may be be ignored later if errno != 0 + PyErr_Format(PyExc_RuntimeError, + "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed"); + goto error; + } + if (nb < sizeof(vi)) { + PyErr_Format(PyExc_RuntimeError, + "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed (buffer mismatch)"); + goto error; + } + // --- /errors checking + + // --- construct python list + tuple = Py_BuildValue("(si)", vi.pvip.vip_path, + (int)fdp_pointer->proc_fd); + if (!tuple) + goto error; + if (PyList_Append(retList, tuple)) + goto error; + Py_DECREF(tuple); + // --- /construct python list + } + } + + free(fds_pointer); + return retList; + +error: + Py_XDECREF(tuple); + Py_DECREF(retList); + if (fds_pointer != NULL) { + free(fds_pointer); + } + if (errno != 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + else if (! pid_exists(pid)) { + return NoSuchProcess(); + } + else { + // exception has already been set earlier + return NULL; + } +} + + +/* + * mathes Linux net/tcp_states.h: + * http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h + */ +static char * +get_connection_status(int st) { + switch (st) { + case TCPS_CLOSED: + return "CLOSE"; + case TCPS_CLOSING: + return "CLOSING"; + case TCPS_CLOSE_WAIT: + return "CLOSE_WAIT"; + case TCPS_LISTEN: + return "LISTEN"; + case TCPS_ESTABLISHED: + return "ESTABLISHED"; + case TCPS_SYN_SENT: + return "SYN_SENT"; + case TCPS_SYN_RECEIVED: + return "SYN_RECV"; + case TCPS_FIN_WAIT_1: + return "FIN_WAIT_1"; + case TCPS_FIN_WAIT_2: + return "FIN_WAIT_2"; + case TCPS_LAST_ACK: + return "LAST_ACK"; + case TCPS_TIME_WAIT: + return "TIME_WAIT"; + default: + return ""; + } +} + + +/* + * 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 = NULL; + struct proc_fdinfo *fdp_pointer; + struct socket_fdinfo si; + + PyObject *retList = PyList_New(0); + PyObject *tuple = NULL; + PyObject *laddr = NULL; + PyObject *raddr = NULL; + PyObject *af_filter = NULL; + PyObject *type_filter = NULL; + + if (! PyArg_ParseTuple(args, "lOO", &pid, &af_filter, &type_filter)) { + goto error; + } + + if (!PySequence_Check(af_filter) || !PySequence_Check(type_filter)) { + PyErr_SetString(PyExc_TypeError, "arg 2 or 3 is not a sequence"); + goto error; + } + + if (pid == 0) { + return retList; + } + + 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); + + if (pidinfo_result <= 0) { + goto error; + } + + iterations = (pidinfo_result / PROC_PIDLISTFD_SIZE); + + for (i = 0; i < iterations; i++) { + tuple = NULL; + laddr = NULL; + raddr = NULL; + 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) { + PyErr_SetFromErrno(PyExc_OSError); + } + else { + PyErr_Format(PyExc_RuntimeError, + "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed"); + } + goto error; + } + if (nb < sizeof(si)) { + PyErr_Format(PyExc_RuntimeError, + "proc_pidinfo(PROC_PIDFDVNODEPATHINFO) failed (buffer mismatch)"); + goto error; + } + // --- /errors checking + + // + int fd, family, type, lport, rport; + char lip[200], rip[200]; + char *state; + int inseq; + PyObject* _family; + PyObject* _type; + + fd = (int)fdp_pointer->proc_fd; + family = si.psi.soi_family; + type = si.psi.soi_type; + + // apply filters + _family = PyLong_FromLong((long)family); + inseq = PySequence_Contains(af_filter, _family); + Py_DECREF(_family); + if (inseq == 0) { + continue; + } + _type = PyLong_FromLong((long)type); + inseq = PySequence_Contains(type_filter, _type); + Py_DECREF(_type); + if (inseq == 0) { + continue; + } + + if (errno != 0) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + if ((family == AF_INET) || (family == AF_INET6)) { + 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(rip)); + } + 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, + rip, sizeof(rip)); + } + + // check for inet_ntop failures + if (errno != 0) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + 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 (!laddr) + goto error; + if (rport != 0) { + raddr = Py_BuildValue("(si)", rip, rport); + } + else { + raddr = Py_BuildValue("()"); + } + if (!raddr) + goto error; + + // construct the python list + tuple = Py_BuildValue("(iiiNNs)", fd, family, type, laddr, raddr, + state); + if (!tuple) + goto error; + if (PyList_Append(retList, tuple)) + goto error; + Py_DECREF(tuple); + } + else if (family == AF_UNIX) { + // construct the python list + tuple = Py_BuildValue("(iiisss)", + fd, family, type, + si.psi.soi_proto.pri_un.unsi_addr.ua_sun.sun_path, + si.psi.soi_proto.pri_un.unsi_caddr.ua_sun.sun_path, + ""); + if (!tuple) + goto error; + if (PyList_Append(retList, tuple)) + goto error; + Py_DECREF(tuple); + } + } + } + + free(fds_pointer); + return retList; + +error: + Py_XDECREF(tuple); + Py_XDECREF(laddr); + Py_XDECREF(raddr); + Py_DECREF(retList); + + if (fds_pointer != NULL) { + free(fds_pointer); + } + 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"); + } +} + + +/* + * Return number of file descriptors opened by process. + */ +static PyObject* +get_process_num_fds(PyObject* self, PyObject* args) +{ + long pid; + int pidinfo_result; + int num; + struct proc_fdinfo *fds_pointer; + + if (! PyArg_ParseTuple(args, "l", &pid)) { + return NULL; + } + + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, NULL, 0); + if (pidinfo_result <= 0) { + return PyErr_SetFromErrno(PyExc_OSError); + } + + fds_pointer = malloc(pidinfo_result); + pidinfo_result = proc_pidinfo(pid, PROC_PIDLISTFDS, 0, fds_pointer, + pidinfo_result); + if (pidinfo_result <= 0) { + free(fds_pointer); + return PyErr_SetFromErrno(PyExc_OSError); + } + + num = (pidinfo_result / PROC_PIDLISTFD_SIZE); + free(fds_pointer); + return Py_BuildValue("i", num); +} + + +/* + * Return a Python list of named tuples with overall network I/O information + */ +static PyObject* +get_network_io_counters(PyObject* self, PyObject* args) +{ + PyObject* py_retdict = PyDict_New(); + PyObject* py_ifc_info = NULL; + + char *buf = NULL, *lim, *next; + struct if_msghdr *ifm; + int mib[6]; + size_t len; + + mib[0] = CTL_NET; // networking subsystem + mib[1] = PF_ROUTE; // type of information + mib[2] = 0; // protocol (IPPROTO_xxx) + mib[3] = 0; // address family + mib[4] = NET_RT_IFLIST2; // operation + mib[5] = 0; + + if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) { + PyErr_SetFromErrno(0); + goto error; + } + + buf = malloc(len); + + if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) { + PyErr_SetFromErrno(0); + goto error; + } + + lim = buf + len; + + for (next = buf; next < lim; ) { + ifm = (struct if_msghdr *)next; + next += ifm->ifm_msglen; + + if (ifm->ifm_type == RTM_IFINFO2) { + py_ifc_info = NULL; + struct if_msghdr2 *if2m = (struct if_msghdr2 *)ifm; + struct sockaddr_dl *sdl = (struct sockaddr_dl *)(if2m + 1); + char ifc_name[32]; + + strncpy(ifc_name, sdl->sdl_data, sdl->sdl_nlen); + ifc_name[sdl->sdl_nlen] = 0; + + py_ifc_info = Py_BuildValue("(KKKKKKKi)", + if2m->ifm_data.ifi_obytes, + if2m->ifm_data.ifi_ibytes, + if2m->ifm_data.ifi_opackets, + if2m->ifm_data.ifi_ipackets, + if2m->ifm_data.ifi_ierrors, + if2m->ifm_data.ifi_oerrors, + if2m->ifm_data.ifi_iqdrops, + 0); // dropout not supported + + if (!py_ifc_info) + goto error; + if (PyDict_SetItemString(py_retdict, ifc_name, py_ifc_info)) + goto error; + Py_DECREF(py_ifc_info); + } + else { + continue; + } + } + + free(buf); + return py_retdict; + +error: + Py_XDECREF(py_ifc_info); + Py_DECREF(py_retdict); + if (buf != NULL) + free(buf); + return NULL; +} + + +/* + * Return a Python dict of tuples for disk I/O information + */ +static PyObject* +get_disk_io_counters(PyObject* self, PyObject* args) +{ + PyObject* py_retdict = PyDict_New(); + PyObject* py_disk_info = NULL; + + CFDictionaryRef parent_dict; + CFDictionaryRef props_dict; + CFDictionaryRef stats_dict; + io_registry_entry_t parent; + io_registry_entry_t disk; + io_iterator_t disk_list; + + /* Get list of disks */ + if (IOServiceGetMatchingServices(kIOMasterPortDefault, + IOServiceMatching(kIOMediaClass), + &disk_list) != kIOReturnSuccess) { + PyErr_SetString(PyExc_RuntimeError, "Unable to get the list of disks."); + goto error; + } + + /* Iterate over disks */ + while ((disk = IOIteratorNext(disk_list)) != 0) { + py_disk_info = NULL; + parent_dict = NULL; + props_dict = NULL; + stats_dict = NULL; + + if (IORegistryEntryGetParentEntry(disk, kIOServicePlane, &parent) != kIOReturnSuccess) { + PyErr_SetString(PyExc_RuntimeError, "Unable to get the disk's parent."); + IOObjectRelease(disk); + goto error; + } + + if (IOObjectConformsTo(parent, "IOBlockStorageDriver")) { + if(IORegistryEntryCreateCFProperties( + disk, + (CFMutableDictionaryRef *) &parent_dict, + kCFAllocatorDefault, + kNilOptions) != kIOReturnSuccess) + { + PyErr_SetString(PyExc_RuntimeError, + "Unable to get the parent's properties."); + IOObjectRelease(disk); + IOObjectRelease(parent); + goto error; + } + + if (IORegistryEntryCreateCFProperties(parent, + (CFMutableDictionaryRef *) &props_dict, + kCFAllocatorDefault, + kNilOptions) != kIOReturnSuccess) + { + PyErr_SetString(PyExc_RuntimeError, + "Unable to get the disk properties."); + CFRelease(props_dict); + IOObjectRelease(disk); + IOObjectRelease(parent); + goto error; + } + + const int kMaxDiskNameSize = 64; + CFStringRef disk_name_ref = (CFStringRef)CFDictionaryGetValue( + parent_dict, + CFSTR(kIOBSDNameKey)); + char disk_name[kMaxDiskNameSize]; + + CFStringGetCString(disk_name_ref, + disk_name, + kMaxDiskNameSize, + CFStringGetSystemEncoding()); + + stats_dict = (CFDictionaryRef)CFDictionaryGetValue( + props_dict, + CFSTR(kIOBlockStorageDriverStatisticsKey)); + + if (stats_dict == NULL) { + PyErr_SetString(PyExc_RuntimeError, "Unable to get disk stats."); + goto error; + } + + CFNumberRef number; + int64_t reads, writes, read_bytes, write_bytes, read_time, write_time = 0; + + /* Get disk reads/writes */ + if ((number = (CFNumberRef)CFDictionaryGetValue( + stats_dict, + CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) + { + CFNumberGetValue(number, kCFNumberSInt64Type, &reads); + } + if ((number = (CFNumberRef)CFDictionaryGetValue( + stats_dict, + CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) + { + CFNumberGetValue(number, kCFNumberSInt64Type, &writes); + } + + /* Get disk bytes read/written */ + if ((number = (CFNumberRef)CFDictionaryGetValue( + stats_dict, + CFSTR(kIOBlockStorageDriverStatisticsBytesReadKey)))) + { + CFNumberGetValue(number, kCFNumberSInt64Type, &read_bytes); + } + if ((number = (CFNumberRef)CFDictionaryGetValue( + stats_dict, + CFSTR(kIOBlockStorageDriverStatisticsBytesWrittenKey)))) + { + CFNumberGetValue(number, kCFNumberSInt64Type, &write_bytes); + } + + /* Get disk time spent reading/writing (nanoseconds) */ + if ((number = (CFNumberRef)CFDictionaryGetValue( + stats_dict, + CFSTR(kIOBlockStorageDriverStatisticsTotalReadTimeKey)))) + { + CFNumberGetValue(number, kCFNumberSInt64Type, &read_time); + } + if ((number = (CFNumberRef)CFDictionaryGetValue( + stats_dict, + CFSTR(kIOBlockStorageDriverStatisticsTotalWriteTimeKey)))) { + CFNumberGetValue(number, kCFNumberSInt64Type, &write_time); + } + + // Read/Write time on OS X comes back in nanoseconds and in psutil + // we've standardized on milliseconds so do the conversion. + py_disk_info = Py_BuildValue("(KKKKKK)", + reads, writes, + read_bytes, write_bytes, + read_time / 1000, write_time / 1000); + if (!py_disk_info) + goto error; + if (PyDict_SetItemString(py_retdict, disk_name, py_disk_info)) + goto error; + Py_DECREF(py_disk_info); + + CFRelease(parent_dict); + IOObjectRelease(parent); + CFRelease(props_dict); + IOObjectRelease(disk); + } + } + + IOObjectRelease (disk_list); + + return py_retdict; + +error: + Py_XDECREF(py_disk_info); + Py_DECREF(py_retdict); + return NULL; +} + + +/* + * Return currently connected users as a list of tuples. + */ +static PyObject* +get_system_users(PyObject* self, PyObject* args) +{ + PyObject *ret_list = PyList_New(0); + PyObject *tuple = NULL; + struct utmpx ut; + FILE *fp = NULL; + + fp = fopen(_PATH_UTMPX, "r"); + if (fp == NULL) { + // man fopen says errno is set but it seems it's not (OSX 10.6) + PyErr_SetFromErrnoWithFilename(PyExc_OSError, _PATH_UTMPX); + goto error; + } + + while (fread(&ut, sizeof(ut), 1, fp) == 1) { + if (*ut.ut_user == '\0') { + continue; + } +#ifdef UTMPX_USER_PROCESS + if (ut.ut_type != UTMPX_USER_PROCESS) { + continue; + } +#endif + tuple = Py_BuildValue("(sssf)", + ut.ut_user, // username + ut.ut_line, // tty + ut.ut_host, // hostname + (float)ut.ut_tv.tv_sec // login time + ); + if (!tuple) + goto error; + if (PyList_Append(ret_list, tuple)) + goto error; + Py_DECREF(tuple); + } + + fclose(fp); + return ret_list; + +error: + Py_XDECREF(tuple); + Py_DECREF(ret_list); + if (fp != NULL) + fclose(fp); + return NULL; +} + + +/* + * define the psutil C module methods and initialize the module. + */ +static PyMethodDef +PsutilMethods[] = +{ + // --- per-process functions + + {"get_process_name", get_process_name, METH_VARARGS, + "Return process name"}, + {"get_process_cmdline", get_process_cmdline, METH_VARARGS, + "Return process cmdline as a list of cmdline arguments"}, + {"get_process_exe", get_process_exe, METH_VARARGS, + "Return path of the process executable"}, + {"get_process_cwd", get_process_cwd, METH_VARARGS, + "Return process current working directory."}, + {"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_process_cpu_times", get_process_cpu_times, METH_VARARGS, + "Return tuple of user/kern time for the given PID"}, + {"get_process_create_time", get_process_create_time, METH_VARARGS, + "Return a float indicating the process create time expressed in " + "seconds since the epoch"}, + {"get_process_memory_info", get_process_memory_info, METH_VARARGS, + "Return memory information about a process"}, + {"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_num_fds", get_process_num_fds, METH_VARARGS, + "Return the number of fds opened by process."}, + {"get_process_num_ctx_switches", get_process_num_ctx_switches, METH_VARARGS, + "Return the number of context switches performed by process"}, + {"get_process_connections", get_process_connections, METH_VARARGS, + "Get process TCP and UDP connections as a list of tuples"}, + {"get_process_tty_nr", get_process_tty_nr, METH_VARARGS, + "Return process tty number as an integer"}, + {"get_process_memory_maps", get_process_memory_maps, METH_VARARGS, + "Return a list of tuples for every process's memory map"}, + + // --- system-related functions + + {"get_pid_list", get_pid_list, METH_VARARGS, + "Returns a list of PIDs currently running on the system"}, + {"get_num_cpus", get_num_cpus, METH_VARARGS, + "Return number of CPUs on the system"}, + {"get_virtual_mem", get_virtual_mem, METH_VARARGS, + "Return system virtual memory stats"}, + {"get_swap_mem", get_swap_mem, METH_VARARGS, + "Return stats about swap 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_per_cpu_times", get_system_per_cpu_times, METH_VARARGS, + "Return system per-cpu times as a list of tuples"}, + {"get_system_boot_time", get_system_boot_time, METH_VARARGS, + "Return a float indicating the system boot time expressed in " + "seconds since the epoch"}, + {"get_disk_partitions", get_disk_partitions, METH_VARARGS, + "Return a list of tuples including device, mount point and " + "fs type for all partitions mounted on the system."}, + {"get_network_io_counters", get_network_io_counters, METH_VARARGS, + "Return dict of tuples of networks I/O information."}, + {"get_disk_io_counters", get_disk_io_counters, METH_VARARGS, + "Return dict of tuples of disks I/O information."}, + {"get_system_users", get_system_users, METH_VARARGS, + "Return currently connected users as a list of tuples"}, + + {NULL, NULL, 0, NULL} +}; + + +struct module_state { + PyObject *error; +}; + +#if PY_MAJOR_VERSION >= 3 +#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) +#else +#define GETSTATE(m) (&_state) +#endif + +#if PY_MAJOR_VERSION >= 3 + +static int +psutil_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; + } +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} diff --git a/vendor/psutil/_psutil_osx.h b/vendor/psutil/_psutil_osx.h new file mode 100644 index 000000000..5b6dbb908 --- /dev/null +++ b/vendor/psutil/_psutil_osx.h @@ -0,0 +1,44 @@ +/* + * $Id: _psutil_osx.h 1498 2012-07-24 21:41:28Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * 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_cwd(PyObject* self, PyObject* args); +static PyObject* get_process_exe(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_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_process_create_time(PyObject* self, PyObject* args); +static PyObject* get_process_memory_info(PyObject* self, PyObject* args); +static PyObject* get_process_num_threads(PyObject* self, PyObject* args); +static PyObject* get_process_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); +static PyObject* get_process_num_fds(PyObject* self, PyObject* args); +static PyObject* get_process_tty_nr(PyObject* self, PyObject* args); +static PyObject* get_process_memory_maps(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_virtual_mem(PyObject* self, PyObject* args); +static PyObject* get_swap_mem(PyObject* self, PyObject* args); +static PyObject* get_system_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_system_per_cpu_times(PyObject* self, PyObject* args); +static PyObject* get_system_boot_time(PyObject* self, PyObject* args); +static PyObject* get_disk_partitions(PyObject* self, PyObject* args); +static PyObject* get_network_io_counters(PyObject* self, PyObject* args); +static PyObject* get_disk_io_counters(PyObject* self, PyObject* args); +static PyObject* get_system_users(PyObject* self, PyObject* args); diff --git a/vendor/psutil/_psutil_posix.c b/vendor/psutil/_psutil_posix.c new file mode 100644 index 000000000..c4d55f43b --- /dev/null +++ b/vendor/psutil/_psutil_posix.c @@ -0,0 +1,134 @@ +/* + * $Id: _psutil_posix.c 1223 2011-11-09 23:47:55Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * 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) +#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; + } +#if PY_MAJOR_VERSION >= 3 + return module; +#endif +} + + + diff --git a/vendor/psutil/_psutil_posix.h b/vendor/psutil/_psutil_posix.h new file mode 100644 index 000000000..b6dd0b42d --- /dev/null +++ b/vendor/psutil/_psutil_posix.h @@ -0,0 +1,14 @@ +/* + * $Id: _psutil_posix.h 1223 2011-11-09 23:47:55Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * 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 --git a/vendor/psutil/arch/bsd/process_info.c b/vendor/psutil/arch/bsd/process_info.c new file mode 100644 index 000000000..22740a119 --- /dev/null +++ b/vendor/psutil/arch/bsd/process_info.c @@ -0,0 +1,271 @@ +/* + * $Id: process_info.c 1462 2012-07-18 03:12:08Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Helper functions related to fetching process information. Used by _psutil_bsd + * module methods. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "process_info.h" + + +/* + * Returns a list of all BSD processes on the system. This routine + * allocates the list and puts it in *procList and a count of the + * number of entries in *procCount. You are responsible for freeing + * this list (use "free" from System framework). + * On success, the function returns 0. + * On error, the function returns a BSD errno value. + */ +int +get_proc_list(struct kinfo_proc **procList, size_t *procCount) +{ + int err; + struct kinfo_proc * result; + int done; + static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_PROC, 0 }; + // Declaring name as const requires us to cast it when passing it to + // sysctl because the prototype doesn't include the const modifier. + size_t length; + + assert( procList != NULL); + assert(*procList == NULL); + assert(procCount != NULL); + + *procCount = 0; + + /* + * We start by calling sysctl with result == NULL and length == 0. + * That will succeed, and set length to the appropriate length. + * We then allocate a buffer of that size and call sysctl again + * with that buffer. If that succeeds, we're done. If that fails + * with ENOMEM, we have to throw away our buffer and loop. Note + * that the loop causes use to call sysctl with NULL again; this + * is necessary because the ENOMEM failure case sets length to + * the amount of data returned, not the amount of data that + * could have been returned. + */ + result = NULL; + done = 0; + do { + assert(result == NULL); + // Call sysctl with a NULL buffer. + length = 0; + err = sysctl((int *)name, (sizeof(name) / sizeof(*name)) - 1, + NULL, &length, NULL, 0); + if (err == -1) + err = errno; + + // Allocate an appropriately sized buffer based on the results + // from the previous call. + if (err == 0) { + result = malloc(length); + if (result == NULL) + err = ENOMEM; + } + + // Call sysctl again with the new buffer. If we get an ENOMEM + // error, toss away our buffer and start again. + if (err == 0) { + err = sysctl((int *) name, (sizeof(name) / sizeof(*name)) - 1, + result, &length, NULL, 0); + if (err == -1) + err = errno; + if (err == 0) { + done = 1; + } + else if (err == ENOMEM) { + assert(result != NULL); + free(result); + result = NULL; + err = 0; + } + } + } while (err == 0 && ! done); + + // Clean up and establish post conditions. + if (err != 0 && result != NULL) { + free(result); + result = NULL; + } + + *procList = result; + *procCount = length / sizeof(struct kinfo_proc); + + assert((err == 0) == (*procList != NULL)); + return err; +} + + +char +*getcmdpath(long pid, size_t *pathsize) +{ + int mib[4]; + char *path; + size_t size = 0; + + /* + * Make a sysctl() call to get the raw argument space of the process. + */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PATHNAME; + mib[3] = pid; + + // call with a null buffer first to determine if we need a buffer + if (sysctl(mib, 4, NULL, &size, NULL, 0) == -1) { + return NULL; + } + + path = malloc(size); + if (path == NULL) { + PyErr_SetString(PyExc_MemoryError, "couldn't allocate memory"); + return NULL; + } + + *pathsize = size; + if (sysctl(mib, 4, path, &size, NULL, 0) == -1) { + free(path); + return NULL; /* Insufficient privileges */ + } + + return path; +} + + +/* + * Borrowed from psi Python System Information project + * + * Get command arguments and environment variables. + * + * Based on code from ps. + * + * Returns: + * 0 for success; + * -1 for failure (Exception raised); + * 1 for insufficient privileges. + */ +char +*getcmdargs(long pid, size_t *argsize) +{ + int mib[4]; + size_t size, argmax; + char *procargs = NULL; + + /* Get the maximum process arguments size. */ + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + size = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) + return NULL; + + /* Allocate space for the arguments. */ + procargs = (char *)malloc(argmax); + if (procargs == NULL) { + PyErr_SetString(PyExc_MemoryError, "couldn't allocate memory"); + return NULL; + } + + /* + * Make a sysctl() call to get the raw argument space of the process. + */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_ARGS; + mib[3] = pid; + + size = argmax; + if (sysctl(mib, 4, procargs, &size, NULL, 0) == -1) { + free(procargs); + return NULL; /* Insufficient privileges */ + } + + // return string and set the length of arguments + *argsize = size; + return procargs; +} + + +/* returns the command line as a python list object */ +PyObject* +get_arg_list(long pid) +{ + char *argstr = NULL; + int pos = 0; + size_t argsize = 0; + PyObject *retlist = Py_BuildValue("[]"); + PyObject *item = NULL; + + if (pid < 0) { + return retlist; + } + + argstr = getcmdargs(pid, &argsize); + if (argstr == NULL) { + goto error; + } + + // args are returned as a flattened string with \0 separators between + // arguments add each string to the list then step forward to the next + // separator + if (argsize > 0) { + while(pos < argsize) { + item = Py_BuildValue("s", &argstr[pos]); + if (!item) + goto error; + if (PyList_Append(retlist, item)) + goto error; + Py_DECREF(item); + pos = pos + strlen(&argstr[pos]) + 1; + } + } + + free(argstr); + return retlist; + +error: + Py_XDECREF(item); + Py_DECREF(retlist); + if (argstr != NULL) + free(argstr); + return NULL; +} + + +/* + * 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 --git a/vendor/psutil/arch/bsd/process_info.h b/vendor/psutil/arch/bsd/process_info.h new file mode 100644 index 000000000..91291533d --- /dev/null +++ b/vendor/psutil/arch/bsd/process_info.h @@ -0,0 +1,21 @@ +/* + * $Id: process_info.h 1142 2011-10-05 18:45:49Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Helper functions related to fetching process information. Used by _psutil_bsd + * module methods. + */ + +#include + +typedef struct kinfo_proc kinfo_proc; + +int get_proc_list(struct kinfo_proc **procList, size_t *procCount); +char *getcmdargs(long pid, size_t *argsize); +char *getcmdpath(long pid, size_t *pathsize); +PyObject* get_arg_list(long pid); +int pid_exists(long pid); + diff --git a/vendor/psutil/arch/mswindows/ntextapi.h b/vendor/psutil/arch/mswindows/ntextapi.h new file mode 100644 index 000000000..f11d1aa21 --- /dev/null +++ b/vendor/psutil/arch/mswindows/ntextapi.h @@ -0,0 +1,232 @@ +/* + * $Id: ntextapi.h 1452 2012-07-13 19:02:07Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + */ + +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; + DWORD 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; + + +// ================================================ +// get_system_users support () +// ================================================ + +typedef struct _WINSTATION_INFO { + BYTE Reserved1[72]; + ULONG SessionId; + BYTE Reserved2[4]; + FILETIME ConnectTime; + FILETIME DisconnectTime; + FILETIME LastInputTime; + FILETIME LoginTime; + BYTE Reserved3[1096]; + FILETIME CurrentTime; +} WINSTATION_INFO, *PWINSTATION_INFO; + +typedef enum _WINSTATIONINFOCLASS { + WinStationInformation = 8 +} WINSTATIONINFOCLASS; + +typedef BOOLEAN (WINAPI * PWINSTATIONQUERYINFORMATIONW) + (HANDLE,ULONG,WINSTATIONINFOCLASS,PVOID,ULONG,PULONG); + +typedef struct _WINSTATIONINFORMATIONW { + BYTE Reserved2[70]; + ULONG LogonId; + BYTE Reserved3[1140]; +} WINSTATIONINFORMATIONW, *PWINSTATIONINFORMATIONW; + +// start mingw support: +// http://www.koders.com/c/fid7C02CAE627C526914CDEB427405B51DF393A5EFA.aspx +#ifndef _INC_WTSAPI +typedef struct _WTS_CLIENT_ADDRESS { + DWORD AddressFamily; // AF_INET, AF_IPX, AF_NETBIOS, AF_UNSPEC + BYTE Address[20]; // client network address +} WTS_CLIENT_ADDRESS, * PWTS_CLIENT_ADDRESS; + +HANDLE +WINAPI +WTSOpenServerA( + IN LPSTR pServerName + ); + +VOID +WINAPI +WTSCloseServer( + IN HANDLE hServer + ); +#endif diff --git a/vendor/psutil/arch/mswindows/process_handles.c b/vendor/psutil/arch/mswindows/process_handles.c new file mode 100644 index 000000000..71da633e1 --- /dev/null +++ b/vendor/psutil/arch/mswindows/process_handles.c @@ -0,0 +1,322 @@ +/* + * $Id: process_handles.c 1463 2012-07-18 13:06:49Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + */ + +#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"); + Py_DECREF(filesList); + free(handleInfo); + return NULL; + } + + for (i = 0; i < handleInfo->HandleCount; i++) + { + SYSTEM_HANDLE handle = handleInfo->Handles[i]; + HANDLE dupHandle = NULL; + POBJECT_TYPE_INFORMATION objectTypeInfo = NULL; + PVOID objectNameInfo; + UNICODE_STRING objectName; + ULONG returnLength; + fileFromWchar = NULL; + arg = NULL; + + /* 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); + free(objectTypeInfo); + 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 (fileFromWchar == NULL) + goto error_py_fun; + #if PY_MAJOR_VERSION >= 3 + arg = Py_BuildValue("N", PyUnicode_AsUTF8String(fileFromWchar)); + #else + arg = Py_BuildValue("N", PyUnicode_FromObject(fileFromWchar)); + #endif + if (!arg) + goto error_py_fun; + Py_XDECREF(fileFromWchar); + fileFromWchar = NULL; + if (PyList_Append(filesList, arg)) + goto error_py_fun; + 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; + +error_py_fun: + Py_XDECREF(arg); + Py_XDECREF(fileFromWchar); + Py_DECREF(filesList); + return NULL; +} diff --git a/vendor/psutil/arch/mswindows/process_handles.h b/vendor/psutil/arch/mswindows/process_handles.h new file mode 100644 index 000000000..3d007b8cc --- /dev/null +++ b/vendor/psutil/arch/mswindows/process_handles.h @@ -0,0 +1,12 @@ +/* + * $Id: process_info.h 1060 2011-07-02 18:05:26Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include +#include + +PyObject* get_open_files(long pid, HANDLE processHandle); diff --git a/vendor/psutil/arch/mswindows/process_info.c b/vendor/psutil/arch/mswindows/process_info.c new file mode 100644 index 000000000..2ff07dde8 --- /dev/null +++ b/vendor/psutil/arch/mswindows/process_info.c @@ -0,0 +1,510 @@ +/* + * $Id: process_info.c 1463 2012-07-18 13:06:49Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Helper functions related to fetching process information. Used by + * _psutil_mswindows module methods. + */ + +#include +#include +#include +#include + +#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; + +typedef NTSTATUS (NTAPI *_NtQueryInformationProcess)( + HANDLE ProcessHandle, + DWORD ProcessInformationClass, + PVOID ProcessInformation, + DWORD ProcessInformationLength, + PDWORD ReturnLength + ); + +typedef struct _PROCESS_BASIC_INFORMATION +{ + PVOID Reserved1; + PVOID PebBaseAddress; + 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; + + if (pid == 0) { + // otherwise we'd get NoSuchProcess + return AccessDenied(); + } + + hProcess = OpenProcess(dwDesiredAccess, FALSE, pid); + if (hProcess == NULL) { + if (GetLastError() == ERROR_INVALID_PARAMETER) { + NoSuchProcess(); + } + else { + PyErr_SetFromWindowsErr(0); + } + return NULL; + } + + /* make sure the process is running */ + GetExitCodeProcess(hProcess, &processExitCode); + if (processExitCode == 0) { + NoSuchProcess(); + CloseHandle(hProcess); + return NULL; + } + return hProcess; +} + + +/* + * Same as handle_from_pid_waccess but implicitly uses + * PROCESS_QUERY_INFORMATION | PROCESS_VM_READ as dwDesiredAccess + * parameter for OpenProcess. + */ +HANDLE +handle_from_pid(DWORD pid) { + DWORD dwDesiredAccess = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ; + return handle_from_pid_waccess(pid, dwDesiredAccess); +} + + +// fetch the PEB base address from NtQueryInformationProcess() +PVOID +GetPebAddress(HANDLE ProcessHandle) +{ + _NtQueryInformationProcess NtQueryInformationProcess = + (_NtQueryInformationProcess)GetProcAddress( + GetModuleHandleA("ntdll.dll"), "NtQueryInformationProcess"); + PROCESS_BASIC_INFORMATION pbi; + + NtQueryInformationProcess(ProcessHandle, 0, &pbi, sizeof(pbi), NULL); + return pbi.PebBaseAddress; +} + + +DWORD* +get_pids(DWORD *numberOfReturnedPIDs) { + /* 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; + int procArraySz = 0; + + // Stores the byte size of the returned array from enumprocesses + DWORD enumReturnSz = 0; + + do { + procArraySz += 1024; + free(procArray); + procArrayByteSz = procArraySz * sizeof(DWORD); + procArray = malloc(procArrayByteSz); + + if (! EnumProcesses(procArray, procArrayByteSz, &enumReturnSz)) { + free(procArray); + PyErr_SetFromWindowsErr(0); + return NULL; + } + } while(enumReturnSz == procArraySz * sizeof(DWORD)); + + // The number of elements is the returned size / size of each element + *numberOfReturnedPIDs = enumReturnSz / sizeof(DWORD); + + return procArray; +} + + +int +pid_is_running(DWORD pid) +{ + HANDLE hProcess; + DWORD exitCode; + + // Special case for PID 0 System Idle Process + if (pid == 0) { + return 1; + } + + if (pid < 0) { + return 0; + } + + hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + FALSE, pid); + if (NULL == hProcess) { + // invalid parameter is no such process + if (GetLastError() == ERROR_INVALID_PARAMETER) { + CloseHandle(hProcess); + return 0; + } + + // access denied obviously means there's a process to deny access to... + if (GetLastError() == ERROR_ACCESS_DENIED) { + CloseHandle(hProcess); + return 1; + } + + CloseHandle(hProcess); + PyErr_SetFromWindowsErr(0); + return -1; + } + + if (GetExitCodeProcess(hProcess, &exitCode)) { + CloseHandle(hProcess); + return (exitCode == STILL_ACTIVE); + } + + // access denied means there's a process there so we'll assume it's running + if (GetLastError() == ERROR_ACCESS_DENIED) { + CloseHandle(hProcess); + return 1; + } + + PyErr_SetFromWindowsErr(0); + CloseHandle(hProcess); + return -1; +} + + +int +pid_in_proclist(DWORD pid) +{ + DWORD *proclist = NULL; + DWORD numberOfReturnedPIDs; + DWORD i; + + proclist = get_pids(&numberOfReturnedPIDs); + if (NULL == proclist) { + return -1; + } + + for (i = 0; i < numberOfReturnedPIDs; i++) { + if (pid == proclist[i]) { + free(proclist); + return 1; + } + } + + free(proclist); + return 0; +} + + +// Check exit code from a process handle. Return FALSE on an error also +BOOL is_running(HANDLE hProcess) +{ + DWORD dwCode; + + if (NULL == hProcess) { + return FALSE; + } + + if (GetExitCodeProcess(hProcess, &dwCode)) { + return (dwCode == STILL_ACTIVE); + } + return FALSE; +} + + +// Return None to represent NoSuchProcess, else return NULL for +// other exception or the name as a Python string +PyObject* +get_name(long pid) +{ + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe = { 0 }; + pe.dwSize = sizeof(PROCESSENTRY32); + + if( Process32First(h, &pe)) { + do { + if (pe.th32ProcessID == pid) { + CloseHandle(h); + return Py_BuildValue("s", pe.szExeFile); + } + } while(Process32Next(h, &pe)); + + // the process was never found, set NoSuchProcess exception + NoSuchProcess(); + CloseHandle(h); + return NULL; + } + + CloseHandle(h); + return PyErr_SetFromWindowsErr(0); +} + + +/* returns parent pid (as a Python int) for given pid or None on failure */ +PyObject* +get_ppid(long pid) +{ + HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + PROCESSENTRY32 pe = { 0 }; + pe.dwSize = sizeof(PROCESSENTRY32); + + if( Process32First(h, &pe)) { + do { + if (pe.th32ProcessID == pid) { + CloseHandle(h); + return Py_BuildValue("I", pe.th32ParentProcessID); + } + } while(Process32Next(h, &pe)); + + // the process was never found, set NoSuchProcess exception + NoSuchProcess(); + CloseHandle(h); + return NULL; + } + + CloseHandle(h); + return PyErr_SetFromWindowsErr(0); +} + + +/* + * returns a Python list representing the arguments for the process + * with given pid or NULL on error. + */ +PyObject* +get_arg_list(long pid) +{ + int nArgs, i; + LPWSTR *szArglist = NULL; + HANDLE hProcess = NULL; + PVOID pebAddress; + PVOID rtlUserProcParamsAddress; + UNICODE_STRING commandLine; + WCHAR *commandLineContents = NULL; + PyObject *arg = NULL; + PyObject *arg_from_wchar = NULL; + PyObject *argList = NULL; + + hProcess = handle_from_pid(pid); + if(hProcess == NULL) { + return NULL; + } + + pebAddress = GetPebAddress(hProcess); + + /* get the address of ProcessParameters */ +#ifdef _WIN64 + if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 32, + &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) +#else + if (!ReadProcessMemory(hProcess, (PCHAR)pebAddress + 0x10, + &rtlUserProcParamsAddress, sizeof(PVOID), NULL)) +#endif + { + ////printf("Could not read the address of ProcessParameters!\n"); + PyErr_SetFromWindowsErr(0); + goto error; + } + + /* 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); + goto error; + } + + + /* allocate memory to hold the command line */ + commandLineContents = (WCHAR *)malloc(commandLine.Length+1); + + /* read the command line */ + if (!ReadProcessMemory(hProcess, commandLine.Buffer, + commandLineContents, commandLine.Length, NULL)) + { + ////printf("Could not read the command line string!\n"); + PyErr_SetFromWindowsErr(0); + goto error; + } + + /* print the commandline */ + ////printf("%.*S\n", commandLine.Length / 2, commandLineContents); + + // null-terminate the string to prevent wcslen from returning incorrect length + // the length specifier is in characters, but commandLine.Length is in bytes + commandLineContents[(commandLine.Length/sizeof(WCHAR))] = '\0'; + + // attemempt tp parse the command line using Win32 API, fall back on string + // cmdline version otherwise + szArglist = CommandLineToArgvW(commandLineContents, &nArgs); + if (NULL == szArglist) { + // failed to parse arglist + // encode as a UTF8 Python string object from WCHAR string + arg_from_wchar = PyUnicode_FromWideChar(commandLineContents, + commandLine.Length / 2); + if (arg_from_wchar == NULL) + goto error; + #if PY_MAJOR_VERSION >= 3 + argList = Py_BuildValue("N", PyUnicode_AsUTF8String(arg_from_wchar)); + #else + argList = Py_BuildValue("N", PyUnicode_FromObject(arg_from_wchar)); + #endif + if (!argList) + goto error; + } + else { + // arglist parsed as array of UNICODE_STRING, so convert each to Python + // string object and add to arg list + argList = Py_BuildValue("[]"); + if (!argList) + goto error; + for(i=0; i= 3 + arg = PyUnicode_FromObject(arg_from_wchar); + #else + arg = PyUnicode_AsUTF8String(arg_from_wchar); + #endif + if (arg == NULL) + goto error; + Py_XDECREF(arg_from_wchar); + if (PyList_Append(argList, arg)) + goto error; + Py_XDECREF(arg); + } + } + + if (szArglist != NULL) + LocalFree(szArglist); + free(commandLineContents); + CloseHandle(hProcess); + return argList; + +error: + Py_XDECREF(arg); + Py_XDECREF(arg_from_wchar); + Py_XDECREF(argList); + if (hProcess != NULL) + CloseHandle(hProcess); + if (commandLineContents != NULL) + free(commandLineContents); + if (szArglist != NULL) + LocalFree(szArglist); + return NULL; +} + + +#define PH_FIRST_PROCESS(Processes) ((PSYSTEM_PROCESS_INFORMATION)(Processes)) + +#define PH_NEXT_PROCESS(Process) ( \ + ((PSYSTEM_PROCESS_INFORMATION)(Process))->NextEntryOffset ? \ + (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, PVOID *retBuffer) +{ + 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) { + PyErr_Format(PyExc_RuntimeError, "NtQuerySystemInformation() failed"); + FreeLibrary(hNtDll); + free(buffer); + return 0; + } + + if (bufferSize <= 0x20000) { + initialBufferSize = bufferSize; + } + + process = PH_FIRST_PROCESS(buffer); + do { + if (process->UniqueProcessId == (HANDLE)pid) { + *retProcess = process; + *retBuffer = buffer; + return 1; + } + } while ( (process = PH_NEXT_PROCESS(process)) ); + + NoSuchProcess(); + FreeLibrary(hNtDll); + free(buffer); + return 0; +} diff --git a/vendor/psutil/arch/mswindows/process_info.h b/vendor/psutil/arch/mswindows/process_info.h new file mode 100644 index 000000000..e7a855c05 --- /dev/null +++ b/vendor/psutil/arch/mswindows/process_info.h @@ -0,0 +1,25 @@ +/* + * $Id: process_info.h 1142 2011-10-05 18:45:49Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Helper functions related to fetching process information. Used by _psutil_mswindows + * module methods. + */ + +#include +#include + +HANDLE handle_from_pid_waccess(DWORD pid, DWORD dwDesiredAccess); +HANDLE handle_from_pid(DWORD pid); +PVOID GetPebAddress(HANDLE ProcessHandle); +HANDLE handle_from_pid(DWORD pid); +BOOL is_running(HANDLE hProcess); +int pid_in_proclist(DWORD pid); +int pid_is_running(DWORD pid); +PyObject* get_arg_list(long pid); +PyObject* get_ppid(long pid); +PyObject* get_name(long pid); +DWORD* get_pids(DWORD *numberOfReturnedPIDs); diff --git a/vendor/psutil/arch/mswindows/security.c b/vendor/psutil/arch/mswindows/security.c new file mode 100644 index 000000000..7d9f88358 --- /dev/null +++ b/vendor/psutil/arch/mswindows/security.c @@ -0,0 +1,240 @@ +/* + * $Id: security.c 1296 2012-04-25 01:29:43Z david.daeschler@gmail.com $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Security related functions for Windows platform (Set privileges such as + * SeDebug), as well as security helper functions. + */ + +#include +#include + +/* + * Convert a process handle to a process token handle. + */ +HANDLE +token_from_handle(HANDLE hProcess) { + HANDLE hToken = NULL; + + if (! OpenProcessToken(hProcess, TOKEN_QUERY, &hToken) ) { + return PyErr_SetFromWindowsErr(0); + } + + return hToken; +} + + +/* + * http://www.ddj.com/windows/184405986 + * + * There's a way to determine whether we're running under the Local System + * account. However (you guessed it), we have to call more Win32 functions to + * determine this. Backing up through the code listing, we need to make another + * call to GetTokenInformation, but instead of passing through the TOKEN_USER + * constant, we pass through the TOKEN_PRIVILEGES constant. This value returns + * an array of privileges that the account has in the environment. Iterating + * through the array, we call the function LookupPrivilegeName looking for the + * string SeTcbPrivilege. If the function returns this string, then this + * account has Local System privileges + */ +int HasSystemPrivilege(HANDLE hProcess) { + DWORD i; + DWORD dwSize = 0; + DWORD dwRetval = 0; + TCHAR privName[256]; + DWORD dwNameSize = 256; + //PTOKEN_PRIVILEGES tp = NULL; + BYTE *pBuffer = NULL; + TOKEN_PRIVILEGES* tp = NULL; + HANDLE hToken = token_from_handle(hProcess); + + if (NULL == hToken) { + return -1; + } + + // call GetTokenInformation first to get the buffer size + if (! GetTokenInformation(hToken, TokenPrivileges, NULL, 0, &dwSize)) { + dwRetval = GetLastError(); + // if it failed for a reason other than the buffer, bail out + if (dwRetval != ERROR_INSUFFICIENT_BUFFER ) { + PyErr_SetFromWindowsErr(dwRetval); + return 0; + } + } + + // allocate buffer and call GetTokenInformation again + //tp = (PTOKEN_PRIVILEGES) GlobalAlloc(GPTR, dwSize); + pBuffer = (BYTE *) malloc(dwSize); + + if (pBuffer == NULL) { + PyErr_SetFromWindowsErr(0); + free(pBuffer); + return -1; + } + + if (! GetTokenInformation(hToken, TokenPrivileges, pBuffer, dwSize, &dwSize) ) { + PyErr_SetFromWindowsErr(0); + free(pBuffer); + return -1; + } + + // convert the BYTE buffer to a TOKEN_PRIVILEGES struct pointer + tp = (TOKEN_PRIVILEGES*)pBuffer; + + // check all the privileges looking for SeTcbPrivilege + for(i=0; i < tp->PrivilegeCount; i++) { + // reset the buffer contents and the buffer size + strcpy(privName, ""); + dwNameSize = sizeof(privName) / sizeof(TCHAR); + if (! LookupPrivilegeName(NULL, + &tp->Privileges[i].Luid, + (LPTSTR)privName, + &dwNameSize)) { + + PyErr_SetFromWindowsErr(0); + free(pBuffer); + return -1; + } + + // if we find the SeTcbPrivilege then it's a LocalSystem process + if (! lstrcmpi(privName, TEXT("SeTcbPrivilege"))) { + free(pBuffer); + return 1; + } + + } //for + + free(pBuffer); + return 0; +} + + +BOOL SetPrivilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege) +{ + TOKEN_PRIVILEGES tp; + LUID luid; + TOKEN_PRIVILEGES tpPrevious; + DWORD cbPrevious=sizeof(TOKEN_PRIVILEGES); + + if(!LookupPrivilegeValue( NULL, Privilege, &luid )) return FALSE; + + // first pass. get current privilege setting + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = 0; + + AdjustTokenPrivileges( + hToken, + FALSE, + &tp, + sizeof(TOKEN_PRIVILEGES), + &tpPrevious, + &cbPrevious + ); + + if (GetLastError() != ERROR_SUCCESS) return FALSE; + + // second pass. set privilege based on previous setting + tpPrevious.PrivilegeCount = 1; + tpPrevious.Privileges[0].Luid = luid; + + if(bEnablePrivilege) { + tpPrevious.Privileges[0].Attributes |= (SE_PRIVILEGE_ENABLED); + } + + else { + tpPrevious.Privileges[0].Attributes ^= (SE_PRIVILEGE_ENABLED & + tpPrevious.Privileges[0].Attributes); + } + + AdjustTokenPrivileges( + hToken, + FALSE, + &tpPrevious, + cbPrevious, + NULL, + NULL + ); + + if (GetLastError() != ERROR_SUCCESS) return FALSE; + + return TRUE; +} + + +int SetSeDebug() +{ + HANDLE hToken; + if(! OpenThreadToken(GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + FALSE, + &hToken) + ){ + if (GetLastError() == ERROR_NO_TOKEN){ + if (!ImpersonateSelf(SecurityImpersonation)){ + CloseHandle(hToken); + return 0; + } + if (!OpenThreadToken(GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + FALSE, + &hToken) + ){ + RevertToSelf(); + CloseHandle(hToken); + return 0; + } + } + } + + // enable SeDebugPrivilege (open any process) + if (! SetPrivilege(hToken, SE_DEBUG_NAME, TRUE)){ + RevertToSelf(); + CloseHandle(hToken); + return 0; + } + + RevertToSelf(); + CloseHandle(hToken); + return 1; +} + + +int UnsetSeDebug() +{ + HANDLE hToken; + if(! OpenThreadToken(GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + FALSE, + &hToken) + ){ + if(GetLastError() == ERROR_NO_TOKEN){ + if(! ImpersonateSelf(SecurityImpersonation)){ + //Log2File("Error setting impersonation! [UnsetSeDebug()]", L_DEBUG); + return 0; + } + + if(!OpenThreadToken(GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, + FALSE, + &hToken) + ){ + //Log2File("Error Opening Thread Token! [UnsetSeDebug()]", L_DEBUG); + return 0; + } + } + } + + //now disable SeDebug + if(!SetPrivilege(hToken, SE_DEBUG_NAME, FALSE)){ + //Log2File("Error unsetting SeDebug Privilege [SetPrivilege()]", L_WARN); + return 0; + } + + CloseHandle(hToken); + return 1; +} + diff --git a/vendor/psutil/arch/mswindows/security.h b/vendor/psutil/arch/mswindows/security.h new file mode 100644 index 000000000..2cfce29ca --- /dev/null +++ b/vendor/psutil/arch/mswindows/security.h @@ -0,0 +1,20 @@ +/* + * $Id: security.h 1142 2011-10-05 18:45:49Z g.rodola $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Security related functions for Windows platform (Set privileges such as + * SeDebug), as well as security helper functions. + */ + +#include + + +BOOL SetPrivilege(HANDLE hToken, LPCTSTR Privilege, BOOL bEnablePrivilege); +int SetSeDebug(); +int UnsetSeDebug(); +HANDLE token_from_handle(HANDLE hProcess); +int HasSystemPrivilege(HANDLE hProcess); + diff --git a/vendor/psutil/arch/osx/process_info.c b/vendor/psutil/arch/osx/process_info.c new file mode 100644 index 000000000..323157ffd --- /dev/null +++ b/vendor/psutil/arch/osx/process_info.c @@ -0,0 +1,295 @@ +/* + * $Id: process_info.c 1460 2012-07-18 02:49:24Z g.rodola@gmail.com $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Helper functions related to fetching process information. Used by _psutil_osx + * module methods. + */ + +#include +#include +#include +#include /* for INT_MAX */ +#include +#include +#include +#include +#include +#include + +#include "process_info.h" +#include "../../_psutil_common.h" + + +/* + * 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; +} + + + +/* + * Returns a list of all BSD processes on the system. This routine + * allocates the list and puts it in *procList and a count of the + * number of entries in *procCount. You are responsible for freeing + * this list (use "free" from System framework). + * On success, the function returns 0. + * On error, the function returns a BSD errno value. + */ +int +get_proc_list(kinfo_proc **procList, size_t *procCount) +{ + /* Declaring mib as const requires use of a cast since the + * sysctl prototype doesn't include the const modifier. */ + static const int mib3[3] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL }; + size_t size, size2; + void *ptr; + int err, lim = 8; /* some limit */ + + assert( procList != NULL); + assert(*procList == NULL); + assert(procCount != NULL); + + *procCount = 0; + + /* We start by calling sysctl with ptr == NULL and size == 0. + * That will succeed, and set size to the appropriate length. + * We then allocate a buffer of at least that size and call + * sysctl with that buffer. If that succeeds, we're done. + * If that call fails with ENOMEM, we throw the buffer away + * and try again. + * Note that the loop calls sysctl with NULL again. This is + * is necessary because the ENOMEM failure case sets size to + * the amount of data returned, not the amount of data that + * could have been returned. + */ + while (lim-- > 0) { + size = 0; + if (sysctl((int *)mib3, 3, NULL, &size, NULL, 0) == -1) { + return errno; + } + + size2 = size + (size >> 3); /* add some */ + if (size2 > size) { + ptr = malloc(size2); + if (ptr == NULL) { + ptr = malloc(size); + } else { + size = size2; + } + } + else { + ptr = malloc(size); + } + if (ptr == NULL) { + return ENOMEM; + } + + if (sysctl((int *)mib3, 3, ptr, &size, NULL, 0) == -1) { + err = errno; + free(ptr); + if (err != ENOMEM) { + return err; + } + + } else { + *procList = (kinfo_proc *)ptr; + *procCount = size / sizeof(kinfo_proc); + return 0; + } + } + return ENOMEM; +} + + +/* Read the maximum argument size for processes */ +int +get_argmax() +{ + int 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; +} + + +/* return process args as a python list */ +PyObject* +get_arg_list(long pid) +{ + int mib[3]; + int nargs; + int len; + char *procargs = NULL; + 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("[]"); + } + + /* read argmax and allocate memory for argument space. */ + argmax = get_argmax(); + if (! argmax) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + procargs = (char *)malloc(argmax); + if (NULL == procargs) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + + /* read argument space */ + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS2; + 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(); + } + } + goto error; + } + + arg_end = &procargs[argmax]; + /* copy the number of arguments to nargs */ + memcpy(&nargs, procargs, sizeof(nargs)); + + arg_ptr = procargs + sizeof(nargs); + len = strlen(arg_ptr); + arg_ptr += len + 1; + + if (arg_ptr == arg_end) { + free(procargs); + return Py_BuildValue("[]"); + } + + // skip ahead to the first argument + for (; arg_ptr < arg_end; arg_ptr++) { + if (*arg_ptr != '\0') { + break; + } + } + + /* iterate through arguments */ + curr_arg = arg_ptr; + arglist = Py_BuildValue("[]"); + if (!arglist) + goto error; + while (arg_ptr < arg_end && nargs > 0) { + if (*arg_ptr++ == '\0') { + arg = Py_BuildValue("s", curr_arg); + if (!arg) + goto error; + if (PyList_Append(arglist, arg)) + goto error; + Py_DECREF(arg); + // iterate to next arg and decrement # of args + curr_arg = arg_ptr; + nargs--; + } + } + + free(procargs); + return arglist; + +error: + Py_XDECREF(arg); + Py_XDECREF(arglist); + if (procargs != NULL) + free(procargs); + return NULL; +} + + +int +get_kinfo_proc(pid_t pid, struct kinfo_proc *kp) +{ + int mib[4]; + size_t len; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = pid; + + // fetch the info with sysctl() + len = sizeof(struct kinfo_proc); + + // now read the data from sysctl + if (sysctl(mib, 4, kp, &len, NULL, 0) == -1) { + // raise an exception and throw errno as the error + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + + /* + * sysctl succeeds but len is zero, happens when process has gone away + */ + if (len == 0) { + NoSuchProcess(); + return -1; + } + return 0; +} + + +/* + * A thin wrapper around proc_pidinfo() + */ +int +psutil_proc_pidinfo(long pid, int flavor, void *pti, int size) +{ + int ret = proc_pidinfo((int)pid, flavor, 0, pti, size); + if (ret == 0) { + if (! pid_exists(pid)) { + NoSuchProcess(); + return 0; + } + else { + AccessDenied(); + return 0; + } + } + else if (ret != size) { + AccessDenied(); + return 0; + } + else { + return 1; + } +} diff --git a/vendor/psutil/arch/osx/process_info.h b/vendor/psutil/arch/osx/process_info.h new file mode 100644 index 000000000..1bb986304 --- /dev/null +++ b/vendor/psutil/arch/osx/process_info.h @@ -0,0 +1,22 @@ +/* + * $Id: process_info.h 1407 2012-06-30 17:14:54Z g.rodola@gmail.com $ + * + * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + * + * Helper functions related to fetching process information. Used by _psutil_osx + * module methods. + */ + +#include + + +typedef struct kinfo_proc kinfo_proc; + +int get_proc_list(kinfo_proc **procList, size_t *procCount); +int get_kinfo_proc(pid_t pid, struct kinfo_proc *kp); +int get_argmax(void); +int pid_exists(long pid); +int psutil_proc_pidinfo(long pid, int flavor, void *pti, int size); +PyObject* get_arg_list(long pid); diff --git a/vendor/psutil/error.py b/vendor/psutil/error.py new file mode 100644 index 000000000..0298cb947 --- /dev/null +++ b/vendor/psutil/error.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python +# +# $Id: error.py 1142 2011-10-05 18:45:49Z g.rodola $ +# +# Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""psutil exception classes; do not import this directly""" + + +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 +