Просмотр исходного кода

allow Pre-Install-Pkgs hooks to get info over an FD != stdin

This adds ::InfoFD option alongside the ::Version one to request sending
the information to the specified FD, by default it is STDIN as it was
the case before.

The environment variable APT_HOOK_INFO_FD contains the FD the data is on as
a confirmation that the APT version used understood the request.

Allowing the hook to choose the FD is needed/helpful e.g. for shellscripts
which have a hard time accessing FDs above 9 (as >= 10 are usually used
 internally by them)

Closes: #671728
David Kalnischkies лет назад: 13
Родитель
Сommit
48498443e7
3 измененных файлов с 81 добавлено и 46 удалено
  1. 15 5
      apt-pkg/deb/dpkgpm.cc
  2. 11 2
      doc/apt.conf.5.xml
  3. 55 39
      test/integration/test-bug-712116-dpkg-pre-install-pkgs-hook-multiarch

+ 15 - 5
apt-pkg/deb/dpkgpm.cc

@@ -382,24 +382,32 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
       OptSec = "DPkg::Tools::Options::" + string(Opts->Value.c_str(),Pos);
       OptSec = "DPkg::Tools::Options::" + string(Opts->Value.c_str(),Pos);
       
       
       unsigned int Version = _config->FindI(OptSec+"::Version",1);
       unsigned int Version = _config->FindI(OptSec+"::Version",1);
+      unsigned int InfoFD = _config->FindI(OptSec + "::InfoFD", STDIN_FILENO);
       
       
       // Create the pipes
       // Create the pipes
       int Pipes[2];
       int Pipes[2];
       if (pipe(Pipes) != 0)
       if (pipe(Pipes) != 0)
 	 return _error->Errno("pipe","Failed to create IPC pipe to subprocess");
 	 return _error->Errno("pipe","Failed to create IPC pipe to subprocess");
-      SetCloseExec(Pipes[0],true);
+      if (InfoFD != (unsigned)Pipes[0])
+	 SetCloseExec(Pipes[0],true);
+      else
+	 _config->Set("APT::Keep-Fds::", Pipes[0]);
       SetCloseExec(Pipes[1],true);
       SetCloseExec(Pipes[1],true);
-      
+
       // Purified Fork for running the script
       // Purified Fork for running the script
-      pid_t Process = ExecFork();      
+      pid_t Process = ExecFork();
       if (Process == 0)
       if (Process == 0)
       {
       {
 	 // Setup the FDs
 	 // Setup the FDs
-	 dup2(Pipes[0],STDIN_FILENO);
+	 dup2(Pipes[0], InfoFD);
 	 SetCloseExec(STDOUT_FILENO,false);
 	 SetCloseExec(STDOUT_FILENO,false);
-	 SetCloseExec(STDIN_FILENO,false);      
+	 SetCloseExec(STDIN_FILENO,false);
 	 SetCloseExec(STDERR_FILENO,false);
 	 SetCloseExec(STDERR_FILENO,false);
 
 
+	 string hookfd;
+	 strprintf(hookfd, "%d", InfoFD);
+	 setenv("APT_HOOK_INFO_FD", hookfd.c_str(), 1);
+
 	 dpkgChrootDirectory();
 	 dpkgChrootDirectory();
 	 const char *Args[4];
 	 const char *Args[4];
 	 Args[0] = "/bin/sh";
 	 Args[0] = "/bin/sh";
@@ -409,6 +417,8 @@ bool pkgDPkgPM::RunScriptsWithPkgs(const char *Cnf)
 	 execv(Args[0],(char **)Args);
 	 execv(Args[0],(char **)Args);
 	 _exit(100);
 	 _exit(100);
       }
       }
+      if (InfoFD == (unsigned)Pipes[0])
+	 _config->Clear("APT::Keep-Fds", Pipes[0]);
       close(Pipes[0]);
       close(Pipes[0]);
       FILE *F = fdopen(Pipes[1],"w");
       FILE *F = fdopen(Pipes[1],"w");
       if (F == 0)
       if (F == 0)

+ 11 - 2
doc/apt.conf.5.xml

@@ -688,7 +688,8 @@ DPkg::Pre-Install-Pkgs {"/usr/sbin/dpkg-preconfigure --apt";};
      <literal>options</literal> this must be specified in list notation. The commands
      <literal>options</literal> this must be specified in list notation. The commands
      are invoked in order using <filename>/bin/sh</filename>; should any fail APT 
      are invoked in order using <filename>/bin/sh</filename>; should any fail APT 
      will abort. APT will pass the filenames of all .deb files it is going to
      will abort. APT will pass the filenames of all .deb files it is going to
