# Makefile.am for snapper
#
-SUBDIRS = snapper examples tools scripts data doc po testsuite-real bindings
+SUBDIRS = snapper examples tools dbus server client scripts data doc po testsuite-real bindings
AUTOMAKE_OPTIONS = foreign dist-bzip2 no-dist-gzip
--- /dev/null
+*.o
+snapper
--- /dev/null
+#
+# Makefile.am for snapper/tools/client
+#
+
+INCLUDES = -I$(top_srcdir) -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include
+
+
+bin_PROGRAMS = snapper
+
+snapper_SOURCES = snapper.cc types.cc commands.cc
+snapper_LDADD = ../snapper/libsnapper.la ../tools/utils/libutils.la ../dbus/libdbus.la -ldbus-1
+
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include "commands.h"
+
+
+list<XConfigInfo>
+command_list_xconfigs(DBus::Connection& conn)
+{
+ DBus::MessageMethodCall call("org.opensuse.snapper", "/org/opensuse/snapper",
+ "org.opensuse.snapper", "ListConfigs");
+
+ DBus::Message reply = conn.send_and_reply_and_block(call);
+
+ list<XConfigInfo> ret;
+
+ DBus::Hihi hihi(reply);
+ hihi >> ret;
+
+ return ret;
+}
+
+
+list<XSnapshot>
+command_list_xsnapshots(DBus::Connection& conn, const string& config_name)
+{
+ DBus::MessageMethodCall call("org.opensuse.snapper", "/org/opensuse/snapper",
+ "org.opensuse.snapper", "ListSnapshots");
+
+ DBus::Hoho hoho(call);
+ hoho << config_name;
+
+ DBus::Message reply = conn.send_and_reply_and_block(call);
+
+ list<XSnapshot> ret;
+
+ DBus::Hihi hihi(reply);
+ hihi >> ret;
+
+ return ret;
+}
+
+
+unsigned int
+command_create_xsnapshot(DBus::Connection& conn, const string& config_name, XSnapshotType type,
+ unsigned int prenum, const string& description, const string& cleanup,
+ const map<string, string>& userdata)
+{
+ DBus::MessageMethodCall call("org.opensuse.snapper", "/org/opensuse/snapper",
+ "org.opensuse.snapper", "CreateSnapshot");
+
+ DBus::Hoho hoho(call);
+ hoho << config_name << type << prenum << description << cleanup << userdata;
+
+ DBus::Message reply = conn.send_and_reply_and_block(call);
+
+ unsigned int number;
+
+ DBus::Hihi hihi(reply);
+ hihi >> number;
+
+ return number;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include <string>
+#include <list>
+#include <map>
+
+using std::string;
+using std::list;
+using std::map;
+
+#include "types.h"
+
+
+list<XConfigInfo>
+command_list_xconfigs(DBus::Connection& conn);
+
+list<XSnapshot>
+command_list_xsnapshots(DBus::Connection& conn, const string& config_name);
+
+unsigned int
+command_create_xsnapshot(DBus::Connection& conn, const string& config_name, XSnapshotType type,
+ unsigned int prenum, const string& description, const string& cleanup,
+ const map<string, string>& userdata);
+
--- /dev/null
+/*
+ * Copyright (c) [2011-2012] 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.
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <iostream>
+#include <boost/algorithm/string.hpp>
+
+#include "config.h"
+#include <snapper/Factory.h>
+#include <snapper/Snapper.h>
+#include <snapper/Snapshot.h>
+#include <snapper/Comparison.h>
+#include <snapper/File.h>
+#include <snapper/AppUtil.h>
+#include <snapper/SnapperTmpl.h>
+#include <snapper/Compare.h>
+#include <snapper/Enum.h>
+#include <snapper/AsciiFile.h>
+
+#include "tools/utils/Table.h"
+#include "tools/utils/GetOpts.h"
+
+#include "commands.h"
+
+using namespace snapper;
+using namespace std;
+
+
+typedef void (*cmd_fnc)(DBus::Connection& conn);
+map<string, cmd_fnc> cmds;
+
+GetOpts getopts;
+
+bool quiet = false;
+bool verbose = false;
+string config_name = "root";
+bool disable_filters = false;
+
+Snapper* sh = NULL;
+
+
+Snapshots::iterator
+read_num(const string& str)
+{
+ Snapshots& snapshots = sh->getSnapshots();
+
+ istringstream s(str);
+ unsigned int num = 0;
+ s >> num;
+
+ if (s.fail() || !s.eof())
+ {
+ cerr << sformat(_("Invalid snapshot '%s'."), str.c_str()) << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ Snapshots::iterator snap = snapshots.find(num);
+ if (snap == snapshots.end())
+ {
+ cerr << sformat(_("Snapshot '%u' not found."), num) << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ return snap;
+}
+
+
+pair<Snapshots::iterator, Snapshots::iterator>
+read_nums(const string& str)
+{
+ string::size_type pos = str.find("..");
+ if (pos == string::npos)
+ {
+ cerr << _("Invalid snapshots.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ Snapshots::iterator snap1 = read_num(str.substr(0, pos));
+ Snapshots::iterator snap2 = read_num(str.substr(pos + 2));
+
+ if (snap1 == snap2)
+ {
+ cerr << _("Identical snapshots.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ return pair<Snapshots::iterator, Snapshots::iterator>(snap1, snap2);
+}
+
+
+map<string, string>
+read_userdata(const string& s, const map<string, string>& old = map<string, string>())
+{
+ map<string, string> userdata = old;
+
+ list<string> tmp;
+ boost::split(tmp, s, boost::is_any_of(","), boost::token_compress_on);
+ if (tmp.empty())
+ {
+ cerr << _("Invalid userdata.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ for (list<string>::const_iterator it = tmp.begin(); it != tmp.end(); ++it)
+ {
+ string::size_type pos = it->find("=");
+ if (pos == string::npos)
+ {
+ cerr << _("Invalid userdata.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ string key = boost::trim_copy(it->substr(0, pos));
+ string value = boost::trim_copy(it->substr(pos + 1));
+
+ if (key.empty())
+ {
+ cerr << _("Invalid userdata.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ if (value.empty())
+ userdata.erase(key);
+ else
+ userdata[key] = value;
+ }
+
+ return userdata;
+}
+
+
+string
+show_userdata(const map<string, string>& userdata)
+{
+ string s;
+
+ for (map<string, string>::const_iterator it = userdata.begin(); it != userdata.end(); ++it)
+ {
+ if (!s.empty())
+ s += ", ";
+ s += it->first + "=" + it->second;
+ }
+
+ return s;
+}
+
+
+void
+help_list_configs()
+{
+ cout << _(" List configs:") << endl
+ << _("\tsnapper list-configs") << endl
+ << endl;
+}
+
+
+void
+command_list_configs(DBus::Connection& conn)
+{
+ getopts.parse("list-configs", GetOpts::no_options);
+ if (getopts.hasArgs())
+ {
+ cerr << _("Command 'list-configs' does not take arguments.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ Table table;
+
+ TableHeader header;
+ header.add(_("Config"));
+ header.add(_("Subvolume"));
+ table.setHeader(header);
+
+ try
+ {
+ list<XConfigInfo> config_infos = command_list_xconfigs(conn);
+ for (list<XConfigInfo>::const_iterator it = config_infos.begin(); it != config_infos.end(); ++it)
+ {
+ TableRow row;
+ row.add(it->config_name);
+ row.add(it->subvolume);
+ table.add(row);
+ }
+ }
+ catch (const ListConfigsFailedException& e)
+ {
+ cerr << sformat(_("Listing configs failed (%s)."), e.what()) << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ cout << table;
+}
+
+
+void
+help_create_config()
+{
+ cout << _(" Create config:") << endl
+ << _("\tsnapper create-config <subvolume>") << endl
+ << endl
+ << _(" Options for 'create-config' command:") << endl
+ << _("\t--fstype, -f <fstype>\t\tManually set filesystem type.") << endl
+ << _("\t--template, -t <name>\t\tName of config template to use.") << endl
+ << endl;
+}
+
+
+void
+command_create_config()
+{
+ const struct option options[] = {
+ { "fstype", required_argument, 0, 'f' },
+ { "template", required_argument, 0, 't' },
+ { 0, 0, 0, 0 }
+ };
+
+ GetOpts::parsed_opts opts = getopts.parse("create-config", options);
+ if (getopts.numArgs() != 1)
+ {
+ cerr << _("Command 'create-config' needs one argument.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ string subvolume = realpath(getopts.popArg());
+ if (subvolume.empty())
+ {
+ cerr << _("Invalid subvolume.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ string fstype = "";
+ string template_name = "default";
+
+ GetOpts::parsed_opts::const_iterator opt;
+
+ if ((opt = opts.find("fstype")) != opts.end())
+ fstype = opt->second;
+
+ if ((opt = opts.find("template")) != opts.end())
+ template_name = opt->second;
+
+ if (fstype.empty() && !Snapper::detectFstype(subvolume, fstype))
+ {
+ cerr << _("Detecting filesystem type failed.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ try
+ {
+ Snapper::createConfig(config_name, subvolume, fstype, template_name);
+ }
+ catch (const CreateConfigFailedException& e)
+ {
+ cerr << sformat(_("Creating config failed (%s)."), e.what()) << endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+void
+help_delete_config()
+{
+ cout << _(" Delete config:") << endl
+ << _("\tsnapper delete-config") << endl
+ << endl;
+}
+
+
+void
+command_delete_config()
+{
+ getopts.parse("delete-config", GetOpts::no_options);
+ if (getopts.hasArgs())
+ {
+ cerr << _("Command 'delete-config' does not take arguments.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ try
+ {
+ Snapper::deleteConfig(config_name);
+ }
+ catch (const DeleteConfigFailedException& e)
+ {
+ cerr << sformat(_("Deleting config failed (%s)."), e.what()) << endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+void
+help_list()
+{
+ cout << _(" List snapshots:") << endl
+ << _("\tsnapper list") << endl
+ << endl
+ << _(" Options for 'list' command:") << endl
+ << _("\t--type, -t <type>\t\tType of snapshots to list.") << endl
+ << endl;
+}
+
+
+void
+command_list()
+{
+ const struct option options[] = {
+ { "type", required_argument, 0, 't' },
+ { 0, 0, 0, 0 }
+ };
+
+ GetOpts::parsed_opts opts = getopts.parse("list", options);
+ if (getopts.hasArgs())
+ {
+ cerr << _("Command 'list' does not take arguments.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ enum ListMode { LM_ALL, LM_SINGLE, LM_PRE_POST };
+ ListMode list_mode = LM_ALL;
+
+ GetOpts::parsed_opts::const_iterator opt;
+
+ if ((opt = opts.find("type")) != opts.end())
+ {
+ if (opt->second == "all")
+ list_mode = LM_ALL;
+ else if (opt->second == "single")
+ list_mode = LM_SINGLE;
+ else if (opt->second == "pre-post")
+ list_mode = LM_PRE_POST;
+ else
+ {
+ cerr << _("Unknown type of snapshots.") << endl;
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ Table table;
+
+ switch (list_mode)
+ {
+ case LM_ALL:
+ {
+ TableHeader header;
+ header.add(_("Type"));
+ header.add(_("#"));
+ header.add(_("Pre #"));
+ header.add(_("Date"));
+ header.add(_("Cleanup"));
+ header.add(_("Description"));
+ header.add(_("Userdata"));
+ table.setHeader(header);
+
+ const Snapshots& snapshots = sh->getSnapshots();
+ for (Snapshots::const_iterator it1 = snapshots.begin(); it1 != snapshots.end(); ++it1)
+ {
+ TableRow row;
+ row.add(toString(it1->getType()));
+ row.add(decString(it1->getNum()));
+ // row.add(it1->getType() == POST ? decString(it1->getPreNum()) : "");
+ row.add(it1->isCurrent() ? "" : datetime(it1->getDate(), false, false));
+ row.add(it1->getCleanup());
+ row.add(it1->getDescription());
+ row.add(show_userdata(it1->getUserdata()));
+ table.add(row);
+ }
+ }
+ break;
+
+ case LM_SINGLE:
+ {
+ TableHeader header;
+ header.add(_("#"));
+ header.add(_("Date"));
+ header.add(_("Description"));
+ header.add(_("Userdata"));
+ table.setHeader(header);
+
+ const Snapshots& snapshots = sh->getSnapshots();
+ for (Snapshots::const_iterator it1 = snapshots.begin(); it1 != snapshots.end(); ++it1)
+ {
+ // if (it1->getType() != SINGLE)
+ // continue;
+
+ TableRow row;
+ row.add(decString(it1->getNum()));
+ row.add(it1->isCurrent() ? "" : datetime(it1->getDate(), false, false));
+ row.add(it1->getDescription());
+ row.add(show_userdata(it1->getUserdata()));
+ table.add(row);
+ }
+ }
+ break;
+
+ case LM_PRE_POST:
+ {
+ TableHeader header;
+ header.add(_("Pre #"));
+ header.add(_("Post #"));
+ header.add(_("Pre Date"));
+ header.add(_("Post Date"));
+ header.add(_("Description"));
+ header.add(_("Userdata"));
+ table.setHeader(header);
+
+ const Snapshots& snapshots = sh->getSnapshots();
+ for (Snapshots::const_iterator it1 = snapshots.begin(); it1 != snapshots.end(); ++it1)
+ {
+ // if (it1->getType() != PRE)
+ // continue;
+
+ Snapshots::const_iterator it2 = snapshots.findPost(it1);
+ if (it2 == snapshots.end())
+ continue;
+
+ TableRow row;
+ row.add(decString(it1->getNum()));
+ row.add(decString(it2->getNum()));
+ row.add(datetime(it1->getDate(), false, false));
+ row.add(datetime(it2->getDate(), false, false));
+ row.add(it1->getDescription());
+ row.add(show_userdata(it1->getUserdata()));
+ table.add(row);
+ }
+ }
+ break;
+ }
+
+ cout << table;
+}
+
+
+void
+help_create()
+{
+ cout << _(" Create snapshot:") << endl
+ << _("\tsnapper create") << endl
+ << endl
+ << _(" Options for 'create' command:") << endl
+ << _("\t--type, -t <type>\t\tType for snapshot.") << endl
+ << _("\t--pre-number <number>\t\tNumber of corresponding pre snapshot.") << endl
+ << _("\t--print-number, -p\t\tPrint number of created snapshot.") << endl
+ << _("\t--description, -d <description>\tDescription for snapshot.") << endl
+ << _("\t--cleanup-algorithm, -c <algo>\tCleanup algorithm for snapshot.") << endl
+ << _("\t--userdata, -u <userdata>\tUserdata for snapshot.") << endl
+ << _("\t--command <command>\tRun command and create pre and post snapshots.") << endl
+ << endl;
+}
+
+
+void
+command_create()
+{
+ const struct option options[] = {
+ { "type", required_argument, 0, 't' },
+ { "pre-number", required_argument, 0, 0 },
+ { "print-number", no_argument, 0, 'p' },
+ { "description", required_argument, 0, 'd' },
+ { "cleanup-algorithm", required_argument, 0, 'c' },
+ { "userdata", required_argument, 0, 'u' },
+ { "command", required_argument, 0, 0 },
+ { 0, 0, 0, 0 }
+ };
+
+ GetOpts::parsed_opts opts = getopts.parse("create", options);
+ if (getopts.hasArgs())
+ {
+ cerr << _("Command 'create' does not take arguments.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ enum CreateType { CT_SINGLE, CT_PRE, CT_POST, CT_PRE_POST };
+
+ const Snapshots& snapshots = sh->getSnapshots();
+
+ CreateType type = CT_SINGLE;
+ Snapshots::const_iterator snap1 = snapshots.end();
+ bool print_number = false;
+ string description;
+ string cleanup;
+ map<string, string> userdata;
+ string command;
+
+ GetOpts::parsed_opts::const_iterator opt;
+
+ if ((opt = opts.find("type")) != opts.end())
+ {
+ if (opt->second == "single")
+ type = CT_SINGLE;
+ else if (opt->second == "pre")
+ type = CT_PRE;
+ else if (opt->second == "post")
+ type = CT_POST;
+ else if (opt->second == "pre-post")
+ type = CT_PRE_POST;
+ else
+ {
+ cerr << _("Unknown type of snapshot.") << endl;
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if ((opt = opts.find("pre-number")) != opts.end())
+ snap1 = read_num(opt->second);
+
+ if ((opt = opts.find("print-number")) != opts.end())
+ print_number = true;
+
+ if ((opt = opts.find("description")) != opts.end())
+ description = opt->second;
+
+ if ((opt = opts.find("cleanup-algorithm")) != opts.end())
+ cleanup = opt->second;
+
+ if ((opt = opts.find("userdata")) != opts.end())
+ userdata = read_userdata(opt->second);
+
+ if ((opt = opts.find("command")) != opts.end())
+ {
+ command = opt->second;
+ type = CT_PRE_POST;
+ }
+
+ if (type == CT_POST && (snap1 == snapshots.end() || snap1->isCurrent()))
+ {
+ cerr << _("Missing or invalid pre-number.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ if (type == CT_PRE_POST && command.empty())
+ {
+ cerr << _("Missing command argument.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ try
+ {
+ switch (type)
+ {
+ case CT_SINGLE: {
+ Snapshots::iterator snap1 = sh->createSingleSnapshot(description);
+ snap1->setCleanup(cleanup);
+ snap1->setUserdata(userdata);
+ snap1->flushInfo();
+ if (print_number)
+ cout << snap1->getNum() << endl;
+ } break;
+
+ case CT_PRE: {
+ Snapshots::iterator snap1 = sh->createPreSnapshot(description);
+ snap1->setCleanup(cleanup);
+ snap1->setUserdata(userdata);
+ snap1->flushInfo();
+ if (print_number)
+ cout << snap1->getNum() << endl;
+ } break;
+
+ case CT_POST: {
+ Snapshots::iterator snap2 = sh->createPostSnapshot(description, snap1);
+ snap2->setCleanup(cleanup);
+ snap2->setUserdata(userdata);
+ snap2->flushInfo();
+ if (print_number)
+ cout << snap2->getNum() << endl;
+ sh->startBackgroundComparsion(snap1, snap2);
+ } break;
+
+ case CT_PRE_POST: {
+ Snapshots::iterator snap1 = sh->createPreSnapshot(description);
+ snap1->setCleanup(cleanup);
+ snap1->setUserdata(userdata);
+ snap1->flushInfo();
+
+ system(command.c_str());
+
+ Snapshots::iterator snap2 = sh->createPostSnapshot("", snap1);
+ snap2->setCleanup(cleanup);
+ snap2->setUserdata(userdata);
+ snap2->flushInfo();
+ if (print_number)
+ cout << snap1->getNum() << ".." << snap2->getNum() << endl;
+ sh->startBackgroundComparsion(snap1, snap2);
+ } break;
+ }
+ }
+ catch (const InvalidUserdataException& e)
+ {
+ cerr << _("Invalid userdata.") << endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+void
+help_modify()
+{
+ cout << _(" Modify snapshot:") << endl
+ << _("\tsnapper modify <number>") << endl
+ << endl
+ << _(" Options for 'modify' command:") << endl
+ << _("\t--description, -d <description>\tDescription for snapshot.") << endl
+ << _("\t--cleanup-algorithm, -c <algo>\tCleanup algorithm for snapshot.") << endl
+ << _("\t--userdata, -u <userdata>\tUserdata for snapshot.") << endl
+ << endl;
+}
+
+
+void
+command_modify()
+{
+ const struct option options[] = {
+ { "description", required_argument, 0, 'd' },
+ { "cleanup-algorithm", required_argument, 0, 'c' },
+ { "userdata", required_argument, 0, 'u' },
+ { 0, 0, 0, 0 }
+ };
+
+ GetOpts::parsed_opts opts = getopts.parse("modify", options);
+
+ if (!getopts.hasArgs())
+ {
+ cerr << _("Command 'modify' needs at least one argument.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ try
+ {
+ while (getopts.hasArgs())
+ {
+ Snapshots::iterator snapshot = read_num(getopts.popArg());
+
+ GetOpts::parsed_opts::const_iterator opt;
+
+ if ((opt = opts.find("description")) != opts.end())
+ snapshot->setDescription(opt->second);
+
+ if ((opt = opts.find("cleanup-algorithm")) != opts.end())
+ snapshot->setCleanup(opt->second);
+
+ if ((opt = opts.find("userdata")) != opts.end())
+ snapshot->setUserdata(read_userdata(opt->second, snapshot->getUserdata()));
+
+ snapshot->flushInfo();
+ }
+ }
+ catch (const IllegalSnapshotException& e)
+ {
+ cerr << _("Invalid snapshot.") << endl;
+ exit(EXIT_FAILURE);
+ }
+ catch (const InvalidUserdataException& e)
+ {
+ cerr << _("Invalid userdata.") << endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+void
+help_delete()
+{
+ cout << _(" Delete snapshot:") << endl
+ << _("\tsnapper delete <number>") << endl
+ << endl;
+}
+
+
+void
+command_delete()
+{
+ getopts.parse("delete", GetOpts::no_options);
+ if (!getopts.hasArgs())
+ {
+ cerr << _("Command 'delete' needs at least one argument.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ try
+ {
+ while (getopts.hasArgs())
+ {
+ Snapshots::iterator snapshot = read_num(getopts.popArg());
+
+ sh->deleteSnapshot(snapshot);
+ }
+ }
+ catch (const IllegalSnapshotException& e)
+ {
+ cerr << _("Invalid snapshot.") << endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+void
+help_mount()
+{
+ cout << _(" Mount snapshot:") << endl
+ << _("\tsnapper mount <number>") << endl
+ << endl;
+}
+
+
+void
+command_mount()
+{
+ getopts.parse("mount", GetOpts::no_options);
+ if (!getopts.hasArgs())
+ {
+ cerr << _("Command 'mount' needs at least one argument.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ try
+ {
+ while (getopts.hasArgs())
+ {
+ Snapshots::iterator snapshot = read_num(getopts.popArg());
+
+ snapshot->mountFilesystemSnapshot();
+ }
+ }
+ catch (const IllegalSnapshotException& e)
+ {
+ cerr << _("Invalid snapshot.") << endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+void
+help_umount()
+{
+ cout << _(" Umount snapshot:") << endl
+ << _("\tsnapper umount <number>") << endl
+ << endl;
+}
+
+
+void
+command_umount()
+{
+ getopts.parse("mount", GetOpts::no_options);
+ if (!getopts.hasArgs())
+ {
+ cerr << _("Command 'mount' needs at least one argument.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ try
+ {
+ while (getopts.hasArgs())
+ {
+ Snapshots::iterator snapshot = read_num(getopts.popArg());
+
+ snapshot->umountFilesystemSnapshot();
+ }
+ }
+ catch (const IllegalSnapshotException& e)
+ {
+ cerr << _("Invalid snapshot.") << endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+void
+help_status()
+{
+ cout << _(" Comparing snapshots:") << endl
+ << _("\tsnapper status <number1>..<number2>") << endl
+ << endl
+ << _(" Options for 'status' command:") << endl
+ << _("\t--output, -o <file>\t\tSave status to file.") << endl
+ << endl;
+}
+
+
+void
+command_status()
+{
+ const struct option options[] = {
+ { "output", required_argument, 0, 'o' },
+ { 0, 0, 0, 0 }
+ };
+
+ GetOpts::parsed_opts opts = getopts.parse("status", options);
+ if (getopts.numArgs() != 1)
+ {
+ cerr << _("Command 'status' needs one argument.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ GetOpts::parsed_opts::const_iterator opt;
+
+ pair<Snapshots::const_iterator, Snapshots::const_iterator> snaps(read_nums(getopts.popArg()));
+
+ Comparison comparison(sh, snaps.first, snaps.second);
+
+ const Files& files = comparison.getFiles();
+
+ FILE* file = stdout;
+
+ if ((opt = opts.find("output")) != opts.end())
+ {
+ file = fopen(opt->second.c_str(), "w");
+ if (!file)
+ {
+ cerr << sformat(_("Opening file '%s' failed."), opt->second.c_str()) << endl;
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ for (Files::const_iterator it = files.begin(); it != files.end(); ++it)
+ fprintf(file, "%s %s\n", statusToString(it->getPreToPostStatus()).c_str(),
+ it->getAbsolutePath(LOC_SYSTEM).c_str());
+
+ if (file != stdout)
+ fclose(file);
+}
+
+
+void
+help_diff()
+{
+ cout << _(" Comparing snapshots:") << endl
+ << _("\tsnapper diff <number1>..<number2> [files]") << endl
+ << endl;
+}
+
+
+void
+command_diff()
+{
+ GetOpts::parsed_opts opts = getopts.parse("diff", GetOpts::no_options);
+
+ GetOpts::parsed_opts::const_iterator opt;
+
+ pair<Snapshots::const_iterator, Snapshots::const_iterator> snaps(read_nums(getopts.popArg()));
+
+ Comparison comparison(sh, snaps.first, snaps.second);
+
+ const Files& files = comparison.getFiles();
+
+ if (getopts.numArgs() == 0)
+ {
+ for (Files::const_iterator it1 = files.begin(); it1 != files.end(); ++it1)
+ {
+ vector<string> lines = it1->getDiff("--unified --new-file");
+ for (vector<string>::const_iterator it2 = lines.begin(); it2 != lines.end(); ++it2)
+ cout << it2->c_str() << endl;
+ }
+ }
+ else
+ {
+ while (getopts.numArgs() > 0)
+ {
+ string name = getopts.popArg();
+
+ Files::const_iterator tmp = files.findAbsolutePath(name);
+ if (tmp == files.end())
+ continue;
+
+ vector<string> lines = tmp->getDiff("--unified --new-file");
+ for (vector<string>::const_iterator it2 = lines.begin(); it2 != lines.end(); ++it2)
+ cout << it2->c_str() << endl;
+ }
+ }
+}
+
+
+void
+help_undo()
+{
+ cout << _(" Undo changes:") << endl
+ << _("\tsnapper undochange <number1>..<number2> [files]") << endl
+ << endl
+ << _(" Options for 'undochange' command:") << endl
+ << _("\t--input, -i <file>\t\tRead files for which to undo changes from file.") << endl
+ << endl;
+}
+
+
+void
+command_undo()
+{
+ const struct option options[] = {
+ { "input", required_argument, 0, 'i' },
+ { 0, 0, 0, 0 }
+ };
+
+ GetOpts::parsed_opts opts = getopts.parse("undochange", options);
+ if (getopts.numArgs() < 1)
+ {
+ cerr << _("Command 'undochange' needs at least one argument.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ pair<Snapshots::const_iterator, Snapshots::const_iterator> snaps(read_nums(getopts.popArg()));
+
+ FILE* file = NULL;
+
+ GetOpts::parsed_opts::const_iterator opt;
+
+ if ((opt = opts.find("input")) != opts.end())
+ {
+ file = fopen(opt->second.c_str(), "r");
+ if (!file)
+ {
+ cerr << sformat(_("Opening file '%s' failed."), opt->second.c_str()) << endl;
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (snaps.first->isCurrent())
+ {
+ cerr << _("Invalid snapshots.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ Comparison comparison(sh, snaps.first, snaps.second);
+
+ Files& files = comparison.getFiles();
+
+ if (file)
+ {
+ AsciiFileReader asciifile(file);
+
+ string line;
+ while (asciifile.getline(line))
+ {
+ if (line.empty())
+ continue;
+
+ string name = line;
+
+ // strip optional status
+ if (name[0] != '/')
+ {
+ string::size_type pos = name.find(" ");
+ if (pos == string::npos)
+ continue;
+
+ name.erase(0, pos + 1);
+ }
+
+ Files::iterator it = files.findAbsolutePath(name);
+ if (it == files.end())
+ {
+ cerr << sformat(_("File '%s' not found."), name.c_str()) << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ it->setUndo(true);
+ }
+ }
+ else
+ {
+ if (getopts.numArgs() == 0)
+ {
+ for (Files::iterator it = files.begin(); it != files.end(); ++it)
+ it->setUndo(true);
+ }
+ else
+ {
+ while (getopts.numArgs() > 0)
+ {
+ string name = getopts.popArg();
+
+ Files::iterator tmp = files.findAbsolutePath(name);
+ if (tmp == files.end())
+ continue;
+
+ tmp->setUndo(true);
+ }
+ }
+ }
+
+ UndoStatistic rs = comparison.getUndoStatistic();
+
+ if (rs.empty())
+ {
+ cout << "nothing to do" << endl;
+ return;
+ }
+
+ cout << "create:" << rs.numCreate << " modify:" << rs.numModify << " delete:" << rs.numDelete
+ << endl;
+
+ comparison.doUndo();
+}
+
+
+void
+help_cleanup()
+{
+ cout << _(" Cleanup snapshots:") << endl
+ << _("\tsnapper cleanup <cleanup-algorithm>") << endl
+ << endl;
+}
+
+
+void
+command_cleanup()
+{
+ const struct option options[] = {
+ { 0, 0, 0, 0 }
+ };
+
+ GetOpts::parsed_opts opts = getopts.parse("cleanup", options);
+ if (getopts.numArgs() != 1)
+ {
+ cerr << _("Command 'cleanup' needs one arguments.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ string cleanup = getopts.popArg();
+
+ if (cleanup == "number")
+ {
+ sh->doCleanupNumber();
+ }
+ else if (cleanup == "timeline")
+ {
+ sh->doCleanupTimeline();
+ }
+ else if (cleanup == "empty-pre-post")
+ {
+ sh->doCleanupEmptyPrePost();
+ }
+ else
+ {
+ cerr << sformat(_("Unknown cleanup algorithm '%s'."), cleanup.c_str()) << endl;
+ exit(EXIT_FAILURE);
+ }
+}
+
+
+void
+command_help()
+{
+ getopts.parse("help", GetOpts::no_options);
+ if (getopts.hasArgs())
+ {
+ cerr << _("Command 'help' does not take arguments.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ cout << _("usage: snapper [--global-options] <command> [--command-options] [command-arguments]") << endl
+ << endl;
+
+ cout << _(" Global options:") << endl
+ << _("\t--quiet, -q\t\t\tSuppress normal output.") << endl
+ << _("\t--verbose, -v\t\t\tIncrease verbosity.") << endl
+ << _("\t--table-style, -t <style>\tTable style (integer).") << endl
+ << _("\t--config, -c <name>\t\tSet name of config to use.") << endl
+ << _("\t--disable-filters\t\tDisable filters.") << endl
+ << _("\t--version\t\t\tPrint version and exit.") << endl
+ << endl;
+
+ help_list_configs();
+ help_create_config();
+ help_delete_config();
+ help_list();
+ help_create();
+ help_modify();
+ help_delete();
+ help_mount();
+ help_umount();
+ help_status();
+ help_diff();
+ help_undo();
+ help_cleanup();
+}
+
+
+struct CompareCallbackImpl : public CompareCallback
+{
+ void start() { cout << _("comparing snapshots...") << flush; }
+ void stop() { cout << " " << _("done") << endl; }
+};
+
+CompareCallbackImpl compare_callback_impl;
+
+
+struct UndoCallbackImpl : public UndoCallback
+{
+ void start() { cout << _("undoing change...") << endl; }
+ void stop() { cout << _("undoing change done") << endl; }
+
+ void createInfo(const string& name)
+ { if (verbose) cout << sformat(_("creating %s"), name.c_str()) << endl; }
+ void modifyInfo(const string& name)
+ { if (verbose) cout << sformat(_("modifying %s"), name.c_str()) << endl; }
+ void deleteInfo(const string& name)
+ { if (verbose) cout << sformat(_("deleting %s"), name.c_str()) << endl; }
+
+ void createError(const string& name)
+ { cerr << sformat(_("failed to create %s"), name.c_str()) << endl; }
+ void modifyError(const string& name)
+ { cerr << sformat(_("failed to modify %s"), name.c_str()) << endl; }
+ void deleteError(const string& name)
+ { cerr << sformat(_("failed to delete %s"), name.c_str()) << endl; }
+};
+
+UndoCallbackImpl undo_callback_impl;
+
+
+int
+main(int argc, char** argv)
+{
+ umask(0027);
+
+ setlocale(LC_ALL, "");
+
+ initDefaultLogger();
+
+ cmds["list-configs"] = command_list_configs;
+ // cmds["create-config"] = command_create_config;
+ // cmds["delete-config"] = command_delete_config;
+ // cmds["list"] = command_list;
+ // cmds["create"] = command_create;
+ // cmds["modify"] = command_modify;
+ // cmds["delete"] = command_delete;
+ // cmds["mount"] = command_mount;
+ // cmds["umount"] = command_umount;
+ // cmds["status"] = command_status;
+ // cmds["diff"] = command_diff;
+ // cmds["undochange"] = command_undo;
+ // cmds["cleanup"] = command_cleanup;
+ // cmds["help"] = command_help;
+
+ const struct option options[] = {
+ { "quiet", no_argument, 0, 'q' },
+ { "verbose", no_argument, 0, 'v' },
+ { "table-style", required_argument, 0, 't' },
+ { "config", required_argument, 0, 'c' },
+ { "disable-filters", no_argument, 0, 0 },
+ { "version", no_argument, 0, 0 },
+ { "help", no_argument, 0, 0 },
+ { 0, 0, 0, 0 }
+ };
+
+ getopts.init(argc, argv);
+
+ GetOpts::parsed_opts opts = getopts.parse(options);
+
+ GetOpts::parsed_opts::const_iterator opt;
+
+ if ((opt = opts.find("quiet")) != opts.end())
+ quiet = true;
+
+ if ((opt = opts.find("verbose")) != opts.end())
+ verbose = true;
+
+ if ((opt = opts.find("table-style")) != opts.end())
+ {
+ unsigned int s;
+ opt->second >> s;
+ if (s >= _End)
+ {
+ cerr << sformat(_("Invalid table style %d."), s) << " "
+ << sformat(_("Use an integer number from %d to %d"), 0, _End - 1) << endl;
+ exit(EXIT_FAILURE);
+ }
+ Table::defaultStyle = (TableLineStyle) s;
+ }
+
+ if ((opt = opts.find("config")) != opts.end())
+ config_name = opt->second;
+
+ if ((opt = opts.find("disable-filters")) != opts.end())
+ disable_filters = true;
+
+ if ((opt = opts.find("version")) != opts.end())
+ {
+ cout << "snapper " << VERSION << endl;
+ exit(EXIT_SUCCESS);
+ }
+
+ if ((opt = opts.find("help")) != opts.end())
+ {
+ command_help();
+ exit(EXIT_SUCCESS);
+ }
+
+ if (!getopts.hasArgs())
+ {
+ cerr << _("No command provided.") << endl
+ << _("Try 'snapper help' for more information.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ const char* command = getopts.popArg();
+ map<string, cmd_fnc>::const_iterator cmd = cmds.find(command);
+ if (cmd == cmds.end())
+ {
+ cerr << sformat(_("Unknown command '%s'."), command) << endl
+ << _("Try 'snapper help' for more information.") << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ DBus::Connection conn(DBUS_BUS_SYSTEM);
+
+ if (cmd->first == "help" || cmd->first == "list-configs" ||
+ cmd->first == "create-config" || cmd->first == "delete-config")
+ {
+ (*cmd->second)(conn);
+ }
+ else
+ {
+ try
+ {
+ sh = createSnapper(config_name, disable_filters);
+ }
+ 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);
+ }
+
+ if (!quiet)
+ {
+ sh->setCompareCallback(&compare_callback_impl);
+ sh->setUndoCallback(&undo_callback_impl);
+ }
+
+ try
+ {
+ (*cmd->second)(conn);
+ }
+ catch (const SnapperException& e)
+ {
+ y2err("caught final exception");
+ cerr << sformat(_("Command failed (%s). See log for more information."),
+ e.what()) << endl;
+ exit(EXIT_FAILURE);
+ }
+
+ deleteSnapper(sh);
+ }
+
+ exit(EXIT_SUCCESS);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include "types.h"
+
+
+namespace DBus
+{
+ const char* TypeInfo<XConfigInfo>::signature = "(ss)";
+ const char* TypeInfo<XSnapshot>::signature = "(uqusa{ss})";
+ const char* TypeInfo<XFile>::signature = "(ssb)";
+ const char* TypeInfo<XUndo>::signature = "(sb)";
+
+
+ Hihi&
+ operator>>(Hihi& hihi, XConfigInfo& data)
+ {
+ hihi.open_recurse();
+ hihi >> data.config_name >> data.subvolume;
+ hihi.close_recurse();
+ return hihi;
+ }
+
+
+ Hihi&
+ operator>>(Hihi& hihi, XSnapshotType& data)
+ {
+ dbus_uint16_t tmp;
+ hihi >> tmp;
+ data = static_cast<XSnapshotType>(tmp);
+ return hihi;
+ }
+
+
+ Hihi&
+ operator>>(Hihi& hihi, XSnapshot& data)
+ {
+ hihi.open_recurse();
+ hihi >> data.num >> data.type >> data.date >> data.description >> data.userdata;
+ hihi.close_recurse();
+ return hihi;
+ }
+
+
+ Hihi&
+ operator>>(Hihi& hihi, XFile& data)
+ {
+ hihi.open_recurse();
+ hihi >> data.filename >> data.status >> data.undo;
+ hihi.close_recurse();
+ return hihi;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, XSnapshotType data)
+ {
+ hoho << static_cast<dbus_uint16_t>(data);
+ return hoho;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, const XUndo& data)
+ {
+ hoho.open_struct();
+ hoho << data.filename << data.undo;
+ hoho.close_struct();
+ return hoho;
+ }
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include <string>
+#include <list>
+#include <map>
+
+using std::string;
+using std::list;
+using std::map;
+
+#include "dbus/DBusConnection.h"
+#include "dbus/DBusMessage.h"
+
+
+struct XConfigInfo
+{
+ string config_name;
+ string subvolume;
+};
+
+
+enum XSnapshotType { SINGLE, PRE, POST };
+
+
+struct XSnapshot
+{
+ unsigned int num;
+ XSnapshotType type;
+ unsigned int date;
+ string description;
+ map<string, string> userdata;
+};
+
+
+struct XFile
+{
+ string status;
+ string filename;
+ bool undo;
+};
+
+
+struct XUndo
+{
+ string filename;
+ bool undo;
+};
+
+
+namespace DBus
+{
+
+ template <> struct TypeInfo<XSnapshot> { static const char* signature; };
+ template <> struct TypeInfo<XConfigInfo> { static const char* signature; };
+ template <> struct TypeInfo<XFile> { static const char* signature; };
+ template <> struct TypeInfo<XUndo> { static const char* signature; };
+
+ Hihi& operator>>(Hihi& hihi, XConfigInfo& data);
+ Hihi& operator>>(Hihi& hihi, XSnapshotType& data);
+ Hihi& operator>>(Hihi& hihi, XSnapshot& data);
+ Hihi& operator>>(Hihi& hihi, XFile& data);
+
+ Hoho& operator<<(Hoho& hoho, XSnapshotType data);
+ Hoho& operator<<(Hoho& hoho, const XUndo& data);
+
+};
+
bindings/python/Makefile
tools/Makefile
tools/utils/Makefile
+ dbus/Makefile
+ server/Makefile
+ client/Makefile
scripts/Makefile
data/Makefile
doc/Makefile
# Makefile.am for snapper/data
#
-EXTRA_DIST = sysconfig.snapper base.txt x11.txt snapper.logrotate default-config
+EXTRA_DIST = sysconfig.snapper base.txt x11.txt snapper.logrotate default-config \
+ org.opensuse.snapper.conf org.opensuse.snapper.service
install-data-local:
install -D -m 644 snapper.logrotate $(DESTDIR)/etc/logrotate.d/snapper
install -d -m 755 $(DESTDIR)/etc/snapper/filters
install -D -m 644 base.txt $(DESTDIR)/etc/snapper/filters/base.txt
install -D -m 644 x11.txt $(DESTDIR)/etc/snapper/filters/x11.txt
-
+ install -D -m 644 org.opensuse.snapper.conf /etc/dbus-1/system.d/org.opensuse.snapper.conf
+ install -D -m 644 org.opensuse.snapper.service /usr/share/dbus-1/system-services/org.opensuse.snapper.service
--- /dev/null
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+
+ <policy user="root">
+ <allow own="org.opensuse.snapper"/>
+ <allow send_destination="org.opensuse.snapper" send_interface="org.opensuse.snapper"/>
+ <allow send_destination="org.opensuse.snapper" send_interface="org.freedesktop.DBus.Introspectable"/>
+ </policy>
+ <policy context="default">
+ <deny own="org.opensuse.snapper"/>
+ <allow send_destination="org.opensuse.snapper" send_interface="org.opensuse.snapper"/>
+ <allow send_destination="org.opensuse.snapper" send_interface="org.freedesktop.DBus.Introspectable"/>
+ </policy>
+
+</busconfig>
--- /dev/null
+# DBus service activation config
+[D-BUS Service]
+Name=org.opensuse.snapper
+Exec=/usr/sbin/snapperd
+User=root
--- /dev/null
+*.lo
+*.la
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <pwd.h>
+
+#include "DBusConnection.h"
+
+namespace DBus
+{
+
+ Connection::Connection(DBusBusType type)
+ {
+ DBusError err;
+ dbus_error_init(&err);
+
+ conn = dbus_bus_get(type, &err);
+ if (dbus_error_is_set(&err))
+ {
+ dbus_error_free(&err);
+ throw FatalException();
+ }
+
+ if (!conn)
+ {
+ throw FatalException();
+ }
+ }
+
+
+ Connection::~Connection()
+ {
+ dbus_connection_unref(conn);
+ }
+
+
+ void
+ Connection::request_name(const char* name, unsigned int flags)
+ {
+ DBusError err;
+ dbus_error_init(&err);
+
+ int ret = dbus_bus_request_name(conn, name, flags, &err);
+ if (dbus_error_is_set(&err))
+ {
+ dbus_error_free(&err);
+ throw FatalException();
+ }
+
+ if (ret != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
+ {
+ throw FatalException();
+ }
+ }
+
+
+ void
+ Connection::read_write(int timeout)
+ {
+ dbus_connection_read_write(conn, timeout);
+ }
+
+
+ void
+ Connection::send(Message& m)
+ {
+ if (!dbus_connection_send(conn, m.get_message(), NULL))
+ {
+ throw FatalException();
+ }
+ }
+
+
+ Message
+ Connection::send_and_reply_and_block(Message& m)
+ {
+ DBusError err;
+ dbus_error_init(&err);
+
+ DBusMessage* tmp = dbus_connection_send_with_reply_and_block(conn, m.get_message(),
+ 0x7fffffff, &err);
+ if (dbus_error_is_set(&err))
+ {
+ dbus_error_free(&err);
+ throw FatalException();
+ }
+
+ Message reply(tmp);
+ return reply;
+ }
+
+
+ void
+ Connection::add_match(const char* rule)
+ {
+ DBusError err;
+ dbus_error_init(&err);
+
+ dbus_bus_add_match(conn, rule, &err);
+
+ if (dbus_error_is_set(&err))
+ {
+ throw FatalException();
+ }
+ }
+
+
+ unsigned long
+ Connection::get_unix_userid(const Message& m)
+ {
+ string sender = m.get_sender();
+ if (sender.empty())
+ {
+ throw FatalException();
+ }
+
+ DBusError err;
+ dbus_error_init(&err);
+
+ unsigned long uid = dbus_bus_get_unix_user(conn, sender.c_str(), &err);
+ if (dbus_error_is_set(&err))
+ {
+ dbus_error_free(&err);
+ throw FatalException();
+ }
+
+ return uid;
+ }
+
+
+ string
+ Connection::get_unix_username(const Message& m)
+ {
+ unsigned long userid = get_unix_userid(m);
+
+ long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
+ char* buf = (char*) malloc(bufsize);
+ if (!buf)
+ throw FatalException();
+
+ struct passwd pwd;
+ struct passwd *result;
+ getpwuid_r(userid, &pwd, buf, bufsize, &result);
+ if (!result)
+ throw FatalException();
+
+ string username(pwd.pw_name);
+
+ free(buf);
+
+ return username;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#ifndef SNAPPER_DBUSCONN_H
+#define SNAPPER_DBUSCONN_H
+
+
+#include <dbus/dbus.h>
+
+#include <boost/noncopyable.hpp>
+
+#include "DBusMessage.h"
+
+
+namespace DBus
+{
+
+ class Connection : boost::noncopyable
+ {
+ public:
+
+ Connection(DBusBusType type);
+ ~Connection();
+
+ DBusConnection* get_connection() { return conn; }
+
+ void request_name(const char* name, unsigned int flags);
+
+ void read_write(int timeout);
+
+ void send(Message& m);
+
+ Message send_and_reply_and_block(Message& m);
+
+ void add_match(const char* rule);
+
+ unsigned long get_unix_userid(const Message& m);
+ string get_unix_username(const Message& m);
+
+ private:
+
+ DBusConnection* conn;
+
+ };
+
+}
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include <assert.h>
+#include <stdlib.h>
+
+#include "DBusMessage.h"
+
+
+namespace DBus
+{
+
+ Message::Message(DBusMessage* m)
+ : msg(m)
+ {
+ assert(msg);
+ }
+
+
+ Message::Message(const Message& m)
+ : msg(m.msg)
+ {
+ dbus_message_ref(msg);
+ }
+
+
+ Message::~Message()
+ {
+ dbus_message_unref(msg);
+ }
+
+
+ Message&
+ Message::operator=(const Message& m)
+ {
+ if (this != &m)
+ {
+ dbus_message_unref(msg);
+ msg = m.msg;
+ dbus_message_ref(msg);
+ }
+ return *this;
+ }
+
+
+ const char* TypeInfo<string>::signature = "s";
+
+
+ Hihi::Hihi(Message& msg)
+ {
+ DBusMessageIter args;
+ if (!dbus_message_iter_init(msg.get_message(), &args))
+ {
+ throw FatalException();
+ }
+ iters.push_back(args);
+ }
+
+
+ Hihi::~Hihi()
+ {
+ iters.pop_back();
+ assert(iters.empty());
+ }
+
+
+ void
+ Hihi::open_recurse()
+ {
+ DBusMessageIter iter2;
+ dbus_message_iter_recurse(top(), &iter2);
+ iters.push_back(iter2);
+ }
+
+
+ void
+ Hihi::close_recurse()
+ {
+ iters.pop_back();
+ dbus_message_iter_next(top());
+ }
+
+
+ Hoho::Hoho(Message& msg)
+ {
+ DBusMessageIter args;
+ dbus_message_iter_init_append(msg.get_message(), &args);
+ iters.push_back(args);
+ }
+
+
+ Hoho::~Hoho()
+ {
+ iters.pop_back();
+ assert(iters.empty());
+ }
+
+ void
+ Hoho::open_struct()
+ {
+ DBusMessageIter iter2;
+ dbus_message_iter_open_container(top(), DBUS_TYPE_STRUCT, NULL, &iter2);
+ iters.push_back(iter2);
+ }
+
+
+ void
+ Hoho::close_struct()
+ {
+ DBusMessageIter iter2 = *top();
+ iters.pop_back();
+ dbus_message_iter_close_container(top(), &iter2);
+ }
+
+
+ void
+ Hoho::open_array(const char* signature)
+ {
+ DBusMessageIter iter2;
+ dbus_message_iter_open_container(top(), DBUS_TYPE_ARRAY, signature, &iter2);
+ iters.push_back(iter2);
+ }
+
+
+ void
+ Hoho::close_array()
+ {
+ DBusMessageIter iter2 = *top();
+ iters.pop_back();
+ dbus_message_iter_close_container(top(), &iter2);
+ }
+
+
+ void
+ Hoho::open_dict_entry()
+ {
+ DBusMessageIter iter2;
+ dbus_message_iter_open_container(top(), DBUS_TYPE_DICT_ENTRY, 0, &iter2);
+ iters.push_back(iter2);
+ }
+
+
+ void
+ Hoho::close_dict_entry()
+ {
+ DBusMessageIter iter2 = *top();
+ iters.pop_back();
+ dbus_message_iter_close_container(top(), &iter2);
+ }
+
+
+ Hihi&
+ operator>>(Hihi& hihi, bool& data)
+ {
+ if (hihi.get_type() != DBUS_TYPE_BOOLEAN)
+ throw MarshallingException();
+
+ dbus_bool_t tmp;
+ dbus_message_iter_get_basic(hihi.top(), &tmp);
+ dbus_message_iter_next(hihi.top());
+ data = tmp;
+
+ return hihi;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, bool data)
+ {
+ dbus_bool_t tmp = data;
+ dbus_message_iter_append_basic(hoho.top(), DBUS_TYPE_BOOLEAN, &tmp);
+
+ return hoho;
+ }
+
+
+ Hihi&
+ operator>>(Hihi& hihi, dbus_uint16_t& data)
+ {
+ if (hihi.get_type() != DBUS_TYPE_UINT16)
+ throw MarshallingException();
+
+ dbus_message_iter_get_basic(hihi.top(), &data);
+ dbus_message_iter_next(hihi.top());
+
+ return hihi;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, dbus_uint16_t data)
+ {
+ dbus_message_iter_append_basic(hoho.top(), DBUS_TYPE_UINT16, &data);
+
+ return hoho;
+ }
+
+
+ Hihi&
+ operator>>(Hihi& hihi, dbus_uint32_t& data)
+ {
+ if (hihi.get_type() != DBUS_TYPE_UINT32)
+ throw MarshallingException();
+
+ dbus_message_iter_get_basic(hihi.top(), &data);
+ dbus_message_iter_next(hihi.top());
+
+ return hihi;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, dbus_uint32_t data)
+ {
+ dbus_message_iter_append_basic(hoho.top(), DBUS_TYPE_UINT32, &data);
+
+ return hoho;
+ }
+
+
+ Hihi&
+ operator>>(Hihi& hihi, time_t& data)
+ {
+ if (hihi.get_type() != DBUS_TYPE_UINT32)
+ throw MarshallingException();
+
+ dbus_message_iter_get_basic(hihi.top(), &data);
+ dbus_message_iter_next(hihi.top());
+
+ return hihi;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, time_t data)
+ {
+ dbus_message_iter_append_basic(hoho.top(), DBUS_TYPE_UINT32, &data);
+
+ return hoho;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, const char* data)
+ {
+ dbus_message_iter_append_basic(hoho.top(), DBUS_TYPE_STRING, &data);
+
+ return hoho;
+ }
+
+ Hihi&
+ operator>>(Hihi& hihi, string& data)
+ {
+ if (hihi.get_type() != DBUS_TYPE_STRING)
+ throw MarshallingException();
+
+ const char* p = NULL;
+ dbus_message_iter_get_basic(hihi.top(), &p);
+ dbus_message_iter_next(hihi.top());
+ data = p;
+
+ return hihi;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, const string& data)
+ {
+ const char* p = data.c_str();
+ dbus_message_iter_append_basic(hoho.top(), DBUS_TYPE_STRING, &p);
+
+ return hoho;
+ }
+
+
+ Hihi&
+ operator>>(Hihi& hihi, map<string, string>& data)
+ {
+ if (hihi.get_type() != DBUS_TYPE_ARRAY)
+ throw MarshallingException();
+
+ hihi.open_recurse();
+
+ while (hihi.get_type() != DBUS_TYPE_INVALID)
+ {
+ if (hihi.get_signature() != "{ss}")
+ throw MarshallingException();
+
+ hihi.open_recurse();
+
+ string k, v;
+ hihi >> k >> v;
+ data[k] = v;
+
+ hihi.close_recurse();
+ }
+
+ hihi.close_recurse();
+
+ return hihi;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, const map<string, string>& data)
+ {
+ hoho.open_array("{ss}");
+
+ for (map<string, string>::const_iterator it = data.begin(); it != data.end() ; ++it)
+ {
+ hoho.open_dict_entry();
+
+ hoho << it->first << it->second;
+
+ hoho.close_dict_entry();
+ }
+
+ hoho.close_array();
+
+ return hoho;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#ifndef SNAPPER_DBUSMSG_H
+#define SNAPPER_DBUSMSG_H
+
+
+#include <dbus/dbus.h>
+
+#include <string>
+#include <vector>
+#include <list>
+#include <map>
+
+
+namespace DBus
+{
+ using std::string;
+ using std::vector;
+ using std::list;
+ using std::map;
+
+
+ struct Exception : public std::exception
+ {
+ explicit Exception() throw() {}
+ virtual const char* what() const throw() { return "dbus generic exception"; }
+ };
+
+
+ struct MarshallingException : public Exception
+ {
+ explicit MarshallingException() throw() {}
+ virtual const char* what() const throw() { return "dbus marshalling exception"; }
+ };
+
+
+ struct FatalException : public Exception
+ {
+ explicit FatalException() throw() {}
+ virtual const char* what() const throw() { return "dbus fatal exception"; }
+ };
+
+
+ class Message
+ {
+ public:
+
+ Message(DBusMessage* m);
+ Message(const Message& m);
+ ~Message();
+
+ Message& operator=(const Message& m);
+
+ DBusMessage* get_message() { return msg; }
+
+ int get_type() const { return dbus_message_get_type(msg); }
+ string get_member() const { return dbus_message_get_member(msg); }
+ string get_sender() const { return dbus_message_get_sender(msg); }
+ string get_path() const { return dbus_message_get_path(msg); }
+ string get_interface() const { return dbus_message_get_interface(msg); }
+ string get_error_name() const { return dbus_message_get_error_name(msg); }
+
+ bool is_method_call(const char* interface, const char* method) const
+ {
+ return dbus_message_is_method_call(msg, interface, method);
+ }
+
+ bool is_signal(const char* interface, const char* name) const
+ {
+ return dbus_message_is_signal(msg, interface, name);
+ }
+
+ private:
+
+ DBusMessage* msg;
+
+ };
+
+
+ class MessageMethodCall : public Message
+ {
+ public:
+
+ MessageMethodCall(const char* service, const char* object, const char* interface,
+ const char* method)
+ : Message(dbus_message_new_method_call(service, object, interface, method))
+ {
+ }
+
+ };
+
+
+ class MessageMethodReturn : public Message
+ {
+ public:
+
+ MessageMethodReturn(Message& m)
+ : Message(dbus_message_new_method_return(m.get_message()))
+ {
+ if (m.get_type() != DBUS_MESSAGE_TYPE_METHOD_CALL)
+ throw FatalException();
+ }
+
+ };
+
+
+ class MessageError : public Message
+ {
+ public:
+
+ MessageError(Message& m, const char* error_msg, const char* error_code)
+ : Message(dbus_message_new_error(m.get_message(), error_msg, error_code))
+ {
+ if (m.get_type() != DBUS_MESSAGE_TYPE_METHOD_CALL)
+ throw FatalException();
+ }
+
+ };
+
+
+ class MessageSignal : public Message
+ {
+ public:
+
+ MessageSignal(const char* path, const char* interface, const char* name)
+ : Message(dbus_message_new_signal(path, interface, name))
+ {
+ }
+
+ };
+
+
+ template <typename Type> struct TypeInfo {};
+
+ template <> struct TypeInfo<string> { static const char* signature; };
+
+
+ class Marshalling
+ {
+
+ public:
+
+ DBusMessageIter* top() { return &iters.back(); }
+
+ int get_type() { return dbus_message_iter_get_arg_type(top()); }
+ string get_signature() { return dbus_message_iter_get_signature(top()); }
+
+ protected:
+
+ list<DBusMessageIter> iters;
+
+ };
+
+
+ class Hihi : public Marshalling
+ {
+
+ public:
+
+ Hihi(Message& msg);
+ ~Hihi();
+
+ void open_recurse();
+ void close_recurse();
+
+ };
+
+
+ class Hoho : public Marshalling
+ {
+
+ public:
+
+ Hoho(Message& msg);
+ ~Hoho();
+
+ void open_struct();
+ void close_struct();
+
+ void open_array(const char* signature);
+ void close_array();
+
+ void open_dict_entry();
+ void close_dict_entry();
+
+ };
+
+
+ Hihi& operator>>(Hihi& hihi, bool& data);
+ Hoho& operator<<(Hoho& hoho, bool data);
+
+ Hihi& operator>>(Hihi& hihi, dbus_uint16_t& data);
+ Hoho& operator<<(Hoho& hoho, dbus_uint16_t data);
+
+ Hihi& operator>>(Hihi& hihi, dbus_uint32_t& data);
+ Hoho& operator<<(Hoho& hoho, dbus_uint32_t data);
+
+ Hihi& operator>>(Hihi& hihi, time_t& data);
+ Hoho& operator<<(Hoho& hoho, time_t data);
+
+ Hoho& operator<<(Hoho& hoho, const char* data);
+
+ Hihi& operator>>(Hihi& hihi, string& data);
+ Hoho& operator<<(Hoho& hoho, const string& data);
+
+ Hihi& operator>>(Hihi& hihi, map<string, string>& data);
+ Hoho& operator<<(Hoho& hoho, const map<string, string>& data);
+
+
+ template <typename Type>
+ Hihi& operator>>(Hihi& hihi, vector<Type>& data)
+ {
+ if (hihi.get_type() != DBUS_TYPE_ARRAY)
+ throw MarshallingException();
+
+ hihi.open_recurse();
+
+ while (hihi.get_type() != DBUS_TYPE_INVALID)
+ {
+ if (hihi.get_signature() != TypeInfo<Type>::signature)
+ throw MarshallingException();
+
+ Type tmp;
+ hihi >> tmp;
+ data.push_back(tmp);
+ }
+
+ hihi.close_recurse();
+
+ return hihi;
+ }
+
+
+ template <typename Type>
+ Hoho& operator<<(Hoho& hoho, const vector<Type>& data)
+ {
+ hoho.open_array(TypeInfo<Type>::signature);
+
+ for (typename vector<Type>::const_iterator it = data.begin(); it != data.end(); ++it)
+ {
+ hoho << *it;
+ }
+
+ hoho.close_array();
+
+ return hoho;
+ }
+
+
+ template <typename Type>
+ Hihi& operator>>(Hihi& hihi, list<Type>& data)
+ {
+ if (hihi.get_type() != DBUS_TYPE_ARRAY)
+ throw MarshallingException();
+
+ hihi.open_recurse();
+
+ while (hihi.get_type() != DBUS_TYPE_INVALID)
+ {
+ if (hihi.get_signature() != TypeInfo<Type>::signature)
+ throw MarshallingException();
+
+ Type tmp;
+ hihi >> tmp;
+ data.push_back(tmp);
+ }
+
+ hihi.close_recurse();
+
+ return hihi;
+ }
+
+
+ template <typename Type>
+ Hoho& operator<<(Hoho& hoho, const list<Type>& data)
+ {
+ hoho.open_array(TypeInfo<Type>::signature);
+
+ for (typename list<Type>::const_iterator it = data.begin(); it != data.end(); ++it)
+ {
+ hoho << *it;
+ }
+
+ hoho.close_array();
+
+ return hoho;
+ }
+
+}
+
+
+#endif
--- /dev/null
+#
+# Makefile.am for snapper/tools/utils/dbus
+#
+
+INCLUDES = -I$(top_srcdir) -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include
+
+noinst_LTLIBRARIES = libdbus.la
+
+libdbus_la_SOURCES = \
+ DBusConnection.cc DBusConnection.h \
+ DBusMessage.cc DBusMessage.h
+
--- /dev/null
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+snapper = dbus.Interface(bus.get_object('org.opensuse.snapper', '/org/opensuse/snapper'),
+ dbus_interface='org.opensuse.snapper')
+
+
+num_files = snapper.CreateComparison("root", 1306, 1307)
+
+print num_files
+
+
+files = snapper.GetFiles("root", 1306, 1307)
+
+for file in files:
+ print file[0], file[1], file[2]
+
+
+undo = [ [ "/hello", False ], [ "/world", True ] ]
+
+snapper.SetUndo("root", 1306, 1307, undo)
+
+
+files = snapper.GetFiles("root", 1306, 1307)
+
+for file in files:
+ print file[0], file[1], file[2]
+
--- /dev/null
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+snapper = dbus.Interface(bus.get_object('org.opensuse.snapper', '/org/opensuse/snapper'),
+ dbus_interface='org.opensuse.snapper')
+
+
+print snapper.CreateSnapshot("root", 0, 0, "test", "", { "tid" : "456" })
+
--- /dev/null
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+snapper = dbus.Interface(bus.get_object('org.opensuse.snapper', '/org/opensuse/snapper'),
+ dbus_interface='org.opensuse.snapper')
+
+
+lines = snapper.Debug()
+
+for line in lines:
+ print line
+
--- /dev/null
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+snapper = dbus.Interface(bus.get_object('org.opensuse.snapper', '/org/opensuse/snapper'),
+ dbus_interface='org.opensuse.snapper')
+
+
+snapper.DeleteSnapshot("root", 1450)
+
--- /dev/null
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+snapper = dbus.Interface(bus.get_object('org.opensuse.snapper', '/org/opensuse/snapper'),
+ dbus_interface='org.opensuse.snapper')
+
+
+configs = snapper.ListConfigs()
+
+for config in configs:
+ print config[0], config[1]
+
--- /dev/null
+#!/usr/bin/python
+
+import dbus
+
+bus = dbus.SystemBus()
+
+snapper = dbus.Interface(bus.get_object('org.opensuse.snapper', '/org/opensuse/snapper'),
+ dbus_interface='org.opensuse.snapper')
+
+
+snapshots = snapper.ListSnapshots("root")
+
+for snapshot in snapshots:
+ print snapshot[0], snapshot[1], snapshot[2], snapshot[3],
+ for k, v in snapshot[4].items():
+ print "%s=%s" % (k, v),
+ print
+
--- /dev/null
+#!/usr/bin/python
+
+from time import sleep
+import dbus
+
+bus = dbus.SystemBus()
+
+snapper = dbus.Interface(bus.get_object('org.opensuse.snapper', '/org/opensuse/snapper'),
+ dbus_interface='org.opensuse.snapper')
+
+
+snapper.LockConfig("root")
+
+sleep(10)
+
+snapper.UnlockConfig("root")
+
--- /dev/null
+
+// g++ client-qt.cc -o client-qt -Wall -O2 -lQtCore -lQtDBus
+
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtDBus/QtDBus>
+
+
+enum SnapshotType { SINGLE, PRE, POST };
+
+struct Snapshot
+{
+ unsigned int num;
+ SnapshotType type;
+ unsigned int date;
+ QString description;
+ QMap<QString, QString> userdata;
+};
+
+
+Q_DECLARE_METATYPE(Snapshot)
+
+
+QDBusArgument& operator<<(QDBusArgument& argument, const Snapshot& mystruct)
+{
+ argument.beginStructure();
+ argument << mystruct.num << static_cast<unsigned short>(mystruct.type) << mystruct.date
+ << mystruct.description << mystruct.userdata;
+ argument.endStructure();
+ return argument;
+}
+
+
+const QDBusArgument& operator>>(const QDBusArgument& argument, Snapshot& mystruct)
+{
+ unsigned short tmp1;
+
+ argument.beginStructure();
+ argument >> mystruct.num >> tmp1 >> mystruct.date >> mystruct.description >> mystruct.userdata;
+ argument.endStructure();
+
+ mystruct.type = static_cast<SnapshotType>(tmp1);
+
+ return argument;
+}
+
+
+void
+command_list_snapshots()
+{
+ QDBusInterface dbus_iface("org.opensuse.snapper", "/org/opensuse/snapper",
+ "org.opensuse.snapper", QDBusConnection::systemBus());
+
+ QDBusMessage reply = dbus_iface.call("ListSnapshots", "root");
+ // qDebug() << reply;
+ // qDebug() << (reply.type() == QDBusMessage::ReplyMessage);
+
+ QList<QVariant> args = reply.arguments();
+ // qDebug() << args.size();
+
+ QDBusArgument arg = args.at(0).value<QDBusArgument>();
+ // qDebug() << arg.currentSignature();
+
+ QList<Snapshot> snapshots;
+ arg >> snapshots;
+
+ QListIterator<Snapshot> it(snapshots);
+ while (it.hasNext())
+ {
+ const Snapshot& snapshot = it.next();
+ printf("%d %d %d %s", snapshot.num, snapshot.type, snapshot.date,
+ qPrintable(snapshot.description));
+ QMapIterator<QString, QString> it2(snapshot.userdata);
+ while (it2.hasNext())
+ {
+ it2.next();
+ printf(" %s=%s", qPrintable(it2.key()), qPrintable(it2.value()));
+ }
+ printf("\n");
+ }
+}
+
+
+int
+main(int argc, char** argv)
+{
+ QCoreApplication app(argc, argv);
+
+ qDBusRegisterMetaType<Snapshot>();
+
+ if (!QDBusConnection::systemBus().isConnected())
+ {
+ fprintf(stderr, "Cannot connect to the D-Bus system bus.\n");
+ return 1;
+ }
+
+ command_list_snapshots();
+
+ return 0;
+}
--- /dev/null
+*.o
+snapperd
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include <algorithm>
+
+
+
+#include "Client.h"
+#include "MetaSnapper.h"
+
+
+template <typename ListType, typename Type>
+bool contains(const ListType& l, const Type& value)
+{
+ return find(l.begin(), l.end(), value) != l.end();
+}
+
+
+Client::Client(const string& name)
+ : name(name)
+{
+}
+
+
+Client::~Client()
+{
+}
+
+
+Comparison*
+Client::find_comparison(Snapper* snapper, Snapshots::const_iterator snapshot1,
+ Snapshots::const_iterator snapshot2)
+{
+ for (list<Comparison*>::iterator it = comparisons.begin(); it != comparisons.end(); ++it)
+ {
+ if ((*it)->getSnapper() == snapper && (*it)->getSnapshot1() == snapshot1 &&
+ (*it)->getSnapshot2() == snapshot2)
+ return *it;
+ }
+
+ return NULL;
+}
+
+
+Comparison*
+Client::find_comparison(const string& config_name, unsigned int number1, unsigned int number2)
+{
+ Snapper* snapper = getSnapper(config_name);
+ Snapshots& snapshots = snapper->getSnapshots();
+ Snapshots::const_iterator snapshot1 = snapshots.find(number1);
+ Snapshots::const_iterator snapshot2 = snapshots.find(number2);
+
+ return find_comparison(snapper, snapshot1, snapshot2);
+}
+
+
+void
+Client::add_lock(const string& config_name)
+{
+ locks.insert(config_name);
+}
+
+
+void
+Client::remove_lock(const string& config_name)
+{
+ locks.erase(config_name);
+}
+
+
+bool
+Client::has_lock(const string& config_name) const
+{
+ return contains(locks, config_name);
+}
+
+
+Clients::iterator
+Clients::find(const string& name)
+{
+ for (iterator it = entries.begin(); it != entries.end(); ++it)
+ if (it->name == name)
+ return it;
+
+ return entries.end();
+}
+
+
+void
+Clients::add(const string& name)
+{
+ entries.push_back(Client(name));
+}
+
+
+void
+Clients::remove(const string& name)
+{
+ iterator it = find(name);
+ if (it != entries.end())
+ entries.erase(it);
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#ifndef SNAPPER_CLIENT_H
+#define SNAPPER_CLIENT_H
+
+
+#include <string>
+#include <list>
+#include <set>
+
+#include <snapper/Snapper.h>
+#include <snapper/Snapshot.h>
+#include <snapper/Factory.h>
+#include <snapper/Comparison.h>
+
+
+using namespace std;
+using namespace snapper;
+
+
+class Client
+{
+public:
+
+ Client(const string& name);
+ ~Client();
+
+ Comparison* find_comparison(const string& config_name, unsigned int number1,
+ unsigned int number2);
+
+ Comparison* find_comparison(Snapper* snapper, Snapshots::const_iterator snapshot1,
+ Snapshots::const_iterator snapshot2);
+
+ void add_lock(const string& config_name);
+ void remove_lock(const string& config_name);
+ bool has_lock(const string& config_name) const;
+
+ string name;
+
+ list<Comparison*> comparisons;
+
+ set<string> locks;
+
+};
+
+
+class Clients
+{
+public:
+
+ typedef list<Client>::iterator iterator;
+ typedef list<Client>::const_iterator const_iterator;
+
+ iterator begin() { return entries.begin(); }
+ const_iterator begin() const { return entries.begin(); }
+
+ iterator end() { return entries.end(); }
+ const_iterator end() const { return entries.end(); }
+
+ bool empty() const { return entries.empty(); }
+
+ iterator find(const string& name);
+
+ void add(const string& name);
+ void remove(const string& name);
+
+private:
+
+ list<Client> entries;
+
+};
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include "Job.h"
+
+
+void
+Jobs::add(Job* job)
+{
+ job->start();
+ entries.push_back(job);
+}
+
+
+void
+Jobs::handle()
+{
+ list<Job*>::iterator it = entries.begin();
+ while (it != entries.end())
+ {
+ if (!(*it)->is_running())
+ {
+ (*it)->done();
+ delete *it;
+ it = entries.erase(it);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#ifndef SNAPPER_JOB_H
+#define SNAPPER_JOB_H
+
+
+#include <list>
+#include <boost/thread.hpp>
+
+
+using namespace std;
+using namespace boost::posix_time;
+
+
+struct Job
+{
+public:
+
+ virtual ~Job() {}
+
+ void start() { t = boost::thread(boost::ref(*this)); }
+
+ bool is_running() { return !t.timed_join(seconds(0)); }
+
+ virtual void done() = 0;
+
+ virtual void operator()() = 0;
+
+protected:
+
+ boost::thread t;
+
+};
+
+
+class Jobs
+{
+public:
+
+ void add(Job* job);
+
+ void handle();
+
+ size_t size() const { return entries.size(); }
+ bool empty() const { return entries.empty(); }
+
+private:
+
+ list<Job*> entries;
+
+};
+
+
+#endif
--- /dev/null
+#
+# Makefile.am for snapper/tools/server
+#
+
+INCLUDES = -I$(top_srcdir) -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include
+
+
+sbin_PROGRAMS = snapperd
+
+snapperd_SOURCES = snapperd.cc Client.cc Job.cc MetaSnapper.cc Types.cc
+snapperd_LDADD = ../snapper/libsnapper.la ../dbus/libdbus.la -ldbus-1 -lboost_thread
+
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include "MetaSnapper.h"
+
+
+Snapper*
+getSnapper(const string& config_name)
+{
+ for (list<Snapper*>::iterator it = snappers.begin(); it != snappers.end(); ++it)
+ if ((*it)->configName() == config_name)
+ return *it;
+
+ Snapper* tmp = new Snapper(config_name);
+ snappers.push_back(tmp);
+ return tmp;
+}
+
+
+list<Snapper*> snappers;
+
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#ifndef SNAPPER_META_SNAPPER_H
+#define SNAPPER_META_SNAPPER_H
+
+
+#include <snapper/Snapper.h>
+#include <snapper/Snapshot.h>
+#include <snapper/Comparison.h>
+
+
+using namespace std;
+using namespace snapper;
+
+
+extern list<Snapper*> snappers;
+
+
+Snapper*
+getSnapper(const string& config_name);
+
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include "Types.h"
+
+
+namespace DBus
+{
+ const char* TypeInfo<ConfigInfo>::signature = "(ss)";
+ const char* TypeInfo<Snapshot>::signature = "(uqusa{ss})";
+ const char* TypeInfo<File>::signature = "(ssb)";
+ const char* TypeInfo<Undo>::signature = "(sb)";
+
+
+ Hoho&
+ operator<<(Hoho& hoho, const ConfigInfo& data)
+ {
+ hoho.open_struct();
+ hoho << data.config_name << data.subvolume;
+ hoho.close_struct();
+ return hoho;
+ }
+
+
+ Hihi&
+ operator>>(Hihi& hihi, SnapshotType& data)
+ {
+ dbus_uint16_t tmp;
+ hihi >> tmp;
+ data = static_cast<SnapshotType>(tmp);
+ return hihi;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, SnapshotType data)
+ {
+ hoho << static_cast<dbus_uint16_t>(data);
+ return hoho;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, const Snapshot& data)
+ {
+ hoho.open_struct();
+ hoho << data.getNum() << data.getType() << data.getDate()
+ << data.getDescription() << data.getUserdata();
+ hoho.close_struct();
+ return hoho;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, const Snapshots& data)
+ {
+ hoho.open_array(TypeInfo<Snapshot>::signature);
+ for (Snapshots::const_iterator it = data.begin(); it != data.end(); ++it)
+ hoho << *it;
+ hoho.close_array();
+ return hoho;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, const File& data)
+ {
+ hoho.open_struct();
+ hoho << data.getName() << statusToString(data.getPreToPostStatus()) << data.getUndo();
+ hoho.close_struct();
+ return hoho;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, const Files& data)
+ {
+ hoho.open_array(TypeInfo<File>::signature);
+ for (Files::const_iterator it = data.begin(); it != data.end(); ++it)
+ hoho << *it;
+ hoho.close_array();
+ return hoho;
+ }
+
+
+ Hihi&
+ operator>>(Hihi& hihi, Undo& data)
+ {
+ hihi.open_recurse();
+ hihi >> data.filename >> data.undo;
+ hihi.close_recurse();
+ return hihi;
+ }
+
+
+ Hoho&
+ operator<<(Hoho& hoho, const Undo& data)
+ {
+ hoho.open_struct();
+ hoho << data.filename << data.undo;
+ hoho.close_struct();
+ return hoho;
+ }
+
+}
+
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include <string>
+#include <list>
+
+#include <snapper/Snapper.h>
+#include <snapper/Snapshot.h>
+#include <snapper/File.h>
+
+using std::string;
+using std::list;
+
+using namespace snapper;
+
+#include "dbus/DBusMessage.h"
+
+
+struct Undo
+{
+ Undo() {}
+ Undo(const string& filename, bool undo) : filename(filename), undo(undo) {}
+
+ string filename;
+ bool undo;
+};
+
+
+namespace DBus
+{
+ template <> struct TypeInfo<ConfigInfo> { static const char* signature; };
+ template <> struct TypeInfo<Snapshot> { static const char* signature; };
+ template <> struct TypeInfo<File> { static const char* signature; };
+ template <> struct TypeInfo<Undo> { static const char* signature; };
+
+ Hoho& operator<<(Hoho& hoho, const ConfigInfo& data);
+
+ Hihi& operator>>(Hihi& hihi, SnapshotType& data);
+ Hoho& operator<<(Hoho& hoho, SnapshotType data);
+
+ Hoho& operator<<(Hoho& hoho, const Snapshot& data);
+
+ Hoho& operator<<(Hoho& hoho, const Snapshots& data);
+
+ Hoho& operator<<(Hoho& hoho, const File& data);
+
+ Hoho& operator<<(Hoho& hoho, const Files& data);
+
+ Hihi& operator>>(Hihi& hihi, Undo& data);
+ Hoho& operator<<(Hoho& hoho, const Undo& data);
+
+};
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#include <unistd.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dbus/dbus.h>
+
+#include <string>
+#include <iostream>
+#include <boost/algorithm/string.hpp>
+
+#include <snapper/Snapper.h>
+#include <snapper/Snapshot.h>
+#include <snapper/Comparison.h>
+#include <snapper/Log.h>
+
+#include "dbus/DBusConnection.h"
+#include "dbus/DBusMessage.h"
+
+#include "MetaSnapper.h"
+#include "Client.h"
+#include "Job.h"
+#include "Types.h"
+
+
+using namespace std;
+using namespace snapper;
+
+
+Clients clients;
+
+Jobs jobs;
+
+
+void
+reply_to_introspect(DBus::Connection& conn, DBus::Message& msg)
+{
+ const char* introspect =
+ DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE "\n"
+ "<node name='/org/opensuse/snapper'>\n"
+ " <interface name='" DBUS_INTERFACE_INTROSPECTABLE "'>\n"
+ " <method name='Introspect'>\n"
+ " <arg name='xml_data' type='s' direction='out'/>\n"
+ " </method>\n"
+ " </interface>\n"
+ " <interface name='org.opensuse.snapper'>\n"
+
+ " <method name='ListConfigs'>\n"
+ " <arg name='configs' type='v' direction='out'/>\n"
+ " </method>\n"
+
+ " <method name='CreateConfig'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " <arg name='subvolume' type='s' direction='in'/>\n"
+ " </method>\n"
+
+ " <method name='DeleteConfig'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " </method>\n"
+
+ " <method name='LockConfig'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " </method>\n"
+
+ " <method name='UnlockConfig'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " </method>\n"
+
+ " <method name='ListSnapshots'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " <arg name='snapshots' type='v' direction='out'/>\n"
+ " </method>\n"
+
+ " <method name='CreateSnapshot'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " <arg name='type' type='q' direction='in'/>\n"
+ " <arg name='pre-number' type='u' direction='in'/>\n"
+ " <arg name='description' type='s' direction='in'/>\n"
+ " <arg name='cleanup' type='s' direction='in'/>\n"
+ " <arg name='userdata' type='a{ss}' direction='in'/>\n"
+ " <arg name='number' type='u' direction='out'/>\n"
+ " </method>\n"
+
+ " <method name='DeleteSnapshot'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " <arg name='number' type='u' direction='in'/>\n"
+ " </method>\n"
+
+ " <method name='CreateComparison'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " <arg name='number1' type='u' direction='in'/>\n"
+ " <arg name='number2' type='u' direction='in'/>\n"
+ " <arg name='num-files' type='u' direction='out'/>\n"
+ " </method>\n"
+
+ " <method name='GetFiles'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " <arg name='number1' type='u' direction='in'/>\n"
+ " <arg name='number2' type='u' direction='in'/>\n"
+ " <arg name='files' type='v' direction='out'/>\n"
+ " </method>\n"
+
+ " <method name='SetUndo'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " <arg name='number1' type='u' direction='in'/>\n"
+ " <arg name='number2' type='u' direction='in'/>\n"
+ " <arg name='files' type='a(sb)' direction='in'/>\n"
+ " </method>\n"
+
+ " <method name='GetDiff'>\n"
+ " <arg name='config-name' type='s' direction='in'/>\n"
+ " <arg name='number1' type='u' direction='in'/>\n"
+ " <arg name='number2' type='u' direction='in'/>\n"
+ " <arg name='filename' type='s' direction='in'/>\n"
+ " <arg name='options' type='s' direction='in'/>\n"
+ " <arg name='diff' type='v' direction='out'/>\n"
+ " </method>\n"
+
+ " </interface>\n"
+ "</node>\n";
+
+ DBus::MessageMethodReturn reply(msg);
+
+ DBus::Hoho hoho(reply);
+ hoho << introspect;
+
+ conn.send(reply);
+}
+
+
+struct Permissions : public std::exception
+{
+ explicit Permissions() throw() {}
+ virtual const char* what() const throw() { return "permissions"; }
+};
+
+
+void
+check_permission(DBus::Connection& conn, DBus::Message& msg)
+{
+ unsigned long uid = conn.get_unix_userid(msg);
+ if (uid == 0)
+ return;
+
+ throw Permissions();
+}
+
+
+void
+check_permission(DBus::Connection& conn, DBus::Message& msg, const string& config_name)
+{
+ list<ConfigInfo> config_infos = Snapper::getConfigs();
+
+ for (list<ConfigInfo>::const_iterator it = config_infos.begin();
+ it != config_infos.end(); ++it)
+ {
+ if (it->config_name == config_name)
+ {
+ unsigned long uid = conn.get_unix_userid(msg);
+ if (uid == 0)
+ return;
+
+ string username = conn.get_unix_username(msg);
+ if (find(it->users.begin(), it->users.end(), username) != it->users.end())
+ return;
+
+ break;
+ }
+ }
+
+ throw Permissions();
+}
+
+
+struct Lock : public std::exception
+{
+ explicit Lock() throw() {}
+ virtual const char* what() const throw() { return "locked"; }
+};
+
+
+void
+check_lock(DBus::Connection& conn, DBus::Message& msg, const string& config_name)
+{
+ for (Clients::const_iterator it = clients.begin(); it != clients.end(); ++it)
+ {
+ if (it->name == msg.get_sender())
+ continue;
+
+ if (it->has_lock(config_name))
+ throw Lock();
+ }
+}
+
+
+void
+send_signal_config_created(DBus::Connection& conn, const string& config_name,
+ const string& subvolume)
+{
+ cout << "sending signal ConfigCreated" << endl;
+
+ DBus::MessageSignal msg("/org/opensuse/snapper", "org.opensuse.snapper", "ConfigCreated");
+
+ DBus::Hoho hoho(msg);
+ hoho << config_name;
+
+ conn.send(msg);
+}
+
+
+void
+send_signal_snapshot_created(DBus::Connection& conn, const string& config_name,
+ unsigned int number)
+{
+ cout << "sending signal SnapshotCreated" << endl;
+
+ DBus::MessageSignal msg("/org/opensuse/snapper", "org.opensuse.snapper", "SnapshotCreated");
+
+ DBus::Hoho hoho(msg);
+ hoho << config_name << number;
+
+ conn.send(msg);
+}
+
+
+void
+send_signal_snapshot_deleted(DBus::Connection& conn, const string& config_name,
+ unsigned int number)
+{
+ cout << "sending signal SnapshotDeleted" << endl;
+
+ DBus::MessageSignal msg("/org/opensuse/snapper", "org.opensuse.snapper", "SnapshotDeleted");
+
+ DBus::Hoho hoho(msg);
+ hoho << config_name << number;
+
+ conn.send(msg);
+}
+
+
+void
+reply_to_command_list_configs(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command ListConfigs" << endl;
+
+ list<ConfigInfo> config_infos = Snapper::getConfigs();
+
+ DBus::MessageMethodReturn reply(msg);
+
+ DBus::Hoho hoho(reply);
+ hoho << config_infos;
+
+ conn.send(reply);
+}
+
+
+void
+reply_to_command_create_config(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command CreateConfigs" << endl;
+
+ check_permission(conn, msg);
+
+ string config_name;
+ string subvolume;
+
+ DBus::Hihi hihi(msg);
+ hihi >> config_name >> subvolume;
+
+ DBus::MessageMethodReturn reply(msg);
+
+ conn.send(reply);
+
+ send_signal_config_created(conn, config_name, subvolume);
+}
+
+
+void
+reply_to_command_lock_config(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command LockConfig" << endl;
+
+ string config_name;
+
+ DBus::Hihi hihi(msg);
+ hihi >> config_name;
+
+ cout << "Method called with " << config_name << endl;
+
+ check_permission(conn, msg, config_name);
+
+ string sender = msg.get_sender();
+
+ Clients::iterator it = clients.find(sender);
+ it->add_lock(config_name);
+
+ DBus::MessageMethodReturn reply(msg);
+ conn.send(reply);
+}
+
+
+void
+reply_to_command_unlock_config(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command UnlockConfig" << endl;
+
+ string config_name;
+
+ DBus::Hihi hihi(msg);
+ hihi >> config_name;
+
+ cout << "Method called with " << config_name << endl;
+
+ check_permission(conn, msg, config_name);
+
+ string sender = msg.get_sender();
+
+ Clients::iterator it = clients.find(sender);
+ it->remove_lock(config_name);
+
+ DBus::MessageMethodReturn reply(msg);
+ conn.send(reply);
+}
+
+
+void
+reply_to_command_list_snapshots(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command ListSnapshots" << endl;
+
+ string config_name;
+
+ DBus::Hihi hihi(msg);
+ hihi >> config_name;
+
+ cout << "Method called with " << config_name << endl;
+
+ check_permission(conn, msg, config_name);
+
+ DBus::MessageMethodReturn reply(msg);
+
+ Snapper* snapper = getSnapper(config_name);
+
+ DBus::Hoho hoho(reply);
+ hoho << snapper->getSnapshots();
+
+ conn.send(reply);
+}
+
+
+void
+reply_to_command_create_snapshot(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command CreateSnapshots" << endl;
+
+ string config_name;
+ SnapshotType type;
+ unsigned int prenum;
+ string description;
+ string cleanup;
+ map<string, string> userdata;
+
+ DBus::Hihi hihi(msg);
+ hihi >> config_name >> type >> prenum >> description >> cleanup >> userdata;
+
+ cout << "Method called with " << config_name << endl;
+
+ check_permission(conn, msg, config_name);
+
+ DBus::MessageMethodReturn reply(msg);
+
+ Snapper* snapper = getSnapper(config_name);
+
+ // TODO type
+ Snapshots::iterator snap1 = snapper->createSingleSnapshot(description);
+ snap1->setCleanup(cleanup);
+ snap1->setUserdata(userdata);
+ snap1->flushInfo();
+
+ DBus::Hoho hoho(reply);
+ hoho << snap1->getNum();
+
+ conn.send(reply);
+
+ send_signal_snapshot_created(conn, config_name, snap1->getNum());
+}
+
+
+void
+reply_to_command_delete_snapshot(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command DeleteSnapshots" << endl;
+
+ string config_name;
+ unsigned int number;
+
+ DBus::Hihi hihi(msg);
+ hihi >> config_name >> number;
+
+ cout << "Method called with " << config_name << endl;
+
+ check_permission(conn, msg, config_name);
+ check_lock(conn, msg, config_name);
+
+ DBus::MessageMethodReturn reply(msg);
+
+ // TODO
+
+ DBus::Hoho hoho(reply);
+
+ conn.send(reply);
+
+ send_signal_snapshot_deleted(conn, config_name, number);
+}
+
+
+struct Comparing : public Job
+{
+ DBus::Connection* conn;
+ DBus::MessageMethodReturn* reply;
+
+ Comparison* comparison;
+
+ void done();
+
+protected:
+
+ virtual void operator()();
+
+};
+
+
+void
+reply_to_command_create_comparison(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command CreateComparison" << endl;
+
+ string config_name;
+ dbus_uint32_t number1, number2;
+
+ DBus::Hihi hihi(msg);
+ hihi >> config_name >> number1 >> number2;
+
+ cout << "Method called with " << config_name << " " << number1 << " " << number2 << endl;
+
+ check_permission(conn, msg, config_name);
+
+ Snapper* snapper = getSnapper(config_name);
+ Snapshots& snapshots = snapper->getSnapshots();
+ Snapshots::const_iterator snapshot1 = snapshots.find(number1);
+ Snapshots::const_iterator snapshot2 = snapshots.find(number2);
+
+ Comparison* comparison = new Comparison(snapper, snapshot1, snapshot2, true);
+ // TODO try, catch
+
+ Clients::iterator it = clients.find(msg.get_sender());
+ assert(it != clients.end());
+ it->comparisons.push_back(comparison);
+
+ Comparing* job = new Comparing;
+ job->conn = &conn;
+ job->reply = new DBus::MessageMethodReturn(msg);
+ job->comparison = comparison;
+
+ jobs.add(job);
+}
+
+
+void
+Comparing::operator()()
+{
+ boost::this_thread::sleep(seconds(2));
+
+ comparison->initialize();
+
+ boost::this_thread::sleep(seconds(2));
+}
+
+
+void
+Comparing::done()
+{
+ const Files& files = comparison->getFiles();
+ cout << comparison->getFiles().size() << endl;
+
+ for (Files::const_iterator it = files.begin(); it != files.end(); ++it)
+ cout << statusToString(it->getPreToPostStatus()) << " "
+ << it->getAbsolutePath(LOC_SYSTEM) << endl;
+
+ DBus::Hoho hoho(*reply);
+ hoho << (dbus_uint32_t) comparison->getFiles().size();
+ conn->send(*reply);
+
+ delete reply;
+}
+
+
+void
+reply_to_command_get_files(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command GetFiles" << endl;
+
+ string config_name;
+ dbus_uint32_t number1, number2;
+
+ DBus::Hihi hihi(msg);
+ hihi >> config_name >> number1 >> number2;
+
+ cout << "Method called with " << config_name << " " << number1 << " " << number2 << endl;
+
+ check_permission(conn, msg, config_name);
+
+ string sender = msg.get_sender();
+
+ DBus::MessageMethodReturn reply(msg);
+
+ Clients::iterator it = clients.find(sender);
+ assert(it != clients.end());
+
+ Comparison* comparison = it->find_comparison(config_name, number1, number2);
+
+ if (!comparison || !comparison->isInitialized())
+ {
+ // error
+ }
+
+ const Files& files = comparison->getFiles();
+
+ DBus::Hoho hoho(reply);
+ hoho << files;
+
+ conn.send(reply);
+}
+
+
+void
+reply_to_command_set_undo(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command SetUndo" << endl;
+
+ string config_name;
+ dbus_uint32_t number1, number2;
+ list<Undo> u;
+
+ DBus::Hihi hihi(msg);
+ hihi >> config_name >> number1 >> number2 >> u;
+
+ check_permission(conn, msg, config_name);
+
+ cout << "Method called with " << config_name << " " << number1 << " " << number2 << endl;
+
+ string sender = msg.get_sender();
+
+ DBus::MessageMethodReturn reply(msg);
+
+ Clients::iterator it = clients.find(sender);
+ assert(it != clients.end());
+
+ Comparison* comparison = it->find_comparison(config_name, number1, number2);
+
+ if (!comparison || !comparison->isInitialized())
+ {
+ // error
+ }
+
+ Files& files = comparison->getFiles();
+
+ for (list<Undo>::const_iterator it2 = u.begin(); it2 != u.end(); ++it2)
+ {
+ Files::iterator it3 = files.find(it2->filename);
+ if (it3 != files.end())
+ it3->setUndo(it2->undo);
+ }
+
+ conn.send(reply);
+}
+
+
+void
+reply_to_command_get_diff(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command GetDiff" << endl;
+
+ string config_name;
+ dbus_uint32_t number1, number2;
+ string filename;
+ string options;
+
+ DBus::Hihi hihi(msg);
+ hihi >> config_name >> number1 >> number2 >> filename >> options;
+
+ cout << "Method called with " << config_name << " " << number1 << " " << number2 << endl;
+
+ check_permission(conn, msg, config_name);
+
+ string sender = msg.get_sender();
+
+ Clients::iterator it = clients.find(sender);
+ assert(it != clients.end());
+
+ Comparison* comparison = it->find_comparison(config_name, number1, number2);
+
+ if (!comparison || !comparison->isInitialized())
+ {
+ // error
+ }
+
+ Files& files = comparison->getFiles();
+
+ Files::iterator it3 = files.find(filename);
+ assert(it3 != files.end());
+
+ vector<string> d = it3->getDiff(options);
+
+ DBus::MessageMethodReturn reply(msg);
+ DBus::Hoho hoho(reply);
+ hoho << d;
+
+ conn.send(reply);
+}
+
+
+void
+reply_to_command_debug(DBus::Connection& conn, DBus::Message& msg)
+{
+ cout << "command Debug" << endl;
+
+ // check_permission(conn, msg);
+
+ DBus::MessageMethodReturn reply(msg);
+
+ DBus::Hoho hoho(reply);
+
+ hoho.open_array("s");
+
+ hoho << "clients:";
+ for (list<Client>::iterator it = clients.begin(); it != clients.end(); ++it)
+ {
+ std::ostringstream s;
+ s << " name:'" << it->name << "'";
+ if (it->name == msg.get_sender())
+ s << " myself";
+ s << " username:'" << conn.get_unix_username(msg) << "'";
+ if (!it->locks.empty())
+ s << " locks:'" << boost::join(it->locks, ",") << "'";
+ if (!it->comparisons.empty())
+ s << " comparisons:" << it->comparisons.size();
+ hoho << s.str();
+ }
+
+ hoho << "snappers:";
+ for (list<Snapper*>::iterator it = snappers.begin(); it != snappers.end(); ++it)
+ {
+ std::ostringstream s;
+ s << " name:'" << (*it)->configName() << "'";
+ hoho << s.str();
+ }
+
+ if (!jobs.empty())
+ {
+ std::ostringstream s;
+ s << "running jobs:" << jobs.size();
+ hoho << s.str();
+ }
+
+ hoho.close_array();
+
+ conn.send(reply);
+}
+
+
+void
+client_connected(const string& name)
+{
+ clients.add(name);
+}
+
+
+void
+client_disconnected(const string& name)
+{
+ clients.remove(name);
+}
+
+
+void
+dispatch(DBus::Connection& conn, DBus::Message& msg)
+{
+ try
+ {
+ if (msg.is_method_call("org.opensuse.snapper", "Debug"))
+ reply_to_command_debug(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "ListConfigs"))
+ reply_to_command_list_configs(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "CreateConfig"))
+ reply_to_command_create_config(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "LockConfig"))
+ reply_to_command_lock_config(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "UnlockConfig"))
+ reply_to_command_unlock_config(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "ListSnapshots"))
+ reply_to_command_list_snapshots(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "CreateSnapshot"))
+ reply_to_command_create_snapshot(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "DeleteSnapshot"))
+ reply_to_command_delete_snapshot(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "CreateComparison"))
+ reply_to_command_create_comparison(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "GetFiles"))
+ reply_to_command_get_files(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "SetUndo"))
+ reply_to_command_set_undo(conn, msg);
+ else if (msg.is_method_call("org.opensuse.snapper", "GetDiff"))
+ reply_to_command_get_diff(conn, msg);
+ }
+ catch (DBus::MarshallingException)
+ {
+ DBus::MessageError reply(msg, "error.marshalling", DBUS_ERROR_FAILED);
+ conn.send(reply);
+ }
+ catch (Permissions)
+ {
+ DBus::MessageError reply(msg, "error.permissions", DBUS_ERROR_FAILED);
+ conn.send(reply);
+ }
+ catch (Lock)
+ {
+ DBus::MessageError reply(msg, "error.locked", DBUS_ERROR_FAILED);
+ conn.send(reply);
+ }
+}
+
+
+void
+listen(DBus::Connection& conn)
+{
+ y2mil("Listening for method calls and signals");
+
+ conn.request_name("org.opensuse.snapper", DBUS_NAME_FLAG_REPLACE_EXISTING);
+
+ conn.add_match("type='signal', interface='" DBUS_INTERFACE_DBUS "', member='NameOwnerChanged'");
+
+ int idle = 0;
+ while (++idle < 1000 || !clients.empty() || !jobs.empty())
+ {
+ conn.read_write(100); // TODO
+
+ jobs.handle();
+
+ DBusMessage* tmp = dbus_connection_pop_message(conn.get_connection());
+ if (!tmp)
+ continue;
+
+ DBus::Message msg(tmp);
+
+ switch (msg.get_type())
+ {
+ case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ {
+ y2mil("method call sender:'" << msg.get_sender() << "' path:'" <<
+ msg.get_path() << "' interface:'" << msg.get_interface() <<
+ "' member:'" << msg.get_member() << "'");
+
+ if (msg.is_method_call(DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
+ {
+ reply_to_introspect(conn, msg);
+ break;
+ }
+
+ if (clients.find(msg.get_sender()) == clients.end())
+ {
+ y2mil("client connected invisible '" << msg.get_sender() << "'");
+ client_connected(msg.get_sender());
+ }
+
+ dispatch(conn, msg);
+ }
+ break;
+
+ case DBUS_MESSAGE_TYPE_SIGNAL:
+ {
+ y2mil("signal sender:'" << msg.get_sender() << "' path:'" <<
+ msg.get_path() << "' interface:'" << msg.get_interface() <<
+ "' member:'" << msg.get_member() << "'");
+
+ if (msg.is_signal(DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
+ {
+ string name, old_owner, new_owner;
+
+ DBus::Hihi hihi(msg);
+ hihi >> name >> old_owner >> new_owner;
+
+ if (name == new_owner && old_owner.empty())
+ {
+ y2mil("client connected '" << name << "'");
+ client_connected(name);
+ }
+
+ if (name == old_owner && new_owner.empty())
+ {
+ y2mil("client disconnected '" << name << "'");
+ client_disconnected(name);
+ }
+ }
+ }
+ break;
+ }
+
+ idle = 0;
+ }
+
+ y2mil("Idle - exiting");
+}
+
+
+int
+main(int argc, char** argv)
+{
+ initDefaultLogger();
+
+ DBus::Connection conn(DBUS_BUS_SYSTEM);
+
+ listen(conn);
+
+ return 0;
+}
/*
- * Copyright (c) 2011 Novell, Inc.
+ * Copyright (c) [2011-2012] Novell, Inc.
*
* All Rights Reserved.
*
{
Comparison::Comparison(const Snapper* snapper, Snapshots::const_iterator snapshot1,
- Snapshots::const_iterator snapshot2)
- : snapper(snapper), snapshot1(snapshot1), snapshot2(snapshot2), files(this)
+ Snapshots::const_iterator snapshot2, bool delay)
+ : snapper(snapper), snapshot1(snapshot1), snapshot2(snapshot2), initialized(false),
+ files(this)
{
if (snapshot1 == snapper->getSnapshots().end() ||
snapshot2 == snapper->getSnapshots().end() ||
snapshot1 == snapshot2)
throw IllegalSnapshotException();
- y2mil("num1:" << snapshot1->getNum() << " num2:" << snapshot2->getNum());
+ y2mil("num1:" << snapshot1->getNum() << " num2:" << snapshot2->getNum() << " delay:" <<
+ delay);
- files.initialize();
+ if (!delay)
+ {
+ files.initialize();
+ initialized = true;
+ }
+ }
+
+
+ void
+ Comparison::initialize()
+ {
+ if (!initialized)
+ {
+ files.initialize();
+ initialized = true;
+ }
+ }
+
+
+ Files&
+ Comparison::getFiles()
+ {
+ if (!initialized)
+ throw;
+
+ return files;
+ }
+
+
+ const Files&
+ Comparison::getFiles() const
+ {
+ if (!initialized)
+ throw;
+
+ return files;
}
UndoStatistic
Comparison::getUndoStatistic() const
{
+ if (!initialized)
+ throw;
+
return files.getUndoStatistic();
}
bool
Comparison::doUndo()
{
+ if (!initialized)
+ throw;
+
return files.doUndo();
}
/*
- * Copyright (c) 2011 Novell, Inc.
+ * Copyright (c) [2011-2012] Novell, Inc.
*
* All Rights Reserved.
*
public:
Comparison(const Snapper* snapper, Snapshots::const_iterator snapshot1,
- Snapshots::const_iterator snapshot2);
+ Snapshots::const_iterator snapshot2, bool delay = false);
const Snapper* getSnapper() const { return snapper; }
Snapshots::const_iterator getSnapshot1() const { return snapshot1; }
Snapshots::const_iterator getSnapshot2() const { return snapshot2; }
- Files& getFiles() { return files; }
- const Files& getFiles() const { return files; }
+ void initialize();
+ bool isInitialized() const { return initialized; }
+
+ Files& getFiles();
+ const Files& getFiles() const;
UndoStatistic getUndoStatistic() const;
Snapshots::const_iterator snapshot1;
Snapshots::const_iterator snapshot2;
+ bool initialized;
+
Files files;
};
# Makefile.am for snapper/snapper
#
-AM_CXXFLAGS = -D_FILE_OFFSET_BITS=64 -I/usr/include/libxml2
+AM_CXXFLAGS = -D_FILE_OFFSET_BITS=64
+
+INCLUDES = -I/usr/include/libxml2
+
lib_LTLIBRARIES = libsnapper.la
string subvolume = "/";
config.getValue("SUBVOLUME", subvolume);
- config_infos.push_back(ConfigInfo(*it, subvolume));
+
+ vector<string> users;
+ config.getValue("USERS", users);
+
+ config_infos.push_back(ConfigInfo(*it, subvolume, users));
}
catch (const FileNotFoundException& e)
{
/*
- * Copyright (c) 2011 Novell, Inc.
+ * Copyright (c) [2011-2012] Novell, Inc.
*
* All Rights Reserved.
*
#include <vector>
+#include <boost/noncopyable.hpp>
#include "snapper/Snapshot.h"
struct ConfigInfo
{
- ConfigInfo(const string& config_name, const string& subvolume)
- : config_name(config_name), subvolume(subvolume) {}
+ ConfigInfo(const string& config_name, const string& subvolume,
+ const vector<string>& users)
+ : config_name(config_name), subvolume(subvolume), users(users) {}
string config_name;
string subvolume;
+ vector<string> users;
};
};
- class Snapper
+ class Snapper : boost::noncopyable
{
public: