// Include Files /*{{{*/
#include <config.h>
+#include <apt-pkg/algorithms.h>
#include <apt-pkg/error.h>
#include <apt-pkg/cacheset.h>
#include <apt-pkg/depcache.h>
#include <apt-pkg/pkgcache.h>
#include <apt-pkg/cacheiterators.h>
+#include <apt-pkg/prettyprinters.h>
+#include <apt-pkg/packagemanager.h>
#include <apt-pkg/progress.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/edsp.h>
static bool SkipUnavailableVersions(pkgDepCache &Cache, pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const &Ver)/*{{{*/
{
/* versions which aren't current and aren't available in
- any "online" source file are bad, expect if they are the choosen
+ any "online" source file are bad, expect if they are the chosen
candidate: The exception is for build-dep implementation as it creates
such pseudo (package) versions and removes them later on again.
We filter out versions at all so packages in 'rc' state only available
Progress->Progress(p);
}
}
- return true;
+ return Okay;
}
/*}}}*/
// EDSP::WriteLimitedScenario - to the given file descriptor /*{{{*/
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) {
/*}}}*/
// 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();
}
return true;
}
/*}}}*/
+static bool CreateDumpFile(char const * const id, char const * const type, FileFd &output)/*{{{*/
+{
+ auto const dumpfile = _config->FindFile((std::string("Dir::Log::") + type).c_str());
+ if (dumpfile.empty())
+ return false;
+ auto const dumpdir = flNotFile(dumpfile);
+ _error->PushToStack();
+ bool errored_out = CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false ||
+ output.Open(dumpfile, FileFd::WriteOnly | FileFd::Exclusive | FileFd::Create, FileFd::Extension, 0644) == false;
+ std::vector<std::string> 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(id, _("Could not open file '%s'"), dumpfile.c_str());
+ return true;
+}
+ /*}}}*/
// EDSP::ResolveExternal - resolve problems by asking external for help {{{*/
bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
unsigned int const flags, OpProgress *Progress) {
if (strcmp(solver, "internal") == 0)
{
- auto const dumpfile = _config->FindFile("Dir::Log::Solver");
- if (dumpfile.empty())
- 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)
- return _error->WarningE("EDSP::Resolve", _("Could not open file '%s'"), dumpfile.c_str());
- bool Okay = EDSP::WriteRequest(Cache, output, flags, nullptr);
+ bool Okay = CreateDumpFile("EDSP::Resolve", "solver", output);
+ Okay &= EDSP::WriteRequest(Cache, output, flags, nullptr);
return Okay && EDSP::WriteScenario(Cache, output, nullptr);
}
int solver_in, solver_out;
return _error->Errno("ResolveExternal", "Opening solver %s stdin on fd %d for writing failed", solver, solver_in);
bool Okay = output.Failed() == false;
- if (Progress != NULL)
+ if (Okay && Progress != NULL)
Progress->OverallProgress(0, 100, 5, _("Execute external solver"));
Okay &= EDSP::WriteRequest(Cache, output, flags, Progress);
- if (Progress != NULL)
+ if (Okay && Progress != NULL)
Progress->OverallProgress(5, 100, 20, _("Execute external solver"));
Okay &= EDSP::WriteScenario(Cache, output, Progress);
output.Close();
- if (Progress != NULL)
+ if (Okay && Progress != NULL)
Progress->OverallProgress(25, 100, 75, _("Execute external solver"));
if (Okay && EDSP::ReadResponse(solver_out, Cache, Progress) == false)
return false;
- return ExecWait(solver_pid, solver);
+ bool const waited = ExecWait(solver_pid, solver);
+ return Okay && waited;
}
bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
bool const upgrade, bool const distUpgrade,
}
/*}}}*/
-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)
+ {
+ FileFd output;
+ _error->PushToStack();
+ bool Okay = CreateDumpFile("EIPP::OrderInstall", "planner", output);
+ if (Okay == false && dynamic_cast<pkgSimulate*>(PM) != nullptr)
+ {
+ _error->RevertToStack();
+ return false;
+ }
+ _error->MergeWithStack();
+ 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);
- if (Progress != NULL)
- Progress->OverallProgress(5, 100, 20, _("Execute external planer"));
- Okay &= EIPP::WriteScenario(Cache, output, Progress);
+ if (Okay && Progress != NULL)
+ Progress->OverallProgress(0, 100, 5, _("Execute external planner"));
+ Okay &= EIPP::WriteRequest(PM->Cache, output, flags, Progress);
+ if (Okay && Progress != NULL)
+ 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)
+ if (Okay)
+ {
+ if (Progress != nullptr)
+ 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 (EIPP::ReadResponse(solver_out, PM, Progress) == false)
return false;
- return ExecWait(solver_pid, solver);
+ bool const waited = ExecWait(solver_pid, solver);
+ return Okay && waited;
}
/*}}}*/
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;
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");
}
/*}}}*/
bool EIPP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress * const Progress)
{
if (Progress != NULL)
- Progress->SubProgress(Cache.Head().PackageCount, _("Send scenario to planer"));
+ Progress->SubProgress(Cache.Head().PackageCount, _("Send scenario to planner"));
unsigned long p = 0;
bool Okay = output.Failed() == false;
std::vector<std::string> archs = APT::Configuration::getArchitectures();
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)
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::set<decltype(Cache.PkgBegin()->ID)> seenOnce;
while (response.Step(section) == true) {
+ char const * type = nullptr;
if (section.Exists("Progress") == true) {
if (Progress != NULL) {
string msg = section.FindS("Message");
} 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<std::pair<std::string,PKG_ACTION>> &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<std::pair<std::string,PKG_ACTION>> &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;