""" Miscellaneous functions to manage unix processes """ from commands import getoutput from string import split, atoi, ljust, strip, find, upper, join from types import ListType, StringType, DictType import sys import log4py import re import tools # Boolean constants TRUE = 1 FALSE = 0 # Define some necessary constants FILTERON = 1 FILTEROFF = 0 FILTERMODEEXACT = 1 FILTERMODESUB = 2 # Operating system depended ps parameters OSTYPEBSD = "BSD" OSTYPESYSV = "System V" OSTYPEUNKNOWN = "Unknown" OSTYPESUNOSUCB = "SunOS UCB" # The platform can be determined by executing # python -c "import sys;print sys.platform" osFamily = {} osFamily[OSTYPEBSD] = ["freebsd4", "freebsd5", "bsdos4"] osFamily[OSTYPESYSV] = ["linux-i386", "linux2" ] osFamily[OSTYPESUNOSUCB] = ["sunos5"] osPsParam = {} osPsParam[OSTYPEBSD] = "-j -p %s" osPsParam[OSTYPESYSV] = "-f -p %s" osPsParam[OSTYPEUNKNOWN] = "-f -p %s" osPsParam[OSTYPESUNOSUCB] = "-l %s" osPsProcessListParam = {} osPsProcessListParam["freebsd4"] = "-ax" osPsProcessListParam["freebsd5"] = "-ax" osPsProcessListParam["sunos5"] = "-ef" osPsProcessListParam["linux2"] = "ax" osPsProcessListParam[OSTYPEUNKNOWN] = "ax" # Definition of the ProcessTable class class ProcessTable: # ProcessTable default settings processes = {} # List of Dictionaries containing processes parameter = "" # Parameters passed to read processmap = {} # Dictionary containing the various columns columnlength = {} # Dictionary containing the maximum length of each column filteredpids = [] # List of filtered pids mode = FILTEROFF # Current mode ppidparam = "" def __init__(self, loglevel = log4py.LOGLEVEL_NORMAL): """ Initialize the process class. """ self.log4py = log4py.Logger().get_instance(self) self.log4py.set_loglevel(loglevel) # self.log4py.set_loglevel(log4py.LOGLEVEL_DEBUG) self.__ProcessTable_ps_command = tools.which("ps") def read(self, parameter = None): """ Reads the current processes with optional parameters. """ if (parameter == None): if (osPsProcessListParam.has_key(sys.platform)): parameter = osPsProcessListParam[sys.platform] else: parameter = osPsProcessListParam[OSTYPEUNKNOWN] self.processes = {} self.parameter = parameter self.reread() def __ProcessTable_regexp(self, columns, command): """ Create a regular expression for the given list of columns. """ strRegExp = " *" for intCounter in range(len(columns)): column = upper(columns[intCounter]) if ((column == "UID") or (column == "USER")): # Strings e.g. 'root' strRegExp = "%s([\w-]+) +" % strRegExp elif ((column == "PID") or (column == "PPID") or (column == "RSS") or (column == "VSZ") or (column == "C")): # Digits e.g. 762 strRegExp = "%s(\d+) +" % strRegExp elif ((column == "TIME") or (column == "STIME") or (column == "START")): # Time columns: Oct 02, Oct02, 02:10, 00:01:15 strRegExp = "%s(\w{3} {0,1}\d+|\d+:\d+:\d+|\d+:\d+) +" % strRegExp elif (column == "STAT"): # Status: S, W, N, R, L, < strRegExp = "%s([SWNRL<]+) +" % strRegExp elif ((column == "TTY") or (column == "TT")): # TTY: ?, pty/1, console strRegExp = "%s(\?|[\w/]+\d*) +" % strRegExp elif ((column == "CMD") or (column == "COMMAND")): # Command: a string at the end of the line strRegExp = "%s(.+)$" % strRegExp elif ((column == "%CPU") or (column == "%MEM")): # Digits with a comma strRegExp = "%s(\d+\.\d+) +" % strRegExp else: self.log4py.error("Unknown column '%s' in output of '%s'. Please send the output of '%s' to Martin.Preishuber@eclipt.at." % (column, command, command)) self.log4py.debug("Regular expression: %s" % strRegExp) return strRegExp def reread(self): """ Re-read processes list (no parameters necessary). """ command = "%s %s" % (self.__ProcessTable_ps_command, self.parameter) self.log4py.debug("Reading process table with command: %s" % command) output = getoutput(command) splitted = split(output, "\n") self.processmap = tools.cleanlist(split(splitted[0], " ")) processes = splitted[1:] regexp = re.compile(self.__ProcessTable_regexp(self.processmap, command)) self.processes = {} for i in range(len(processes)): process = processes[i] matchobject = regexp.match(process) if (not matchobject): if (strip(process) != ""): self.log4py.warn("The process line '%s' does not match the given regular expression." % process) else: processentry = {} for j in range(len(self.processmap)): processentry[self.processmap[j]] = matchobject.group(j + 1) if (self.processmap[j] == "PID"): pid = atoi(matchobject.group(j + 1)) self.processes[pid] = processentry def filter(self, filters = None, filtermode = FILTERMODEEXACT): """ Filter for certain conditions in the ProcessTable. filters is a dictionary containing parameter:[valuelist] """ self.log4py.debug("Applying filter: (%s, %d)" % (str(filters), filtermode)) self.mode = FILTERON self.filteredpids = [] for i in range(len(self.processes.keys())): processid = self.processes.keys()[i] process = self.processes[processid] processmatches = TRUE for j in range(len(filters.keys())): filtername = filters.keys()[j] filterlist = filters[filtername] if type(filterlist) != ListType: filterlist = [filterlist] filtername = upper(filtername) # special case for different ps implementations if (filtername == "CMD") and (process.has_key("COMMAND")): filtername = "COMMAND" parameter = process[upper(filtername)] for k in range(len(filterlist)): singlefilter = filterlist[k] if (filtermode == FILTERMODEEXACT) or (type(singlefilter) != StringType): if (str(singlefilter) <> str(parameter)): processmatches = FALSE else: if (find(parameter, singlefilter) < 0): processmatches = FALSE if (processmatches == TRUE): self.filteredpids.append(atoi(process["PID"])) def removefilter(self): """ Remove applied filter. """ self.log4py.debug("Removing filter.") self.filteredpids = [] self.mode = FILTEROFF def exists(self, filters = None, filtermode = FILTERMODEEXACT): """ Checks, wether a process specified by the given condition(s) exists. """ tempfilter = self.filteredpids self.filter(filters, filtermode) exists = (len(self.filteredpids) > 0) self.filteredpids = tempfilter return exists def process(self, pid): """ Returns a dictionary with the specified process. """ if (self.processes.has_key(pid)): return self.processes[pid] else: return None def printtable(self, list = None): """ Print the process table (optional just a list of given pids). """ if (list == None): list = self.processes.keys() elif (type(list) == DictType): list = list.keys() list.sort() if len(list) == 0: return sys.stdout.write("%s\n" % strip(join(self.processmap))) for i in range(len(list)): if (self.processes.has_key(list[i])): process = self.processes[list[i]] line = "" for j in range(len(process.keys())): line = "%s%s " % (line, process[process.keys()[j]]) sys.stdout.write("%s\n" % line) else: sys.stdout.write("No process with pid #%s in the internal process table.\n" % list[i]) def pids(self): """ Returns either all pids or all filtered pids. """ if (self.mode == FILTERON): list = self.filteredpids else: list = self.processes.keys() list.sort() return list def ppids(self, pids = None): """ Returns a dictionary of parent pids of given pids, filtered pids or all pids (depends on filter mode). """ if (pids == None): if (self.mode == FILTERON): pids = self.filteredpids else: pids = self.processes.keys() pids.sort() self.log4py.debug("Searching PPIDS of PIDS %s" % str(pids)) ppids = {} for i in range(len(pids)): pptab = ProcessTable() if (self.ppidparam == ""): for j in range(len(osPsParam.keys())): opSysFamily = osPsParam.keys()[j] if (osFamily.has_key(opSysFamily)): if (sys.platform in osFamily[opSysFamily]): self.ppidparam = osPsParam[opSysFamily] if (self.ppidparam == ""): self.log4py.warn("Unknown operating system platform: %s" % sys.platform) self.ppidparam = osPsParam[OSTYPEUNKNOWN] pptab.read(self.ppidparam % pids[i]) ppid = pptab.process(pptab.pids()[0])["PPID"] if (not ppids.has_key(ppid)): ppids[ppid] = [pids[i]] else: ppids[ppid].append(pids[i]) return ppids