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 +++++++++++++-- test/integration/test-essential-force-loopbreak | 3 +++ test/integration/test-ubuntu-bug-761175-remove-purge | 4 ++++ 3 files changed, 20 insertions(+), 2 deletions(-) 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; diff --git a/test/integration/test-essential-force-loopbreak b/test/integration/test-essential-force-loopbreak index 6f7d99cb6..f585e9c42 100755 --- a/test/integration/test-essential-force-loopbreak +++ b/test/integration/test-essential-force-loopbreak @@ -43,6 +43,9 @@ E: Internal Error, Could not early remove sysvinit:$(dpkg --print-architecture) # with enough force however … cp -a dpkg.status.backup rootdir/var/lib/dpkg/status + testsuccess aptget install systemd-sysv -y -t "$1" -o APT::Force-LoopBreak=1 -o Debug::pkgDpkgPm=1 + cp rootdir/tmp/testsuccess.output apt.output + testsuccess grep -- '--force-remove-essential --remove sysvinit' apt.output testsuccess aptget install systemd-sysv -y -t "$1" -o APT::Force-LoopBreak=1 testdpkginstalled 'sysvinit' 'systemd-sysv' } diff --git a/test/integration/test-ubuntu-bug-761175-remove-purge b/test/integration/test-ubuntu-bug-761175-remove-purge index aae394ad8..6ae1a04e8 100755 --- a/test/integration/test-ubuntu-bug-761175-remove-purge +++ b/test/integration/test-ubuntu-bug-761175-remove-purge @@ -30,6 +30,8 @@ runtests() { testsuccess aptget install compiz-core-${PKG} -t "${RELEASE}" "$@" testdpkginstalled compiz-core-${PKG} + testsuccess aptget remove compiz-core-${PKG} -y "$@" -o Debug::pkgDpkgPm=1 + testfailure grep -- '--force-remove-essential' rootdir/tmp/testsuccess.output testsuccess aptget remove compiz-core-${PKG} -y "$@" testdpkgnotinstalled compiz-core-${PKG} testdpkgstatus 'rc' '1' "compiz-core-${PKG}" @@ -61,6 +63,8 @@ The following packages will be REMOVED: 0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded. Purg compiz-core-${PKG}" aptget purge compiz-core-${PKG} -s "$@" fi + testsuccess aptget purge compiz-core-${PKG} -y "$@" -o Debug::pkgDpkgPm=1 + testfailure grep -- '--force-remove-essential' rootdir/tmp/testsuccess.output testsuccess aptget purge compiz-core-${PKG} -y "$@" echo -n '' > rootdir/var/lib/dpkg/available # dpkg -l < 1.16.2 reads the available file by default, where the package can be found testequalor2 "dpkg-query: no packages found matching compiz-core-${PKG}" "No packages found matching compiz-core-${PKG}." dpkg -l compiz-core-${PKG} -- cgit v1.2.3-70-g09d2 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 ++++++++++++++++++++++++++-- test/integration/test-apt-progress-fd-error | 2 +- 2 files changed, 96 insertions(+), 8 deletions(-) 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; diff --git a/test/integration/test-apt-progress-fd-error b/test/integration/test-apt-progress-fd-error index e66210304..4439c042a 100755 --- a/test/integration/test-apt-progress-fd-error +++ b/test/integration/test-apt-progress-fd-error @@ -19,7 +19,7 @@ setupaptarchive exec 3> apt-progress.log testfailure aptget install foo1 foo2 -y -o APT::Status-Fd=3 msgtest 'Ensure correct error message' -testsuccess --nomsg grep "aptarchive/pool/foo2_0.8.15_[^.]\+.deb:36.3636:trying to overwrite '/usr/bin/file-conflict', which is also in package foo1 0.8.15" apt-progress.log +testsuccess --nomsg grep "foo2_0.8.15_[^.]\+.deb:36.3636:trying to overwrite '/usr/bin/file-conflict', which is also in package foo1 0.8.15" apt-progress.log testsuccess test -s rootdir/var/crash/foo2.0.crash testsuccess grep '^Package: foo2 0.8.15$' rootdir/var/crash/foo2.0.crash -- cgit v1.2.3-70-g09d2 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 ++- test/integration/test-apt-get-autoremove | 10 +++++++- 3 files changed, 47 insertions(+), 8 deletions(-) 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() \ { \ diff --git a/test/integration/test-apt-get-autoremove b/test/integration/test-apt-get-autoremove index 17dba9aec..cfee748af 100755 --- a/test/integration/test-apt-get-autoremove +++ b/test/integration/test-apt-get-autoremove @@ -18,6 +18,8 @@ testmarkedauto 'po-debconf' testsuccess aptget remove debhelper -y testdpkgnotinstalled 'debhelper' testdpkginstalled 'po-debconf' 'unrelated' +echo 'unrelated purge' | dpkg --set-selections +testdpkgstatus 'pi' '1' 'unrelated' AUTOREMOVE='apt autoremove' if [ -n "$SUDO_USER" ]; then @@ -49,14 +51,17 @@ testdpkginstalled 'po-debconf' echo 'APT::NeverAutoRemove { "^po-debconf$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove testsuccess aptget autoremove -y testdpkginstalled 'po-debconf' +testdpkgstatus 'pi' '1' 'unrelated' echo 'APT::NeverAutoRemove { "^po-.*$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove testsuccess aptget autoremove -y testdpkginstalled "po-debconf" +testdpkgstatus 'pi' '1' 'unrelated' rm rootdir/etc/apt/apt.conf.d/00autoremove testsuccess aptget autoremove -y testdpkgnotinstalled 'po-debconf' +testdpkgstatus 'pi' '1' 'unrelated' testmarkedauto sed rootdir/var/log/apt/history.log -e '/^Commandline: / d' \ @@ -71,7 +76,8 @@ Remove: debhelper:i386 (8.0.0) Remove: po-debconf:i386 (1.0.16)' testsuccess aptget install debhelper -y -testdpkginstalled 'unrelated' 'debhelper' 'po-debconf' +testdpkgstatus 'pi' '1' 'unrelated' +testdpkginstalled 'debhelper' 'po-debconf' testsuccess aptmark auto debhelper testmarkedauto 'debhelper' 'po-debconf' @@ -105,9 +111,11 @@ Reading state information... testsuccess aptget autoremove debhelper -y --allow-change-held-packages testdpkgnotinstalled 'po-debconf' 'debhelper' +testdpkgstatus 'pi' '1' 'unrelated' testmarkedauto testsuccess aptget install debhelper --solver apt -y -o Debug::pkgDepCache::Marker=1 testmarkedauto 'po-debconf' +testdpkgstatus 'pi' '1' 'unrelated' insertinstalledpackage 'bar' 'all' '1' 'Depends: foo-provider' insertinstalledpackage 'foo-multi1-1' 'all' '1' 'Provides: foo-provider -- cgit v1.2.3-70-g09d2 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 +- doc/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 -- cgit v1.2.3-70-g09d2 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(-) 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-70-g09d2 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 ++++++++++++--------- .../test-no-fds-leaked-to-maintainer-scripts | 4 +- 2 files changed, 88 insertions(+), 63 deletions(-) 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!"); diff --git a/test/integration/test-no-fds-leaked-to-maintainer-scripts b/test/integration/test-no-fds-leaked-to-maintainer-scripts index 56cbff095..a9c198580 100755 --- a/test/integration/test-no-fds-leaked-to-maintainer-scripts +++ b/test/integration/test-no-fds-leaked-to-maintainer-scripts @@ -34,7 +34,7 @@ rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log testsuccess aptget install -y fdleaks -qq < /dev/null checkfdleak() { - msgtest 'Check if fds were not' 'leaked' + msgtest 'Check if fds were not' "leaked: expect $1" if [ "$(grep 'root root' rootdir/tmp/testsuccess.output | wc -l)" = "$1" ]; then msgpass else @@ -73,7 +73,6 @@ checkpurge() { testfileequal 'terminal.log' "$(cat terminal.output)" testequal "startup packages purge -status installed $PKGNAME 1.0 remove $PKGNAME 1.0 status half-configured $PKGNAME 1.0 status half-installed $PKGNAME 1.0 @@ -86,6 +85,7 @@ status config-files $PKGNAME 1.0 status config-files $PKGNAME 1.0 status not-installed $PKGNAME startup packages configure" cut -f 3- -d' ' rootdir/var/log/dpkg.log + testequalor2 "dpkg-query: no packages found matching ${PKGNAME}" "No packages found matching ${PKGNAME}." dpkg -l "$PKGNAME" } checkpurge -- cgit v1.2.3-70-g09d2 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(+) 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-70-g09d2 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(+) 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-70-g09d2 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 ++++++++++++++++++++++++++++++++------ test/integration/test-crossgrades | 49 +++++++++++ 2 files changed, 189 insertions(+), 26 deletions(-) create mode 100755 test/integration/test-crossgrades 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"); diff --git a/test/integration/test-crossgrades b/test/integration/test-crossgrades new file mode 100755 index 000000000..d412546c1 --- /dev/null +++ b/test/integration/test-crossgrades @@ -0,0 +1,49 @@ +#!/bin/sh +set -e + +TESTDIR="$(readlink -f "$(dirname "$0")")" +. "$TESTDIR/framework" + +setupenvironment +configarchitecture 'i386' 'amd64' 'armel' +configdpkgnoopchroot + +buildsimplenativepackage 'unrelated' 'amd64' '1' 'stable' +buildsimplenativepackage 'crosser' 'i386,armel' '1' 'stable' 'Multi-Arch: same' +buildsimplenativepackage 'crosser' 'amd64' '2' 'unstable' +setupaptarchive + +singleinstance() { + testsuccess apt install crosser:i386=1 unrelated:amd64 -y --planner $1 + testdpkginstalled 'crosser:i386' 'unrelated' + + testsuccess apt install crosser:amd64 -y -o Debug::pkgDpkgPm=1 -o Dpkg::Use-Pty=0 --purge --planner $1 + cp -a rootdir/tmp/testsuccess.output crosser.output + testfailure grep -- '--remove.*crosser.*' crosser.output + testfailure grep -- '--purge' crosser.output + testsuccess apt install crosser:amd64 unrelated:amd64- -y -o Dpkg::Use-Pty=0 --purge -o Debug::pkgDPkgProgressReporting=1 --planner $1 + testdpkgnotinstalled 'crosser:i386' 'unrelated' + testdpkginstalled 'crosser:amd64' + + testsuccess apt purge crosser:amd64 -y --planner $1 + testdpkgnotinstalled 'crosser:amd64' +} +singleinstance 'internal' +singleinstance 'apt' + +multiinstance() { + testsuccess apt install crosser:i386=1 crosser:armel=1 unrelated:amd64 -y --planner $1 + testdpkginstalled 'crosser:i386' 'crosser:armel' 'unrelated' + + testsuccess apt install crosser:amd64 -y -o Debug::pkgDpkgPm=1 -o Dpkg::Use-Pty=0 --purge --planner $1 + cp -a rootdir/tmp/testsuccess.output crosser.output + testsuccess grep -- '--remove.*crosser.*' crosser.output + testsuccess grep -- '--purge' crosser.output + testsuccess apt install crosser:amd64 unrelated:amd64- -y -o Dpkg::Use-Pty=0 --purge -o Debug::pkgDPkgProgressReporting=1 --planner $1 + testdpkgnotinstalled 'crosser:i386' 'crosser:armel' 'unrelated' + testdpkginstalled 'crosser:amd64' + + testsuccess apt purge crosser:amd64 -y --planner $1 + testdpkgnotinstalled 'crosser:amd64' +} +multiinstance 'internal' -- cgit v1.2.3-70-g09d2 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(-) 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-70-g09d2 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 +- doc/apt.conf.5.xml | 84 ++------------------- .../test-allow-scores-for-all-dependency-types | 8 +- ...bug-712116-dpkg-pre-install-pkgs-hook-multiarch | 86 +++++++++++++--------- .../test-bug-735967-lib32-to-i386-unavailable | 6 +- .../test-bug-740843-versioned-up-down-breaks | 16 ++-- .../test-bug-lp1562402-nomark-removals-as-keep | 4 +- test/integration/test-bug-multiarch-upgrade | 4 +- test/integration/test-dpkg-assert-multi-arch | 2 +- .../test-external-installation-planner-protocol | 7 +- .../test-ignore-provides-if-versioned-breaks | 2 +- .../test-ignore-provides-if-versioned-conflicts | 2 +- .../test-no-fds-leaked-to-maintainer-scripts | 6 +- ...prevent-markinstall-multiarch-same-versionscrew | 8 +- ...ight-loop-configure-with-unpacking-new-packages | 16 ++-- 16 files changed, 105 insertions(+), 164 deletions(-) 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)) diff --git a/doc/apt.conf.5.xml b/doc/apt.conf.5.xml index 09db5a0e0..cfc840ae9 100644 --- a/doc/apt.conf.5.xml +++ b/doc/apt.conf.5.xml @@ -904,84 +904,14 @@ APT::Compressor::rev { These options are passed to &dpkg-buildpackage; when compiling packages; the default is to disable signing and produce all binaries. - - dpkg trigger usage (and related options) - APT can call &dpkg; in such a way as to let it make aggressive use of triggers over - multiple calls of &dpkg;. Without further options &dpkg; will use triggers once each time it runs. - Activating these options can therefore decrease the time needed to perform the - install or upgrade. Note that it is intended to activate these options per default in the - future, but as it drastically changes the way APT calls &dpkg; it needs a lot more testing. - These options are therefore currently experimental and should not be used in - production environments. It also breaks progress reporting such that all front-ends will - currently stay around half (or more) of the time in the 100% state while it actually configures - all packages. - Note that it is not guaranteed that APT will support these options or that these options will - not cause (big) trouble in the future. If you have understand the current risks and problems with - these options, but are brave enough to help testing them, create a new configuration file and test a - combination of options. Please report any bugs, problems and improvements you encounter and make sure - to note which options you have used in your reports. Asking &dpkg; for help could also be useful for - debugging proposes, see e.g. dpkg --audit. A defensive option combination would be -DPkg::NoTriggers "true"; -PackageManager::Configure "smart"; -DPkg::ConfigurePending "true"; -DPkg::TriggersPending "true"; - - - - Add the no triggers flag to all &dpkg; calls (except the ConfigurePending call). - See &dpkg; if you are interested in what this actually means. In short: &dpkg; will not run the - triggers when this flag is present unless it is explicitly called to do so in an extra call. - Note that this option exists (undocumented) also in older APT versions with a slightly different - meaning: Previously these option only append --no-triggers to the configure calls to &dpkg; - - now APT will also add this flag to the unpack and remove calls. - - - Valid values are "all", - "smart" and "no". - The default value is "all", which causes APT to - configure all packages. The "smart" way is to - configure only packages which need to be configured before another - package can be unpacked (Pre-Depends), and let the rest be configured - by &dpkg; with a call generated by the ConfigurePending option (see - below). On the other hand, "no" will not configure - anything, and totally relies on &dpkg; for configuration (which at the - moment will fail if a Pre-Depends is encountered). Setting this option - to any value other than all will implicitly also - activate the next option by default, as otherwise the system could end - in an unconfigured and potentially unbootable state. - - - If this option is set APT will call dpkg --configure --pending - to let &dpkg; handle all required configurations and triggers. This option is activated automatically - per default if the previous option is not set to all, but deactivating it could be useful - if you want to run APT multiple times in a row - e.g. in an installer. In these sceneries you could - deactivate this option in all but the last run. - - - Useful for the smart configuration as a package which has pending - triggers is not considered as installed, and &dpkg; treats them as unpacked - currently which is a showstopper for Pre-Dependencies (see debbugs #526774). Note that this will - process all triggers, not only the triggers needed to configure this package. - - - Essential packages (and their dependencies) should be configured immediately - after unpacking. It is a good idea to do this quite early in the upgrade process as these - configure calls also currently require DPkg::TriggersPending which - will run quite a few triggers (which may not be needed). Essentials get per default a high score - but the immediate flag is relatively low (a package which has a Pre-Depends is rated higher). - These option and the others in the same group can be used to change the scoring. The following - example shows the settings with their default values. - OrderList::Score { - Delete 500; - Essential 200; - Immediate 10; - PreDepends 50; -}; - - - - + + If this option is set APT will call dpkg --configure --pending + to let &dpkg; handle all required configurations and triggers. This option is activated by default, + but deactivating it could be useful if you want to run APT multiple times in a row - e.g. in an installer. + In this scenario you could deactivate this option in all but the last run. + + diff --git a/test/integration/test-allow-scores-for-all-dependency-types b/test/integration/test-allow-scores-for-all-dependency-types index 1a08f2ed2..9b300b7a7 100755 --- a/test/integration/test-allow-scores-for-all-dependency-types +++ b/test/integration/test-allow-scores-for-all-dependency-types @@ -55,8 +55,8 @@ The following packages will be upgraded: Remv libdb5.1-dev [5.1.29-7] [libdb-dev:amd64 ] Inst libdb-dev [5.1.7] (5.3.0 unversioned [amd64]) [] Inst libdb5.3-dev (5.3.28-3 unversioned [amd64]) -Conf libdb5.3-dev (5.3.28-3 unversioned [amd64]) -Conf libdb-dev (5.3.0 unversioned [amd64])' aptget dist-upgrade -st unversioned +Conf libdb-dev (5.3.0 unversioned [amd64]) +Conf libdb5.3-dev (5.3.28-3 unversioned [amd64])' aptget dist-upgrade -st unversioned testsuccessequal 'Reading package lists... Building dependency tree... Calculating upgrade... @@ -70,8 +70,8 @@ The following packages will be upgraded: Remv libdb5.1-dev [5.1.29-7] [libdb-dev:amd64 ] Inst libdb-dev [5.1.7] (5.3.0 versioned [amd64]) [] Inst libdb5.3-dev (5.3.28-3 versioned [amd64]) -Conf libdb5.3-dev (5.3.28-3 versioned [amd64]) -Conf libdb-dev (5.3.0 versioned [amd64])' aptget dist-upgrade -st versioned +Conf libdb-dev (5.3.0 versioned [amd64]) +Conf libdb5.3-dev (5.3.28-3 versioned [amd64])' aptget dist-upgrade -st versioned cp -f rootdir/var/lib/dpkg/status-backup rootdir/var/lib/dpkg/status insertinstalledpackage 'foo' 'amd64' '1' diff --git a/test/integration/test-bug-712116-dpkg-pre-install-pkgs-hook-multiarch b/test/integration/test-bug-712116-dpkg-pre-install-pkgs-hook-multiarch index a15ac06ed..d9fd3d30d 100755 --- a/test/integration/test-bug-712116-dpkg-pre-install-pkgs-hook-multiarch +++ b/test/integration/test-bug-712116-dpkg-pre-install-pkgs-hook-multiarch @@ -48,33 +48,42 @@ DPkg::Tools::options::\"./${hook}-v${1}.sh\"::Version \"$1\";" > rootdir/etc/apt observehook() { rm -f ${hook}-v2.list ${hook}-v3.list msgtest 'Observe hooks while' "$*" - testsuccess --nomsg aptget "$@" -y --allow-downgrades + testsuccess --nomsg aptget "$@" -y --allow-downgrades --planner $planner + # different planners have different orders – we don't care in this test here + if [ -e ${hook}-v2.list ]; then + sort < ${hook}-v2.list > ${hook}-v2.list.new + mv ${hook}-v2.list.new ${hook}-v2.list + fi + if [ -e ${hook}-v3.list ]; then + sort < ${hook}-v3.list > ${hook}-v3.list.new + mv ${hook}-v3.list.new ${hook}-v3.list + fi } testrun() { observehook install stuff -t stable testfileequal "${hook}-v2.list" 'libsame - < 1 **CONFIGURE** -toolkit - < 1 **CONFIGURE** -stuff - < 1 **CONFIGURE**' +stuff - < 1 **CONFIGURE** +toolkit - < 1 **CONFIGURE**' testfileequal "${hook}-v3.list" 'libsame - - none < 1 amd64 same **CONFIGURE** -toolkit - - none < 1 all foreign **CONFIGURE** -stuff - - none < 1 amd64 none **CONFIGURE**' +stuff - - none < 1 amd64 none **CONFIGURE** +toolkit - - none < 1 all foreign **CONFIGURE**' observehook install stuff -t unstable testfileequal "${hook}-v2.list" 'libsame 1 < 2 **CONFIGURE** -toolkit 1 < 2 **CONFIGURE** -stuff 1 < 2 **CONFIGURE**' +stuff 1 < 2 **CONFIGURE** +toolkit 1 < 2 **CONFIGURE**' testfileequal "${hook}-v3.list" 'libsame 1 amd64 same < 2 amd64 same **CONFIGURE** -toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE** -stuff 1 amd64 none < 2 amd64 none **CONFIGURE**' +stuff 1 amd64 none < 2 amd64 none **CONFIGURE** +toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE**' observehook install stuff:i386 -t unstable - testfileequal "${hook}-v2.list" 'stuff 2 > - **REMOVE** -libsame - < 2 **CONFIGURE** -stuff - < 2 **CONFIGURE**' - testfileequal "${hook}-v3.list" 'stuff 2 amd64 none > - - none **REMOVE** -libsame - - none < 2 i386 same **CONFIGURE** -stuff - - none < 2 i386 none **CONFIGURE**' + testfileequal "${hook}-v2.list" 'libsame - < 2 **CONFIGURE** +stuff - < 2 **CONFIGURE** +stuff 2 > - **REMOVE**' + testfileequal "${hook}-v3.list" 'libsame - - none < 2 i386 same **CONFIGURE** +stuff - - none < 2 i386 none **CONFIGURE** +stuff 2 amd64 none > - - none **REMOVE**' observehook remove libsame testfileequal "${hook}-v2.list" 'libsame 2 > - **REMOVE**' @@ -82,30 +91,30 @@ stuff - - none < 2 i386 none **CONFIGURE**' observehook install stuff:i386/stable libsame:i386/stable toolkit/stable testfileequal "${hook}-v2.list" 'libsame 2 > 1 **CONFIGURE** -toolkit 2 > 1 **CONFIGURE** -stuff 2 > 1 **CONFIGURE**' +stuff 2 > 1 **CONFIGURE** +toolkit 2 > 1 **CONFIGURE**' testfileequal "${hook}-v3.list" 'libsame 2 i386 same > 1 i386 same **CONFIGURE** -toolkit 2 amd64 foreign > 1 all foreign **CONFIGURE** -stuff 2 i386 none > 1 i386 none **CONFIGURE**' +stuff 2 i386 none > 1 i386 none **CONFIGURE** +toolkit 2 amd64 foreign > 1 all foreign **CONFIGURE**' observehook install 'libsame:*' - testfileequal "${hook}-v2.list" 'libsame 1 < 2 **CONFIGURE** -libsame - < 2 **CONFIGURE** -toolkit 1 < 2 **CONFIGURE** -stuff 1 < 2 **CONFIGURE**' - testfileequal "${hook}-v3.list" 'libsame 1 i386 same < 2 i386 same **CONFIGURE** -libsame - - none < 2 amd64 same **CONFIGURE** -toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE** -stuff 1 i386 none < 2 i386 none **CONFIGURE**' + testfileequal "${hook}-v2.list" 'libsame - < 2 **CONFIGURE** +libsame 1 < 2 **CONFIGURE** +stuff 1 < 2 **CONFIGURE** +toolkit 1 < 2 **CONFIGURE**' + testfileequal "${hook}-v3.list" 'libsame - - none < 2 amd64 same **CONFIGURE** +libsame 1 i386 same < 2 i386 same **CONFIGURE** +stuff 1 i386 none < 2 i386 none **CONFIGURE** +toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE**' observehook purge stuff:i386 'libsame:*' toolkit testfileequal "${hook}-v2.list" 'libsame 2 > - **REMOVE** -stuff 2 > - **REMOVE** libsame 2 > - **REMOVE** +stuff 2 > - **REMOVE** toolkit 2 > - **REMOVE**' testfileequal "${hook}-v3.list" 'libsame 2 amd64 same > - - none **REMOVE** -stuff 2 i386 none > - - none **REMOVE** libsame 2 i386 same > - - none **REMOVE** +stuff 2 i386 none > - - none **REMOVE** toolkit 2 amd64 foreign > - - none **REMOVE**' observehook install confpkg @@ -127,10 +136,17 @@ toolkit 2 amd64 foreign > - - none **REMOVE**' dpkg -l confpkg 2>/dev/null | grep -q '^rc' && msgfail || msgpass } -enablehookversion 2 -enablehookversion 3 -testrun +runwithplanner() +{ + msgmsg 'Running with planner' "$1" + planner="$1" + enablehookversion 2 + enablehookversion 3 + testrun + + enablehookversion 2 13 + enablehookversion 3 13 + testrun +} -enablehookversion 2 13 -enablehookversion 3 13 -testrun +runwithplanner 'apt' diff --git a/test/integration/test-bug-735967-lib32-to-i386-unavailable b/test/integration/test-bug-735967-lib32-to-i386-unavailable index 290df8a3f..3b705d5f9 100755 --- a/test/integration/test-bug-735967-lib32-to-i386-unavailable +++ b/test/integration/test-bug-735967-lib32-to-i386-unavailable @@ -71,10 +71,10 @@ Inst lib32nss-mdns [0.9-1] (0.10-6 unstable [amd64]) [] Inst libnss-mdns [0.9-1] (0.10-6 unstable [amd64]) [] Inst libnss-mdns:i386 (0.10-6 unstable [i386]) [] Inst libnss-mdns-i386:i386 (0.10-6 unstable [i386]) -Conf libnss-mdns:i386 (0.10-6 unstable [i386]) +Conf lib32nss-mdns (0.10-6 unstable [amd64]) Conf libnss-mdns (0.10-6 unstable [amd64]) -Conf libnss-mdns-i386:i386 (0.10-6 unstable [i386]) -Conf lib32nss-mdns (0.10-6 unstable [amd64])' aptget dist-upgrade -s +Conf libnss-mdns:i386 (0.10-6 unstable [i386]) +Conf libnss-mdns-i386:i386 (0.10-6 unstable [i386])' aptget dist-upgrade -s testsuccessequal 'Reading package lists... Building dependency tree... diff --git a/test/integration/test-bug-740843-versioned-up-down-breaks b/test/integration/test-bug-740843-versioned-up-down-breaks index 143175cca..69adceffd 100755 --- a/test/integration/test-bug-740843-versioned-up-down-breaks +++ b/test/integration/test-bug-740843-versioned-up-down-breaks @@ -33,11 +33,11 @@ Inst libgl1-foo-glx:i386 [1] (2 stable [i386]) [foo-driver:amd64 on libgl1-foo-g Inst foo-driver [1] (2 stable [amd64]) [] Inst libfoo:i386 [1] (2 stable [i386]) [libfoo:amd64 on libfoo:i386] [libfoo:i386 on libfoo:amd64] [libfoo:amd64 ] Inst libfoo [1] (2 stable [amd64]) -Conf libfoo:i386 (2 stable [i386]) -Conf libfoo (2 stable [amd64]) -Conf libgl1-foo-glx:i386 (2 stable [i386]) Conf libgl1-foo-glx (2 stable [amd64]) -Conf foo-driver (2 stable [amd64])' 'Reading package lists... +Conf libgl1-foo-glx:i386 (2 stable [i386]) +Conf foo-driver (2 stable [amd64]) +Conf libfoo:i386 (2 stable [i386]) +Conf libfoo (2 stable [amd64])' 'Reading package lists... Building dependency tree... Calculating upgrade... The following packages will be upgraded: @@ -48,10 +48,10 @@ Inst libgl1-foo-glx:i386 [1] (2 stable [i386]) [foo-driver:amd64 on libgl1-foo-g Inst foo-driver [1] (2 stable [amd64]) [] Inst libfoo:i386 [1] (2 stable [i386]) [libfoo:amd64 on libfoo:i386] [libfoo:i386 on libfoo:amd64] [libfoo:amd64 ] Inst libfoo [1] (2 stable [amd64]) -Conf libfoo:i386 (2 stable [i386]) -Conf libfoo (2 stable [amd64]) -Conf libgl1-foo-glx:i386 (2 stable [i386]) Conf libgl1-foo-glx (2 stable [amd64]) -Conf foo-driver (2 stable [amd64])' aptget dist-upgrade -s +Conf libgl1-foo-glx:i386 (2 stable [i386]) +Conf foo-driver (2 stable [amd64]) +Conf libfoo:i386 (2 stable [i386]) +Conf libfoo (2 stable [amd64])' aptget dist-upgrade -s testsuccess aptget dist-upgrade -y -o Debug::pkgPackageManager=1 -o Debug::pkgOrderList=1 diff --git a/test/integration/test-bug-lp1562402-nomark-removals-as-keep b/test/integration/test-bug-lp1562402-nomark-removals-as-keep index 05f43701d..6e8225aa4 100755 --- a/test/integration/test-bug-lp1562402-nomark-removals-as-keep +++ b/test/integration/test-bug-lp1562402-nomark-removals-as-keep @@ -33,7 +33,7 @@ Inst maas-region-controller [2.0.0~alpha3+bzr4810-0ubuntu1] (2.0.0~alpha4+bzr484 Remv maas-region-controller-min [2.0.0~alpha3+bzr4810-0ubuntu1] [] Inst maas-common [2.0.0~alpha3+bzr4810-0ubuntu1] (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [all]) [] Inst maas-region-api (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [amd64]) +Conf maas-region-controller (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [all]) Conf maas-common (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [all]) -Conf maas-region-api (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [amd64]) -Conf maas-region-controller (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [all])' \ +Conf maas-region-api (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [amd64])' \ aptget dist-upgrade -s diff --git a/test/integration/test-bug-multiarch-upgrade b/test/integration/test-bug-multiarch-upgrade index cb5159c05..38e70531e 100755 --- a/test/integration/test-bug-multiarch-upgrade +++ b/test/integration/test-bug-multiarch-upgrade @@ -25,5 +25,5 @@ The following packages will be upgraded: 2 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. Inst libcups2 [1] (2 unstable [amd64]) [libcups2:amd64 on libcups2:i386] [libcups2:i386 on libcups2:amd64] [libcups2:i386 ] Inst libcups2:i386 [1] (2 unstable [i386]) -Conf libcups2:i386 (2 unstable [i386]) -Conf libcups2 (2 unstable [amd64])' aptget install -s libcups2:i386 +Conf libcups2 (2 unstable [amd64]) +Conf libcups2:i386 (2 unstable [i386])' aptget install -s libcups2:i386 diff --git a/test/integration/test-dpkg-assert-multi-arch b/test/integration/test-dpkg-assert-multi-arch index 678038cf2..a99f320b3 100755 --- a/test/integration/test-dpkg-assert-multi-arch +++ b/test/integration/test-dpkg-assert-multi-arch @@ -17,7 +17,7 @@ setupaptarchive testqualifier() { msgtest 'Test with' $1 'for correct qualifier mode' $2 #aptget install $1 -qq -o Debug::pkgDPkgPM=1 || true - aptget install $1 -qq -o Debug::pkgDPkgPM=1 -o Dpkg::ExplicitLastConfigure=1 > testqualifier.output 2>&1 || true + aptget install $1 -qq -o Debug::pkgDPkgPM=1 -o Dpkg::ExplicitLastConfigure=1 -o PackageManager::Configure=all > testqualifier.output 2>&1 || true GIVEN="$(grep -v -- '--unpack' testqualifier.output | sed -ne 's/^.*--[rpc][^ ]* \([^ ]*\).*$/\1/p')" if [ "$GIVEN" = "$2" ]; then msgpass diff --git a/test/integration/test-external-installation-planner-protocol b/test/integration/test-external-installation-planner-protocol index bd3c99c32..8d80930bc 100755 --- a/test/integration/test-external-installation-planner-protocol +++ b/test/integration/test-external-installation-planner-protocol @@ -45,9 +45,7 @@ Planner: internal' head -n 6 "$EIPPLOG" aptinternalplanner < "$EIPPLOG" > planner.log || true testsuccessequal 'Remove: 6 Unpack: 2 -Unpack: 4 -Configure: 2 -Configure: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log +Unpack: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log rm -f "$EIPPLOG" testsuccess aptget install foo -s --reinstall @@ -59,8 +57,7 @@ Architectures: amd64 ReInstall: foo:amd64 Planner: internal' head -n 5 "$EIPPLOG" aptinternalplanner < "$EIPPLOG" > planner.log || true -testsuccessequal 'Unpack: 4 -Configure: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log +testsuccessequal 'Unpack: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log rm -f "$EIPPLOG" testsuccess aptget purge foo -s diff --git a/test/integration/test-ignore-provides-if-versioned-breaks b/test/integration/test-ignore-provides-if-versioned-breaks index 8c3750acf..ebcbecf47 100755 --- a/test/integration/test-ignore-provides-if-versioned-breaks +++ b/test/integration/test-ignore-provides-if-versioned-breaks @@ -144,7 +144,7 @@ Inst foo-same [2.0] (4.0 unstable [i386]) [foo-same:i386 on foo-same:amd64] [foo Inst foo-same:amd64 [2.0] (4.0 unstable [amd64]) Inst foo-same-breaker-3 (1.0 unstable [i386]) Inst foo-same-provider (1.0 unstable [i386]) -Conf foo-same:amd64 (4.0 unstable [amd64]) Conf foo-same (4.0 unstable [i386]) +Conf foo-same:amd64 (4.0 unstable [amd64]) Conf foo-same-breaker-3 (1.0 unstable [i386]) Conf foo-same-provider (1.0 unstable [i386])' aptget install foo-same-provider foo-same-breaker-3 -s diff --git a/test/integration/test-ignore-provides-if-versioned-conflicts b/test/integration/test-ignore-provides-if-versioned-conflicts index 839609431..3243cfb51 100755 --- a/test/integration/test-ignore-provides-if-versioned-conflicts +++ b/test/integration/test-ignore-provides-if-versioned-conflicts @@ -144,7 +144,7 @@ Inst foo-same [2.0] (4.0 unstable [i386]) [foo-same:i386 on foo-same:amd64] [foo Inst foo-same:amd64 [2.0] (4.0 unstable [amd64]) Inst foo-same-breaker-3 (1.0 unstable [i386]) Inst foo-same-provider (1.0 unstable [i386]) -Conf foo-same:amd64 (4.0 unstable [amd64]) Conf foo-same (4.0 unstable [i386]) +Conf foo-same:amd64 (4.0 unstable [amd64]) Conf foo-same-breaker-3 (1.0 unstable [i386]) Conf foo-same-provider (1.0 unstable [i386])' aptget install foo-same-provider foo-same-breaker-3 -s diff --git a/test/integration/test-no-fds-leaked-to-maintainer-scripts b/test/integration/test-no-fds-leaked-to-maintainer-scripts index a9c198580..21b394055 100755 --- a/test/integration/test-no-fds-leaked-to-maintainer-scripts +++ b/test/integration/test-no-fds-leaked-to-maintainer-scripts @@ -72,11 +72,15 @@ checkpurge() { tail -n +3 rootdir/var/log/apt/term.log | head -n -1 > terminal.log testfileequal 'terminal.log' "$(cat terminal.output)" - testequal "startup packages purge + testequal "startup packages remove +status installed $PKGNAME 1.0 remove $PKGNAME 1.0 status half-configured $PKGNAME 1.0 status half-installed $PKGNAME 1.0 status config-files $PKGNAME 1.0 +status config-files $PKGNAME 1.0 +startup packages purge +remove $PKGNAME 1.0 purge $PKGNAME 1.0 status config-files $PKGNAME 1.0 status config-files $PKGNAME 1.0 diff --git a/test/integration/test-prevent-markinstall-multiarch-same-versionscrew b/test/integration/test-prevent-markinstall-multiarch-same-versionscrew index a46f2292a..a45c0d55d 100755 --- a/test/integration/test-prevent-markinstall-multiarch-same-versionscrew +++ b/test/integration/test-prevent-markinstall-multiarch-same-versionscrew @@ -59,10 +59,10 @@ Inst fine-installed [1] (2 unstable [amd64]) [fine-installed:amd64 on fine-insta Inst fine-installed:i386 [1] (2 unstable [i386]) Inst out-of-sync-gone-foreign [1] (2 unstable [amd64]) Inst out-of-sync-gone-native:i386 [1] (2 unstable [i386]) -Conf fine:i386 (2 unstable [i386]) Conf fine (2 unstable [amd64]) -Conf fine-installed:i386 (2 unstable [i386]) +Conf fine:i386 (2 unstable [i386]) Conf fine-installed (2 unstable [amd64]) +Conf fine-installed:i386 (2 unstable [i386]) Conf out-of-sync-gone-foreign (2 unstable [amd64]) Conf out-of-sync-gone-native:i386 (2 unstable [i386])' aptget dist-upgrade -s #-o Debug::pkgDepCache::Marker=1 @@ -89,10 +89,10 @@ Inst fine-installed [1] (3 experimental [amd64]) [fine-installed:amd64 on fine-i Inst fine-installed:i386 [1] (3 experimental [i386]) Inst out-of-sync-gone-foreign [1] (2 unstable [amd64]) Inst out-of-sync-gone-native:i386 [1] (2 unstable [i386]) -Conf fine:i386 (3 experimental [i386]) Conf fine (3 experimental [amd64]) -Conf fine-installed:i386 (3 experimental [i386]) +Conf fine:i386 (3 experimental [i386]) Conf fine-installed (3 experimental [amd64]) +Conf fine-installed:i386 (3 experimental [i386]) Conf out-of-sync-gone-foreign (2 unstable [amd64]) Conf out-of-sync-gone-native:i386 (2 unstable [i386])' aptget dist-upgrade -s #-o Debug::pkgDepCache::Marker=1 diff --git a/test/integration/test-very-tight-loop-configure-with-unpacking-new-packages b/test/integration/test-very-tight-loop-configure-with-unpacking-new-packages index 7fcf16ed5..09953e943 100755 --- a/test/integration/test-very-tight-loop-configure-with-unpacking-new-packages +++ b/test/integration/test-very-tight-loop-configure-with-unpacking-new-packages @@ -39,11 +39,11 @@ Inst libreoffice-style-galaxy [3] (4 sid [amd64]) [libreoffice-common:amd64 on l Inst libreoffice-core [3] (4 sid [amd64]) [libreoffice-core:amd64 on libreoffice-common:amd64] [libreoffice-common:amd64 on libreoffice-core:amd64] [libreoffice-common:amd64 on libreoffice-style-galaxy:amd64] [libreoffice-common:amd64 ] Inst libreoffice-common [3] (4 sid [all]) [] Inst ure (4 sid [amd64]) -Conf ure (4 sid [amd64]) -Conf libreoffice-common (4 sid [all]) -Conf libreoffice-core (4 sid [amd64]) +Conf libreoffice (4 sid [amd64]) Conf libreoffice-style-galaxy (4 sid [amd64]) -Conf libreoffice (4 sid [amd64])' 'Reading package lists... +Conf libreoffice-core (4 sid [amd64]) +Conf libreoffice-common (4 sid [all]) +Conf ure (4 sid [amd64])' 'Reading package lists... Building dependency tree... Calculating upgrade... The following NEW packages will be installed: @@ -56,8 +56,8 @@ Inst libreoffice-style-galaxy [3] (4 sid [amd64]) [libreoffice-common:amd64 on l Inst libreoffice-core [3] (4 sid [amd64]) [libreoffice-common:amd64 on libreoffice-core:amd64] [libreoffice-common:amd64 on libreoffice-style-galaxy:amd64] [libreoffice-core:amd64 on libreoffice-common:amd64] [libreoffice-common:amd64 ] Inst libreoffice-common [3] (4 sid [all]) [] Inst ure (4 sid [amd64]) -Conf ure (4 sid [amd64]) -Conf libreoffice-common (4 sid [all]) -Conf libreoffice-core (4 sid [amd64]) +Conf libreoffice (4 sid [amd64]) Conf libreoffice-style-galaxy (4 sid [amd64]) -Conf libreoffice (4 sid [amd64])' aptget dist-upgrade -s +Conf libreoffice-core (4 sid [amd64]) +Conf libreoffice-common (4 sid [all]) +Conf ure (4 sid [amd64])' aptget dist-upgrade -s -- cgit v1.2.3-70-g09d2