-     install to the commands, one per line on standard input.</para>
+     install to the commands, one per line on the requested file descriptor, defaulting
+     to standard input.</para>
 
 
      <para>Version 2 of this protocol dumps more information, including the
      <para>Version 2 of this protocol dumps more information, including the
      protocol version, the APT configuration space and the packages, files
      protocol version, the APT configuration space and the packages, files
@@ -700,7 +701,15 @@ DPkg::Pre-Install-Pkgs {"/usr/sbin/dpkg-preconfigure --apt";};
      <literal>DPkg::Tools::options::<replaceable>cmd</replaceable>::Version</literal>
      <literal>DPkg::Tools::options::<replaceable>cmd</replaceable>::Version</literal>
      accordingly, the default being version 1. If APT isn't supporting the requested
      accordingly, the default being version 1. If APT isn't supporting the requested
      version it will send the information in the highest version it has support for instead.
      version it will send the information in the highest version it has support for instead.
-     </para></listitem>
+     </para>
+
+     <para>The file descriptor to be used to send the information can be requested with
+     <literal>DPkg::Tools::options::<replaceable>cmd</replaceable>::InfoFD</literal>
+     which defaults to <literal>0</literal> for standard input and is available since
+     version 0.9.11. Support for the option can be detected by looking for the environment
+     variable <envar>APT_HOOK_INFO_FD</envar> which contains the number of the used
+     file descriptor as a confirmation.</para>
+     </listitem>
      </varlistentry>
      </varlistentry>
 
 
      <varlistentry><term><option>Run-Directory</option></term>
      <varlistentry><term><option>Run-Directory</option></term>

+ 55 - 39
test/integration/test-bug-712116-dpkg-pre-install-pkgs-hook-multiarch

