]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/deb/dpkgpm.cc
report progress for removing while purging pkgs
[apt.git] / apt-pkg / deb / dpkgpm.cc
index 76b7083595992a98962604845d63459bdaff1ad3..6556f232eee7641c8264d17208f635be0895e372 100644 (file)
@@ -1,10 +1,9 @@
 // -*- mode: cpp; mode: fold -*-
 // Description                                                         /*{{{*/
-// $Id: dpkgpm.cc,v 1.28 2004/01/27 02:25:01 mdz Exp $
 /* ######################################################################
 
    DPKG Package Manager - Provide an interface to dpkg
-   
+
    ##################################################################### */
                                                                        /*}}}*/
 // Includes                                                            /*{{{*/
 #include <apt-pkg/configuration.h>
 #include <apt-pkg/depcache.h>
 #include <apt-pkg/dpkgpm.h>
+#include <apt-pkg/debsystem.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/install-progress.h>
 #include <apt-pkg/packagemanager.h>
-#include <apt-pkg/pkgrecords.h>
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/cacheiterators.h>
 #include <apt-pkg/macros.h>
@@ -27,7 +26,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <grp.h>
-#include <pty.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stddef.h>
@@ -41,7 +39,9 @@
 #include <termios.h>
 #include <time.h>
 #include <unistd.h>
+
 #include <algorithm>
+#include <array>
 #include <cstring>
 #include <iostream>
 #include <map>
 #include <string>
 #include <utility>
 #include <vector>
+#include <sstream>
+#include <numeric>
 
 #include <apti18n.h>
                                                                        /*}}}*/
 
 using namespace std;
 
-class pkgDPkgPMPrivate 
+APT_PURE static string AptHistoryRequestingUser()                      /*{{{*/
+{
+   const char* EnvKeys[]{"SUDO_UID", "PKEXEC_UID", "PACKAGEKIT_CALLER_UID"};
+
+   for (const auto &Key: EnvKeys)
+   {
+      if (getenv(Key) != nullptr)
+      {
+         int uid = atoi(getenv(Key));
+         if (uid > 0) {
+            struct passwd pwd;
+            struct passwd *result;
+            char buf[255];
+            if (getpwuid_r(uid, &pwd, buf, sizeof(buf), &result) == 0 && result != NULL) {
+               std::string res;
+               strprintf(res, "%s (%d)", pwd.pw_name, uid);
+               return res;
+            }
+         }
+      }
+   }
+   return "";
+}
+                                                                       /*}}}*/
+APT_PURE static unsigned int EnvironmentSize()                         /*{{{*/
+{
+  unsigned int size = 0;
+  char **envp = environ;
+
+  while (*envp != NULL)
+    size += strlen (*envp++) + 1;
+
+  return size;
+}
+                                                                       /*}}}*/
+class pkgDPkgPMPrivate                                                 /*{{{*/
 {
 public:
    pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
-                       term_out(NULL), history_out(NULL), 
-                        progress(NULL), master(-1), slave(-1)
+                       term_out(NULL), history_out(NULL),
+                       progress(NULL), tt_is_valid(false), master(-1),
+                       slave(NULL), protect_slave_from_dying(-1),
+                       direct_stdin(false)
    {
       dpkgbuf[0] = '\0';
    }
@@ -70,23 +109,26 @@ public:
    bool stdin_is_dev_null;
    // the buffer we use for the dpkg status-fd reading
    char dpkgbuf[1024];
-   int dpkgbuf_pos;
+   size_t dpkgbuf_pos;
    FILE *term_out;
    FILE *history_out;
    string dpkg_error;
    APT::Progress::PackageManager *progress;
 
    // pty stuff
-   struct      termios tt;
+   struct termios tt;
+   bool tt_is_valid;
    int master;
-   int slave;
+   char * slave;
+   int protect_slave_from_dying;
 
    // signals
    sigset_t sigmask;
    sigset_t original_sigmask;
 
+   bool direct_stdin;
 };
-
+                                                                       /*}}}*/
 namespace
 {
   // Maps the dpkg "processing" info to human readable names.  Entry 0
@@ -110,7 +152,7 @@ namespace
     const char *target;
 
   public:
-    MatchProcessingOp(const char *the_target)
+    explicit MatchProcessingOp(const char *the_target)
       : target(the_target)
     {
     }
@@ -122,13 +164,10 @@ namespace
   };
 }
 
-/* helper function to ionice the given PID 
-
- there is no C header for ionice yet - just the syscall interface
- so we use the binary from util-linux
-*/
-static bool
-ionice(int PID)
+// ionice - helper function to ionice the given PID                    /*{{{*/
+/* there is no C header for ionice yet - just the syscall interface
+   so we use the binary from util-linux */
+static bool ionice(int PID)
 {
    if (!FileExists("/usr/bin/ionice"))
       return false;
@@ -146,58 +185,23 @@ ionice(int PID)
    }
    return ExecWait(Process, "ionice");
 }
-
-static std::string getDpkgExecutable()
-{
-   string Tmp = _config->Find("Dir::Bin::dpkg","dpkg");
-   string const dpkgChrootDir = _config->FindDir("DPkg::Chroot-Directory", "/");
-   size_t dpkgChrootLen = dpkgChrootDir.length();
-   if (dpkgChrootDir != "/" && Tmp.find(dpkgChrootDir) == 0)
-   {
-      if (dpkgChrootDir[dpkgChrootLen - 1] == '/')
-         --dpkgChrootLen;
-      Tmp = Tmp.substr(dpkgChrootLen);
-   }
-   return Tmp;
-}
-
-// dpkgChrootDirectory - chrooting for dpkg if needed                  /*{{{*/
-static void dpkgChrootDirectory()
-{
-   std::string const chrootDir = _config->FindDir("DPkg::Chroot-Directory");
-   if (chrootDir == "/")
-      return;
-   std::cerr << "Chrooting into " << chrootDir << std::endl;
-   if (chroot(chrootDir.c_str()) != 0)
-      _exit(100);
-   if (chdir("/") != 0)
-      _exit(100);
-}
                                                                        /*}}}*/
-
-
 // FindNowVersion - Helper to find a Version in "now" state    /*{{{*/
 // ---------------------------------------------------------------------
-/* This is helpful when a package is no longer installed but has residual 
+/* This is helpful when a package is no longer installed but has residual
  * config files
  */
-static 
+static
 pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg)
 {
    pkgCache::VerIterator Ver;
    for (Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
-   {
-      pkgCache::VerFileIterator Vf = Ver.FileList();
-      pkgCache::PkgFileIterator F = Vf.File();
-      for (F = Vf.File(); F.end() == false; ++F)
-      {
-         if (F && F.Archive())
-         {
-            if (strcmp(F.Archive(), "now")) 
-               return Ver;
-         }
-      }
-   }
+      for (pkgCache::VerFileIterator Vf = Ver.FileList(); Vf.end() == false; ++Vf)
+        for (pkgCache::PkgFileIterator F = Vf.File(); F.end() == false; ++F)
+        {
+           if (F.Archive() != 0 && strcmp(F.Archive(), "now") == 0)
+              return Ver;
+        }
    return Ver;
 }
                                                                        /*}}}*/
@@ -205,10 +209,9 @@ pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg)
 // DPkgPM::pkgDPkgPM - Constructor                                     /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache) 
