def restart_software_updater(): """ <Purpose> Attempts to start a new software updater, and will exit this one if the new one seems to start successfully. If the new one does not start successfully, then we just return. <Arguments> None <Exceptions> Possible exception if there is problems writing the OK file. <Side Effects> If all goes well, a new softwareupdater will be started, and this one will exit. <Returns> In the successful case, it will not return. If the new softwareupdater does not start correctly, we will return None. """ safe_log( "[restart_software_updater] Attempting to restart software updater.") # find an unused mutex thismutex = get_mutex() # starts new with arg that is the mutex junkupdaterobject = portable_popen.Popen( ["python", "softwareupdater.py", thismutex]) # wait for some time (1 minute) for them to init and stop them if they don't for junkcount in range(30): misc.do_sleep(2.0) # if "OK" file exists, release softwareupdater.old, remove OK file and exit if os.path.exists("softwareupdater.OK." + thismutex): runonce.releaseprocesslock('softwareupdater.old') os.remove("softwareupdater.OK." + thismutex) # I'm happy, it is taking over safe_log( "[restart_software_updater] The new instance of the software updater is running. This one is exiting." ) sys.exit(10) # else write "stop" file because it failed... file("softwareupdater.stop." + thismutex, "w").close() safe_log( "[restart_software_updater] Failed to restart software updater. This instance will continue." ) # I continue normal operation return
def restart_software_updater(): """ <Purpose> Attempts to start a new software updater, and will exit this one if the new one seems to start successfully. If the new one does not start successfully, then we just return. <Arguments> None <Exceptions> Possible exception if there is problems writing the OK file. <Side Effects> If all goes well, a new softwareupdater will be started, and this one will exit. <Returns> In the successful case, it will not return. If the new softwareupdater does not start correctly, we will return None. """ safe_log("[restart_software_updater] Attempting to restart software updater.") # find an unused mutex thismutex = get_mutex() # starts new with arg that is the mutex junkupdaterobject = portable_popen.Popen(["python", "softwareupdater.py", thismutex]) # wait for some time (1 minute) for them to init and stop them if they don't for junkcount in range(30): misc.do_sleep(2.0) # if "OK" file exists, release softwareupdater.old, remove OK file and exit if os.path.exists("softwareupdater.OK." + thismutex): runonce.releaseprocesslock("softwareupdater.old") os.remove("softwareupdater.OK." + thismutex) # I'm happy, it is taking over safe_log( "[restart_software_updater] The new instance of the software updater is running. This one is exiting." ) sys.exit(10) # else write "stop" file because it failed... file("softwareupdater.stop." + thismutex, "w").close() safe_log("[restart_software_updater] Failed to restart software updater. This instance will continue.") # I continue normal operation return
def sleep(seconds): """ <Purpose> Allow the current event to pause execution (similar to time.sleep()). This function will not return early for any reason <Arguments> seconds: The number of seconds to sleep. This can be a floating point value <Exceptions> None. <Side Effects> None. <Returns> None. """ restrictions.assertisallowed('sleep', seconds) # Use the do_sleep implementation in misc misc.do_sleep(seconds)
def sleep(seconds): """ <Purpose> Allow the current event to pause execution (similar to time.sleep()). This function will not return early for any reason <Arguments> seconds: The number of seconds to sleep. This can be a floating point value <Exceptions> None. <Side Effects> None. <Returns> None. """ restrictions.assertisallowed('sleep',seconds) # Use the do_sleep implementation in misc misc.do_sleep(seconds)
def main(): """ <Purpose> Has an infinite loop where we sleep for 5-55 minutes, then check for updates. If an update happens, we will restart ourselves and/or the node manager as necesary. <Arguments> None <Exceptions> Any non-RsyncError exceptions from do_rsync. <Side Effects> If there is an update on the update site we are checking, it will be grabbed eventually. <Return> Will not return. Either an exception will be thrown, we exit because we are restarting, or we loop infinitely. """ global restartme # This is similar to init only: # 1) we loop / sleep # 2) we restart ourselves if we are updated # 3) we restart our client if they are updated while True: # sleep for 5-55 minutes for junk in range(random.randint(10, 110)): # We need to wake up every 30 seconds otherwise we will take # the full 5-55 minutes before we die when someone tries to # kill us nicely. misc.do_sleep(30) # Make sure we still have the process lock. # If not, we should exit if not runonce.stillhaveprocesslock('softwareupdater.old'): safe_log('[main] We no longer have the processlock\n') sys.exit(55) # Make sure that if we failed somehow to restart, we keep trying before # every time we try to update. - Brent if restartme: restart_software_updater() # where I'll put files... tempdir = tempfile.mkdtemp()+"/" # I'll clean this up in a minute try: updatedlist = do_rsync(softwareurl, "./",tempdir) except RsyncError: # oops, hopefully this will be fixed next time... continue finally: shutil.rmtree(tempdir) # no updates :) Let's wait again... if updatedlist == []: continue # if there were updates, the metainfo file should be one of them... assert('metainfo' in updatedlist) clientlist = updatedlist[:] if 'softwareupdater.py' in clientlist: restartme = True clientlist.remove('softwareupdater.py') # if the client software changed, let's update it! # AR: On Android, the native app takes care of starting/restarting # the client and/or updater, depending on the exit code we return here. if clientlist != []: if not is_android: restart_client(clientlist) else: sys.exit(200) # Native app should restart both client and updater # oh! I've changed too. I should restart... search for MUTEX for info if restartme: if not is_android: restart_software_updater() else: sys.exit(201) # Native app should restart the updater
def software_updater_start(mutexname): """ <Purpose> When restarting the software updater, this method is called in the new one. It will write an OK file to let the original know it has started, then will wait for the original to acknowledge by either removing the OK file, meaning we should carry on, or by writing a stop file, meaning we should exit. Carrying on means getting the softwareupdater.old lock, and releasing the softwareupdater.new lock, then returning. <Arguments> mutexname - The new software updater was started with a given mutex name, which is used to uniquely identify the stop and OK files as coming from this softwareupdater. This way the old one can know that the softwareupdater it started is the one that is continueing on. <Exceptions> Possible Exception creating the OK file. <Side Effects> Acquires the softwareupdater.old lock and releases the softwareupdater.new lock. <Return> None """ safe_log("[software_updater_start] This is a new software updater process started by an existing one.") # if "stop" file exists, then exit if os.path.exists("softwareupdater.stop."+mutexname): safe_log("[software_updater_start] There's a stop file. Exiting.") sys.exit(2) # write "OK" file file("softwareupdater.OK."+mutexname,"w").close() # while "OK" file exists while os.path.exists("softwareupdater.OK."+mutexname): safe_log("[software_updater_start] Waiting for the file softwareupdater.OK."+mutexname+" to be removed.") misc.do_sleep(1.0) # if "stop" file exists, then exit if os.path.exists("softwareupdater.stop."+mutexname): sys.exit(3) # Get the process lock for the main part of the program. gotlock = runonce.getprocesslock("softwareupdater.old") # Release the lock on the initialization part of the program runonce.releaseprocesslock('softwareupdater.new') if gotlock == True: # I got the lock. All is well... pass else: if gotlock: safe_log("[software_updater_start] Another software updater old process (pid: "+str(gotlock)+") is running") sys.exit(55) else: safe_log("[software_updater_start] Another software updater old process is running") sys.exit(55) safe_log("[software_updater_start] This software updater process is now taking over.") # start normal operation return
if __name__ == '__main__': read_environmental_options() if not run_softwareupdater_in_foreground: daemon.daemonize() # Initialize the service logger. safe_servicelogger_init() # problems here are fatal. If they occur, the old updater won't stop... try: init() except Exception, e: safe_log_last_exception() raise e # in case there is an unexpected exception, continue (we'll sleep first thing # in main) while True: try: main() except SystemExit: # If there is a SystemExit exception, we should probably actually exit... raise except Exception, e: # Log the exception and let main() run again. safe_log_last_exception() # Sleep a little to prevent a fast loop if the exception is happening # before any other calls to do_sleep(). misc.do_sleep(1.0)
def main(debug=False): """ <Purpose> Has an infinite loop where we sleep for 5-55 minutes, then check for updates. If an update happens, we will restart ourselves and/or the node manager as necesary. <Arguments> None <Exceptions> Any non-RsyncError exceptions from do_rsync. <Side Effects> If there is an update on the update site we are checking, it will be grabbed eventually. <Return> Will not return. Either an exception will be thrown, we exit because we are restarting, or we loop infinitely. """ global restartme # This is similar to init only: # 1) we loop / sleep # 2) we restart ourselves if we are updated # 3) we restart our client if they are updated while True: if debug: rint = 1 else: rint = random.randint(10, 110) for junk in range(rint): # We need to wake up every 30 seconds otherwise we will take # the full 5-55 minutes before we die when someone tries to # kill us nicely. misc.do_sleep(1) # Make sure we still have the process lock. # If not, we should exit if not runonce.stillhaveprocesslock('softwareupdater.old'): safe_log('[main] We no longer have the processlock\n') sys.exit(55) # set the softwareurl based on whether debug is set if debug: softwareurl = 'http://localhost:12345' else: softwareurl = seattle_url # Make sure that if we failed somehow to restart, we keep trying before # every time we try to update. - Brent if restartme: restart_software_updater() # where I'll put files... tempdir = tempfile.mkdtemp()+"/" # I'll clean this up in a minute try: updatedlist = do_rsync(softwareurl, "./",tempdir) except Exception: safe_log_last_exception() # oops, hopefully this will be fixed next time... continue finally: shutil.rmtree(tempdir) safe_log('[main] rsync with server yielded the following changes: %s' % str(updatedlist)) # no updates :) Let's wait again... if updatedlist == []: continue clientlist = updatedlist[:] if 'softwareupdater.py' in clientlist: restartme = True clientlist.remove('softwareupdater.py') # if the client software changed, let's update it! if clientlist != []: restart_client(clientlist) # oh! I've changed too. I should restart... search for MUTEX for info if restartme: restart_software_updater() if debug: break