]> git.saurik.com Git - apt.git/commitdiff
eipp: let apt make a plan, not make stuff plane
authorDavid Kalnischkies <david@kalnischkies.de>
Wed, 29 Jun 2016 07:16:53 +0000 (09:16 +0200)
committerDavid Kalnischkies <david@kalnischkies.de>
Wed, 29 Jun 2016 10:17:41 +0000 (12:17 +0200)
Julian noticed on IRC that I fall victim to a lovely false friend by
calling referring to a 'planer' all the time even through these are
machines to e.g. remove splinters from woodwork ("make stuff plane").
The term I meant is written in german in this way (= with a single n)
but in english there are two, aka: 'planner'.

As that is unreleased code switching all instances without any
transitional provisions. Also the reason why its skipped in changelog.

Thanks: Julian Andres Klode
Gbp-Dch: Ignore

23 files changed:
apt-pkg/edsp.cc
apt-pkg/edsp.h
apt-pkg/edsp/edspsystem.cc
apt-pkg/init.cc
apt-pkg/packagemanager.cc
apt-pkg/packagemanager.h
apt-private/private-cmndline.cc
apt-private/private-cmndline.h
apt-private/private-main.cc
cmdline/apt-internal-planer.cc [deleted file]
cmdline/apt-internal-planner.cc [new file with mode: 0644]
cmdline/makefile
debian/apt-doc.docs
debian/apt-utils.dirs
debian/apt.dirs
debian/rules
debian/tests/run-tests
doc/external-installation-planer-protocol.txt [deleted file]
doc/external-installation-planner-protocol.txt [new file with mode: 0644]
test/integration/framework
test/integration/test-00-commands-have-help
test/integration/test-external-installation-planer-protocol [deleted file]
test/integration/test-external-installation-planner-protocol [new file with mode: 0755]

