blob: 2af24bffbd8c80e435d824156cc0dc0e26b84b87 [file] [log] [blame]
#!/usr/bin/env python
# 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 warnings
import _psutil_bsd
import _psutil_posix
from psutil import _psposix
from psutil._error import AccessDenied, NoSuchProcess, TimeoutExpired
from psutil._compat import namedtuple, wraps
from psutil._common import *
__extra__all__ = []
# --- constants
# Since these constants get determined at import time we do not want to
# crash immediately; instead we'll set them to None and most likely
# we'll crash later as they're used for determining process CPU stats
# and creation_time
try:
NUM_CPUS = _psutil_bsd.get_num_cpus()
except Exception:
NUM_CPUS = None
warnings.warn("couldn't determine platform's NUM_CPUS", RuntimeWarning)
try:
TOTAL_PHYMEM = _psutil_bsd.get_virtual_mem()[0]
except Exception:
TOTAL_PHYMEM = None
warnings.warn("couldn't determine platform's TOTAL_PHYMEM", RuntimeWarning)
try:
BOOT_TIME = _psutil_bsd.get_system_boot_time()
except Exception:
BOOT_TIME = None
warnings.warn("couldn't determine platform's BOOT_TIME", RuntimeWarning)
_PAGESIZE = os.sysconf("SC_PAGE_SIZE")
_cputimes_ntuple = namedtuple('cputimes', 'user nice system idle irq')
# --- public functions
get_system_boot_time = _psutil_bsd.get_system_boot_time
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(fun):
"""Decorator which translates bare OSError exceptions into
NoSuchProcess and AccessDenied.
"""
@wraps(fun)
def wrapper(self, *args, **kwargs):
try:
return fun(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)
tmap = _psposix._get_terminal_map()
try:
return tmap[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