X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/7b197262212f49b3b355b1124edf2ba9adb73411..27f38567fe327ecaf7fb361c3cca6ee29e6300c9:/apt-pkg/edsp.cc diff --git a/apt-pkg/edsp.cc b/apt-pkg/edsp.cc index 58982ade2..ef2401f9e 100644 --- a/apt-pkg/edsp.cc +++ b/apt-pkg/edsp.cc @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -437,7 +439,7 @@ bool EDSP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress *Progres Progress->Progress(p); } } - return true; + return Okay; } /*}}}*/ // EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/ @@ -659,8 +661,12 @@ bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progres return false; } else if (section.Exists("Autoremove") == true) type = "Autoremove"; - else + else { + char const *Start, *End; + section.GetSection(Start, End); + _error->Warning("Encountered an unexpected section with %d fields: %s", section.Count(), std::string(Start, End).c_str()); continue; + } size_t const id = section.FindULL(type.c_str(), VersionCount); if (id == VersionCount) { @@ -905,14 +911,14 @@ bool EDSP::WriteSolutionStanza(FileFd &output, char const * const Type, pkgCache /*}}}*/ // EDSP::WriteProgess - pulse to the given file descriptor /*{{{*/ bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FILE* output) { - fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL)).c_str()); + fprintf(output, "Progress: %s\n", TimeRFC1123(time(NULL), true).c_str()); fprintf(output, "Percentage: %d\n", percent); fprintf(output, "Message: %s\n\n", message); fflush(output); return true; } bool EDSP::WriteProgress(unsigned short const percent, const char* const message, FileFd &output) { - return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL)), "\n", + return WriteOkay(output, "Progress: ", TimeRFC1123(time(NULL), true), "\n", "Percentage: ", percent, "\n", "Message: ", message, "\n\n") && output.Flush(); } @@ -1020,8 +1026,20 @@ bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache, return false; auto const dumpdir = flNotFile(dumpfile); FileFd output; - if (CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false || - output.Open(dumpfile, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false) + _error->PushToStack(); + bool errored_out = CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false || + output.Open(dumpfile, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false; + std::vector downgrademsgs; + while (_error->empty() == false) + { + std::string msg; + _error->PopMessage(msg); + downgrademsgs.emplace_back(std::move(msg)); + } + _error->RevertToStack(); + for (auto && msg : downgrademsgs) + _error->Warning("%s", msg.c_str()); + if (errored_out) return _error->WarningE("EDSP::Resolve", _("Could not open file '%s'"), dumpfile.c_str()); bool Okay = EDSP::WriteRequest(Cache, output, flags, nullptr); return Okay && EDSP::WriteScenario(Cache, output, nullptr); @@ -1065,30 +1083,64 @@ bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache, } /*}}}*/ -bool EIPP::OrderInstall(char const * const solver, pkgDepCache &Cache, /*{{{*/ +bool EIPP::OrderInstall(char const * const solver, pkgPackageManager * const PM, /*{{{*/ unsigned int const flags, OpProgress * const Progress) { + if (strcmp(solver, "internal") == 0) + { + auto const dumpfile = _config->FindFile("Dir::Log::Planner"); + if (dumpfile.empty()) + return false; + auto const dumpdir = flNotFile(dumpfile); + FileFd output; + _error->PushToStack(); + bool errored_out = CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false || + output.Open(dumpfile, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false; + std::vector downgrademsgs; + while (_error->empty() == false) + { + std::string msg; + _error->PopMessage(msg); + downgrademsgs.emplace_back(std::move(msg)); + } + _error->RevertToStack(); + for (auto && msg : downgrademsgs) + _error->Warning("%s", msg.c_str()); + if (errored_out) + return _error->WarningE("EIPP::OrderInstall", _("Could not open file '%s'"), dumpfile.c_str()); + bool Okay = EIPP::WriteRequest(PM->Cache, output, flags, nullptr); + return Okay && EIPP::WriteScenario(PM->Cache, output, nullptr); + } + int solver_in, solver_out; - pid_t const solver_pid = ExecuteExternal("planer", solver, "Dir::Bin::Planers", &solver_in, &solver_out); + pid_t const solver_pid = ExecuteExternal("planner", solver, "Dir::Bin::Planners", &solver_in, &solver_out); if (solver_pid == 0) return false; FileFd output; if (output.OpenDescriptor(solver_in, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false) - return _error->Errno("OrderInstall", "Opening planer %s stdin on fd %d for writing failed", solver, solver_in); + return _error->Errno("EIPP::OrderInstall", "Opening planner %s stdin on fd %d for writing failed", solver, solver_in); bool Okay = output.Failed() == false; if (Progress != NULL) - Progress->OverallProgress(0, 100, 5, _("Execute external planer")); - Okay &= EIPP::WriteRequest(Cache, output, flags, Progress); + Progress->OverallProgress(0, 100, 5, _("Execute external planner")); + Okay &= EIPP::WriteRequest(PM->Cache, output, flags, Progress); if (Progress != NULL) - Progress->OverallProgress(5, 100, 20, _("Execute external planer")); - Okay &= EIPP::WriteScenario(Cache, output, Progress); + Progress->OverallProgress(5, 100, 20, _("Execute external planner")); + Okay &= EIPP::WriteScenario(PM->Cache, output, Progress); output.Close(); if (Progress != NULL) - Progress->OverallProgress(25, 100, 75, _("Execute external planer")); - if (Okay && EIPP::ReadResponse(solver_out, Cache, Progress) == false) + Progress->OverallProgress(25, 100, 75, _("Execute external planner")); + + // we don't tell the external planners about boring things + for (auto Pkg = PM->Cache.PkgBegin(); Pkg.end() == false; ++Pkg) + { + if (Pkg->CurrentState == pkgCache::State::ConfigFiles && PM->Cache[Pkg].Purge() == true) + PM->Remove(Pkg, true); + } + + if (Okay && EIPP::ReadResponse(solver_out, PM, Progress) == false) return false; return ExecWait(solver_pid, solver); @@ -1098,22 +1150,21 @@ bool EIPP::WriteRequest(pkgDepCache &Cache, FileFd &output, /*{{{*/ unsigned int const flags, OpProgress * const Progress) { - (void)(flags); if (Progress != NULL) - Progress->SubProgress(Cache.Head().PackageCount, _("Send request to planer")); + Progress->SubProgress(Cache.Head().PackageCount, _("Send request to planner")); unsigned long p = 0; - string del, purge, inst, reinst; + string del, inst, reinst; for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p) { if (Progress != NULL && p % 100 == 0) Progress->Progress(p); string* req; pkgDepCache::StateCache &P = Cache[Pkg]; - if (P.Purge() == true) - req = &purge; + if (P.Purge() == true && Pkg->CurrentState == pkgCache::State::ConfigFiles) + continue; if (P.Delete() == true) req = &del; - else if (P.NewInstall() == true || P.Upgrade() == true) + else if (P.NewInstall() == true || P.Upgrade() == true || P.Downgrade() == true) req = &inst; else if (P.ReInstall() == true) req = &reinst; @@ -1131,19 +1182,23 @@ bool EIPP::WriteRequest(pkgDepCache &Cache, FileFd &output, /*{{{*/ WriteOkay(Okay, output, " ", *a); WriteOkay(Okay, output, "\n"); - if (purge.empty() == false) - WriteOkay(Okay, output, "Purge:", purge, "\n"); if (del.empty() == false) WriteOkay(Okay, output, "Remove:", del, "\n"); if (inst.empty() == false) WriteOkay(Okay, output, "Install:", inst, "\n"); if (reinst.empty() == false) WriteOkay(Okay, output, "ReInstall:", reinst, "\n"); - WriteOkay(Okay, output, "Planer: ", _config->Find("APT::Planer", "internal"), "\n"); + WriteOkay(Okay, output, "Planner: ", _config->Find("APT::Planner", "internal"), "\n"); + if ((flags & Request::IMMEDIATE_CONFIGURATION_ALL) != 0) + WriteOkay(Okay, output, "Immediate-Configuration: yes\n"); + else if ((flags & Request::NO_IMMEDIATE_CONFIGURATION) != 0) + WriteOkay(Okay, output, "Immediate-Configuration: no\n"); + else if ((flags & Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS) != 0) + WriteOkay(Okay, output, "Allow-Temporary-Remove-of-Essentials: yes\n"); return WriteOkay(Okay, output, "\n"); } /*}}}*/ -static bool WriteScenarioEIPPVersion(pkgDepCache &Cache, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/ +static bool WriteScenarioEIPPVersion(pkgDepCache &, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/ pkgCache::VerIterator const &Ver) { bool Okay = true; @@ -1159,63 +1214,123 @@ static bool WriteScenarioEIPPVersion(pkgDepCache &Cache, FileFd &output, pkgCach case pkgCache::State::TriggersPending: WriteOkay(Okay, output, "\nStatus: triggers-pending"); break; case pkgCache::State::Installed: WriteOkay(Okay, output, "\nStatus: installed"); break; } - if (Pkg->SelectedState == pkgCache::State::Hold) - WriteOkay(Okay, output, "\nHold: yes"); - // FIXME: Ideally, an EIPP request contains at most two versions (installed and to install) - if (Cache.GetCandidateVersion(Pkg) == Ver) - WriteOkay(Okay, output, "\nAPT-Candidate: yes"); return Okay; } /*}}}*/ // EIPP::WriteScenario - to the given file descriptor /*{{{*/ +template void forAllInterestingVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, forVersion const &func) +{ + if (Pkg->CurrentState == pkgCache::State::NotInstalled) + { + auto P = Cache[Pkg]; + if (P.Install() == false) + return; + func(Pkg, P.InstVerIter(Cache)); + } + else + { + if (Pkg->CurrentVer != 0) + func(Pkg, Pkg.CurrentVer()); + auto P = Cache[Pkg]; + auto const V = P.InstVerIter(Cache); + if (P.Delete() == false && Pkg.CurrentVer() != V) + func(Pkg, V); + } +} + bool EIPP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress * const Progress) { if (Progress != NULL) - Progress->SubProgress(Cache.Head().VersionCount, _("Send scenario to planer")); + Progress->SubProgress(Cache.Head().PackageCount, _("Send scenario to planner")); unsigned long p = 0; bool Okay = output.Failed() == false; std::vector archs = APT::Configuration::getArchitectures(); - std::vector pkgset(Cache.Head().VersionCount, true); - for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg) - { - std::string const arch = Pkg.Arch(); - if (std::find(archs.begin(), archs.end(), arch) == archs.end()) - continue; - for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false && likely(Okay); ++Ver, ++p) + std::vector pkgset(Cache.Head().PackageCount, false); + auto const MarkVersion = [&](pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver) { + pkgset[Pkg->ID] = true; + for (auto D = Ver.DependsList(); D.end() == false; ++D) { - Okay &= WriteScenarioVersion(output, Pkg, Ver); - Okay &= WriteScenarioEIPPVersion(Cache, output, Pkg, Ver); - Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset, true); - WriteOkay(Okay, output, "\n"); - if (Progress != NULL && p % 100 == 0) - Progress->Progress(p); + if (D.IsCritical() == false) + continue; + auto const P = D.TargetPkg(); + for (auto Prv = P.ProvidesList(); Prv.end() == false; ++Prv) + { + auto const V = Prv.OwnerVer(); + auto const PV = V.ParentPkg(); + if (V == PV.CurrentVer() || V == Cache[PV].InstVerIter(Cache)) + pkgset[PV->ID] = true; + } + pkgset[P->ID] = true; + if (strcmp(P.Arch(), "any") == 0) + { + APT::StringView const pkgname(P.Name()); + auto const idxColon = pkgname.find(':'); + if (idxColon != APT::StringView::npos) + { + pkgCache::PkgIterator PA; + if (pkgname.substr(idxColon + 1) == "any") + { + auto const GA = Cache.FindGrp(pkgname.substr(0, idxColon).to_string()); + for (auto PA = GA.PackageList(); PA.end() == false; PA = GA.NextPkg(PA)) + { + pkgset[PA->ID] = true; + } + } + else + { + auto const PA = Cache.FindPkg(pkgname.to_string()); + if (PA.end() == false) + pkgset[PA->ID] = true; + } + } + } + else + { + auto const PA = Cache.FindPkg(P.FullName(false), "any"); + if (PA.end() == false) + pkgset[PA->ID] = true; + } } + }; + for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg) + forAllInterestingVersions(Cache, Pkg, MarkVersion); + auto const WriteVersion = [&](pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver) { + Okay &= WriteScenarioVersion(output, Pkg, Ver); + Okay &= WriteScenarioEIPPVersion(Cache, output, Pkg, Ver); + Okay &= WriteScenarioLimitedDependency(output, Ver, pkgset, true); + WriteOkay(Okay, output, "\n"); + if (Progress != NULL && p % 100 == 0) + Progress->Progress(p); + }; + for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false && likely(Okay); ++Pkg, ++p) + { + if (pkgset[Pkg->ID] == false || Pkg->VersionList == 0) + continue; + forAllInterestingVersions(Cache, Pkg, WriteVersion); } - return true; + return Okay; } /*}}}*/ // EIPP::ReadResponse - from the given file descriptor /*{{{*/ -bool EIPP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) { +bool EIPP::ReadResponse(int const input, pkgPackageManager * const PM, OpProgress *Progress) { /* We build an map id to mmap offset here In theory we could use the offset as ID, but then VersionCount couldn't be used to create other versionmappings anymore and it would be too easy for a (buggy) solver to segfault APT… */ - /* - unsigned long long const VersionCount = Cache.Head().VersionCount; + unsigned long long const VersionCount = PM->Cache.Head().VersionCount; unsigned long VerIdx[VersionCount]; - for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) { + for (pkgCache::PkgIterator P = PM->Cache.PkgBegin(); P.end() == false; ++P) { for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V) VerIdx[V->ID] = V.Index(); } - */ FileFd in; in.OpenDescriptor(input, FileFd::ReadOnly); pkgTagFile response(&in, 100); pkgTagSection section; - std::setID)> seenOnce; while (response.Step(section) == true) { + char const * type = nullptr; if (section.Exists("Progress") == true) { if (Progress != NULL) { string msg = section.FindS("Message"); @@ -1227,18 +1342,146 @@ bool EIPP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progres } else if (section.Exists("Error") == true) { std::string msg = SubstVar(SubstVar(section.FindS("Message"), "\n .\n", "\n\n"), "\n ", "\n"); if (msg.empty() == true) { - msg = _("External planer failed without a proper error message"); + msg = _("External planner failed without a proper error message"); _error->Error("%s", msg.c_str()); } else - _error->Error("External planer failed with: %s", msg.substr(0,msg.find('\n')).c_str()); + _error->Error("External planner failed with: %s", msg.substr(0,msg.find('\n')).c_str()); if (Progress != NULL) Progress->Done(); - std::cerr << "The planer encountered an error of type: " << section.FindS("Error") << std::endl; + std::cerr << "The planner encountered an error of type: " << section.FindS("Error") << std::endl; std::cerr << "The following information might help you to understand what is wrong:" << std::endl; std::cerr << msg << std::endl << std::endl; return false; - } else { - _error->Warning("Encountered an unexpected section with %d fields", section.Count()); + } else if (section.Exists("Unpack") == true) + type = "Unpack"; + else if (section.Exists("Configure") == true) + type = "Configure"; + else if (section.Exists("Remove") == true) + type = "Remove"; + else { + char const *Start, *End; + section.GetSection(Start, End); + _error->Warning("Encountered an unexpected section with %d fields: %s", section.Count(), std::string(Start, End).c_str()); + continue; + } + + if (type == nullptr) + continue; + size_t const id = section.FindULL(type, VersionCount); + if (id == VersionCount) { + _error->Warning("Unable to parse %s request with id value '%s'!", type, section.FindS(type).c_str()); + continue; + } else if (id > PM->Cache.Head().VersionCount) { + _error->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section.FindS(type).c_str(), type); + continue; + } + + pkgCache::VerIterator Ver(PM->Cache.GetCache(), PM->Cache.GetCache().VerP + VerIdx[id]); + auto const Pkg = Ver.ParentPkg(); + if (strcmp(type, "Unpack") == 0) + PM->Install(Pkg, PM->FileNames[Pkg->ID]); + else if (strcmp(type, "Configure") == 0) + PM->Configure(Pkg); + else if (strcmp(type, "Remove") == 0) + PM->Remove(Pkg, PM->Cache[Pkg].Purge()); + } + return in.Failed() == false; +} + /*}}}*/ +bool EIPP::ReadRequest(int const input, std::list> &actions,/*{{{*/ + unsigned int &flags) +{ + actions.clear(); + flags = 0; + std::string line; + while (ReadLine(input, line) == true) + { + // Skip empty lines before request + if (line.empty() == true) + continue; + // The first Tag must be a request, so search for it + if (line.compare(0, 8, "Request:") != 0) + continue; + + while (ReadLine(input, line) == true) + { + // empty lines are the end of the request + if (line.empty() == true) + return true; + + PKG_ACTION pkgact = PKG_ACTION::NOOP; + if (LineStartsWithAndStrip(line, "Install:")) + pkgact = PKG_ACTION::INSTALL; + else if (LineStartsWithAndStrip(line, "ReInstall:")) + pkgact = PKG_ACTION::REINSTALL; + else if (LineStartsWithAndStrip(line, "Remove:")) + pkgact = PKG_ACTION::REMOVE; + else if (LineStartsWithAndStrip(line, "Architecture:")) + _config->Set("APT::Architecture", line); + else if (LineStartsWithAndStrip(line, "Architectures:")) + _config->Set("APT::Architectures", SubstVar(line, " ", ",")); + else if (LineStartsWithAndStrip(line, "Planner:")) + ; // purely informational line + else if (LineStartsWithAndStrip(line, "Immediate-Configuration:")) + { + if (localStringToBool(line, true)) + flags |= Request::IMMEDIATE_CONFIGURATION_ALL; + else + flags |= Request::NO_IMMEDIATE_CONFIGURATION; + } + else if (ReadFlag(flags, line, "Allow-Temporary-Remove-of-Essentials:", Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS)) + ; + else + _error->Warning("Unknown line in EIPP Request stanza: %s", line.c_str()); + + if (pkgact == PKG_ACTION::NOOP) + continue; + for (auto && p: VectorizeString(line, ' ')) + actions.emplace_back(std::move(p), pkgact); + } + } + return false; +} + /*}}}*/ +bool EIPP::ApplyRequest(std::list> &actions,/*{{{*/ + pkgDepCache &Cache) +{ + for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg) + { + short versions = 0; + for (auto Ver = Pkg.VersionList(); Ver.end() == false; ++Ver) + { + ++versions; + if (Pkg.CurrentVer() == Ver) + continue; + Cache.SetCandidateVersion(Ver); + } + if (unlikely(versions > 2)) + _error->Warning("Package %s has %d versions, but should have at most 2!", Pkg.FullName().c_str(), versions); + } + for (auto && a: actions) + { + pkgCache::PkgIterator P = Cache.FindPkg(a.first); + if (P.end() == true) + { + _error->Warning("Package %s is not known, so can't be acted on", a.first.c_str()); + continue; + } + switch (a.second) + { + case PKG_ACTION::NOOP: + _error->Warning("Package %s has NOOP as action?!?", a.first.c_str()); + break; + case PKG_ACTION::INSTALL: + Cache.MarkInstall(P, false); + break; + case PKG_ACTION::REINSTALL: + Cache.MarkInstall(P, false); + Cache.SetReInstall(P, true); + break; + case PKG_ACTION::REMOVE: + Cache.MarkDelete(P); + break; } } return true;