-   : pkgPackageManager(Cache), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
+pkgDPkgPM::pkgDPkgPM(pkgDepCache *Cache)
+   : pkgPackageManager(Cache),d(new pkgDPkgPMPrivate()), pkgFailures(0), PackagesDone(0), PackagesTotal(0)
 {
-   d = new pkgDPkgPMPrivate();
 }
                                                                        /*}}}*/
 // DPkgPM::pkgDPkgPM - Destructor                                      /*{{{*/
@@ -268,7 +271,7 @@ bool pkgDPkgPM::Remove(PkgIterator Pkg,bool Purge)
 {
    if (Pkg.end() == true)
       return false;
-   
+
    if (Purge == true)
       List.push_back(Item(Item::Purge,Pkg));
    else
@@ -309,14 +312,14 @@ bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version)
         Top = Top->Child;
         continue;
       }
-      
+
       while (Top != 0 && Top->Next == 0)
         Top = Top->Parent;
       if (Top != 0)
         Top = Top->Next;
-   }   
+   }
    fprintf(F,"\n");
+
    // Write out the package actions in order.
    for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
    {
@@ -324,7 +327,7 @@ bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version)
         continue;
 
       pkgDepCache::StateCache &S = Cache[I->Pkg];
-      
+
       fprintf(F,"%s ",I->Pkg.Name());
 
       // Current version which we are going to replace
@@ -379,13 +382,13 @@ bool pkgDPkgPM::SendPkgsInfo(FILE * const F, unsigned int const &Version)
            fprintf(F,"**ERROR**\n");
         else
            fprintf(F,"%s\n",I->File.c_str());
-      }      
+      }
       else if (I->Op == Item::Configure)
         fprintf(F,"**CONFIGURE**\n");
       else if (I->Op == Item::Remove ||
          I->Op == Item::Purge)
         fprintf(F,"**REMOVE**\n");
-      
+
       if (ferror(F) != 0)
         return false;
    }
@@ -407,7 +410,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
    Opts = Opts->Child;
 
    sighandler_t old_sigpipe = signal(SIGPIPE, SIG_IGN);
-   
+
    unsigned int Count = 1;
    for (; Opts != 0; Opts = Opts->Next, Count++)
    {
@@ -424,10 +427,10 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
       if ((Pos = OptSec.find(' ')) == string::npos || Pos == 0)
         Pos = OptSec.length();
       OptSec = "DPkg::Tools::Options::" + string(Opts->Value.c_str(),Pos);
-      
+
       unsigned int Version = _config->FindI(OptSec+"::Version",1);
       unsigned int InfoFD = _config->FindI(OptSec + "::InfoFD", STDIN_FILENO);
-      
+
       // Create the pipes
       std::set<int> KeepFDs;
       MergeKeepFdsFromConfiguration(KeepFDs);
@@ -458,7 +461,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
         strprintf(hookfd, "%d", InfoFD);
         setenv("APT_HOOK_INFO_FD", hookfd.c_str(), 1);
 
-        dpkgChrootDirectory();
+        debSystem::DpkgChrootDirectory();
         const char *Args[4];
         Args[0] = "/bin/sh";
         Args[1] = "-c";
@@ -470,10 +473,10 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
       close(Pipes[0]);
       FILE *F = fdopen(Pipes[1],"w");
       if (F == 0) {
-         result = _error->Errno("fdopen","Faild to open new FD");
+         result = _error->Errno("fdopen","Failed to open new FD");
          break;
       }
-      
+
       // Feed it the filenames.
       if (Version <= 1)
       {
@@ -486,7 +489,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
            // No errors here..
            if (I->File[0] != '/')
               continue;
-           
+
            /* Feed the filename of each package that is pending install
               into the pipe. */
            fprintf(F,"%s\n",I->File.c_str());
@@ -498,7 +501,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
         SendPkgsInfo(F, Version);
 
       fclose(F);
-      
+
       // Clean up the sub process
       if (ExecWait(Process,Opts->Value.c_str()) == false) {
         result = _error->Error("Failure running script %s",Opts->Value.c_str());
@@ -510,7 +513,7 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
    return result;
 }
                                                                        /*}}}*/
-// DPkgPM::DoStdin - Read stdin and pass to slave pty                  /*{{{*/
+// DPkgPM::DoStdin - Read stdin and pass to master pty                 /*{{{*/
 // ---------------------------------------------------------------------
 /*
 */
@@ -542,18 +545,15 @@ void pkgDPkgPM::DoTerminalPty(int master)
       struct timespec sleepfor = { 0, 500000000 };
       nanosleep(&sleepfor, NULL);
       return;
-   }  
-   if(len <= 0) 
+   }
+   if(len <= 0)
       return;
    FileFd::Write(1, term_buf, len);
    if(d->term_out)
       fwrite(term_buf, len, sizeof(char), d->term_out);
 }
                                                                        /*}}}*/
