#include <apt-pkg/string_view.h>
#include <apt-pkg/pkgsystem.h>
+#include <sys/stat.h>
#include <ctype.h>
#include <stddef.h>
#include <string.h>
WriteOkay(Okay, output, "Forbid-New-Install: yes\n");
if (flags & Request::FORBID_REMOVE)
WriteOkay(Okay, output, "Forbid-Remove: yes\n");
+ auto const solver = _config->Find("APT::Solver", "internal");
+ WriteOkay(Okay, output, "Solver: ", solver, "\n");
if (_config->FindB("APT::Solver::Strict-Pinning", true) == false)
WriteOkay(Okay, output, "Strict-Pinning: no\n");
string solverpref("APT::Solver::");
- solverpref.append(_config->Find("APT::Solver", "internal")).append("::Preferences");
+ solverpref.append(solver).append("::Preferences");
if (_config->Exists(solverpref) == true)
WriteOkay(Okay, output, "Preferences: ", _config->Find(solverpref,""), "\n");
return WriteOkay(Okay, output, "\n");
}
FileFd in;
- in.OpenDescriptor(input, FileFd::ReadOnly);
+ in.OpenDescriptor(input, FileFd::ReadOnly, true);
pkgTagFile response(&in, 100);
pkgTagSection section;
"\n\n");
}
/*}}}*/
+static std::string findExecutable(std::vector<std::string> const &dirs, char const * const binary) {/*{{{*/
+ for (auto && dir : dirs) {
+ std::string const file = flCombine(dir, binary);
+ if (RealFileExists(file) == true)
+ return file;
+ }
+ return "";
+}
+ /*}}}*/
static pid_t ExecuteExternal(char const* const type, char const * const binary, char const * const configdir, int * const solver_in, int * const solver_out) {/*{{{*/
- std::vector<std::string> const solverDirs = _config->FindVector(configdir);
- std::string file;
- for (std::vector<std::string>::const_iterator dir = solverDirs.begin();
- dir != solverDirs.end(); ++dir) {
- file = flCombine(*dir, binary);
- if (RealFileExists(file.c_str()) == true)
- break;
- file.clear();
+ auto const solverDirs = _config->FindVector(configdir);
+ auto const file = findExecutable(solverDirs, binary);
+ std::string dumper;
+ {
+ dumper = findExecutable(solverDirs, "apt-dump-solver");
+ if (dumper.empty())
+ dumper = findExecutable(solverDirs, "dump");
}
if (file.empty() == true)
if (Solver == 0) {
dup2(external[0], STDIN_FILENO);
dup2(external[3], STDOUT_FILENO);
- const char* calling[2] = { file.c_str(), 0 };
- execv(calling[0], (char**) calling);
+ auto const dumpfile = _config->FindFile((std::string("Dir::Log::") + type).c_str());
+ auto const dumpdir = flNotFile(dumpfile);
+ auto const runasuser = _config->Find(std::string("APT::") + type + "::" + binary + "::RunAsUser",
+ _config->Find(std::string("APT::") + type + "::RunAsUser",
+ _config->Find("APT::Sandbox::User")));
+ if (dumper.empty() || dumpfile.empty() || dumper == file || CreateAPTDirectoryIfNeeded(dumpdir, dumpdir) == false)
+ {
+ _config->Set("APT::Sandbox::User", runasuser);
+ DropPrivileges();
+ char const * const calling[] = { file.c_str(), nullptr };
+ execv(calling[0], const_cast<char**>(calling));
+ }
+ else
+ {
+ char const * const calling[] = { dumper.c_str(), "--user", runasuser.c_str(), dumpfile.c_str(), file.c_str(), nullptr };
+ execv(calling[0], const_cast<char**>(calling));
+ }
std::cerr << "Failed to execute " << type << " '" << binary << "'!" << std::endl;
_exit(100);
}
// 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);
+ return Okay && EDSP::WriteScenario(Cache, output, nullptr);
+ }
int solver_in, solver_out;
pid_t const solver_pid = EDSP::ExecuteSolver(solver, &solver_in, &solver_out, true);
if (solver_pid == 0)
return ResolveExternal(solver, Cache, flags, Progress);
}
/*}}}*/
+
+bool EIPP::OrderInstall(char const * const solver, pkgDepCache &Cache, /*{{{*/
+ unsigned int const flags, OpProgress * const Progress)
+{
+ int solver_in, solver_out;
+ pid_t const solver_pid = ExecuteExternal("planer", solver, "Dir::Bin::Planers", &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);
+
+ 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);
+ output.Close();
+
+ if (Progress != NULL)
+ Progress->OverallProgress(25, 100, 75, _("Execute external planer"));
+ if (Okay && EIPP::ReadResponse(solver_out, Cache, Progress) == false)
+ return false;
+
+ return ExecWait(solver_pid, solver);
+}
+ /*}}}*/
+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"));
+ unsigned long p = 0;
+ string del, purge, 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.Delete() == true)
+ req = &del;
+ else if (P.NewInstall() == true || P.Upgrade() == true)
+ req = &inst;
+ else if (P.ReInstall() == true)
+ req = &reinst;
+ else
+ continue;
+ req->append(" ").append(Pkg.FullName());
+ }
+ bool Okay = WriteOkay(output, "Request: EIPP 0.1\n");
+
+ const char *arch = _config->Find("APT::Architecture").c_str();
+ std::vector<string> archs = APT::Configuration::getArchitectures();
+ WriteOkay(Okay, output, "Architecture: ", arch, "\n",
+ "Architectures:");
+ for (std::vector<string>::const_iterator a = archs.begin(); a != archs.end(); ++a)
+ 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");
+ return WriteOkay(Okay, output, "\n");
+}
+ /*}}}*/
+static bool WriteScenarioEIPPVersion(pkgDepCache &, FileFd &output, pkgCache::PkgIterator const &Pkg,/*{{{*/
+ pkgCache::VerIterator const &Ver)
+{
+ bool Okay = true;
+ if (Pkg.CurrentVer() == Ver)
+ switch (Pkg->CurrentState)
+ {
+ case pkgCache::State::NotInstalled: WriteOkay(Okay, output, "\nStatus: not-installed"); break;
+ case pkgCache::State::ConfigFiles: WriteOkay(Okay, output, "\nStatus: config-files"); break;
+ case pkgCache::State::HalfInstalled: WriteOkay(Okay, output, "\nStatus: half-installed"); break;
+ case pkgCache::State::UnPacked: WriteOkay(Okay, output, "\nStatus: unpacked"); break;
+ case pkgCache::State::HalfConfigured: WriteOkay(Okay, output, "\nStatus: half-configured"); break;
+ case pkgCache::State::TriggersAwaited: WriteOkay(Okay, output, "\nStatus: triggers-awaited"); break;
+ case pkgCache::State::TriggersPending: WriteOkay(Okay, output, "\nStatus: triggers-pending"); break;
+ case pkgCache::State::Installed: WriteOkay(Okay, output, "\nStatus: installed"); break;
+ }
+ return Okay;
+}
+ /*}}}*/
+// EIPP::WriteScenario - to the given file descriptor /*{{{*/
+template<typename forVersion> 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().PackageCount, _("Send scenario to planer"));
+ unsigned long p = 0;
+ bool Okay = output.Failed() == false;
+ std::vector<std::string> archs = APT::Configuration::getArchitectures();
+ std::vector<bool> 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)
+ {
+ 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;
+ }
+ };
+ 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;
+}
+ /*}}}*/
+// EIPP::ReadResponse - from the given file descriptor /*{{{*/
+bool EIPP::ReadResponse(int const input, pkgDepCache &Cache, 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 VerIdx[VersionCount];
+ for (pkgCache::PkgIterator P = 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) {
+ if (section.Exists("Progress") == true) {
+ if (Progress != NULL) {
+ string msg = section.FindS("Message");
+ if (msg.empty() == true)
+ msg = _("Prepare for receiving solution");
+ Progress->SubProgress(100, msg, section.FindI("Percentage", 0));
+ }
+ continue;
+ } 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");
+ _error->Error("%s", msg.c_str());
+ } else
+ _error->Error("External planer 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 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());
+ }
+ }
+ return true;
+}
+ /*}}}*/