PATCH: Mark result stream with FD_CLOEXEC

Mark Mitchell mark at codesourcery.com
Mon Nov 24 23:02:20 UTC 2003


This patch makes the file opened for writing to the results stream
have the FD_CLOEXEC flag set.  That prevents child processes from
having an open file descriptor that they (a) do not need, and (b)
should not use.

--
Mark Mitchell
CodeSourcery, LLC
mark at codesourcery.com

2003-11-24  Mark Mitchell  <mark at codesourcery.com>

	* qm/common.py (fnctl): Import it.
	(close_file_on_exec): New method.
	* qm/executable.py (qm.common): Import it.
	(Executable.Run): Use close_file_on_exec.
	(Executable._MakeCloseOnExec): Remove.
	(RedirectedExecutable._CreatePipe): Use close_file_on_exec.
	* qm/test/file_result_stream.py (qm.common): Import it.
	(FileResultStream.__init__): Use close_file_on_exec.

Index: qm/common.py
===================================================================
RCS file: /home/qm/Repository/qm/qm/common.py,v
retrieving revision 1.78
diff -c -5 -p -r1.78 common.py
*** qm/common.py	24 Nov 2003 00:52:57 -0000	1.78
--- qm/common.py	24 Nov 2003 22:59:03 -0000
*************** import string
*** 37,47 ****
  import sys
  import tempfile
  import time
  import traceback
  import types
! 
  ########################################################################
  # program name
  ########################################################################
  
  program_name = None
--- 37,49 ----
  import sys
  import tempfile
  import time
  import traceback
  import types
! if sys.platform != "win32":
!     import fcntl
!     
  ########################################################################
  # program name
  ########################################################################
  
  program_name = None
*************** def open_temporary_file():
*** 536,545 ****
--- 538,567 ----
      Like 'open_temporary_file_fd', except that the second element of the
      return value is a file object."""
  
      file_name, fd = open_temporary_file_fd()
      return (file_name, os.fdopen(fd, "w+b"))
+ 
+ 
+ def close_file_on_exec(fd):
+     """Prevent 'fd' from being inherited across 'exec'.
+     
+     'fd' -- A file descriptor, or object providing a 'fileno()'
+     method.
+ 
+     This function has no effect on Windows."""
+ 
+     if sys.platform != "win32":
+         flags = fcntl.fcntl(fd, fcntl.F_GETFD)
+         try:
+             flags |= fcntl.FD_CLOEXEC
+         except AttributeError:
+             # The Python 2.2 RPM shipped with Red Hat Linux 7.3 does
+             # not define FD_CLOEXEC.  Fortunately, FD_CLOEXEC is 1 on
+             # every UNIX system.
+             flags |= 1
+         fcntl.fcntl(fd, fcntl.F_SETFD, flags)
  
  
  def copy(object):
      """Make a best-effort attempt to copy 'object'.
  
Index: qm/executable.py
===================================================================
RCS file: /home/qm/Repository/qm/qm/executable.py,v
retrieving revision 1.21
diff -c -5 -p -r1.21 executable.py
*** qm/executable.py	26 Sep 2003 19:55:35 -0000	1.21
--- qm/executable.py	24 Nov 2003 22:59:03 -0000
***************
*** 14,23 ****
--- 14,24 ----
  ########################################################################
  # Imports
  #######################################################################
  
  import os
+ import qm.common
  import signal
  import string
  import sys
  import time
  
*************** class Executable(object):
*** 221,231 ****
          # it to the parent.
          if sys.platform != "win32":
              exception_pipe = os.pipe()
              # Mark the write end as close-on-exec so that the file
              # descriptor is not passed on to the child.
!             self._MakeCloseOnExec(exception_pipe[1])
          else:
              exception_pipe = None
  
          # Start the program.
          child = self.Spawn(arguments, environment, dir, path, exception_pipe)
--- 222,232 ----
          # it to the parent.
          if sys.platform != "win32":
              exception_pipe = os.pipe()
              # Mark the write end as close-on-exec so that the file
              # descriptor is not passed on to the child.
!             qm.common.close_file_on_exec(exception_pipe[1])
          else:
              exception_pipe = None
  
          # Start the program.
          child = self.Spawn(arguments, environment, dir, path, exception_pipe)
*************** class Executable(object):
*** 321,351 ****
          the value returned is the process handle.)"""
  
          return self.__child
      
          
-     def _MakeCloseOnExec(self, fd):
-         """Prevent 'fd' from being inherited across 'exec'.
- 
-         'fd' -- A file descriptor, or object providing a 'fileno()'
-         method.
- 
-         UNIX only."""
- 
-         assert sys.platform != "win32"
- 
-         flags = fcntl.fcntl(fd, fcntl.F_GETFD)
-         try:
-             flags |= fcntl.FD_CLOEXEC
-         except AttributeError:
-             # The Python 2.2 RPM shipped with Red Hat Linux 7.3 does
-             # not define FD_CLOEXEC.  Fortunately, FD_CLOEXEC is 1 on
-             # every UNIX system.
-             flags |= 1
-         fcntl.fcntl(fd, fcntl.F_SETFD, flags)
- 
- 
      def __CreateCommandLine(self, arguments):
          """Return a string giving the process command line.
  
          arguments -- A sequence of arguments (including argv[0])
          indicating the command to be run.
--- 322,331 ----
*************** class RedirectedExecutable(TimeoutExecut
*** 886,896 ****
              r, w = win32pipe.CreatePipe(sa, 0)
              return [r, w]
          else:
              pipe = os.pipe()
              for fd in pipe:
!                 self._MakeCloseOnExec(fd)
              return pipe
  
  
      def __CallUntilNone(self, f, attribute):
          """Call 'f' until 'self.attribute' is 'None'.
--- 866,876 ----
              r, w = win32pipe.CreatePipe(sa, 0)
              return [r, w]
          else:
              pipe = os.pipe()
              for fd in pipe:
!                 qm.common.close_file_on_exec(fd)
              return pipe
  
  
      def __CallUntilNone(self, f, attribute):
          """Call 'f' until 'self.attribute' is 'None'.
Index: qm/test/file_result_stream.py
===================================================================
RCS file: /home/qm/Repository/qm/qm/test/file_result_stream.py,v
retrieving revision 1.4
diff -c -5 -p -r1.4 file_result_stream.py
*** qm/test/file_result_stream.py	15 Aug 2003 09:05:23 -0000	1.4
--- qm/test/file_result_stream.py	24 Nov 2003 22:59:03 -0000
***************
*** 13,22 ****
--- 13,23 ----
  
  ########################################################################
  # Imports
  ########################################################################
  
+ import qm.common
  import qm.fields
  from   qm.test.result_stream import ResultStream
  import sys
  
  ########################################################################
*************** class FileResultStream(ResultStream):
*** 63,72 ****
--- 64,76 ----
                  if self._is_binary_file:
                      mode = "wb"
                  else:
                      mode = "w"
                  self.file = open(self.filename, mode, 0)
+                 # Child processes do not need to write to the results
+                 # file.
+                 qm.common.close_file_on_exec(self.file)
              else:
                  self.file = sys.stdout
              
  
          



More information about the qmtest mailing list