import os import sys import subprocess from subprocess import Popen from enum import Enum from time import sleep, localtime, strftime from threading import Thread import tempfile import getpass from casa_shutdown import add_shutdown_hook class output_option(Enum): PIPE = 1 STDOUT = 2 DISCARD = 3 ## ## dgtool's pylot class was a useful reference for creating this class: ## http://dgtool.treitos.com/2012/05/python-subprocess-manager-pylot.html ## class procmgr(Thread): from procmgr import output_option class proc(Thread): def __init__(self, tag, cmd, output=output_option.DISCARD): """ tag: identification for this process cmd: list of strings representing the command followed by the arguments output: disposition of output STDOUT, PIPE, DISCARD (default is to discard output)""" Thread.__init__(self) self.tag = tag self.pid = None self.stderr = None self.stdout = None self.stdin = None self.__cmd = cmd self.__output_option = output self.__running = False self.__proc = None self.__watchdog = None def stop(self): """ stop( ) -> None stops the process if it is running""" if self.__running: self.__running = False if self.__proc and self.__proc.poll( ) == None: #print "%s => proc %s is being stopped" % (strftime("%y-%m-%d %H:%M:%S", localtime()), self.tag) try: self.__proc.terminate() sleep(0.5) if self.__proc.poll( ) is None: print "%s => proc %s is being killed" % (strftime("%y-%m-%d %H:%M:%S", localtime()), self.tag) self.__proc.kill() if self.__watchdog.poll( ) is None: self.__watchdog.kill() except OSError: pass def running(self): """ running( ) -> Bool check to see if the process is still running""" return self.__running def run(self): """ run( ) -> None start the process""" self.stop( ) #print "%s => proc %s is being started" % (strftime("%y-%m-%d %H:%M:%S", localtime()), self.tag) if self.__output_option is output_option.PIPE: out = subprocess.PIPE elif self.__output_option is output_option.STDOUT: try: out = os.fdopen(sys.stdout.fileno(), 'a', 0) except: # nose likes to wedge their own "nose.plugins.xunit.Tee" object into # sys.stdout... unfortunately, it does not have 'fileno( )' et al. out = os.fdopen(1,'a',0) else: out = file(os.devnull,'a') self.__proc = Popen( self.__cmd, stderr=out, stdout=out, stdin=subprocess.PIPE ) self.__watchdog = Popen( [ '/bin/bash','-c', 'while kill -0 %d > /dev/null 2>&1; do sleep 1; kill -0 %d > /dev/null 2>&1 || kill -9 %d > /dev/null 2>&1; done' % \ (self.__proc.pid, os.getpid( ), self.__proc.pid) ] ) self.stdin = self.__proc.stdin self.pid = self.__proc.pid if self.__output_option is output_option.PIPE: self.stdout = self.__proc.stdout self.stderr = self.__proc.stderr self.__running = True ## at this point, processes could automatically be restarted ## (in a loop) if __running is not false when the process ## expires... (as pylot does) self.__proc.wait( ) # --- --- --- procmgr --- --- --- --- --- --- --- --- --- --- --- --- def __init__(self): Thread.__init__(self) self.__procs = { } self.__running = True add_shutdown_hook(self.shutdown) add_shutdown_hook(self.removeCrashReporterDir) def running(self, tag): """tag: process identifier to check return True if a process with the given tag exists and is running""" if self.__running == False: return False if self.__procs.has_key( tag ): return self.__procs[tag].running( ) return False def create(self, tag, cmd, with_output=False): """create(...) -> proc tag: identifier for the new process cmd: list of strings representing the command followed by the arguments with_output: should output be collected (default is to discard output)""" if self.__running == False: return None if not isinstance(cmd, (list,str)): print "'cmd' is not a list of strings" return None if self.running(tag): print "process with %s tag already exists" % tag return None self.__procs[tag] = self.proc(tag,cmd,with_output) self.__procs[tag].start( ) sleep(0.1) return self.__procs[tag] def fetch(self,tag): """fetch the process associated with 'tag'""" if self.__running == False: return None if self.__procs.has_key( tag ): return self.__procs[tag] return None def shutdown(self): """stops all managed processes""" if self.__running: for p in self.__procs: self.__procs[p].stop( ) self.__running = False def removeCrashReporterDir(self): temporaryDirectory = tempfile.gettempdir() + "/casa-crashreporter/" + getpass.getuser() + "/" + str(os.getpid()) if os.path.exists(temporaryDirectory): try: os.rmdir(temporaryDirectory) except: print "Couldn't remove " + temporaryDirectory def __delitem__(self, key): print "you cannot stop processes this way" return None def __getitem__(self, tag): """fetch the process associated with 'tag'""" return self.fetch(tag) def __setitem__(self, key, value): print "you cannot create processes this way" return None def run(self): while self.__running: sleep(10)