debian/control: Set Standards-Version to 3.9.7
[apt.git] / apt-pkg / deb / dpkgpm.cc
index 3fbda05522ef5dccf7dcc2691597a253ce5facc3..1afcb65273419a6ecc3f3cba57ceda14491fed07 100644 (file)
 #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 +27,6 @@
 #include <errno.h>
 #include <fcntl.h>
 #include <grp.h>
-#include <pty.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stddef.h>
 #include <string>
 #include <utility>
 #include <vector>
+#include <sstream>
+#include <numeric>
 
 #include <apti18n.h>
                                                                        /*}}}*/
 
 using namespace std;
 
+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()
 {
@@ -73,7 +99,8 @@ public:
    pkgDPkgPMPrivate() : stdin_is_dev_null(false), dpkgbuf_pos(0),
                        term_out(NULL), history_out(NULL),
                        progress(NULL), tt_is_valid(false), master(-1),
-                       slave(NULL), protect_slave_from_dying(-1)
+                       slave(NULL), protect_slave_from_dying(-1),
+                       direct_stdin(false)
    {
       dpkgbuf[0] = '\0';
    }
@@ -83,7 +110,7 @@ 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;
@@ -100,6 +127,7 @@ public:
    sigset_t sigmask;
    sigset_t original_sigmask;
 
+   bool direct_stdin;
 };
 
 namespace
@@ -125,7 +153,7 @@ namespace
     const char *target;
 
   public:
-    MatchProcessingOp(const char *the_target)
+    explicit MatchProcessingOp(const char *the_target)
       : target(the_target)
     {
     }
@@ -162,35 +190,6 @@ 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 
@@ -203,8 +202,10 @@ pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg)
    for (Ver = Pkg.VersionList(); Ver.end() == false; ++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)
+        {
+           if (F.Archive() != 0 && strcmp(F.Archive(), "now") == 0)
               return Ver;
+        }
    return Ver;
 }
                                                                        /*}}}*/
@@ -212,10 +213,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                                      /*{{{*/
@@ -465,7 +465,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";
@@ -781,40 +781,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                                             /*{{{*/
@@ -839,7 +832,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
@@ -909,6 +903,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);
@@ -926,7 +923,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)
@@ -1039,6 +1037,12 @@ void pkgDPkgPM::BuildPackagesProgressMap()
         PackagesTotal++;
       }
    }
+   /* 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;
 }
                                                                         /*}}}*/
 bool pkgDPkgPM::Go(int StatusFd)
@@ -1063,6 +1067,9 @@ void pkgDPkgPM::StartPtyMagic()
       return;
    }
 
+   if (isatty(STDIN_FILENO) == 0)
+      d->direct_stdin = true;
+
    _error->PushToStack();
 
    d->master = posix_openpt(O_RDWR | O_NOCTTY);
@@ -1072,8 +1079,13 @@ void pkgDPkgPM::StartPtyMagic()
       _error->Errno("unlockpt", "Unlocking the slave of master fd %d failed!", d->master);
    else
    {
+#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
       {
@@ -1121,7 +1133,7 @@ void pkgDPkgPM::StartPtyMagic()
               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);
+           d->protect_slave_from_dying = open(d->slave, O_RDWR | O_CLOEXEC | O_NOCTTY);
         }
       }
    }
@@ -1138,7 +1150,7 @@ void pkgDPkgPM::StartPtyMagic()
         free(d->slave);
         d->slave = NULL;
       }
-      _error->DumpErrors(std::cerr);
+      _error->DumpErrors(std::cerr, GlobalError::DEBUG, false);
    }
    _error->RevertToStack();
 }
@@ -1153,14 +1165,17 @@ void pkgDPkgPM::SetupSlavePtyMagic()
    if (setsid() == -1)
       _error->FatalE("setsid", "Starting a new session for child failed!");
 
-   int const slaveFd = open(d->slave, O_RDWR);
+   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
    {
-      for (unsigned short i = 0; i < 3; ++i)
+      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);
 
@@ -1205,44 +1220,13 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
    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;
@@ -1266,9 +1250,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 
    // 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)
+   if (_config->FindB("DPkg::ConfigurePending", true) == true)
       List.push_back(Item(Item::ConfigurePending, PkgIterator()));
 
    // for the progress
@@ -1279,20 +1262,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
    // 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();
@@ -1470,6 +1440,10 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
              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();
         continue;
       }
       Args.push_back(NULL);
@@ -1505,7 +1479,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
         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);
@@ -1527,9 +1501,6 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
               _exit(100);
         }
 
-        /* No Job Control Stop Env is a magic dpkg var that prevents it
-           from using sigstop */
-        putenv((char *)"DPKG_NO_TSTP=yes");
         execvp(Args[0], (char**) &Args[0]);
         cerr << "Could not exec dpkg!" << endl;
         _exit(100);
@@ -1577,8 +1548,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 
         // 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);
@@ -1649,7 +1620,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
    {
       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)
@@ -1770,11 +1741,11 @@ 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();
@@ -1826,18 +1797,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());
-#if APT_PKG_ABI >= 413
    fprintf(report, "SourcePackage: %s\n", Ver.SourcePkgName());
-#else
-   pkgRecords Recs(Cache);
-   pkgRecords::Parser &Parse = Recs.Lookup(Ver.FileList());
-   std::string srcpkgname = Parse.SourcePkg();
-   if(srcpkgname.empty())
-      srcpkgname = pkgname;
-   fprintf(report, "SourcePackage: %s\n", srcpkgname.c_str());
-#endif
    fprintf(report, "ErrorMessage:\n %s\n", errormsg);
 
    // ensure that the log is flushed
@@ -1877,8 +1840,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)