AM_CPPFLAGS = $(DBUS_CFLAGS) $(XML2_CFLAGS) $(JSONC_CFLAGS)
snapper_zypp_plugin_SOURCES = \
- snapper-zypp-plugin.cc \
+ snapper-zypp-commit-plugin.cc snapper-zypp-commit-plugin.h \
solvable-matcher.cc solvable-matcher.h \
zypp-commit-plugin.cc zypp-commit-plugin.h \
zypp-plugin.cc zypp-plugin.h
--- /dev/null
+/*
+ * Copyright (c) [2019-2023] SUSE LLC
+ *
+ * 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 SUSE LLC.
+ *
+ * To contact SUSE about this file by physical or electronic mail, you may
+ * find current contact information at www.suse.com.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <map>
+#include <set>
+#include <string>
+#include <regex>
+#include <boost/algorithm/string.hpp>
+
+#include <json.h>
+// a collision with client/errors.h
+#ifdef error_description
+#undef error_description
+#endif
+
+#include "dbus/DBusConnection.h"
+#include "snapper/Exception.h"
+#include "client/commands.h"
+#include "client/errors.h"
+
+#include "snapper-zypp-commit-plugin.h"
+
+using namespace std;
+using namespace snapper;
+
+
+ostream&
+operator<<(ostream& os, const set<string>& ss)
+{
+ bool seen_first = false;
+ os << '{';
+ for (auto s : ss)
+ {
+ if (seen_first)
+ os << ", ";
+
+ os << s;
+ seen_first = true;
+ }
+ os << '}';
+ return os;
+}
+
+
+ProgramOptions::ProgramOptions()
+{
+ const char* s;
+
+ s = getenv("SNAPPER_ZYPP_PLUGIN_CONFIG");
+ if (s)
+ plugin_config = s;
+ else
+ plugin_config = locate_file("zypp-plugin.conf", "/etc/snapper", "/usr/share/snapper");
+
+ s = getenv("SNAPPER_ZYPP_PLUGIN_SNAPPER_CONFIG");
+ if (s)
+ snapper_config = s;
+
+ s = getenv("SNAPPER_ZYPP_PLUGIN_DBUS_SESSION");
+ if (s)
+ bus = DBUS_BUS_SESSION;
+}
+
+
+SnapperZyppCommitPlugin::SnapperZyppCommitPlugin(const ProgramOptions& opts)
+ : snapper_cfg(opts.snapper_config), dbus_conn(opts.bus), pre_snapshot_num(0),
+ solvable_matchers(SolvableMatcher::load_config(opts.plugin_config))
+{
+ string caller_prog;
+ readlink(sformat("/proc/%d/exe", getppid()), caller_prog);
+ snapshot_description = sformat("zypp(%s)", basename(caller_prog).c_str());
+}
+
+
+ZyppCommitPlugin::Message
+SnapperZyppCommitPlugin::plugin_begin(const Message& msg)
+{
+ cerr << "INFO:" << "PLUGINBEGIN" << endl;
+ userdata = get_userdata(msg);
+
+ return ack();
+}
+
+
+ZyppCommitPlugin::Message
+SnapperZyppCommitPlugin::plugin_end(const Message& msg)
+{
+ cerr << "INFO:" << "PLUGINEND" << endl;
+ return ack();
+}
+
+
+ZyppCommitPlugin::Message
+SnapperZyppCommitPlugin::commit_begin(const Message& msg)
+{
+ cerr << "INFO:" << "COMMITBEGIN" << endl;
+
+ set<string> solvables = get_solvables(msg, Phase::BEFORE);
+ cerr << "DEBUG:" << "solvables: " << solvables << endl;
+
+ bool found, important;
+ match_solvables(solvables, found, important);
+ cerr << "INFO:" << "found: " << found << ", important: " << important << endl;
+
+ if (found || important)
+ {
+ userdata["important"] = important ? "yes" : "no";
+
+ try
+ {
+ cerr << "INFO:" << "creating pre snapshot" << endl;
+ pre_snapshot_num = command_create_pre_snapshot(
+ dbus_conn, snapper_cfg,
+ snapshot_description, cleanup_algorithm, userdata
+ );
+ cerr << "DEBUG:" << "created pre snapshot " << pre_snapshot_num << endl;
+ }
+ catch (const DBus::ErrorException& ex)
+ {
+ SN_CAUGHT(ex);
+ cerr << "ERROR:" << error_description(ex) << endl;
+ }
+ catch (const Exception& ex)
+ {
+ SN_CAUGHT(ex);
+ }
+ }
+
+ return ack();
+}
+
+
+ZyppCommitPlugin::Message
+SnapperZyppCommitPlugin::commit_end(const Message& msg)
+{
+ cerr << "INFO:" << "COMMITEND" << endl;
+
+ if (pre_snapshot_num != 0)
+ {
+ set<string> solvables = get_solvables(msg, Phase::AFTER);
+ cerr << "DEBUG:" << "solvables: " << solvables << endl;
+
+ bool found, important;
+ match_solvables(solvables, found, important);
+ cerr << "INFO:" << "found: " << found << ", important: " << important << endl;
+
+ if (found || important)
+ {
+ userdata["important"] = important ? "yes" : "no";
+
+ try
+ {
+ cerr << "INFO:" << "setting snapshot data" << endl;
+ snapper::SMD modification_data;
+ modification_data.description = snapshot_description;
+ modification_data.cleanup = cleanup_algorithm;
+ modification_data.userdata = userdata;
+ command_set_snapshot(dbus_conn, snapper_cfg, pre_snapshot_num, modification_data);
+ }
+ catch (const DBus::ErrorException& ex)
+ {
+ SN_CAUGHT(ex);
+ cerr << "ERROR:" << error_description(ex) << endl;
+ }
+ catch (const Exception& ex)
+ {
+ SN_CAUGHT(ex);
+ }
+
+ try
+ {
+ cerr << "INFO:" << "creating post snapshot" << endl;
+ unsigned int post_snapshot_num = command_create_post_snapshot(
+ dbus_conn, snapper_cfg,
+ pre_snapshot_num, "", cleanup_algorithm, userdata
+ );
+ cerr << "DEBUG:" << "created post snapshot " << post_snapshot_num << endl;
+ }
+ catch (const DBus::ErrorException& ex)
+ {
+ SN_CAUGHT(ex);
+ cerr << "ERROR:" << error_description(ex) << endl;
+ }
+ catch (const Exception& ex)
+ {
+ SN_CAUGHT(ex);
+ }
+ }
+ else
+ {
+ try
+ {
+ cerr << "INFO:" << "deleting pre snapshot" << endl;
+ vector<unsigned int> nums{ pre_snapshot_num };
+ bool verbose = false;
+ command_delete_snapshots(dbus_conn, snapper_cfg, nums, verbose);
+ cerr << "DEBUG:" << "deleted pre snapshot " << pre_snapshot_num << endl;
+ }
+ catch (const DBus::ErrorException& ex)
+ {
+ SN_CAUGHT(ex);
+ cerr << "ERROR:" << error_description(ex) << endl;
+ }
+ catch (const Exception& ex)
+ {
+ SN_CAUGHT(ex);
+ }
+ }
+ }
+ return ack();
+}
+
+
+const string SnapperZyppCommitPlugin::cleanup_algorithm = "number";
+
+
+map<string, string>
+SnapperZyppCommitPlugin::get_userdata(const Message& msg)
+{
+ map<string, string> result;
+ auto it = msg.headers.find("userdata");
+ if (it != msg.headers.end())
+ {
+ const string& userdata_s = it->second;
+ vector<string> key_values;
+ boost::split(key_values, userdata_s, boost::is_any_of(","));
+ for (auto kv : key_values)
+ {
+ static const regex rx_keyval("([^=]*)=(.+)", regex::extended);
+ smatch match;
+
+ if (regex_match(kv, match, rx_keyval))
+ {
+ string key = boost::trim_copy(match[1].str());
+ string value = boost::trim_copy(match[2].str());
+ result[key] = value;
+ }
+ else
+ {
+ cerr << "ERROR:" << "invalid userdata: expecting comma separated key=value pairs" << endl;
+ }
+ }
+ }
+ return result;
+}
+
+
+static json_object*
+object_get(json_object* obj, const char* name)
+{
+ json_object * result;
+ if (!json_object_object_get_ex(obj, name, &result))
+ {
+ cerr << "ERROR:" << '"' << name << "\" not found" << endl;
+ return NULL;
+ }
+ return result;
+}
+
+
+class JsonTokener
+{
+public:
+
+ JsonTokener()
+ : p(json_tokener_new())
+ {
+ if (!p)
+ throw runtime_error("out of memory");
+ }
+
+ ~JsonTokener()
+ {
+ json_tokener_free(p);
+ }
+
+ json_tokener* get() { return p; }
+
+private:
+
+ json_tokener* p;
+
+};
+
+
+set<string>
+SnapperZyppCommitPlugin::get_solvables(const Message& msg, Phase phase) const
+{
+ set<string> result;
+
+ JsonTokener tokener;
+ json_object* zypp = json_tokener_parse_ex(tokener.get(), msg.body.c_str(), msg.body.size());
+ json_tokener_error jerr = json_tokener_get_error(tokener.get());
+ if (jerr != json_tokener_success)
+ {
+ cerr << "ERROR:" << "parsing zypp JSON failed: "
+ << json_tokener_error_desc(jerr) << endl;
+ return result;
+ }
+
+ // JSON structure:
+ // {"TransactionStepList":[{"type":"?","stage":"?","solvable":{"n":"mypackage"}}]}
+ // https://doc.opensuse.org/projects/libzypp/SLE12SP2/plugin-commit.html
+ json_object* steps = object_get(zypp, "TransactionStepList");
+ if (!steps)
+ return result;
+
+ if (json_object_get_type(steps) == json_type_array)
+ {
+ size_t len = json_object_array_length(steps);
+ for (size_t i = 0; i < len; ++i)
+ {
+ json_object* step = json_object_array_get_idx(steps, i);
+ bool have_type = json_object_object_get_ex(step, "type", NULL);
+ bool have_stage = json_object_object_get_ex(step, "stage", NULL);
+ if (have_type && (phase == Phase::BEFORE || have_stage))
+ {
+ json_object* solvable = object_get(step, "solvable");
+ if (!solvable)
+ {
+ cerr << "ERROR:" << "in item #" << i << endl;
+ continue;
+ }
+
+ json_object* name = object_get(solvable, "n");
+ if (!name)
+ {
+ cerr << "ERROR:" << "in item #" << i << endl;
+ continue;
+ }
+
+ if (json_object_get_type(name) != json_type_string)
+ {
+ cerr << "ERROR:" << "\"n\" is not a string" << endl;
+ cerr << "ERROR:" << "in item #" << i << endl;
+ continue;
+ }
+ else
+ {
+ const char * prize = json_object_get_string(name);
+ result.insert(prize);
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+
+void
+SnapperZyppCommitPlugin::match_solvables(const set<string>& solvables, bool& found, bool& important) const
+{
+ found = false;
+ important = false;
+
+ for (const string& solvable : solvables)
+ {
+ for (const SolvableMatcher& solvable_matcher : solvable_matchers)
+ {
+ if (solvable_matcher.match(solvable))
+ {
+ found = true;
+
+ if (solvable_matcher.is_important())
+ {
+ important = true;
+ return; // short circuit
+ }
+ }
+ }
+ }
+}
+
+
+int
+main()
+{
+ if (getenv("DISABLE_SNAPPER_ZYPP_PLUGIN"))
+ {
+ cerr << "INFO:" << "$DISABLE_SNAPPER_ZYPP_PLUGIN is set - disabling snapper-zypp-plugin" << endl;
+ DummyZyppCommitPlugin plugin;
+ return plugin.main();
+ }
+
+ ProgramOptions options;
+ SnapperZyppCommitPlugin plugin(options);
+ return plugin.main();
+}
--- /dev/null
+/*
+ * Copyright (c) [2019-2023] SUSE LLC
+ *
+ * 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 SUSE LLC.
+ *
+ * To contact SUSE about this file by physical or electronic mail, you may
+ * find current contact information at www.suse.com.
+ */
+
+#ifndef SNAPPER_ZYPP_COMMIT_PLUGIN_H
+#define SNAPPER_ZYPP_COMMIT_PLUGIN_H
+
+#include "zypp-commit-plugin.h"
+#include "solvable-matcher.h"
+
+
+// Normally the only configuration this program needs is
+// the zypp-plugin.conf file in /etc/snapper or /usr/share/snapper.
+// But for testing we need more places to inject mocks.
+// This is done with SNAPPER_ZYPP_PLUGIN_* environment variables.
+// (Using argv is not useful since libzypp does not use it in the
+// plugin protocol.)
+class ProgramOptions
+{
+public:
+
+ ProgramOptions();
+
+ string plugin_config;
+ string snapper_config = "root";
+ DBusBusType bus = DBUS_BUS_SYSTEM;
+
+};
+
+
+class SnapperZyppCommitPlugin : public ZyppCommitPlugin
+{
+public:
+
+ SnapperZyppCommitPlugin(const ProgramOptions& opts);
+
+ Message plugin_begin(const Message& msg) override;
+ Message plugin_end(const Message& msg) override;
+
+ Message commit_begin(const Message& msg) override;
+ Message commit_end(const Message& msg) override;
+
+private:
+
+ static const string cleanup_algorithm;
+
+ const string snapper_cfg;
+
+ DBus::Connection dbus_conn;
+ unsigned int pre_snapshot_num;
+ string snapshot_description;
+ map<string, string> userdata;
+
+ vector<SolvableMatcher> solvable_matchers;
+
+ map<string, string> get_userdata(const Message& msg);
+
+ enum class Phase { BEFORE, AFTER };
+
+ std::set<string> get_solvables(const Message& msg, Phase phase) const;
+
+ void match_solvables(const std::set<string>& solvables, bool& found, bool& important) const;
+
+};
+
+
+#endif
+++ /dev/null
-/*
- * Copyright (c) [2019-2023] SUSE LLC
- *
- * 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 SUSE LLC.
- *
- * To contact SUSE about this file by physical or electronic mail, you may
- * find current contact information at www.suse.com.
- */
-
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <iostream>
-#include <map>
-#include <set>
-#include <string>
-#include <regex>
-#include <boost/algorithm/string.hpp>
-
-using namespace std;
-
-#include <json.h>
-// a collision with client/errors.h
-#ifdef error_description
-#undef error_description
-#endif
-
-#include "dbus/DBusConnection.h"
-#include "snapper/Exception.h"
-using snapper::Exception;
-using snapper::CodeLocation;
-#include "client/commands.h"
-#include "client/errors.h"
-
-#include "zypp-commit-plugin.h"
-#include "solvable-matcher.h"
-
-ostream&
-operator<<(ostream& os, const set<string>& ss)
-{
- bool seen_first = false;
- os << '{';
- for (auto s : ss)
- {
- if (seen_first)
- os << ", ";
-
- os << s;
- seen_first = true;
- }
- os << '}';
- return os;
-}
-
-
-// Normally the only configuration this program needs is
-// the zypp-plugin.conf file in /etc/snapper or /usr/share/snapper.
-// But for testing we need more places to inject mocks.
-// This is done with SNAPPER_ZYPP_PLUGIN_* environment variables.
-// (Using argv is not useful since libzypp does not use it in the
-// plugin protocol.)
-class ProgramOptions
-{
-
-public:
-
- string plugin_config;
- string snapper_config = "root";
- DBusBusType bus = DBUS_BUS_SYSTEM;
-
- ProgramOptions()
- {
- const char* s;
-
- s = getenv("SNAPPER_ZYPP_PLUGIN_CONFIG");
- if (s)
- plugin_config = s;
- else
- plugin_config = locate_file("zypp-plugin.conf", "/etc/snapper", "/usr/share/snapper");
-
- s = getenv("SNAPPER_ZYPP_PLUGIN_SNAPPER_CONFIG");
- if (s)
- snapper_config = s;
-
- s = getenv("SNAPPER_ZYPP_PLUGIN_DBUS_SESSION");
- if (s)
- bus = DBUS_BUS_SESSION;
- }
-
-};
-
-
-class SnapperZyppPlugin : public ZyppCommitPlugin
-{
-public:
- SnapperZyppPlugin(const ProgramOptions& opts)
- : snapper_cfg(opts.snapper_config)
- , dbus_conn(opts.bus)
- , pre_snapshot_num(0)
- , solvable_matchers(SolvableMatcher::load_config(opts.plugin_config))
- {
- string caller_prog;
- readlink(sformat("/proc/%d/exe", getppid()), caller_prog);
- snapshot_description = sformat("zypp(%s)", basename(caller_prog).c_str());
- }
-
- Message plugin_begin(const Message& m) override {
- cerr << "INFO:" << "PLUGINBEGIN" << endl;
- userdata = get_userdata(m);
-
- return ack();
- }
- Message plugin_end(const Message& m) override {
- cerr << "INFO:" << "PLUGINEND" << endl;
- return ack();
- }
- Message commit_begin(const Message& msg) override {
- cerr << "INFO:" << "COMMITBEGIN" << endl;
-
- set<string> solvables = get_solvables(msg, Phase::BEFORE);
- cerr << "DEBUG:" << "solvables: " << solvables << endl;
-
- bool found, important;
- match_solvables(solvables, found, important);
- cerr << "INFO:" << "found: " << found << ", important: " << important << endl;
-
- if (found || important) {
- userdata["important"] = important ? "yes" : "no";
-
- try {
- cerr << "INFO:" << "creating pre snapshot" << endl;
- pre_snapshot_num = command_create_pre_snapshot(
- dbus_conn, snapper_cfg,
- snapshot_description, cleanup_algorithm, userdata
- );
- cerr << "DEBUG:" << "created pre snapshot " << pre_snapshot_num << endl;
- }
- catch (const DBus::ErrorException& ex) {
- SN_CAUGHT(ex);
- cerr << "ERROR:" << error_description(ex) << endl;
- }
- catch (const Exception& ex) {
- SN_CAUGHT(ex);
- }
- }
-
- return ack();
- }
-
- Message commit_end(const Message& msg) override {
- cerr << "INFO:" << "COMMITEND" << endl;
-
- if (pre_snapshot_num != 0) {
- set<string> solvables = get_solvables(msg, Phase::AFTER);
- cerr << "DEBUG:" << "solvables: " << solvables << endl;
-
- bool found, important;
- match_solvables(solvables, found, important);
- cerr << "INFO:" << "found: " << found << ", important: " << important << endl;
-
- if (found || important) {
- userdata["important"] = important ? "yes" : "no";
-
- try {
- cerr << "INFO:" << "setting snapshot data" << endl;
- snapper::SMD modification_data;
- modification_data.description = snapshot_description;
- modification_data.cleanup = cleanup_algorithm;
- modification_data.userdata = userdata;
- command_set_snapshot(
- dbus_conn, snapper_cfg,
- pre_snapshot_num, modification_data
- );
- }
- catch (const DBus::ErrorException& ex) {
- SN_CAUGHT(ex);
- cerr << "ERROR:" << error_description(ex) << endl;
- }
- catch (const Exception& ex) {
- SN_CAUGHT(ex);
- }
- try {
- cerr << "INFO:" << "creating post snapshot" << endl;
- unsigned int post_snapshot_num = command_create_post_snapshot(
- dbus_conn, snapper_cfg,
- pre_snapshot_num, "", cleanup_algorithm, userdata
- );
- cerr << "DEBUG:" << "created post snapshot " << post_snapshot_num << endl;
- }
- catch (const DBus::ErrorException& ex) {
- SN_CAUGHT(ex);
- cerr << "ERROR:" << error_description(ex) << endl;
- }
- catch (const Exception& ex) {
- SN_CAUGHT(ex);
- }
- }
- else {
- try {
- cerr << "INFO:" << "deleting pre snapshot" << endl;
- vector<unsigned int> nums{ pre_snapshot_num };
- bool verbose = false;
- command_delete_snapshots(dbus_conn, snapper_cfg, nums, verbose);
- cerr << "DEBUG:" << "deleted pre snapshot " << pre_snapshot_num << endl;
- }
- catch (const DBus::ErrorException& ex) {
- SN_CAUGHT(ex);
- cerr << "ERROR:" << error_description(ex) << endl;
- }
- catch (const Exception& ex) {
- SN_CAUGHT(ex);
- }
- }
- }
- return ack();
- }
-
-private:
-
- static const string cleanup_algorithm;
-
- const string snapper_cfg;
-
- DBus::Connection dbus_conn;
- unsigned int pre_snapshot_num;
- string snapshot_description;
- map<string, string> userdata;
-
- vector<SolvableMatcher> solvable_matchers;
-
- map<string, string> get_userdata(const Message&);
-
- enum class Phase { BEFORE, AFTER };
-
- set<string> get_solvables(const Message&, Phase phase);
-
- void match_solvables(const set<string>& solvables, bool& found, bool& important);
-
-};
-
-
-const string SnapperZyppPlugin::cleanup_algorithm = "number";
-
-
-map<string, string>
-SnapperZyppPlugin::get_userdata(const Message& msg)
-{
- map<string, string> result;
- auto it = msg.headers.find("userdata");
- if (it != msg.headers.end()) {
- const string& userdata_s = it->second;
- vector<string> key_values;
- boost::split(key_values, userdata_s, boost::is_any_of(","));
- for (auto kv : key_values)
- {
- static const regex rx_keyval("([^=]*)=(.+)", regex::extended);
- smatch match;
-
- if (regex_match(kv, match, rx_keyval))
- {
- string key = boost::trim_copy(match[1].str());
- string value = boost::trim_copy(match[2].str());
- result[key] = value;
- }
- else
- {
- cerr << "ERROR:" << "invalid userdata: expecting comma separated key=value pairs" << endl;
- }
- }
- }
- return result;
-}
-
-
-static
-json_object*
-object_get(json_object* obj, const char* name)
-{
- json_object * result;
- if (!json_object_object_get_ex(obj, name, &result)) {
- cerr << "ERROR:" << '"' << name << "\" not found" << endl;
- return NULL;
- }
- return result;
-}
-
-
-class JsonTokener
-{
-public:
-
- JsonTokener()
- : p(json_tokener_new())
- {
- if (!p)
- throw runtime_error("out of memory");
- }
-
- ~JsonTokener()
- {
- json_tokener_free(p);
- }
-
- json_tokener* get() { return p; }
-
-private:
-
- json_tokener* p;
-
-};
-
-
-set<string>
-SnapperZyppPlugin::get_solvables(const Message& msg, Phase phase)
-{
- set<string> result;
-
- JsonTokener tokener;
- json_object* zypp = json_tokener_parse_ex(tokener.get(), msg.body.c_str(), msg.body.size());
- json_tokener_error jerr = json_tokener_get_error(tokener.get());
- if (jerr != json_tokener_success) {
- cerr << "ERROR:" << "parsing zypp JSON failed: "
- << json_tokener_error_desc(jerr) << endl;
- return result;
- }
-
- // JSON structure:
- // {"TransactionStepList":[{"type":"?","stage":"?","solvable":{"n":"mypackage"}}]}
- // https://doc.opensuse.org/projects/libzypp/SLE12SP2/plugin-commit.html
- json_object * steps = object_get(zypp, "TransactionStepList");
- if (!steps)
- return result;
-
- if (json_object_get_type(steps) == json_type_array) {
- size_t i, len = json_object_array_length(steps);
- for (i = 0; i < len; ++i) {
- json_object * step = json_object_array_get_idx(steps, i);
- bool have_type = json_object_object_get_ex(step, "type", NULL);
- bool have_stage = json_object_object_get_ex(step, "stage", NULL);
- if (have_type && (phase == Phase::BEFORE || have_stage)) {
- json_object * solvable = object_get(step, "solvable");
- if (!solvable) {
- cerr << "ERROR:" << "in item #" << i << endl;
- continue;
- }
- json_object * name = object_get(solvable, "n");
- if (!name) {
- cerr << "ERROR:" << "in item #" << i << endl;
- continue;
- }
- if (json_object_get_type(name) != json_type_string) {
- cerr << "ERROR:" << "\"n\" is not a string" << endl;
- cerr << "ERROR:" << "in item #" << i << endl;
- continue;
- }
- else {
- const char * prize = json_object_get_string(name);
- result.insert(prize);
- }
- }
- }
- }
-
- return result;
-}
-
-
-void
-SnapperZyppPlugin::match_solvables(const set<string>& solvables, bool& found, bool& important)
-{
- found = false;
- important = false;
-
- for (const string& solvable : solvables)
- {
- for (const SolvableMatcher& solvable_matcher : solvable_matchers)
- {
- if (solvable_matcher.match(solvable))
- {
- found = true;
-
- if (solvable_matcher.is_important())
- {
- important = true;
- return; // short circuit
- }
- }
- }
- }
-}
-
-
-int
-main()
-{
- if (getenv("DISABLE_SNAPPER_ZYPP_PLUGIN") != nullptr) {
- cerr << "INFO:" << "$DISABLE_SNAPPER_ZYPP_PLUGIN is set - disabling snapper-zypp-plugin" << endl;
- ZyppCommitPlugin plugin;
- return plugin.main();
- }
-
- ProgramOptions options;
- SnapperZyppPlugin plugin(options);
- return plugin.main();
-}
#include "zypp-commit-plugin.h"
-ZyppPlugin::Message ZyppCommitPlugin::dispatch(const Message& msg)
+ZyppPlugin::Message
+ZyppCommitPlugin::dispatch(const Message& msg)
{
if (msg.command == "PLUGINBEGIN") {
return plugin_begin(msg);
}
+
if (msg.command == "PLUGINEND") {
return plugin_end(msg);
}
+
if (msg.command == "COMMITBEGIN") {
return commit_begin(msg);
}
+
if (msg.command == "COMMITEND") {
return commit_end(msg);
}
#include "zypp-plugin.h"
+
/// Dispatches begin+end of plugin+commit in dedicated methods.
// The default implementations just ack.
-class ZyppCommitPlugin : public ZyppPlugin {
+class ZyppCommitPlugin : public ZyppPlugin
+{
public:
+
Message dispatch(const Message& msg) override;
- virtual Message plugin_begin(const Message& m) {
+ virtual Message plugin_begin(const Message& msg) = 0;
+ virtual Message plugin_end(const Message& msg) = 0;
+
+ virtual Message commit_begin(const Message& msg) = 0;
+ virtual Message commit_end(const Message& msg) = 0;
+
+};
+
+
+class DummyZyppCommitPlugin : public ZyppCommitPlugin
+{
+public:
+
+ virtual Message plugin_begin(const Message& msg) override
+ {
return ack();
}
- virtual Message plugin_end(const Message& m) {
+
+ virtual Message plugin_end(const Message& msg) override
+ {
return ack();
}
- virtual Message commit_begin(const Message& m) {
+
+ virtual Message commit_begin(const Message& msg) override
+ {
return ack();
}
- virtual Message commit_end(const Message& m) {
+
+ virtual Message commit_end(const Message& msg) override
+ {
return ack();
}
+
};
-#endif //ZYPP_COMMIT_PLUGIN_H
+#endif
int
ZyppPlugin::main()
{
- while(true)
+ while (true)
{
Message msg = read_message(pin);
if (pin.eof())
ZyppPlugin::write_message(ostream& os, const Message& msg)
{
os << msg.command << endl;
- for(auto it: msg.headers) {
+ for (auto it: msg.headers) {
os << it.first << ':' << it.second << endl;
}
os << endl;
ZyppPlugin::Message
ZyppPlugin::read_message(istream& is)
{
- enum class State {
- Start,
- Headers,
- Body
- } state = State::Start;
+ enum class State { Start, Headers, Body } state = State::Start;
Message msg;
getline(is, line);
boost::trim_right(line);
- if (state == State::Start) {
+ if (state == State::Start)
+ {
if (is.eof())
return msg; //empty
throw runtime_error("Plugin protocol error: expected a command. Got '" + line + "'");
}
}
- else if (state == State::Headers) {
- if (line.empty()) {
+ else if (state == State::Headers)
+ {
+ if (line.empty())
+ {
state = State::Body;
getline(is, msg.body, '\0');
ZyppPlugin::Message
ZyppPlugin::dispatch(const Message& msg)
{
- if (msg.command == "_DISCONNECT") {
+ if (msg.command == "_DISCONNECT")
+ {
return ack();
}
+
Message a;
a.command = "_ENOMETHOD";
a.headers["Command"] = msg.command;
#include <map>
#include <string>
-class ZyppPlugin {
+class ZyppPlugin
+{
public:
+
// Plugin message aka frame
// https://doc.opensuse.org/projects/libzypp/SLE12SP2/zypp-plugins.html
- struct Message {
+ struct Message
+ {
std::string command;
std::map<std::string, std::string> headers;
std::string body;
std::ostream& pout;
/// Where the plugin writes log messages to
std::ostream& plog;
- ZyppPlugin(std::istream& in = std::cin,
- std::ostream& out = std::cout,
- std::ostream& log = std::cerr)
- : pin(in)
- , pout(out)
- , plog(log)
+
+ ZyppPlugin(std::istream& in = std::cin, std::ostream& out = std::cout, std::ostream& log = std::cerr)
+ : pin(in), pout(out), plog(log)
{}
+
virtual ~ZyppPlugin() {}
virtual int main();
protected:
+
/// Handle a message and return a reply.
// Derived classes should override it.
// The base acks a _DISCONNECT and replies _ENOMETHOD to everything else.
- virtual Message dispatch(const Message&);
+ virtual Message dispatch(const Message& msg);
Message read_message(std::istream& is);
void write_message(std::ostream& os, const Message& msg);
- Message ack() {
+ Message ack()
+ {
Message a;
a.command = "ACK";
return a;
}
};
-#endif //ZYPP_PLUGIN_H
+#endif