def _stopfile_exit(exitcode, pid): # On Windows, we are in the Repy process, so we can just use harshexit if harshexit.ostype in ["Windows", "WindowsCE"]: # Harshexit will store the appriopriate status for us harshexit.harshexit(exitcode) else: # On NIX we are on the external process try: if exitcode == 44: # Write out status information, repy was Stopped statusstorage.write_status("Stopped") else: # Status terminated statusstorage.write_status("Terminated") except: pass # Disable the other status thread, in case the resource thread detects we've killed repy statusstorage.init(None) # Kill repy harshexit.portablekill(pid) # Fix Derek proposed, this should solve the problem of # the monitor exiting before the repy process. time.sleep(1) # Exit harshexit.harshexit(78)
def run(self): # Run forever while True: # Read a message try: mesg = read_message_from_pipe(self.readhandle) except Exception as e: break # Check for a handler function if mesg[0] in IPC_HANDLER_FUNCTIONS: # Invoke the handler function with the data handler = IPC_HANDLER_FUNCTIONS[mesg[0]] handler(mesg[1]) # Print a message if there is a message on an unknown channel else: print("[WARN] Message on unknown channel from parent process:" + mesg[0]) ### We only leave the loop on a fatal error, so we need to exit now # Write out status information, our parent would do this, but its dead. statusstorage.write_status("Terminated") print("Monitor process died! Terminating!", file=sys.stderr) harshexit.harshexit(70)
def parse_options(options): """ Parse the specified options and initialize all required structures Note: This modifies global state, specifically, the emulcomm module """ if options.ip: emulcomm.user_ip_interface_preferences = True # Append this ip to the list of available ones if it is new for ip in options.ip: if (True, ip) not in emulcomm.user_specified_ip_interface_list: emulcomm.user_specified_ip_interface_list.append((True, ip)) if options.interface: emulcomm.user_ip_interface_preferences = True # Append this interface to the list of available ones if it is new for interface in options.interface: if (False, interface ) not in emulcomm.user_specified_ip_interface_list: emulcomm.user_specified_ip_interface_list.append( (False, interface)) # Check if they have told us to only use explicitly allowed IP's and interfaces if options.nootherips: # Set user preference to True emulcomm.user_ip_interface_preferences = True # Disable nonspecified IP's emulcomm.allow_nonspecified_ips = False # set up the circular log buffer... # Armon: Initialize the circular logger before starting the nanny if options.logfile: # time to set up the circular logger loggerfo = loggingrepy.circular_logger(options.logfile) # and redirect err and out there... sys.stdout = loggerfo sys.stderr = loggerfo else: # let's make it so that the output (via print) is always flushed sys.stdout = loggingrepy.flush_logger(sys.stdout) # We also need to pass in whether or not we are going to be using the service # log for repy. We provide the repy directory so that the vessel information # can be found regardless of where we are called from... tracebackrepy.initialize(options.servicelog, repy_constants.REPY_START_DIR) # Set Current Working Directory if options.cwd: os.chdir(options.cwd) # Update repy current directory repy_constants.REPY_CURRENT_DIR = os.path.abspath(os.getcwd()) # Initialize the NM status interface nmstatusinterface.init(options.stopfile, options.statusfile) # Write out our initial status statusstorage.write_status("Started")
def parse_options(options): """ Parse the specified options and initialize all required structures Note: This modifies global state, specifically, the emulcomm module """ if options.ip: emulcomm.user_ip_interface_preferences = True # Append this ip to the list of available ones if it is new for ip in options.ip: if (True, ip) not in emulcomm.user_specified_ip_interface_list: emulcomm.user_specified_ip_interface_list.append((True, ip)) if options.interface: emulcomm.user_ip_interface_preferences = True # Append this interface to the list of available ones if it is new for interface in options.interface: if (False, interface) not in emulcomm.user_specified_ip_interface_list: emulcomm.user_specified_ip_interface_list.append((False, interface)) # Check if they have told us to only use explicitly allowed IP's and interfaces if options.nootherips: # Set user preference to True emulcomm.user_ip_interface_preferences = True # Disable nonspecified IP's emulcomm.allow_nonspecified_ips = False # set up the circular log buffer... # Armon: Initialize the circular logger before starting the nanny if options.logfile: # time to set up the circular logger loggerfo = loggingrepy.circular_logger(options.logfile) # and redirect err and out there... sys.stdout = loggerfo sys.stderr = loggerfo else: # let's make it so that the output (via print) is always flushed sys.stdout = loggingrepy.flush_logger(sys.stdout) # We also need to pass in whether or not we are going to be using the service # log for repy. We provide the repy directory so that the vessel information # can be found regardless of where we are called from... tracebackrepy.initialize(options.servicelog, repy_constants.REPY_START_DIR) # Set Current Working Directory if options.cwd: os.chdir(options.cwd) # Update repy current directory repy_constants.REPY_CURRENT_DIR = os.path.abspath(os.getcwd()) # Initialize the NM status interface nmstatusinterface.init(options.stopfile, options.statusfile) # Write out our initial status statusstorage.write_status("Started")
def resetvessel(vesselname, exitparams=(44, '')): if vesselname not in vesseldict: raise BadRequest, "No such vessel" # need to try to stop it until it works... while True: try: returnstring = stopvessel(vesselname, exitparams) except BadRequest: # due to the vessel not running... break # if we successfully stopped it, done... if returnstring.endswith('Success'): break # Okay, it is stopped now. Now I'll clean up the file system... filelist = os.listdir(vesselname + "/") # don't delete any files in the protect part of the namespace for filename in filelist: if not filename.startswith("private_"): os.remove(vesselname + "/" + filename) # and remove the log files and stop file... if os.path.exists(vesseldict[vesselname]['logfilename']): os.remove(vesseldict[vesselname]['logfilename']) if os.path.exists(vesseldict[vesselname]['logfilename'] + ".new"): os.remove(vesseldict[vesselname]['logfilename'] + ".new") if os.path.exists(vesseldict[vesselname]['logfilename'] + ".old"): os.remove(vesseldict[vesselname]['logfilename'] + ".old") if os.path.exists(vesseldict[vesselname]['stopfilename']): os.remove(vesseldict[vesselname]['stopfilename']) if os.path.exists(vesseldict[vesselname]['stopfilename']): os.remove(vesseldict[vesselname]['stopfilename']) # change the status to Fresh statusstorage.write_status('Fresh', vesseldict[vesselname]['statusfilename']) # We need to update the status in the table because the status thread might # not notice this before the next request. nmstatusmonitor.update_status(vesseldict, vesselname, 'Fresh', time.time()) return "\nSuccess"
def resetvessel(vesselname,exitparams=(44, '')): if vesselname not in vesseldict: raise BadRequest, "No such vessel" # need to try to stop it until it works... while True: try: returnstring = stopvessel(vesselname,exitparams) except BadRequest: # due to the vessel not running... break # if we successfully stopped it, done... if returnstring.endswith('Success'): break # Okay, it is stopped now. Now I'll clean up the file system... filelist = os.listdir(vesselname+"/") # don't delete any files in the protect part of the namespace for filename in filelist: if not filename.startswith("private_"): os.remove(vesselname+"/"+filename) # and remove the log files and stop file... if os.path.exists(vesseldict[vesselname]['logfilename']): os.remove(vesseldict[vesselname]['logfilename']) if os.path.exists(vesseldict[vesselname]['logfilename']+".new"): os.remove(vesseldict[vesselname]['logfilename']+".new") if os.path.exists(vesseldict[vesselname]['logfilename']+".old"): os.remove(vesseldict[vesselname]['logfilename']+".old") if os.path.exists(vesseldict[vesselname]['stopfilename']): os.remove(vesseldict[vesselname]['stopfilename']) if os.path.exists(vesseldict[vesselname]['stopfilename']): os.remove(vesseldict[vesselname]['stopfilename']) # change the status to Fresh statusstorage.write_status('Fresh',vesseldict[vesselname]['statusfilename']) # We need to update the status in the table because the status thread might # not notice this before the next request. nmstatusmonitor.update_status(vesseldict, vesselname, 'Fresh', time.time()) return "\nSuccess"
def _internal_error(message): try: print >> sys.stderr, message sys.stderr.flush() except: pass # Stop the nmstatusinterface, we don't want any more status updates nmstatusinterface.stop() # Kill repy harshexit.portablekill(childpid) try: # Write out status information, repy was Stopped statusstorage.write_status("Terminated") except: pass
def harshexit(val): global ostype global osrealtype if ostype == None: init_ostype() # The problem is that there can be multiple calls to harshexit before we # stop. For example, a signal (like we may send to kill) may trigger a # call. As a result, we block all other status writers the first time this # is called, but don't later on... if not statusexiting[0]: # do this once (now) statusexiting[0] = True # prevent concurrent writes to status info (acquire the lock to stop others, # but do not block... statuslock.acquire() # we are stopped by the stop file watcher, not terminated through another # mechanism if val == 4: # we were stopped by another thread. Let's exit pass # Special Termination signal to notify the NM of excessive threads elif val == 56: statusstorage.write_status("ThreadErr") elif val == 44: statusstorage.write_status("Stopped") else: # generic error, normal exit, or exitall in the user code... statusstorage.write_status("Terminated") # We intentionally do not release the lock. We don't want anyone else # writing over our status information (we're killing them). if ostype == 'Linux': # The Nokia N800 refuses to exit on os._exit() by a thread. I'm going to # signal our pid with SIGTERM (or SIGKILL if needed) portablekill(os.getpid()) # os._exit(val) elif ostype == 'Darwin': os._exit(val) elif ostype == 'Windows': # stderr is not automatically flushed in Windows... sys.stderr.flush() os._exit(val) else: raise UnsupportedSystemException, "Unsupported system type: '"+osrealtype+"' (alias: "+ostype+")"
def harshexit(val): global ostype global osrealtype if ostype == None: init_ostype() # The problem is that there can be multiple calls to harshexit before we # stop. For example, a signal (like we may send to kill) may trigger a # call. As a result, we block all other status writers the first time this # is called, but don't later on... if not statusexiting[0]: # do this once (now) statusexiting[0] = True # prevent concurrent writes to status info (acquire the lock to stop others, # but do not block... statuslock.acquire() # we are stopped by the stop file watcher, not terminated through another # mechanism if val == 4: # we were stopped by another thread. Let's exit pass # Special Termination signal to notify the NM of excessive threads elif val == 56: statusstorage.write_status("ThreadErr") elif val == 44: statusstorage.write_status("Stopped") else: # generic error, normal exit, or exitall in the user code... statusstorage.write_status("Terminated") # We intentionally do not release the lock. We don't want anyone else # writing over our status information (we're killing them). if ostype == 'Linux': # The Nokia N800 refuses to exit on os._exit() by a thread. I'm going to # signal our pid with SIGTERM (or SIGKILL if needed) portablekill(os.getpid()) # os._exit(val) elif ostype == 'Darwin': os._exit(val) elif ostype == 'Windows' or ostype == 'WindowsCE': # stderr is not automatically flushed in Windows... sys.stderr.flush() os._exit(val) else: raise UnsupportedSystemException, "Unsupported system type: '"+osrealtype+"' (alias: "+ostype+")"
class monitor_process_checker(threading.Thread): def __init__(self, readhandle): """ <Purpose> Terminates harshly if the monitor process dies before we do. <Arguments> readhandle: A file descriptor to the handle of a pipe to the monitor process. """ # Name our self threading.Thread.__init__(self, name="ProcessChecker") # Store the handle self.readhandle = readhandle def run(self): # Run forever while True: # Read a message try: mesg = read_message_from_pipe(self.readhandle) except Exception, e: break # Check for a handler function if mesg[0] in IPC_HANDLER_FUNCTIONS: # Invoke the handler function with the data handler = IPC_HANDLER_FUNCTIONS[mesg[0]] handler(mesg[1]) # Print a message if there is a message on an unknown channel else: print "[WARN] Message on unknown channel from monitor process:", mesg[ 0] ### We only leave the loop on a fatal error, so we need to exit now # Write out status information, the monitor process would do this, but its dead. statusstorage.write_status("Terminated") print >> sys.stderr, "Monitor process died! Terminating!", repr(e) harshexit.harshexit(70)
def repy_main(argv=sys.argv): """Run repy with these command line arguments""" global simpleexec global logfile # Armon: The CMD line path to repy is the first argument repy_location = argv[0] # Get the directory repy is in repy_directory = os.path.dirname(repy_location) # Translate into an absolute path if os.path.isabs(repy_directory): absolute_repy_directory = repy_directory else: # This will join the currect directory with the relative path # and then get the absolute path to that location absolute_repy_directory = os.path.abspath(os.path.join(os.getcwd(), repy_directory)) # Store the absolute path as the repy startup directory repy_constants.REPY_START_DIR = absolute_repy_directory # For security, we need to make sure that the Python path doesn't change even # if the directory does... newsyspath = [] for item in sys.path[:]: if item == '' or item == '.': newsyspath.append(os.getcwd()) else: newsyspath.append(item) # It should be safe now. I'm assuming the user isn't trying to undercut us # by setting a crazy python path sys.path = newsyspath args = argv[1:] try: optlist, fnlist = getopt.getopt(args, '', [ 'simple', 'ip=', 'iface=', 'nootherips', 'logfile=', 'stop=', 'status=', 'cwd=', 'servicelog', 'safebinary' ]) except getopt.GetoptError: usage() sys.exit(1) # Set up the simple variable if needed simpleexec = False # By default we don't want to use the service logger servicelog = False # Default logfile (if the option --logfile isn't passed) logfile = None # Default stopfile (if the option --stopfile isn't passed) stopfile = None # Default stopfile (if the option --stopfile isn't passed) statusfile = None if len(fnlist) < 2: usage("Must supply a resource file and a program file to execute") sys.exit(1) for option, value in optlist: if option == '--simple': simpleexec = True elif option == '--ip': emulcomm.user_ip_interface_preferences = True # Append this ip to the list of available ones if it is new, since # multiple IP's may be specified if (True, value) not in emulcomm.user_specified_ip_interface_list: emulcomm.user_specified_ip_interface_list.append((True, value)) elif option == '--iface': emulcomm.user_ip_interface_preferences = True # Append this interface to the list of available ones if it is new if (False, value) not in emulcomm.user_specified_ip_interface_list: emulcomm.user_specified_ip_interface_list.append((False, value)) # Check if they have told us explicitly not to allow other IP's elif option == '--nootherips': # Set user preference to True emulcomm.user_ip_interface_preferences = True # Disable nonspecified IP's emulcomm.allow_nonspecified_ips = False elif option == '--logfile': # set up the circular log buffer... logfile = value elif option == '--stop': # Watch for the creation of this file and abort when it happens... stopfile = value elif option == '--status': # Write status information into this file... statusfile = value # Set Current Working Directory elif option == '--cwd': os.chdir(value) # Enable logging of internal errors to the service logger. elif option == '--servicelog': servicelog = True # Enable safe binary mode elif option == '--safebinary': safebinary.SAFEBINARY = True # Update repy current directory repy_constants.REPY_CURRENT_DIR = os.path.abspath(os.getcwd()) # Initialize the NM status interface nmstatusinterface.init(stopfile, statusfile) # Write out our initial status statusstorage.write_status("Started") resourcefn = fnlist[0] progname = fnlist[1] progargs = fnlist[2:] # We also need to pass in whether or not we are going to be using the service # log for repy. We provide the repy directory so that the vessel information # can be found regardless of where we are called from... tracebackrepy.initialize(servicelog, absolute_repy_directory) try: main(resourcefn, progname, progargs) except SystemExit: harshexit.harshexit(4) except: tracebackrepy.handle_exception() harshexit.harshexit(3)
# Set Current Working Directory elif option == '--cwd': os.chdir(value) # Enable logging of internal errors to the service logger. elif option == '--servicelog': servicelog = True # Update repy current directory repy_constants.REPY_CURRENT_DIR = os.path.abspath(os.getcwd()) # Initialize the NM status interface nmstatusinterface.init(stopfile, statusfile) # Write out our initial status statusstorage.write_status("Started") restrictionsfn = fnlist[0] progname = fnlist[1] progargs = fnlist[2:] # We also need to pass in whether or not we are going to be using the service # log for repy. We provide the repy directory so that the vessel information # can be found regardless of where we are called from... tracebackrepy.initialize(servicelog, absolute_repy_directory) try: main(restrictionsfn, progname, progargs) except SystemExit: harshexit.harshexit(4) except:
def run(self): global stopfilename, frequency, run_thread_lock # On Windows elevate our priority above the user code. if harshexit.ostype in ["Windows", "WindowsCE"]: # Elevate our priority, above normal is higher than the usercode windows_api.set_current_thread_priority(windows_api.THREAD_PRIORITY_ABOVE_NORMAL) while True: # Attempt to get the lock have_lock = run_thread_lock.acquire(False) # If we have the lock, release and continue. Else break and exit the thread if have_lock: run_thread_lock.release() else: break # Get the status lock statuslock.acquire() # Write out our status statusstorage.write_status("Started") # Release the status lock statuslock.release() # Look for the stopfile if stopfilename != None and os.path.exists(stopfilename): try: # Get a file object for the file fileobject = safe_open(stopfilename) # Read in the contents, close the object contents = fileobject.read() fileobject.close() # Check the length, if there is nothing then just close as stopped if len(contents) > 0: # Split, at most we have 2 parts, the exit code and message (exitcode, mesg) = contents.split(";",1) exitcode = int(exitcode) # Check if exitcode is 56, which stands for ThreadErr is specified # ThreadErr cannot be specified externally, since it has side-affects # such as changing global thread restrictions if exitcode == 56: raise Exception, "ThreadErr exit code specified. Exit code not allowed." # Print the message, then call harshexit with the exitcode if mesg != "": print mesg _stopfile_exit(exitcode, self.repy_process_id) else: raise Exception, "Stopfile has no content." except: # On any issue, just do "Stopped" (44) _stopfile_exit(44, self.repy_process_id) # Sleep until the next loop around. time.sleep(frequency)
exp) + " Monitor death! Impolitely killing repy process!" monitor_exit_code = 98 finally: if (error_msg): print >> sys.stderr, error_msg # Repy/Montior proccesses both _exit, so the thread should be stopped anyway # nmstatusinterface.stop() if (kill_repy): harshexit.portablekill(repypid) # XXX LP: Is this actually doeing something??? try: statusstorage.write_status("Terminated") except: pass # The monitor process (child) should always exit this way on android # because we don't want the child to return back to Java if repy (parent) # is not alive anymore, which it should not be at this point harshexit.harshexit(monitor_exit_code) def resource_monitor(repypid, pipe_handle): """ <Purpose> Function runs in a loop forever, checking resource usage and throttling CPU. Checks CPU, memory, and disk.
# Enable logging of internal errors to the service logger. elif option == '--servicelog': servicelog = True # Insert program execution separators and log calling information elif option == '--execinfo': displayexecinfo = True # Update repy current directory repy_constants.REPY_CURRENT_DIR = os.path.abspath(os.getcwd()) # Initialize the NM status interface nmstatusinterface.init(stopfile, statusfile) # Write out our initial status statusstorage.write_status("Started") restrictionsfn = fnlist[0] progname = fnlist[1] progargs = fnlist[2:] # We also need to pass in whether or not we are going to be using the service # log for repy. We provide the repy directory so that the vessel information # can be found regardless of where we are called from... tracebackrepy.initialize(servicelog, absolute_repy_directory) try: main(restrictionsfn, progname, progargs) except SystemExit: harshexit.harshexit(4) except:
def repy_main(argv=sys.argv): """Run repy with these command line arguments""" global simpleexec global logfile # Armon: The CMD line path to repy is the first argument repy_location = argv[0] # Get the directory repy is in repy_directory = os.path.dirname(repy_location) # Translate into an absolute path if os.path.isabs(repy_directory): absolute_repy_directory = repy_directory else: # This will join the currect directory with the relative path # and then get the absolute path to that location absolute_repy_directory = os.path.abspath( os.path.join(os.getcwd(), repy_directory)) # Store the absolute path as the repy startup directory repy_constants.REPY_START_DIR = absolute_repy_directory # For security, we need to make sure that the Python path doesn't change even # if the directory does... newsyspath = [] for item in sys.path[:]: if item == '' or item == '.': newsyspath.append(os.getcwd()) else: newsyspath.append(item) # It should be safe now. I'm assuming the user isn't trying to undercut us # by setting a crazy python path sys.path = newsyspath args = argv[1:] try: optlist, fnlist = getopt.getopt(args, '', [ 'simple', 'ip=', 'iface=', 'nootherips', 'logfile=', 'stop=', 'status=', 'cwd=', 'servicelog', 'safebinary' ]) except getopt.GetoptError: usage() sys.exit(1) # Set up the simple variable if needed simpleexec = False # By default we don't want to use the service logger servicelog = False # Default logfile (if the option --logfile isn't passed) logfile = None # Default stopfile (if the option --stopfile isn't passed) stopfile = None # Default stopfile (if the option --stopfile isn't passed) statusfile = None if len(fnlist) < 2: usage("Must supply a resource file and a program file to execute") sys.exit(1) for option, value in optlist: if option == '--simple': simpleexec = True elif option == '--ip': emulcomm.user_ip_interface_preferences = True # Append this ip to the list of available ones if it is new, since # multiple IP's may be specified if (True, value) not in emulcomm.user_specified_ip_interface_list: emulcomm.user_specified_ip_interface_list.append((True, value)) elif option == '--iface': emulcomm.user_ip_interface_preferences = True # Append this interface to the list of available ones if it is new if (False, value) not in emulcomm.user_specified_ip_interface_list: emulcomm.user_specified_ip_interface_list.append( (False, value)) # Check if they have told us explicitly not to allow other IP's elif option == '--nootherips': # Set user preference to True emulcomm.user_ip_interface_preferences = True # Disable nonspecified IP's emulcomm.allow_nonspecified_ips = False elif option == '--logfile': # set up the circular log buffer... logfile = value elif option == '--stop': # Watch for the creation of this file and abort when it happens... stopfile = value elif option == '--status': # Write status information into this file... statusfile = value # Set Current Working Directory elif option == '--cwd': os.chdir(value) # Enable logging of internal errors to the service logger. elif option == '--servicelog': servicelog = True # Enable safe binary mode elif option == '--safebinary': safebinary.SAFEBINARY = True # Update repy current directory repy_constants.REPY_CURRENT_DIR = os.path.abspath(os.getcwd()) # Initialize the NM status interface nmstatusinterface.init(stopfile, statusfile) # Write out our initial status statusstorage.write_status("Started") resourcefn = fnlist[0] progname = fnlist[1] progargs = fnlist[2:] # We also need to pass in whether or not we are going to be using the service # log for repy. We provide the repy directory so that the vessel information # can be found regardless of where we are called from... tracebackrepy.initialize(servicelog, absolute_repy_directory) return init_namespace(resourcefn, progname, progargs)
def run(self): global stopfilename, frequency, run_thread_lock # On Windows elevate our priority above the user code. if harshexit.ostype in ["Windows", "WindowsCE"]: # Elevate our priority, above normal is higher than the usercode windows_api.set_current_thread_priority( windows_api.THREAD_PRIORITY_ABOVE_NORMAL) while True: # Attempt to get the lock have_lock = run_thread_lock.acquire(False) # If we have the lock, release and continue. Else break and exit the thread if have_lock: run_thread_lock.release() else: break # Get the status lock statuslock.acquire() # Write out our status statusstorage.write_status("Started") # Release the status lock statuslock.release() # Look for the stopfile if stopfilename != None and os.path.exists(stopfilename): try: # Get a file object for the file fileobject = safe_open(stopfilename) # Read in the contents, close the object contents = fileobject.read() fileobject.close() # Check the length, if there is nothing then just close as stopped if len(contents) > 0: # Split, at most we have 2 parts, the exit code and message (exitcode, mesg) = contents.split(";", 1) exitcode = int(exitcode) # Check if exitcode is 56, which stands for ThreadErr is specified # ThreadErr cannot be specified externally, since it has side-affects # such as changing global thread restrictions if exitcode == 56: raise Exception, "ThreadErr exit code specified. Exit code not allowed." # Print the message, then call harshexit with the exitcode if mesg != "": print mesg _stopfile_exit(exitcode, self.repy_process_id) else: raise Exception, "Stopfile has no content." except: # On any issue, just do "Stopped" (44) _stopfile_exit(44, self.repy_process_id) # Sleep until the next loop around. time.sleep(frequency)