##// END OF EJS Templates
procutil: don't assign stdin to None, use os.devnull instead...
Pulkit Goyal -
r46718:7ce24d37 default
parent child Browse files
Show More
@@ -1,788 +1,792 b''
1 # procutil.py - utility for managing processes and executable environment
1 # procutil.py - utility for managing processes and executable environment
2 #
2 #
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
3 # Copyright 2005 K. Thananchayan <thananck@yahoo.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
5 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
6 #
6 #
7 # This software may be used and distributed according to the terms of the
7 # This software may be used and distributed according to the terms of the
8 # GNU General Public License version 2 or any later version.
8 # GNU General Public License version 2 or any later version.
9
9
10 from __future__ import absolute_import
10 from __future__ import absolute_import
11
11
12 import contextlib
12 import contextlib
13 import errno
13 import errno
14 import io
14 import io
15 import os
15 import os
16 import signal
16 import signal
17 import subprocess
17 import subprocess
18 import sys
18 import sys
19 import threading
19 import threading
20 import time
20 import time
21
21
22 from ..i18n import _
22 from ..i18n import _
23 from ..pycompat import (
23 from ..pycompat import (
24 getattr,
24 getattr,
25 open,
25 open,
26 )
26 )
27
27
28 from .. import (
28 from .. import (
29 encoding,
29 encoding,
30 error,
30 error,
31 policy,
31 policy,
32 pycompat,
32 pycompat,
33 )
33 )
34
34
35 # Import like this to keep import-checker happy
35 # Import like this to keep import-checker happy
36 from ..utils import resourceutil
36 from ..utils import resourceutil
37
37
38 osutil = policy.importmod('osutil')
38 osutil = policy.importmod('osutil')
39
39
40 if pycompat.iswindows:
40 if pycompat.iswindows:
41 from .. import windows as platform
41 from .. import windows as platform
42 else:
42 else:
43 from .. import posix as platform
43 from .. import posix as platform
44
44
45
45
46 def isatty(fp):
46 def isatty(fp):
47 try:
47 try:
48 return fp.isatty()
48 return fp.isatty()
49 except AttributeError:
49 except AttributeError:
50 return False
50 return False
51
51
52
52
53 class LineBufferedWrapper(object):
53 class LineBufferedWrapper(object):
54 def __init__(self, orig):
54 def __init__(self, orig):
55 self.orig = orig
55 self.orig = orig
56
56
57 def __getattr__(self, attr):
57 def __getattr__(self, attr):
58 return getattr(self.orig, attr)
58 return getattr(self.orig, attr)
59
59
60 def write(self, s):
60 def write(self, s):
61 orig = self.orig
61 orig = self.orig
62 res = orig.write(s)
62 res = orig.write(s)
63 if s.endswith(b'\n'):
63 if s.endswith(b'\n'):
64 orig.flush()
64 orig.flush()
65 return res
65 return res
66
66
67
67
68 io.BufferedIOBase.register(LineBufferedWrapper)
68 io.BufferedIOBase.register(LineBufferedWrapper)
69
69
70
70
71 def make_line_buffered(stream):
71 def make_line_buffered(stream):
72 if pycompat.ispy3 and not isinstance(stream, io.BufferedIOBase):
72 if pycompat.ispy3 and not isinstance(stream, io.BufferedIOBase):
73 # On Python 3, buffered streams can be expected to subclass
73 # On Python 3, buffered streams can be expected to subclass
74 # BufferedIOBase. This is definitively the case for the streams
74 # BufferedIOBase. This is definitively the case for the streams
75 # initialized by the interpreter. For unbuffered streams, we don't need
75 # initialized by the interpreter. For unbuffered streams, we don't need
76 # to emulate line buffering.
76 # to emulate line buffering.
77 return stream
77 return stream
78 if isinstance(stream, LineBufferedWrapper):
78 if isinstance(stream, LineBufferedWrapper):
79 return stream
79 return stream
80 return LineBufferedWrapper(stream)
80 return LineBufferedWrapper(stream)
81
81
82
82
83 def unwrap_line_buffered(stream):
83 def unwrap_line_buffered(stream):
84 if isinstance(stream, LineBufferedWrapper):
84 if isinstance(stream, LineBufferedWrapper):
85 assert not isinstance(stream.orig, LineBufferedWrapper)
85 assert not isinstance(stream.orig, LineBufferedWrapper)
86 return stream.orig
86 return stream.orig
87 return stream
87 return stream
88
88
89
89
90 class WriteAllWrapper(object):
90 class WriteAllWrapper(object):
91 def __init__(self, orig):
91 def __init__(self, orig):
92 self.orig = orig
92 self.orig = orig
93
93
94 def __getattr__(self, attr):
94 def __getattr__(self, attr):
95 return getattr(self.orig, attr)
95 return getattr(self.orig, attr)
96
96
97 def write(self, s):
97 def write(self, s):
98 write1 = self.orig.write
98 write1 = self.orig.write
99 m = memoryview(s)
99 m = memoryview(s)
100 total_to_write = len(s)
100 total_to_write = len(s)
101 total_written = 0
101 total_written = 0
102 while total_written < total_to_write:
102 while total_written < total_to_write:
103 total_written += write1(m[total_written:])
103 total_written += write1(m[total_written:])
104 return total_written
104 return total_written
105
105
106
106
107 io.IOBase.register(WriteAllWrapper)
107 io.IOBase.register(WriteAllWrapper)
108
108
109
109
110 def _make_write_all(stream):
110 def _make_write_all(stream):
111 assert pycompat.ispy3
111 assert pycompat.ispy3
112 if isinstance(stream, WriteAllWrapper):
112 if isinstance(stream, WriteAllWrapper):
113 return stream
113 return stream
114 if isinstance(stream, io.BufferedIOBase):
114 if isinstance(stream, io.BufferedIOBase):
115 # The io.BufferedIOBase.write() contract guarantees that all data is
115 # The io.BufferedIOBase.write() contract guarantees that all data is
116 # written.
116 # written.
117 return stream
117 return stream
118 # In general, the write() method of streams is free to write only part of
118 # In general, the write() method of streams is free to write only part of
119 # the data.
119 # the data.
120 return WriteAllWrapper(stream)
120 return WriteAllWrapper(stream)
121
121
122
122
123 if pycompat.ispy3:
123 if pycompat.ispy3:
124 # Python 3 implements its own I/O streams.
124 # Python 3 implements its own I/O streams.
125 # TODO: .buffer might not exist if std streams were replaced; we'll need
125 # TODO: .buffer might not exist if std streams were replaced; we'll need
126 # a silly wrapper to make a bytes stream backed by a unicode one.
126 # a silly wrapper to make a bytes stream backed by a unicode one.
127
127
128 # sys.stdin can be None
128 # sys.stdin can be None
129 stdin = sys.stdin.buffer if sys.stdin else sys.stdin
129 if sys.stdin:
130 stdin = sys.stdin.buffer
131 else:
132 stdin = open(os.devnull, 'rb')
133 os.close(stdin.fileno())
130 stdout = _make_write_all(sys.stdout.buffer)
134 stdout = _make_write_all(sys.stdout.buffer)
131 stderr = _make_write_all(sys.stderr.buffer)
135 stderr = _make_write_all(sys.stderr.buffer)
132 if pycompat.iswindows:
136 if pycompat.iswindows:
133 # Work around Windows bugs.
137 # Work around Windows bugs.
134 stdout = platform.winstdout(stdout)
138 stdout = platform.winstdout(stdout)
135 stderr = platform.winstdout(stderr)
139 stderr = platform.winstdout(stderr)
136 if isatty(stdout):
140 if isatty(stdout):
137 # The standard library doesn't offer line-buffered binary streams.
141 # The standard library doesn't offer line-buffered binary streams.
138 stdout = make_line_buffered(stdout)
142 stdout = make_line_buffered(stdout)
139 else:
143 else:
140 # Python 2 uses the I/O streams provided by the C library.
144 # Python 2 uses the I/O streams provided by the C library.
141 stdin = sys.stdin
145 stdin = sys.stdin
142 stdout = sys.stdout
146 stdout = sys.stdout
143 stderr = sys.stderr
147 stderr = sys.stderr
144 if pycompat.iswindows:
148 if pycompat.iswindows:
145 # Work around Windows bugs.
149 # Work around Windows bugs.
146 stdout = platform.winstdout(stdout)
150 stdout = platform.winstdout(stdout)
147 stderr = platform.winstdout(stderr)
151 stderr = platform.winstdout(stderr)
148 if isatty(stdout):
152 if isatty(stdout):
149 if pycompat.iswindows:
153 if pycompat.iswindows:
150 # The Windows C runtime library doesn't support line buffering.
154 # The Windows C runtime library doesn't support line buffering.
151 stdout = make_line_buffered(stdout)
155 stdout = make_line_buffered(stdout)
152 else:
156 else:
153 # glibc determines buffering on first write to stdout - if we
157 # glibc determines buffering on first write to stdout - if we
154 # replace a TTY destined stdout with a pipe destined stdout (e.g.
158 # replace a TTY destined stdout with a pipe destined stdout (e.g.
155 # pager), we want line buffering.
159 # pager), we want line buffering.
156 stdout = os.fdopen(stdout.fileno(), 'wb', 1)
160 stdout = os.fdopen(stdout.fileno(), 'wb', 1)
157
161
158
162
159 findexe = platform.findexe
163 findexe = platform.findexe
160 _gethgcmd = platform.gethgcmd
164 _gethgcmd = platform.gethgcmd
161 getuser = platform.getuser
165 getuser = platform.getuser
162 getpid = os.getpid
166 getpid = os.getpid
163 hidewindow = platform.hidewindow
167 hidewindow = platform.hidewindow
164 readpipe = platform.readpipe
168 readpipe = platform.readpipe
165 setbinary = platform.setbinary
169 setbinary = platform.setbinary
166 setsignalhandler = platform.setsignalhandler
170 setsignalhandler = platform.setsignalhandler
167 shellquote = platform.shellquote
171 shellquote = platform.shellquote
168 shellsplit = platform.shellsplit
172 shellsplit = platform.shellsplit
169 spawndetached = platform.spawndetached
173 spawndetached = platform.spawndetached
170 sshargs = platform.sshargs
174 sshargs = platform.sshargs
171 testpid = platform.testpid
175 testpid = platform.testpid
172
176
173 try:
177 try:
174 setprocname = osutil.setprocname
178 setprocname = osutil.setprocname
175 except AttributeError:
179 except AttributeError:
176 pass
180 pass
177 try:
181 try:
178 unblocksignal = osutil.unblocksignal
182 unblocksignal = osutil.unblocksignal
179 except AttributeError:
183 except AttributeError:
180 pass
184 pass
181
185
182 closefds = pycompat.isposix
186 closefds = pycompat.isposix
183
187
184
188
185 def explainexit(code):
189 def explainexit(code):
186 """return a message describing a subprocess status
190 """return a message describing a subprocess status
187 (codes from kill are negative - not os.system/wait encoding)"""
191 (codes from kill are negative - not os.system/wait encoding)"""
188 if code >= 0:
192 if code >= 0:
189 return _(b"exited with status %d") % code
193 return _(b"exited with status %d") % code
190 return _(b"killed by signal %d") % -code
194 return _(b"killed by signal %d") % -code
191
195
192
196
193 class _pfile(object):
197 class _pfile(object):
194 """File-like wrapper for a stream opened by subprocess.Popen()"""
198 """File-like wrapper for a stream opened by subprocess.Popen()"""
195
199
196 def __init__(self, proc, fp):
200 def __init__(self, proc, fp):
197 self._proc = proc
201 self._proc = proc
198 self._fp = fp
202 self._fp = fp
199
203
200 def close(self):
204 def close(self):
201 # unlike os.popen(), this returns an integer in subprocess coding
205 # unlike os.popen(), this returns an integer in subprocess coding
202 self._fp.close()
206 self._fp.close()
203 return self._proc.wait()
207 return self._proc.wait()
204
208
205 def __iter__(self):
209 def __iter__(self):
206 return iter(self._fp)
210 return iter(self._fp)
207
211
208 def __getattr__(self, attr):
212 def __getattr__(self, attr):
209 return getattr(self._fp, attr)
213 return getattr(self._fp, attr)
210
214
211 def __enter__(self):
215 def __enter__(self):
212 return self
216 return self
213
217
214 def __exit__(self, exc_type, exc_value, exc_tb):
218 def __exit__(self, exc_type, exc_value, exc_tb):
215 self.close()
219 self.close()
216
220
217
221
218 def popen(cmd, mode=b'rb', bufsize=-1):
222 def popen(cmd, mode=b'rb', bufsize=-1):
219 if mode == b'rb':
223 if mode == b'rb':
220 return _popenreader(cmd, bufsize)
224 return _popenreader(cmd, bufsize)
221 elif mode == b'wb':
225 elif mode == b'wb':
222 return _popenwriter(cmd, bufsize)
226 return _popenwriter(cmd, bufsize)
223 raise error.ProgrammingError(b'unsupported mode: %r' % mode)
227 raise error.ProgrammingError(b'unsupported mode: %r' % mode)
224
228
225
229
226 def _popenreader(cmd, bufsize):
230 def _popenreader(cmd, bufsize):
227 p = subprocess.Popen(
231 p = subprocess.Popen(
228 tonativestr(cmd),
232 tonativestr(cmd),
229 shell=True,
233 shell=True,
230 bufsize=bufsize,
234 bufsize=bufsize,
231 close_fds=closefds,
235 close_fds=closefds,
232 stdout=subprocess.PIPE,
236 stdout=subprocess.PIPE,
233 )
237 )
234 return _pfile(p, p.stdout)
238 return _pfile(p, p.stdout)
235
239
236
240
237 def _popenwriter(cmd, bufsize):
241 def _popenwriter(cmd, bufsize):
238 p = subprocess.Popen(
242 p = subprocess.Popen(
239 tonativestr(cmd),
243 tonativestr(cmd),
240 shell=True,
244 shell=True,
241 bufsize=bufsize,
245 bufsize=bufsize,
242 close_fds=closefds,
246 close_fds=closefds,
243 stdin=subprocess.PIPE,
247 stdin=subprocess.PIPE,
244 )
248 )
245 return _pfile(p, p.stdin)
249 return _pfile(p, p.stdin)
246
250
247
251
248 def popen2(cmd, env=None):
252 def popen2(cmd, env=None):
249 # Setting bufsize to -1 lets the system decide the buffer size.
253 # Setting bufsize to -1 lets the system decide the buffer size.
250 # The default for bufsize is 0, meaning unbuffered. This leads to
254 # The default for bufsize is 0, meaning unbuffered. This leads to
251 # poor performance on Mac OS X: http://bugs.python.org/issue4194
255 # poor performance on Mac OS X: http://bugs.python.org/issue4194
252 p = subprocess.Popen(
256 p = subprocess.Popen(
253 tonativestr(cmd),
257 tonativestr(cmd),
254 shell=True,
258 shell=True,
255 bufsize=-1,
259 bufsize=-1,
256 close_fds=closefds,
260 close_fds=closefds,
257 stdin=subprocess.PIPE,
261 stdin=subprocess.PIPE,
258 stdout=subprocess.PIPE,
262 stdout=subprocess.PIPE,
259 env=tonativeenv(env),
263 env=tonativeenv(env),
260 )
264 )
261 return p.stdin, p.stdout
265 return p.stdin, p.stdout
262
266
263
267
264 def popen3(cmd, env=None):
268 def popen3(cmd, env=None):
265 stdin, stdout, stderr, p = popen4(cmd, env)
269 stdin, stdout, stderr, p = popen4(cmd, env)
266 return stdin, stdout, stderr
270 return stdin, stdout, stderr
267
271
268
272
269 def popen4(cmd, env=None, bufsize=-1):
273 def popen4(cmd, env=None, bufsize=-1):
270 p = subprocess.Popen(
274 p = subprocess.Popen(
271 tonativestr(cmd),
275 tonativestr(cmd),
272 shell=True,
276 shell=True,
273 bufsize=bufsize,
277 bufsize=bufsize,
274 close_fds=closefds,
278 close_fds=closefds,
275 stdin=subprocess.PIPE,
279 stdin=subprocess.PIPE,
276 stdout=subprocess.PIPE,
280 stdout=subprocess.PIPE,
277 stderr=subprocess.PIPE,
281 stderr=subprocess.PIPE,
278 env=tonativeenv(env),
282 env=tonativeenv(env),
279 )
283 )
280 return p.stdin, p.stdout, p.stderr, p
284 return p.stdin, p.stdout, p.stderr, p
281
285
282
286
283 def pipefilter(s, cmd):
287 def pipefilter(s, cmd):
284 '''filter string S through command CMD, returning its output'''
288 '''filter string S through command CMD, returning its output'''
285 p = subprocess.Popen(
289 p = subprocess.Popen(
286 tonativestr(cmd),
290 tonativestr(cmd),
287 shell=True,
291 shell=True,
288 close_fds=closefds,
292 close_fds=closefds,
289 stdin=subprocess.PIPE,
293 stdin=subprocess.PIPE,
290 stdout=subprocess.PIPE,
294 stdout=subprocess.PIPE,
291 )
295 )
292 pout, perr = p.communicate(s)
296 pout, perr = p.communicate(s)
293 return pout
297 return pout
294
298
295
299
296 def tempfilter(s, cmd):
300 def tempfilter(s, cmd):
297 """filter string S through a pair of temporary files with CMD.
301 """filter string S through a pair of temporary files with CMD.
298 CMD is used as a template to create the real command to be run,
302 CMD is used as a template to create the real command to be run,
299 with the strings INFILE and OUTFILE replaced by the real names of
303 with the strings INFILE and OUTFILE replaced by the real names of
300 the temporary files generated."""
304 the temporary files generated."""
301 inname, outname = None, None
305 inname, outname = None, None
302 try:
306 try:
303 infd, inname = pycompat.mkstemp(prefix=b'hg-filter-in-')
307 infd, inname = pycompat.mkstemp(prefix=b'hg-filter-in-')
304 fp = os.fdopen(infd, 'wb')
308 fp = os.fdopen(infd, 'wb')
305 fp.write(s)
309 fp.write(s)
306 fp.close()
310 fp.close()
307 outfd, outname = pycompat.mkstemp(prefix=b'hg-filter-out-')
311 outfd, outname = pycompat.mkstemp(prefix=b'hg-filter-out-')
308 os.close(outfd)
312 os.close(outfd)
309 cmd = cmd.replace(b'INFILE', inname)
313 cmd = cmd.replace(b'INFILE', inname)
310 cmd = cmd.replace(b'OUTFILE', outname)
314 cmd = cmd.replace(b'OUTFILE', outname)
311 code = system(cmd)
315 code = system(cmd)
312 if pycompat.sysplatform == b'OpenVMS' and code & 1:
316 if pycompat.sysplatform == b'OpenVMS' and code & 1:
313 code = 0
317 code = 0
314 if code:
318 if code:
315 raise error.Abort(
319 raise error.Abort(
316 _(b"command '%s' failed: %s") % (cmd, explainexit(code))
320 _(b"command '%s' failed: %s") % (cmd, explainexit(code))
317 )
321 )
318 with open(outname, b'rb') as fp:
322 with open(outname, b'rb') as fp:
319 return fp.read()
323 return fp.read()
320 finally:
324 finally:
321 try:
325 try:
322 if inname:
326 if inname:
323 os.unlink(inname)
327 os.unlink(inname)
324 except OSError:
328 except OSError:
325 pass
329 pass
326 try:
330 try:
327 if outname:
331 if outname:
328 os.unlink(outname)
332 os.unlink(outname)
329 except OSError:
333 except OSError:
330 pass
334 pass
331
335
332
336
333 _filtertable = {
337 _filtertable = {
334 b'tempfile:': tempfilter,
338 b'tempfile:': tempfilter,
335 b'pipe:': pipefilter,
339 b'pipe:': pipefilter,
336 }
340 }
337
341
338
342
339 def filter(s, cmd):
343 def filter(s, cmd):
340 """filter a string through a command that transforms its input to its
344 """filter a string through a command that transforms its input to its
341 output"""
345 output"""
342 for name, fn in pycompat.iteritems(_filtertable):
346 for name, fn in pycompat.iteritems(_filtertable):
343 if cmd.startswith(name):
347 if cmd.startswith(name):
344 return fn(s, cmd[len(name) :].lstrip())
348 return fn(s, cmd[len(name) :].lstrip())
345 return pipefilter(s, cmd)
349 return pipefilter(s, cmd)
346
350
347
351
348 _hgexecutable = None
352 _hgexecutable = None
349
353
350
354
351 def hgexecutable():
355 def hgexecutable():
352 """return location of the 'hg' executable.
356 """return location of the 'hg' executable.
353
357
354 Defaults to $HG or 'hg' in the search path.
358 Defaults to $HG or 'hg' in the search path.
355 """
359 """
356 if _hgexecutable is None:
360 if _hgexecutable is None:
357 hg = encoding.environ.get(b'HG')
361 hg = encoding.environ.get(b'HG')
358 mainmod = sys.modules['__main__']
362 mainmod = sys.modules['__main__']
359 if hg:
363 if hg:
360 _sethgexecutable(hg)
364 _sethgexecutable(hg)
361 elif resourceutil.mainfrozen():
365 elif resourceutil.mainfrozen():
362 if getattr(sys, 'frozen', None) == 'macosx_app':
366 if getattr(sys, 'frozen', None) == 'macosx_app':
363 # Env variable set by py2app
367 # Env variable set by py2app
364 _sethgexecutable(encoding.environ[b'EXECUTABLEPATH'])
368 _sethgexecutable(encoding.environ[b'EXECUTABLEPATH'])
365 else:
369 else:
366 _sethgexecutable(pycompat.sysexecutable)
370 _sethgexecutable(pycompat.sysexecutable)
367 elif (
371 elif (
368 not pycompat.iswindows
372 not pycompat.iswindows
369 and os.path.basename(getattr(mainmod, '__file__', '')) == 'hg'
373 and os.path.basename(getattr(mainmod, '__file__', '')) == 'hg'
370 ):
374 ):
371 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
375 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
372 else:
376 else:
373 _sethgexecutable(
377 _sethgexecutable(
374 findexe(b'hg') or os.path.basename(pycompat.sysargv[0])
378 findexe(b'hg') or os.path.basename(pycompat.sysargv[0])
375 )
379 )
376 return _hgexecutable
380 return _hgexecutable
377
381
378
382
379 def _sethgexecutable(path):
383 def _sethgexecutable(path):
380 """set location of the 'hg' executable"""
384 """set location of the 'hg' executable"""
381 global _hgexecutable
385 global _hgexecutable
382 _hgexecutable = path
386 _hgexecutable = path
383
387
384
388
385 def _testfileno(f, stdf):
389 def _testfileno(f, stdf):
386 fileno = getattr(f, 'fileno', None)
390 fileno = getattr(f, 'fileno', None)
387 try:
391 try:
388 return fileno and fileno() == stdf.fileno()
392 return fileno and fileno() == stdf.fileno()
389 except io.UnsupportedOperation:
393 except io.UnsupportedOperation:
390 return False # fileno() raised UnsupportedOperation
394 return False # fileno() raised UnsupportedOperation
391
395
392
396
393 def isstdin(f):
397 def isstdin(f):
394 return _testfileno(f, sys.__stdin__)
398 return _testfileno(f, sys.__stdin__)
395
399
396
400
397 def isstdout(f):
401 def isstdout(f):
398 return _testfileno(f, sys.__stdout__)
402 return _testfileno(f, sys.__stdout__)
399
403
400
404
401 def protectstdio(uin, uout):
405 def protectstdio(uin, uout):
402 """Duplicate streams and redirect original if (uin, uout) are stdio
406 """Duplicate streams and redirect original if (uin, uout) are stdio
403
407
404 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's
408 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's
405 redirected to stderr so the output is still readable.
409 redirected to stderr so the output is still readable.
406
410
407 Returns (fin, fout) which point to the original (uin, uout) fds, but
411 Returns (fin, fout) which point to the original (uin, uout) fds, but
408 may be copy of (uin, uout). The returned streams can be considered
412 may be copy of (uin, uout). The returned streams can be considered
409 "owned" in that print(), exec(), etc. never reach to them.
413 "owned" in that print(), exec(), etc. never reach to them.
410 """
414 """
411 uout.flush()
415 uout.flush()
412 fin, fout = uin, uout
416 fin, fout = uin, uout
413 if _testfileno(uin, stdin):
417 if _testfileno(uin, stdin):
414 newfd = os.dup(uin.fileno())
418 newfd = os.dup(uin.fileno())
415 nullfd = os.open(os.devnull, os.O_RDONLY)
419 nullfd = os.open(os.devnull, os.O_RDONLY)
416 os.dup2(nullfd, uin.fileno())
420 os.dup2(nullfd, uin.fileno())
417 os.close(nullfd)
421 os.close(nullfd)
418 fin = os.fdopen(newfd, 'rb')
422 fin = os.fdopen(newfd, 'rb')
419 if _testfileno(uout, stdout):
423 if _testfileno(uout, stdout):
420 newfd = os.dup(uout.fileno())
424 newfd = os.dup(uout.fileno())
421 os.dup2(stderr.fileno(), uout.fileno())
425 os.dup2(stderr.fileno(), uout.fileno())
422 fout = os.fdopen(newfd, 'wb')
426 fout = os.fdopen(newfd, 'wb')
423 return fin, fout
427 return fin, fout
424
428
425
429
426 def restorestdio(uin, uout, fin, fout):
430 def restorestdio(uin, uout, fin, fout):
427 """Restore (uin, uout) streams from possibly duplicated (fin, fout)"""
431 """Restore (uin, uout) streams from possibly duplicated (fin, fout)"""
428 uout.flush()
432 uout.flush()
429 for f, uif in [(fin, uin), (fout, uout)]:
433 for f, uif in [(fin, uin), (fout, uout)]:
430 if f is not uif:
434 if f is not uif:
431 os.dup2(f.fileno(), uif.fileno())
435 os.dup2(f.fileno(), uif.fileno())
432 f.close()
436 f.close()
433
437
434
438
435 def shellenviron(environ=None):
439 def shellenviron(environ=None):
436 """return environ with optional override, useful for shelling out"""
440 """return environ with optional override, useful for shelling out"""
437
441
438 def py2shell(val):
442 def py2shell(val):
439 """convert python object into string that is useful to shell"""
443 """convert python object into string that is useful to shell"""
440 if val is None or val is False:
444 if val is None or val is False:
441 return b'0'
445 return b'0'
442 if val is True:
446 if val is True:
443 return b'1'
447 return b'1'
444 return pycompat.bytestr(val)
448 return pycompat.bytestr(val)
445
449
446 env = dict(encoding.environ)
450 env = dict(encoding.environ)
447 if environ:
451 if environ:
448 env.update((k, py2shell(v)) for k, v in pycompat.iteritems(environ))
452 env.update((k, py2shell(v)) for k, v in pycompat.iteritems(environ))
449 env[b'HG'] = hgexecutable()
453 env[b'HG'] = hgexecutable()
450 return env
454 return env
451
455
452
456
453 if pycompat.iswindows:
457 if pycompat.iswindows:
454
458
455 def shelltonative(cmd, env):
459 def shelltonative(cmd, env):
456 return platform.shelltocmdexe( # pytype: disable=module-attr
460 return platform.shelltocmdexe( # pytype: disable=module-attr
457 cmd, shellenviron(env)
461 cmd, shellenviron(env)
458 )
462 )
459
463
460 tonativestr = encoding.strfromlocal
464 tonativestr = encoding.strfromlocal
461 else:
465 else:
462
466
463 def shelltonative(cmd, env):
467 def shelltonative(cmd, env):
464 return cmd
468 return cmd
465
469
466 tonativestr = pycompat.identity
470 tonativestr = pycompat.identity
467
471
468
472
469 def tonativeenv(env):
473 def tonativeenv(env):
470 """convert the environment from bytes to strings suitable for Popen(), etc."""
474 """convert the environment from bytes to strings suitable for Popen(), etc."""
471 return pycompat.rapply(tonativestr, env)
475 return pycompat.rapply(tonativestr, env)
472
476
473
477
474 def system(cmd, environ=None, cwd=None, out=None):
478 def system(cmd, environ=None, cwd=None, out=None):
475 """enhanced shell command execution.
479 """enhanced shell command execution.
476 run with environment maybe modified, maybe in different dir.
480 run with environment maybe modified, maybe in different dir.
477
481
478 if out is specified, it is assumed to be a file-like object that has a
482 if out is specified, it is assumed to be a file-like object that has a
479 write() method. stdout and stderr will be redirected to out."""
483 write() method. stdout and stderr will be redirected to out."""
480 try:
484 try:
481 stdout.flush()
485 stdout.flush()
482 except Exception:
486 except Exception:
483 pass
487 pass
484 env = shellenviron(environ)
488 env = shellenviron(environ)
485 if out is None or isstdout(out):
489 if out is None or isstdout(out):
486 rc = subprocess.call(
490 rc = subprocess.call(
487 tonativestr(cmd),
491 tonativestr(cmd),
488 shell=True,
492 shell=True,
489 close_fds=closefds,
493 close_fds=closefds,
490 env=tonativeenv(env),
494 env=tonativeenv(env),
491 cwd=pycompat.rapply(tonativestr, cwd),
495 cwd=pycompat.rapply(tonativestr, cwd),
492 )
496 )
493 else:
497 else:
494 proc = subprocess.Popen(
498 proc = subprocess.Popen(
495 tonativestr(cmd),
499 tonativestr(cmd),
496 shell=True,
500 shell=True,
497 close_fds=closefds,
501 close_fds=closefds,
498 env=tonativeenv(env),
502 env=tonativeenv(env),
499 cwd=pycompat.rapply(tonativestr, cwd),
503 cwd=pycompat.rapply(tonativestr, cwd),
500 stdout=subprocess.PIPE,
504 stdout=subprocess.PIPE,
501 stderr=subprocess.STDOUT,
505 stderr=subprocess.STDOUT,
502 )
506 )
503 for line in iter(proc.stdout.readline, b''):
507 for line in iter(proc.stdout.readline, b''):
504 out.write(line)
508 out.write(line)
505 proc.wait()
509 proc.wait()
506 rc = proc.returncode
510 rc = proc.returncode
507 if pycompat.sysplatform == b'OpenVMS' and rc & 1:
511 if pycompat.sysplatform == b'OpenVMS' and rc & 1:
508 rc = 0
512 rc = 0
509 return rc
513 return rc
510
514
511
515
512 _is_gui = None
516 _is_gui = None
513
517
514
518
515 def _gui():
519 def _gui():
516 '''Are we running in a GUI?'''
520 '''Are we running in a GUI?'''
517 if pycompat.isdarwin:
521 if pycompat.isdarwin:
518 if b'SSH_CONNECTION' in encoding.environ:
522 if b'SSH_CONNECTION' in encoding.environ:
519 # handle SSH access to a box where the user is logged in
523 # handle SSH access to a box where the user is logged in
520 return False
524 return False
521 elif getattr(osutil, 'isgui', None):
525 elif getattr(osutil, 'isgui', None):
522 # check if a CoreGraphics session is available
526 # check if a CoreGraphics session is available
523 return osutil.isgui()
527 return osutil.isgui()
524 else:
528 else:
525 # pure build; use a safe default
529 # pure build; use a safe default
526 return True
530 return True
527 else:
531 else:
528 return pycompat.iswindows or encoding.environ.get(b"DISPLAY")
532 return pycompat.iswindows or encoding.environ.get(b"DISPLAY")
529
533
530
534
531 def gui():
535 def gui():
532 global _is_gui
536 global _is_gui
533 if _is_gui is None:
537 if _is_gui is None:
534 _is_gui = _gui()
538 _is_gui = _gui()
535 return _is_gui
539 return _is_gui
536
540
537
541
538 def hgcmd():
542 def hgcmd():
539 """Return the command used to execute current hg
543 """Return the command used to execute current hg
540
544
541 This is different from hgexecutable() because on Windows we want
545 This is different from hgexecutable() because on Windows we want
542 to avoid things opening new shell windows like batch files, so we
546 to avoid things opening new shell windows like batch files, so we
543 get either the python call or current executable.
547 get either the python call or current executable.
544 """
548 """
545 if resourceutil.mainfrozen():
549 if resourceutil.mainfrozen():
546 if getattr(sys, 'frozen', None) == 'macosx_app':
550 if getattr(sys, 'frozen', None) == 'macosx_app':
547 # Env variable set by py2app
551 # Env variable set by py2app
548 return [encoding.environ[b'EXECUTABLEPATH']]
552 return [encoding.environ[b'EXECUTABLEPATH']]
549 else:
553 else:
550 return [pycompat.sysexecutable]
554 return [pycompat.sysexecutable]
551 return _gethgcmd()
555 return _gethgcmd()
552
556
553
557
554 def rundetached(args, condfn):
558 def rundetached(args, condfn):
555 """Execute the argument list in a detached process.
559 """Execute the argument list in a detached process.
556
560
557 condfn is a callable which is called repeatedly and should return
561 condfn is a callable which is called repeatedly and should return
558 True once the child process is known to have started successfully.
562 True once the child process is known to have started successfully.
559 At this point, the child process PID is returned. If the child
563 At this point, the child process PID is returned. If the child
560 process fails to start or finishes before condfn() evaluates to
564 process fails to start or finishes before condfn() evaluates to
561 True, return -1.
565 True, return -1.
562 """
566 """
563 # Windows case is easier because the child process is either
567 # Windows case is easier because the child process is either
564 # successfully starting and validating the condition or exiting
568 # successfully starting and validating the condition or exiting
565 # on failure. We just poll on its PID. On Unix, if the child
569 # on failure. We just poll on its PID. On Unix, if the child
566 # process fails to start, it will be left in a zombie state until
570 # process fails to start, it will be left in a zombie state until
567 # the parent wait on it, which we cannot do since we expect a long
571 # the parent wait on it, which we cannot do since we expect a long
568 # running process on success. Instead we listen for SIGCHLD telling
572 # running process on success. Instead we listen for SIGCHLD telling
569 # us our child process terminated.
573 # us our child process terminated.
570 terminated = set()
574 terminated = set()
571
575
572 def handler(signum, frame):
576 def handler(signum, frame):
573 terminated.add(os.wait())
577 terminated.add(os.wait())
574
578
575 prevhandler = None
579 prevhandler = None
576 SIGCHLD = getattr(signal, 'SIGCHLD', None)
580 SIGCHLD = getattr(signal, 'SIGCHLD', None)
577 if SIGCHLD is not None:
581 if SIGCHLD is not None:
578 prevhandler = signal.signal(SIGCHLD, handler)
582 prevhandler = signal.signal(SIGCHLD, handler)
579 try:
583 try:
580 pid = spawndetached(args)
584 pid = spawndetached(args)
581 while not condfn():
585 while not condfn():
582 if (pid in terminated or not testpid(pid)) and not condfn():
586 if (pid in terminated or not testpid(pid)) and not condfn():
583 return -1
587 return -1
584 time.sleep(0.1)
588 time.sleep(0.1)
585 return pid
589 return pid
586 finally:
590 finally:
587 if prevhandler is not None:
591 if prevhandler is not None:
588 signal.signal(signal.SIGCHLD, prevhandler)
592 signal.signal(signal.SIGCHLD, prevhandler)
589
593
590
594
591 @contextlib.contextmanager
595 @contextlib.contextmanager
592 def uninterruptible(warn):
596 def uninterruptible(warn):
593 """Inhibit SIGINT handling on a region of code.
597 """Inhibit SIGINT handling on a region of code.
594
598
595 Note that if this is called in a non-main thread, it turns into a no-op.
599 Note that if this is called in a non-main thread, it turns into a no-op.
596
600
597 Args:
601 Args:
598 warn: A callable which takes no arguments, and returns True if the
602 warn: A callable which takes no arguments, and returns True if the
599 previous signal handling should be restored.
603 previous signal handling should be restored.
600 """
604 """
601
605
602 oldsiginthandler = [signal.getsignal(signal.SIGINT)]
606 oldsiginthandler = [signal.getsignal(signal.SIGINT)]
603 shouldbail = []
607 shouldbail = []
604
608
605 def disabledsiginthandler(*args):
609 def disabledsiginthandler(*args):
606 if warn():
610 if warn():
607 signal.signal(signal.SIGINT, oldsiginthandler[0])
611 signal.signal(signal.SIGINT, oldsiginthandler[0])
608 del oldsiginthandler[0]
612 del oldsiginthandler[0]
609 shouldbail.append(True)
613 shouldbail.append(True)
610
614
611 try:
615 try:
612 try:
616 try:
613 signal.signal(signal.SIGINT, disabledsiginthandler)
617 signal.signal(signal.SIGINT, disabledsiginthandler)
614 except ValueError:
618 except ValueError:
615 # wrong thread, oh well, we tried
619 # wrong thread, oh well, we tried
616 del oldsiginthandler[0]
620 del oldsiginthandler[0]
617 yield
621 yield
618 finally:
622 finally:
619 if oldsiginthandler:
623 if oldsiginthandler:
620 signal.signal(signal.SIGINT, oldsiginthandler[0])
624 signal.signal(signal.SIGINT, oldsiginthandler[0])
621 if shouldbail:
625 if shouldbail:
622 raise KeyboardInterrupt
626 raise KeyboardInterrupt
623
627
624
628
625 if pycompat.iswindows:
629 if pycompat.iswindows:
626 # no fork on Windows, but we can create a detached process
630 # no fork on Windows, but we can create a detached process
627 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx
631 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx
628 # No stdlib constant exists for this value
632 # No stdlib constant exists for this value
629 DETACHED_PROCESS = 0x00000008
633 DETACHED_PROCESS = 0x00000008
630 # Following creation flags might create a console GUI window.
634 # Following creation flags might create a console GUI window.
631 # Using subprocess.CREATE_NEW_CONSOLE might helps.
635 # Using subprocess.CREATE_NEW_CONSOLE might helps.
632 # See https://phab.mercurial-scm.org/D1701 for discussion
636 # See https://phab.mercurial-scm.org/D1701 for discussion
633 _creationflags = (
637 _creationflags = (
634 DETACHED_PROCESS
638 DETACHED_PROCESS
635 | subprocess.CREATE_NEW_PROCESS_GROUP # pytype: disable=module-attr
639 | subprocess.CREATE_NEW_PROCESS_GROUP # pytype: disable=module-attr
636 )
640 )
637
641
638 def runbgcommand(
642 def runbgcommand(
639 script,
643 script,
640 env,
644 env,
641 shell=False,
645 shell=False,
642 stdout=None,
646 stdout=None,
643 stderr=None,
647 stderr=None,
644 ensurestart=True,
648 ensurestart=True,
645 record_wait=None,
649 record_wait=None,
646 stdin_bytes=None,
650 stdin_bytes=None,
647 ):
651 ):
648 '''Spawn a command without waiting for it to finish.'''
652 '''Spawn a command without waiting for it to finish.'''
649 # we can't use close_fds *and* redirect stdin. I'm not sure that we
653 # we can't use close_fds *and* redirect stdin. I'm not sure that we
650 # need to because the detached process has no console connection.
654 # need to because the detached process has no console connection.
651
655
652 try:
656 try:
653 stdin = None
657 stdin = None
654 if stdin_bytes is not None:
658 if stdin_bytes is not None:
655 stdin = pycompat.unnamedtempfile()
659 stdin = pycompat.unnamedtempfile()
656 stdin.write(stdin_bytes)
660 stdin.write(stdin_bytes)
657 stdin.flush()
661 stdin.flush()
658 stdin.seek(0)
662 stdin.seek(0)
659
663
660 p = subprocess.Popen(
664 p = subprocess.Popen(
661 pycompat.rapply(tonativestr, script),
665 pycompat.rapply(tonativestr, script),
662 shell=shell,
666 shell=shell,
663 env=tonativeenv(env),
667 env=tonativeenv(env),
664 close_fds=True,
668 close_fds=True,
665 creationflags=_creationflags,
669 creationflags=_creationflags,
666 stdin=stdin,
670 stdin=stdin,
667 stdout=stdout,
671 stdout=stdout,
668 stderr=stderr,
672 stderr=stderr,
669 )
673 )
670 if record_wait is not None:
674 if record_wait is not None:
671 record_wait(p.wait)
675 record_wait(p.wait)
672 finally:
676 finally:
673 if stdin is not None:
677 if stdin is not None:
674 stdin.close()
678 stdin.close()
675
679
676
680
677 else:
681 else:
678
682
679 def runbgcommand(
683 def runbgcommand(
680 cmd,
684 cmd,
681 env,
685 env,
682 shell=False,
686 shell=False,
683 stdout=None,
687 stdout=None,
684 stderr=None,
688 stderr=None,
685 ensurestart=True,
689 ensurestart=True,
686 record_wait=None,
690 record_wait=None,
687 stdin_bytes=None,
691 stdin_bytes=None,
688 ):
692 ):
689 """Spawn a command without waiting for it to finish.
693 """Spawn a command without waiting for it to finish.
690
694
691
695
692 When `record_wait` is not None, the spawned process will not be fully
696 When `record_wait` is not None, the spawned process will not be fully
693 detached and the `record_wait` argument will be called with a the
697 detached and the `record_wait` argument will be called with a the
694 `Subprocess.wait` function for the spawned process. This is mostly
698 `Subprocess.wait` function for the spawned process. This is mostly
695 useful for developers that need to make sure the spawned process
699 useful for developers that need to make sure the spawned process
696 finished before a certain point. (eg: writing test)"""
700 finished before a certain point. (eg: writing test)"""
697 if pycompat.isdarwin:
701 if pycompat.isdarwin:
698 # avoid crash in CoreFoundation in case another thread
702 # avoid crash in CoreFoundation in case another thread
699 # calls gui() while we're calling fork().
703 # calls gui() while we're calling fork().
700 gui()
704 gui()
701
705
702 # double-fork to completely detach from the parent process
706 # double-fork to completely detach from the parent process
703 # based on http://code.activestate.com/recipes/278731
707 # based on http://code.activestate.com/recipes/278731
704 if record_wait is None:
708 if record_wait is None:
705 pid = os.fork()
709 pid = os.fork()
706 if pid:
710 if pid:
707 if not ensurestart:
711 if not ensurestart:
708 # Even though we're not waiting on the child process,
712 # Even though we're not waiting on the child process,
709 # we still must call waitpid() on it at some point so
713 # we still must call waitpid() on it at some point so
710 # it's not a zombie/defunct. This is especially relevant for
714 # it's not a zombie/defunct. This is especially relevant for
711 # chg since the parent process won't die anytime soon.
715 # chg since the parent process won't die anytime soon.
712 # We use a thread to make the overhead tiny.
716 # We use a thread to make the overhead tiny.
713 def _do_wait():
717 def _do_wait():
714 os.waitpid(pid, 0)
718 os.waitpid(pid, 0)
715
719
716 t = threading.Thread(target=_do_wait)
720 t = threading.Thread(target=_do_wait)
717 t.daemon = True
721 t.daemon = True
718 t.start()
722 t.start()
719 return
723 return
720 # Parent process
724 # Parent process
721 (_pid, status) = os.waitpid(pid, 0)
725 (_pid, status) = os.waitpid(pid, 0)
722 if os.WIFEXITED(status):
726 if os.WIFEXITED(status):
723 returncode = os.WEXITSTATUS(status)
727 returncode = os.WEXITSTATUS(status)
724 else:
728 else:
725 returncode = -(os.WTERMSIG(status))
729 returncode = -(os.WTERMSIG(status))
726 if returncode != 0:
730 if returncode != 0:
727 # The child process's return code is 0 on success, an errno
731 # The child process's return code is 0 on success, an errno
728 # value on failure, or 255 if we don't have a valid errno
732 # value on failure, or 255 if we don't have a valid errno
729 # value.
733 # value.
730 #
734 #
731 # (It would be slightly nicer to return the full exception info
735 # (It would be slightly nicer to return the full exception info
732 # over a pipe as the subprocess module does. For now it
736 # over a pipe as the subprocess module does. For now it
733 # doesn't seem worth adding that complexity here, though.)
737 # doesn't seem worth adding that complexity here, though.)
734 if returncode == 255:
738 if returncode == 255:
735 returncode = errno.EINVAL
739 returncode = errno.EINVAL
736 raise OSError(
740 raise OSError(
737 returncode,
741 returncode,
738 b'error running %r: %s'
742 b'error running %r: %s'
739 % (cmd, os.strerror(returncode)),
743 % (cmd, os.strerror(returncode)),
740 )
744 )
741 return
745 return
742
746
743 returncode = 255
747 returncode = 255
744 try:
748 try:
745 if record_wait is None:
749 if record_wait is None:
746 # Start a new session
750 # Start a new session
747 os.setsid()
751 os.setsid()
748 # connect stdin to devnull to make sure the subprocess can't
752 # connect stdin to devnull to make sure the subprocess can't
749 # muck up that stream for mercurial.
753 # muck up that stream for mercurial.
750 if stdin_bytes is None:
754 if stdin_bytes is None:
751 stdin = open(os.devnull, b'r')
755 stdin = open(os.devnull, b'r')
752 else:
756 else:
753 stdin = pycompat.unnamedtempfile()
757 stdin = pycompat.unnamedtempfile()
754 stdin.write(stdin_bytes)
758 stdin.write(stdin_bytes)
755 stdin.flush()
759 stdin.flush()
756 stdin.seek(0)
760 stdin.seek(0)
757
761
758 if stdout is None:
762 if stdout is None:
759 stdout = open(os.devnull, b'w')
763 stdout = open(os.devnull, b'w')
760 if stderr is None:
764 if stderr is None:
761 stderr = open(os.devnull, b'w')
765 stderr = open(os.devnull, b'w')
762
766
763 p = subprocess.Popen(
767 p = subprocess.Popen(
764 cmd,
768 cmd,
765 shell=shell,
769 shell=shell,
766 env=env,
770 env=env,
767 close_fds=True,
771 close_fds=True,
768 stdin=stdin,
772 stdin=stdin,
769 stdout=stdout,
773 stdout=stdout,
770 stderr=stderr,
774 stderr=stderr,
771 )
775 )
772 if record_wait is not None:
776 if record_wait is not None:
773 record_wait(p.wait)
777 record_wait(p.wait)
774 returncode = 0
778 returncode = 0
775 except EnvironmentError as ex:
779 except EnvironmentError as ex:
776 returncode = ex.errno & 0xFF
780 returncode = ex.errno & 0xFF
777 if returncode == 0:
781 if returncode == 0:
778 # This shouldn't happen, but just in case make sure the
782 # This shouldn't happen, but just in case make sure the
779 # return code is never 0 here.
783 # return code is never 0 here.
780 returncode = 255
784 returncode = 255
781 except Exception:
785 except Exception:
782 returncode = 255
786 returncode = 255
783 finally:
787 finally:
784 # mission accomplished, this child needs to exit and not
788 # mission accomplished, this child needs to exit and not
785 # continue the hg process here.
789 # continue the hg process here.
786 stdin.close()
790 stdin.close()
787 if record_wait is None:
791 if record_wait is None:
788 os._exit(returncode)
792 os._exit(returncode)
General Comments 0
You need to be logged in to leave comments. Login now