@@ -112,6 +112,45 @@ def shlex_split(s, comments=False):
112112 for token in shlex .split (str (s ), comments = comments )]
113113
114114
115+ class TempFileWatcher (object ):
116+ """
117+ Since Ansible 2.7.0, lineinfile leaks file descriptors returned by
118+ :func:`tempfile.mkstemp` (ansible/ansible#57327). Handle this and all
119+ similar cases by recording descriptors produced by mkstemp during module
120+ execution, and cleaning up any leaked descriptors on completion.
121+ """
122+ def __init__ (self ):
123+ self ._real_mkstemp = tempfile .mkstemp
124+ # (fd, st.st_dev, st.st_ino)
125+ self ._fd_dev_inode = []
126+ tempfile .mkstemp = self ._wrap_mkstemp
127+
128+ def _wrap_mkstemp (self , * args , ** kwargs ):
129+ fd , path = self ._real_mkstemp (* args , ** kwargs )
130+ st = os .fstat (fd )
131+ self ._fd_dev_inode .append ((fd , st .st_dev , st .st_ino ))
132+ return fd , path
133+
134+ def revert (self ):
135+ tempfile .mkstemp = self ._real_mkstemp
136+ for tup in self ._fd_dev_inode :
137+ self ._revert_one (* tup )
138+
139+ def _revert_one (self , fd , st_dev , st_ino ):
140+ try :
141+ st = os .fstat (fd )
142+ except OSError :
143+ # FD no longer exists.
144+ return
145+
146+ if not (st .st_dev == st_dev and st .st_ino == st_ino ):
147+ # FD reused.
148+ return
149+
150+ LOG .info ("a tempfile.mkstemp() FD was leaked during the last task" )
151+ os .close (fd )
152+
153+
115154class EnvironmentFileWatcher (object ):
116155 """
117156 Usually Ansible edits to /etc/environment and ~/.pam_environment are
@@ -803,6 +842,7 @@ def setup(self):
803842 # module, but this has never been a bug report. Instead act like an
804843 # interpreter that had its script piped on stdin.
805844 self ._argv = TemporaryArgv (['' ])
845+ self ._temp_watcher = TempFileWatcher ()
806846 self ._importer = ModuleUtilsImporter (
807847 context = self .service_context ,
808848 module_utils = self .module_map ['custom' ],
@@ -818,6 +858,7 @@ def _revert_excepthook(self):
818858
819859 def revert (self ):
820860 self .atexit_wrapper .revert ()
861+ self ._temp_watcher .revert ()
821862 self ._argv .revert ()
822863 self ._stdio .revert ()
823864 self ._revert_excepthook ()
0 commit comments