-// DPkgPM::ProcessDpkgStatusBuf                                                /*{{{*/
-// ---------------------------------------------------------------------
-/*
- */
+// DPkgPM::ProcessDpkgStatusBuf                                                /*{{{*/
 void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
 {
    bool const Debug = _config->FindB("Debug::pkgDPkgProgressReporting",false);
@@ -563,14 +563,14 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
    /* dpkg sends strings like this:
       'status:   <pkg>: <pkg  qstate>'
       'status:   <pkg>:<arch>: <pkg  qstate>'
-      
-      'processing: {install,configure,remove,purge,disappear,trigproc}: pkg'
-      'processing: {install,configure,remove,purge,disappear,trigproc}: trigger'
+
+      'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: pkg'
+      'processing: {install,upgrade,configure,remove,purge,disappear,trigproc}: trigger'
    */
 
    // we need to split on ": " (note the appended space) as the ':' is
    // part of the pkgname:arch information that dpkg sends
-   // 
+   //
    // A dpkg error message may contain additional ":" (like
    //  "failed in buffer_write(fd) (10, ret=-1): backend dpkg-deb ..."
    // so we need to ensure to not split too much
@@ -589,12 +589,15 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
    std::string action;
 
    // "processing" has the form "processing: action: pkg or trigger"
-   // with action = ["install", "configure", "remove", "purge", "disappear",
-   //                "trigproc"]
+   // with action = ["install", "upgrade", "configure", "remove", "purge",
+   //                "disappear", "trigproc"]
    if (prefix == "processing")
    {
       pkgname = APT::String::Strip(list[2]);
       action = APT::String::Strip(list[1]);
+      // we don't care for the difference (as dpkg doesn't really either)
+      if (action == "upgrade")
+        action = "install";
    }
    // "status" has the form: "status: pkg: state"
    // with state in ["half-installed", "unpacked", "half-configured", 
@@ -621,15 +624,15 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
    {
       if(action == "error")
       {
-         d->progress->Error(list[1], PackagesDone, PackagesTotal,
+         d->progress->Error(pkgname, PackagesDone, PackagesTotal,
                             list[3]);
          pkgFailures++;
-         WriteApportReport(list[1].c_str(), list[3].c_str());
+         WriteApportReport(pkgname.c_str(), list[3].c_str());
          return;
       }
       else if(action == "conffile-prompt")
       {
-         d->progress->ConffilePrompt(list[1], PackagesDone, PackagesTotal,
+         d->progress->ConffilePrompt(pkgname, PackagesDone, PackagesTotal,
                                      list[3]);
          return;
       }
@@ -638,27 +641,26 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
    // at this point we know that we should have a valid pkgname, so build all 
    // the info from it
 
-   // dpkg does not send always send "pkgname:arch" so we add it here 
-   // if needed
+   // dpkg does not always send "pkgname:arch" so we add it here if needed
    if (pkgname.find(":") == std::string::npos)
    {
-      // find the package in the group that is in a touched by dpkg
-      // if there are multiple dpkg will send us a full pkgname:arch
+      // find the package in the group that is touched by dpkg
+      // if there are multiple pkgs dpkg would send us a full pkgname:arch
       pkgCache::GrpIterator Grp = Cache.FindGrp(pkgname);
-      if (Grp.end() == false) 
+      if (Grp.end() == false)
       {
-          pkgCache::PkgIterator P = Grp.PackageList();
-          for (; P.end() != true; P = Grp.NextPkg(P))
-          {
-              if(Cache[P].Mode != pkgDepCache::ModeKeep)
-              {
-                  pkgname = P.FullName();
-                  break;
-              }
-          }
+        pkgCache::PkgIterator P = Grp.PackageList();
+        for (; P.end() != true; P = Grp.NextPkg(P))
+        {
+           if(Cache[P].Keep() == false || Cache[P].ReInstall() == true)
+           {
+              pkgname = P.FullName();
+              break;
+           }
+        }
       }
    }
-   
+
    const char* const pkg = pkgname.c_str();
    std::string short_pkgname = StringSplit(pkgname, ":")[0];
    std::string arch = "";
@@ -687,38 +689,39 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
       d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
 
       // FIXME: this needs a muliarch testcase
-      // FIXME2: is "pkgname" here reliable with dpkg only sending us 
+      // FIXME2: is "pkgname" here reliable with dpkg only sending us
       //         short pkgnames?
       if (action == "disappear")
         handleDisappearAction(pkgname);
       return;
-   } 
+   }
 
    if (prefix == "status")
    {
       vector<struct DpkgState> const &states = PackageOps[pkg];
-      const char *next_action = NULL;
       if(PackageOpsDone[pkg] < states.size())
-         next_action = states[PackageOpsDone[pkg]].state;
-      // check if the package moved to the next dpkg state
-      if(next_action && (action == next_action))
       {
-         // only read the translation if there is actually a next
-         // action
-         const char *translation = _(states[PackageOpsDone[pkg]].str);
-         std::string msg;
-
-         // we moved from one dpkg state to a new one, report that
-         PackageOpsDone[pkg]++;
-         PackagesDone++;
-
-         strprintf(msg, translation, i18n_pkgname.c_str());
-         d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
-         
+         char const * const next_action = states[PackageOpsDone[pkg]].state;
+        if (next_action && Debug == true)
+           std::clog << "(parsed from dpkg) pkg: " << short_pkgname
+              << " action: " << action << " (expected: '" << next_action << "' "
+              << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl;
+
+        // check if the package moved to the next dpkg state
+        if(next_action && (action == next_action))
+        {
+           // only read the translation if there is actually a next action
+           char const * const translation = _(states[PackageOpsDone[pkg]].str);
+
+           // we moved from one dpkg state to a new one, report that
+           ++PackageOpsDone[pkg];
+           ++PackagesDone;
+
+           std::string msg;
+           strprintf(msg, translation, i18n_pkgname.c_str());
+           d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
+        }
       }
-      if (Debug == true) 
-         std::clog << "(parsed from dpkg) pkg: " << short_pkgname
-                   << " action: " << action << endl;
    }
 }
                                                                        /*}}}*/
@@ -771,40 +774,33 @@ void pkgDPkgPM::handleDisappearAction(string const &pkgname)
 }
                                                                        /*}}}*/
 // DPkgPM::DoDpkgStatusFd                                              /*{{{*/
-// ---------------------------------------------------------------------
-/*
- */
 void pkgDPkgPM::DoDpkgStatusFd(int statusfd)
 {
-   char *p, *q;
-   int len;
-
-   len=read(statusfd, &d->dpkgbuf[d->dpkgbuf_pos], sizeof(d->dpkgbuf)-d->dpkgbuf_pos);
-   d->dpkgbuf_pos += len;
+   ssize_t const len = read(statusfd, &d->dpkgbuf[d->dpkgbuf_pos],
+        (sizeof(d->dpkgbuf)/sizeof(d->dpkgbuf[0])) - d->dpkgbuf_pos);
    if(len <= 0)
       return;
+   d->dpkgbuf_pos += (len / sizeof(d->dpkgbuf[0]));
 
-   // process line by line if we have a buffer
-   p = q = d->dpkgbuf;
-   while((q=(char*)memchr(p, '\n', d->dpkgbuf+d->dpkgbuf_pos-p)) != NULL)
+   // process line by line from the buffer
+   char *p = d->dpkgbuf, *q = nullptr;
+   while((q=(char*)memchr(p, '\n', (d->dpkgbuf + d->dpkgbuf_pos) - p)) != nullptr)
    {
-      *q = 0;
+      *q = '\0';
       ProcessDpkgStatusLine(p);
-      p=q+1; // continue with next line
+      p = q + 1; // continue with next line
    }
 
-   // now move the unprocessed bits (after the final \n that is now a 0x0) 
-   // to the start and update d->dpkgbuf_pos
-   p = (char*)memrchr(d->dpkgbuf, 0, d->dpkgbuf_pos);
-   if(p == NULL)
+   // check if we stripped the buffer clean
+   if (p > (d->dpkgbuf + d->dpkgbuf_pos))
+   {
+      d->dpkgbuf_pos = 0;
       return;
+   }
 
-   // we are interessted in the first char *after* 0x0
-   p++;
-
-   // move the unprocessed tail to the start and update pos
-   memmove(d->dpkgbuf, p, p-d->dpkgbuf);
-   d->dpkgbuf_pos = d->dpkgbuf+d->dpkgbuf_pos-p;
+   // otherwise move the unprocessed tail to the start and update pos
+   memmove(d->dpkgbuf, p, (p - d->dpkgbuf));
+   d->dpkgbuf_pos = (d->dpkgbuf + d->dpkgbuf_pos) - p;
 }
                                                                        /*}}}*/
 // DPkgPM::WriteHistoryTag                                             /*{{{*/
@@ -829,7 +825,8 @@ bool pkgDPkgPM::OpenLog()
    // get current time
    char timestr[200];
    time_t const t = time(NULL);
-   struct tm const * const tmp = localtime(&t);
+   struct tm tm_buf;
+   struct tm const * const tmp = localtime_r(&t, &tm_buf);
    strftime(timestr, sizeof(timestr), "%F  %T", tmp);
 
    // open terminal log
@@ -899,6 +896,9 @@ bool pkgDPkgPM::OpenLog()
       }
       if (_config->Exists("Commandline::AsString") == true)
         WriteHistoryTag("Commandline", _config->Find("Commandline::AsString"));
