[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#592575: `yes '' | apt-get dist-upgrade' hangs



Package: apt
Version: 0.7.20.2+lenny2
Severity: normal
Tags: patch


#apt-get -s dist-upgrade
Reading package lists... Done
Building dependency tree       
Reading state information... Done
Calculating upgrade... Done
The following packages have been kept back:
  acroread-debian-files fvwm gdk-imlib11 gtk-engines-lighthouseblue libgtk1.2
  lvm2
The following packages will be upgraded:
  gzip
1 upgraded, 0 newly installed, 0 to remove and 6 not upgraded.
Inst gzip [1.3.12-6+lenny] (1.3.12-6+lenny1 Debian:5.0.5/stable, Debian-Security:5.0/stable)
Conf gzip (1.3.12-6+lenny1 Debian:5.0.5/stable, Debian-Security:5.0/stable)

`yes '' | apt-get dist-upgrade' causes apt-get to block in write(2) because
nobody reads (or even has open) the pseudoterminal it writes to (since its
child, dpkg, is a zombie already).

Please see the patch for details. Thanks.

-- Package-specific info:

-- apt-config dump --

APT "";
APT::Architecture "i386";
APT::Build-Essential "";
APT::Build-Essential:: "build-essential";
APT::Install-Recommends "1";
APT::Install-Suggests "0";
APT::Acquire "";
APT::Acquire::Translation "environment";
APT::NeverAutoRemove "";
APT::NeverAutoRemove:: "^linux-image.*";
APT::NeverAutoRemove:: "^linux-restricted-modules.*";
APT::Cache-Limit "16777216";
Dir "/";
Dir::State "var/lib/apt/";
Dir::State::lists "lists/";
Dir::State::cdroms "cdroms.list";
Dir::State::userstatus "status.user";
Dir::State::status "/var/lib/dpkg/status";
Dir::Cache "var/cache/apt/";
Dir::Cache::archives "archives/";
Dir::Cache::srcpkgcache "srcpkgcache.bin";
Dir::Cache::pkgcache "pkgcache.bin";
Dir::Etc "etc/apt/";
Dir::Etc::sourcelist "sources.list";
Dir::Etc::sourceparts "sources.list.d";
Dir::Etc::vendorlist "vendors.list";
Dir::Etc::vendorparts "vendors.list.d";
Dir::Etc::main "apt.conf";
Dir::Etc::parts "apt.conf.d";
Dir::Etc::preferences "preferences";
Dir::Bin "";
Dir::Bin::methods "/usr/lib/apt/methods";
Dir::Bin::dpkg "/usr/bin/dpkg";
Dir::Log "var/log/apt";
Dir::Log::Terminal "term.log";
DPkg "";
DPkg::Pre-Install-Pkgs "";
DPkg::Pre-Install-Pkgs:: "/usr/sbin/dpkg-preconfigure --apt || true";
DPkg::Post-Invoke "";
DPkg::Post-Invoke:: "if [ -x /usr/bin/debsums ]; then /usr/bin/debsums --generate=nocheck -sp /var/cache/apt/archives; fi";
DPkg::Post-Invoke:: "test -f /var/run/junior-config.usermenu && if [ -x /usr/sbin/cdd-update-usermenus ] ; then /usr/sbin/cdd-update-usermenus junior ; fi ; rm -f /var/run/junior-config.usermenu";

-- /etc/apt/preferences --

Package: *
Pin: release a=stable
Pin-Priority: 600

Package: *
Pin: release a=oldstable
Pin-Priority: 50

-- (/etc/apt/sources.list present, but not submitted) --


-- System Information:
Debian Release: 5.0.5
  APT prefers stable
  APT policy: (600, 'stable')
Architecture: i386 (i686)

Kernel: Linux 2.6.26-1-686 (SMP w/1 CPU core)
Locale: LANG=ru_RU.UTF-8, LC_CTYPE=ru_RU.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/bash

Versions of packages apt depends on:
ii  debian-archive-keyring      2009.01.31   GnuPG archive keys of the Debian a
ii  libc6                       2.7-18lenny4 GNU C Library: Shared libraries
ii  libgcc1                     1:4.3.2-1.1  GCC support library
ii  libstdc++6                  4.3.2-1.1    The GNU Standard C++ Library v3

apt recommends no packages.

Versions of packages apt suggests:
pn  apt-doc               <none>             (no description available)
ii  aptitude              0.4.11.11-1~lenny1 terminal-based package manager
ii  bzip2                 1.0.5-1            high-quality block-sorting file co
ii  dpkg-dev              1.14.29            Debian package development tools
ii  lzma                  4.43-14            Compression method of 7z format in
ii  python-apt            0.7.7.1+nmu1       Python interface to libapt-pkg

-- no debconf information
`yes '' | apt-get dist-upgrade' causes apt-get to block in write because nobody
reads (or even has open) the pseudoterminal it writes to (since its child, dpkg,
is a zombie already).

Also, this patch fixes the follwing problems:
  * If write(2) in DoStdin writes not the whole input_buf (or interrupted), the
    rest of input_buf would be lost.
  * If read(2) in DoStdin fails, write(2) would be called with a negative
    `count' argument.

--
/Dmitry Maksyoma <dmaksema@allot.com>

diff -ru apt-0.7.20.2+lenny2/apt-pkg/deb/dpkgpm.cc /home/dmaks/devel/6.0/apt/apt-0.7.20.2+lenny2/apt-pkg/deb/dpkgpm.cc
--- apt-0.7.20.2+lenny2/apt-pkg/deb/dpkgpm.cc	2009-02-08 04:09:35.000000000 +1300
+++ /home/dmaks/devel/6.0/apt/apt-0.7.20.2+lenny2/apt-pkg/deb/dpkgpm.cc	2010-08-11 16:56:03.000000000 +1200
@@ -81,6 +81,7 @@
    : pkgPackageManager(Cache), dpkgbuf_pos(0),
      term_out(NULL), PackagesDone(0), PackagesTotal(0)
 {
+   stdin_buf_start = stdin_buf_end = 0;
 }
 									/*}}}*/
 // DPkgPM::pkgDPkgPM - Destructor					/*{{{*/
@@ -314,12 +315,39 @@
 */
 void pkgDPkgPM::DoStdin(int master)
 {
-   unsigned char input_buf[256] = {0,}; 
-   ssize_t len = read(0, input_buf, sizeof(input_buf));
-   if (len)
-      write(master, input_buf, len);
-   else
-      stdin_is_dev_null = true;
+   ssize_t to_write_len = stdin_buf_end - stdin_buf_start;
+   
+   // Read from stdin only if there's nothing in the stdin_buf.
+   if (to_write_len == 0) {
+      ssize_t len;
+      while ((len = read(0, stdin_buf, sizeof(stdin_buf))) == EINTR);
+      if (len <= 0) {
+	 // Stop reading from stdin when end of file was reached or there are
+	 // problems with rd.
+	 stdin_is_dev_null = true;
+	 return;
+      }
+
+      to_write_len = len;
+      stdin_buf_start = 0;
+      stdin_buf_end = len;
+   }
+
+   if (master != -1 && to_write_len) {
+      ssize_t wrote_len;
+      while ((wrote_len =
+	 write(master, &stdin_buf[stdin_buf_start], to_write_len)) == EINTR);
+
+      if (wrote_len < 0) {
+	 perror("write");
+	 return;
+      }
+
+      if (wrote_len == to_write_len)
+	 stdin_buf_start = stdin_buf_end = 0;
+      else
+	 stdin_buf_start += wrote_len;
+   }
 }
 									/*}}}*/
 // DPkgPM::DoTerminalPty - Read the terminal pty and write log		/*{{{*/
@@ -866,6 +894,7 @@
 
       // setups fds
       fd_set rfds;
+      fd_set wfds;
       struct timespec tv;
       sigset_t sigmask;
       sigset_t original_sigmask;
@@ -890,17 +919,20 @@
 
 	 // wait for input or output here
 	 FD_ZERO(&rfds);
-	 if (!stdin_is_dev_null)
+	 if (!stdin_is_dev_null && stdin_buf_end == 0)
 	    FD_SET(0, &rfds); 
 	 FD_SET(_dpkgin, &rfds);
 	 if(master >= 0)
 	    FD_SET(master, &rfds);
+	 FD_ZERO(&wfds);
+	 if(master >= 0)
+	    FD_SET(master, &wfds);
 	 tv.tv_sec = 1;
 	 tv.tv_nsec = 0;
-	 select_ret = pselect(max(master, _dpkgin)+1, &rfds, NULL, NULL, 
+	 select_ret = pselect(max(master, _dpkgin)+1, &rfds, &wfds, NULL, 
 			      &tv, &original_sigmask);
 	 if (select_ret < 0 && (errno == EINVAL || errno == ENOSYS))
-	    select_ret = racy_pselect(max(master, _dpkgin)+1, &rfds, NULL,
+	    select_ret = racy_pselect(max(master, _dpkgin)+1, &rfds, &wfds,
 				      NULL, &tv, &original_sigmask);
 	 if (select_ret == 0) 
   	    continue;
@@ -914,8 +946,8 @@
 	 
 	 if(master >= 0 && FD_ISSET(master, &rfds))
 	    DoTerminalPty(master);
-	 if(master >= 0 && FD_ISSET(0, &rfds))
-	    DoStdin(master);
+	 if(master >= 0 && (stdin_buf_end > 0 || FD_ISSET(0, &rfds)))
+	    DoStdin(FD_ISSET(master, &wfds) ? master : -1);
 	 if(FD_ISSET(_dpkgin, &rfds))
 	    DoDpkgStatusFd(_dpkgin, OutStatusFd);
       }
diff -ru apt-0.7.20.2+lenny2/apt-pkg/deb/dpkgpm.h /home/dmaks/devel/6.0/apt/apt-0.7.20.2+lenny2/apt-pkg/deb/dpkgpm.h
--- apt-0.7.20.2+lenny2/apt-pkg/deb/dpkgpm.h	2009-02-08 04:09:35.000000000 +1300
+++ /home/dmaks/devel/6.0/apt/apt-0.7.20.2+lenny2/apt-pkg/deb/dpkgpm.h	2010-08-11 12:53:48.000000000 +1200
@@ -29,6 +29,11 @@
    char dpkgbuf[1024];
    int dpkgbuf_pos;
    FILE *term_out;
+
+   // the buffer for reading stdin
+   unsigned char stdin_buf[256]; 
+   unsigned int stdin_buf_start;
+   unsigned int stdin_buf_end;
    
    protected:
 

Reply to: