#!/usr/bin/python #*********************************************************************************************************** #*********************************************************************************************************** #********* j_baseClasses.py #********* #********* Copyright (C) 2014 Diamond Light Source & Imperial College London #********* #********* Authors: James Foadi & Gwyndaf Evans #********* #********* This code is distributed under the BSD license, a copy of which is #********* included in the root directory of this package. #************************************************************************************************************ #************************************************************************************************************ # Python script # # The date of last modification: 24/03/2005 import os import sys import glob import shutil import re import math import string import time import select, fcntl import signal ################################################# class CExeCode : """ A generic abstract base class that is to be inheritted by other classes that warp a executable code. Basically this class defines two main methods that characterize the procedures a job by an executable code, i.e. forming a command line object and then run the code according to the command line object""" def __init__( self, path_obj = None ): # set pathes if path_obj : self._path_dict = path_obj self.exitCode = 0 # test self._setCmdLineAndFile() def _setCmdLineAndFile(self ): # will be overrided by individual derived classes self._cmdline = 'ls ' self.batch_name = None self.log_name = None def execute(self, mode = 0, err_str ="" ): if self.log_name : self.logMode(mode, err_str) else : self.interactiveMode() def logMode(self, mode = 0, err_str=""): """ execute self._cmdline and output to a log file (self.log_name) in a nonblocking way""" logfile = open(self.log_name, 'w') logfile.write("\n============ PROCESS INFORMATION =============\n") # spawn a sub-process for a job and connect to its input/output(and error) # streams using pipes try : import subprocess subProcess = subprocess.Popen(self._cmdline, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) subProcess.stdin.close() outfile_sub = subProcess.stdout outfd_sub = outfile_sub.fileno() except ImportError: print "module 'subprocess' has not be found, you use a version of PYTHON below 2.4" import popen2 subProcess = popen2.Popen4(self._cmdline) subProcess.tochild.close() outfile_sub = subProcess.fromchild outfd_sub = outfile_sub.fileno() self._pid = subProcess.pid pid_c = self._pid + 1 p_name = self._cmdline.strip().split()[0].split("/")[-1] t_log_name = self.log_name.strip().split("/")[-1] log_size = 0 log_max = 100000000 outfile_eof = 0 if not mode: self._nonBlockingFile(outfd_sub) while not outfile_eof : ready_to_read, ready_to_write, in_error = \ select.select([outfd_sub],[],[],60.0) if outfd_sub in ready_to_read: out_quantum = outfile_sub.read() out_quantum_bt = len(out_quantum) log_size = log_size + out_quantum_bt if log_size > log_max: logfile.write("\nTHE SIZE OF LOG FILE, %s, EXCEEDED THE LIMIT AND PROCESS STOPPED\n"%t_log_name) logfile.write("The process running %s is killed\n"%p_name) logfile.close() os.kill(self._pid, signal.SIGKILL) os.kill(pid_c, signal.SIGKILL) print "The process running %s is killed " %p_name time.sleep(1.0) # be safe stop all process sys.exit(1) else : if out_quantum == '': outfile_eof = 1 logfile.write(out_quantum) logfile.flush() else : self._nonBlockingFile(outfd_sub) while not outfile_eof : ready_to_read, ready_to_write, in_error = \ select.select([outfd_sub],[],[],10) if outfd_sub in ready_to_read: out_quantum = outfile_sub.read() if out_quantum == '': outfile_eof = 1 logfile.write(out_quantum) logfile.flush() # allProcInfo.append(out_quantum) self.exitCode = subProcess.wait() # if mode: # for item in allProcInfo: # logfile.write(item) logfile.write("=========END OF PROCESS INFORMATION\n ==========\n") logfile.close() outfile_sub.close() if self.exitCode: print "#-----------------------------------------------------------#" print "The process stoped because of a runtime error in code '%s'!"%p_name if err_str: print "%s"%err_str else: print "See the associated log file '%s'."%t_log_name print "#-----------------------------------------------------------#" if not err_str: # if normal runtime errors, stop the program(using log. no err_str) # otherwise balbes will go further and let the next procedure to handle the error. sys.exit(1) return True def _nonBlockingFile(self,fd): f_flag = fcntl.fcntl(fd, fcntl.F_GETFL,0) fcntl.fcntl(fd, fcntl.F_SETFL, f_flag | os.O_NONBLOCK) def interactiveMode(self): """ execute self._cmdline just like what is done in a shell """ os.system(self._cmdline) # end