summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Kalnischkies <david@kalnischkies.de>2016-07-08 09:40:46 +0200
committerDavid Kalnischkies <david@kalnischkies.de>2016-08-10 23:18:04 +0200
commite7d4e0cf4f6d79810e4b4e7de505729e759213dd (patch)
treeba19ea1f8d96b71f20a16e07ec082332a6196002
parentb820fd59c4fe6e3581901eee648e88209be56137 (diff)
select remove/purge packages early on for dpkg
Telling dpkg early on that we are going to remove these packages later helps it with auto-deconfiguration decisions and its another area where a planner can ignore the nitty gritty details and let dpkg decide the course of action if there are no special requirements.
-rw-r--r--apt-pkg/deb/dpkgpm.cc95
-rw-r--r--apt-pkg/deb/dpkgpm.h3
-rw-r--r--doc/external-installation-planner-protocol.txt4
-rwxr-xr-xtest/integration/test-bug-673536-pre-depends-breaks-loop2
4 files changed, 88 insertions, 16 deletions
diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc
index 38285d14b..bc34e50e0 100644
--- a/apt-pkg/deb/dpkgpm.cc
+++ b/apt-pkg/deb/dpkgpm.cc
@@ -1372,6 +1372,50 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
return _error->Error("Couldn't clean the currently selected dpkg states");
}
}
+ APT::StateChanges approvedStates;
+ if (_config->FindB("dpkg::selection::remove::approved", true))
+ {
+ for (auto && I: List)
+ if (I.Op == Item::Remove && Cache[I.Pkg].Delete())
+ approvedStates.Remove(FindToBeRemovedVersion(I.Pkg));
+ else if (I.Op == Item::Purge && Cache[I.Pkg].Purge())
+ approvedStates.Purge(FindToBeRemovedVersion(I.Pkg));
+ if (approvedStates.Save(false) == false)
+ {
+ _error->Error("Couldn't record the approved state changes as dpkg selection states");
+ if (currentStates.Save(false) == false)
+ _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
+ return false;
+ }
+
+ {
+ std::vector<bool> toBeRemoved(Cache.Head().PackageCount, false);
+ std::vector<bool> toBePurged(Cache.Head().PackageCount, false);
+ for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
+ if (Cache[Pkg].Purge())
+ toBePurged[Pkg->ID] = true;
+ else if (Cache[Pkg].Delete())
+ toBeRemoved[Pkg->ID] = true;
+ for (auto && I: approvedStates.Remove())
+ toBeRemoved[I.ParentPkg()->ID] = false;
+ for (auto && I: approvedStates.Purge())
+ toBePurged[I.ParentPkg()->ID] = false;
+ if (std::find(toBeRemoved.begin(), toBeRemoved.end(), true) != toBeRemoved.end())
+ {
+ if (ConfigurePending)
+ List.emplace(std::prev(List.end()), Item::RemovePending, pkgCache::PkgIterator());
+ else
+ List.emplace_back(Item::RemovePending, pkgCache::PkgIterator());
+ }
+ if (std::find(toBePurged.begin(), toBePurged.end(), true) != toBePurged.end())
+ {
+ if (ConfigurePending)
+ List.emplace(std::prev(List.end()), Item::PurgePending, pkgCache::PkgIterator());
+ else
+ List.emplace_back(Item::PurgePending, pkgCache::PkgIterator());
+ }
+ }
+ }
d->stdin_is_dev_null = false;
@@ -1460,6 +1504,16 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
ADDARGC("--pending");
break;
+ case Item::RemovePending:
+ ADDARGC("--remove");
+ ADDARGC("--pending");
+ break;
+
+ case Item::PurgePending:
+ ADDARGC("--purge");
+ ADDARGC("--pending");
+ break;
+
case Item::Install:
ADDARGC("--unpack");
ADDARGC("--auto-deconfigure");
@@ -1741,6 +1795,17 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
StopPtyMagic();
CloseLog();
+ if (d->dpkg_error.empty() == false)
+ {
+ APT::StateChanges undo;
+ auto && undoRem = approvedStates.Remove();
+ std::move(undoRem.begin(), undoRem.end(), std::back_inserter(undo.Remove()));
+ auto && undoPur = approvedStates.Purge();
+ std::move(undoPur.begin(), undoPur.end(), std::back_inserter(undo.Purge()));
+ approvedStates.clear();
+ if (undo.Save(false) == false)
+ _error->Error("Couldn't revert dpkg selection for approved remove/purge after an error was encountered!");
+ }
if (currentStates.Save(false) == false)
_error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
@@ -1991,20 +2056,24 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
}
// log the ordering, see dpkgpm.h and the "Ops" enum there
- const char *ops_str[] = {
- "Install",
- "Configure",
- "Remove",
- "Purge",
- "ConfigurePending",
- "TriggersPending",
- };
fprintf(report, "AptOrdering:\n");
- for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
- if ((*I).Pkg != NULL)
- fprintf(report, " %s: %s\n", (*I).Pkg.Name(), ops_str[(*I).Op]);
- else
- fprintf(report, " %s: %s\n", "NULL", ops_str[(*I).Op]);
+ for (auto && I : List)
+ {
+ char const * opstr = nullptr;
+ switch (I.Op)
+ {
+ case Item::Install: opstr = "Install"; break;
+ case Item::Configure: opstr = "Configure"; break;
+ case Item::Remove: opstr = "Remove"; break;
+ case Item::Purge: opstr = "Purge"; break;
+ case Item::ConfigurePending: opstr = "ConfigurePending"; break;
+ case Item::TriggersPending: opstr = "TriggersPending"; break;
+ case Item::RemovePending: opstr = "RemovePending"; break;
+ case Item::PurgePending: opstr = "PurgePending"; break;
+ }
+ auto const pkgname = I.Pkg.end() ? "NULL" : I.Pkg.FullName();
+ fprintf(report, " %s: %s\n", pkgname.c_str(), opstr);
+ }
// attach dmesg log (to learn about segfaults)
if (FileExists("/bin/dmesg"))
diff --git a/apt-pkg/deb/dpkgpm.h b/apt-pkg/deb/dpkgpm.h
index 408a37334..07c99aead 100644
--- a/apt-pkg/deb/dpkgpm.h
+++ b/apt-pkg/deb/dpkgpm.h
@@ -79,7 +79,8 @@ class pkgDPkgPM : public pkgPackageManager
struct Item
{
- enum Ops {Install, Configure, Remove, Purge, ConfigurePending, TriggersPending} Op;
+ enum Ops {Install, Configure, Remove, Purge, ConfigurePending, TriggersPending,
+ RemovePending, PurgePending } Op;
std::string File;
PkgIterator Pkg;
Item(Ops Op,PkgIterator Pkg,std::string File = "") : Op(Op),
diff --git a/doc/external-installation-planner-protocol.txt b/doc/external-installation-planner-protocol.txt
index 44fa8ff53..4bad9da0a 100644
--- a/doc/external-installation-planner-protocol.txt
+++ b/doc/external-installation-planner-protocol.txt
@@ -248,7 +248,9 @@ before it was unpacked, dependency relations must be satisfied, …), but
they don't need to be complete: A planner can and should expect that any
package which wasn't explicitly configured will be configured at the end
automatically. That also means through that a planner is not allowed to
-produce a solution in which a package remains unconfigured.
+produce a solution in which a package remains unconfigured. Also,
+packages which are requested to be removed will be automatically removed
+at the end if not marked for removal explicitly earlier.
In terms of expressivity, all stanzas can carry one single field each, as
APT-IDs are enough to pinpoint packages to be installed/removed.
diff --git a/test/integration/test-bug-673536-pre-depends-breaks-loop b/test/integration/test-bug-673536-pre-depends-breaks-loop
index bf3d2e2eb..a4cf66973 100755
--- a/test/integration/test-bug-673536-pre-depends-breaks-loop
+++ b/test/integration/test-bug-673536-pre-depends-breaks-loop
@@ -11,7 +11,7 @@ buildsimplenativepackage 'advanced' 'native' '2' 'unstable' 'Pre-Depends: basic'
buildsimplenativepackage 'basic' 'native' '2' 'unstable' 'Pre-Depends: common'
buildsimplenativepackage 'common' 'native' '2~conflict' 'unstable-conflict' 'Conflicts: advanced (<= 1)'
-buildsimplenativepackage 'common' 'native' '2~break' 'unstable-break' 'Conflicts: advanced (<= 1)'
+buildsimplenativepackage 'common' 'native' '2~break' 'unstable-break' 'Breaks: advanced (<= 1)'
setupaptarchive