+      std::string RequestingUser = AptHistoryRequestingUser();
+      if (RequestingUser != "")
+         WriteHistoryTag("Requested-By", RequestingUser);
       WriteHistoryTag("Install", install);
       WriteHistoryTag("Reinstall", reinstall);
       WriteHistoryTag("Upgrade", upgrade);
@@ -907,7 +907,7 @@ bool pkgDPkgPM::OpenLog()
       WriteHistoryTag("Purge",purge);
       fflush(d->history_out);
    }
-   
+
    return true;
 }
                                                                        /*}}}*/
@@ -916,7 +916,8 @@ bool pkgDPkgPM::CloseLog()
 {
    char timestr[200];
    time_t t = time(NULL);
-   struct tm *tmp = localtime(&t);
+   struct tm tm_buf;
+   struct tm *tmp = localtime_r(&t, &tm_buf);
    strftime(timestr, sizeof(timestr), "%F  %T", tmp);
 
    if(d->term_out)
@@ -955,219 +956,269 @@ bool pkgDPkgPM::CloseLog()
    return true;
 }
                                                                        /*}}}*/
-                                                                       /*}}}*/
-/*{{{*/
-// This implements a racy version of pselect for those architectures
-// that don't have a working implementation.
-// FIXME: Probably can be removed on Lenny+1
-static int racy_pselect(int nfds, fd_set *readfds, fd_set *writefds,
-   fd_set *exceptfds, const struct timespec *timeout,
-   const sigset_t *sigmask)
-{
-   sigset_t origmask;
-   struct timeval tv;
-   int retval;
-
-   tv.tv_sec = timeout->tv_sec;
-   tv.tv_usec = timeout->tv_nsec/1000;
-
-   sigprocmask(SIG_SETMASK, sigmask, &origmask);
-   retval = select(nfds, readfds, writefds, exceptfds, &tv);
-   sigprocmask(SIG_SETMASK, &origmask, 0);
-   return retval;
-}
-                                                                        /*}}}*/
 
 // DPkgPM::BuildPackagesProgressMap                                    /*{{{*/
 void pkgDPkgPM::BuildPackagesProgressMap()
 {
    // map the dpkg states to the operations that are performed
    // (this is sorted in the same way as Item::Ops)
-   static const struct DpkgState DpkgStatesOpMap[][7] = {
+   static const std::array<std::array<DpkgState, 3>, 4> DpkgStatesOpMap = {{
       // Install operation
-      { 
-        {"half-installed", N_("Preparing %s")}, 
-        {"unpacked", N_("Unpacking %s") }, 
-        {NULL, NULL}
-      },
+      {{
+        {"half-installed", N_("Preparing %s")},
+        {"unpacked", N_("Unpacking %s") },
+        {nullptr, nullptr}
+      }},
       // Configure operation
-      { 
+      {{
         {"unpacked",N_("Preparing to configure %s") },
         {"half-configured", N_("Configuring %s") },
         { "installed", N_("Installed %s")},
-        {NULL, NULL}
-      },
+      }},
       // Remove operation
-      { 
+      {{
         {"half-configured", N_("Preparing for removal of %s")},
         {"half-installed", N_("Removing %s")},
         {"config-files",  N_("Removed %s")},
-        {NULL, NULL}
-      },
+      }},
       // Purge operation
-      { 
+      {{
         {"config-files", N_("Preparing to completely remove %s")},
         {"not-installed", N_("Completely removed %s")},
-        {NULL, NULL}
-      },
-   };
+        {nullptr, nullptr}
+      }},
+   }};
+   static_assert(Item::Purge == 3, "Enum item has unexpected index for mapping array");
 
    // init the PackageOps map, go over the list of packages that
    // that will be [installed|configured|removed|purged] and add
    // them to the PackageOps map (the dpkg states it goes through)
    // and the PackageOpsTranslations (human readable strings)
-   for (vector<Item>::const_iterator I = List.begin(); I != List.end(); ++I)
+   for (auto &&I : List)
    {
-      if((*I).Pkg.end() == true)
+      if(I.Pkg.end() == true)
         continue;
 
-      string const name = (*I).Pkg.FullName();
+      string const name = I.Pkg.FullName();
       PackageOpsDone[name] = 0;
-      for(int i=0; (DpkgStatesOpMap[(*I).Op][i]).state != NULL; ++i)
+      auto AddToPackageOps = std::back_inserter(PackageOps[name]);
+      if (I.Op == Item::Purge && I.Pkg->CurrentVer != 0)
       {
-        PackageOps[name].push_back(DpkgStatesOpMap[(*I).Op][i]);
-        PackagesTotal++;
+        // purging a package which is installed first passes through remove states
+        auto const DpkgOps = DpkgStatesOpMap[Item::Remove];
+        std::copy(DpkgOps.begin(), DpkgOps.end(), AddToPackageOps);
+        PackagesTotal += DpkgOps.size();
       }
+      auto const DpkgOps = DpkgStatesOpMap[I.Op];
+      std::copy_if(DpkgOps.begin(), DpkgOps.end(), AddToPackageOps, [&](DpkgState const &state) {
+        if (state.state == nullptr)
+           return false;
+        ++PackagesTotal;
+        return true;
+      });
    }
+   /* one extra: We don't want the progress bar to reach 100%, especially not
+      if we call dpkg --configure --pending and process a bunch of triggers
+      while showing 100%. Also, spindown takes a while, so never reaching 100%
+      is way more correct than reaching 100% while still doing stuff even if
+      doing it this way is slightly bending the rules */
+   ++PackagesTotal;
 }
                                                                         /*}}}*/
-#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR < 13)
-bool pkgDPkgPM::Go(int StatusFd)
+bool pkgDPkgPM::Go(int StatusFd)                                       /*{{{*/
 {
    APT::Progress::PackageManager *progress = NULL;
    if (StatusFd == -1)
       progress = APT::Progress::PackageManagerProgressFactory();
    else
       progress = new APT::Progress::PackageManagerProgressFd(StatusFd);
-   
-   return GoNoABIBreak(progress);
-}
-#endif
 
