diff options
author | David Kalnischkies <david@kalnischkies.de> | 2016-05-28 15:40:59 +0200 |
---|---|---|
committer | David Kalnischkies <david@kalnischkies.de> | 2016-06-27 11:57:12 +0200 |
commit | f74d99c6a78caafdc6e32d8cb135683b7154795c (patch) | |
tree | 3056bcf8fa40dd70c65ae073e634aa142ab8e628 | |
parent | dae197476f1831269d13f4e990276ce25c483842 (diff) |
eipp: provide the internal planer as an external one
Testing the current implementation can benefit from being able to be
feed an EIPP request and produce a fully compliant response. It is also
a great test for EIPP in general.
-rw-r--r-- | apt-pkg/edsp.cc | 154 | ||||
-rw-r--r-- | apt-pkg/edsp.h | 21 | ||||
-rw-r--r-- | apt-pkg/edsp/edspindexfile.cc | 37 | ||||
-rw-r--r-- | apt-pkg/edsp/edspindexfile.h | 13 | ||||
-rw-r--r-- | apt-pkg/edsp/edsplistparser.cc | 54 | ||||
-rw-r--r-- | apt-pkg/edsp/edsplistparser.h | 10 | ||||
-rw-r--r-- | apt-pkg/edsp/edspsystem.cc | 18 | ||||
-rw-r--r-- | apt-pkg/edsp/edspsystem.h | 9 | ||||
-rw-r--r-- | apt-pkg/packagemanager.cc | 6 | ||||
-rw-r--r-- | apt-pkg/packagemanager.h | 9 | ||||
-rw-r--r-- | apt-private/private-cmndline.cc | 10 | ||||
-rw-r--r-- | apt-private/private-cmndline.h | 1 | ||||
-rw-r--r-- | apt-private/private-main.cc | 1 | ||||
-rw-r--r-- | cmdline/apt-internal-planer.cc | 187 | ||||
-rw-r--r-- | cmdline/makefile | 8 | ||||
-rw-r--r-- | debian/apt-utils.dirs | 1 | ||||
-rw-r--r-- | debian/apt.dirs | 3 | ||||
-rw-r--r-- | debian/apt.lintian-overrides | 2 | ||||
-rwxr-xr-x | debian/rules | 4 | ||||
-rw-r--r-- | debian/tests/run-tests | 1 | ||||
-rw-r--r-- | test/integration/framework | 3 | ||||
-rwxr-xr-x | test/integration/test-00-commands-have-help | 2 | ||||
-rwxr-xr-x | test/integration/test-external-installation-planer-protocol | 3 |
23 files changed, 530 insertions, 27 deletions
diff --git a/apt-pkg/edsp.cc b/apt-pkg/edsp.cc index e37ab04b4..ae5c7a373 100644 --- a/apt-pkg/edsp.cc +++ b/apt-pkg/edsp.cc @@ -12,6 +12,8 @@ #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> @@ -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) { @@ -1065,7 +1071,7 @@ 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) { int solver_in, solver_out; @@ -1080,15 +1086,15 @@ bool EIPP::OrderInstall(char const * const solver, pkgDepCache &Cache, /*{{{*/ bool Okay = output.Failed() == false; if (Progress != NULL) Progress->OverallProgress(0, 100, 5, _("Execute external planer")); - Okay &= EIPP::WriteRequest(Cache, output, flags, Progress); + 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); + 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 && EIPP::ReadResponse(solver_out, PM, Progress) == false) return false; return ExecWait(solver_pid, solver); @@ -1113,7 +1119,7 @@ bool EIPP::WriteRequest(pkgDepCache &Cache, FileFd &output, /*{{{*/ req = &purge; 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; @@ -1228,27 +1234,25 @@ bool EIPP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress * const } /*}}}*/ // 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"); @@ -1270,8 +1274,130 @@ bool EIPP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progres 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("Install") == true) + type = "Install"; + 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, "Install") == 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 true; +} + /*}}}*/ +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, "Planer:")) + ; // purely informational line + 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; + + + 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; diff --git a/apt-pkg/edsp.h b/apt-pkg/edsp.h index 3e0982a56..271cbb6a8 100644 --- a/apt-pkg/edsp.h +++ b/apt-pkg/edsp.h @@ -236,16 +236,31 @@ namespace EDSP /*{{{*/ bool const autoRemove, OpProgress *Progress = NULL); } /*}}}*/ +class pkgPackageManager; namespace EIPP /*{{{*/ { - APT_HIDDEN bool OrderInstall(char const * const solver, pkgDepCache &Cache, - unsigned int const version, OpProgress * const Progress); APT_HIDDEN bool WriteRequest(pkgDepCache &Cache, FileFd &output, unsigned int const version, OpProgress * const Progress); APT_HIDDEN bool WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress * const Progress); - APT_HIDDEN bool ReadResponse(int const input, pkgDepCache &Cache, + + APT_HIDDEN bool OrderInstall(char const * const planer, pkgPackageManager * const PM, + unsigned int const version, OpProgress * const Progress); + APT_HIDDEN bool ReadResponse(int const input, pkgPackageManager * const PM, OpProgress * const Progress); + + enum class PKG_ACTION + { + NOOP, + INSTALL, + REINSTALL, + REMOVE + }; + bool ReadRequest(int const input, + std::list<std::pair<std::string,PKG_ACTION>> &actions, + unsigned int &flags); + bool ApplyRequest(std::list<std::pair<std::string,PKG_ACTION>> &actions, + pkgDepCache &Cache); } /*}}}*/ #endif diff --git a/apt-pkg/edsp/edspindexfile.cc b/apt-pkg/edsp/edspindexfile.cc index e2863a2cc..042a88cf9 100644 --- a/apt-pkg/edsp/edspindexfile.cc +++ b/apt-pkg/edsp/edspindexfile.cc @@ -69,6 +69,25 @@ pkgCacheListParser * edspIndex::CreateListParser(FileFd &Pkg) return newError ? NULL : Parser; } /*}}}*/ +// EIPP Index /*{{{*/ +eippIndex::eippIndex(std::string const &File) : edspLikeIndex(File) +{ +} +std::string eippIndex::GetComponent() const +{ + return "eipp"; +} +pkgCacheListParser * eippIndex::CreateListParser(FileFd &Pkg) +{ + if (Pkg.IsOpen() == false) + return NULL; + _error->PushToStack(); + pkgCacheListParser * const Parser = new eippListParser(&Pkg); + bool const newError = _error->PendingError(); + _error->MergeWithStack(); + return newError ? NULL : Parser; +} + /*}}}*/ // Index File types for APT /*{{{*/ class APT_HIDDEN edspIFType: public pkgIndexFile::Type @@ -82,12 +101,28 @@ class APT_HIDDEN edspIFType: public pkgIndexFile::Type edspIFType() {Label = "EDSP scenario file";}; }; APT_HIDDEN edspIFType _apt_Edsp; - const pkgIndexFile::Type *edspIndex::GetType() const { return &_apt_Edsp; } + +class APT_HIDDEN eippIFType: public pkgIndexFile::Type +{ + public: + virtual pkgRecords::Parser *CreatePkgParser(pkgCache::PkgFileIterator const &) const APT_OVERRIDE + { + // we don't have a record parser for this type as the file is not presistent + return NULL; + }; + eippIFType() {Label = "EIPP scenario file";}; +}; +APT_HIDDEN eippIFType _apt_Eipp; +const pkgIndexFile::Type *eippIndex::GetType() const +{ + return &_apt_Eipp; +} /*}}}*/ edspLikeIndex::~edspLikeIndex() {} edspIndex::~edspIndex() {} +eippIndex::~eippIndex() {} diff --git a/apt-pkg/edsp/edspindexfile.h b/apt-pkg/edsp/edspindexfile.h index 26bd1232b..e146ca80c 100644 --- a/apt-pkg/edsp/edspindexfile.h +++ b/apt-pkg/edsp/edspindexfile.h @@ -46,4 +46,17 @@ public: virtual ~edspIndex(); }; +class APT_HIDDEN eippIndex : public edspLikeIndex +{ +protected: + APT_HIDDEN virtual pkgCacheListParser * CreateListParser(FileFd &Pkg) APT_OVERRIDE; + virtual std::string GetComponent() const APT_OVERRIDE; + +public: + virtual const Type *GetType() const APT_OVERRIDE APT_CONST; + + eippIndex(std::string const &File); + virtual ~eippIndex(); +}; + #endif diff --git a/apt-pkg/edsp/edsplistparser.cc b/apt-pkg/edsp/edsplistparser.cc index 39a6e8a6e..dd8890f0e 100644 --- a/apt-pkg/edsp/edsplistparser.cc +++ b/apt-pkg/edsp/edsplistparser.cc @@ -125,5 +125,59 @@ bool edspListParser::ParseStatus(pkgCache::PkgIterator &Pkg, } /*}}}*/ +// ListParser::eippListParser - Constructor /*{{{*/ +eippListParser::eippListParser(FileFd *File) : edspLikeListParser(File) +{ +} + /*}}}*/ +// ListParser::ParseStatus - Parse the status field /*{{{*/ +// --------------------------------------------------------------------- +/* The Status: line here is not a normal dpkg one but just one which tells + use if the package is installed or not, where missing means not. */ +bool eippListParser::ParseStatus(pkgCache::PkgIterator &Pkg, + pkgCache::VerIterator &Ver) +{ + // Process the flag field + static std::array<WordList, 8> const statusvalues = {{ + {"not-installed",pkgCache::State::NotInstalled}, + {"config-files",pkgCache::State::ConfigFiles}, + {"half-installed",pkgCache::State::HalfInstalled}, + {"unpacked",pkgCache::State::UnPacked}, + {"half-configured",pkgCache::State::HalfConfigured}, + {"triggers-awaited",pkgCache::State::TriggersAwaited}, + {"triggers-pending",pkgCache::State::TriggersPending}, + {"installed",pkgCache::State::Installed}, + }}; + auto const status = Section.Find("Status"); + if (status.empty() == false) + { + for (auto && sv: statusvalues) + { + if (status != sv.Str) + continue; + Pkg->CurrentState = sv.Val; + switch (Pkg->CurrentState) + { + case pkgCache::State::NotInstalled: + case pkgCache::State::ConfigFiles: + break; + case pkgCache::State::HalfInstalled: + case pkgCache::State::UnPacked: + case pkgCache::State::HalfConfigured: + case pkgCache::State::TriggersAwaited: + case pkgCache::State::TriggersPending: + case pkgCache::State::Installed: + Pkg->CurrentVer = Ver.Index(); + break; + } + break; + } + } + + return true; +} + /*}}}*/ + edspLikeListParser::~edspLikeListParser() {} edspListParser::~edspListParser() {} +eippListParser::~eippListParser() {} diff --git a/apt-pkg/edsp/edsplistparser.h b/apt-pkg/edsp/edsplistparser.h index 7cd5ab2b3..84138d6a8 100644 --- a/apt-pkg/edsp/edsplistparser.h +++ b/apt-pkg/edsp/edsplistparser.h @@ -51,4 +51,14 @@ public: edspListParser(FileFd *File); virtual ~edspListParser(); }; + +class APT_HIDDEN eippListParser : public edspLikeListParser +{ +protected: + virtual bool ParseStatus(pkgCache::PkgIterator &Pkg,pkgCache::VerIterator &Ver) APT_OVERRIDE; + +public: + eippListParser(FileFd *File); + virtual ~eippListParser(); +}; #endif diff --git a/apt-pkg/edsp/edspsystem.cc b/apt-pkg/edsp/edspsystem.cc index 2a78efe58..b0e7b8a21 100644 --- a/apt-pkg/edsp/edspsystem.cc +++ b/apt-pkg/edsp/edspsystem.cc @@ -35,6 +35,9 @@ edspLikeSystem::edspLikeSystem(char const * const Label) : pkgSystem(Label, &deb edspSystem::edspSystem() : edspLikeSystem("Debian APT solver interface") { } +eippSystem::eippSystem() : edspLikeSystem("Debian APT planer interface") +{ +} /*}}}*/ // System::Lock - Get the lock /*{{{*/ bool edspLikeSystem::Lock() @@ -134,6 +137,19 @@ bool edspSystem::AddStatusFiles(std::vector<pkgIndexFile *> &List) /*{{{*/ return true; } /*}}}*/ +bool eippSystem::AddStatusFiles(std::vector<pkgIndexFile *> &List) /*{{{*/ +{ + if (StatusFile == nullptr) + { + if (_config->Find("eipp::scenario", "") == "/nonexistent/stdin") + StatusFile.reset(new eippIndex("/nonexistent/stdin")); + else + StatusFile.reset(new eippIndex(_config->FindFile("eipp::scenario"))); + } + List.push_back(StatusFile.get()); + return true; +} + /*}}}*/ edspLikeSystem::~edspLikeSystem() {} edspSystem::~edspSystem() @@ -145,5 +161,7 @@ edspSystem::~edspSystem() RemoveFile("~edspSystem", tempPrefsFile); rmdir(tempDir.c_str()); } +eippSystem::~eippSystem() {} APT_HIDDEN edspSystem edspSys; +APT_HIDDEN eippSystem eippSys; diff --git a/apt-pkg/edsp/edspsystem.h b/apt-pkg/edsp/edspsystem.h index 2c429c3d6..c0c9526b5 100644 --- a/apt-pkg/edsp/edspsystem.h +++ b/apt-pkg/edsp/edspsystem.h @@ -57,4 +57,13 @@ public: virtual ~edspSystem(); }; +class APT_HIDDEN eippSystem : public edspLikeSystem +{ + public: + virtual bool AddStatusFiles(std::vector<pkgIndexFile *> &List) APT_OVERRIDE; + + eippSystem(); + virtual ~eippSystem(); +}; + #endif diff --git a/apt-pkg/packagemanager.cc b/apt-pkg/packagemanager.cc index 8f884eac6..173fa8085 100644 --- a/apt-pkg/packagemanager.cc +++ b/apt-pkg/packagemanager.cc @@ -1039,8 +1039,12 @@ pkgPackageManager::OrderResult pkgPackageManager::OrderInstall() std::string const planer = _config->Find("APT::Planer", "internal"); if (planer != "internal") - if (EIPP::OrderInstall(planer.c_str(), Cache, 0, nullptr) == false) + { + if (EIPP::OrderInstall(planer.c_str(), this, 0, nullptr)) + return Completed; + else return Failed; + } bool const ordering = _config->FindB("PackageManager::UnpackAll",true) ? diff --git a/apt-pkg/packagemanager.h b/apt-pkg/packagemanager.h index 83d26115f..145fe40a8 100644 --- a/apt-pkg/packagemanager.h +++ b/apt-pkg/packagemanager.h @@ -24,6 +24,7 @@ #include <apt-pkg/pkgcache.h> #include <apt-pkg/init.h> +#include <apt-pkg/edsp.h> #include <apt-pkg/macros.h> #include <string> @@ -43,13 +44,14 @@ class pkgDepCache; class pkgSourceList; class pkgOrderList; class pkgRecords; +class OpProgress; +class pkgPackageManager; namespace APT { namespace Progress { class PackageManager; } } - class pkgPackageManager : protected pkgCache::Namespace { public: @@ -115,6 +117,11 @@ class pkgPackageManager : protected pkgCache::Namespace // compat APT_DEPRECATED_MSG("Use APT::Progress::PackageManager subclass instead of fd") OrderResult DoInstall(int statusFd=-1); + friend bool EIPP::OrderInstall(char const * const planer, pkgPackageManager * const PM, + unsigned int const version, OpProgress * const Progress); + friend bool EIPP::ReadResponse(int const input, pkgPackageManager * const PM, + OpProgress * const Progress); + // stuff that needs to be done before the fork() of a library that // uses apt OrderResult DoInstallPreFork() { diff --git a/apt-private/private-cmndline.cc b/apt-private/private-cmndline.cc index 839b55964..8fb4a48dd 100644 --- a/apt-private/private-cmndline.cc +++ b/apt-private/private-cmndline.cc @@ -160,6 +160,11 @@ static bool addArgumentsAPTFTPArchive(std::vector<CommandLine::Args> &Args, char return true; } /*}}}*/ +static bool addArgumentsAPTInternalPlaner(std::vector<CommandLine::Args> &, char const * const)/*{{{*/ +{ + return true; +} + /*}}}*/ static bool addArgumentsAPTInternalSolver(std::vector<CommandLine::Args> &, char const * const)/*{{{*/ { return true; @@ -356,6 +361,7 @@ std::vector<CommandLine::Args> getCommandArgs(APT_CMD const Program, char const case APT_CMD::APT_EXTRACTTEMPLATES: addArgumentsAPTExtractTemplates(Args, Cmd); break; case APT_CMD::APT_FTPARCHIVE: addArgumentsAPTFTPArchive(Args, Cmd); break; case APT_CMD::APT_HELPER: addArgumentsAPTHelper(Args, Cmd); break; + case APT_CMD::APT_INTERNAL_PLANER: addArgumentsAPTInternalPlaner(Args, Cmd); break; case APT_CMD::APT_INTERNAL_SOLVER: addArgumentsAPTInternalSolver(Args, Cmd); break; case APT_CMD::APT_MARK: addArgumentsAPTMark(Args, Cmd); break; case APT_CMD::APT_SORTPKG: addArgumentsAPTSortPkgs(Args, Cmd); break; @@ -412,13 +418,15 @@ static bool ShowCommonHelp(APT_CMD const Binary, CommandLine &CmdL, std::vector< case APT_CMD::APT_FTPARCHIVE: cmd = "apt-ftparchive(1)"; break; case APT_CMD::APT_GET: cmd = "apt-get(8)"; break; case APT_CMD::APT_HELPER: cmd = nullptr; break; + case APT_CMD::APT_INTERNAL_PLANER: cmd = nullptr; break; case APT_CMD::APT_INTERNAL_SOLVER: cmd = nullptr; break; case APT_CMD::APT_MARK: cmd = "apt-mark(8)"; break; case APT_CMD::APT_SORTPKG: cmd = "apt-sortpkgs(1)"; break; } if (cmd != nullptr) ioprintf(std::cout, _("See %s for more information about the available commands."), cmd); - if (Binary != APT_CMD::APT_DUMP_SOLVER && Binary != APT_CMD::APT_INTERNAL_SOLVER) + if (Binary != APT_CMD::APT_DUMP_SOLVER && Binary != APT_CMD::APT_INTERNAL_SOLVER && + Binary != APT_CMD::APT_INTERNAL_PLANER) std::cout << std::endl << _("Configuration options and syntax is detailed in apt.conf(5).\n" "Information about how to configure sources can be found in sources.list(5).\n" diff --git a/apt-private/private-cmndline.h b/apt-private/private-cmndline.h index c0c5a7455..6882add0c 100644 --- a/apt-private/private-cmndline.h +++ b/apt-private/private-cmndline.h @@ -22,6 +22,7 @@ enum class APT_CMD { APT_MARK, APT_SORTPKG, APT_DUMP_SOLVER, + APT_INTERNAL_PLANER, }; struct aptDispatchWithHelp { diff --git a/apt-private/private-main.cc b/apt-private/private-main.cc index 64f4bc563..6405b71b8 100644 --- a/apt-private/private-main.cc +++ b/apt-private/private-main.cc @@ -38,6 +38,7 @@ void InitLocale(APT_CMD const binary) /*{{{*/ break; case APT_CMD::APT_EXTRACTTEMPLATES: case APT_CMD::APT_FTPARCHIVE: + case APT_CMD::APT_INTERNAL_PLANER: case APT_CMD::APT_INTERNAL_SOLVER: case APT_CMD::APT_SORTPKG: textdomain("apt-utils"); diff --git a/cmdline/apt-internal-planer.cc b/cmdline/apt-internal-planer.cc new file mode 100644 index 000000000..676d84001 --- /dev/null +++ b/cmdline/apt-internal-planer.cc @@ -0,0 +1,187 @@ +// -*- mode: cpp; mode: fold -*- +// Description /*{{{*/ +/* ##################################################################### + + cover around the internal solver to be able to run it like an external + + ##################################################################### */ + /*}}}*/ +// Include Files /*{{{*/ +#include <config.h> + +#include <apt-pkg/error.h> +#include <apt-pkg/cmndline.h> +#include <apt-pkg/init.h> +#include <apt-pkg/cachefile.h> +#include <apt-pkg/cacheset.h> +#include <apt-pkg/strutl.h> +#include <apt-pkg/edsp.h> +#include <apt-pkg/fileutl.h> +#include <apt-pkg/pkgsystem.h> +#include <apt-pkg/configuration.h> +#include <apt-pkg/packagemanager.h> +#include <apt-pkg/prettyprinters.h> +#include <apt-pkg/depcache.h> +#include <apt-pkg/pkgcache.h> +#include <apt-pkg/cacheiterators.h> + +#include <apt-private/private-output.h> +#include <apt-private/private-cmndline.h> +#include <apt-private/private-main.h> + +#include <string.h> +#include <iostream> +#include <sstream> +#include <list> +#include <string> +#include <unistd.h> +#include <cstdio> +#include <stdlib.h> + +#include <apti18n.h> + /*}}}*/ + +static bool ShowHelp(CommandLine &) /*{{{*/ +{ + std::cout << + _("Usage: apt-internal-planer\n" + "\n" + "apt-internal-planer is an interface to use the current internal\n" + "installation planer for the APT family like an external one,\n" + "for debugging or the like.\n"); + return true; +} + /*}}}*/ +APT_NORETURN static void DIE(std::string const &message) { /*{{{*/ + std::cerr << "ERROR: " << message << std::endl; + _error->DumpErrors(std::cerr); + exit(EXIT_FAILURE); +} + /*}}}*/ +static std::vector<aptDispatchWithHelp> GetCommands() /*{{{*/ +{ + return {}; +} + /*}}}*/ +class PMOutput: public pkgPackageManager /*{{{*/ +{ + FileFd &output; + bool const Debug; + +protected: + virtual bool Install(PkgIterator Pkg,std::string) APT_OVERRIDE + { + //std::cerr << "INSTALL: " << APT::PrettyPkg(&Cache, Pkg) << std::endl; + return EDSP::WriteSolutionStanza(output, "Install", Cache[Pkg].InstVerIter(Cache)); + } + virtual bool Configure(PkgIterator Pkg) APT_OVERRIDE + { + //std::cerr << "CONFIGURE: " << APT::PrettyPkg(&Cache, Pkg) << " " << std::endl; + return EDSP::WriteSolutionStanza(output, "Configure", Cache[Pkg].InstVerIter(Cache)); + } + virtual bool Remove(PkgIterator Pkg,bool) APT_OVERRIDE + { + //std::cerr << "REMOVE: " << APT::PrettyPkg(&Cache, Pkg) << " " << std::endl; + return EDSP::WriteSolutionStanza(output, "Remove", Pkg.CurrentVer()); + } +public: + PMOutput(pkgDepCache *Cache, FileFd &file) : pkgPackageManager(Cache), output(file), + Debug(_config->FindB("Debug::EDSP::WriteSolution", false)) + {} + + bool ApplyRequest(std::list<std::pair<std::string,EIPP::PKG_ACTION>> const &actions) + { + for (auto && a: actions) + { + auto const Pkg = Cache.FindPkg(a.first); + if (unlikely(Pkg.end() == true)) + continue; + switch (a.second) + { + case EIPP::PKG_ACTION::NOOP: + break; + case EIPP::PKG_ACTION::INSTALL: + case EIPP::PKG_ACTION::REINSTALL: + FileNames[Pkg->ID] = "EIPP"; + break; + case EIPP::PKG_ACTION::REMOVE: + break; + } + } + return true; + } +}; + /*}}}*/ +int main(int argc,const char *argv[]) /*{{{*/ +{ + // we really don't need anything + DropPrivileges(); + + CommandLine CmdL; + ParseCommandLine(CmdL, APT_CMD::APT_INTERNAL_PLANER, &_config, NULL, argc, argv, &ShowHelp, &GetCommands); + + // Deal with stdout not being a tty + if (!isatty(STDOUT_FILENO) && _config->FindI("quiet", -1) == -1) + _config->Set("quiet","1"); + + if (_config->FindI("quiet", 0) < 1) + _config->Set("Debug::EIPP::WriteSolution", true); + + _config->Set("APT::System", "Debian APT planer interface"); + _config->Set("APT::Planer", "internal"); + _config->Set("eipp::scenario", "/nonexistent/stdin"); + FileFd output; + if (output.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false) + DIE("stdout couldn't be opened"); + int const input = STDIN_FILENO; + SetNonBlock(input, false); + + EDSP::WriteProgress(0, "Start up planer…", output); + + if (pkgInitSystem(*_config,_system) == false) + DIE("System could not be initialized!"); + + EDSP::WriteProgress(1, "Read request…", output); + + if (WaitFd(input, false, 5) == false) + DIE("WAIT timed out in the planer"); + + std::list<std::pair<std::string,EIPP::PKG_ACTION>> actions; + unsigned int flags; + if (EIPP::ReadRequest(input, actions, flags) == false) + DIE("Parsing the request failed!"); + + EDSP::WriteProgress(5, "Read scenario…", output); + + pkgCacheFile CacheFile; + if (CacheFile.Open(NULL, false) == false) + DIE("Failed to open CacheFile!"); + + EDSP::WriteProgress(50, "Apply request on scenario…", output); + + if (EIPP::ApplyRequest(actions, CacheFile) == false) + DIE("Failed to apply request to depcache!"); + + EDSP::WriteProgress(60, "Call orderinstall on current scenario…", output); + + //_config->Set("Debug::pkgOrderList", true); + //_config->Set("Debug::pkgPackageManager", true); + PMOutput PM(CacheFile, output); + if (PM.ApplyRequest(actions) == false) + DIE("Failed to apply request to packagemanager!"); + pkgPackageManager::OrderResult const Res = PM.DoInstallPreFork(); + switch (Res) + { + case pkgPackageManager::Completed: + EDSP::WriteProgress(100, "Done", output); + break; + case pkgPackageManager::Incomplete: + EDSP::WriteError("pm-incomplete", "Planer could only plan Incompletely", output); + case pkgPackageManager::Failed: + EDSP::WriteError("pm-failed", "Planer failed to find an order", output); + break; + } + + return DispatchCommandLine(CmdL, {}); +} + /*}}}*/ diff --git a/cmdline/makefile b/cmdline/makefile index cd8ff8252..ee50224e1 100644 --- a/cmdline/makefile +++ b/cmdline/makefile @@ -79,13 +79,19 @@ LIB_MAKES = apt-pkg/makefile apt-inst/makefile apt-private/makefile SOURCE = apt-extracttemplates.cc include $(PROGRAM_H) -# The internal solver acting as an external +# The internal solver/planer acting as an external PROGRAM=apt-internal-solver SLIBS = -lapt-pkg -lapt-private $(INTLLIBS) LIB_MAKES = apt-pkg/makefile apt-private/makefile SOURCE = apt-internal-solver.cc include $(PROGRAM_H) +PROGRAM=apt-internal-planer +SLIBS = -lapt-pkg -lapt-private $(INTLLIBS) +LIB_MAKES = apt-pkg/makefile apt-private/makefile +SOURCE = apt-internal-planer.cc +include $(PROGRAM_H) + # This just dumps out the state PROGRAM=apt-dump-solver SLIBS = -lapt-pkg -lapt-private $(INTLLIBS) diff --git a/debian/apt-utils.dirs b/debian/apt-utils.dirs index 681e55192..cb3a9ebef 100644 --- a/debian/apt-utils.dirs +++ b/debian/apt-utils.dirs @@ -1,2 +1,3 @@ usr/lib/apt/solvers +usr/lib/apt/planers usr/bin diff --git a/debian/apt.dirs b/debian/apt.dirs index 4a83d1151..7486a7b69 100644 --- a/debian/apt.dirs +++ b/debian/apt.dirs @@ -1,6 +1,7 @@ usr/bin usr/lib/apt/methods usr/lib/apt/solvers +usr/lib/apt/planers usr/lib/dpkg/methods/apt etc/apt etc/apt/apt.conf.d @@ -16,4 +17,4 @@ var/lib/apt/periodic var/log/apt usr/share/bug/apt usr/share/bash-completion/completions/ -lib/systemd/system/
\ No newline at end of file +lib/systemd/system/ diff --git a/debian/apt.lintian-overrides b/debian/apt.lintian-overrides index 0c4d42d1b..e51d1119f 100644 --- a/debian/apt.lintian-overrides +++ b/debian/apt.lintian-overrides @@ -1,4 +1,2 @@ # the private library is for internal sharing only apt: package-name-doesnt-match-sonames -# external solvers are shipped in here: -apt: package-contains-empty-directory diff --git a/debian/rules b/debian/rules index cd21f30ef..43e979dc8 100755 --- a/debian/rules +++ b/debian/rules @@ -189,8 +189,9 @@ apt: build-binary build-manpages debian/apt.install dh_install -p$@ --sourcedir=$(BLD) # Remove the bits that are in apt-utils - rm $(addprefix debian/$@/usr/bin/apt-,$(APT_UTILS) dump-solver internal-solver) + rm $(addprefix debian/$@/usr/bin/apt-,$(APT_UTILS) dump-solver internal-solver internal-planer) cp $(BLD)/bin/apt-dump-solver debian/$@/usr/lib/apt/solvers/dump + ln -s ../solvers/dump debian/$@/usr/lib/apt/planers/dump # https has its own package rm debian/$@/usr/lib/apt/methods/https @@ -252,6 +253,7 @@ apt-utils: build-binary build-manpages cp $(addprefix $(BLD)/bin/apt-,$(APT_UTILS)) debian/$@/usr/bin/ cp $(BLD)/bin/apt-internal-solver debian/$@/usr/lib/apt/solvers/apt + cp $(BLD)/bin/apt-internal-planer debian/$@/usr/lib/apt/planers/apt dh_install -p$@ --sourcedir=$(BLD) dh_link -p$@ diff --git a/debian/tests/run-tests b/debian/tests/run-tests index f858adf5f..11139e3cd 100644 --- a/debian/tests/run-tests +++ b/debian/tests/run-tests @@ -17,6 +17,7 @@ APT_INTEGRATION_TESTS_METHODS_DIR=/usr/lib/apt/methods \ APT_INTEGRATION_TESTS_LIBEXEC_DIR=/usr/lib/apt/ \ APT_INTEGRATION_TESTS_INTERNAL_SOLVER=/usr/lib/apt/solvers/apt \ APT_INTEGRATION_TESTS_DUMP_SOLVER=/usr/lib/apt/solvers/dump \ +APT_INTEGRATION_TESTS_INTERNAL_PLANER=/usr/lib/apt/planers/apt \ APT_INTEGRATION_TESTS_BUILD_DIR=/usr/bin \ APT_INTEGRATION_TESTS_LIBRARY_PATH=/dev/null/does/not/exist \ ./test/integration/run-tests -q diff --git a/test/integration/framework b/test/integration/framework index 6276ae825..93e17e454 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -191,6 +191,7 @@ aptitude() { runapt aptitude "$@"; } aptextracttemplates() { runapt apt-extracttemplates "$@"; } aptinternalsolver() { runapt "${APTINTERNALSOLVER}" "$@"; } aptdumpsolver() { runapt "${APTDUMPSOLVER}" "$@"; } +aptinternalplaner() { runapt "${APTINTERNALPLANER}" "$@"; } dpkg() { "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" "$@" @@ -289,6 +290,7 @@ setupenvironment() { APTWEBSERVERBINDIR="${APT_INTEGRATION_TESTS_WEBSERVER_BIN_DIR:-"${BUILDDIRECTORY}"}" APTINTERNALSOLVER="${APT_INTEGRATION_TESTS_INTERNAL_SOLVER:-"${BUILDDIRECTORY}/apt-internal-solver"}" APTDUMPSOLVER="${APT_INTEGRATION_TESTS_DUMP_SOLVER:-"${BUILDDIRECTORY}/apt-dump-solver"}" + APTINTERNALPLANER="${APT_INTEGRATION_TESTS_INTERNAL_PLANER:-"${BUILDDIRECTORY}/apt-internal-planer"}" test -x "${BUILDDIRECTORY}/apt-get" || msgdie "You need to build tree first" # ----- @@ -306,6 +308,7 @@ setupenvironment() { ln -s "${BUILDDIRECTORY}/apt-dump-solver" usr/lib/apt/solvers/dump ln -s "${BUILDDIRECTORY}/apt-dump-solver" usr/lib/apt/planers/dump ln -s "${BUILDDIRECTORY}/apt-internal-solver" usr/lib/apt/solvers/apt + ln -s "${BUILDDIRECTORY}/apt-internal-planer" usr/lib/apt/planers/apt echo "Dir::Bin::Solvers \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/solvers\";" > etc/apt/apt.conf.d/externalsolver.conf echo "Dir::Bin::Planers \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/planers\";" > etc/apt/apt.conf.d/externalplaner.conf fi diff --git a/test/integration/test-00-commands-have-help b/test/integration/test-00-commands-have-help index c7d794b34..ae724fb9e 100755 --- a/test/integration/test-00-commands-have-help +++ b/test/integration/test-00-commands-have-help @@ -49,7 +49,7 @@ for CMD in 'apt-cache' 'apt-cdrom' 'apt-config' \ checkoptions "$cmd" done -for CMD in 'apt-dump-solver' 'apt-internal-solver'; do +for CMD in 'apt-dump-solver' 'apt-internal-solver' 'apt-internal-planer'; do checkoptions "$(echo "$CMD" | tr -d '-')" done diff --git a/test/integration/test-external-installation-planer-protocol b/test/integration/test-external-installation-planer-protocol index db43675e1..605efcd7b 100755 --- a/test/integration/test-external-installation-planer-protocol +++ b/test/integration/test-external-installation-planer-protocol @@ -26,3 +26,6 @@ testfailure aptget install foo --planer dump -y testfailure grep 'unrelated-2' "$APT_EDSP_DUMP_FILENAME" testsuccessequal '2' grep -c '^Package: foo$' "$APT_EDSP_DUMP_FILENAME" testsuccessequal '1' grep -c '^Package: libfoo$' "$APT_EDSP_DUMP_FILENAME" +#less "$APT_EDSP_DUMP_FILENAME" + +aptget install foo -ys #--planer apt |