[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. |