From d96abc73ac90bafc863acdf8adf1400edb91a6af Mon Sep 17 00:00:00 2001 From: Arvin Schnell Date: Mon, 31 Aug 2015 16:36:09 +0200 Subject: [PATCH] - improved error reporting (bsc#940046) --- client/errors.cc | 2 +- client/snapper.cc | 45 +++-- dbus/DBusMessage.h | 18 +- package/snapper.changes | 5 + server/Client.cc | 50 ++++-- server/Client.h | 5 +- server/MetaSnapper.h | 5 +- snapper/AsciiFile.h | 5 +- snapper/Btrfs.cc | 29 ++-- snapper/Compare.cc | 20 +-- snapper/Comparison.cc | 6 +- snapper/Exception.cc | 127 ++++++++++++++ snapper/Exception.h | 360 ++++++++++++++++++++++++++++++++++++---- snapper/FileUtils.cc | 60 +++---- snapper/Lvm.cc | 8 +- snapper/Makefile.am | 2 +- snapper/Snapper.h | 38 ++--- snapper/Snapshot.cc | 12 +- snapper/Snapshot.h | 25 ++- snapper/XmlFile.cc | 10 +- 20 files changed, 625 insertions(+), 207 deletions(-) create mode 100644 snapper/Exception.cc diff --git a/client/errors.cc b/client/errors.cc index b0323911..4670badc 100644 --- a/client/errors.cc +++ b/client/errors.cc @@ -61,7 +61,7 @@ error_description(const DBus::ErrorException& e) return _("Snapshot is in use."); if (name == "error.io_error") - return _("IO Error."); + return sformat(_("IO Error (%s)."), e.message()); if (name == "error.create_config_failed") return sformat(_("Creating config failed (%s)."), e.message()); diff --git a/client/snapper.cc b/client/snapper.cc index 4c2ad31c..7194687f 100644 --- a/client/snapper.cc +++ b/client/snapper.cc @@ -1283,6 +1283,7 @@ getFilesystem(DBus::Connection* conn, Snapper* snapper) } catch (const InvalidConfigException& e) { + SN_CAUGHT(e); cerr << _("Failed to initialize filesystem handler.") << endl; exit(EXIT_FAILURE); } @@ -1473,7 +1474,8 @@ help_xa_diff() void print_xa_diff(const string loc_pre, const string loc_post) { - try { + try + { XAModification xa_mod = XAModification(XAttributes(loc_pre), XAttributes(loc_post)); if (!xa_mod.empty()) @@ -1482,7 +1484,10 @@ print_xa_diff(const string loc_pre, const string loc_post) xa_mod.dumpDiffReport(cout); } } - catch (const XAttributesException& e) {} + catch (const XAttributesException& e) + { + SN_CAUGHT(e); + } } void @@ -1737,47 +1742,62 @@ main(int argc, char** argv) } catch (const ConfigNotFoundException& e) - { - cerr << sformat(_("Config '%s' not found."), config_name.c_str()) << endl; - exit(EXIT_FAILURE); - } - catch (const InvalidConfigException& e) - { - cerr << sformat(_("Config '%s' is invalid."), config_name.c_str()) << endl; - exit(EXIT_FAILURE); - } + { + SN_CAUGHT(e); + cerr << sformat(_("Config '%s' not found."), config_name.c_str()) << endl; + exit(EXIT_FAILURE); + } + catch (const InvalidConfigException& e) + { + SN_CAUGHT(e); + cerr << sformat(_("Config '%s' is invalid."), config_name.c_str()) << endl; + exit(EXIT_FAILURE); + } catch (const ListConfigsFailedException& e) { + SN_CAUGHT(e); cerr << sformat(_("Listing configs failed (%s)."), e.what()) << endl; exit(EXIT_FAILURE); } catch (const CreateConfigFailedException& e) { + SN_CAUGHT(e); cerr << sformat(_("Creating config failed (%s)."), e.what()) << endl; exit(EXIT_FAILURE); } catch (const DeleteConfigFailedException& e) { + SN_CAUGHT(e); cerr << sformat(_("Deleting config failed (%s)."), e.what()) << endl; exit(EXIT_FAILURE); } catch (const InvalidConfigdataException& e) { + SN_CAUGHT(e); cerr << _("Invalid configdata.") << endl; exit(EXIT_FAILURE); } catch (const AclException& e) { + SN_CAUGHT(e); cerr << _("ACL error.") << endl; exit(EXIT_FAILURE); } + catch (const IOErrorException& e) + { + SN_CAUGHT(e); + cerr << sformat(_("IO error (%s)."), e.what()) << endl; + exit(EXIT_FAILURE); + } catch (const InvalidUserException& e) { + SN_CAUGHT(e); cerr << _("Invalid user.") << endl; exit(EXIT_FAILURE); } catch (const InvalidGroupException& e) { + SN_CAUGHT(e); cerr << _("Invalid group.") << endl; exit(EXIT_FAILURE); } @@ -1792,6 +1812,8 @@ main(int argc, char** argv) } catch (const DBus::ErrorException& e) { + SN_CAUGHT(e); + if (strcmp(e.name(), "error.unknown_config") == 0 && config_name == "root") { cerr << _("The config 'root' does not exist. Likely snapper is not configured.") << endl @@ -1804,6 +1826,7 @@ main(int argc, char** argv) } catch (const DBus::FatalException& e) { + SN_CAUGHT(e); cerr << _("Failure") << " (" << e.what() << ")." << endl; exit(EXIT_FAILURE); } diff --git a/dbus/DBusMessage.h b/dbus/DBusMessage.h index 0e395c28..cb0e319d 100644 --- a/dbus/DBusMessage.h +++ b/dbus/DBusMessage.h @@ -31,6 +31,8 @@ #include #include +#include "snapper/Exception.h" + namespace DBus { @@ -40,18 +42,18 @@ namespace DBus using std::map; - struct Exception : public std::exception + struct Exception : public snapper::Exception { - explicit Exception() throw() {} - virtual const char* what() const throw() { return "dbus generic exception"; } + explicit Exception() : snapper::Exception("dbus generic exception") {} + explicit Exception(const string& msg) : snapper::Exception(msg) {} }; struct ErrorException : public Exception { - explicit ErrorException(const DBusError err) throw() : err(err) {} + explicit ErrorException(const DBusError err) + : Exception("dbus error exception"), err(err) {} virtual ~ErrorException() throw() { dbus_error_free(&err); } - virtual const char* what() const throw() { return "dbus error exception"; } virtual const char* name() const throw() { return err.name; } virtual const char* message() const throw() { return err.message; } DBusError err; @@ -60,15 +62,13 @@ namespace DBus struct MarshallingException : public Exception { - explicit MarshallingException() throw() {} - virtual const char* what() const throw() { return "dbus marshalling exception"; } + explicit MarshallingException() : Exception("dbus marshalling exception") {} }; struct FatalException : public Exception { - explicit FatalException() throw() {} - virtual const char* what() const throw() { return "dbus fatal exception"; } + explicit FatalException() : Exception("dbus fatal exception") {} }; diff --git a/package/snapper.changes b/package/snapper.changes index ea5cbddd..ceca6fbc 100644 --- a/package/snapper.changes +++ b/package/snapper.changes @@ -1,3 +1,8 @@ +------------------------------------------------------------------- +Mon Aug 31 16:26:51 CEST 2015 - aschnell@suse.com + +- improved error reporting (bsc#940046) + ------------------------------------------------------------------- Wed Aug 26 11:58:24 CEST 2015 - aschnell@suse.de diff --git a/server/Client.cc b/server/Client.cc index 570a4a70..e5fd2c5e 100644 --- a/server/Client.cc +++ b/server/Client.cc @@ -363,10 +363,9 @@ Client::introspect(DBus::Connection& conn, DBus::Message& msg) } -struct Permissions : public std::exception +struct Permissions : public Exception { - explicit Permissions() throw() {} - virtual const char* what() const throw() { return "permissions"; } + explicit Permissions() : Exception("no permissions") {} }; @@ -417,10 +416,9 @@ Client::check_permission(DBus::Connection& conn, DBus::Message& msg, } -struct Lock : public std::exception +struct Lock : public Exception { - explicit Lock() throw() {} - virtual const char* what() const throw() { return "locked"; } + explicit Lock() : Exception("locked") {} }; @@ -438,17 +436,15 @@ Client::check_lock(DBus::Connection& conn, DBus::Message& msg, const string& con } -struct ConfigInUse : public std::exception +struct ConfigInUse : public Exception { - explicit ConfigInUse() throw() {} - virtual const char* what() const throw() { return "config in use"; } + explicit ConfigInUse() : Exception("config in use") {} }; -struct SnapshotInUse : public std::exception +struct SnapshotInUse : public Exception { - explicit SnapshotInUse() throw() {} - virtual const char* what() const throw() { return "snapshot in use"; } + explicit SnapshotInUse() : Exception("snapshot in use") {} }; @@ -1447,114 +1443,142 @@ Client::dispatch(DBus::Connection& conn, DBus::Message& msg) } catch (const DBus::MarshallingException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.dbus.marshalling", DBUS_ERROR_FAILED); conn.send(reply); } catch (const DBus::FatalException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.dbus.fatal", DBUS_ERROR_FAILED); conn.send(reply); } catch (const UnknownConfig& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.unknown_config", DBUS_ERROR_FAILED); conn.send(reply); } catch (const CreateConfigFailedException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.create_config_failed", e.what()); conn.send(reply); } catch (const DeleteConfigFailedException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.delete_config_failed", e.what()); conn.send(reply); } catch (const Permissions& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.no_permissions", DBUS_ERROR_FAILED); conn.send(reply); } catch (const Lock& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.config_locked", DBUS_ERROR_FAILED); conn.send(reply); } catch (const ConfigInUse& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.config_in_use", DBUS_ERROR_FAILED); conn.send(reply); } catch (const SnapshotInUse& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.snapshot_in_use", DBUS_ERROR_FAILED); conn.send(reply); } catch (const NoComparison& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.no_comparisons", DBUS_ERROR_FAILED); conn.send(reply); } catch (const IllegalSnapshotException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.illegal_snapshot", DBUS_ERROR_FAILED); conn.send(reply); } catch (const CreateSnapshotFailedException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.create_snapshot_failed", DBUS_ERROR_FAILED); conn.send(reply); } catch (const DeleteSnapshotFailedException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.delete_snapshot_failed", DBUS_ERROR_FAILED); conn.send(reply); } catch (const InvalidConfigdataException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.invalid_configdata", DBUS_ERROR_FAILED); conn.send(reply); } catch (const InvalidUserdataException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.invalid_userdata", DBUS_ERROR_FAILED); conn.send(reply); } catch (const AclException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.acl_error", DBUS_ERROR_FAILED); conn.send(reply); } catch (const IOErrorException& e) { - DBus::MessageError reply(msg, "error.io_error", DBUS_ERROR_FAILED); + SN_CAUGHT(e); + DBus::MessageError reply(msg, "error.io_error", e.what()); conn.send(reply); } catch (const IsSnapshotMountedFailedException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.is_snapshot_mounted", DBUS_ERROR_FAILED); conn.send(reply); } catch (const MountSnapshotFailedException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.mount_snapshot", DBUS_ERROR_FAILED); conn.send(reply); } catch (const UmountSnapshotFailedException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.umount_snapshot", DBUS_ERROR_FAILED); conn.send(reply); } catch (const InvalidUserException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.invalid_user", DBUS_ERROR_FAILED); conn.send(reply); } catch (const InvalidGroupException& e) { + SN_CAUGHT(e); DBus::MessageError reply(msg, "error.invalid_group", DBUS_ERROR_FAILED); conn.send(reply); } + catch (const Exception& e) + { + SN_CAUGHT(e); + DBus::MessageError reply(msg, "error.something", DBUS_ERROR_FAILED); + conn.send(reply); + } catch (...) { y2err("caught unknown exception"); diff --git a/server/Client.h b/server/Client.h index b24e66f2..4e4eb9f2 100644 --- a/server/Client.h +++ b/server/Client.h @@ -51,10 +51,9 @@ using namespace snapper; extern boost::shared_mutex big_mutex; -struct NoComparison : public std::exception +struct NoComparison : Exception { - explicit NoComparison() throw() {} - virtual const char* what() const throw() { return "no comparison"; } + explicit NoComparison() : Exception("no comparison") {} }; diff --git a/server/MetaSnapper.h b/server/MetaSnapper.h index eec66977..c370c034 100644 --- a/server/MetaSnapper.h +++ b/server/MetaSnapper.h @@ -75,10 +75,9 @@ private: }; -struct UnknownConfig : public std::exception +struct UnknownConfig : public Exception { - explicit UnknownConfig() throw() {} - virtual const char* what() const throw() { return "unknown config"; } + explicit UnknownConfig() : Exception("unknown config") {} }; diff --git a/snapper/AsciiFile.h b/snapper/AsciiFile.h index 2d6df7a6..63e0abb3 100644 --- a/snapper/AsciiFile.h +++ b/snapper/AsciiFile.h @@ -96,10 +96,9 @@ namespace snapper { public: - struct InvalidKeyException : public SnapperException + struct InvalidKeyException : public Exception { - explicit InvalidKeyException() throw() {} - virtual const char* what() const throw() { return "invalid key"; } + explicit InvalidKeyException() : Exception("invalid key") {} }; SysconfigFile(const char* name) : AsciiFile(name), modified(false) {} diff --git a/snapper/Btrfs.cc b/snapper/Btrfs.cc index 193c52ac..b8b101eb 100644 --- a/snapper/Btrfs.cc +++ b/snapper/Btrfs.cc @@ -56,6 +56,7 @@ #include "snapper/SnapperTmpl.h" #include "snapper/SnapperDefines.h" #include "snapper/Acls.h" +#include "snapper/Exception.h" namespace snapper @@ -200,13 +201,12 @@ namespace snapper struct stat stat; if (subvolume_dir.stat(&stat) != 0) { - throw IOErrorException(); + throw IOErrorException("stat on subvolume directory failed"); } if (!is_subvolume(stat)) { - y2err("subvolume is not a btrfs snapshot"); - throw IOErrorException(); + throw IOErrorException("subvolume is not a btrfs snapshot"); } return subvolume_dir; @@ -222,31 +222,30 @@ namespace snapper struct stat stat; if (infos_dir.stat(&stat) != 0) { - throw IOErrorException(); + throw IOErrorException("stat on info directory failed"); } if (!is_subvolume(stat)) { - y2err(".snapshots is not a btrfs snapshot"); - throw IOErrorException(); + SN_THROW(IOErrorException(".snapshots is not a btrfs snapshot")); } if (stat.st_uid != 0) { y2err(".snapshots must have owner root"); - throw IOErrorException(); + throw IOErrorException(".snapshots must have owner root"); } if (stat.st_gid != 0 && stat.st_mode & S_IWGRP) { y2err(".snapshots must have group root or must not be group-writable"); - throw IOErrorException(); + throw IOErrorException(".snapshots must have group root or must not be group-writable"); } if (stat.st_mode & S_IWOTH) { y2err(".snapshots must not be world-writable"); - throw IOErrorException(); + throw IOErrorException(".snapshots must not be world-writable"); } return infos_dir; @@ -578,10 +577,9 @@ namespace snapper } - struct BtrfsSendReceiveException : public SnapperException + struct BtrfsSendReceiveException : public Exception { - explicit BtrfsSendReceiveException() throw() {} - virtual const char* what() const throw() { return "Btrfs send/receive error"; } + explicit BtrfsSendReceiveException() : Exception("btrfs send/receive error") {} }; @@ -1280,7 +1278,7 @@ namespace snapper u64 flags; if (ioctl(dir.fd(), BTRFS_IOC_SUBVOL_GETFLAGS, &flags) < 0) { - throw IOErrorException(); + throw IOErrorException("ioctl BTRFS_IOC_SUBVOL_GETFLAGS failed"); } return flags & BTRFS_SUBVOL_RDONLY; @@ -1333,7 +1331,7 @@ namespace snapper y2mil("stopwatch " << stopwatch << " for comparing directories"); } - catch (const SnapperException& e) + catch (const Exception& e) { y2err("special btrfs cmpDirs failed, " << e.what()); y2mil("cmpDirs fallback"); @@ -1381,8 +1379,7 @@ namespace snapper } catch (const runtime_error& e) { - y2err("set default failed, " << e.what()); - throw IOErrorException(); + throw IOErrorException(string("set default failed, ") + e.what()); } } diff --git a/snapper/Compare.cc b/snapper/Compare.cc index 50069a54..ce68b7f0 100644 --- a/snapper/Compare.cc +++ b/snapper/Compare.cc @@ -247,16 +247,10 @@ namespace snapper return DELETED; if (r1 != 0) - { - y2err("stat failed path:" << file1.fullname()); - throw IOErrorException(); - } + throw IOErrorException("stat failed path:" + file1.fullname()); if (r2 != 0) - { - y2err("lstat failed path:" << file2.fullname()); - throw IOErrorException(); - } + throw IOErrorException("lstat failed path:" + file2.fullname()); return cmpFiles(file1, stat1, file2, stat2); } @@ -439,18 +433,12 @@ namespace snapper struct stat stat1; int r1 = dir1.stat(&stat1); if (r1 != 0) - { - y2err("stat failed path:" << dir1.fullname() << " errno:" << errno); - throw IOErrorException(); - } + throw IOErrorException(sformat("stat failed path:%s errno:%d", dir1.fullname().c_str(), errno)); struct stat stat2; int r2 = dir2.stat(&stat2); if (r2 != 0) - { - y2err("stat failed path:" << dir2.fullname() << " errno:" << errno); - throw IOErrorException(); - } + throw IOErrorException(sformat("stat failed path:%s errno:%d", dir2.fullname().c_str(), errno)); CmpData cmp_data; cmp_data.cb = cb; diff --git a/snapper/Comparison.cc b/snapper/Comparison.cc index bca9c2e2..8a2a2d73 100644 --- a/snapper/Comparison.cc +++ b/snapper/Comparison.cc @@ -227,10 +227,8 @@ namespace snapper FILE* file = fdopen(info_dir.mktemp(tmp_name), "w"); if (!file) - { - y2err("mkstemp failed errno:" << errno << " (" << stringerror(errno) << ")"); - throw IOErrorException(); - } + throw IOErrorException(sformat("mkstemp failed errno:%d (%s)", errno, + stringerror(errno).c_str())); for (Files::const_iterator it = files.begin(); it != files.end(); ++it) { diff --git a/snapper/Exception.cc b/snapper/Exception.cc new file mode 100644 index 00000000..f3c99d2b --- /dev/null +++ b/snapper/Exception.cc @@ -0,0 +1,127 @@ +/* + * Copyright (c) [2014-2015] Novell, Inc. + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact Novell, Inc. + * + * To contact Novell about this file by physical or electronic mail, you may + * find current contact information at www.novell.com. + */ + + +/* + * Large parts taken from libyui/YUIException.h + */ + + +#include +#include +#include + +#include "snapper/Exception.h" +#include "snapper/AppUtil.h" +#include "snapper/Log.h" +#include "snapper/Logger.h" + + +namespace snapper +{ + + std::string + CodeLocation::asString() const + { + // Format as "MySource.cc(myFunc):177" + std::string str(_file); + str += "(" + _func + "):" + std::to_string(_line); + + return str; + } + + + std::ostream& + operator<<(std::ostream& str, const CodeLocation& obj) + { + return str << obj.asString(); + } + + + Exception::Exception() + { + } + + + Exception::Exception(const std::string& msg_r) + : _msg(msg_r) + { + } + + + Exception::~Exception() throw() + { + } + + + std::string + Exception::asString() const + { + std::ostringstream str; + dumpOn(str); + return str.str(); + } + + + std::ostream& + Exception::dumpOn(std::ostream& str) const + { + return str << _msg; + } + + + std::ostream& + Exception::dumpError(std::ostream& str) const + { + return dumpOn(str << _where << ": "); + } + + + std::ostream& + operator<<(std::ostream& str, const Exception& obj) + { + return obj.dumpError(str); + } + + + std::string + Exception::strErrno(int errno_r) + { + return strerror(errno_r); + } + + + std::string + Exception::strErrno(int errno_r, const std::string& msg) + { + return msg + ": " + strErrno(errno_r); + } + + + void + Exception::log(const Exception& exception, const CodeLocation& location, + const char* const prefix) + { + y2log_op(WARNING, location.file().c_str(), location.line(), + location.func().c_str(), string(prefix) + " " + exception.asString()); + } + +} diff --git a/snapper/Exception.h b/snapper/Exception.h index aeba0cec..dcadfa34 100644 --- a/snapper/Exception.h +++ b/snapper/Exception.h @@ -1,5 +1,6 @@ /* * Copyright (c) [2011-2014] Novell, Inc. + * Copyright (c) [2015] SUSE LLC * * All Rights Reserved. * @@ -25,78 +26,371 @@ #include +#include namespace snapper { + // + // Macros for application use + // - struct SnapperException : public std::exception + /** + * Usage summary: + * + * Use SN_THROW to throw exceptions. + * Use SN_CAUGHT If you caught an exceptions in order to handle it. + * Use SN_RETHROW to rethrow a caught exception. + * + * The use of these macros is not mandatory. But SN_THROW and SN_RETHROW + * will adjust the code location information stored in the exception. All + * three macros will drop a line in the log file. + * + * 43 try + * 44 { + * 45 try + * 46 { + * 47 SN_THROW(Exception("Something bad happened.")); + * 48 } + * 49 catch (const Exception& exception) + * 50 { + * 51 SN_RETHROW(exception); + * 52 } + * 53 } + * 54 catch (const Exception& exception) + * 55 { + * 56 SN_CAUGHT(exception); + * 57 } + * + * The above produces the following log lines: + * + * Main.cc(main):47 THROW: Main.cc(main):47: Something bad happened. + * Main.cc(main):51 RETHROW: Main.cc(main):47: Something bad happened. + * Main.cc(main):56 CAUGHT: Main.cc(main):51: Something bad happened. + **/ + + + /** + * Create CodeLocation object storing the current location. + **/ +#define SN_EXCEPTION_CODE_LOCATION \ + CodeLocation(__FILE__, __FUNCTION__, __LINE__) + + + /** + * Drops a log line and throws the Exception. + **/ +#define SN_THROW(EXCEPTION) \ + _SN_THROW((EXCEPTION), SN_EXCEPTION_CODE_LOCATION) + + /** + * Drops a log line telling the Exception was caught and handled. + **/ +#define SN_CAUGHT(EXCEPTION) \ + _SN_CAUGHT((EXCEPTION), SN_EXCEPTION_CODE_LOCATION) + + + /** + * Drops a log line and rethrows, updating the CodeLocation. + **/ +#define SN_RETHROW(EXCEPTION) \ + _SN_RETHROW((EXCEPTION), SN_EXCEPTION_CODE_LOCATION) + + + /** + * Throw Exception built from a message string. + **/ +#define SN_THROW_MSG(EXCEPTION_TYPE, MSG) \ + SN_THROW(EXCEPTION_TYPE(MSG)) + + + /** + * Throw Exception built from errno. + **/ +#define SN_THROW_ERRNO(EXCEPTION_TYPE) \ + SN_THROW(EXCEPTION_TYPE(Exception::strErrno(errno))) + + + /** + * Throw Exception built from errno provided as argument. + **/ +#define SN_THROW_ERRNO1(EXCEPTION_TYPE, ERRNO) \ + SN_THROW(EXCEPTION_TYPE(Exception::strErrno(ERRNO))) + + + /** + * Throw Exception built from errno and a message string. + **/ +#define SN_THROW_ERRNO_MSG(EXCEPTION_TYPE, MSG) \ + SN_THROW(EXCEPTION_TYPE(Exception::strErrno(errno, MSG))) + + + /** + * Throw Exception built from errno provided as argument and a message + * string. + **/ +#define SN_THROW_ERRNO_MSG1(EXCEPTION_TYPE, ERRNO, MSG) \ + SN_THROW(EXCEPTION_TYPE(Exception::strErrno(ERRNO, MSG))) + + + /** + * Helper class for UI exceptions: Store _FILE_, _FUNCTION_ and _LINE_. + * Construct this using the SN_EXCEPTION_CODE_LOCATION macro. + **/ + class CodeLocation + { + public: + /** + * Constructor. + * Commonly called using the SN_EXCEPTION_CODE_LOCATION macro. + **/ + CodeLocation(const std::string& file_r, const std::string& func_r, int line_r) + : _file(file_r), _func(func_r), _line(line_r) {} + + /** + * Default constructor. + ***/ + CodeLocation() + : _line(0) {} + + /** + * Returns the source file name where the exception occured. + **/ + const std::string& file() const { return _file; } + + /** + * Returns the name of the function where the exception occured. + **/ + const std::string& func() const { return _func; } + + /** + * Returns the source line number where the exception occured. + **/ + int line() const { return _line; } + + /** + * Returns the location in normalized string format. + **/ + std::string asString() const; + + /** + * Stream output + **/ + friend std::ostream& operator<<(std::ostream& str, const CodeLocation& obj); + + private: + + std::string _file; + std::string _func; + int _line; + + }; + + + /** + * CodeLocation stream output + **/ + std::ostream& operator<<(std::ostream& str, const CodeLocation& obj); + + + /** + * Base class for snapper exceptions. + * + * Exception offers to store a message string passed to the constructor. + * Derived classes may provide additional information. + * Overload dumpOn to provide a proper error text. + **/ + class Exception : public std::exception { - explicit SnapperException() throw() {} - virtual const char* what() const throw() { return "generic snapper exception"; } + public: + + /** + * Default constructor. + * Use SN_THROW to throw exceptions. + **/ + Exception(); + + /** + * Constructor taking a message. + * Use SN_THROW to throw exceptions. + **/ + Exception(const std::string& msg); + + /** + * Destructor. + **/ + virtual ~Exception() throw(); + + /** + * Return CodeLocation. + **/ + const CodeLocation& where() const { return _where; } + + /** + * Exchange location on rethrow. + **/ + void relocate(const CodeLocation& newLocation) const { _where = newLocation; } + + /** + * Return the message string provided to the constructor. + * Note: This is not neccessarily the complete error message. + * The whole error message is provided by asString or dumpOn. + **/ + const std::string& msg() const { return _msg; } + + /** + * Set a new message string. + **/ + void setMsg(const std::string& msg) { _msg = msg; } + + /** + * Error message provided by dumpOn as string. + **/ + std::string asString() const; + + /** + * Make a string from errno_r. + **/ + static std::string strErrno(int errno_r); + + /** + * Make a string from errno_r and msg_r. + **/ + static std::string strErrno(int errno_r, const std::string& msg); + + /** + * Drop a log line on throw, catch or rethrow. + * Used by SN_THROW macros. + **/ + static void log(const Exception& exception, const CodeLocation& location, + const char* const prefix); + + /** + * Return message string. + * + * Reimplemented from std::exception. + **/ + virtual const char* what() const throw() { return _msg.c_str(); } + + protected: + + /** + * Overload this to print a proper error message. + **/ + virtual std::ostream& dumpOn(std::ostream& str) const; + + private: + + friend std::ostream& operator<<(std::ostream& str, const Exception& obj); + + mutable CodeLocation _where; + std::string _msg; + + /** + * Called by std::ostream& operator<<() . + * Prints CodeLocation and the error message provided by dumpOn. + **/ + std::ostream& dumpError(std::ostream& str) const; + }; - struct FileNotFoundException : public SnapperException + /** + * Exception stream output + **/ + std::ostream& operator<<(std::ostream& str, const Exception& obj); + + + // + // Helper templates + // + + + /** + * Helper for SN_THROW() + **/ + template + void _SN_THROW(const _Exception& exception, const CodeLocation& where) + { + exception.relocate(where); + Exception::log(exception, where, "THROW:"); + + throw exception; + } + + + /** + * Helper for SN_CAUGHT() + **/ + template + void _SN_CAUGHT(const _Exception& exception, const CodeLocation& where) + { + Exception::log(exception, where, "CAUGHT:"); + } + + + /** + * Helper for SN_RETHROW() + **/ + template + void _SN_RETHROW(const _Exception& exception, const CodeLocation& where) + { + Exception::log(exception, where, "RETHROW:"); + exception.relocate(where); + + throw; + } + + + struct FileNotFoundException : public Exception { - explicit FileNotFoundException() throw() {} - virtual const char* what() const throw() { return "file not found"; } + explicit FileNotFoundException() : Exception("file not found") {} }; - struct IllegalSnapshotException : public SnapperException + struct IllegalSnapshotException : public Exception { - explicit IllegalSnapshotException() throw() {} - virtual const char* what() const throw() { return "illegal snapshot"; } + explicit IllegalSnapshotException() : Exception("illegal snapshot") {} }; - struct BadAllocException : public SnapperException + struct BadAllocException : public Exception { - explicit BadAllocException() throw() {} - virtual const char* what() const throw() { return "bad alloc"; } + explicit BadAllocException() : Exception("bad alloc") {} }; - struct LogicErrorException : public SnapperException + struct LogicErrorException : public Exception { - explicit LogicErrorException() throw() {} - virtual const char* what() const throw() { return "logic error"; } + explicit LogicErrorException() : Exception("logic error") {} }; - struct IOErrorException : public SnapperException + struct IOErrorException : public Exception { - explicit IOErrorException() throw() {} - virtual const char* what() const throw() { return "IO error"; } + explicit IOErrorException(const std::string& msg) : Exception(msg) {} }; struct AclException : public IOErrorException { - explicit AclException() throw() {} - virtual const char* what() const throw() { return "ACL error"; } + explicit AclException() : IOErrorException("ACL error") {} }; - struct ProgramNotInstalledException : public SnapperException + struct ProgramNotInstalledException : public Exception { - explicit ProgramNotInstalledException(const char* msg) throw() : msg(msg) {} - virtual const char* what() const throw() { return msg; } - const char* msg; + explicit ProgramNotInstalledException(const std::string& msg) : Exception(msg) {} }; - struct XAttributesException : public SnapperException + struct XAttributesException : public Exception { - explicit XAttributesException() throw() {} - virtual const char* what() const throw() { return "XAttributes error"; } + explicit XAttributesException() : Exception("XAttributes error") {} }; - struct InvalidUserException : public SnapperException + struct InvalidUserException : public Exception { - explicit InvalidUserException() throw() {} - virtual const char* what() const throw() { return "invalid user"; } + explicit InvalidUserException() : Exception("invalid user") {} }; - struct InvalidGroupException : public SnapperException + struct InvalidGroupException : public Exception { - explicit InvalidGroupException() throw() {} - virtual const char* what() const throw() { return "invalid group"; } + explicit InvalidGroupException() : Exception("invalid group") {} }; } diff --git a/snapper/FileUtils.cc b/snapper/FileUtils.cc index 78ff7a39..ec8d8cc2 100644 --- a/snapper/FileUtils.cc +++ b/snapper/FileUtils.cc @@ -55,23 +55,16 @@ namespace snapper { dirfd = ::open(base_path.c_str(), O_RDONLY | O_NOATIME | O_CLOEXEC); if (dirfd < 0) - { - y2err("open failed path:" << base_path << " error:" << stringerror(errno)); - throw IOErrorException(); - } + throw IOErrorException(sformat("open failed path:%s errno:%d (%s)", base_path.c_str(), + errno, stringerror(errno).c_str())); struct stat buf; if (fstat(dirfd, &buf) != 0) - { - y2err("fstat failed path:" << base_path << " error:" << stringerror(errno)); - throw IOErrorException(); - } + throw IOErrorException(sformat("fstat failed path:%s errno:%d (%s)", base_path.c_str(), + errno, stringerror(errno).c_str())); if (!S_ISDIR(buf.st_mode)) - { - y2err("not a directory path:" << base_path); - throw IOErrorException(); - } + throw IOErrorException("not a directory path:" + base_path); setXaStatus(); } @@ -85,23 +78,18 @@ namespace snapper dirfd = ::openat(dir.dirfd, name.c_str(), O_RDONLY | O_NOFOLLOW | O_NOATIME | O_CLOEXEC); if (dirfd < 0) - { - y2err("open failed path:" << dir.fullname(name) << " (" << stringerror(errno) << ")"); - throw IOErrorException(); - } + throw IOErrorException(sformat("open failed path:%s errno:%d (%s)", dir.fullname().c_str(), + errno, stringerror(errno).c_str())); struct stat buf; if (fstat(dirfd, &buf) != 0) - { - y2err("fstat failed path:" << base_path << " error:" << stringerror(errno)); - throw IOErrorException(); - } + throw IOErrorException(sformat("fstat failed path:%s errno:%d (%s)", base_path.c_str(), + errno, stringerror(errno).c_str())); if (!S_ISDIR(buf.st_mode)) { - y2err("not a directory path:" << dir.fullname(name)); close(dirfd); - throw IOErrorException(); + throw IOErrorException("not a directory path:" + dir.fullname(name)); } xastatus = dir.xastatus; @@ -113,10 +101,8 @@ namespace snapper { dirfd = fcntl(dir.dirfd, F_DUPFD_CLOEXEC, 0); if (dirfd == -1) - { - y2err("fcntl(F_DUPFD_CLOEXEC) failed error:" << stringerror(errno)); - throw IOErrorException(); - } + throw IOErrorException(sformat("fcntl(F_DUPFD_CLOEXEC) failed error:%d (%s)", errno, + stringerror(errno).c_str())); xastatus = dir.xastatus; } @@ -130,10 +116,8 @@ namespace snapper ::close(dirfd); dirfd = fcntl(dir.dirfd, F_DUPFD_CLOEXEC, 0); if (dirfd == -1) - { - y2err("fcntl(F_DUPFD_CLOEXEC) failed error:" << stringerror(errno)); - throw IOErrorException(); - } + throw IOErrorException(sformat("fcntl(F_DUPFD_CLOEXEC) failed error:%d (%s)", errno, + stringerror(errno).c_str())); xastatus = dir.xastatus; } @@ -192,17 +176,15 @@ namespace snapper { int fd = fcntl(dirfd, F_DUPFD_CLOEXEC, 0); if (fd == -1) - { - y2err("fcntl(F_DUPFD_CLOEXEC) failed error:" << stringerror(errno)); - throw IOErrorException(); - } + throw IOErrorException(sformat("fcntl(F_DUPFD_CLOEXEC) failed error:%d (%s)", errno, + stringerror(errno).c_str())); DIR* dp = fdopendir(fd); if (dp == NULL) { - y2err("fdopendir failed path:" << fullname() << " error:" << stringerror(errno)); ::close(fd); - throw IOErrorException(); + throw IOErrorException(sformat("fdopendir failed path:%s error:%d (%s)", + fullname().c_str(), errno, stringerror(errno).c_str())); } vector ret; @@ -517,9 +499,9 @@ namespace snapper } else { - y2err("Couldn't get extended attributes status for " << base_path << "/" << - path << stringerror(errno)); - throw IOErrorException(); + throw IOErrorException(sformat("Couldn't get extended attributes status for %s/%s, " + "errno:%d (%s)", base_path.c_str(), path.c_str(), + errno, stringerror(errno).c_str())); } } else diff --git a/snapper/Lvm.cc b/snapper/Lvm.cc index 790c85b1..766dee42 100644 --- a/snapper/Lvm.cc +++ b/snapper/Lvm.cc @@ -146,25 +146,25 @@ namespace snapper struct stat stat; if (infos_dir.stat(&stat) != 0) { - throw IOErrorException(); + throw IOErrorException("stat on .snapshots failed"); } if (stat.st_uid != 0) { y2err(".snapshots must have owner root"); - throw IOErrorException(); + throw IOErrorException(".snapshots must have owner root"); } if (stat.st_gid != 0 && stat.st_mode & S_IWGRP) { y2err(".snapshots must have group root or must not be group-writable"); - throw IOErrorException(); + throw IOErrorException(".snapshots must have group root or must not be group-writable"); } if (stat.st_mode & S_IWOTH) { y2err(".snapshots must not be world-writable"); - throw IOErrorException(); + throw IOErrorException(".snapshots must not be world-writable"); } return infos_dir; diff --git a/snapper/Makefile.am b/snapper/Makefile.am index 77728d75..4e79b86a 100644 --- a/snapper/Makefile.am +++ b/snapper/Makefile.am @@ -27,7 +27,7 @@ libsnapper_la_SOURCES = \ Regex.cc Regex.h \ Acls.cc Acls.h \ Hooks.cc Hooks.h \ - Exception.h \ + Exception.cc Exception.h \ SnapperTmpl.h \ SnapperTypes.h \ SnapperDefines.h \ diff --git a/snapper/Snapper.h b/snapper/Snapper.h index e942b310..9ebd32c6 100644 --- a/snapper/Snapper.h +++ b/snapper/Snapper.h @@ -60,49 +60,39 @@ namespace snapper }; - struct ConfigNotFoundException : public SnapperException + struct ConfigNotFoundException : public Exception { - explicit ConfigNotFoundException() throw() {} - virtual const char* what() const throw() { return "config not found"; } + explicit ConfigNotFoundException() : Exception("config not found") {} }; - struct InvalidConfigException : public SnapperException + struct InvalidConfigException : public Exception { - explicit InvalidConfigException() throw() {} - virtual const char* what() const throw() { return "invalid config"; } + explicit InvalidConfigException() : Exception("invalid config") {} }; - struct InvalidConfigdataException : public SnapperException + struct InvalidConfigdataException : public Exception { - explicit InvalidConfigdataException() throw() {} - virtual const char* what() const throw() { return "invalid configdata"; } + explicit InvalidConfigdataException() : Exception("invalid configdata") {} }; - struct InvalidUserdataException : public SnapperException + struct InvalidUserdataException : public Exception { - explicit InvalidUserdataException() throw() {} - virtual const char* what() const throw() { return "invalid userdata"; } + explicit InvalidUserdataException() : Exception("invalid userdata") {} }; - struct ListConfigsFailedException : public SnapperException + struct ListConfigsFailedException : public Exception { - explicit ListConfigsFailedException(const char* msg) throw() : msg(msg) {} - virtual const char* what() const throw() { return msg; } - const char* msg; + explicit ListConfigsFailedException(const char* msg) : Exception(msg) {} }; - struct CreateConfigFailedException : public SnapperException + struct CreateConfigFailedException : public Exception { - explicit CreateConfigFailedException(const char* msg) throw() : msg(msg) {} - virtual const char* what() const throw() { return msg; } - const char* msg; + explicit CreateConfigFailedException(const char* msg) : Exception(msg) {} }; - struct DeleteConfigFailedException : public SnapperException + struct DeleteConfigFailedException : public Exception { - explicit DeleteConfigFailedException(const char* msg) throw() : msg(msg) {} - virtual const char* what() const throw() { return msg; } - const char* msg; + explicit DeleteConfigFailedException(const char* msg) : Exception(msg) {} }; diff --git a/snapper/Snapshot.cc b/snapper/Snapshot.cc index 41e37a96..4b96e8d7 100644 --- a/snapper/Snapshot.cc +++ b/snapper/Snapshot.cc @@ -389,8 +389,8 @@ namespace snapper if (errno == EEXIST) continue; - y2err("mkdir failed errno:" << errno << " (" << stringerror(errno) << ")"); - throw IOErrorException(); + throw IOErrorException(sformat("mkdir failed errno:%d (%s)", errno, + stringerror(errno).c_str())); } infos_dir.chmod(decString(num), 0755, 0); @@ -439,11 +439,9 @@ namespace snapper xml.save(info_dir.mktemp(tmp_name)); if (info_dir.rename(tmp_name, file_name) != 0) - { - y2err("rename info.xml failed infoDir: " << info_dir.fullname() << " errno: " << - errno << " (" << stringerror(errno) << ")"); - throw IOErrorException(); - } + throw IOErrorException(sformat("rename info.xml failed infoDir:%s errno:%d (%s)", + info_dir.fullname().c_str(), errno, + stringerror(errno).c_str())); } diff --git a/snapper/Snapshot.h b/snapper/Snapshot.h index e95be7c5..fc38889f 100644 --- a/snapper/Snapshot.h +++ b/snapper/Snapshot.h @@ -46,35 +46,30 @@ namespace snapper enum SnapshotType { SINGLE, PRE, POST }; - struct CreateSnapshotFailedException : public SnapperException + struct CreateSnapshotFailedException : public Exception { - explicit CreateSnapshotFailedException() throw() {} - virtual const char* what() const throw() { return "create snapshot failed"; } + explicit CreateSnapshotFailedException() : Exception("create snapshot failed") {} }; - struct DeleteSnapshotFailedException : public SnapperException + struct DeleteSnapshotFailedException : public Exception { - explicit DeleteSnapshotFailedException() throw() {} - virtual const char* what() const throw() { return "delete snapshot failed"; } + explicit DeleteSnapshotFailedException() : Exception("delete snapshot failed") {} }; - struct IsSnapshotMountedFailedException : public SnapperException + struct IsSnapshotMountedFailedException : public Exception { - explicit IsSnapshotMountedFailedException() throw() {} - virtual const char* what() const throw() { return "is snapshot mounted failed"; } + explicit IsSnapshotMountedFailedException() : Exception("is snapshot mounted failed") {} }; - struct MountSnapshotFailedException : public SnapperException + struct MountSnapshotFailedException : public Exception { - explicit MountSnapshotFailedException() throw() {} - virtual const char* what() const throw() { return "mount snapshot failed"; } + explicit MountSnapshotFailedException() : Exception("mount snapshot failed") {} }; - struct UmountSnapshotFailedException : public SnapperException + struct UmountSnapshotFailedException : public Exception { - explicit UmountSnapshotFailedException() throw() {} - virtual const char* what() const throw() { return "umount snapshot failed"; } + explicit UmountSnapshotFailedException() : Exception("umount snapshot failed") {} }; diff --git a/snapper/XmlFile.cc b/snapper/XmlFile.cc index c01d82c9..df754860 100644 --- a/snapper/XmlFile.cc +++ b/snapper/XmlFile.cc @@ -44,7 +44,7 @@ namespace snapper close(fd); if (!doc) - throw IOErrorException(); + throw IOErrorException("xmlReadFd failed"); } @@ -52,7 +52,7 @@ namespace snapper : doc(xmlReadFile(filename.c_str(), NULL, XML_PARSE_NOBLANKS | XML_PARSE_NONET)) { if (!doc) - throw IOErrorException(); + throw IOErrorException("xmlReadFile failed"); } @@ -67,12 +67,12 @@ namespace snapper { FILE* f = fdopen(fd, "w"); if (!f) - throw IOErrorException(); + throw IOErrorException("fdopen"); if (xmlDocFormatDump(f, doc, 1) == -1) { fclose(f); - throw IOErrorException(); + throw IOErrorException("xmlDocFormatDump failed"); } fclose(f); @@ -83,7 +83,7 @@ namespace snapper XmlFile::save(const string& filename) { if (xmlSaveFormatFile(filename.c_str(), doc, 1) == -1) - throw IOErrorException(); + throw IOErrorException("xmlSaveFormatFile failed"); } -- 2.47.3