X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/7ec343309b7bc6001b465c870609b3c570026149..422a2eba84361a8dfd84b549c13037512779c572:/apt-pkg/deb/dpkgpm.cc?ds=sidebyside diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index b47988d6c..9d1739d68 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -60,6 +61,8 @@ #include /*}}}*/ +extern char **environ; + using namespace std; APT_PURE static string AptHistoryRequestingUser() /*{{{*/ @@ -416,7 +419,6 @@ bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version) bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) { bool result = true; - static bool interrupted = false; Configuration::Item const *Opts = _config->Tree(Cnf); if (Opts == 0 || Opts->Child == 0) @@ -424,9 +426,8 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) Opts = Opts->Child; sighandler_t old_sigpipe = signal(SIGPIPE, SIG_IGN); - sighandler_t old_sigint = signal(SIGINT, [](int signum){ - interrupted = true; - }); + sighandler_t old_sigint = signal(SIGINT, SIG_IGN); + sighandler_t old_sigquit = signal(SIGQUIT, SIG_IGN); unsigned int Count = 1; for (; Opts != 0; Opts = Opts->Next, Count++) @@ -527,9 +528,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf) } signal(SIGINT, old_sigint); signal(SIGPIPE, old_sigpipe); - - if (interrupted) - result = _error->Error("Interrupted"); + signal(SIGQUIT, old_sigquit); return result; } @@ -669,27 +668,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' @@ -719,21 +714,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) { /* @@ -750,24 +745,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; + } + } + } + } + } } } } @@ -1272,30 +1295,62 @@ static void cleanUpTmpDir(char * const tmpdir) /*{{{*/ * through to human readable (and i10n-able) * names and calculates a percentage for each step. */ +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) +{ + { + std::unordered_set alreadyRemoved; + for (auto && I : List) + if (I.Op == Item::Remove || I.Op == Item::Purge) + alreadyRemoved.insert(I.Pkg->ID); + 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)); + } + { + std::unordered_set alreadyConfigured; + for (auto && I : List) + if (I.Op == Item::Configure) + alreadyConfigured.insert(I.Pkg->ID); + 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); + 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)); + } + 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 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") - return true; - if (unlikely(I.Pkg.end())) - return true; - return (I.Pkg->Flags & pkgCache::Flag::Essential) != 0; - }; - - auto const StripAlreadyDoneFromPending = [&](APT::VersionVector & Pending) { + // 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); + + /* if dpkg told us that it has already done everything to the package we wanted it to do, + we shouldn't ask it for "more" later. That can e.g. happen if packages without conffiles + are purged as they will have pass through the purge states on remove already */ + auto const StripAlreadyDoneFrom = [&](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]; + auto const POD = PackageOpsDone.find(PN); + if (POD == PackageOpsDone.end()) + return false; + return PackageOps[PN].size() <= POD->second; }), Pending.end()); }; @@ -1349,6 +1404,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)) { @@ -1370,14 +1520,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"); @@ -1398,9 +1543,13 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) if (I.Op == Item::Remove || I.Op == Item::Purge) toBeRemoved[I.Pkg->ID] = false; - if (std::find(toBeRemoved.begin(), toBeRemoved.end(), true) != toBeRemoved.end()) + bool const RemovePending = std::find(toBeRemoved.begin(), toBeRemoved.end(), true) != toBeRemoved.end(); + bool const PurgePending = approvedStates.Purge().empty() == false; + if (RemovePending != false || PurgePending != false) + List.emplace_back(Item::ConfigurePending, pkgCache::PkgIterator()); + if (RemovePending) List.emplace_back(Item::RemovePending, pkgCache::PkgIterator()); - if (approvedStates.Purge().empty() == false) + if (PurgePending) List.emplace_back(Item::PurgePending, pkgCache::PkgIterator()); // support subpressing of triggers processing for special @@ -1469,7 +1618,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) unsigned long const Op = I->Op; if (NoTriggers == true && I->Op != Item::TriggersPending && - I->Op != Item::ConfigurePending) + (I->Op != Item::ConfigurePending || std::next(I) != List.end())) { ADDARGC("--no-triggers"); } @@ -1534,7 +1683,9 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { 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); + auto file = flNotDir(I->File); + if (flExtension(file) != "deb") + file.append(".deb"); std::string linkpath; if (dpkg_recursive_install_numbered) strprintf(linkpath, "%s/%.*lu-%s", tmpdir_to_free, p, n, file.c_str()); @@ -1560,7 +1711,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) else if (I->Op == Item::RemovePending) { ++I; - StripAlreadyDoneFromPending(approvedStates.Remove()); + StripAlreadyDoneFrom(approvedStates.Remove()); if (approvedStates.Remove().empty()) continue; } @@ -1569,7 +1720,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) ++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()); + StripAlreadyDoneFrom(approvedStates.Purge()); if (approvedStates.Purge().empty()) continue; std::remove_reference::type approvedRemoves; @@ -1821,8 +1972,8 @@ 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()); + StripAlreadyDoneFrom(approvedStates.Remove()); + StripAlreadyDoneFrom(approvedStates.Purge()); APT::StateChanges undo; auto && undoRem = approvedStates.Remove(); std::move(undoRem.begin(), undoRem.end(), std::back_inserter(undo.Install())); @@ -1832,6 +1983,9 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) if (undo.Save(false) == false) _error->Error("Couldn't revert dpkg selection for approved remove/purge after an error was encountered!"); } + + StripAlreadyDoneFrom(currentStates.Remove()); + StripAlreadyDoneFrom(currentStates.Purge()); if (currentStates.Save(false) == false) _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");