-void pkgDPkgPM::StartPtyMagic()
+   return Go(progress);
+}
+                                                                       /*}}}*/
+void pkgDPkgPM::StartPtyMagic()                                                /*{{{*/
 {
    if (_config->FindB("Dpkg::Use-Pty", true) == false)
    {
-      d->master = d->slave = -1;
+      d->master = -1;
+      if (d->slave != NULL)
+        free(d->slave);
+      d->slave = NULL;
       return;
    }
 
-   // setup the pty and stuff
-   struct winsize win;
+   if (isatty(STDIN_FILENO) == 0)
+      d->direct_stdin = true;
 
-   // if tcgetattr for both stdin/stdout returns 0 (no error)
-   // we do the pty magic
    _error->PushToStack();
-   if (tcgetattr(STDIN_FILENO, &d->tt) == 0 &&
-       tcgetattr(STDOUT_FILENO, &d->tt) == 0)
+
+   d->master = posix_openpt(O_RDWR | O_NOCTTY);
+   if (d->master == -1)
+      _error->Errno("posix_openpt", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
+   else if (unlockpt(d->master) == -1)
+      _error->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d->master);
+   else
    {
-       if (ioctl(STDOUT_FILENO, TIOCGWINSZ, (char *)&win) < 0)
-       {
-           _error->Errno("ioctl", _("ioctl(TIOCGWINSZ) failed"));
-       } else if (openpty(&d->master, &d->slave, NULL, &d->tt, &win) < 0)
-       {
-           _error->Errno("openpty", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
-           d->master = d->slave = -1;
-        } else {
-           struct termios rtt;
-           rtt = d->tt;
-           cfmakeraw(&rtt);
-           rtt.c_lflag &= ~ECHO;
-           rtt.c_lflag |= ISIG;
+#ifdef HAVE_PTS_NAME_R
+      char slave_name[64];     // 64 is used by bionic
+      if (ptsname_r(d->master, slave_name, sizeof(slave_name)) != 0)
+#else
+      char const * const slave_name = ptsname(d->master);
+      if (slave_name == NULL)
+#endif
+        _error->Errno("ptsname", "Getting name for slave of master fd %d failed!", d->master);
+      else
+      {
+        d->slave = strdup(slave_name);
+        if (d->slave == NULL)
+           _error->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name, d->master);
+        else if (grantpt(d->master) == -1)
+           _error->Errno("grantpt", "Granting access to slave %s based on master fd %d failed!", slave_name, d->master);
+        else if (tcgetattr(STDIN_FILENO, &d->tt) == 0)
+        {
+           d->tt_is_valid = true;
+           struct termios raw_tt;
+           // copy window size of stdout if its a 'good' terminal
+           if (tcgetattr(STDOUT_FILENO, &raw_tt) == 0)
+           {
+              struct winsize win;
+              if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) < 0)
+                 _error->Errno("ioctl", "Getting TIOCGWINSZ from stdout failed!");
+              if (ioctl(d->master, TIOCSWINSZ, &win) < 0)
+                 _error->Errno("ioctl", "Setting TIOCSWINSZ for master fd %d failed!", d->master);
+           }
+           if (tcsetattr(d->master, TCSANOW, &d->tt) == -1)
+              _error->Errno("tcsetattr", "Setting in Start via TCSANOW for master fd %d failed!", d->master);
+
+           raw_tt = d->tt;
+           cfmakeraw(&raw_tt);
+           raw_tt.c_lflag &= ~ECHO;
+           raw_tt.c_lflag |= ISIG;
            // block SIGTTOU during tcsetattr to prevent a hang if
            // the process is a member of the background process group
            // http://www.opengroup.org/onlinepubs/000095399/functions/tcsetattr.html
            sigemptyset(&d->sigmask);
            sigaddset(&d->sigmask, SIGTTOU);
            sigprocmask(SIG_BLOCK,&d->sigmask, &d->original_sigmask);
-           tcsetattr(0, TCSAFLUSH, &rtt);
-           sigprocmask(SIG_SETMASK, &d->original_sigmask, 0);
-        }
+           if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_tt) == -1)
+              _error->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdin failed!");
+           sigprocmask(SIG_SETMASK, &d->original_sigmask, NULL);
+
+        }
+        if (d->slave != NULL)
+        {
+           /* on linux, closing (and later reopening) all references to the slave
+              makes the slave a death end, so we open it here to have one open all
+              the time. We could use this fd in SetupSlavePtyMagic() for linux, but
+              on kfreebsd we get an incorrect ("step like") output then while it has
+              no problem with closing all references… so to avoid platform specific
+              code here we combine both and be happy once more */
+           d->protect_slave_from_dying = open(d->slave, O_RDWR | O_CLOEXEC | O_NOCTTY);
+        }
       }
-   // complain only if stdout is either a terminal (but still failed) or is an invalid
-      // descriptor otherwise we would complain about redirection to e.g. /dev/null as well.
-      else if (isatty(STDOUT_FILENO) == 1 || errno == EBADF)
-         _error->Errno("tcgetattr", _("Can not write log (%s)"), _("Is stdout a terminal?"));
-
-      if (_error->PendingError() == true)
-        _error->DumpErrors(std::cerr);
-      _error->RevertToStack();
+   }
+
+   if (_error->PendingError() == true)
+   {
+      if (d->master != -1)
+      {
+        close(d->master);
+        d->master = -1;
+      }
+      if (d->slave != NULL)
+      {
+        free(d->slave);
+        d->slave = NULL;
+      }
+      _error->DumpErrors(std::cerr, GlobalError::DEBUG, false);
+   }
+   _error->RevertToStack();
 }
+                                                                       /*}}}*/
+void pkgDPkgPM::SetupSlavePtyMagic()                                   /*{{{*/
+{
+   if(d->master == -1 || d->slave == NULL)
+      return;
+
+   if (close(d->master) == -1)
+      _error->FatalE("close", "Closing master %d in child failed!", d->master);
+   d->master = -1;
+   if (setsid() == -1)
+      _error->FatalE("setsid", "Starting a new session for child failed!");
+
+   int const slaveFd = open(d->slave, O_RDWR | O_NOCTTY);
+   if (slaveFd == -1)
+      _error->FatalE("open", _("Can not write log (%s)"), _("Is /dev/pts mounted?"));
+   else if (ioctl(slaveFd, TIOCSCTTY, 0) < 0)
+      _error->FatalE("ioctl", "Setting TIOCSCTTY for slave fd %d failed!", slaveFd);
+   else
+   {
+      unsigned short i = 0;
+      if (d->direct_stdin == true)
+        ++i;
+      for (; i < 3; ++i)
+        if (dup2(slaveFd, i) == -1)
+           _error->FatalE("dup2", "Dupping %d to %d in child failed!", slaveFd, i);
+
+      if (d->tt_is_valid == true && tcsetattr(STDIN_FILENO, TCSANOW, &d->tt) < 0)
+        _error->FatalE("tcsetattr", "Setting in Setup via TCSANOW for slave fd %d failed!", slaveFd);
+   }
 
-void pkgDPkgPM::StopPtyMagic()
+   if (slaveFd != -1)
+      close(slaveFd);
+}
+                                                                       /*}}}*/
+void pkgDPkgPM::StopPtyMagic()                                         /*{{{*/
 {
-   if(d->slave > 0)
-      close(d->slave);
-   if(d->master >= 0) 
+   if (d->slave != NULL)
+      free(d->slave);
+   d->slave = NULL;
+   if (d->protect_slave_from_dying != -1)
    {
-      tcsetattr(0, TCSAFLUSH, &d->tt);
+      close(d->protect_slave_from_dying);
+      d->protect_slave_from_dying = -1;
+   }
+   if(d->master >= 0)
+   {
+      if (d->tt_is_valid == true && tcsetattr(STDIN_FILENO, TCSAFLUSH, &d->tt) == -1)
+        _error->FatalE("tcsetattr", "Setting in Stop via TCSAFLUSH for stdin failed!");
       close(d->master);
+      d->master = -1;
    }
 }
 
 // DPkgPM::Go - Run the sequence                                       /*{{{*/
 // ---------------------------------------------------------------------
-/* This globs the operations and calls dpkg 
+/* This globs the operations and calls dpkg
  *
  * If it is called with a progress object apt will report the install
  * progress to this object. It maps the dpkg states a package goes
  * through to human readable (and i10n-able)
  * names and calculates a percentage for each step.
  */
-#if (APT_PKG_MAJOR >= 4 && APT_PKG_MINOR >= 13)
 bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
