From d3930f8716f439c229cd3d11813823d847a2ecff Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 3 Jul 2016 13:57:25 +0200 Subject: pass --force-remove-essential to dpkg only if needed APT (usually) knows which package is essential or not, so we can avoid passing this force flag to dpkg unconditionally if the user hasn't chosen a non-default essential handling obscuring the information. --- apt-pkg/deb/dpkgpm.cc | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 3d0fd622c..8938cb3d4 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1233,6 +1233,15 @@ void pkgDPkgPM::StopPtyMagic() /*{{{*/ */ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { + auto const ItemIsEssential = [](pkgDPkgPM::Item const &I) { + static auto const cachegen = _config->Find("pkgCacheGen::Essential"); + if (cachegen == "none" || cachegen == "native") + return true; + if (unlikely(I.Pkg.end())) + return true; + return (I.Pkg->Flags & pkgCache::Flag::Essential) != 0; + }; + pkgPackageManager::SigINTStop = false; d->progress = progress; @@ -1350,13 +1359,15 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { case Item::Remove: ADDARGC("--force-depends"); - ADDARGC("--force-remove-essential"); + if (std::any_of(I, J, ItemIsEssential)) + ADDARGC("--force-remove-essential"); ADDARGC("--remove"); break; case Item::Purge: ADDARGC("--force-depends"); - ADDARGC("--force-remove-essential"); + if (std::any_of(I, J, ItemIsEssential)) + ADDARGC("--force-remove-essential"); ADDARGC("--purge"); break; -- cgit v1.2.3 From f495992428a396e0f98886c9a761a804aa161c68 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Thu, 9 Jun 2016 11:48:16 +0200 Subject: use dpkg --unpack --recursive to avoid long cmdlines Having long commandlines split into two is a huge problem if it happens and additionally if we want to introduce planners which perform less micromanagment its a good idea to leave the details for dpkg to decide. In practice this doesn't work yet unconditionally as a bug is hiding in the ordering code of dpkg, but it works if apt imposes its ordering so this commit allows for now at least to solve the first problem. --- apt-pkg/deb/dpkgpm.cc | 102 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 95 insertions(+), 7 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 8938cb3d4..c82a09b09 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -36,6 +37,8 @@ #include #include #include +#include +#include #include #include #include @@ -1221,6 +1224,34 @@ void pkgDPkgPM::StopPtyMagic() /*{{{*/ d->master = -1; } } + /*}}}*/ +static void cleanUpTmpDir(char * const tmpdir) /*{{{*/ +{ + if (tmpdir == nullptr) + return; + DIR * const D = opendir(tmpdir); + if (D == nullptr) + _error->Errno("opendir", _("Unable to read %s"), tmpdir); + else + { + auto const dfd = dirfd(D); + for (struct dirent *Ent = readdir(D); Ent != nullptr; Ent = readdir(D)) + { + if (Ent->d_name[0] == '.') + continue; +#ifdef _DIRENT_HAVE_D_TYPE + if (unlikely(Ent->d_type != DT_LNK && Ent->d_type != DT_UNKNOWN)) + continue; +#endif + if (unlikely(unlinkat(dfd, Ent->d_name, 0) != 0)) + break; + } + closedir(D); + rmdir(tmpdir); + } + free(tmpdir); +} + /*}}}*/ // DPkgPM::Go - Run the sequence /*{{{*/ // --------------------------------------------------------------------- @@ -1276,6 +1307,19 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) if (noopDPkgInvocation == false) Cache.writeStateFile(NULL); + bool dpkg_recursive_install = _config->FindB("dpkg::install::recursive", false); + if (_config->FindB("dpkg::install::recursive::force", false) == false) + { + // dpkg uses a sorted treewalk since that version which enables the workaround to work + auto const dpkgpkg = Cache.FindPkg("dpkg"); + if (likely(dpkgpkg.end() == false && dpkgpkg->CurrentVer != 0)) + dpkg_recursive_install = Cache.VS().CmpVersion("1.18.5", dpkgpkg.CurrentVer().VerStr()) <= 0; + } + // no point in doing this dance for a handful of packages only + unsigned int const dpkg_recursive_install_min = _config->FindB("dpkg::install::recursive::minimum", 5); + // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test + bool const dpkg_recursive_install_numbered = _config->FindB("dpkg::install::recursive::numbered", true); + decltype(List)::const_iterator::difference_type const notconfidx = _config->FindB("Dpkg::ExplicitLastConfigure", false) ? std::numeric_limits::max() : std::distance(List.cbegin(), std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }).base()); @@ -1396,17 +1440,47 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { ADDARGC("--no-triggers"); } -#undef ADDARGC + char * tmpdir_to_free = nullptr; // Write in the file or package names if (I->Op == Item::Install) { - for (;I != J && Size < MaxArgBytes; ++I) + auto const installsToDo = J - I; + if (dpkg_recursive_install == true && dpkg_recursive_install_min < installsToDo) { - if (I->File[0] != '/') - return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str()); - Args.push_back(I->File.c_str()); - Size += I->File.length(); + std::string tmpdir; + strprintf(tmpdir, "%s/apt-dpkg-install-XXXXXX", GetTempDir().c_str()); + tmpdir_to_free = strndup(tmpdir.data(), tmpdir.length()); + if (mkdtemp(tmpdir_to_free) == nullptr) + return _error->Errno("DPkg::Go", "mkdtemp of %s failed in preparation of calling dpkg unpack", tmpdir_to_free); + + char p = 1; + for (auto c = installsToDo - 1; (c = c/10) != 0; ++p); + for (unsigned long n = 0; I != J; ++n, ++I) + { + if (I->File[0] != '/') + return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str()); + auto const file = flNotDir(I->File); + std::string linkpath; + if (dpkg_recursive_install_numbered) + strprintf(linkpath, "%s/%.*lu-%s", tmpdir_to_free, p, n, file.c_str()); + else + strprintf(linkpath, "%s/%s", tmpdir_to_free, file.c_str()); + if (symlink(I->File.c_str(), linkpath.c_str()) != 0) + return _error->Errno("DPkg::Go", "Symlinking %s to %s failed!", I->File.c_str(), linkpath.c_str()); + } + ADDARGC("--recursive"); + ADDARG(tmpdir_to_free); + } + else + { + for (;I != J && Size < MaxArgBytes; ++I) + { + if (I->File[0] != '/') + return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str()); + Args.push_back(I->File.c_str()); + Size += I->File.length(); + } } } else @@ -1454,6 +1528,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) if (oldSize == Size) continue; } +#undef ADDARGC #undef ADDARG J = I; @@ -1470,6 +1545,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) Packages.clear(); close(fd[0]); close(fd[1]); + cleanUpTmpDir(tmpdir_to_free); continue; } Args.push_back(NULL); @@ -1605,6 +1681,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) signal(SIGINT,old_SIGINT); signal(SIGHUP,old_SIGHUP); + cleanUpTmpDir(tmpdir_to_free); + if (waitpid_failure == true) { strprintf(d->dpkg_error, "Sub-process %s couldn't be waited for.",Args[0]); @@ -1775,7 +1853,17 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) // find the package version and source package name pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname); if (Pkg.end() == true) - return; + { + if (pos == std::string::npos || _config->FindB("dpkg::install::recursive::numbered", true) == false) + return; + auto const dash = pkgname.find_first_not_of("0123456789"); + if (dash == std::string::npos || pkgname[dash] != '-') + return; + pkgname.erase(0, dash + 1); + Pkg = Cache.FindPkg(pkgname); + if (Pkg.end() == true) + return; + } pkgCache::VerIterator Ver = Cache.GetCandidateVersion(Pkg); if (Ver.end() == true) return; -- cgit v1.2.3 From b820fd59c4fe6e3581901eee648e88209be56137 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 3 Jul 2016 14:39:16 +0200 Subject: save and restore selection states before/after calling dpkg dpkg decides certain things on its own based on selections and especially if we want to call --pending on purge/remove actions, we need to ensure a clean slate or otherwise we surprise the user by removing packages we weren't allowed to remove by the user in this run (the selection might be an overarching plan for the not-yet "future"). Ideally dpkg would have some kind of temporal selection interface for this case, but it hasn't, so we make it temporal with the risk of loosing state if we don't manage to restore them. --- apt-pkg/deb/dpkgpm.cc | 42 ++++++++++++++++++++++++++++++++++++------ apt-pkg/statechanges.cc | 3 ++- 2 files changed, 38 insertions(+), 7 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index c82a09b09..38285d14b 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -208,6 +209,14 @@ pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg) return Ver; } /*}}}*/ +static pkgCache::VerIterator FindToBeRemovedVersion(pkgCache::PkgIterator const &Pkg)/*{{{*/ +{ + auto const PV = Pkg.CurrentVer(); + if (PV.end() == false) + return PV; + return FindNowVersion(Pkg); +} + /*}}}*/ // DPkgPM::pkgDPkgPM - Constructor /*{{{*/ // --------------------------------------------------------------------- @@ -1342,6 +1351,28 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) List.erase(std::next(List.begin(), notconfidx), List.end()); } + APT::StateChanges currentStates; + if (_config->FindB("dpkg::selection::current::saveandrestore", true)) + { + for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg) + if (Pkg->CurrentVer == 0) + continue; + else if (Pkg->SelectedState == pkgCache::State::Purge) + currentStates.Purge(FindToBeRemovedVersion(Pkg)); + else if (Pkg->SelectedState == pkgCache::State::DeInstall) + currentStates.Remove(FindToBeRemovedVersion(Pkg)); + if (currentStates.empty() == false) + { + APT::StateChanges cleanStates; + for (auto && P: currentStates.Remove()) + cleanStates.Install(P); + for (auto && P: currentStates.Purge()) + cleanStates.Install(P); + if (cleanStates.Save(false) == false) + return _error->Error("Couldn't clean the currently selected dpkg states"); + } + } + d->stdin_is_dev_null = false; // create log @@ -1505,12 +1536,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { pkgCache::VerIterator PkgVer; std::string name = I->Pkg.Name(); - if (Op == Item::Remove || Op == Item::Purge) - { - PkgVer = I->Pkg.CurrentVer(); - if(PkgVer.end() == true) - PkgVer = FindNowVersion(I->Pkg); - } + if (Op == Item::Remove || Op == Item::Purge) + PkgVer = FindToBeRemovedVersion(I->Pkg); else PkgVer = Cache[I->Pkg].InstVerIter(Cache); if (strcmp(I->Pkg.Arch(), "none") == 0) @@ -1714,6 +1741,9 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) StopPtyMagic(); CloseLog(); + if (currentStates.Save(false) == false) + _error->Error("Couldn't restore dpkg selection states which were present before this interaction!"); + if (pkgPackageManager::SigINTStop) _error->Warning(_("Operation was interrupted before it could finish")); diff --git a/apt-pkg/statechanges.cc b/apt-pkg/statechanges.cc index ed8f9f524..35af45538 100644 --- a/apt-pkg/statechanges.cc +++ b/apt-pkg/statechanges.cc @@ -25,7 +25,8 @@ public: #define APT_GETTERSETTER(Name, Container) \ void StateChanges::Name(pkgCache::VerIterator const &Ver) \ { \ - Container.push_back(Ver); \ + if (Ver.end() == false) \ + Container.push_back(Ver); \ }\ APT::VersionVector& StateChanges::Name() \ { \ -- cgit v1.2.3 From e7d4e0cf4f6d79810e4b4e7de505729e759213dd Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Fri, 8 Jul 2016 09:40:46 +0200 Subject: 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 +- 2 files changed, 84 insertions(+), 14 deletions(-) (limited to 'apt-pkg') 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), -- cgit v1.2.3 From 0dd19915f44df0048d594d527797c62ab6195cc6 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Thu, 21 Jul 2016 16:33:01 +0200 Subject: call dpkg with --no-triggers by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented a long while ago now with relatively good progress reporting involving triggers is a good time to try delaying the execution of triggers across dpkg invocations finally by default. Note: The bugreport talks also about 'smarter' configuration which is a much bigger part and approached from multiple directions, but doesn't really involve triggers per-se so considering it decoupled should help in getting it done… Closes: #626599 --- apt-pkg/deb/dpkgpm.cc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index bc34e50e0..e4f9550b0 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1303,7 +1303,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) OSArgMax = 32*1024; OSArgMax -= EnvironmentSize() - 2*1024; unsigned int const MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes", OSArgMax); - bool const NoTriggers = _config->FindB("DPkg::NoTriggers", false); + bool const NoTriggers = _config->FindB("DPkg::NoTriggers", true); if (RunScripts("DPkg::Pre-Invoke") == false) return false; @@ -1474,6 +1474,12 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) ADDARG(status_fd_buf); unsigned long const Op = I->Op; + if (NoTriggers == true && I->Op != Item::TriggersPending && + I->Op != Item::ConfigurePending) + { + ADDARGC("--no-triggers"); + } + switch (I->Op) { case Item::Remove: @@ -1520,11 +1526,6 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) break; } - if (NoTriggers == true && I->Op != Item::TriggersPending && - I->Op != Item::ConfigurePending) - { - ADDARGC("--no-triggers"); - } char * tmpdir_to_free = nullptr; // Write in the file or package names -- cgit v1.2.3 From 7ec343309b7bc6001b465c870609b3c570026149 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Thu, 21 Jul 2016 18:46:34 +0200 Subject: don't purge directly, but remove and do purge at the end If we want a package to be purged from the system tell dpkg in the ordering (if it has to touch it explicitly) to remove it and cover the purging of the config files at the end with a --purge --pending call. That should help packages move conffiles around between packages correctly even if the user is purging packages directly in big actions like dist-upgrades involving many packages. --- apt-pkg/deb/dpkgpm.cc | 147 +++++++++++++++++++++++++++++--------------------- 1 file changed, 86 insertions(+), 61 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index e4f9550b0..b47988d6c 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -1273,6 +1274,15 @@ static void cleanUpTmpDir(char * const tmpdir) /*{{{*/ */ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { + // we remove the last configures (and after that removes) from the list here + // as they will be covered by the pending calls, so explicit calls are busy work + decltype(List)::const_iterator::difference_type const explicitIdx = + std::distance(List.cbegin(), + _config->FindB("Dpkg::ExplicitLastConfigure", false) ? List.cend() : + std::find_if_not( + std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }), + List.crend(), [](Item const &i) { return i.Op == Item::Remove || i.Op == Item::Purge; }).base()); + auto const ItemIsEssential = [](pkgDPkgPM::Item const &I) { static auto const cachegen = _config->Find("pkgCacheGen::Essential"); if (cachegen == "none" || cachegen == "native") @@ -1282,6 +1292,13 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) return (I.Pkg->Flags & pkgCache::Flag::Essential) != 0; }; + auto const StripAlreadyDoneFromPending = [&](APT::VersionVector & Pending) { + Pending.erase(std::remove_if(Pending.begin(), Pending.end(), [&](pkgCache::VerIterator const &Ver) { + auto const PN = Ver.ParentPkg().FullName(); + return PackageOps[PN].size() <= PackageOpsDone[PN]; + }), Pending.end()); + }; + pkgPackageManager::SigINTStop = false; d->progress = progress; @@ -1329,28 +1346,9 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test bool const dpkg_recursive_install_numbered = _config->FindB("dpkg::install::recursive::numbered", true); - decltype(List)::const_iterator::difference_type const notconfidx = - _config->FindB("Dpkg::ExplicitLastConfigure", false) ? std::numeric_limits::max() : - std::distance(List.cbegin(), std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }).base()); - - // support subpressing of triggers processing for special - // cases like d-i that runs the triggers handling manually - bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false); - bool const ConfigurePending = _config->FindB("DPkg::ConfigurePending", true); - if (ConfigurePending) - List.push_back(Item(Item::ConfigurePending, PkgIterator())); - // for the progress BuildPackagesProgressMap(); - if (notconfidx != std::numeric_limits::max()) - { - if (ConfigurePending) - List.erase(std::next(List.begin(), notconfidx), std::prev(List.end())); - else - List.erase(std::next(List.begin(), notconfidx), List.end()); - } - APT::StateChanges currentStates; if (_config->FindB("dpkg::selection::current::saveandrestore", true)) { @@ -1388,34 +1386,29 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) 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()); - } - } + List.erase(std::next(List.begin(), explicitIdx), List.end()); + + std::vector toBeRemoved(Cache.Head().PackageCount, false); + for (auto && I: approvedStates.Remove()) + toBeRemoved[I.ParentPkg()->ID] = true; + for (auto && I: approvedStates.Purge()) + toBeRemoved[I.ParentPkg()->ID] = true; + + for (auto && I: List) + if (I.Op == Item::Remove || I.Op == Item::Purge) + toBeRemoved[I.Pkg->ID] = false; + + if (std::find(toBeRemoved.begin(), toBeRemoved.end(), true) != toBeRemoved.end()) + List.emplace_back(Item::RemovePending, pkgCache::PkgIterator()); + if (approvedStates.Purge().empty() == false) + List.emplace_back(Item::PurgePending, pkgCache::PkgIterator()); + + // support subpressing of triggers processing for special + // cases like d-i that runs the triggers handling manually + if (_config->FindB("DPkg::ConfigurePending", true)) + List.emplace_back(Item::ConfigurePending, pkgCache::PkgIterator()); } + bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false); d->stdin_is_dev_null = false; @@ -1431,7 +1424,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) d->progress->Start(d->master); // this loop is runs once per dpkg operation - vector::const_iterator I = List.begin(); + vector::const_iterator I = List.cbegin(); while (I != List.end()) { // Do all actions with the same Op in one run @@ -1448,9 +1441,10 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) continue; break; } + else if (J->Op == Item::Remove || J->Op == Item::Purge) + J = std::find_if(J, List.cend(), [](Item const &I) { return I.Op != Item::Remove && I.Op != Item::Purge; }); else - for (; J != List.end() && J->Op == I->Op; ++J) - /* nothing */; + J = std::find_if(J, List.cend(), [&J](Item const &I) { return I.Op != J->Op; }); auto const size = (J - I) + 10; @@ -1483,17 +1477,11 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) switch (I->Op) { case Item::Remove: - ADDARGC("--force-depends"); - if (std::any_of(I, J, ItemIsEssential)) - ADDARGC("--force-remove-essential"); - ADDARGC("--remove"); - break; - case Item::Purge: ADDARGC("--force-depends"); if (std::any_of(I, J, ItemIsEssential)) ADDARGC("--force-remove-essential"); - ADDARGC("--purge"); + ADDARGC("--remove"); break; case Item::Configure: @@ -1569,10 +1557,37 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) } } } + else if (I->Op == Item::RemovePending) + { + ++I; + StripAlreadyDoneFromPending(approvedStates.Remove()); + if (approvedStates.Remove().empty()) + continue; + } + else if (I->Op == Item::PurgePending) + { + ++I; + // explicit removes of packages without conffiles passthrough the purge states instantly, too. + // Setting these non-installed packages up for purging generates 'unknown pkg' warnings from dpkg + StripAlreadyDoneFromPending(approvedStates.Purge()); + if (approvedStates.Purge().empty()) + continue; + std::remove_reference::type approvedRemoves; + std::swap(approvedRemoves, approvedStates.Remove()); + // we apply it again here as an explicit remove in the ordering will have cleared the purge state + if (approvedStates.Save(false) == false) + { + _error->Error("Couldn't record the approved purges 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::swap(approvedRemoves, approvedStates.Remove()); + } else { string const nativeArch = _config->Find("APT::Architecture"); - unsigned long const oldSize = I->Op == Item::Configure ? Size : 0; + unsigned long const oldSize = I->Pkg.end() == false ? Size : 0; for (;I != J && Size < MaxArgBytes; ++I) { if((*I).Pkg.end() == true) @@ -1591,8 +1606,15 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { pkgCache::VerIterator PkgVer; std::string name = I->Pkg.Name(); - if (Op == Item::Remove || Op == Item::Purge) - PkgVer = FindToBeRemovedVersion(I->Pkg); + if (Op == Item::Remove) + PkgVer = I->Pkg.CurrentVer(); + else if (Op == Item::Purge) + { + // we purge later with --purge --pending, so if it isn't installed (aka rc-only), skip it here + PkgVer = I->Pkg.CurrentVer(); + if (PkgVer.end() == true) + continue; + } else PkgVer = Cache[I->Pkg].InstVerIter(Cache); if (strcmp(I->Pkg.Arch(), "none") == 0) @@ -1798,11 +1820,14 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) if (d->dpkg_error.empty() == false) { + // no point in reseting packages we already completed removal for + StripAlreadyDoneFromPending(approvedStates.Remove()); + StripAlreadyDoneFromPending(approvedStates.Purge()); APT::StateChanges undo; auto && undoRem = approvedStates.Remove(); - std::move(undoRem.begin(), undoRem.end(), std::back_inserter(undo.Remove())); + std::move(undoRem.begin(), undoRem.end(), std::back_inserter(undo.Install())); auto && undoPur = approvedStates.Purge(); - std::move(undoPur.begin(), undoPur.end(), std::back_inserter(undo.Purge())); + std::move(undoPur.begin(), undoPur.end(), std::back_inserter(undo.Install())); approvedStates.clear(); if (undo.Save(false) == false) _error->Error("Couldn't revert dpkg selection for approved remove/purge after an error was encountered!"); -- cgit v1.2.3 From 9ffbac99e52c91182ed8ff8678a994626b194e69 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 24 Jul 2016 11:52:04 +0200 Subject: ensure all configures are reported to hook scripts A planner might not explicitly configure all packages, but we need to know all packages which will be configured for progress reporting and to tell the hook scripts about them as they rely on this for their own functionality. --- apt-pkg/deb/dpkgpm.cc | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index b47988d6c..9d48401dc 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -1283,6 +1284,22 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }), List.crend(), [](Item const &i) { return i.Op == Item::Remove || i.Op == Item::Purge; }).base()); + // explicitely configure everything for hookscripts and progress building + { + std::unordered_set alreadyConfigured; + for (auto && I : List) + if (I.Op == Item::Configure) + alreadyConfigured.insert(I.Pkg->ID); + decltype(List) AppendList; + for (auto && I : List) + if (I.Op == Item::Install && alreadyConfigured.insert(I.Pkg->ID).second == true) + AppendList.emplace_back(Item::Configure, I.Pkg); + for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg) + if (Pkg.State() == pkgCache::PkgIterator::NeedsConfigure && alreadyConfigured.insert(Pkg->ID).second == true) + AppendList.emplace_back(Item::Configure, Pkg); + std::move(AppendList.begin(), AppendList.end(), std::back_inserter(List)); + } + auto const ItemIsEssential = [](pkgDPkgPM::Item const &I) { static auto const cachegen = _config->Find("pkgCacheGen::Essential"); if (cachegen == "none" || cachegen == "native") -- cgit v1.2.3 From bfc0933abedcd57e0a5bd07e282f3a50ba1fa5b2 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Sun, 24 Jul 2016 19:00:08 +0200 Subject: ensure all removes are reported to hook scripts Same reason and implementation as for configure. --- apt-pkg/deb/dpkgpm.cc | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 9d48401dc..025dfbfd3 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1284,6 +1284,19 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }), List.crend(), [](Item const &i) { return i.Op == Item::Remove || i.Op == Item::Purge; }).base()); + // explicitely remove everything for hookscripts and progress building + { + std::unordered_set alreadyRemoved; + for (auto && I : List) + if (I.Op == Item::Remove || I.Op == Item::Purge) + alreadyRemoved.insert(I.Pkg->ID); + decltype(List) AppendList; + for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg) + if (Cache[Pkg].Delete() && alreadyRemoved.insert(Pkg->ID).second == true) + AppendList.emplace_back(Cache[Pkg].Purge() ? Item::Purge : Item::Remove, Pkg); + std::move(AppendList.begin(), AppendList.end(), std::back_inserter(List)); + } + // explicitely configure everything for hookscripts and progress building { std::unordered_set alreadyConfigured; -- cgit v1.2.3 From 83e5cffc2015aa809acac84737756d292d7bf106 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Mon, 25 Jul 2016 16:36:53 +0200 Subject: try to avoid removal of crossgraded packages The user has to approve the removal of a crossgraded package as it might be needed to remove it (temporarily) in the process, but in most cases we can happily avoid it and let dpkg unpack over it skipping the remove. This has some effects on progress reporting and how deal with selections through which makes this a tiny bit complicated. --- apt-pkg/deb/dpkgpm.cc | 166 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 140 insertions(+), 26 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 025dfbfd3..d14155d01 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -670,27 +670,23 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) // if there are multiple pkgs dpkg would send us a full pkgname:arch pkgCache::GrpIterator Grp = Cache.FindGrp(pkgname); if (Grp.end() == false) - { - pkgCache::PkgIterator P = Grp.PackageList(); - for (; P.end() != true; P = Grp.NextPkg(P)) - { + for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P)) if(Cache[P].Keep() == false || Cache[P].ReInstall() == true) { - pkgname = P.FullName(); + auto fullname = P.FullName(); + if (Cache[P].Delete() && PackageOps[fullname].size() <= PackageOpsDone[fullname]) + continue; + pkgname = std::move(fullname); break; } - } - } } - const char* const pkg = pkgname.c_str(); - std::string short_pkgname = StringSplit(pkgname, ":")[0]; std::string arch = ""; if (pkgname.find(":") != string::npos) arch = StringSplit(pkgname, ":")[1]; std::string i18n_pkgname = pkgname; if (arch.size() != 0) - strprintf(i18n_pkgname, "%s (%s)", short_pkgname.c_str(), arch.c_str()); + strprintf(i18n_pkgname, "%s (%s)", StringSplit(pkgname, ":")[0].c_str(), arch.c_str()); // 'processing' from dpkg looks like // 'processing: action: pkg' @@ -720,21 +716,21 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) if (prefix == "status") { - std::vector &states = PackageOps[pkg]; + std::vector &states = PackageOps[pkgname]; if (action == "triggers-pending") { if (Debug == true) - std::clog << "(parsed from dpkg) pkg: " << short_pkgname + std::clog << "(parsed from dpkg) pkg: " << pkgname << " action: " << action << " (prefix 2 to " - << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl; + << PackageOpsDone[pkgname] << " of " << states.size() << ")" << endl; states.insert(states.begin(), {"installed", N_("Installed %s")}); states.insert(states.begin(), {"half-configured", N_("Configuring %s")}); PackagesTotal += 2; } - else if(PackageOpsDone[pkg] < states.size()) + else if(PackageOpsDone[pkgname] < states.size()) { - char const * next_action = states[PackageOpsDone[pkg]].state; + char const * next_action = states[PackageOpsDone[pkgname]].state; if (next_action) { /* @@ -751,24 +747,52 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) } */ if (Debug == true) - std::clog << "(parsed from dpkg) pkg: " << short_pkgname + std::clog << "(parsed from dpkg) pkg: " << pkgname << " action: " << action << " (expected: '" << next_action << "' " - << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl; + << PackageOpsDone[pkgname] << " of " << states.size() << ")" << endl; // check if the package moved to the next dpkg state if(action == next_action) { // only read the translation if there is actually a next action - char const * const translation = _(states[PackageOpsDone[pkg]].str); + char const * const translation = _(states[PackageOpsDone[pkgname]].str); // we moved from one dpkg state to a new one, report that - ++PackageOpsDone[pkg]; + ++PackageOpsDone[pkgname]; ++PackagesDone; std::string msg; strprintf(msg, translation, i18n_pkgname.c_str()); d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg); } + else if (action == "unpacked" && strcmp(next_action, "config-files") == 0) + { + // in a crossgrade what looked like a remove first is really an unpack over it + ++PackageOpsDone[pkgname]; + ++PackagesDone; + + auto const Pkg = Cache.FindPkg(pkgname); + if (likely(Pkg.end() == false)) + { + auto const Grp = Pkg.Group(); + if (likely(Grp.end() == false)) + { + for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P)) + if(Cache[P].Install()) + { + auto && Ops = PackageOps[P.FullName()]; + auto const unpackOp = std::find_if(Ops.cbegin(), Ops.cend(), [](DpkgState const &s) { return strcmp(s.state, "unpacked") == 0; }); + if (unpackOp != Ops.cend()) + { + auto const skipped = std::distance(Ops.cbegin(), unpackOp); + PackagesDone += skipped; + PackageOpsDone[P.FullName()] += skipped; + break; + } + } + } + } + } } } } @@ -1277,7 +1301,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { // we remove the last configures (and after that removes) from the list here // as they will be covered by the pending calls, so explicit calls are busy work - decltype(List)::const_iterator::difference_type const explicitIdx = + decltype(List)::const_iterator::difference_type explicitIdx = std::distance(List.cbegin(), _config->FindB("Dpkg::ExplicitLastConfigure", false) ? List.cend() : std::find_if_not( @@ -1379,6 +1403,101 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) // for the progress BuildPackagesProgressMap(); + APT::StateChanges approvedStates; + if (_config->FindB("dpkg::selection::remove::approved", true)) + { + for (auto && I : List) + if (I.Op == Item::Purge) + approvedStates.Purge(FindToBeRemovedVersion(I.Pkg)); + else if (I.Op == Item::Remove) + approvedStates.Remove(FindToBeRemovedVersion(I.Pkg)); + } + + // Skip removes if we install another architecture of this package soon (crossgrade) + // We can't just skip them all the time as it could be an ordering requirement [of another package] + if ((approvedStates.Remove().empty() == false || approvedStates.Purge().empty() == false) && + _config->FindB("dpkg::remove::crossgrade::implicit", true) == true) + { + std::unordered_set crossgraded; + std::vector> toCrossgrade; + auto const PlanedEnd = std::next(List.begin(), explicitIdx); + for (auto I = List.begin(); I != PlanedEnd; ++I) + { + if (I->Op != Item::Remove && I->Op != Item::Purge) + continue; + + auto const Grp = I->Pkg.Group(); + size_t installedInstances = 0; + for (auto Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg)) + if (Pkg->CurrentVer != 0 || Cache[Pkg].Install()) + ++installedInstances; + if (installedInstances == 2) + { + auto const FirstInstall = std::find_if_not(I, List.end(), + [](Item const &i) { return i.Op == Item::Remove || i.Op == Item::Purge; }); + auto const LastInstall = std::find_if_not(FirstInstall, List.end(), + [](Item const &i) { return i.Op == Item::Install; }); + auto const crosser = std::find_if(FirstInstall, LastInstall, + [&I](Item const &i) { return i.Pkg->Group == I->Pkg->Group; }); + if (crosser != LastInstall) + { + crossgraded.insert(I->Pkg->ID); + toCrossgrade.emplace_back(&(*I), crosser->Pkg.FullName()); + } + } + } + for (auto I = PlanedEnd; I != List.end(); ++I) + { + if (I->Op != Item::Remove && I->Op != Item::Purge) + continue; + + auto const Grp = I->Pkg.Group(); + for (auto Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg)) + { + if (Pkg == I->Pkg || Cache[Pkg].Install() == false) + continue; + toCrossgrade.emplace_back(&(*I), Pkg.FullName()); + break; + } + } + for (auto C : toCrossgrade) + { + // we never do purges on packages which are crossgraded, even if "requested" + if (C.first->Op == Item::Purge) + { + C.first->Op = Item::Remove; // crossgrades should never be purged + auto && Purges = approvedStates.Purge(); + auto const Ver = std::find_if( +#if __GNUC__ >= 5 || (__GNUC_MINOR__ >= 9 && __GNUC__ >= 4) + Purges.cbegin(), Purges.cend(), +#else + Purges.begin(), Purges.end(), +#endif + [&C](pkgCache::VerIterator const &V) { return V.ParentPkg() == C.first->Pkg; }); + approvedStates.Remove(*Ver); + Purges.erase(Ver); + auto && RemOp = PackageOps[C.first->Pkg.FullName()]; + if (RemOp.size() == 5) + { + RemOp.erase(std::next(RemOp.begin(), 3), RemOp.end()); + PackagesTotal -= 2; + } + else + _error->Warning("Unexpected amount of planned ops for package %s: %lu", C.first->Pkg.FullName().c_str(), RemOp.size()); + } + } + if (crossgraded.empty() == false) + { + auto const oldsize = List.size(); + List.erase(std::remove_if(List.begin(), PlanedEnd, + [&crossgraded](Item const &i){ + return (i.Op == Item::Remove || i.Op == Item::Purge) && + crossgraded.find(i.Pkg->ID) != crossgraded.end(); + }), PlanedEnd); + explicitIdx -= (oldsize - List.size()); + } + } + APT::StateChanges currentStates; if (_config->FindB("dpkg::selection::current::saveandrestore", true)) { @@ -1400,14 +1519,9 @@ 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"); -- cgit v1.2.3 From 4326680d2ed23d597f45ca8872a7054368560acc Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Thu, 28 Jul 2016 11:43:36 +0200 Subject: simulate all package manager actions explicitly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a planner lets actions to be figured out by dpkg in pending calls these actions aren't mentioned in a simulation. While that might be a good thing for debugging, it would be a change in behavior and especially if a planner avoids explicit removals could be confusing for users. As such we perform the same 'trick' as in the dpkg implementation by performing explicitly what would be done by the pending calls. To save us some work and avoid desyncs we perform a layer violation by using deb/ code in the generic simulation – and further we perform ugly dynamic_cast to avoid breaking the ABI for nothing; aptitude is the only other user of the simulation class according to codesearch.d.n and for that our little trick works. It just isn't working if you happen to extend pkgSimulate or otherwise manage to call the protected Go methods directly – which isn't very realistic/practical. --- apt-pkg/algorithms.cc | 65 +++++++++++++++++++++++++++++++++++++++++++++-- apt-pkg/algorithms.h | 10 +++++++- apt-pkg/deb/dpkgpm.cc | 49 ++++++++++++++++++----------------- apt-pkg/deb/dpkgpm.h | 9 ++++--- apt-pkg/packagemanager.cc | 7 ++++- apt-pkg/packagemanager.h | 2 +- 6 files changed, 111 insertions(+), 31 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/algorithms.cc b/apt-pkg/algorithms.cc index b173979c3..65c5ff85d 100644 --- a/apt-pkg/algorithms.cc +++ b/apt-pkg/algorithms.cc @@ -25,22 +25,29 @@ #include #include #include +#include #include #include #include #include +#include #include /*}}}*/ using namespace std; +class APT_HIDDEN pkgSimulatePrivate +{ +public: + std::vector List; +}; // Simulate::Simulate - Constructor /*{{{*/ // --------------------------------------------------------------------- /* The legacy translations here of input Pkg iterators is obsolete, this is not necessary since the pkgCaches are fully shared now. */ pkgSimulate::pkgSimulate(pkgDepCache *Cache) : pkgPackageManager(Cache), - d(NULL), iPolicy(Cache), + d(new pkgSimulatePrivate()), iPolicy(Cache), Sim(&Cache->GetCache(),&iPolicy), group(Sim) { @@ -58,6 +65,7 @@ pkgSimulate::pkgSimulate(pkgDepCache *Cache) : pkgPackageManager(Cache), pkgSimulate::~pkgSimulate() { delete[] Flags; + delete d; } /*}}}*/ // Simulate::Describe - Describe a package /*{{{*/ @@ -90,7 +98,14 @@ void pkgSimulate::Describe(PkgIterator Pkg,ostream &out,bool Current,bool Candid // Simulate::Install - Simulate unpacking of a package /*{{{*/ // --------------------------------------------------------------------- /* */ -bool pkgSimulate::Install(PkgIterator iPkg,string /*File*/) +bool pkgSimulate::Install(PkgIterator iPkg,string File) +{ + if (iPkg.end() || File.empty()) + return false; + d->List.emplace_back(pkgDPkgPM::Item::Install, iPkg, File); + return true; +} +bool pkgSimulate::RealInstall(PkgIterator iPkg,string /*File*/) { // Adapt the iterator PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch()); @@ -137,6 +152,13 @@ bool pkgSimulate::Install(PkgIterator iPkg,string /*File*/) install the package.. For some investigations it may be necessary however. */ bool pkgSimulate::Configure(PkgIterator iPkg) +{ + if (iPkg.end()) + return false; + d->List.emplace_back(pkgDPkgPM::Item::Configure, iPkg); + return true; +} +bool pkgSimulate::RealConfigure(PkgIterator iPkg) { // Adapt the iterator PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch()); @@ -187,6 +209,13 @@ bool pkgSimulate::Configure(PkgIterator iPkg) // --------------------------------------------------------------------- /* */ bool pkgSimulate::Remove(PkgIterator iPkg,bool Purge) +{ + if (iPkg.end()) + return false; + d->List.emplace_back(Purge ? pkgDPkgPM::Item::Purge : pkgDPkgPM::Item::Remove, iPkg); + return true; +} +bool pkgSimulate::RealRemove(PkgIterator iPkg,bool Purge) { // Adapt the iterator PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch()); @@ -232,6 +261,38 @@ void pkgSimulate::ShortBreaks() cout << ']' << endl; } /*}}}*/ +bool pkgSimulate::Go2(APT::Progress::PackageManager *) /*{{{*/ +{ + if (pkgDPkgPM::ExpandPendingCalls(d->List, Cache) == false) + return false; + for (auto && I : d->List) + switch (I.Op) + { + case pkgDPkgPM::Item::Install: + if (RealInstall(I.Pkg, I.File) == false) + return false; + break; + case pkgDPkgPM::Item::Configure: + if (RealConfigure(I.Pkg) == false) + return false; + break; + case pkgDPkgPM::Item::Remove: + if (RealRemove(I.Pkg, false) == false) + return false; + break; + case pkgDPkgPM::Item::Purge: + if (RealRemove(I.Pkg, true) == false) + return false; + break; + case pkgDPkgPM::Item::ConfigurePending: + case pkgDPkgPM::Item::TriggersPending: + case pkgDPkgPM::Item::RemovePending: + case pkgDPkgPM::Item::PurgePending: + return _error->Error("Internal error, simulation encountered unexpected pending item"); + } + return true; +} + /*}}}*/ // ApplyStatus - Adjust for non-ok packages /*{{{*/ // --------------------------------------------------------------------- /* We attempt to change the state of the all packages that have failed diff --git a/apt-pkg/algorithms.h b/apt-pkg/algorithms.h index c1a26587d..5148ff19d 100644 --- a/apt-pkg/algorithms.h +++ b/apt-pkg/algorithms.h @@ -52,9 +52,10 @@ using std::ostream; #endif +class pkgSimulatePrivate; class pkgSimulate : public pkgPackageManager /*{{{*/ { - void * const d; + pkgSimulatePrivate * const d; protected: class Policy : public pkgDepCache::Policy @@ -81,9 +82,16 @@ class pkgSimulate : public pkgPackageManager /*{{{*/ virtual bool Configure(PkgIterator Pkg) APT_OVERRIDE; virtual bool Remove(PkgIterator Pkg,bool Purge) APT_OVERRIDE; + // FIXME: trick to avoid ABI break for virtual reimplementation; fix on next ABI break +public: + APT_HIDDEN bool Go2(APT::Progress::PackageManager * progress); + private: APT_HIDDEN void ShortBreaks(); APT_HIDDEN void Describe(PkgIterator iPkg,std::ostream &out,bool Current,bool Candidate); + APT_HIDDEN bool RealInstall(PkgIterator Pkg,std::string File); + APT_HIDDEN bool RealConfigure(PkgIterator Pkg); + APT_HIDDEN bool RealRemove(PkgIterator Pkg,bool Purge); public: diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index d14155d01..4a49774f8 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1297,37 +1297,34 @@ static void cleanUpTmpDir(char * const tmpdir) /*{{{*/ * through to human readable (and i10n-able) * names and calculates a percentage for each step. */ -bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) +static bool ItemIsEssential(pkgDPkgPM::Item const &I) +{ + static auto const cachegen = _config->Find("pkgCacheGen::Essential"); + if (cachegen == "none" || cachegen == "native") + return true; + if (unlikely(I.Pkg.end())) + return true; + return (I.Pkg->Flags & pkgCache::Flag::Essential) != 0; +} +bool pkgDPkgPM::ExpandPendingCalls(std::vector &List, pkgDepCache &Cache) { - // we remove the last configures (and after that removes) from the list here - // as they will be covered by the pending calls, so explicit calls are busy work - decltype(List)::const_iterator::difference_type explicitIdx = - std::distance(List.cbegin(), - _config->FindB("Dpkg::ExplicitLastConfigure", false) ? List.cend() : - std::find_if_not( - std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }), - List.crend(), [](Item const &i) { return i.Op == Item::Remove || i.Op == Item::Purge; }).base()); - - // explicitely remove everything for hookscripts and progress building { std::unordered_set alreadyRemoved; for (auto && I : List) if (I.Op == Item::Remove || I.Op == Item::Purge) alreadyRemoved.insert(I.Pkg->ID); - decltype(List) AppendList; + std::remove_reference::type AppendList; for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg) if (Cache[Pkg].Delete() && alreadyRemoved.insert(Pkg->ID).second == true) AppendList.emplace_back(Cache[Pkg].Purge() ? Item::Purge : Item::Remove, Pkg); std::move(AppendList.begin(), AppendList.end(), std::back_inserter(List)); } - - // explicitely configure everything for hookscripts and progress building { std::unordered_set alreadyConfigured; for (auto && I : List) if (I.Op == Item::Configure) alreadyConfigured.insert(I.Pkg->ID); - decltype(List) AppendList; + std::remove_reference::type AppendList; for (auto && I : List) if (I.Op == Item::Install && alreadyConfigured.insert(I.Pkg->ID).second == true) AppendList.emplace_back(Item::Configure, I.Pkg); @@ -1336,15 +1333,21 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) AppendList.emplace_back(Item::Configure, Pkg); std::move(AppendList.begin(), AppendList.end(), std::back_inserter(List)); } + return true; +} +bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) +{ + // we remove the last configures (and after that removes) from the list here + // as they will be covered by the pending calls, so explicit calls are busy work + decltype(List)::const_iterator::difference_type explicitIdx = + std::distance(List.cbegin(), + _config->FindB("Dpkg::ExplicitLastConfigure", false) ? List.cend() : + std::find_if_not( + std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }), + List.crend(), [](Item const &i) { return i.Op == Item::Remove || i.Op == Item::Purge; }).base()); - auto const ItemIsEssential = [](pkgDPkgPM::Item const &I) { - static auto const cachegen = _config->Find("pkgCacheGen::Essential"); - if (cachegen == "none" || cachegen == "native") - return true; - if (unlikely(I.Pkg.end())) - return true; - return (I.Pkg->Flags & pkgCache::Flag::Essential) != 0; - }; + // explicitely remove&configure everything for hookscripts and progress building + ExpandPendingCalls(List, Cache); auto const StripAlreadyDoneFromPending = [&](APT::VersionVector & Pending) { Pending.erase(std::remove_if(Pending.begin(), Pending.end(), [&](pkgCache::VerIterator const &Ver) { diff --git a/apt-pkg/deb/dpkgpm.h b/apt-pkg/deb/dpkgpm.h index 07c99aead..193754644 100644 --- a/apt-pkg/deb/dpkgpm.h +++ b/apt-pkg/deb/dpkgpm.h @@ -76,7 +76,8 @@ class pkgDPkgPM : public pkgPackageManager // progress reporting unsigned int PackagesDone; unsigned int PackagesTotal; - + + public: struct Item { enum Ops {Install, Configure, Remove, Purge, ConfigurePending, TriggersPending, @@ -86,8 +87,8 @@ class pkgDPkgPM : public pkgPackageManager Item(Ops Op,PkgIterator Pkg,std::string File = "") : Op(Op), File(File), Pkg(Pkg) {}; Item() {}; - }; + protected: std::vector List; // Helpers @@ -126,7 +127,7 @@ class pkgDPkgPM : public pkgPackageManager virtual bool Remove(PkgIterator Pkg,bool Purge = false) APT_OVERRIDE; virtual bool Go(APT::Progress::PackageManager *progress) APT_OVERRIDE; - virtual bool Go(int StatusFd=-1) APT_OVERRIDE; + APT_DEPRECATED_MSG("Use overload with explicit progress manager") virtual bool Go(int StatusFd=-1) APT_OVERRIDE; virtual void Reset() APT_OVERRIDE; @@ -134,6 +135,8 @@ class pkgDPkgPM : public pkgPackageManager pkgDPkgPM(pkgDepCache *Cache); virtual ~pkgDPkgPM(); + + APT_HIDDEN static bool ExpandPendingCalls(std::vector &List, pkgDepCache &Cache); }; void SigINT(int sig); diff --git a/apt-pkg/packagemanager.cc b/apt-pkg/packagemanager.cc index 7fdd0393f..3d9c44555 100644 --- a/apt-pkg/packagemanager.cc +++ b/apt-pkg/packagemanager.cc @@ -1148,7 +1148,12 @@ pkgPackageManager::DoInstallPostFork(int statusFd) pkgPackageManager::OrderResult pkgPackageManager::DoInstallPostFork(APT::Progress::PackageManager *progress) { - bool goResult = Go(progress); + bool goResult; + auto simulation = dynamic_cast(this); + if (simulation == nullptr) + goResult = Go(progress); + else + goResult = simulation->Go2(progress); if(goResult == false) return Failed; diff --git a/apt-pkg/packagemanager.h b/apt-pkg/packagemanager.h index 25b6ee7c9..5046e2dc0 100644 --- a/apt-pkg/packagemanager.h +++ b/apt-pkg/packagemanager.h @@ -99,7 +99,7 @@ class pkgPackageManager : protected pkgCache::Namespace virtual bool Configure(PkgIterator /*Pkg*/) {return false;}; virtual bool Remove(PkgIterator /*Pkg*/,bool /*Purge*/=false) {return false;}; virtual bool Go(APT::Progress::PackageManager * /*progress*/) {return true;}; - virtual bool Go(int /*statusFd*/=-1) {return true;}; + APT_DEPRECATED_MSG("Use overload with explicit progress manager") virtual bool Go(int /*statusFd*/=-1) {return true;}; virtual void Reset() {}; -- cgit v1.2.3 From 28557f94578602f9ce0011501a2259bd98ab0688 Mon Sep 17 00:00:00 2001 From: David Kalnischkies Date: Thu, 28 Jul 2016 09:13:24 +0200 Subject: disable explicit configuration of all packages at the end With b4450f1dd6bca537e60406b2383ab154a3e1485f we dropped what we calculated here later on and now that we don't need it in the meantime either we can just skip the busy work by default and expect dpkg to do the right thing dropping also our little "last explicit configures" removal trick introduced in b4450f1dd6bca537e60406b2383ab154a3e1485f. This enables the last of a bunch of previously experimental options, some of them existing still, but are very special and hence not really worth documenting anymore (especially as it would need to be rewritten now entirely) which is why the documentation is nearly completely dropped. The order of configuration stanzas in the simulation code changes slightly as it isn't concerning itself with finding the 'right' order, but any order is valid anyhow as long as the entire set happens in the same call. --- apt-pkg/deb/dpkgpm.cc | 12 +++--------- apt-pkg/packagemanager.cc | 6 +++--- 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 4a49774f8..54a8dffd7 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -1337,16 +1337,10 @@ bool pkgDPkgPM::ExpandPendingCalls(std::vector &List, pkgDepCache &Cache) } bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { - // we remove the last configures (and after that removes) from the list here - // as they will be covered by the pending calls, so explicit calls are busy work - decltype(List)::const_iterator::difference_type explicitIdx = - std::distance(List.cbegin(), - _config->FindB("Dpkg::ExplicitLastConfigure", false) ? List.cend() : - std::find_if_not( - std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }), - List.crend(), [](Item const &i) { return i.Op == Item::Remove || i.Op == Item::Purge; }).base()); - // explicitely remove&configure everything for hookscripts and progress building + // we need them only temporarily through, so keep the length and erase afterwards + decltype(List)::const_iterator::difference_type explicitIdx = + std::distance(List.cbegin(), List.cend()); ExpandPendingCalls(List, Cache); auto const StripAlreadyDoneFromPending = [&](APT::VersionVector & Pending) { diff --git a/apt-pkg/packagemanager.cc b/apt-pkg/packagemanager.cc index 3d9c44555..90dd3a3ee 100644 --- a/apt-pkg/packagemanager.cc +++ b/apt-pkg/packagemanager.cc @@ -315,8 +315,8 @@ bool pkgPackageManager::ConfigureAll() if (OList.OrderConfigure() == false) return false; - std::string const conf = _config->Find("PackageManager::Configure","all"); - bool const ConfigurePkgs = (conf == "all"); + std::string const conf = _config->Find("PackageManager::Configure", "smart"); + bool const ConfigurePkgs = (ImmConfigureAll || conf == "all"); // Perform the configuring for (pkgOrderList::iterator I = OList.begin(); I != OList.end(); ++I) @@ -600,7 +600,7 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth) if (PkgLoop) return true; - static std::string const conf = _config->Find("PackageManager::Configure","all"); + static std::string const conf = _config->Find("PackageManager::Configure", "smart"); static bool const ConfigurePkgs = (conf == "all" || conf == "smart"); if (List->IsFlag(Pkg,pkgOrderList::Configured)) -- cgit v1.2.3