]> git.ipfire.org Git - thirdparty/snapper.git/commitdiff
- improved error reporting (bsc#940046) 192/head
authorArvin Schnell <aschnell@suse.de>
Mon, 31 Aug 2015 14:36:09 +0000 (16:36 +0200)
committerArvin Schnell <aschnell@suse.de>
Mon, 31 Aug 2015 14:36:09 +0000 (16:36 +0200)
20 files changed:
client/errors.cc
client/snapper.cc
dbus/DBusMessage.h
package/snapper.changes
server/Client.cc
server/Client.h
server/MetaSnapper.h
snapper/AsciiFile.h
snapper/Btrfs.cc
snapper/Compare.cc
snapper/Comparison.cc
snapper/Exception.cc [new file with mode: 0644]
snapper/Exception.h
snapper/FileUtils.cc
snapper/Lvm.cc
snapper/Makefile.am
snapper/Snapper.h
snapper/Snapshot.cc
snapper/Snapshot.h
snapper/XmlFile.cc

index b0323911a05e83c8d2e09bed0a305b35c3d8b9f5..4670badca2cb520f2bb6b7fda837291cd3ecc5c7 100644 (file)
@@ -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());
index 4c2ad31c9f8f0fd890aa81952b318dac6049a36a..7194687fdc89af09efd13db861a95faba15d9e7d 100644 (file)
@@ -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);
        }
index 0e395c28fe9fc2854fe3c4a7765e4cc325ce5f14..cb0e319d54d0dc5a0655f8a3648b496c63f2dec1 100644 (file)
@@ -31,6 +31,8 @@
 #include <list>
 #include <map>
 
+#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") {}
     };
 
 
index ea5cbddd852e100aeb98fd93d39b6b7164ed4b9e..ceca6fbcd8c9404218c5d742bf2d488f146038f3 100644 (file)
@@ -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
 
index 570a4a70bfb861f4b40f1548356242c7d4764433..e5fd2c5eae0367af7b606f524c704e4ac7478220 100644 (file)
@@ -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");
index b24e66f25f54d6182688dc3d11a9a34d08bf532d..4e4eb9f2842012dcc46e5ecb396d9bae4f3cc7d0 100644 (file)
@@ -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") {}
 };
 
 
index eec66977bf257d61859ebdcca79883b4a70a6a78..c370c03434f3bbffe62051859197e825a8e30c2a 100644 (file)
@@ -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") {}
 };
 
 
index 2d6df7a60fdc2f69898a83bd07e0dda2fb0df95e..63e0abb31486a9e71216a4db971709419ffbbcfd 100644 (file)
@@ -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) {}
index 193c52acc49634a67cbdcf181a4710996726cef9..b8b101ebd2a1ff916ad5d140416f8640e44ab331 100644 (file)
@@ -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());
        }
     }
 
index 50069a549603d46df2cd3c41109fff4d8614456d..ce68b7f07164d9c1a9e4bf9cec1868d19f72e225 100644 (file)
@@ -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;
index bca9c2e2a3ec21122b2d290a6e035ee2989e584e..8a2a2d7374c3f7417fa37ac03086d7a20cdddebd 100644 (file)
@@ -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 (file)
index 0000000..f3c99d2
--- /dev/null
@@ -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 <string.h>
+#include <stdio.h>
+#include <sstream>
+
+#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());
+    }
+
+}
index aeba0cecc9744da605bc6f51c826f4015a6c6560..dcadfa34211875a80f2892e1e45f40973062eae1 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (c) [2011-2014] Novell, Inc.
+ * Copyright (c) [2015] SUSE LLC
  *
  * All Rights Reserved.
  *
 
 
 #include <exception>
+#include <string>
 
 
 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<class _Exception>
+    void _SN_THROW(const _Exception& exception, const CodeLocation& where)
+    {
+       exception.relocate(where);
+       Exception::log(exception, where, "THROW:");
+
+       throw exception;
+    }
+
+
+    /**
+     * Helper for SN_CAUGHT()
+     **/
+    template<class _Exception>
+    void _SN_CAUGHT(const _Exception& exception, const CodeLocation& where)
+    {
+       Exception::log(exception, where, "CAUGHT:");
+    }
+
+
+    /**
+     * Helper for SN_RETHROW()
+     **/
+    template<class _Exception>
+    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") {}
     };
 
 }
index 78ff7a395a5949d27a22f71a5d9a31ffa8c28eeb..ec8d8cc296fa3ac3482379c3addfdacd253095e2 100644 (file)
@@ -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<string> 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
index 790c85b164be38ab924d0a2535ecc9566509ab69..766dee42d9e5862c97118670a04af7d54752617d 100644 (file)
@@ -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;
index 77728d75c018cd523a805b8f89daa98356fc2816..4e79b86abd0dc79f0e4f59d27949fc9fbc28f503 100644 (file)
@@ -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                                \
index e942b3104a55a29f2015a82fae77d9889233cd7c..9ebd32c6b9e586df1dacaab7112cdebe76e2cd96 100644 (file)
@@ -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) {}
     };
 
 
index 41e37a9613a28c4804f9f0b68fe4230cb03f3926..4b96e8d7f8371bf21dcfec2390968c2b605c3a9e 100644 (file)
@@ -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()));
     }
 
 
index e95be7c55d5d8900edb42c069ae5ec87741d70c7..fc38889feabeb7f762e31882f0100ec15fdfcb00 100644 (file)
@@ -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") {}
     };
 
 
index c01d82c91584ead07e9cb5b10cd6503b3579c9a6..df7548601afd58c4a15b5beaa7168d1c2c9ab796 100644 (file)
@@ -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");
     }