From 1bb62eed4dd97067ae35e922db3669701dc0a3fe Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Thu, 10 Sep 2015 22:35:44 -0400 Subject: [PATCH] Started crash recovery mechanism --- letsencrypt/error_handler.py | 46 ++++++++++++++++++++++++++++++++++++ letsencrypt/interfaces.py | 11 +++++++++ 2 files changed, 57 insertions(+) create mode 100644 letsencrypt/error_handler.py diff --git a/letsencrypt/error_handler.py b/letsencrypt/error_handler.py new file mode 100644 index 000000000..884c73927 --- /dev/null +++ b/letsencrypt/error_handler.py @@ -0,0 +1,46 @@ +"""Registers and calls cleanup functions in case of an error.""" +import os +import signal + + +_SIGNALS = [signal.SIGTERM] if os.name == "nt" else + [signal.SIGTERM, signal.SIGHUP, signal.SIGQUIT, + signal.SIGXCPU, signal.SIGXFSZ, signal.SIGPWR,] + + +class ErrorHandler(): + """Registers and calls cleanup functions in case of an error.""" + def __init__(self, func=None): + self.funcs = [] + if func: + self.funcs.append(func) + + def __enter__(self): + self.set_signal_handlers() + + def __exit__(self, exec_type, exec_value, traceback): + if exec_value is not None: + self.cleanup() + self.reset_signal_handlers() + + def register(self, func): + """Registers func to be called if an error occurs.""" + self.funcs.append(func) + + def cleanup(self): + """Calls all registered functions.""" + while self.funcs: + self.funcs.pop()() + + def set_signal_handlers(self): + for signal_type in _SIGNALS: + signal.signal(signal_type, self._signal_handler) + + def reset_signal_handlers(self): + for signal_type in _SIGNALS: + signal.signal(signal_type, signal.SIG_DFL) + + def _signal_handler(self, signum, frame): + self.cleanup() + signal.signal(signal_type, signal.SIG_DFL) + os.kill(os.getpid(), signum) diff --git a/letsencrypt/interfaces.py b/letsencrypt/interfaces.py index f330e28ce..653b5685b 100644 --- a/letsencrypt/interfaces.py +++ b/letsencrypt/interfaces.py @@ -322,6 +322,17 @@ class IInstaller(IPlugin): """ + def recovery_routine(self): + """Revert configuration to most recent finalized checkpoint. + + Remove all changes (temporary and permanent) that have not been + finalized. This is useful to protect against crashes and other + execution interruptions. + + :raises .errors.PluginError: If unable to recover the configuration + + """ + def view_config_changes(): """Display all of the LE config changes.