[Pymoul-svn] SF.net SVN: pymoul: [198] pymoul/trunk/src/moul/osdependent/processinfo.py
Status: Alpha
Brought to you by:
tiran
|
From: <ti...@us...> - 2007-02-27 03:02:02
|
Revision: 198
http://pymoul.svn.sourceforge.net/pymoul/?rev=198&view=rev
Author: tiran
Date: 2007-02-26 19:01:53 -0800 (Mon, 26 Feb 2007)
Log Message:
-----------
Added ps parser to processinfo
Modified Paths:
--------------
pymoul/trunk/src/moul/osdependent/processinfo.py
Modified: pymoul/trunk/src/moul/osdependent/processinfo.py
===================================================================
--- pymoul/trunk/src/moul/osdependent/processinfo.py 2007-02-26 17:43:35 UTC (rev 197)
+++ pymoul/trunk/src/moul/osdependent/processinfo.py 2007-02-27 03:01:53 UTC (rev 198)
@@ -17,6 +17,13 @@
#
"""Get process informations
+The module contains for implementations:
+
+ - an Unsupported implementation that raises UnsupportedError
+ - a Linux implementation that read the data from /proc
+ - a Unix/POSIX implementation that parses the output of ps
+ - a Windows implementation that uses ctypes to get the infos from psapi.dll
+
API
===
getPids() - list of ints/longs
@@ -56,11 +63,9 @@
LOG = getLogger("processinfo")
_plat = sys.platform.startswith
-if _plat('linux'):
- PLAT = 'linux'
- import os.path
-elif _plat('win') or _plat('cygwin'):
- PLAT = 'win'
+if _plat('win') or _plat('cygwin'):
+ LOG.debug("Using ctypes on Windows")
+ IMPL = 'win'
from ctypes import windll, c_ulong, sizeof, c_buffer, byref
PSAPI = windll.psapi
@@ -68,25 +73,147 @@
PROCESS_QUERY_INFORMATION = 0x0400
PROCESS_VM_READ = 0x0010
PROCESS_FLAGS = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ
-#elif _plat('darwin'):
-# pass
else:
- raise OSError("OS %s is not supported" % os.name)
+ import os
+ PROC = '/proc'
+ if os.path.isfile("%s/self/status" % PROC):
+ LOG.debug("Using /proc on Linux")
+ IMPL = 'proc'
+ elif os.system('ps') == 0:
+ LOG.debug("Using the 'ps' command on POSIX os")
+ IMPL = 'ps'
+ from subprocess import Popen
+ from subprocess import PIPE
+ else:
+ LOG.warning("Unsupported OS. Neither /proc nor 'ps' works.")
+ IMPL = "unsupported"
NULL = "\x00"
+class UnsupportedError(OSError):
+ pass
+
+class Unsupported(object):
+ """Unsupported OS
+ """
+ __slots__ = ()
+
+ def supported(self):
+ return False
+ supported = property(supported)
+
+ def getPids(self):
+ """Get a list of pids
+ """
+ raise UnsupportedError
+
+ def getPidNames(self):
+ """Get a list of pid -> name
+ """
+ raise UnsupportedError
+
+ def getPidDetails(self, pid):
+ """Get detailed informations about a process
+ """
+ raise UnsupportedError
+
+class PsParser(object):
+ """Parse the output of the ps command
+ """
+ __slots__ = ()
+
+ CMD = "ps -e --no-header --cols=1024"
+ PIDNAMES = "%s --format=pid,ucmd" % CMD
+ PIDS = "%s --format=pid" % CMD
+
+ def supported(self):
+ return False
+ supported = property(supported)
+
+ def getPids(self):
+ """Get a list of pids
+ """
+ stdout = self._exec(self.PIDS)
+ if stdout is None:
+ return None
+ pids = []
+ for line in stdout:
+ try:
+ pid = int(line.strip())
+ except ValueError:
+ pass
+ else:
+ pids.append(pid)
+ return pids
+
+ def getPidNames(self):
+ """Get a list of pid -> name
+ """
+ stdout = self._exec(self.PIDNAMES)
+ if stdout is None:
+ return None
+ mapping = {}
+ for line in stdout:
+ line = line.strip()
+ idx = line.find(' ')
+ pid, name = line[:idx], line[idx+1:]
+ try:
+ pid = int(pid)
+ except ValueError:
+ pass
+ else:
+ mapping[pid] = name
+ return mapping
+
+ def getPidDetails(self, pid):
+ """Get detailed informations about a process
+
+ TODO
+ """
+ raise UnsupportedError
+
+ def _exec(self, cmd):
+ """Execute command cmd
+
+ The method waits until the command has finished. It returns None of
+ something went wrong.
+
+ @param cmd: Command to execute
+ @type cmd: str
+ @return: None or stdin as file like object
+ """
+ try:
+ popen = Popen(cmd, shell=True, bufsize=-1, stdout=PIPE,
+ env = {'LC_ALL' : 'C'})
+ rc = popen.wait()
+ except (OSError, ValueError):
+ LOG.exception("Failed to execute '%s'" % cmd)
+ return None
+ else:
+ if rc != 0:
+ LOG.error("'%s' returned with error code %i" % (cmd, rc))
+ return None
+ else:
+ return popen.stdout
+
class LinuxProcReader(object):
"""Get process informations under Linux by reading /proc
-
+
Tested under Linux, may work on other POSIX os with /proc, too.
"""
+ __slots__ = ()
- def getPids(self):
+ def supported(self):
+ return True
+ supported = property(supported)
+
+ @staticmethod
+ def getPids():
"""Get a list of pids
"""
pids = []
- for name in os.listdir('/proc'):
- if os.path.isdir('/proc/' + name):
+ for name in os.listdir(PROC):
+ if os.path.isdir(PROC + '/' + name):
try:
pids.append(int(name))
except ValueError:
@@ -113,9 +240,9 @@
def _readProcStatus(self, pid, searchkey=None):
"""Read and parse status informations for PID pid
-
+
pid - pid as long or int or 'self'
-
+
If searchkey is None the method returns a mapping of lower keys
to values (stripped).
If searchkey is given than the method immediatly returns the value
@@ -124,7 +251,7 @@
mapping = {}
try:
# read entiry file to avoid race conditions
- lines = open('/proc/%s/status' % pid, 'r').readlines()
+ lines = open('%s/%i/status' % (PROC, pid), 'r').readlines()
except IOError:
return None
for line in lines:
@@ -142,34 +269,40 @@
if searchkey is not None:
return None
return mapping
-
+
def _readProcCmdline(self, pid):
"""Read cmdline informations for pid and returns a list similar to sys.argv
"""
try:
# read entiry file to avoid race conditions
- data = open('/proc/%s/cmdline' % pid, 'r').read()
+ data = open('%s/%i/cmdline' % (PROC, pid), 'r').read()
except IOError:
return None
return data.split(NULL)
def _readProcOther(self, pid):
"""Read other possible useful things
-
+
cwd -> current work directory (may not exist)
exe -> path to executable (may not exist)
"""
return {
- 'cwd' : os.path.realpath('/proc/%s/cwd' % pid),
- 'exe' : os.path.realpath('/proc/%s/exe' % pid),
+ 'cwd' : os.path.realpath('%s/%i/cwd' % (PROC, pid)),
+ 'exe' : os.path.realpath('%s/%i/exe' % (PROC, pid)),
}
class WinEnumProcesses(object):
"""""Get process informations under Win32 with psapi.dll
-
+
Based on enumprocesses from Eric Koome
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/305279
"""
+ __slots__ = ()
+
+ def supported(self):
+ return True
+ supported = property(supported)
+
def getPids(self):
"""Get a list of pids
"""
@@ -214,32 +347,44 @@
"""
if pid == 'self':
pid = os.getpid()
-
+
hModule = c_ulong()
count = c_ulong()
modname = c_buffer(51)
hProcess = KERNEL.OpenProcess(PROCESS_FLAGS, False, pid)
if not hProcess:
return None
-
+
PSAPI.EnumProcessModules(hProcess, byref(hModule),
sizeof(hModule), byref(count))
PSAPI.GetModuleBaseNameA(hProcess, hModule.value, modname,
sizeof(modname))
- name = u"".join([c for c in modname if c != NULL])
+ try:
+ name = u"".join([c for c in modname if c != NULL])
+ except UnicodeError, msg:
+ LOG.exception("Can't decode name of pid %i" % pid)
+ else:
+ name = None
+
KERNEL.CloseHandle(hProcess)
return {'name' : name}
# Initialize global methods
-if PLAT == 'linux':
+if IMPL == 'proc':
_enumProcesses = LinuxProcReader()
-elif PLAT == 'win':
+elif IMPL == 'ps':
+ _enumProcesses = PsParser()
+elif IMPL == 'win':
_enumProcesses = WinEnumProcesses()
+else:
+ LOG.error("System %s is not supported" % sys.platform)
+ _enumProcesses = Unsupported()
getPids = _enumProcesses.getPids
getPidNames = _enumProcesses.getPidNames
getPidDetails = _enumProcesses.getPidDetails
+supportedOS = _enumProcesses.supported
if __name__ == '__main__':
print getPidNames()
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|