ref: refs/heads/master
commit 1d67b0488f33aedb88191433a8383a061cd486c5
Author: Ján Kupec
Date: Wed Jul 15 08:30:49 2009 +0200
Manage updates for bugzilla and CVE issues (fate #305503).
- list-updates: --bugzilla, --cve, --issues, --all options added
- patch: --bugzilla, --cve options added
---
src/Zypper.cc | 40 ++++++++-
src/update.cc | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
src/update.h | 20 ++++-
3 files changed, 318 insertions(+), 9 deletions(-)
diff --git a/src/Zypper.cc b/src/Zypper.cc
index 37736cd..9f2ee60 100644
--- a/src/Zypper.cc
+++ b/src/Zypper.cc
@@ -1566,6 +1566,9 @@ void Zypper::processCommandOptions()
{"no-recommends", no_argument, 0, 0 },
{"recommends", no_argument, 0, 0 },
{"dry-run", no_argument, 0, 'D'},
+ {"bz", required_argument, 0, 'b'},
+ {"bugzilla", required_argument, 0, 'b'},
+ {"cve", required_argument, 0, 0 },
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
@@ -1577,16 +1580,18 @@ void Zypper::processCommandOptions()
"\n"
" Command options:\n"
"\n"
- "-r, --repo Load only the specified repository.\n"
" --skip-interactive Skip interactive patches.\n"
"-l, --auto-agree-with-licenses\n"
" Automatically say 'yes' to third party license\n"
" confirmation prompt.\n"
" See man zypper for more details.\n"
+ "-b, --bugzilla # Install patch fixing the specified bugzilla issue.\n"
+ " --cve # Install patch fixing the specified CVE issue.\n"
" --debug-solver Create solver test case for debugging.\n"
" --no-recommends Do not install recommended packages, only required.\n"
" --recommends Install also recommended packages in addition\n"
" to the required.\n"
+ "-r, --repo Load only the specified repository.\n"
"-D, --dry-run Test the update, do not actually update.\n"
);
break;
@@ -1596,6 +1601,11 @@ void Zypper::processCommandOptions()
{
static struct option list_updates_options[] = {
{"repo", required_argument, 0, 'r'},
+ {"bz", optional_argument, 0, 'b'},
+ {"bugzilla", optional_argument, 0, 'b'},
+ {"cve", optional_argument, 0, 0 },
+ {"issues", optional_argument, 0, 0 },
+ {"all", no_argument, 0, 'a'},
{"help", no_argument, 0, 'h'},
{0, 0, 0, 0}
};
@@ -1606,7 +1616,11 @@ void Zypper::processCommandOptions()
"List all available needed patches.\n"
"\n"
" Command options:\n"
- "-r, --repo List only patches from the specified repository.\n"
+ "-b, --bugzilla[=#] List needed patches for Bugzilla issues.\n"
+ " --cve[=#] List needed patches for CVE issues.\n"
+ " --issues[=string] Look for issues matching the specified string.\n"
+ "-a, --all List all patches, not only the needed ones.\n"
+ "-r, --repo List only patches from the specified repository.\n"
);
break;
}
@@ -3591,6 +3605,8 @@ void Zypper::doCommand()
else
kinds.insert(ResKind::package);
+ //! \todo drop this option - it's the default for packages now, irrelevant
+ //! for patches; just test with products and patterns
bool best_effort = copts.count( "best-effort" );
if (globalOpts().is_rug_compatible && best_effort)
@@ -3602,6 +3618,15 @@ void Zypper::doCommand()
Out::HIGH);
}
+ if ((copts.count("bugzilla") || copts.count("bz") || copts.count("cve")) &&
+ copts.count("issues"))
+ {
+ out().error(str::form(
+ _("Cannot use %s together with %s."), "--issues", "--bz, --cve"));
+ setExitCode(ZYPPER_EXIT_ERR_INVALID_ARGS);
+ return;
+ }
+
initRepoManager();
init_target(*this);
init_repos(*this);
@@ -3610,7 +3635,11 @@ void Zypper::doCommand()
load_resolvables(*this);
resolve(*this);
- list_updates(*this, kinds, best_effort);
+ if (copts.count("bugzilla") || copts.count("bz")
+ || copts.count("cve") || copts.count("issues"))
+ list_patches_by_issue(*this);
+ else
+ list_updates(*this, kinds, best_effort);
break;
}
@@ -3698,7 +3727,10 @@ void Zypper::doCommand()
bool skip_interactive =
copts.count("skip-interactive") || globalOpts().non_interactive;
- mark_updates(*this, kinds, skip_interactive, best_effort);
+ if (copts.count("bugzilla") || copts.count("bz") || copts.count("cve"))
+ mark_updates_by_issue(*this);
+ else
+ mark_updates(*this, kinds, skip_interactive, best_effort);
solve_and_commit(*this);
diff --git a/src/update.cc b/src/update.cc
index 4b95f8c..91925d8 100755
--- a/src/update.cc
+++ b/src/update.cc
@@ -11,6 +11,7 @@
#include "Table.h"
#include "update.h"
+#include "main.h"
using namespace std;
using namespace zypp;
@@ -181,6 +182,8 @@ static void xml_list_updates(const ResKindSet & kinds)
static bool list_patch_updates(Zypper & zypper)
{
+ bool all = zypper.cOpts().count("all");
+
Table tbl;
Table pm_tbl; // only those that affect packagemanager (restartSuggested()), they have priority
TableHeader th;
@@ -200,7 +203,7 @@ static bool list_patch_updates(Zypper & zypper)
{
ResObject::constPtr res = it->resolvable();
- if ( it->isRelevant() && ! it->isSatisfied() )
+ if (all || it->isBroken())
{
Patch::constPtr patch = asKind<Patch>(res);
@@ -209,9 +212,9 @@ static bool list_patch_updates(Zypper & zypper)
tr << patch->repoInfo().name();
tr << res->name () << res->edition ().asString();
tr << patch->category();
- tr << _("Needed");
+ tr << (it->isBroken() ? _("needed") : _("not needed"));
- if (patch->restartSuggested ())
+ if (!all && patch->restartSuggested ())
pm_tbl << tr;
else
tbl << tr;
@@ -646,6 +649,8 @@ mark_patch_update(ui::Selectable & s,
bool skip_interactive, bool ignore_affects_pm)
{
Patch::constPtr patch = asKind<Patch>(s.candidateObj());
+ XXX << "candidate patch " << patch->name() << " " << ignore_affects_pm << ", "
+ << patch->restartSuggested() << endl;
if (s.isBroken()) // bnc #506860
{
DBG << "candidate patch " << patch->name() << " " << ignore_affects_pm << ", "
@@ -825,3 +830,259 @@ void mark_updates(Zypper & zypper, const ResKindSet & kinds, bool skip_interacti
}
}
}
+
+// ----------------------------------------------------------------------------
+
+void list_patches_by_issue(Zypper & zypper)
+{
+ // lu --issues - list all issues which need to be fixed
+ // lu --issues=foo - look for foo in issue # or description
+ // lu --bz - list all bugzilla issues
+ // lu --cve - list all CVE issues
+ // lu --bz=foo --cve=foo - look for foo in bugzillas or CVEs
+ // --all - list all, not only needed patches
+
+ // --bz, --cve can't be used together with --issue; this case is ruled out
+ // in the initial arguments validation in Zypper.cc
+
+ typedef set > Issues;
+ bool only_needed = !zypper.cOpts().count("all");
+ bool specific = false; // whether specific issue numbers were given
+
+ Table t;
+ TableHeader th;
+ th << _("Issue") << _("No.") << _("Patch") << _("Category");
+ t << th;
+
+#define FILL_ISSUES(TYPE, ID) \
+ it = zypper.cOpts().find(TYPE); \
+ if (it != zypper.cOpts().end()) \
+ for_(i, it->second.begin(), it->second.end()) \
+ { \
+ issues.insert(pair(ID, *i)); \
+ if (!i->empty()) \
+ specific = true; \
+ } \
+
+ // make a set of unique issues
+ Issues issues;
+ parsed_opts::const_iterator it;
+ FILL_ISSUES("bugzilla", "bugzilla");
+ FILL_ISSUES("bz", "bugzilla");
+ FILL_ISSUES("cve", "cve");
+ FILL_ISSUES("issues", "issues");
+
+ // remove those without arguments if there are any with arguments
+ // (will show only the specified ones)
+ if (specific)
+ for (Issues::const_iterator i = issues.begin(); i != issues.end(); )
+ {
+ if (i->second.empty())
+ {
+ zypper.out().warning(str::form(_(
+ "Ignoring %s without argument because similar option with"
+ " an argument has been specified."),
+ ("--" + i->first).c_str()));
+ issues.erase(i++);
+ }
+ else
+ ++i;
+ }
+
+ // construct a PoolQuery for each argument separately and add the results
+ // to the Table.
+ string issuesstr;
+ for_(issue, issues.begin(), issues.end())
+ {
+ PoolQuery q;
+ q.setMatchSubstring();
+ q.setCaseSensitive(false);
+ q.addKind(ResKind::patch); // is this unnecessary? only patches should have updateReference* attributes
+
+ // specific bugzilla or CVE
+ if (specific)
+ {
+ if (issue->first == "bugzilla" || issue->first == "cve")
+ q.addAttribute(sat::SolvAttr::updateReferenceId, issue->second);
+ }
+ // all bugzillas or CVEs
+ else
+ {
+ if (issue->first == "bugzilla")
+ q.addAttribute(sat::SolvAttr::updateReferenceType, "bugzilla");
+ else if (issue->first == "cve")
+ q.addAttribute(sat::SolvAttr::updateReferenceType, "cve");
+ }
+ // look for substring in description and reference ID (issue number)
+ if (issue->first == "issues")
+ {
+ // whether argument was given or not, it does not matter; without
+ // argument, all issues will be found
+ q.addAttribute(sat::SolvAttr::updateReferenceId, issue->second);
+ issuesstr = issue->second;
+ }
+
+ cout << "*****" << endl << q << endl << "*****" << endl;
+
+ for_(it, q.begin(), q.end())
+ {
+ PoolItem pi(*it);
+ if (only_needed && !pi.status().isBroken())
+ continue;
+ Patch::constPtr patch = asKind<Patch>(pi.resolvable());
+
+ // Print details about each match in that solvable:
+ for_( d, it.matchesBegin(), it.matchesEnd() )
+ {
+ string itype =
+ d->subFind(sat::SolvAttr::updateReferenceType).asString();
+ if (itype != issue->first)
+ continue;
+
+ TableRow tr;
+ tr << itype;
+ tr << d->subFind(sat::SolvAttr::updateReferenceId).asString();
+ tr << (patch->name() + "-" + patch->edition().asString());
+ tr << patch->category();
+ t << tr;
+ }
+ }
+ }
+
+ // look for matches in patch descriptions
+ Table t1;
+ TableHeader th1;
+ th1 << _("Name") << _("Version") << _("Category") << _("Summary");
+ t1 << th1;
+ if (!issuesstr.empty())
+ {
+ PoolQuery q;
+ q.setMatchSubstring();
+ q.setCaseSensitive(false);
+ q.addKind(ResKind::patch);
+ q.addAttribute(sat::SolvAttr::summary, issuesstr);
+ q.addAttribute(sat::SolvAttr::description, issuesstr);
+
+ for_(it, q.begin(), q.end())
+ {
+ PoolItem pi(*it);
+ if (only_needed && !pi.status().isBroken())
+ continue;
+ Patch::constPtr patch = asKind<Patch>(pi.resolvable());
+
+ TableRow tr;
+ tr << patch->name() << patch->edition().asString() << patch->category();
+ //! \todo could show a highlighted match with a portion of surrounding
+ //! text. Needs case-insensitive find.
+ tr << patch->summary();
+ t1 << tr;
+ }
+ }
+
+ if (!zypper.globalOpts().no_abbrev)
+ t1.allowAbbrev(3);
+ t.sort(3);
+ t1.sort(0);
+
+ if (t.empty() && t1.empty())
+ zypper.out().info(_("No matching issues found."));
+ else
+ {
+ if (!t.empty())
+ {
+ if (!issuesstr.empty())
+ {
+ cout << endl;
+ zypper.out().info(_(
+ "The following matches in issue numbers have been found:"));
+ }
+
+ cout << endl << t;
+ }
+
+ if (!t1.empty())
+ {
+ if (!t.empty())
+ cout << endl;
+ zypper.out().info(_(
+ "Matches in patch descriptions of the following patches have been"
+ " found:"));
+ cout << endl << t1;
+ }
+ }
+}
+
+// ----------------------------------------------------------------------------
+
+void mark_updates_by_issue(Zypper & zypper)
+{
+ typedef set > Issues;
+ Issues issues;
+ parsed_opts::const_iterator it = zypper.cOpts().find("bugzilla");
+ if (it != zypper.cOpts().end())
+ for_(i, it->second.begin(), it->second.end())
+ issues.insert(pair("b", *i));
+ it = zypper.cOpts().find("bz");
+ if (it != zypper.cOpts().end())
+ for_(i, it->second.begin(), it->second.end())
+ issues.insert(pair("b", *i));
+ it = zypper.cOpts().find("cve");
+ if (it != zypper.cOpts().end())
+ for_(i, it->second.begin(), it->second.end())
+ issues.insert(pair("c", *i));
+
+ for_(issue, issues.begin(), issues.end())
+ {
+ PoolQuery q;
+ q.setMatchExact();
+ q.setCaseSensitive(false);
+ q.addKind(ResKind::patch); // is this unnecessary?
+ if (issue->first == "b")
+ q.addAttribute(sat::SolvAttr::updateReferenceId, issue->second);
+ else if (issue->first == "c")
+ q.addAttribute(sat::SolvAttr::updateReferenceId, issue->second);
+
+ bool found = false;
+ for_(sit, q.begin(), q.end())
+ {
+ if (!PoolItem(*sit).status().isBroken()) // not needed
+ continue;
+
+ for_( d, sit.matchesBegin(), sit.matchesEnd() )
+ {
+ if (issue->first == "b" &&
+ d->subFind(sat::SolvAttr::updateReferenceType).asString() == "bugzilla")
+ {
+ if (mark_patch_update(*God->pool().proxy().lookup(*sit),
+ zypper.cOpts().count("skip-interactive"), true))
+ found = true;
+ else
+ DBG << str::form("fix for bugzilla issue number %s was not marked.",
+ issue->second.c_str());
+ }
+ else if (issue->first == "b" &&
+ d->subFind(sat::SolvAttr::updateReferenceType).asString() == "bugzilla")
+ {
+ if (mark_patch_update(*God->pool().proxy().lookup(*sit),
+ zypper.cOpts().count("skip-interactive"), true))
+ found = true;
+ else
+ DBG << str::form("fix for CVE issue number %s was not marked.",
+ issue->second.c_str());
+ }
+ }
+ }
+ if (!found)
+ {
+ if (issue->first == "b")
+ zypper.out().info(str::form(_(
+ "Fix for bugzilla issue number %s was not found or is not needed."),
+ issue->second.c_str()));
+ else if (issue->first == "c")
+ zypper.out().info(str::form(_(
+ "Fix for CVE issue number %s was not found or is not needed."),
+ issue->second.c_str()));
+ zypper.setExitCode(ZYPPER_EXIT_INF_CAP_NOT_FOUND);
+ }
+ } // next issue from --bz --cve
+}
diff --git a/src/update.h b/src/update.h
index a7c96c9..2f4c8b1 100755
--- a/src/update.h
+++ b/src/update.h
@@ -23,9 +23,19 @@ void list_updates(Zypper & zypper,
bool best_effort);
/**
- * \param kind resolvable type
- * \param skip_interactive whether to skip updates that need user interaction
+ * List available fixes to all issues or issues specified in --bugzilla
+ * or --cve options, or look for --issues[=str[ in numbers and descriptions
+ */
+void list_patches_by_issue(Zypper & zypper);
+
+/**
+ * Mark patches for installation or package for update.
+ *
+ * \param kinds set of resolvable types
+ * \param skip_interactive
+ * whether to skip updates that need user interaction
* \param best_effort
+ * only require the resolvable name, let the solver choose
*/
void mark_updates(Zypper & zypper,
const ResKindSet & kinds,
@@ -33,6 +43,12 @@ void mark_updates(Zypper & zypper,
bool best_effort);
/**
+ * Mark patches for installation according to bugzilla or CVE number specified
+ * in --cve or --bugzilla or --bz.
+ */
+void mark_updates_by_issue(Zypper & zypper);
+
+/**
* Find best (according to edition) uninstalled item
* with same kind/name/arch as \a item.
*
--
To unsubscribe, e-mail: zypp-commit+unsubscribe@opensuse.org
For additional commands, e-mail: zypp-commit+help@opensuse.org