-#else
-bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
-#endif
 {
    pkgPackageManager::SigINTStop = false;
    d->progress = progress;
 
    // Generate the base argument list for dpkg
-   unsigned long StartSize = 0;
-   std::vector<const char *> Args;
-   std::string DpkgExecutable = getDpkgExecutable();
-   Args.push_back(DpkgExecutable.c_str());
-   StartSize += DpkgExecutable.length();
-
-   // Stick in any custom dpkg options
-   Configuration::Item const *Opts = _config->Tree("DPkg::Options");
-   if (Opts != 0)
-   {
-      Opts = Opts->Child;
-      for (; Opts != 0; Opts = Opts->Next)
-      {
-        if (Opts->Value.empty() == true)
-           continue;
-        Args.push_back(Opts->Value.c_str());
-        StartSize += Opts->Value.length();
-      }
-   }
-
+   std::vector<std::string> const sArgs = debSystem::GetDpkgBaseCommand();
+   std::vector<const char *> Args(sArgs.size(), NULL);
+   std::transform(sArgs.begin(), sArgs.end(), Args.begin(),
+        [](std::string const &s) { return s.c_str(); });
+   unsigned long long const StartSize = std::accumulate(sArgs.begin(), sArgs.end(), 0llu,
+        [](unsigned long long const i, std::string const &s) { return i + s.length(); });
    size_t const BaseArgs = Args.size();
-   // we need to detect if we can qualify packages with the architecture or not
-   Args.push_back("--assert-multi-arch");
-   Args.push_back(NULL);
-
-   pid_t dpkgAssertMultiArch = ExecFork();
-   if (dpkgAssertMultiArch == 0)
-   {
-      dpkgChrootDirectory();
-      // redirect everything to the ultimate sink as we only need the exit-status
-      int const nullfd = open("/dev/null", O_RDONLY);
-      dup2(nullfd, STDIN_FILENO);
-      dup2(nullfd, STDOUT_FILENO);
-      dup2(nullfd, STDERR_FILENO);
-      execvp(Args[0], (char**) &Args[0]);
-      _error->WarningE("dpkgGo", "Can't detect if dpkg supports multi-arch!");
-      _exit(2);
-   }
 
    fd_set rfds;
    struct timespec tv;
 
-   unsigned int const MaxArgs = _config->FindI("Dpkg::MaxArgs",8*1024);
-   unsigned int const MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes",32*1024);
+   // try to figure out the max environment size
+   int OSArgMax = sysconf(_SC_ARG_MAX);
+   if(OSArgMax < 0)
+      OSArgMax = 32*1024;
+   OSArgMax -= EnvironmentSize() - 2*1024;
+   unsigned int const MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes", OSArgMax);
    bool const NoTriggers = _config->FindB("DPkg::NoTriggers", false);
 
    if (RunScripts("DPkg::Pre-Invoke") == false)
@@ -1176,40 +1227,44 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
    if (RunScriptsWithPkgs("DPkg::Pre-Install-Pkgs") == false)
       return false;
 
+   auto const noopDPkgInvocation = _config->FindB("Debug::pkgDPkgPM",false);
+   // store auto-bits as they are supposed to be after dpkg is run
+   if (noopDPkgInvocation == false)
+      Cache.writeStateFile(NULL);
+
+   decltype(List)::const_iterator::difference_type const notconfidx =
+      _config->FindB("Dpkg::ExplicitLastConfigure", false) ? std::numeric_limits<decltype(notconfidx)>::max() :
+      std::distance(List.cbegin(), std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }).base());
+
    // support subpressing of triggers processing for special
    // cases like d-i that runs the triggers handling manually
-   bool const SmartConf = (_config->Find("PackageManager::Configure", "all") != "all");
    bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false);
-   if (_config->FindB("DPkg::ConfigurePending", SmartConf) == true)
+   bool const ConfigurePending = _config->FindB("DPkg::ConfigurePending", true);
+   if (ConfigurePending)
       List.push_back(Item(Item::ConfigurePending, PkgIterator()));
 
    // for the progress
    BuildPackagesProgressMap();
 
+   if (notconfidx != std::numeric_limits<decltype(notconfidx)>::max())
+   {
+      if (ConfigurePending)
+        List.erase(std::next(List.begin(), notconfidx), std::prev(List.end()));
+      else
+        List.erase(std::next(List.begin(), notconfidx), List.end());
+   }
+
    d->stdin_is_dev_null = false;
 
    // create log
    OpenLog();
 
-   bool dpkgMultiArch = false;
-   if (dpkgAssertMultiArch > 0)
-   {
-      int Status = 0;
-      while (waitpid(dpkgAssertMultiArch, &Status, 0) != dpkgAssertMultiArch)
-      {
-        if (errno == EINTR)
-           continue;
-        _error->WarningE("dpkgGo", _("Waited for %s but it wasn't there"), "dpkg --assert-multi-arch");
-        break;
-      }
-      if (WIFEXITED(Status) == true && WEXITSTATUS(Status) == 0)
-        dpkgMultiArch = true;
-   }
+   bool dpkgMultiArch = debSystem::SupportsMultiArch();
 
    // start pty magic before the loop
    StartPtyMagic();
 
-   // Tell the progress that its starting and fork dpkg 
+   // Tell the progress that its starting and fork dpkg
    d->progress->Start(d->master);
 
    // this loop is runs once per dpkg operation
@@ -1234,33 +1289,14 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
         for (; J != List.end() && J->Op == I->Op; ++J)
            /* nothing */;
 
-      // keep track of allocated strings for multiarch package names
-      std::vector<char *> Packages;
+      auto const size = (J - I) + 10;
 
       // start with the baseset of arguments
-      unsigned long Size = StartSize;
+      auto Size = StartSize;
       Args.erase(Args.begin() + BaseArgs, Args.end());
-
-      // Now check if we are within the MaxArgs limit
-      //
-      // this code below is problematic, because it may happen that
-      // the argument list is split in a way that A depends on B
-      // and they are in the same "--configure A B" run
-      // - with the split they may now be configured in different
-      //   runs, using Immediate-Configure-All can help prevent this.
-      if (J - I > (signed)MaxArgs)
-      {
-        J = I + MaxArgs;
-        unsigned long const size = MaxArgs + 10;
-        Args.reserve(size);
-        Packages.reserve(size);
-      }
-      else
-      {
-        unsigned long const size = (J - I) + 10;
-        Args.reserve(size);
-        Packages.reserve(size);
-      }
+      Args.reserve(size);
+      // keep track of allocated strings for multiarch package names
+      std::vector<char *> Packages(size, nullptr);
 
       int fd[2];
       if (pipe(fd) != 0)
@@ -1282,13 +1318,13 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
         ADDARGC("--force-remove-essential");
         ADDARGC("--remove");
         break;
-        
+
         case Item::Purge:
         ADDARGC("--force-depends");
         ADDARGC("--force-remove-essential");
         ADDARGC("--purge");
         break;
-        
+
         case Item::Configure:
         ADDARGC("--configure");
         break;
@@ -1375,13 +1411,19 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
 #undef ADDARG
 
       J = I;
