From e7d4e0cf4f6d79810e4b4e7de505729e759213dd Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Fri, 8 Jul 2016 09:40:46 +0200 Subject: [PATCH 1/1] 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. --- apt-pkg/deb/dpkgpm.cc | 95 ++++++++++++++++--- apt-pkg/deb/dpkgpm.h | 3 +- ...external-installation-planner-protocol.txt | 4 +- .../test-bug-673536-pre-depends-breaks-loop | 2 +- 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 toBeRemoved(Cache.Head().PackageCount, false); + std::vector 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::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 -- 2.45.2