@@ -26,95 +26,111 @@ hook='pre-install-pkgs'
 
 
 enablehookversion() {
 enablehookversion() {
 	echo "#!/bin/sh
 	echo "#!/bin/sh
-while read line; do
+FD=0
+echo -n > ${hook}-v${1}.list
+if [ -n \"${2}\" ]; then
+	FD=\$APT_HOOK_INFO_FD
+	if [ "\$FD" != \"${2}\" ]; then echo \"ERROR: Information is not on requested FD: \$FD != ${2}\" >> ${hook}-v${1}.list; fi
+fi
+while read </proc/\$\$/fd/\$FD line; do
 	if echo \"\$line\" | grep -Fq '**'; then
 	if echo \"\$line\" | grep -Fq '**'; then
 		echo \"\$line\"
 		echo \"\$line\"
 	fi
 	fi
-done > ${hook}-v${1}.list" > ${hook}-v${1}.sh
+done >> ${hook}-v${1}.list" > ${hook}-v${1}.sh
 	chmod +x ${hook}-v${1}.sh
 	chmod +x ${hook}-v${1}.sh
 	echo "dpkg::${hook}:: \"./${hook}-v${1}.sh --foo -bar\";
 	echo "dpkg::${hook}:: \"./${hook}-v${1}.sh --foo -bar\";
 DPkg::Tools::options::\"./${hook}-v${1}.sh\"::Version \"$1\";" > rootdir/etc/apt/apt.conf.d/hook-v$1
 DPkg::Tools::options::\"./${hook}-v${1}.sh\"::Version \"$1\";" > rootdir/etc/apt/apt.conf.d/hook-v$1
+	if [ -n "$2" ]; then
+		echo "DPkg::Tools::options::\"./${hook}-v${1}.sh\"::InfoFD \"${2}\";" >> rootdir/etc/apt/apt.conf.d/hook-v$1
+	fi
 }
 }
 
 
-enablehookversion 2
-enablehookversion 3
-
 observehook() {
 observehook() {
 	rm -f ${hook}-v2.list ${hook}-v3.list
 	rm -f ${hook}-v2.list ${hook}-v3.list
 	msgtest 'Observe hooks while' "$*"
 	msgtest 'Observe hooks while' "$*"
 	testsuccess --nomsg aptget "$@" -y --force-yes
 	testsuccess --nomsg aptget "$@" -y --force-yes
 }
 }
 
 
-observehook install stuff -t stable
-testfileequal "${hook}-v2.list" 'libsame - < 1 **CONFIGURE**
+testrun() {
+	observehook install stuff -t stable
+	testfileequal "${hook}-v2.list" 'libsame - < 1 **CONFIGURE**
 toolkit - < 1 **CONFIGURE**
 toolkit - < 1 **CONFIGURE**
 stuff - < 1 **CONFIGURE**'
 stuff - < 1 **CONFIGURE**'
-testfileequal "${hook}-v3.list" 'libsame - - none < 1 amd64 same **CONFIGURE**
+	testfileequal "${hook}-v3.list" 'libsame - - none < 1 amd64 same **CONFIGURE**
 toolkit - - none < 1 all foreign **CONFIGURE**
 toolkit - - none < 1 all foreign **CONFIGURE**
 stuff - - none < 1 amd64 none **CONFIGURE**'
 stuff - - none < 1 amd64 none **CONFIGURE**'
 
 
-observehook install stuff -t unstable
-testfileequal "${hook}-v2.list" 'libsame 1 < 2 **CONFIGURE**
+	observehook install stuff -t unstable
+	testfileequal "${hook}-v2.list" 'libsame 1 < 2 **CONFIGURE**
 toolkit 1 < 2 **CONFIGURE**
 toolkit 1 < 2 **CONFIGURE**
 stuff 1 < 2 **CONFIGURE**'
 stuff 1 < 2 **CONFIGURE**'
-testfileequal "${hook}-v3.list" 'libsame 1 amd64 same < 2 amd64 same **CONFIGURE**
+	testfileequal "${hook}-v3.list" 'libsame 1 amd64 same < 2 amd64 same **CONFIGURE**
 toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE**
 toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE**
 stuff 1 amd64 none < 2 amd64 none **CONFIGURE**'
 stuff 1 amd64 none < 2 amd64 none **CONFIGURE**'
 
 
-observehook install stuff:i386 -t unstable
-testfileequal "${hook}-v2.list" 'stuff 2 > - **REMOVE**
+	observehook install stuff:i386 -t unstable
+	testfileequal "${hook}-v2.list" 'stuff 2 > - **REMOVE**
 libsame - < 2 **CONFIGURE**
 libsame - < 2 **CONFIGURE**
 stuff - < 2 **CONFIGURE**'
 stuff - < 2 **CONFIGURE**'
-testfileequal "${hook}-v3.list" 'stuff 2 amd64 none > - - none **REMOVE**
+	testfileequal "${hook}-v3.list" 'stuff 2 amd64 none > - - none **REMOVE**
 libsame - - none < 2 i386 same **CONFIGURE**
 libsame - - none < 2 i386 same **CONFIGURE**
 stuff - - none < 2 i386 none **CONFIGURE**'
 stuff - - none < 2 i386 none **CONFIGURE**'
 
 
-observehook remove libsame
-testfileequal "${hook}-v2.list" 'libsame 2 > - **REMOVE**'
-testfileequal "${hook}-v3.list" 'libsame 2 amd64 same > - - none **REMOVE**'
+	observehook remove libsame
+	testfileequal "${hook}-v2.list" 'libsame 2 > - **REMOVE**'
+	testfileequal "${hook}-v3.list" 'libsame 2 amd64 same > - - none **REMOVE**'
 
 
-observehook install stuff:i386/stable libsame:i386/stable toolkit/stable
-testfileequal "${hook}-v2.list" 'libsame 2 > 1 **CONFIGURE**
+	observehook install stuff:i386/stable libsame:i386/stable toolkit/stable
+	testfileequal "${hook}-v2.list" 'libsame 2 > 1 **CONFIGURE**
 toolkit 2 > 1 **CONFIGURE**
 toolkit 2 > 1 **CONFIGURE**
 stuff 2 > 1 **CONFIGURE**'
 stuff 2 > 1 **CONFIGURE**'
-testfileequal "${hook}-v3.list" 'libsame 2 i386 same > 1 i386 same **CONFIGURE**
+	testfileequal "${hook}-v3.list" 'libsame 2 i386 same > 1 i386 same **CONFIGURE**
 toolkit 2 amd64 foreign > 1 all foreign **CONFIGURE**
 toolkit 2 amd64 foreign > 1 all foreign **CONFIGURE**
 stuff 2 i386 none > 1 i386 none **CONFIGURE**'
 stuff 2 i386 none > 1 i386 none **CONFIGURE**'
 
 
-observehook install 'libsame:*'
-testfileequal "${hook}-v2.list" 'libsame 1 < 2 **CONFIGURE**
+	observehook install 'libsame:*'
+	testfileequal "${hook}-v2.list" 'libsame 1 < 2 **CONFIGURE**
 libsame - < 2 **CONFIGURE**
 libsame - < 2 **CONFIGURE**
 toolkit 1 < 2 **CONFIGURE**
 toolkit 1 < 2 **CONFIGURE**
 stuff 1 < 2 **CONFIGURE**'
 stuff 1 < 2 **CONFIGURE**'
-testfileequal "${hook}-v3.list" 'libsame 1 i386 same < 2 i386 same **CONFIGURE**
+	testfileequal "${hook}-v3.list" 'libsame 1 i386 same < 2 i386 same **CONFIGURE**
 libsame - - none < 2 amd64 same **CONFIGURE**
 libsame - - none < 2 amd64 same **CONFIGURE**
 toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE**
 toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE**
 stuff 1 i386 none < 2 i386 none **CONFIGURE**'
 stuff 1 i386 none < 2 i386 none **CONFIGURE**'
 
 
-observehook purge stuff:i386 'libsame:*' toolkit
-testfileequal "${hook}-v2.list" 'libsame 2 > - **REMOVE**
+	observehook purge stuff:i386 'libsame:*' toolkit
+	testfileequal "${hook}-v2.list" 'libsame 2 > - **REMOVE**
 stuff 2 > - **REMOVE**
 stuff 2 > - **REMOVE**
 libsame 2 > - **REMOVE**
 libsame 2 > - **REMOVE**
 toolkit 2 > - **REMOVE**'
 toolkit 2 > - **REMOVE**'
-testfileequal "${hook}-v3.list" 'libsame 2 amd64 same > - - none **REMOVE**
+	testfileequal "${hook}-v3.list" 'libsame 2 amd64 same > - - none **REMOVE**
 stuff 2 i386 none > - - none **REMOVE**
 stuff 2 i386 none > - - none **REMOVE**
 libsame 2 i386 same > - - none **REMOVE**
 libsame 2 i386 same > - - none **REMOVE**
 toolkit 2 amd64 foreign > - - none **REMOVE**'
 toolkit 2 amd64 foreign > - - none **REMOVE**'
 
 
-observehook install confpkg
-testfileequal "${hook}-v2.list" 'confpkg - < 1 **CONFIGURE**'
-testfileequal "${hook}-v3.list" 'confpkg - - none < 1 amd64 none **CONFIGURE**'
+	observehook install confpkg
+	testfileequal "${hook}-v2.list" 'confpkg - < 1 **CONFIGURE**'
+	testfileequal "${hook}-v3.list" 'confpkg - - none < 1 amd64 none **CONFIGURE**'
+
+	observehook remove confpkg
+	testfileequal "${hook}-v2.list" 'confpkg 1 > - **REMOVE**'
+	testfileequal "${hook}-v3.list" 'confpkg 1 amd64 none > - - none **REMOVE**'
 
 
-observehook remove confpkg
-testfileequal "${hook}-v2.list" 'confpkg 1 > - **REMOVE**'
-testfileequal "${hook}-v3.list" 'confpkg 1 amd64 none > - - none **REMOVE**'
+	msgtest 'Conffiles of package remained after remove' 'confpkg'
+	dpkg -l confpkg | grep -q '^rc' && msgpass || msgfail
 
 
-msgtest 'Conffiles of package remained after remove' 'confpkg'
-dpkg -l confpkg | grep -q '^rc' && msgpass || msgfail
+	observehook purge confpkg
+	testfileequal "${hook}-v2.list" 'confpkg 1 > - **REMOVE**'
+	testfileequal "${hook}-v3.list" 'confpkg 1 amd64 none > - - none **REMOVE**'
 
 
-observehook purge confpkg
-testfileequal "${hook}-v2.list" 'confpkg 1 > - **REMOVE**'
-testfileequal "${hook}-v3.list" 'confpkg 1 amd64 none > - - none **REMOVE**'
+	msgtest 'Conffiles are gone after purge' 'confpkg'
+	dpkg -l confpkg 2>/dev/null | grep -q '^rc' && msgfail || msgpass
+}
+
+enablehookversion 2
+enablehookversion 3
+testrun
 
 
-msgtest 'Conffiles are gone after purge' 'confpkg'
-dpkg -l confpkg 2>/dev/null | grep -q '^rc' && msgfail || msgpass
+enablehookversion 2 13
+enablehookversion 3 13
+testrun