-      
-      if (_config->FindB("Debug::pkgDPkgPM",false) == true)
+
+      if (noopDPkgInvocation == true)
       {
         for (std::vector<const char *>::const_iterator a = Args.begin();
              a != Args.end(); ++a)
            clog << *a << ' ';
         clog << endl;
+        for (std::vector<char *>::const_iterator p = Packages.begin();
+              p != Packages.end(); ++p)
+           free(*p);
+        Packages.clear();
+        close(fd[0]);
+        close(fd[1]);
         continue;
       }
       Args.push_back(NULL);
@@ -1390,18 +1432,17 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
       clog << flush;
       cerr << flush;
 
-      /* Mask off sig int/quit. We do this because dpkg also does when 
+      /* Mask off sig int/quit. We do this because dpkg also does when
          it forks scripts. What happens is that when you hit ctrl-c it sends
-        it to all processes in the group. Since dpkg ignores the signal 
+        it to all processes in the group. Since dpkg ignores the signal
         it doesn't die but we do! So we must also ignore it */
       sighandler_t old_SIGQUIT = signal(SIGQUIT,SIG_IGN);
       sighandler_t old_SIGINT = signal(SIGINT,SigINT);
-      
+
       // Check here for any SIGINT
-      if (pkgPackageManager::SigINTStop && (Op == Item::Remove || Op == Item::Purge || Op == Item::Install)) 
+      if (pkgPackageManager::SigINTStop && (Op == Item::Remove || Op == Item::Purge || Op == Item::Install))
          break;
-      
-      
+
       // ignore SIGHUP as well (debian #463030)
       sighandler_t old_SIGHUP = signal(SIGHUP,SIG_IGN);
 
@@ -1413,25 +1454,11 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
       pid_t Child = ExecFork(KeepFDs);
       if (Child == 0)
       {
-         // This is the child
-        if(d->slave >= 0 && d->master >= 0) 
-        {
-           setsid();
-           int res = ioctl(d->slave, TIOCSCTTY, 0);
-            if (res < 0) {
-               std::cerr << "ioctl(TIOCSCTTY) failed for fd: " 
-                         << d->slave << std::endl;
-            } else {
-               close(d->master);
-               dup2(d->slave, 0);
-               dup2(d->slave, 1);
-               dup2(d->slave, 2);
-               close(d->slave);
-            }
-        }
+        // This is the child
+        SetupSlavePtyMagic();
         close(fd[0]); // close the read end of the pipe
 
-        dpkgChrootDirectory();
+        debSystem::DpkgChrootDirectory();
 
         if (chdir(_config->FindDir("DPkg::Run-Directory","/").c_str()) != 0)
            _exit(100);
@@ -1442,36 +1469,37 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
             int dummy = 0;
            if ((Flags = fcntl(STDIN_FILENO,F_GETFL,dummy)) < 0)
               _exit(100);
-           
+
            // Discard everything in stdin before forking dpkg
            if (fcntl(STDIN_FILENO,F_SETFL,Flags | O_NONBLOCK) < 0)
               _exit(100);
-           
+
            while (read(STDIN_FILENO,&dummy,1) == 1);
-           
+
            if (fcntl(STDIN_FILENO,F_SETFL,Flags & (~(long)O_NONBLOCK)) < 0)
               _exit(100);
         }
 
-        /* No Job Control Stop Env is a magic dpkg var that prevents it
-           from using sigstop */
-        putenv((char *)"DPKG_NO_TSTP=yes");
+        // if color support isn't enabled/disabled explicitly tell
+        // dpkg to use the same state apt is using for its color support
+        if (_config->FindB("APT::Color", false) == true)
+           setenv("DPKG_COLORS", "always", 0);
+        else
+           setenv("DPKG_COLORS", "never", 0);
+
         execvp(Args[0], (char**) &Args[0]);
         cerr << "Could not exec dpkg!" << endl;
         _exit(100);
-      }      
-
-      // apply ionice
-      if (_config->FindB("DPkg::UseIoNice", false) == true)
-        ionice(Child);
-
-      // Wait for dpkg
-      int Status = 0;
+      }
 
       // we read from dpkg here
       int const _dpkgin = fd[0];
       close(fd[1]);                        // close the write end of the pipe
 
+      // apply ionice
+      if (_config->FindB("DPkg::UseIoNice", false) == true)
+        ionice(Child);
+
       // setups fds
       sigemptyset(&d->sigmask);
       sigprocmask(SIG_BLOCK,&d->sigmask,&d->original_sigmask);
@@ -1483,49 +1511,40 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
       Packages.clear();
 
       // the result of the waitpid call
+      int Status = 0;
       int res;
-      int select_ret;
+      bool waitpid_failure = false;
       while ((res=waitpid(Child,&Status, WNOHANG)) != Child) {
         if(res < 0) {
-           // FIXME: move this to a function or something, looks ugly here
            // error handling, waitpid returned -1
            if (errno == EINTR)
               continue;
-           RunScripts("DPkg::Post-Invoke");
-
-           // Restore sig int/quit
-           signal(SIGQUIT,old_SIGQUIT);
-           signal(SIGINT,old_SIGINT);
-
-           signal(SIGHUP,old_SIGHUP);
-           return _error->Errno("waitpid","Couldn't wait for subprocess");
+           waitpid_failure = true;
+           break;
         }
 
         // wait for input or output here
         FD_ZERO(&rfds);
-        if (d->master >= 0 && !d->stdin_is_dev_null)
-           FD_SET(0, &rfds); 
+        if (d->master >= 0 && d->direct_stdin == false && d->stdin_is_dev_null == false)
+           FD_SET(STDIN_FILENO, &rfds);
         FD_SET(_dpkgin, &rfds);
         if(d->master >= 0)
            FD_SET(d->master, &rfds);
          tv.tv_sec = 0;
          tv.tv_nsec = d->progress->GetPulseInterval();
-        select_ret = pselect(max(d->master, _dpkgin)+1, &rfds, NULL, NULL, 
+        auto const select_ret = pselect(max(d->master, _dpkgin)+1, &rfds, NULL, NULL,
                              &tv, &d->original_sigmask);
-        if (select_ret < 0 && (errno == EINVAL || errno == ENOSYS))
-           select_ret = racy_pselect(max(d->master, _dpkgin)+1, &rfds, NULL,
-                                     NULL, &tv, &d->original_sigmask);
          d->progress->Pulse();
-        if (select_ret == 0) 
-           continue;
-        else if (select_ret < 0 && errno == EINTR)
-           continue;
-        else if (select_ret < 0) 
-        {
-           perror("select() returned error");
-           continue;
-        } 
-        
+        if (select_ret == 0)
+           continue;
+        else if (select_ret < 0 && errno == EINTR)
+           continue;
+        else if (select_ret < 0)
+        {
+           perror("select() returned error");
+           continue;
+        }
+
         if(d->master >= 0 && FD_ISSET(d->master, &rfds))
            DoTerminalPty(d->master);
         if(d->master >= 0 && FD_ISSET(0, &rfds))
@@ -1538,12 +1557,19 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
       // Restore sig int/quit
       signal(SIGQUIT,old_SIGQUIT);
       signal(SIGINT,old_SIGINT);
-      
       signal(SIGHUP,old_SIGHUP);
+
+      if (waitpid_failure == true)
+      {
+        strprintf(d->dpkg_error, "Sub-process %s couldn't be waited for.",Args[0]);
+        _error->Error("%s", d->dpkg_error.c_str());
+        break;
+      }
+
       // Check for an error code.
       if (WIFEXITED(Status) == 0 || WEXITSTATUS(Status) != 0)
       {
-        // if it was set to "keep-dpkg-runing" then we won't return
+        // if it was set to "keep-dpkg-running" then we won't return
         // here but keep the loop going and just report it as a error
         // for later
         bool const stopOnError = _config->FindB("Dpkg::StopOnError",true);
@@ -1561,21 +1587,17 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
       }
    }
    // dpkg is done at this point
-   d->progress->Stop();
    StopPtyMagic();
    CloseLog();
 
    if (pkgPackageManager::SigINTStop)
        _error->Warning(_("Operation was interrupted before it could finish"));
 
-   if (RunScripts("DPkg::Post-Invoke") == false)
-      return false;
-
-   if (_config->FindB("Debug::pkgDPkgPM",false) == false)
+   if (noopDPkgInvocation == false)
    {
       std::string const oldpkgcache = _config->FindFile("Dir::cache::pkgcache");
       if (oldpkgcache.empty() == false && RealFileExists(oldpkgcache) == true &&
-         unlink(oldpkgcache.c_str()) == 0)
+         RemoveFile("pkgDPkgPM::Go", oldpkgcache))
       {
         std::string const srcpkgcache = _config->FindFile("Dir::cache::srcpkgcache");
         if (srcpkgcache.empty() == false && RealFileExists(srcpkgcache) == true)
@@ -1588,7 +1610,15 @@ bool pkgDPkgPM::GoNoABIBreak(APT::Progress::PackageManager *progress)
       }
    }
 