index 49873f2438ddcbd8652567ebade9fae35df58d65..55bc0fcbdefc993e7c02fad89c2a50f5bb3ea2cf 100644 (file)
@@ -1076,7 +1076,7 @@ bool EIPP::OrderInstall(char const * const solver, pkgPackageManager * const PM,
 {
    if (strcmp(solver, "internal") == 0)
    {
-      auto const dumpfile = _config->FindFile("Dir::Log::Planer");
+      auto const dumpfile = _config->FindFile("Dir::Log::Planner");
       if (dumpfile.empty())
         return false;
       auto const dumpdir = flNotFile(dumpfile);
@@ -1089,27 +1089,27 @@ bool EIPP::OrderInstall(char const * const solver, pkgPackageManager * const PM,
    }
 
    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("EIPP::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"));
+      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"));
+      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"));
+      Progress->OverallProgress(25, 100, 75, _("Execute external planner"));
 
-   // we don't tell the external planers about boring things
+   // 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)
@@ -1127,7 +1127,7 @@ bool EIPP::WriteRequest(pkgDepCache &Cache, FileFd &output,               /*{{{*/
                        OpProgress * const Progress)
 {
    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, inst, reinst;
    for (pkgCache::PkgIterator Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg, ++p)
@@ -1164,7 +1164,7 @@ bool EIPP::WriteRequest(pkgDepCache &Cache, FileFd &output,               /*{{{*/
       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)
@@ -1217,7 +1217,7 @@ template<typename forVersion> void forAllInterestingVersions(pkgDepCache &Cache,
 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();
@@ -1318,13 +1318,13 @@ bool EIPP::ReadResponse(int const input, pkgPackageManager * const PM, OpProgres
       } 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;
@@ -1396,7 +1396,7 @@ bool EIPP::ReadRequest(int const input, std::list<std::pair<std::string,PKG_ACTI
            _config->Set("APT::Architecture", line);
         else if (LineStartsWithAndStrip(line, "Architectures:"))
            _config->Set("APT::Architectures", SubstVar(line, " ", ","));
-        else if (LineStartsWithAndStrip(line, "Planer:"))
+        else if (LineStartsWithAndStrip(line, "Planner:"))
            ; // purely informational line
         else if (LineStartsWithAndStrip(line, "Immediate-Configuration:"))
         {
index 4adfc95e97e2f64d9fbc7b9c541ccbc2f9feebe5..60f8c28830eb0a8cbe0786e5bc6c054f2e06798d 100644 (file)
@@ -254,7 +254,7 @@ namespace EIPP                                                              /*{{{*/
    APT_HIDDEN bool WriteScenario(pkgDepCache &Cache, FileFd &output,
         OpProgress * const Progress);
 
-   APT_HIDDEN bool OrderInstall(char const * const planer, pkgPackageManager * const PM,
+   APT_HIDDEN bool OrderInstall(char const * const planner, pkgPackageManager * const PM,
         unsigned int const version, OpProgress * const Progress);
    APT_HIDDEN bool ReadResponse(int const input, pkgPackageManager * const PM,
         OpProgress * const Progress);
index b0e7b8a21a3b46e2a1911f8954a8fa6fa6aa80d4..1ceb21a17201db28936011f441864d447b99ef95 100644 (file)
@@ -35,7 +35,7 @@ edspLikeSystem::edspLikeSystem(char const * const Label) : pkgSystem(Label, &deb
 edspSystem::edspSystem() : edspLikeSystem("Debian APT solver interface")
 {
 }
-eippSystem::eippSystem() : edspLikeSystem("Debian APT planer interface")
+eippSystem::eippSystem() : edspLikeSystem("Debian APT planner interface")
 {
 }
                                                                        /*}}}*/
index 0e8d9be8ae87c54d192162b8e9431d9a206bf4cb..9543ca7e8f29ac74c46a4a6eac949fb6f92de416 100644 (file)
@@ -76,7 +76,7 @@ bool pkgInitConfig(Configuration &Cnf)
    Cnf.CndSet("Dir::Log","var/log/apt");
    Cnf.CndSet("Dir::Log::Terminal","term.log");
    Cnf.CndSet("Dir::Log::History","history.log");
-   Cnf.CndSet("Dir::Log::Planer","eipp.log.xz");
+   Cnf.CndSet("Dir::Log::Planner","eipp.log.xz");
 
    Cnf.Set("Dir::Ignore-Files-Silently::", "~$");
    Cnf.Set("Dir::Ignore-Files-Silently::", "\\.disabled$");
index 898e5d156714570a5982634948008b04c13f4d18..7fdd0393f33473b22b980959db98de0fd279d7ad 100644 (file)
@@ -1037,7 +1037,7 @@ pkgPackageManager::OrderResult pkgPackageManager::OrderInstall()
    if (Debug == true)
       clog << "Beginning to order" << endl;
 
-   std::string const planer = _config->Find("APT::Planer", "internal");
+   std::string const planner = _config->Find("APT::Planner", "internal");
    unsigned int flags = 0;
    if (_config->FindB("APT::Immediate-Configure", true) == false)
       flags |= EIPP::Request::NO_IMMEDIATE_CONFIGURATION;
@@ -1045,8 +1045,8 @@ pkgPackageManager::OrderResult pkgPackageManager::OrderInstall()
       flags |= EIPP::Request::IMMEDIATE_CONFIGURATION_ALL;
    else if (_config->FindB("APT::Force-LoopBreak", false))
       flags |= EIPP::Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS;
-   auto const ret = EIPP::OrderInstall(planer.c_str(), this, flags, nullptr);
-   if (planer != "internal")
+   auto const ret = EIPP::OrderInstall(planner.c_str(), this, flags, nullptr);
+   if (planner != "internal")
       return ret ? Completed : Failed;
 
    bool const ordering =
index 145fe40a8266d570d15c1032b8185814895b25ba..25b6ee7c9bed2bf12fa24b2576ff24f8f5d600fa 100644 (file)
@@ -117,7 +117,7 @@ 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,
+   friend bool EIPP::OrderInstall(char const * const planner, pkgPackageManager * const PM,
         unsigned int const version, OpProgress * const Progress);
    friend bool EIPP::ReadResponse(int const input, pkgPackageManager * const PM,
         OpProgress * const Progress);
index 8fb4a48dd6d644b35669cf83e9bb37c972b51cfc..feb8d67a9f99355bde5bbf451898f44790ebddfc 100644 (file)
@@ -160,7 +160,7 @@ static bool addArgumentsAPTFTPArchive(std::vector<CommandLine::Args> &Args, char
    return true;
 }
                                                                        /*}}}*/
-static bool addArgumentsAPTInternalPlaner(std::vector<CommandLine::Args> &, char const * const)/*{{{*/
+static bool addArgumentsAPTInternalPlanner(std::vector<CommandLine::Args> &, char const * const)/*{{{*/
 {
    return true;
 }
@@ -191,7 +191,7 @@ static bool addArgumentsAPTGet(std::vector<CommandLine::Args> &Args, char const
       addArg(0, "auto-remove", "APT::Get::AutomaticRemove", 0);
       addArg(0, "reinstall", "APT::Get::ReInstall", 0);
       addArg(0, "solver", "APT::Solver", CommandLine::HasArg);
-      addArg(0, "planer", "APT::Planer", CommandLine::HasArg);
+      addArg(0, "planner", "APT::Planner", CommandLine::HasArg);
       if (CmdMatches("upgrade"))
       {
          addArg(0, "new-pkgs", "APT::Get::Upgrade-Allow-New", 
@@ -361,7 +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_PLANNER: addArgumentsAPTInternalPlanner(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;
@@ -418,7 +418,7 @@ 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_PLANNER: 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;
@@ -426,7 +426,7 @@ static bool ShowCommonHelp(APT_CMD const Binary, CommandLine &CmdL, std::vector<
    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 &&
-        Binary != APT_CMD::APT_INTERNAL_PLANER)
+        Binary != APT_CMD::APT_INTERNAL_PLANNER)
       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"
index 6882add0c0880c6348da4a326e8f5090a9543cb6..37fe2c91ad61191626de1323f5a51a53694a4734 100644 (file)
@@ -22,7 +22,7 @@ enum class APT_CMD {
    APT_MARK,
    APT_SORTPKG,
    APT_DUMP_SOLVER,
-   APT_INTERNAL_PLANER,
+   APT_INTERNAL_PLANNER,
 };
 struct aptDispatchWithHelp
 {
index 6405b71b888478ea948bf8020eb5510a7fded8d5..52f35cfdc08d4f1b2f5decdaa494161950826006 100644 (file)
@@ -38,7 +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_PLANNER:
       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
deleted file mode 100644 (file)
index 2d5f44c..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-// -*- 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, "Unpack", 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!");
-       _config->Set("APT::Immediate-Configure", (flags & EIPP::Request::NO_IMMEDIATE_CONFIGURATION) == 0);
-       _config->Set("APT::Immediate-Configure-All", (flags & EIPP::Request::IMMEDIATE_CONFIGURATION_ALL) != 0);
-       _config->Set("APT::Force-LoopBreak", (flags & EIPP::Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS) != 0);
-
-       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();
-       std::ostringstream broken;
-       switch (Res)
-       {
-          case pkgPackageManager::Completed:
-             EDSP::WriteProgress(100, "Done", output);
-             break;
-          case pkgPackageManager::Incomplete:
-             broken << "Planer could only incompletely plan an installation order!" << std::endl;
-             _error->DumpErrors(broken, GlobalError::DEBUG);
-             EDSP::WriteError("pm-incomplete", broken.str(), output);
-             break;
-          case pkgPackageManager::Failed:
-             broken << "Planer failed to find an installation order!" << std::endl;
-             _error->DumpErrors(broken, GlobalError::DEBUG);
-             EDSP::WriteError("pm-failed", broken.str(), output);
-             break;
-       }
-
-       return DispatchCommandLine(CmdL, {});
-}
-                                                                       /*}}}*/
diff --git a/cmdline/apt-internal-planner.cc b/cmdline/apt-internal-planner.cc
new file mode 100644 (file)
index 0000000..1f74aa7
--- /dev/null
@@ -0,0 +1,196 @@
+// -*- 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-planner\n"
+               "\n"
+               "apt-internal-planner is an interface to use the current internal\n"
+               "installation planner 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, "Unpack", 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_PLANNER, &_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 planner interface");
+       _config->Set("APT::Planner", "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 planner…", 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 planner");
+
+       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!");
+       _config->Set("APT::Immediate-Configure", (flags & EIPP::Request::NO_IMMEDIATE_CONFIGURATION) == 0);
+       _config->Set("APT::Immediate-Configure-All", (flags & EIPP::Request::IMMEDIATE_CONFIGURATION_ALL) != 0);
+       _config->Set("APT::Force-LoopBreak", (flags & EIPP::Request::ALLOW_TEMPORARY_REMOVE_OF_ESSENTIALS) != 0);
+
+       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();
+       std::ostringstream broken;
+       switch (Res)
+       {
+          case pkgPackageManager::Completed:
+             EDSP::WriteProgress(100, "Done", output);
+             break;
+          case pkgPackageManager::Incomplete:
+             broken << "Planner could only incompletely plan an installation order!" << std::endl;
+             _error->DumpErrors(broken, GlobalError::DEBUG);
+             EDSP::WriteError("pm-incomplete", broken.str(), output);
+             break;
+          case pkgPackageManager::Failed:
+             broken << "Planner failed to find an installation order!" << std::endl;
+             _error->DumpErrors(broken, GlobalError::DEBUG);
+             EDSP::WriteError("pm-failed", broken.str(), output);
+             break;
+       }
+
+       return DispatchCommandLine(CmdL, {});
+}
+                                                                       /*}}}*/
index ee50224e13bd511ab2919ee2088cad96f38115af..8b79ce05f95caf0cdf51076f90f97d88a0957722 100644 (file)
@@ -79,17 +79,17 @@ LIB_MAKES = apt-pkg/makefile apt-inst/makefile apt-private/makefile
 SOURCE = apt-extracttemplates.cc 
 include $(PROGRAM_H)
 
-# The internal solver/planer acting as an external
+# The internal solver/planner 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
+PROGRAM=apt-internal-planner
 SLIBS = -lapt-pkg -lapt-private $(INTLLIBS)
 LIB_MAKES = apt-pkg/makefile apt-private/makefile
-SOURCE = apt-internal-planer.cc
+SOURCE = apt-internal-planner.cc
 include $(PROGRAM_H)
 
 # This just dumps out the state
index dd2a1eee88b3a36e890f0b06f491788359a05644..2058000c184ca91144c25954910a2d0fc644b0a3 100644 (file)
@@ -2,5 +2,5 @@ build/docs/guide*
 build/docs/offline*
 README.progress-reporting
 doc/external-dependency-solver-protocol.txt
-doc/external-installation-planer-protocol.txt
+doc/external-installation-planner-protocol.txt
 doc/acquire-additional-files.txt
index cb3a9ebefa5964e5f41f0a6fff84436673cdd0d0..de7fa756ef87fe0d3d94cfbc3c9d6a6d93c7436c 100644 (file)
@@ -1,3 +1,3 @@
 usr/lib/apt/solvers
-usr/lib/apt/planers
+usr/lib/apt/planners
 usr/bin
index 7486a7b6981bc58d8c1fc563486521cad7103fe4..7d46801df14ebbd18fa14733b1906050edeeb4f1 100644 (file)
@@ -1,7 +1,7 @@
 usr/bin
 usr/lib/apt/methods
 usr/lib/apt/solvers
-usr/lib/apt/planers
+usr/lib/apt/planners
 usr/lib/dpkg/methods/apt
 etc/apt
 etc/apt/apt.conf.d
index 43e979dc854b3c5bb35d7027b864228db65a3fe2..18b3f34f3085c859ef9947a5200461bfc5c9bfe3 100755 (executable)
@@ -189,9 +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 internal-planer)
+       rm $(addprefix debian/$@/usr/bin/apt-,$(APT_UTILS) dump-solver internal-solver internal-planner)
        cp $(BLD)/bin/apt-dump-solver debian/$@/usr/lib/apt/solvers/dump
-       ln -s ../solvers/dump debian/$@/usr/lib/apt/planers/dump
+       ln -s ../solvers/dump debian/$@/usr/lib/apt/planners/dump
 
        # https has its own package
        rm debian/$@/usr/lib/apt/methods/https
@@ -253,7 +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
+       cp $(BLD)/bin/apt-internal-planner debian/$@/usr/lib/apt/planners/apt
 
        dh_install -p$@ --sourcedir=$(BLD)
        dh_link -p$@
index 11139e3cd1816fcdcf5e91578c9c28901ab4c79a..e33992b2813748bd56e3c3add735051cf48a300d 100644 (file)
@@ -17,7 +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_INTERNAL_PLANNER=/usr/lib/apt/planners/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/doc/external-installation-planer-protocol.txt b/doc/external-installation-planer-protocol.txt
deleted file mode 100644 (file)
index 916982c..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-# APT External Installation Planer Protocol (EIPP) - version 0.1
-
-This document describes the communication protocol between APT and
-external installation planer. The protocol is called APT EIPP, for "APT
-External Installation Planer Protocol".
-
-
-## Terminology
-
-In the following we use the term **architecture qualified package name**
-(or *arch-qualified package names* for short) to refer to package
-identifiers of the form "package:arch" where "package" is a package name
-and "arch" a dpkg architecture.
-
-
-## Components
-
-- **APT**: we know this one.
-- APT is equipped with its own **internal planer** for the order of
-  package installation (and removal) which is identified by the string
-  `internal`.
-- **External planer**: an *external* software component able to plan an
-  installation on behalf of APT.
-
-At each interaction with APT, a single planer is in use.  When there is
-a total of 2 or more planers, internals or externals, the user can
-choose which one to use.
-
-Each planer is identified by an unique string, the **planer name**.
-Planer names must be formed using only alphanumeric ASCII characters,
-dashes, and underscores; planer names must start with a lowercase ASCII
-letter.  The special name `internal` denotes APT's internal planer, is
-reserved, and cannot be used by external planers.
-
-
-## Installation
-
-Each external planer is installed as a file under Dir::Bin::Planers (see
-below), which defaults to `/usr/lib/apt/planers`. We will assume in the
-remainder of this section that such a default value is in effect.
-
-The naming scheme is `/usr/lib/apt/planers/NAME`, where `NAME` is the
-name of the external planer.
-
-Each file under `/usr/lib/apt/planers` corresponding to an external
-planer must be executable.
-
-No non-planer files must be installed under `/usr/lib/apt/planers`, so
-that an index of available external planers can be obtained by listing
-the content of that directory.
-
-
-## Configuration
-
-Several APT options can be used to affect installation planing in APT.
-An overview of them is given below. Please refer to proper APT
-configuration documentation for more, and more up to date, information.
-
-- **APT::Planer**: the name of the planer to be used for dependency
-  solving. Defaults to `internal`
-
-- **Dir::Bin::Planers**: absolute path of the directory where to look
-  for external solvers. Defaults to `/usr/lib/apt/planers`.
-
-
-## Protocol
-
-When configured to use an external planer, APT will resort to it to
-decide in which order packages should be installed, configured and
-removed.
-
-The interaction happens **in batch**: APT will invoke the external
-planer passing the current status of (half-)installed packages and of
-packages which should be installed, as well as a request denoting the
-packages to install, reinstall, remove and purge.  The external planer
-will compute a valid plan of when and how to call the low-level package
-manager (like dpkg) with each package to satisfy the request.
-
-External planers are invoked by executing them. Communications happens
-via the file descriptors: **stdin** (standard input) and **stdout**
-(standard output). stderr is not used by the EIPP protocol. Planers can
-therefore use stderr to dump debugging information that could be
-inspected separately.
-
-After invocation, the protocol passes through a sequence of phases:
-
-1. APT invokes the external planer
-2. APT send to the planer an installation planer **scenario**
-3. The planer calculates the order. During this phase the planer may
-   send, repeatedly, **progress** information to APT.
-4. The planer sends back to APT an **answer**, i.e. either a *solution*
-   or an *error* report.
-5. The external planer exits
-
-
-### Scenario
-
-A scenario is a text file encoded in a format very similar to the "Deb
-822" format (AKA "the format used by Debian `Packages` files"). A
-scenario consists of two distinct parts: a **request** and a **package
-universe**, occurring in that order. The request consists of a single
-Deb 822 stanza, while the package universe consists of several such
-stanzas. All stanzas occurring in a scenario are separated by an empty
-line.
-
-
-#### Request
-
-Within an installation planer scenario, a request represents the action
-on packages requested by the user explicitly as well as potentially
-additions calculated by a dependency resolver which the user has
-accepted.
-
-An installation planer is not allowed to suggest the modification of
-package states (e.g. removing additional packages) even if it can't
-calculate a solution otherwise – the planer must error out in such
-a case. An exception is made for scenarios which contain packages which
-aren't completely installed (like half-installed or trigger-awaiting):
-Solvers are free to move these packages to a fully installed state (but
-are still forbidden to remove them).
-
-A request is a single Deb 822 stanza opened by a mandatory Request field
-and followed by a mixture of action, preference, and global
-configuration fields.
-
-The value of the **Request:** field is a string describing the EIPP
-protocol which will be used to communicate and especially which answers
-APT will understand. At present, the string must be `EIPP 0.1`. Request
-fields are mainly used to identify the beginning of a request stanza;
-their actual values are otherwise not used by the EIPP protocol.
-
-The following **configuration fields** are supported in request stanzas:
-
-- **Architecture:** (mandatory) The name of the *native* architecture on
-  the user machine (see also: `dpkg --print-architecture`)
-
-- **Architectures:** (optional, defaults to the native architecture) A
-  space separated list of *all* architectures known to APT (this is
-  roughly equivalent to the union of `dpkg --print-architecture` and
-  `dpkg --print-foreign-architectures`)
-
-The following **action fields** are supported in request stanzas:
-
-- **Install:** (optional, defaults to the empty string) A space
-  separated list of arch-qualified package names, with *no version
-  attached*, to install. This field denotes a list of packages that the
-  user wants to install, usually via an APT `install` request.
-
-- **Remove:** (optional, defaults to the empty string) Same syntax of
-  Install. This field denotes a list of packages that the user wants to
-  remove, usually via APT `remove` or `purge` requests.
-
-- **ReInstall:** (optional, defaults to the empty string) Same syntax of
-  Install. This field denotes a list of packages which are installed,
-  but should be reinstalled again e.g. because files shipped by that
-  package were removed or corrupted accidentally, usually requested via
-  an APT `install` request with the `--reinstall` flag.
-
-The following **preference fields** are supported in request stanzas:
-
-- **Planer:** (optional, defaults to the empty string) a purely
-  informational string specifying to which planer this request was send
-  initially.
-
-- **Immediate-Configuration:** (option, unset by default) A boolean
-  value defining if the planer should try to configure all packages as
-  quickly as possible (true) or shouldn't perform any kind of immediate
-  configuration at all (false). If not explicitly set with this field
-  the planer is free to pick either mode or implementing e.g. a mode
-  which configures only packages immediately if they are flagged as
-  `Essential` (or are dependencies of packages marked as `Essential`).
-
-- **Allow-Temporary-Remove-of-Essentials** (optional, defaults to `no`).
-  A boolean value allowing the planer (if set to yes) to temporarily
-  remove an essential package. Associated with the APT::Force-LoopBreak
-  configuration option its main use is highlighting that planers who do
-  temporary removes must take special care in terms of essentials. Legit
-  uses of this option by users is very uncommon, traditionally
-  a situation in which it is needed indicates a packaging error.
-
-
-#### Package universe
-
-A package universe is a list of Deb 822 stanzas, one per package, called
-**package stanzas**. Each package stanzas starts with a Package
-field. The following fields are supported in package stanzas:
-
-- The fields Package, Version, Architecture (all mandatory) and
-  Multi-Arch, Pre-Depends, Depends, Conflicts, Breaks, Essential
-  (optional) as they are contained in the dpkg database (see the manpage
-  `dpkg-query (1)`).
-
-- **Status:** (optional, defaults to `uninstalled`). Allowed values are
-  the "package status" names as listed in `dpkg-query (1)` and visible
-  e.g. in the dpkg database as the second value in the space separated
-  list of values in the Status field there. In other words: Neither
-  desired action nor error flags are present in this field in EIPP!
-
-- **APT-ID:** (mandatory). Unique package identifier, according to APT.
-
-
-### Answer
-
-An answer from the external planer to APT is either a *solution* or an
-*error*.
-
-The following invariant on **exit codes** must hold true. When the
-external planer is *able to find a solution*, it will write the solution
-to standard output and then exit with an exit code of 0. When the
-external planer is *unable to find a solution* (and is aware of that),
-it will write an error to standard output and then exit with an exit
-code of 0.  An exit code other than 0 will be interpreted as a planer
-crash with no meaningful error about dependency resolution to convey to
-the user.
-
-
-#### Solution
-
-A solution is a list of Deb 822 stanzas. Each of them could be an:
-
-- unpack stanza to cause the extraction of a package to the disk
-
-- configure stanza to cause an unpacked package to be configured and
-  therefore the installation to be completed
-
-- remove stanza to cause the removal of a package from the system
-
-An **unpack stanza** starts with an Unpack field and supports the
-following fields:
-
-- **Unpack:** (mandatory). The value is a package identifier,
-  referencing one of the package stanzas of the package universe via its
-  APT-ID field.
-
-- All fields supported by package stanzas.
-
-**Configure** and **Remove stanzas** require and support the same
-fields with the exception of the Unpack field which is replaced in
-these instances with the Configure or Remove field respectively.
-
-The order of the stanzas is significant (unlike in the EDSP protocol),
-with the first stanza being the first performed action. If multiple
-stanzas of the same type appear in direct succession the order in such
-a set isn't significant through.
-
-The solution needs to be valid (it is not allowed to configure a package
-before it was unpacked, dependency relations must be satisfied, …), but
-they don't need to be complete: A planer can and should expect that any
-package which wasn't explicitly configured will be configured at the end
-automatically. That also means through that a planer is not allowed to
-produce a solution in which a package remains unconfigured.
-
-In terms of expressivity, all stanzas can carry one single field each, as
-APT-IDs are enough to pinpoint packages to be installed/removed. Nonetheless,
-for protocol readability, it is recommended that planers either add
-unconditionally the fields Package, Version, and Architecture to all
-install/remove stanzas or, alternatively, that they support a `--verbose`
-command line flag that explicitly enables the output of those fields in
-solutions.
-
-#### Error
-
-An error is a single Deb 822 stanza, starting the field Error. The
-following fields are supported in error stanzas:
-
-- **Error:** (mandatory). The value of this field is ignored, although
-  it should be a unique error identifier, such as a UUID.
-
-- **Message:** (mandatory). The value of this field is a text string,
-  meant to be read by humans, that explains the cause of the planer
-  error.  Message fields might be multi-line, like the Description field
-  in the dpkg database. The first line conveys a short message, which
-  can be explained in more details using subsequent lines.
-
-
-### Progress
-
-During dependency solving, an external planer may send progress
-information to APT using **progress stanzas**. A progress stanza starts
-with the Progress field and might contain the following fields:
-
-- **Progress:** (mandatory). The value of this field is a date and time
-  timestamp, in RFC 2822 format. The timestamp provides a time
-  annotation for the progress report.
-
-- **Percentage:** (optional). An integer from 0 to 100, representing the
-  completion of the installation planning process, as declared by the
-  planer.
-
-- **Message:** (optional). A textual message, meant to be read by the
-  APT user, telling what is going on within the installation planer
-  (e.g. the current phase of planning, as declared by the planer).
-
-
-# Future extensions
-
-Potential future extensions to this protocol are to be discussed on
-deity@lists.debian.org.
diff --git a/doc/external-installation-planner-protocol.txt b/doc/external-installation-planner-protocol.txt
new file mode 100644 (file)
index 0000000..319d139
--- /dev/null
@@ -0,0 +1,298 @@
+# APT External Installation Planner Protocol (EIPP) - version 0.1
+
+This document describes the communication protocol between APT and
+external installation planner. The protocol is called APT EIPP, for "APT
+External Installation Planner Protocol".
+
+
+## Terminology
+
+In the following we use the term **architecture qualified package name**
+(or *arch-qualified package names* for short) to refer to package
+identifiers of the form "package:arch" where "package" is a package name
+and "arch" a dpkg architecture.
+
+
+## Components
+
+- **APT**: we know this one.
+- APT is equipped with its own **internal planner** for the order of
+  package installation (and removal) which is identified by the string
+  `internal`.
+- **External planner**: an *external* software component able to plan an
+  installation on behalf of APT.
+
+At each interaction with APT, a single planner is in use.  When there is
+a total of 2 or more planners, internals or externals, the user can
+choose which one to use.
+
+Each planner is identified by an unique string, the **planner name**.
+Planner names must be formed using only alphanumeric ASCII characters,
+dashes, and underscores; planner names must start with a lowercase ASCII
+letter.  The special name `internal` denotes APT's internal planner, is
+reserved, and cannot be used by external planners.
+
+
+## Installation
+
+Each external planner is installed as a file under Dir::Bin::Planners
+(see below), which defaults to `/usr/lib/apt/planners`. We will assume
+in the remainder of this section that such a default value is in effect.
+
+The naming scheme is `/usr/lib/apt/planners/NAME`, where `NAME` is the
+name of the external planner.
+
+Each file under `/usr/lib/apt/planners` corresponding to an external
+planner must be executable.
+
+No non-planner files must be installed under `/usr/lib/apt/planners`, so
+that an index of available external planners can be obtained by listing
+the content of that directory.
+
+
+## Configuration
+
+Several APT options can be used to affect installation planing in APT.
+An overview of them is given below. Please refer to proper APT
+configuration documentation for more, and more up to date, information.
+
+- **APT::Planner**: the name of the planner to be used for dependency
+  solving.  Defaults to `internal`
+
+- **Dir::Bin::Planners**: absolute path of the directory where to look
+  for external solvers. Defaults to `/usr/lib/apt/planners`.
+
+
+## Protocol
+
+When configured to use an external planner, APT will resort to it to
+decide in which order packages should be installed, configured and
+removed.
+
+The interaction happens **in batch**: APT will invoke the external
+planner passing the current status of (half-)installed packages and of
+packages which should be installed, as well as a request denoting the
+packages to install, reinstall, remove and purge.  The external planner
+will compute a valid plan of when and how to call the low-level package
+manager (like dpkg) with each package to satisfy the request.
+
+External planners are invoked by executing them. Communications happens
+via the file descriptors: **stdin** (standard input) and **stdout**
+(standard output). stderr is not used by the EIPP protocol. Planners can
+therefore use stderr to dump debugging information that could be
+inspected separately.
+
+After invocation, the protocol passes through a sequence of phases:
+
+1. APT invokes the external planner
+2. APT send to the planner an installation planner **scenario**
+3. The planner calculates the order. During this phase the planner may
+   send, repeatedly, **progress** information to APT.
+4. The planner sends back to APT an **answer**, i.e. either a *solution*
+   or an *error* report.
+5. The external planner exits
+
+
+### Scenario
+
+A scenario is a text file encoded in a format very similar to the "Deb
+822" format (AKA "the format used by Debian `Packages` files"). A
+scenario consists of two distinct parts: a **request** and a **package
+universe**, occurring in that order. The request consists of a single
+Deb 822 stanza, while the package universe consists of several such
+stanzas. All stanzas occurring in a scenario are separated by an empty
+line.
+
+
+#### Request
+
+Within an installation planner scenario, a request represents the action
+on packages requested by the user explicitly as well as potentially
+additions calculated by a dependency resolver which the user has
+accepted.
+
+An installation planner is not allowed to suggest the modification of
+package states (e.g. removing additional packages) even if it can't
+calculate a solution otherwise – the planner must error out in such
+a case. An exception is made for scenarios which contain packages which
+aren't completely installed (like half-installed or trigger-awaiting):
+Solvers are free to move these packages to a fully installed state (but
+are still forbidden to remove them).
+
+A request is a single Deb 822 stanza opened by a mandatory Request field
+and followed by a mixture of action, preference, and global
+configuration fields.
+
+The value of the **Request:** field is a string describing the EIPP
+protocol which will be used to communicate and especially which answers
+APT will understand. At present, the string must be `EIPP 0.1`. Request
+fields are mainly used to identify the beginning of a request stanza;
+their actual values are otherwise not used by the EIPP protocol.
+
+The following **configuration fields** are supported in request stanzas:
+
+- **Architecture:** (mandatory) The name of the *native* architecture on
+  the user machine (see also: `dpkg --print-architecture`)
+
+- **Architectures:** (optional, defaults to the native architecture) A
+  space separated list of *all* architectures known to APT (this is
+  roughly equivalent to the union of `dpkg --print-architecture` and
+  `dpkg --print-foreign-architectures`)
+
+The following **action fields** are supported in request stanzas:
+
+- **Install:** (optional, defaults to the empty string) A space
+  separated list of arch-qualified package names, with *no version
+  attached*, to install. This field denotes a list of packages that the
+  user wants to install, usually via an APT `install` request.
+
+- **Remove:** (optional, defaults to the empty string) Same syntax of
+  Install. This field denotes a list of packages that the user wants to
+  remove, usually via APT `remove` or `purge` requests.
+
+- **ReInstall:** (optional, defaults to the empty string) Same syntax of
+  Install. This field denotes a list of packages which are installed,
+  but should be reinstalled again e.g. because files shipped by that
+  package were removed or corrupted accidentally, usually requested via
+  an APT `install` request with the `--reinstall` flag.
+
+The following **preference fields** are supported in request stanzas:
+
+- **Planner:** (optional, defaults to the empty string) a purely
+  informational string specifying to which planner this request was send
+  initially.
+
+- **Immediate-Configuration:** (option, unset by default) A boolean
+  value defining if the planner should try to configure all packages as
+  quickly as possible (true) or shouldn't perform any kind of immediate
+  configuration at all (false). If not explicitly set with this field
+  the planner is free to pick either mode or implementing e.g. a mode
+  which configures only packages immediately if they are flagged as
+  `Essential` (or are dependencies of packages marked as `Essential`).
+
+- **Allow-Temporary-Remove-of-Essentials** (optional, defaults to `no`).
+  A boolean value allowing the planner (if set to yes) to temporarily
+  remove an essential package. Associated with the APT::Force-LoopBreak
+  configuration option its main use is highlighting that planners who do
+  temporary removes must take special care in terms of essentials. Legit
+  uses of this option by users is very uncommon, traditionally
+  a situation in which it is needed indicates a packaging error.
+
+
+#### Package universe
+
+A package universe is a list of Deb 822 stanzas, one per package, called
+**package stanzas**. Each package stanzas starts with a Package
+field. The following fields are supported in package stanzas:
+
+- The fields Package, Version, Architecture (all mandatory) and
+  Multi-Arch, Pre-Depends, Depends, Conflicts, Breaks, Essential
+  (optional) as they are contained in the dpkg database (see the manpage
+  `dpkg-query (1)`).
+
+- **Status:** (optional, defaults to `uninstalled`). Allowed values are
+  the "package status" names as listed in `dpkg-query (1)` and visible
+  e.g. in the dpkg database as the second value in the space separated
+  list of values in the Status field there. In other words: Neither
+  desired action nor error flags are present in this field in EIPP!
+
+- **APT-ID:** (mandatory). Unique package identifier, according to APT.
+
+
+### Answer
+
+An answer from the external planner to APT is either a *solution* or an
+*error*.
+
+The following invariant on **exit codes** must hold true. When the
+external planner is *able to find a solution*, it will write the
+solution to standard output and then exit with an exit code of 0. When
+the external planner is *unable to find a solution* (and is aware of
+that), it will write an error to standard output and then exit with an
+exit code of 0.  An exit code other than 0 will be interpreted as
+a planner crash with no meaningful error about dependency resolution to
+convey to the user.
+
+
+#### Solution
+
+A solution is a list of Deb 822 stanzas. Each of them could be an:
+
+- unpack stanza to cause the extraction of a package to the disk
+
+- configure stanza to cause an unpacked package to be configured and
+  therefore the installation to be completed
+
+- remove stanza to cause the removal of a package from the system
+
+An **unpack stanza** starts with an Unpack field and supports the
+following fields:
+
+- **Unpack:** (mandatory). The value is a package identifier,
+  referencing one of the package stanzas of the package universe via its
+  APT-ID field.
+
+- All fields supported by package stanzas.
+
+**Configure** and **Remove stanzas** require and support the same
+fields with the exception of the Unpack field which is replaced in
+these instances with the Configure or Remove field respectively.
+
+The order of the stanzas is significant (unlike in the EDSP protocol),
+with the first stanza being the first performed action. If multiple
+stanzas of the same type appear in direct succession the order in such
+a set isn't significant through.
+
+The solution needs to be valid (it is not allowed to configure a package
+before it was unpacked, dependency relations must be satisfied, …), but
+they don't need to be complete: A planner can and should expect that any
+package which wasn't explicitly configured will be configured at the end
+automatically. That also means through that a planner is not allowed to
+produce a solution in which a package remains unconfigured.
+
+In terms of expressivity, all stanzas can carry one single field each, as
+APT-IDs are enough to pinpoint packages to be installed/removed.
+Nonetheless, for protocol readability, it is recommended that planners
+either add unconditionally the fields Package, Version, and Architecture
+to all install/remove stanzas or, alternatively, that they support
+a `--verbose` command line flag that explicitly enables the output of
+those fields in solutions.
+
+#### Error
+
+An error is a single Deb 822 stanza, starting the field Error. The
+following fields are supported in error stanzas:
+
+- **Error:** (mandatory). The value of this field is ignored, although
+  it should be a unique error identifier, such as a UUID.
+
+- **Message:** (mandatory). The value of this field is a text string,
+  meant to be read by humans, that explains the cause of the planner
+  error.  Message fields might be multi-line, like the Description field
+  in the dpkg database. The first line conveys a short message, which
+  can be explained in more details using subsequent lines.
+
+
+### Progress
+
+During dependency solving, an external planner may send progress
+information to APT using **progress stanzas**. A progress stanza starts
+with the Progress field and might contain the following fields:
+
+- **Progress:** (mandatory). The value of this field is a date and time
+  timestamp, in RFC 2822 format. The timestamp provides a time
+  annotation for the progress report.
+
+- **Percentage:** (optional). An integer from 0 to 100, representing the
+  completion of the installation planning process, as declared by the
+  planner.
+
+- **Message:** (optional). A textual message, meant to be read by the
+  APT user, telling what is going on within the installation planner
+  (e.g. the current phase of planning, as declared by the planner).
+
+
+# Future extensions
+
+Potential future extensions to this protocol are to be discussed on
+deity@lists.debian.org.
index cd8597f80f2f6db7c0634537361f3e33cb75ba4a..795731deb85fa28e25bf42aa7418cae6f4802227 100644 (file)
@@ -191,7 +191,7 @@ aptitude() { runapt aptitude "$@"; }
 aptextracttemplates() { runapt apt-extracttemplates "$@"; }
 aptinternalsolver() { runapt "${APTINTERNALSOLVER}" "$@"; }
 aptdumpsolver() { runapt "${APTDUMPSOLVER}" "$@"; }
-aptinternalplaner() { runapt "${APTINTERNALPLANER}" "$@"; }
+aptinternalplanner() { runapt "${APTINTERNALPLANNER}" "$@"; }
 
 dpkg() {
        "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" "$@"
@@ -290,7 +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"}"
+       APTINTERNALPLANNER="${APT_INTEGRATION_TESTS_INTERNAL_PLANNER:-"${BUILDDIRECTORY}/apt-internal-planner"}"
        test -x "${BUILDDIRECTORY}/apt-get" || msgdie "You need to build tree first"
         # -----
 
@@ -304,13 +304,13 @@ setupenvironment() {
        mkdir -p usr/lib/apt
        ln -s "${METHODSDIR}" usr/lib/apt/methods
        if [ "$BUILDDIRECTORY" = "$LIBRARYPATH" ]; then
-               mkdir -p usr/lib/apt/solvers usr/lib/apt/planers
+               mkdir -p usr/lib/apt/solvers usr/lib/apt/planners
                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-dump-solver" usr/lib/apt/planners/dump
                ln -s "${BUILDDIRECTORY}/apt-internal-solver" usr/lib/apt/solvers/apt
-               ln -s "${BUILDDIRECTORY}/apt-internal-planer" usr/lib/apt/planers/apt
+               ln -s "${BUILDDIRECTORY}/apt-internal-planner" usr/lib/apt/planners/apt
                echo "Dir::Bin::Solvers \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/solvers\";" >> ../aptconfig.conf
-               echo "Dir::Bin::Planers \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/planers\";" >> ../aptconfig.conf
+               echo "Dir::Bin::Planners \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/planners\";" >> ../aptconfig.conf
        fi
         # use the autoremove from the BUILDDIRECTORY if its there, otherwise
         # system
@@ -347,6 +347,7 @@ setupenvironment() {
                echo 'Binary::gpgv::APT::Sandbox::User "root";' >> aptconfig.conf
                # same for the solver executables
                echo 'APT::Solver::RunAsUser "root";' >> aptconfig.conf
+               echo 'APT::Planner::RunAsUser "root";' >> aptconfig.conf
        fi
 
        cat > "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" <<EOF
index ae724fb9e1f903b16f5060720c33f2969c0ee0d7..699cdef150aa714b47f6a8b0f9bec5306c938e59 100755 (executable)
@@ -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' 'apt-internal-planer'; do
+for CMD in 'apt-dump-solver'  'apt-internal-solver' 'apt-internal-planner'; 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
deleted file mode 100755 (executable)
index 15ecd23..0000000
+++ /dev/null
@@ -1,75 +0,0 @@
-#!/bin/sh
-set -e
-
-TESTDIR="$(readlink -f "$(dirname "$0")")"
-. "$TESTDIR/framework"
-setupenvironment
-configarchitecture 'amd64'
-
-buildsimplenativepackage 'libfoo' 'amd64' '3' 'experimental' 'Multi-Arch: same'
-buildsimplenativepackage 'foo' 'all' '3' 'experimental' 'Depends: newstuff'
-buildsimplenativepackage 'foo' 'all' '2' 'unstable' 'Depends: libfoo:amd64, stuff
-Conflicts: bar, libfoo:i386
-Recommends: unrelated'
-buildsimplenativepackage 'libfoo' 'amd64' '2' 'unstable' 'Multi-Arch: same'
-buildsimplenativepackage 'unrelated-2' 'amd64' '2' 'unstable'
-insertinstalledpackage 'foo' 'all' '1'
-insertinstalledpackage 'bar' 'all' '1'
-insertinstalledpackage 'stuff' 'all' '1'
-insertinstalledpackage 'unrelated-1' 'all' '1'
-
-setupaptarchive --no-update
-
-EIPPLOG="${TMPWORKINGDIRECTORY}/rootdir/var/log/apt/eipp.log"
-echo "Dir::Log::Planer \"$EIPPLOG\";" > ./rootdir/etc/apt/apt.conf.d/eipp-logging
-
-testsuccess apt update
-export APT_EDSP_DUMP_FILENAME="${TMPWORKINGDIRECTORY}/eipp.dump"
-testfailure test -r "$EIPPLOG"
-testfailure aptget install foo --planer dump -y
-testfailure test -r "$EIPPLOG"
-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"
-testsuccessequal 'Planer: dump' grep '^Planer: ' "$APT_EDSP_DUMP_FILENAME"
-
-testsuccess aptget install foo -s
-testsuccess aptget install foo -y
-testsuccess test -r "$EIPPLOG"
-testsuccessequal 'Request: EIPP 0.1
-Architecture: amd64
-Architectures: amd64
-Remove: bar:amd64
-Install: libfoo:amd64 foo:amd64
-Planer: internal' head -n 6 "$EIPPLOG"
-aptinternalplaner < "$EIPPLOG" > planer.log || true
-testsuccessequal 'Remove: 6
-Unpack: 2
-Unpack: 4
-Configure: 2
-Configure: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planer.log
-
-rm -f "$EIPPLOG"
-testsuccess aptget install foo -s --reinstall
-testsuccess aptget install foo -y --reinstall
-testsuccess test -r "$EIPPLOG"
-testsuccessequal 'Request: EIPP 0.1
-Architecture: amd64
-Architectures: amd64
-ReInstall: foo:amd64
-Planer: internal' head -n 5 "$EIPPLOG"
-aptinternalplaner < "$EIPPLOG" > planer.log || true
-testsuccessequal 'Unpack: 4
-Configure: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planer.log
-
-rm -f "$EIPPLOG"
-testsuccess aptget purge foo -s
-testsuccess aptget purge foo -y
-testsuccess test -r "$EIPPLOG"
-testsuccessequal 'Request: EIPP 0.1
-Architecture: amd64
-Architectures: amd64
-Remove: foo:amd64
-Planer: internal' head -n 5 "$EIPPLOG"
-aptinternalplaner < "$EIPPLOG" > planer.log || true
-testsuccessequal 'Remove: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planer.log
diff --git a/test/integration/test-external-installation-planner-protocol b/test/integration/test-external-installation-planner-protocol
new file mode 100755 (executable)
index 0000000..bd3c99c
--- /dev/null
@@ -0,0 +1,75 @@
+#!/bin/sh
+set -e
+
+TESTDIR="$(readlink -f "$(dirname "$0")")"
+. "$TESTDIR/framework"
+setupenvironment
+configarchitecture 'amd64'
+
+buildsimplenativepackage 'libfoo' 'amd64' '3' 'experimental' 'Multi-Arch: same'
+buildsimplenativepackage 'foo' 'all' '3' 'experimental' 'Depends: newstuff'
+buildsimplenativepackage 'foo' 'all' '2' 'unstable' 'Depends: libfoo:amd64, stuff
+Conflicts: bar, libfoo:i386
+Recommends: unrelated'
+buildsimplenativepackage 'libfoo' 'amd64' '2' 'unstable' 'Multi-Arch: same'
+buildsimplenativepackage 'unrelated-2' 'amd64' '2' 'unstable'
+insertinstalledpackage 'foo' 'all' '1'
+insertinstalledpackage 'bar' 'all' '1'
+insertinstalledpackage 'stuff' 'all' '1'
+insertinstalledpackage 'unrelated-1' 'all' '1'
+
+setupaptarchive --no-update
+
+EIPPLOG="${TMPWORKINGDIRECTORY}/rootdir/var/log/apt/eipp.log"
+echo "Dir::Log::Planner \"$EIPPLOG\";" > ./rootdir/etc/apt/apt.conf.d/eipp-logging
+
+testsuccess apt update
+export APT_EDSP_DUMP_FILENAME="${TMPWORKINGDIRECTORY}/downloaded/dump.eipp"
+testfailure test -r "$EIPPLOG"
+testfailure aptget install foo --planner dump -y
+testfailure test -r "$EIPPLOG"
+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"
+testsuccessequal 'Planner: dump' grep '^Planner: ' "$APT_EDSP_DUMP_FILENAME"
+
+testsuccess aptget install foo -s
+testsuccess aptget install foo -y
+testsuccess test -r "$EIPPLOG"
+testsuccessequal 'Request: EIPP 0.1
+Architecture: amd64
+Architectures: amd64
+Remove: bar:amd64
+Install: libfoo:amd64 foo:amd64
+Planner: internal' head -n 6 "$EIPPLOG"
+aptinternalplanner < "$EIPPLOG" > planner.log || true
+testsuccessequal 'Remove: 6
+Unpack: 2
+Unpack: 4
+Configure: 2
+Configure: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log
+
+rm -f "$EIPPLOG"
+testsuccess aptget install foo -s --reinstall
+testsuccess aptget install foo -y --reinstall
+testsuccess test -r "$EIPPLOG"
+testsuccessequal 'Request: EIPP 0.1
+Architecture: amd64
+Architectures: amd64
+ReInstall: foo:amd64
+Planner: internal' head -n 5 "$EIPPLOG"
+aptinternalplanner < "$EIPPLOG" > planner.log || true
+testsuccessequal 'Unpack: 4
+Configure: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log
+
+rm -f "$EIPPLOG"
+testsuccess aptget purge foo -s
+testsuccess aptget purge foo -y
+testsuccess test -r "$EIPPLOG"
+testsuccessequal 'Request: EIPP 0.1
+Architecture: amd64
+Architectures: amd64
+Remove: foo:amd64
+Planner: internal' head -n 5 "$EIPPLOG"
+aptinternalplanner < "$EIPPLOG" > planner.log || true
+testsuccessequal 'Remove: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log