diff options
author | bescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109> | 2005-11-19 03:10:03 +0000 |
---|---|---|
committer | bescoto <bescoto@2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109> | 2005-11-19 03:10:03 +0000 |
commit | 3f6a251faf324209247a868c91a9bacdea427db1 (patch) | |
tree | e8789dd5b97a4d62c342016c045210df03865ed8 | |
parent | 3f7215648bac76bbdba0ac6dcbf608db2919877c (diff) | |
download | rdiff-backup-3f6a251faf324209247a868c91a9bacdea427db1.tar.gz |
Error reporting cleanup
git-svn-id: http://svn.savannah.nongnu.org/svn/rdiff-backup/trunk@684 2b77aa54-bcbc-44c9-a7ec-4f6cf2b41109
-rw-r--r-- | rdiff-backup/CHANGELOG | 10 | ||||
-rwxr-xr-x | rdiff-backup/rdiff-backup | 2 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/Main.py | 21 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/connection.py | 24 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/log.py | 11 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/robust.py | 23 | ||||
-rw-r--r-- | rdiff-backup/rdiff_backup/rpath.py | 16 |
7 files changed, 81 insertions, 26 deletions
diff --git a/rdiff-backup/CHANGELOG b/rdiff-backup/CHANGELOG index 17c6b07..2008e1c 100644 --- a/rdiff-backup/CHANGELOG +++ b/rdiff-backup/CHANGELOG @@ -1,3 +1,13 @@ +New in v1.1.3 (????/??/??) +-------------------------- + +Regression metadata bug introduced with 1.1.1/1.1.2 fixed. + +rdiff-backup should now give a clean error message (no stack traces!) +when aborted with control-C, killed with a signal, or when the +connection is lost. + + New in v1.1.2 (2005/11/06) -------------------------- diff --git a/rdiff-backup/rdiff-backup b/rdiff-backup/rdiff-backup index 54f5a18..b27be9e 100755 --- a/rdiff-backup/rdiff-backup +++ b/rdiff-backup/rdiff-backup @@ -20,6 +20,6 @@ import sys import rdiff_backup.Main if __name__ == "__main__" and not globals().has_key('__no_execute__'): - rdiff_backup.Main.Main(sys.argv[1:]) + rdiff_backup.Main.error_check_Main(sys.argv[1:]) diff --git a/rdiff-backup/rdiff_backup/Main.py b/rdiff-backup/rdiff_backup/Main.py index 210a246..ccda5b4 100644 --- a/rdiff-backup/rdiff_backup/Main.py +++ b/rdiff-backup/rdiff_backup/Main.py @@ -87,7 +87,7 @@ def parse_cmdlineoptions(arglist): "user-mapping-file=", "verbosity=", "verify", "verify-at-time=", "version"]) except getopt.error, e: - commandline_error("Bad commandline options: %s" % str(e)) + commandline_error("Bad commandline options: " + str(e)) for opt, arg in optlist: if opt == "-b" or opt == "--backup-mode": action = "backup" @@ -231,9 +231,8 @@ def final_set_action(rps): else: action = "backup" def commandline_error(message): - sys.stderr.write("Error: %s\n" % message) - sys.stderr.write("See the rdiff-backup manual page for instructions\n") - sys.exit(2) + Log.FatalError(message + "\nSee the rdiff-backup manual page for " + "more information.") def misc_setup(rps): """Set default change ownership flag, umask, relay regexps""" @@ -291,6 +290,19 @@ def cleanup(): Log.close_logfile() if not Globals.server: SetConnections.CloseConnections() +def error_check_Main(arglist): + """Run Main on arglist, suppressing stack trace for routine errors""" + try: Main(arglist) + except SystemExit: raise + except Exception, exc: + errmsg = robust.is_routine_fatal(exc) + if errmsg: + Log.exception(2, 6) + Log.FatalError(errmsg) + else: + Log.exception(2, 2) + raise + def Main(arglist): """Start everything up!""" parse_cmdlineoptions(arglist) @@ -304,7 +316,6 @@ def Main(arglist): cleanup() if return_val is not None: sys.exit(return_val) - def Backup(rpin, rpout): """Backup, possibly incrementally, src_path to dest_path.""" global incdir diff --git a/rdiff-backup/rdiff_backup/connection.py b/rdiff-backup/rdiff_backup/connection.py index 99b8f72..dadbcd5 100644 --- a/rdiff-backup/rdiff_backup/connection.py +++ b/rdiff-backup/rdiff_backup/connection.py @@ -31,6 +31,7 @@ except ImportError: pass class ConnectionError(Exception): pass class ConnectionReadError(ConnectionError): pass +class ConnectionWriteError(ConnectionError): pass class ConnectionQuit(Exception): pass @@ -197,14 +198,17 @@ class LowLevelPipeConnection(Connection): def _write(self, headerchar, data, req_num): """Write header and then data to the pipe""" - self.outpipe.write(headerchar + chr(req_num) + - C.long2str(long(len(data)))) - self.outpipe.write(data) - self.outpipe.flush() + try: + self.outpipe.write(headerchar + chr(req_num) + + C.long2str(long(len(data)))) + self.outpipe.write(data) + self.outpipe.flush() + except IOError: raise ConnectionWriteError() def _read(self, length): """Read length bytes from inpipe, returning result""" - return self.inpipe.read(length) + try: return self.inpipe.read(length) + except IOError: raise ConnectionReadError() def _s2l_old(self, s): """Convert string to long int""" @@ -228,11 +232,9 @@ class LowLevelPipeConnection(Connection): if not len(header_string) == 9: raise ConnectionReadError("Truncated header string (problem " "probably originated remotely)") - try: - format_string, req_num, length = (header_string[0], - ord(header_string[1]), - C.str2long(header_string[2:])) - except IndexError: raise ConnectionError() + format_string, req_num, length = (header_string[0], + ord(header_string[1]), + C.str2long(header_string[2:])) if format_string == "q": raise ConnectionQuit("Received quit signal") data = self._read(length) @@ -337,6 +339,8 @@ class PipeConnection(LowLevelPipeConnection): def extract_exception(self): """Return active exception""" + if robust.is_routine_fatal(sys.exc_info()[1]): + raise # Fatal error--No logging necessary, but connection down if log.Log.verbosity >= 5 or log.Log.term_verbosity >= 5: log.Log("Sending back exception %s of type %s: \n%s" % (sys.exc_info()[1], sys.exc_info()[0], diff --git a/rdiff-backup/rdiff_backup/log.py b/rdiff-backup/rdiff_backup/log.py index f977420..93933e6 100644 --- a/rdiff-backup/rdiff_backup/log.py +++ b/rdiff-backup/rdiff_backup/log.py @@ -156,9 +156,9 @@ class Logger: assert no_fatal_message == 0 or no_fatal_message == 1 if no_fatal_message: prefix_string = "" else: prefix_string = "Fatal Error: " - self(prefix_string + message, 1) - import Main - Main.cleanup() + self.log_to_term(prefix_string + message, 1) + #import Main + #Main.cleanup() sys.exit(errlevel) def exception_to_string(self, arglist = []): @@ -182,10 +182,13 @@ class Logger: if (only_terminal == 0 or (only_terminal == 1 and self.log_file_open)): logging_func = self.__call__ - else: logging_func = self.log_to_term + else: + logging_func = self.log_to_term + if verbosity >= self.term_verbosity: return logging_func(self.exception_to_string(), verbosity) + Log = Logger() diff --git a/rdiff-backup/rdiff_backup/robust.py b/rdiff-backup/rdiff_backup/robust.py index 67221e3..4fa8e6c 100644 --- a/rdiff-backup/rdiff_backup/robust.py +++ b/rdiff-backup/rdiff_backup/robust.py @@ -19,8 +19,8 @@ """Catch various exceptions given system call""" -import errno, signal -import librsync, C, static, rpath, Globals, log, statistics +import errno, signal, exceptions +import librsync, C, static, rpath, Globals, log, statistics, connection def check_common_error(error_handler, function, args = []): """Apply function to args, if error, run error_handler on exception @@ -38,7 +38,8 @@ def check_common_error(error_handler, function, args = []): if conn is not None: conn.statistics.record_error() if error_handler: return error_handler(exc, *args) else: return None - log.Log.exception(1, 2) + if is_routine_fatal(exc): log.Log.exception(1, 6) + else: log.Log.exception(1, 2) raise def catch_error(exc): @@ -59,6 +60,22 @@ def catch_error(exc): return 1 return 0 +def is_routine_fatal(exc): + """Return string if exception is non-error unrecoverable, None otherwise + + Used to suppress a stack trace for exceptions like keyboard + interrupts or connection drops. Return value is string to use as + an exit message. + + """ + if isinstance(exc, exceptions.KeyboardInterrupt): + return "User abort" + elif isinstance(exc, connection.ConnectionError): + return "Lost connection to the remote system" + elif isinstance(exc, SignalException): + return "Killed with signal %s" % (exc,) + return None + def get_error_handler(error_type): """Return error handler function that can be used above diff --git a/rdiff-backup/rdiff_backup/rpath.py b/rdiff-backup/rdiff_backup/rpath.py index 6793147..47f3b3e 100644 --- a/rdiff-backup/rdiff_backup/rpath.py +++ b/rdiff-backup/rdiff_backup/rpath.py @@ -265,7 +265,7 @@ def make_socket_local(rpath): def gzip_open_local_read(rpath): """Return open GzipFile. See security note directly above""" assert rpath.conn is Globals.local_connection - return gzip.GzipFile(rpath.path, "rb") + return GzipFile(rpath.path, "rb") def open_local_read(rpath): """Return open file (provided for security reasons)""" @@ -982,13 +982,13 @@ class RPath(RORPath): """ if self.conn is Globals.local_connection: - if compress: return gzip.GzipFile(self.path, mode) + if compress: return GzipFile(self.path, mode) else: return open(self.path, mode) if compress: if mode == "r" or mode == "rb": return self.conn.rpath.gzip_open_local_read(self) - else: return self.conn.gzip.GzipFile(self.path, mode) + else: return self.conn.rpath.GzipFile(self.path, mode) else: if mode == "r" or mode == "rb": return self.conn.rpath.open_local_read(self) @@ -1217,6 +1217,16 @@ class RPathFileHook: return result +class GzipFile(gzip.GzipFile): + """Like gzip.GzipFile, except remove destructor + + The default GzipFile's destructor prints out some messy error + messages. Use this class instead to clean those up. + + """ + def __del__(self): pass + + def setdata_local(rpath): """Set eas/acls, uid/gid, resource fork in data dictionary |