-   Cache.writeStateFile(NULL);
+   // disappearing packages can forward their auto-bit
+   if (disappearedPkgs.empty() == false)
+      Cache.writeStateFile(NULL);
+
+   d->progress->Stop();
+
+   if (RunScripts("DPkg::Post-Invoke") == false)
+      return false;
+
    return d->dpkg_error.empty();
 }
 
@@ -1599,7 +1629,7 @@ void SigINT(int /*sig*/) {
 // pkgDpkgPM::Reset - Dump the contents of the command list            /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-void pkgDPkgPM::Reset() 
+void pkgDPkgPM::Reset()
 {
    List.erase(List.begin(),List.end());
 }
@@ -1607,7 +1637,7 @@ void pkgDPkgPM::Reset()
 // pkgDpkgPM::WriteApportReport - write out error report pkg failure   /*{{{*/
 // ---------------------------------------------------------------------
 /* */
-void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg) 
+void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
 {
    // If apport doesn't exist or isn't installed do nothing
    // This e.g. prevents messages in 'universes' without apport
@@ -1615,7 +1645,7 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
    if (apportPkg.end() == true || apportPkg->CurrentVer == 0)
       return;
 
-   string pkgname, reportfile, srcpkgname, pkgver, arch;
+   string pkgname, reportfile, pkgver, arch;
    string::size_type pos;
    FILE *report;
 
@@ -1632,20 +1662,20 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
       return;
    }
 
-   // check if its not a follow up error 
+   // check if its not a follow up error
    const char *needle = dgettext("dpkg", "dependency problems - leaving unconfigured");
    if(strstr(errormsg, needle) != NULL) {
       std::clog << _("No apport report written because the error message indicates its a followup error from a previous failure.") << std::endl;
       return;
    }
 
-   // do not report disk-full failures 
+   // do not report disk-full failures
    if(strstr(errormsg, strerror(ENOSPC)) != NULL) {
       std::clog << _("No apport report written because the error message indicates a disk full error") << std::endl;
       return;
    }
 
-   // do not report out-of-memory failures 
+   // do not report out-of-memory failures
    if(strstr(errormsg, strerror(ENOMEM)) != NULL ||
       strstr(errormsg, "failed to allocate memory") != NULL) {
       std::clog << _("No apport report written because the error message indicates a out of memory error") << std::endl;
@@ -1668,9 +1698,10 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
    // do not report dpkg I/O errors, this is a format string, so we compare
    // the prefix and the suffix of the error with the dpkg error message
    vector<string> io_errors;
-   io_errors.push_back(string("failed to read on buffer copy for %s"));
-   io_errors.push_back(string("failed in write on buffer copy for %s"));
-   io_errors.push_back(string("short read on buffer copy for %s"));
+   io_errors.push_back(string("failed to read"));
+   io_errors.push_back(string("failed to write"));
+   io_errors.push_back(string("failed to seek"));
+   io_errors.push_back(string("unexpected end of file or stream"));
 
    for (vector<string>::iterator I = io_errors.begin(); I != io_errors.end(); ++I)
    {
@@ -1680,7 +1711,7 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
          // to kill the "s" manually
          if (list[1].size() > 1) {
             list[1].erase(0, 1);
-            if(strstr(errormsg, list[0].c_str()) && 
+            if(strstr(errormsg, list[0].c_str()) &&
                strstr(errormsg, list[1].c_str())) {
                std::clog << _("No apport report written because the error message indicates a dpkg I/O error") << std::endl;
                return;
@@ -1695,19 +1726,14 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
    if(pos != string::npos)
       pkgname = pkgname.substr(0, pos);
 
-   // find the package versin and source package name
+   // find the package version and source package name
    pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname);
    if (Pkg.end() == true)
       return;
-   pkgCache::VerIterator Ver = Cache.GetCandidateVer(Pkg);
+   pkgCache::VerIterator Ver = Cache.GetCandidateVersion(Pkg);
    if (Ver.end() == true)
       return;
    pkgver = Ver.VerStr() == NULL ? "unknown" : Ver.VerStr();
-   pkgRecords Recs(Cache);
-   pkgRecords::Parser &Parse = Recs.Lookup(Ver.FileList());
-   srcpkgname = Parse.SourcePkg();
-   if(srcpkgname.empty())
-      srcpkgname = pkgname;
 
    // if the file exists already, we check:
    // - if it was reported already (touched by apport). 
@@ -1756,9 +1782,10 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
    fprintf(report, "ProblemType: Package\n");
    fprintf(report, "Architecture: %s\n", arch.c_str());
    time_t now = time(NULL);
-   fprintf(report, "Date: %s" , ctime(&now));
+   char ctime_buf[26]; // need at least 26 bytes according to ctime(3)
+   fprintf(report, "Date: %s" , ctime_r(&now, ctime_buf));
    fprintf(report, "Package: %s %s\n", pkgname.c_str(), pkgver.c_str());
-   fprintf(report, "SourcePackage: %s\n", srcpkgname.c_str());
+   fprintf(report, "SourcePackage: %s\n", Ver.SourcePkgName());
    fprintf(report, "ErrorMessage:\n %s\n", errormsg);
 
    // ensure that the log is flushed
@@ -1798,8 +1825,15 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
       }
    }
 
-   // log the ordering 
-   const char *ops_str[] = {"Install", "Configure","Remove","Purge"};
+   // log the ordering, see dpkgpm.h and the "Ops" enum there
+   const char *ops_str[] = {
+      "Install",
+      "Configure",
+      "Remove",
+      "Purge",
+      "ConfigurePending",
+      "TriggersPending",
+   };
    fprintf(report, "AptOrdering:\n");
    for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
       if ((*I).Pkg != NULL)