cerr << _("Invalid userdata.") << endl;
else if (name == "error.illegal_snapshot")
cerr << _("Illegal Snapshot.") << endl;
+ else if (name == "error.config_locked")
+ cerr << _("Config is locked.") << endl;
+ else if (name == "error.config_in_use")
+ cerr << _("Config is in use.") << endl;
else
cerr << _("Failure") << " (" << name << ")." << endl;
exit(EXIT_FAILURE);
#include "MetaSnapper.h"
+Clients clients;
+
+
template <typename ListType, typename Type>
bool contains(const ListType& l, const Type& value)
{
Client::~Client()
{
thread.interrupt();
-
thread.join();
for (list<Comparison*>::iterator it = comparisons.begin(); it != comparisons.end(); ++it)
{
+ const Snapper* s = (*it)->getSnapper();
+
+ for (MetaSnappers::iterator it2 = meta_snappers.begin(); it2 != meta_snappers.end(); ++it2)
+ {
+ if (it2->is_equal(s))
+ it2->dec_use_count();
+ }
+
delete *it;
}
}
void remove_lock(const string& config_name);
bool has_lock(const string& config_name) const;
- string name;
+ const string name;
list<Comparison*> comparisons;
void add_task(DBus::Connection& conn, DBus::Message& msg);
+private:
+
void worker();
};
};
+extern Clients clients;
+
+
#endif
#include "MetaSnapper.h"
+MetaSnappers meta_snappers;
+
+
+RefCounter::RefCounter()
+ : counter(0)
+{
+ struct timespec tmp;
+ clock_gettime(CLOCK_MONOTONIC, &tmp);
+ last_used = tmp.tv_sec;
+}
+
+
+int
+RefCounter::inc_use_count()
+{
+ boost::lock_guard<boost::mutex> lock(mutex);
+
+ return ++counter;
+}
+
+
+int
+RefCounter::dec_use_count()
+{
+ boost::lock_guard<boost::mutex> lock(mutex);
+
+ assert(counter > 0);
+
+ if (--counter == 0)
+ {
+ struct timespec tmp;
+ clock_gettime(CLOCK_MONOTONIC, &tmp);
+ last_used = tmp.tv_sec;
+ }
+
+ return counter;
+}
+
+
+void
+RefCounter::update_use_time()
+{
+ boost::lock_guard<boost::mutex> lock(mutex);
+
+ struct timespec tmp;
+ clock_gettime(CLOCK_MONOTONIC, &tmp);
+ last_used = tmp.tv_sec;
+}
+
+
+int
+RefCounter::use_count() const
+{
+ boost::lock_guard<boost::mutex> lock(mutex);
+
+ return counter;
+}
+
+
+int
+RefCounter::unused_for() const
+{
+ boost::lock_guard<boost::mutex> lock(mutex);
+
+ if (counter != 0)
+ return 0;
+
+ struct timespec tmp;
+ clock_gettime(CLOCK_MONOTONIC, &tmp);
+
+ return tmp.tv_sec - last_used;
+}
+
+
bool
get_user_uid(const char* username, uid_t& uid)
{
if (!snapper)
snapper = new Snapper(config_info.config_name);
+ update_use_time();
+
return snapper;
}
+void
+MetaSnapper::unload()
+{
+ delete snapper;
+ snapper = 0;
+}
+
+
MetaSnappers::MetaSnappers()
{
}
for (list<ConfigInfo>::const_iterator it = config_infos.begin(); it != config_infos.end(); ++it)
{
- MetaSnapper meta_snapper(*it);
- entries.push_back(meta_snapper);
+ entries.emplace_back(*it);
}
}
if (it->configName() == config_name)
return it;
- throw;
+ throw UnknownConfig();
}
Snapper::createConfig(config_name, subvolume, fstype, template_name);
ConfigInfo config_info = Snapper::getConfig(config_name);
- MetaSnapper meta_snapper(config_info);
- entries.push_back(meta_snapper);
+ entries.emplace_back(config_info);
}
void
-MetaSnappers::deleteConfig(const string& config_name)
+MetaSnappers::deleteConfig(iterator it)
{
- iterator it = find(config_name);
- if (it == end())
- throw;
-
- Snapper::deleteConfig(config_name);
+ Snapper::deleteConfig(it->configName());
entries.erase(it);
}
#define SNAPPER_META_SNAPPER_H
+#include <boost/thread.hpp>
+
#include <snapper/Snapper.h>
#include <snapper/Snapshot.h>
#include <snapper/Comparison.h>
using namespace snapper;
-class MetaSnapper
+class RefCounter : boost::noncopyable
+{
+public:
+
+ RefCounter();
+
+ int inc_use_count();
+ int dec_use_count();
+ void update_use_time();
+
+ int use_count() const;
+ int unused_for() const;
+
+private:
+
+ mutable boost::mutex mutex;
+
+ int counter;
+
+ time_t last_used;
+
+};
+
+
+class RefHolder
+{
+public:
+
+ RefHolder(RefCounter& ref) : ref(ref)
+ { ref.inc_use_count(); }
+ ~RefHolder()
+ { ref.dec_use_count(); }
+
+private:
+
+ RefCounter& ref;
+
+};
+
+
+struct UnknownConfig : public std::exception
+{
+ explicit UnknownConfig() throw() {}
+ virtual const char* what() const throw() { return "unknown config"; }
+};
+
+
+class MetaSnapper : public RefCounter
{
public:
Snapper* getSnapper();
- bool snapper_loaded() const { return snapper != NULL; }
+ bool is_equal(const Snapper* s) { return snapper && snapper == s; }
+ bool is_loaded() const { return snapper; }
+ void unload();
private:
void createConfig(const string& config_name, const string& subvolume, const string& fstype,
const string& template_name);
- void deleteConfig(const string& config_name);
+ void deleteConfig(iterator);
private:
};
+extern MetaSnappers meta_snappers;
+
+
#endif
#define INTERFACE "org.opensuse.Snapper"
-Clients clients;
-MetaSnappers meta_snappers;
-
boost::shared_mutex big_mutex;
}
-struct UnknownConfig : public std::exception
-{
- explicit UnknownConfig() throw() {}
- virtual const char* what() const throw() { return "unknown config"; }
-};
-
-
struct Permissions : public std::exception
{
explicit Permissions() throw() {}
{
for (Clients::const_iterator it = clients.begin(); it != clients.end(); ++it)
{
- if (it->name == msg.get_sender())
+ if (it->zombie || it->name == msg.get_sender())
continue;
if (it->has_lock(config_name))
}
+struct InUse : public std::exception
+{
+ explicit InUse() throw() {}
+ virtual const char* what() const throw() { return "in use"; }
+};
+
+
+void
+check_in_use(const MetaSnapper& meta_snapper)
+{
+ if (meta_snapper.use_count() != 0)
+ throw InUse();
+}
+
+
void
Client::signal_config_created(DBus::Connection& conn, const string& config_name)
{
boost::unique_lock<boost::shared_mutex> lock(big_mutex);
+ MetaSnappers::iterator it = meta_snappers.find(config_name);
+
check_permission(conn, msg);
+ check_lock(conn, msg, config_name);
+ check_in_use(*it);
- meta_snappers.deleteConfig(config_name);
+ meta_snappers.deleteConfig(it);
DBus::MessageMethodReturn reply(msg);
boost::unique_lock<boost::shared_mutex> lock(big_mutex);
- check_lock(conn, msg, config_name);
-
MetaSnappers::iterator it = meta_snappers.find(config_name);
check_permission(conn, msg, *it);
+ check_lock(conn, msg, config_name);
+ check_in_use(*it);
Snapper* snapper = it->getSnapper();
Snapshots::const_iterator snapshot1 = snapshots.find(num1);
Snapshots::const_iterator snapshot2 = snapshots.find(num2);
+ RefHolder ref_holder(*it);
+
lock.unlock();
Comparison* comparison = new Comparison(snapper, snapshot1, snapshot2);
comparisons.push_back(comparison);
+ it->inc_use_count();
+
DBus::MessageMethodReturn reply(msg);
conn.send(reply);
for (list<Undo>::const_iterator it2 = undos.begin(); it2 != undos.end(); ++it2)
{
Files::iterator it3 = files.find(it2->filename);
- if (it3 == files.end())
- throw;
-
- it3->setUndo(it2->undo);
+ if (it3 != files.end())
+ it3->setUndo(it2->undo);
}
DBus::MessageMethodReturn reply(msg);
std::ostringstream s;
s << " name:'" << it->name << "'";
if (&*it == this)
- s << " myself";
+ s << ", myself";
if (it->zombie)
- s << " zombie";
+ s << ", zombie";
if (!it->locks.empty())
- s << " locks:'" << boost::join(it->locks, ",") << "'";
+ s << ", locks '" << boost::join(it->locks, " ") << "'";
if (!it->comparisons.empty())
- s << " comparisons:" << it->comparisons.size();
+ s << ", comparisons " << it->comparisons.size();
hoho << s.str();
}
{
std::ostringstream s;
s << " name:'" << it->configName() << "'";
- if (it->snapper_loaded())
- s << " (loaded)";
+ if (it->is_loaded())
+ {
+ s << ", loaded";
+ if (it->use_count() == 0)
+ s << ", unused for " << it->unused_for() << "s";
+ else
+ s << ", use count " << it->use_count();
+ }
hoho << s.str();
}
DBus::MessageError reply(msg, "error.config_locked", DBUS_ERROR_FAILED);
conn.send(reply);
}
+ catch (const InUse& e)
+ {
+ DBus::MessageError reply(msg, "error.config_in_use", DBUS_ERROR_FAILED);
+ conn.send(reply);
+ }
catch (const NoComparison& e)
{
DBus::MessageError reply(msg, "error.no_comparisons", DBUS_ERROR_FAILED);
void
MyMainLoop::periodic()
{
- y2deb("periodic");
-
boost::unique_lock<boost::shared_mutex> lock(big_mutex);
clients.remove_zombies();
if (clients.empty())
- {
- y2deb("no clients left");
set_idle_timeout(30);
+
+ for (MetaSnappers::iterator it = meta_snappers.begin(); it != meta_snappers.end(); ++it)
+ {
+ if (it->is_loaded() && it->unused_for() > 10)
+ it->unload();
}
}
if (clients.has_zombies())
return 1000;
+ for (MetaSnappers::const_iterator it = meta_snappers.begin(); it != meta_snappers.end(); ++it)
+ if (it->is_loaded() && it->use_count() == 0)
+ return 1000;
+
return -1;
}