]> git.saurik.com Git - apt.git/blobdiff - apt-pkg/deb/dpkgpm.cc
Merge branch 'debian/jessie' into debian/experimental
[apt.git] / apt-pkg / deb / dpkgpm.cc
index 95fae9a28cf312f9e67ec1b15471e064078267c4..a7a66c75d76273762ca2eb3d9e993403486b8d6d 100644 (file)
 
 using namespace std;
 
+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(NULL)
+                       progress(NULL), tt_is_valid(false), master(-1),
+                       slave(NULL), protect_slave_from_dying(-1),
+                       direct_stdin(false)
    {
       dpkgbuf[0] = '\0';
    }
@@ -78,13 +92,16 @@ public:
 
    // pty stuff
    struct termios tt;
+   bool tt_is_valid;
    int master;
    char * slave;
+   int protect_slave_from_dying;
 
    // signals
    sigset_t sigmask;
    sigset_t original_sigmask;
 
+   bool direct_stdin;
 };
 
 namespace
@@ -186,18 +203,10 @@ 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;
 }
                                                                        /*}}}*/
@@ -1032,6 +1041,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)
@@ -1056,48 +1071,44 @@ void pkgDPkgPM::StartPtyMagic()
       return;
    }
 
+   if (isatty(STDIN_FILENO) == 0)
+      d->direct_stdin = true;
+
    _error->PushToStack();
-   // if tcgetattr for both stdin/stdout returns 0 (no error)
-   // we do the pty magic
-   if (tcgetattr(STDOUT_FILENO, &d->tt) == 0 &&
-        tcgetattr(STDIN_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
    {
-      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);
-        close(d->master);
-        d->master = -1;
-      }
+      char const * const slave_name = ptsname(d->master);
+      if (slave_name == NULL)
+        _error->Errno("ptsname", "Getting name for slave of master fd %d failed!", d->master);
       else
       {
-        char const * const slave_name = ptsname(d->master);
-        if (slave_name == NULL)
-        {
-           _error->Errno("unlockpt", "Getting name for slave of master fd %d failed!", d->master);
-           close(d->master);
-           d->master = -1;
-        }
-        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->slave = strdup(slave_name);
-           if (d->slave == NULL)
+           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)
            {
-              _error->Errno("strdup", "Copying name %s for slave of master fd %d failed!", slave_name, d->master);
-              close(d->master);
-              d->master = -1;
+              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);
            }
-           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);
 
-           struct termios raw_tt;
            raw_tt = d->tt;
            cfmakeraw(&raw_tt);
            raw_tt.c_lflag &= ~ECHO;
@@ -1109,18 +1120,22 @@ void pkgDPkgPM::StartPtyMagic()
            sigaddset(&d->sigmask, SIGTTOU);
            sigprocmask(SIG_BLOCK,&d->sigmask, &d->original_sigmask);
            if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw_tt) == -1)
-              _error->Errno("tcsetattr", "Setting in Start via TCSAFLUSH for stdout failed!");
+              _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);
         }
       }
    }
-   else
-   {
-      // 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.
-      if (isatty(STDOUT_FILENO) == 1 || errno == EBADF)
-        _error->Errno("tcgetattr", _("Can not write log (%s)"), _("Is stdout a terminal?"));
-   }
 
    if (_error->PendingError() == true)
    {
@@ -1129,44 +1144,60 @@ void pkgDPkgPM::StartPtyMagic()
         close(d->master);
         d->master = -1;
       }
+      if (d->slave != NULL)
+      {
+        free(d->slave);
+        d->slave = NULL;
+      }
       _error->DumpErrors(std::cerr);
    }
    _error->RevertToStack();
 }
 void pkgDPkgPM::SetupSlavePtyMagic()
 {
-   if(d->master == -1)
+   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);
+   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?"));
-
-   if (ioctl(slaveFd, TIOCSCTTY, 0) < 0)
+   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);
 
-      if (tcsetattr(0, TCSANOW, &d->tt) < 0)
+      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);
    }
+
+   if (slaveFd != -1)
+      close(slaveFd);
 }
 void pkgDPkgPM::StopPtyMagic()
 {
    if (d->slave != NULL)
       free(d->slave);
    d->slave = NULL;
+   if (d->protect_slave_from_dying != -1)
+   {
+      close(d->protect_slave_from_dying);
+      d->protect_slave_from_dying = -1;
+   }
    if(d->master >= 0) 
    {
-      if (tcsetattr(0, TCSAFLUSH, &d->tt) == -1)
+      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;
@@ -1230,8 +1261,15 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
    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);
+   // FIXME: do we really need this limit when we have MaxArgBytes?
+   unsigned int const MaxArgs = _config->FindI("Dpkg::MaxArgs",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)
@@ -1242,9 +1280,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
@@ -1553,8 +1590,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);
@@ -1665,7 +1702,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;
 
@@ -1804,7 +1841,16 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
    time_t now = time(NULL);
    fprintf(report, "Date: %s" , ctime(&now));
    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
@@ -1844,8 +1890,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)