# -*- coding: iso-8859-1 -*- import os import sys import string import popen2 import stat import re import time import commands import select from os.path import join ############################################################################ ## Error and success return function def S_ERROR( sMessage = '' ): return { 'Status': -1, 'OK' : 0, 'Message' : sMessage } def S_OK( sValue = None, sPname = 'Value' ): dResult = { 'Status': 'OK', 'OK' : 1 } if sValue is not None: dResult[ sPname ] = sValue return dResult ############################################################################ class SubprocessExecuter: def __init__( self, iTimeout = False ): self.changeTimeout( iTimeout ) self.iBufferLimit = 5242880 # 5MB limit for data def changeTimeout( self, iTimeout ): self.iTimeout = iTimeout if self.iTimeout == 0: self.iTimeout = False def __readFromPipe( self, oPipe, iBaseLength = 0 ): sData = "" iMaxSliceLength = 8192 iLastSliceLength = 8192 while iLastSliceLength == iMaxSliceLength: sReadBuffer = os.read( oPipe, iMaxSliceLength ) iLastSliceLength = len( sReadBuffer ) sData += sReadBuffer if len( sData ) + iBaseLength > self.iBufferLimit: dRetVal = S_ERROR( "Reached maximum allowed length (%d bytes) for called function return value" % self.iBufferLimit ) dRetVal[ 'ReadData' ] = sData return dRetVal return S_OK( sData ) def __executePythonFunction( self, oFunc, stArgs, oWritePipe ): try: os.write( oWritePipe, "%s\n" % str( S_OK( oFunc( *stArgs ) ) ) ) except Exception, v: os.write( oWritePipe, "%s\n" % str( S_ERROR( str( v ) ) ) ) try: os.close( oWritePipe ) finally: os._exit(0) def __selectFD( self, lR, iTimeout = False ): if self.iTimeout and not iTimeout: iTimeout = self.iTimeout if not iTimeout: return select.select( lR , [], [] )[0] else: return select.select( lR , [], [], iTimeout )[0] def pythonCall( self, oFunction, stArgs ): oReadPipe, oWritePipe = os.pipe() iPid = os.fork() if iPid == 0: os.close( oReadPipe ) self.__executePythonFunction( oFunction, stArgs, oWritePipe ) os.close( oWritePipe ) else: os.close( oWritePipe ) lReadable = self.__selectFD( [ oReadPipe ] ) if len( lReadable ) == 0: os.close( oReadPipe ) os.kill( iPid, 9 ) os.waitpid( iPid, 0 ) return S_ERROR( "%d seconds timeout for '%s' call" % ( self.iTimeout, oFunction.__name__ ) ) elif lReadable[0] == oReadPipe: dData = self.__readFromPipe( oReadPipe ) os.close( oReadPipe ) os.waitpid( iPid, 0 ) if dData[ 'OK' ]: return eval( dData[ 'Value' ] ) return dData def __generateSystemCommandError( self, sMessage ): retVal = S_ERROR( sMessage ) retVal[ 'stdout' ] = self.lBuffers[0][0] retVal[ 'stderr' ] = self.lBuffers[1][0] return retVal def __readFromFile( self, oFile, iBaseLength, bAll ): try: if bAll: sData = "".join( oFile.readlines() ) else: sData = oFile.readline() except Exception, v: pass if sData == "": #self.checkAlive() self.bAlive = False if len( sData ) + iBaseLength > self.iBufferLimit: dRetVal = S_ERROR( "Reached maximum allowed length (%d bytes) for called function return value" % self.iBufferLimit ) dRetVal[ 'ReadData' ] = sData return dRetVal return S_OK( sData ) def __readFromSystemCommandOutput( self, oFile, iDataIndex, bAll = False ): retVal = self.__readFromFile( oFile, len( self.lBuffers[ iDataIndex ][0] ), bAll ) if retVal[ 'OK' ]: self.lBuffers[ iDataIndex ][0] += retVal[ 'Value' ] if not self.oCallback == None: while self.__callLineCallback( iDataIndex ): pass return S_OK() else: self.lBuffers[ iDataIndex ][0] += retVal[ 'ReadData' ] os.kill( self.oChild.pid, 9 ) self.oChild.wait() return self.__generateSystemCommandError( "Exceeded maximum buffer size ( %d bytes ) timeout for '%s' call" % ( self.iBufferLimit, self.sCmd ) ) def systemCall( self, sCmd, oCallbackFunction = None, arg=None ): self.sCmd = sCmd self.oCallback = oCallbackFunction self.arg = arg self.oChild = popen2.Popen3( self.sCmd, True ) self.lBuffers = [ [ "", 0 ], [ "", 0 ] ] iInitialTime = time.time() iExitStatus = self.oChild.poll() while iExitStatus == -1: retVal = self.__readFromCommand() if not retVal[ 'OK' ]: return retVal if self.iTimeout and time.time() - iInitialTime > self.iTimeout: os.kill( self.oChild.pid, 9 ) self.oChild.wait() self.__readFromCommand( True ) self.oChild.fromchild.close() self.oChild.childerr.close() return self.__generateSystemCommandError( "Timeout (%d seconds) for '%s' call" % ( self.iTimeout, sCmd ) ) iExitStatus = self.oChild.poll() self.__readFromCommand(True ) self.oChild.fromchild.close() self.oChild.childerr.close() return S_OK( ( iExitStatus / 256, self.lBuffers[0][0], self.lBuffers[1][0] ) ) def __readFromCommand( self, bLast = False ): if bLast: retVal = self.__readFromSystemCommandOutput( self.oChild.fromchild, 0, True ) if not retVal[ 'OK' ]: return retVal retVal = self.__readFromSystemCommandOutput( self.oChild.childerr, 1, True ) if not retVal[ 'OK' ]: return retVal else: lReadable = self.__selectFD( [ self.oChild.fromchild, self.oChild.childerr ], 1 ) if self.oChild.fromchild in lReadable: retVal = self.__readFromSystemCommandOutput( self.oChild.fromchild, 0 ) if not retVal[ 'OK' ]: return retVal if self.oChild.childerr in lReadable: retVal = self.__readFromSystemCommandOutput( self.oChild.childerr, 1 ) if not retVal[ 'OK' ]: return retVal return S_OK() def __callLineCallback( self, iIndex ): iNextLine = self.lBuffers[ iIndex ][0][ self.lBuffers[ iIndex ][1]: ].find( "\n" ) if iNextLine > -1: self.oCallback( iIndex, self.lBuffers[ iIndex ][0][ self.lBuffers[ iIndex ][1]: self.lBuffers[ iIndex ][1] + iNextLine ], arg=self.arg ) self.lBuffers[ iIndex ][1] += iNextLine + 1 return True return False ############################################################################ def redirectOutput(index, buffer): """Filter function to redirect the std output and error of the job executable for real-time debugging """ print buffer ############################################################################ def exeCommand( sCmd, iTimeout = 0, oLineCallback = redirectOutput, arg=None): """Return ( status, output, error, pythonError ) of executing cmd in a shell.""" oSPE = SubprocessExecuter( iTimeout ) retVal = oSPE.systemCall( sCmd, oLineCallback, arg=arg) if retVal[ 'OK' ]: return retVal[ 'Value' ][0], retVal[ 'Value' ][1], retVal[ 'Value' ][2], 0 else: if re.search("Timeout",retVal['Message']): return 1, retVal['stdout'], retVal['Message'], 2 else: return 1, retVal['stdout'], retVal['stderr'], 1 ############################################################################ def getstatusoutput(cmd): """Return (status, stdout) of executing cmd in a shell. A trailing line separator is removed from the output string. The exit status of the command is encoded in the format specified for wait(), when the exit status is zero (termination without errors), 0 is returned. """ import os p = os.popen(cmd, 'r') out = p.read() sts = p.close() if sts is None: sts = 0 if out.endswith(os.linesep): out = out[:out.rindex(os.linesep)] return sts, out ################################################################################# ###################################### EoF ###################################### #################################################################################