##// END OF EJS Templates
typing: disable a few errors when accessing Windows specific attributes...
Matt Harbison -
r47542:65f437c2 stable
parent child Browse files
Show More
@@ -1,813 +1,813 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 BadFile(io.RawIOBase):
53 class BadFile(io.RawIOBase):
54 """Dummy file object to simulate closed stdio behavior"""
54 """Dummy file object to simulate closed stdio behavior"""
55
55
56 def readinto(self, b):
56 def readinto(self, b):
57 raise IOError(errno.EBADF, 'Bad file descriptor')
57 raise IOError(errno.EBADF, 'Bad file descriptor')
58
58
59 def write(self, b):
59 def write(self, b):
60 raise IOError(errno.EBADF, 'Bad file descriptor')
60 raise IOError(errno.EBADF, 'Bad file descriptor')
61
61
62
62
63 class LineBufferedWrapper(object):
63 class LineBufferedWrapper(object):
64 def __init__(self, orig):
64 def __init__(self, orig):
65 self.orig = orig
65 self.orig = orig
66
66
67 def __getattr__(self, attr):
67 def __getattr__(self, attr):
68 return getattr(self.orig, attr)
68 return getattr(self.orig, attr)
69
69
70 def write(self, s):
70 def write(self, s):
71 orig = self.orig
71 orig = self.orig
72 res = orig.write(s)
72 res = orig.write(s)
73 if s.endswith(b'\n'):
73 if s.endswith(b'\n'):
74 orig.flush()
74 orig.flush()
75 return res
75 return res
76
76
77
77
78 io.BufferedIOBase.register(LineBufferedWrapper)
78 io.BufferedIOBase.register(LineBufferedWrapper)
79
79
80
80
81 def make_line_buffered(stream):
81 def make_line_buffered(stream):
82 if pycompat.ispy3 and not isinstance(stream, io.BufferedIOBase):
82 if pycompat.ispy3 and not isinstance(stream, io.BufferedIOBase):
83 # On Python 3, buffered streams can be expected to subclass
83 # On Python 3, buffered streams can be expected to subclass
84 # BufferedIOBase. This is definitively the case for the streams
84 # BufferedIOBase. This is definitively the case for the streams
85 # initialized by the interpreter. For unbuffered streams, we don't need
85 # initialized by the interpreter. For unbuffered streams, we don't need
86 # to emulate line buffering.
86 # to emulate line buffering.
87 return stream
87 return stream
88 if isinstance(stream, LineBufferedWrapper):
88 if isinstance(stream, LineBufferedWrapper):
89 return stream
89 return stream
90 return LineBufferedWrapper(stream)
90 return LineBufferedWrapper(stream)
91
91
92
92
93 def unwrap_line_buffered(stream):
93 def unwrap_line_buffered(stream):
94 if isinstance(stream, LineBufferedWrapper):
94 if isinstance(stream, LineBufferedWrapper):
95 assert not isinstance(stream.orig, LineBufferedWrapper)
95 assert not isinstance(stream.orig, LineBufferedWrapper)
96 return stream.orig
96 return stream.orig
97 return stream
97 return stream
98
98
99
99
100 class WriteAllWrapper(object):
100 class WriteAllWrapper(object):
101 def __init__(self, orig):
101 def __init__(self, orig):
102 self.orig = orig
102 self.orig = orig
103
103
104 def __getattr__(self, attr):
104 def __getattr__(self, attr):
105 return getattr(self.orig, attr)
105 return getattr(self.orig, attr)
106
106
107 def write(self, s):
107 def write(self, s):
108 write1 = self.orig.write
108 write1 = self.orig.write
109 m = memoryview(s)
109 m = memoryview(s)
110 total_to_write = len(s)
110 total_to_write = len(s)
111 total_written = 0
111 total_written = 0
112 while total_written < total_to_write:
112 while total_written < total_to_write:
113 total_written += write1(m[total_written:])
113 total_written += write1(m[total_written:])
114 return total_written
114 return total_written
115
115
116
116
117 io.IOBase.register(WriteAllWrapper)
117 io.IOBase.register(WriteAllWrapper)
118
118
119
119
120 def _make_write_all(stream):
120 def _make_write_all(stream):
121 assert pycompat.ispy3
121 assert pycompat.ispy3
122 if isinstance(stream, WriteAllWrapper):
122 if isinstance(stream, WriteAllWrapper):
123 return stream
123 return stream
124 if isinstance(stream, io.BufferedIOBase):
124 if isinstance(stream, io.BufferedIOBase):
125 # The io.BufferedIOBase.write() contract guarantees that all data is
125 # The io.BufferedIOBase.write() contract guarantees that all data is
126 # written.
126 # written.
127 return stream
127 return stream
128 # In general, the write() method of streams is free to write only part of
128 # In general, the write() method of streams is free to write only part of
129 # the data.
129 # the data.
130 return WriteAllWrapper(stream)
130 return WriteAllWrapper(stream)
131
131
132
132
133 if pycompat.ispy3:
133 if pycompat.ispy3:
134 # Python 3 implements its own I/O streams. Unlike stdio of C library,
134 # Python 3 implements its own I/O streams. Unlike stdio of C library,
135 # sys.stdin/stdout/stderr may be None if underlying fd is closed.
135 # sys.stdin/stdout/stderr may be None if underlying fd is closed.
136
136
137 # TODO: .buffer might not exist if std streams were replaced; we'll need
137 # TODO: .buffer might not exist if std streams were replaced; we'll need
138 # a silly wrapper to make a bytes stream backed by a unicode one.
138 # a silly wrapper to make a bytes stream backed by a unicode one.
139
139
140 if sys.stdin is None:
140 if sys.stdin is None:
141 stdin = BadFile()
141 stdin = BadFile()
142 else:
142 else:
143 stdin = sys.stdin.buffer
143 stdin = sys.stdin.buffer
144 if sys.stdout is None:
144 if sys.stdout is None:
145 stdout = BadFile()
145 stdout = BadFile()
146 else:
146 else:
147 stdout = _make_write_all(sys.stdout.buffer)
147 stdout = _make_write_all(sys.stdout.buffer)
148 if sys.stderr is None:
148 if sys.stderr is None:
149 stderr = BadFile()
149 stderr = BadFile()
150 else:
150 else:
151 stderr = _make_write_all(sys.stderr.buffer)
151 stderr = _make_write_all(sys.stderr.buffer)
152
152
153 if pycompat.iswindows:
153 if pycompat.iswindows:
154 # Work around Windows bugs.
154 # Work around Windows bugs.
155 stdout = platform.winstdout(stdout)
155 stdout = platform.winstdout(stdout) # pytype: disable=module-attr
156 stderr = platform.winstdout(stderr)
156 stderr = platform.winstdout(stderr) # pytype: disable=module-attr
157 if isatty(stdout):
157 if isatty(stdout):
158 # The standard library doesn't offer line-buffered binary streams.
158 # The standard library doesn't offer line-buffered binary streams.
159 stdout = make_line_buffered(stdout)
159 stdout = make_line_buffered(stdout)
160 else:
160 else:
161 # Python 2 uses the I/O streams provided by the C library.
161 # Python 2 uses the I/O streams provided by the C library.
162 stdin = sys.stdin
162 stdin = sys.stdin
163 stdout = sys.stdout
163 stdout = sys.stdout
164 stderr = sys.stderr
164 stderr = sys.stderr
165 if pycompat.iswindows:
165 if pycompat.iswindows:
166 # Work around Windows bugs.
166 # Work around Windows bugs.
167 stdout = platform.winstdout(stdout)
167 stdout = platform.winstdout(stdout) # pytype: disable=module-attr
168 stderr = platform.winstdout(stderr)
168 stderr = platform.winstdout(stderr) # pytype: disable=module-attr
169 if isatty(stdout):
169 if isatty(stdout):
170 if pycompat.iswindows:
170 if pycompat.iswindows:
171 # The Windows C runtime library doesn't support line buffering.
171 # The Windows C runtime library doesn't support line buffering.
172 stdout = make_line_buffered(stdout)
172 stdout = make_line_buffered(stdout)
173 else:
173 else:
174 # glibc determines buffering on first write to stdout - if we
174 # glibc determines buffering on first write to stdout - if we
175 # replace a TTY destined stdout with a pipe destined stdout (e.g.
175 # replace a TTY destined stdout with a pipe destined stdout (e.g.
176 # pager), we want line buffering.
176 # pager), we want line buffering.
177 stdout = os.fdopen(stdout.fileno(), 'wb', 1)
177 stdout = os.fdopen(stdout.fileno(), 'wb', 1)
178
178
179
179
180 findexe = platform.findexe
180 findexe = platform.findexe
181 _gethgcmd = platform.gethgcmd
181 _gethgcmd = platform.gethgcmd
182 getuser = platform.getuser
182 getuser = platform.getuser
183 getpid = os.getpid
183 getpid = os.getpid
184 hidewindow = platform.hidewindow
184 hidewindow = platform.hidewindow
185 readpipe = platform.readpipe
185 readpipe = platform.readpipe
186 setbinary = platform.setbinary
186 setbinary = platform.setbinary
187 setsignalhandler = platform.setsignalhandler
187 setsignalhandler = platform.setsignalhandler
188 shellquote = platform.shellquote
188 shellquote = platform.shellquote
189 shellsplit = platform.shellsplit
189 shellsplit = platform.shellsplit
190 spawndetached = platform.spawndetached
190 spawndetached = platform.spawndetached
191 sshargs = platform.sshargs
191 sshargs = platform.sshargs
192 testpid = platform.testpid
192 testpid = platform.testpid
193
193
194 try:
194 try:
195 setprocname = osutil.setprocname
195 setprocname = osutil.setprocname
196 except AttributeError:
196 except AttributeError:
197 pass
197 pass
198 try:
198 try:
199 unblocksignal = osutil.unblocksignal
199 unblocksignal = osutil.unblocksignal
200 except AttributeError:
200 except AttributeError:
201 pass
201 pass
202
202
203 closefds = pycompat.isposix
203 closefds = pycompat.isposix
204
204
205
205
206 def explainexit(code):
206 def explainexit(code):
207 """return a message describing a subprocess status
207 """return a message describing a subprocess status
208 (codes from kill are negative - not os.system/wait encoding)"""
208 (codes from kill are negative - not os.system/wait encoding)"""
209 if code >= 0:
209 if code >= 0:
210 return _(b"exited with status %d") % code
210 return _(b"exited with status %d") % code
211 return _(b"killed by signal %d") % -code
211 return _(b"killed by signal %d") % -code
212
212
213
213
214 class _pfile(object):
214 class _pfile(object):
215 """File-like wrapper for a stream opened by subprocess.Popen()"""
215 """File-like wrapper for a stream opened by subprocess.Popen()"""
216
216
217 def __init__(self, proc, fp):
217 def __init__(self, proc, fp):
218 self._proc = proc
218 self._proc = proc
219 self._fp = fp
219 self._fp = fp
220
220
221 def close(self):
221 def close(self):
222 # unlike os.popen(), this returns an integer in subprocess coding
222 # unlike os.popen(), this returns an integer in subprocess coding
223 self._fp.close()
223 self._fp.close()
224 return self._proc.wait()
224 return self._proc.wait()
225
225
226 def __iter__(self):
226 def __iter__(self):
227 return iter(self._fp)
227 return iter(self._fp)
228
228
229 def __getattr__(self, attr):
229 def __getattr__(self, attr):
230 return getattr(self._fp, attr)
230 return getattr(self._fp, attr)
231
231
232 def __enter__(self):
232 def __enter__(self):
233 return self
233 return self
234
234
235 def __exit__(self, exc_type, exc_value, exc_tb):
235 def __exit__(self, exc_type, exc_value, exc_tb):
236 self.close()
236 self.close()
237
237
238
238
239 def popen(cmd, mode=b'rb', bufsize=-1):
239 def popen(cmd, mode=b'rb', bufsize=-1):
240 if mode == b'rb':
240 if mode == b'rb':
241 return _popenreader(cmd, bufsize)
241 return _popenreader(cmd, bufsize)
242 elif mode == b'wb':
242 elif mode == b'wb':
243 return _popenwriter(cmd, bufsize)
243 return _popenwriter(cmd, bufsize)
244 raise error.ProgrammingError(b'unsupported mode: %r' % mode)
244 raise error.ProgrammingError(b'unsupported mode: %r' % mode)
245
245
246
246
247 def _popenreader(cmd, bufsize):
247 def _popenreader(cmd, bufsize):
248 p = subprocess.Popen(
248 p = subprocess.Popen(
249 tonativestr(cmd),
249 tonativestr(cmd),
250 shell=True,
250 shell=True,
251 bufsize=bufsize,
251 bufsize=bufsize,
252 close_fds=closefds,
252 close_fds=closefds,
253 stdout=subprocess.PIPE,
253 stdout=subprocess.PIPE,
254 )
254 )
255 return _pfile(p, p.stdout)
255 return _pfile(p, p.stdout)
256
256
257
257
258 def _popenwriter(cmd, bufsize):
258 def _popenwriter(cmd, bufsize):
259 p = subprocess.Popen(
259 p = subprocess.Popen(
260 tonativestr(cmd),
260 tonativestr(cmd),
261 shell=True,
261 shell=True,
262 bufsize=bufsize,
262 bufsize=bufsize,
263 close_fds=closefds,
263 close_fds=closefds,
264 stdin=subprocess.PIPE,
264 stdin=subprocess.PIPE,
265 )
265 )
266 return _pfile(p, p.stdin)
266 return _pfile(p, p.stdin)
267
267
268
268
269 def popen2(cmd, env=None):
269 def popen2(cmd, env=None):
270 # Setting bufsize to -1 lets the system decide the buffer size.
270 # Setting bufsize to -1 lets the system decide the buffer size.
271 # The default for bufsize is 0, meaning unbuffered. This leads to
271 # The default for bufsize is 0, meaning unbuffered. This leads to
272 # poor performance on Mac OS X: http://bugs.python.org/issue4194
272 # poor performance on Mac OS X: http://bugs.python.org/issue4194
273 p = subprocess.Popen(
273 p = subprocess.Popen(
274 tonativestr(cmd),
274 tonativestr(cmd),
275 shell=True,
275 shell=True,
276 bufsize=-1,
276 bufsize=-1,
277 close_fds=closefds,
277 close_fds=closefds,
278 stdin=subprocess.PIPE,
278 stdin=subprocess.PIPE,
279 stdout=subprocess.PIPE,
279 stdout=subprocess.PIPE,
280 env=tonativeenv(env),
280 env=tonativeenv(env),
281 )
281 )
282 return p.stdin, p.stdout
282 return p.stdin, p.stdout
283
283
284
284
285 def popen3(cmd, env=None):
285 def popen3(cmd, env=None):
286 stdin, stdout, stderr, p = popen4(cmd, env)
286 stdin, stdout, stderr, p = popen4(cmd, env)
287 return stdin, stdout, stderr
287 return stdin, stdout, stderr
288
288
289
289
290 def popen4(cmd, env=None, bufsize=-1):
290 def popen4(cmd, env=None, bufsize=-1):
291 p = subprocess.Popen(
291 p = subprocess.Popen(
292 tonativestr(cmd),
292 tonativestr(cmd),
293 shell=True,
293 shell=True,
294 bufsize=bufsize,
294 bufsize=bufsize,
295 close_fds=closefds,
295 close_fds=closefds,
296 stdin=subprocess.PIPE,
296 stdin=subprocess.PIPE,
297 stdout=subprocess.PIPE,
297 stdout=subprocess.PIPE,
298 stderr=subprocess.PIPE,
298 stderr=subprocess.PIPE,
299 env=tonativeenv(env),
299 env=tonativeenv(env),
300 )
300 )
301 return p.stdin, p.stdout, p.stderr, p
301 return p.stdin, p.stdout, p.stderr, p
302
302
303
303
304 def pipefilter(s, cmd):
304 def pipefilter(s, cmd):
305 '''filter string S through command CMD, returning its output'''
305 '''filter string S through command CMD, returning its output'''
306 p = subprocess.Popen(
306 p = subprocess.Popen(
307 tonativestr(cmd),
307 tonativestr(cmd),
308 shell=True,
308 shell=True,
309 close_fds=closefds,
309 close_fds=closefds,
310 stdin=subprocess.PIPE,
310 stdin=subprocess.PIPE,
311 stdout=subprocess.PIPE,
311 stdout=subprocess.PIPE,
312 )
312 )
313 pout, perr = p.communicate(s)
313 pout, perr = p.communicate(s)
314 return pout
314 return pout
315
315
316
316
317 def tempfilter(s, cmd):
317 def tempfilter(s, cmd):
318 """filter string S through a pair of temporary files with CMD.
318 """filter string S through a pair of temporary files with CMD.
319 CMD is used as a template to create the real command to be run,
319 CMD is used as a template to create the real command to be run,
320 with the strings INFILE and OUTFILE replaced by the real names of
320 with the strings INFILE and OUTFILE replaced by the real names of
321 the temporary files generated."""
321 the temporary files generated."""
322 inname, outname = None, None
322 inname, outname = None, None
323 try:
323 try:
324 infd, inname = pycompat.mkstemp(prefix=b'hg-filter-in-')
324 infd, inname = pycompat.mkstemp(prefix=b'hg-filter-in-')
325 fp = os.fdopen(infd, 'wb')
325 fp = os.fdopen(infd, 'wb')
326 fp.write(s)
326 fp.write(s)
327 fp.close()
327 fp.close()
328 outfd, outname = pycompat.mkstemp(prefix=b'hg-filter-out-')
328 outfd, outname = pycompat.mkstemp(prefix=b'hg-filter-out-')
329 os.close(outfd)
329 os.close(outfd)
330 cmd = cmd.replace(b'INFILE', inname)
330 cmd = cmd.replace(b'INFILE', inname)
331 cmd = cmd.replace(b'OUTFILE', outname)
331 cmd = cmd.replace(b'OUTFILE', outname)
332 code = system(cmd)
332 code = system(cmd)
333 if pycompat.sysplatform == b'OpenVMS' and code & 1:
333 if pycompat.sysplatform == b'OpenVMS' and code & 1:
334 code = 0
334 code = 0
335 if code:
335 if code:
336 raise error.Abort(
336 raise error.Abort(
337 _(b"command '%s' failed: %s") % (cmd, explainexit(code))
337 _(b"command '%s' failed: %s") % (cmd, explainexit(code))
338 )
338 )
339 with open(outname, b'rb') as fp:
339 with open(outname, b'rb') as fp:
340 return fp.read()
340 return fp.read()
341 finally:
341 finally:
342 try:
342 try:
343 if inname:
343 if inname:
344 os.unlink(inname)
344 os.unlink(inname)
345 except OSError:
345 except OSError:
346 pass
346 pass
347 try:
347 try:
348 if outname:
348 if outname:
349 os.unlink(outname)
349 os.unlink(outname)
350 except OSError:
350 except OSError:
351 pass
351 pass
352
352
353
353
354 _filtertable = {
354 _filtertable = {
355 b'tempfile:': tempfilter,
355 b'tempfile:': tempfilter,
356 b'pipe:': pipefilter,
356 b'pipe:': pipefilter,
357 }
357 }
358
358
359
359
360 def filter(s, cmd):
360 def filter(s, cmd):
361 """filter a string through a command that transforms its input to its
361 """filter a string through a command that transforms its input to its
362 output"""
362 output"""
363 for name, fn in pycompat.iteritems(_filtertable):
363 for name, fn in pycompat.iteritems(_filtertable):
364 if cmd.startswith(name):
364 if cmd.startswith(name):
365 return fn(s, cmd[len(name) :].lstrip())
365 return fn(s, cmd[len(name) :].lstrip())
366 return pipefilter(s, cmd)
366 return pipefilter(s, cmd)
367
367
368
368
369 _hgexecutable = None
369 _hgexecutable = None
370
370
371
371
372 def hgexecutable():
372 def hgexecutable():
373 """return location of the 'hg' executable.
373 """return location of the 'hg' executable.
374
374
375 Defaults to $HG or 'hg' in the search path.
375 Defaults to $HG or 'hg' in the search path.
376 """
376 """
377 if _hgexecutable is None:
377 if _hgexecutable is None:
378 hg = encoding.environ.get(b'HG')
378 hg = encoding.environ.get(b'HG')
379 mainmod = sys.modules['__main__']
379 mainmod = sys.modules['__main__']
380 if hg:
380 if hg:
381 _sethgexecutable(hg)
381 _sethgexecutable(hg)
382 elif resourceutil.mainfrozen():
382 elif resourceutil.mainfrozen():
383 if getattr(sys, 'frozen', None) == 'macosx_app':
383 if getattr(sys, 'frozen', None) == 'macosx_app':
384 # Env variable set by py2app
384 # Env variable set by py2app
385 _sethgexecutable(encoding.environ[b'EXECUTABLEPATH'])
385 _sethgexecutable(encoding.environ[b'EXECUTABLEPATH'])
386 else:
386 else:
387 _sethgexecutable(pycompat.sysexecutable)
387 _sethgexecutable(pycompat.sysexecutable)
388 elif (
388 elif (
389 not pycompat.iswindows
389 not pycompat.iswindows
390 and os.path.basename(getattr(mainmod, '__file__', '')) == 'hg'
390 and os.path.basename(getattr(mainmod, '__file__', '')) == 'hg'
391 ):
391 ):
392 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
392 _sethgexecutable(pycompat.fsencode(mainmod.__file__))
393 else:
393 else:
394 _sethgexecutable(
394 _sethgexecutable(
395 findexe(b'hg') or os.path.basename(pycompat.sysargv[0])
395 findexe(b'hg') or os.path.basename(pycompat.sysargv[0])
396 )
396 )
397 return _hgexecutable
397 return _hgexecutable
398
398
399
399
400 def _sethgexecutable(path):
400 def _sethgexecutable(path):
401 """set location of the 'hg' executable"""
401 """set location of the 'hg' executable"""
402 global _hgexecutable
402 global _hgexecutable
403 _hgexecutable = path
403 _hgexecutable = path
404
404
405
405
406 def _testfileno(f, stdf):
406 def _testfileno(f, stdf):
407 fileno = getattr(f, 'fileno', None)
407 fileno = getattr(f, 'fileno', None)
408 try:
408 try:
409 return fileno and fileno() == stdf.fileno()
409 return fileno and fileno() == stdf.fileno()
410 except io.UnsupportedOperation:
410 except io.UnsupportedOperation:
411 return False # fileno() raised UnsupportedOperation
411 return False # fileno() raised UnsupportedOperation
412
412
413
413
414 def isstdin(f):
414 def isstdin(f):
415 return _testfileno(f, sys.__stdin__)
415 return _testfileno(f, sys.__stdin__)
416
416
417
417
418 def isstdout(f):
418 def isstdout(f):
419 return _testfileno(f, sys.__stdout__)
419 return _testfileno(f, sys.__stdout__)
420
420
421
421
422 def protectstdio(uin, uout):
422 def protectstdio(uin, uout):
423 """Duplicate streams and redirect original if (uin, uout) are stdio
423 """Duplicate streams and redirect original if (uin, uout) are stdio
424
424
425 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's
425 If uin is stdin, it's redirected to /dev/null. If uout is stdout, it's
426 redirected to stderr so the output is still readable.
426 redirected to stderr so the output is still readable.
427
427
428 Returns (fin, fout) which point to the original (uin, uout) fds, but
428 Returns (fin, fout) which point to the original (uin, uout) fds, but
429 may be copy of (uin, uout). The returned streams can be considered
429 may be copy of (uin, uout). The returned streams can be considered
430 "owned" in that print(), exec(), etc. never reach to them.
430 "owned" in that print(), exec(), etc. never reach to them.
431 """
431 """
432 uout.flush()
432 uout.flush()
433 fin, fout = uin, uout
433 fin, fout = uin, uout
434 if _testfileno(uin, stdin):
434 if _testfileno(uin, stdin):
435 newfd = os.dup(uin.fileno())
435 newfd = os.dup(uin.fileno())
436 nullfd = os.open(os.devnull, os.O_RDONLY)
436 nullfd = os.open(os.devnull, os.O_RDONLY)
437 os.dup2(nullfd, uin.fileno())
437 os.dup2(nullfd, uin.fileno())
438 os.close(nullfd)
438 os.close(nullfd)
439 fin = os.fdopen(newfd, 'rb')
439 fin = os.fdopen(newfd, 'rb')
440 if _testfileno(uout, stdout):
440 if _testfileno(uout, stdout):
441 newfd = os.dup(uout.fileno())
441 newfd = os.dup(uout.fileno())
442 os.dup2(stderr.fileno(), uout.fileno())
442 os.dup2(stderr.fileno(), uout.fileno())
443 fout = os.fdopen(newfd, 'wb')
443 fout = os.fdopen(newfd, 'wb')
444 return fin, fout
444 return fin, fout
445
445
446
446
447 def restorestdio(uin, uout, fin, fout):
447 def restorestdio(uin, uout, fin, fout):
448 """Restore (uin, uout) streams from possibly duplicated (fin, fout)"""
448 """Restore (uin, uout) streams from possibly duplicated (fin, fout)"""
449 uout.flush()
449 uout.flush()
450 for f, uif in [(fin, uin), (fout, uout)]:
450 for f, uif in [(fin, uin), (fout, uout)]:
451 if f is not uif:
451 if f is not uif:
452 os.dup2(f.fileno(), uif.fileno())
452 os.dup2(f.fileno(), uif.fileno())
453 f.close()
453 f.close()
454
454
455
455
456 def shellenviron(environ=None):
456 def shellenviron(environ=None):
457 """return environ with optional override, useful for shelling out"""
457 """return environ with optional override, useful for shelling out"""
458
458
459 def py2shell(val):
459 def py2shell(val):
460 """convert python object into string that is useful to shell"""
460 """convert python object into string that is useful to shell"""
461 if val is None or val is False:
461 if val is None or val is False:
462 return b'0'
462 return b'0'
463 if val is True:
463 if val is True:
464 return b'1'
464 return b'1'
465 return pycompat.bytestr(val)
465 return pycompat.bytestr(val)
466
466
467 env = dict(encoding.environ)
467 env = dict(encoding.environ)
468 if environ:
468 if environ:
469 env.update((k, py2shell(v)) for k, v in pycompat.iteritems(environ))
469 env.update((k, py2shell(v)) for k, v in pycompat.iteritems(environ))
470 env[b'HG'] = hgexecutable()
470 env[b'HG'] = hgexecutable()
471 return env
471 return env
472
472
473
473
474 if pycompat.iswindows:
474 if pycompat.iswindows:
475
475
476 def shelltonative(cmd, env):
476 def shelltonative(cmd, env):
477 return platform.shelltocmdexe( # pytype: disable=module-attr
477 return platform.shelltocmdexe( # pytype: disable=module-attr
478 cmd, shellenviron(env)
478 cmd, shellenviron(env)
479 )
479 )
480
480
481 tonativestr = encoding.strfromlocal
481 tonativestr = encoding.strfromlocal
482 else:
482 else:
483
483
484 def shelltonative(cmd, env):
484 def shelltonative(cmd, env):
485 return cmd
485 return cmd
486
486
487 tonativestr = pycompat.identity
487 tonativestr = pycompat.identity
488
488
489
489
490 def tonativeenv(env):
490 def tonativeenv(env):
491 """convert the environment from bytes to strings suitable for Popen(), etc."""
491 """convert the environment from bytes to strings suitable for Popen(), etc."""
492 return pycompat.rapply(tonativestr, env)
492 return pycompat.rapply(tonativestr, env)
493
493
494
494
495 def system(cmd, environ=None, cwd=None, out=None):
495 def system(cmd, environ=None, cwd=None, out=None):
496 """enhanced shell command execution.
496 """enhanced shell command execution.
497 run with environment maybe modified, maybe in different dir.
497 run with environment maybe modified, maybe in different dir.
498
498
499 if out is specified, it is assumed to be a file-like object that has a
499 if out is specified, it is assumed to be a file-like object that has a
500 write() method. stdout and stderr will be redirected to out."""
500 write() method. stdout and stderr will be redirected to out."""
501 try:
501 try:
502 stdout.flush()
502 stdout.flush()
503 except Exception:
503 except Exception:
504 pass
504 pass
505 env = shellenviron(environ)
505 env = shellenviron(environ)
506 if out is None or isstdout(out):
506 if out is None or isstdout(out):
507 rc = subprocess.call(
507 rc = subprocess.call(
508 tonativestr(cmd),
508 tonativestr(cmd),
509 shell=True,
509 shell=True,
510 close_fds=closefds,
510 close_fds=closefds,
511 env=tonativeenv(env),
511 env=tonativeenv(env),
512 cwd=pycompat.rapply(tonativestr, cwd),
512 cwd=pycompat.rapply(tonativestr, cwd),
513 )
513 )
514 else:
514 else:
515 proc = subprocess.Popen(
515 proc = subprocess.Popen(
516 tonativestr(cmd),
516 tonativestr(cmd),
517 shell=True,
517 shell=True,
518 close_fds=closefds,
518 close_fds=closefds,
519 env=tonativeenv(env),
519 env=tonativeenv(env),
520 cwd=pycompat.rapply(tonativestr, cwd),
520 cwd=pycompat.rapply(tonativestr, cwd),
521 stdout=subprocess.PIPE,
521 stdout=subprocess.PIPE,
522 stderr=subprocess.STDOUT,
522 stderr=subprocess.STDOUT,
523 )
523 )
524 for line in iter(proc.stdout.readline, b''):
524 for line in iter(proc.stdout.readline, b''):
525 out.write(line)
525 out.write(line)
526 proc.wait()
526 proc.wait()
527 rc = proc.returncode
527 rc = proc.returncode
528 if pycompat.sysplatform == b'OpenVMS' and rc & 1:
528 if pycompat.sysplatform == b'OpenVMS' and rc & 1:
529 rc = 0
529 rc = 0
530 return rc
530 return rc
531
531
532
532
533 _is_gui = None
533 _is_gui = None
534
534
535
535
536 def _gui():
536 def _gui():
537 '''Are we running in a GUI?'''
537 '''Are we running in a GUI?'''
538 if pycompat.isdarwin:
538 if pycompat.isdarwin:
539 if b'SSH_CONNECTION' in encoding.environ:
539 if b'SSH_CONNECTION' in encoding.environ:
540 # handle SSH access to a box where the user is logged in
540 # handle SSH access to a box where the user is logged in
541 return False
541 return False
542 elif getattr(osutil, 'isgui', None):
542 elif getattr(osutil, 'isgui', None):
543 # check if a CoreGraphics session is available
543 # check if a CoreGraphics session is available
544 return osutil.isgui()
544 return osutil.isgui()
545 else:
545 else:
546 # pure build; use a safe default
546 # pure build; use a safe default
547 return True
547 return True
548 else:
548 else:
549 return (
549 return (
550 pycompat.iswindows
550 pycompat.iswindows
551 or encoding.environ.get(b"DISPLAY")
551 or encoding.environ.get(b"DISPLAY")
552 or encoding.environ.get(b"WAYLAND_DISPLAY")
552 or encoding.environ.get(b"WAYLAND_DISPLAY")
553 )
553 )
554
554
555
555
556 def gui():
556 def gui():
557 global _is_gui
557 global _is_gui
558 if _is_gui is None:
558 if _is_gui is None:
559 _is_gui = _gui()
559 _is_gui = _gui()
560 return _is_gui
560 return _is_gui
561
561
562
562
563 def hgcmd():
563 def hgcmd():
564 """Return the command used to execute current hg
564 """Return the command used to execute current hg
565
565
566 This is different from hgexecutable() because on Windows we want
566 This is different from hgexecutable() because on Windows we want
567 to avoid things opening new shell windows like batch files, so we
567 to avoid things opening new shell windows like batch files, so we
568 get either the python call or current executable.
568 get either the python call or current executable.
569 """
569 """
570 if resourceutil.mainfrozen():
570 if resourceutil.mainfrozen():
571 if getattr(sys, 'frozen', None) == 'macosx_app':
571 if getattr(sys, 'frozen', None) == 'macosx_app':
572 # Env variable set by py2app
572 # Env variable set by py2app
573 return [encoding.environ[b'EXECUTABLEPATH']]
573 return [encoding.environ[b'EXECUTABLEPATH']]
574 else:
574 else:
575 return [pycompat.sysexecutable]
575 return [pycompat.sysexecutable]
576 return _gethgcmd()
576 return _gethgcmd()
577
577
578
578
579 def rundetached(args, condfn):
579 def rundetached(args, condfn):
580 """Execute the argument list in a detached process.
580 """Execute the argument list in a detached process.
581
581
582 condfn is a callable which is called repeatedly and should return
582 condfn is a callable which is called repeatedly and should return
583 True once the child process is known to have started successfully.
583 True once the child process is known to have started successfully.
584 At this point, the child process PID is returned. If the child
584 At this point, the child process PID is returned. If the child
585 process fails to start or finishes before condfn() evaluates to
585 process fails to start or finishes before condfn() evaluates to
586 True, return -1.
586 True, return -1.
587 """
587 """
588 # Windows case is easier because the child process is either
588 # Windows case is easier because the child process is either
589 # successfully starting and validating the condition or exiting
589 # successfully starting and validating the condition or exiting
590 # on failure. We just poll on its PID. On Unix, if the child
590 # on failure. We just poll on its PID. On Unix, if the child
591 # process fails to start, it will be left in a zombie state until
591 # process fails to start, it will be left in a zombie state until
592 # the parent wait on it, which we cannot do since we expect a long
592 # the parent wait on it, which we cannot do since we expect a long
593 # running process on success. Instead we listen for SIGCHLD telling
593 # running process on success. Instead we listen for SIGCHLD telling
594 # us our child process terminated.
594 # us our child process terminated.
595 terminated = set()
595 terminated = set()
596
596
597 def handler(signum, frame):
597 def handler(signum, frame):
598 terminated.add(os.wait())
598 terminated.add(os.wait())
599
599
600 prevhandler = None
600 prevhandler = None
601 SIGCHLD = getattr(signal, 'SIGCHLD', None)
601 SIGCHLD = getattr(signal, 'SIGCHLD', None)
602 if SIGCHLD is not None:
602 if SIGCHLD is not None:
603 prevhandler = signal.signal(SIGCHLD, handler)
603 prevhandler = signal.signal(SIGCHLD, handler)
604 try:
604 try:
605 pid = spawndetached(args)
605 pid = spawndetached(args)
606 while not condfn():
606 while not condfn():
607 if (pid in terminated or not testpid(pid)) and not condfn():
607 if (pid in terminated or not testpid(pid)) and not condfn():
608 return -1
608 return -1
609 time.sleep(0.1)
609 time.sleep(0.1)
610 return pid
610 return pid
611 finally:
611 finally:
612 if prevhandler is not None:
612 if prevhandler is not None:
613 signal.signal(signal.SIGCHLD, prevhandler)
613 signal.signal(signal.SIGCHLD, prevhandler)
614
614
615
615
616 @contextlib.contextmanager
616 @contextlib.contextmanager
617 def uninterruptible(warn):
617 def uninterruptible(warn):
618 """Inhibit SIGINT handling on a region of code.
618 """Inhibit SIGINT handling on a region of code.
619
619
620 Note that if this is called in a non-main thread, it turns into a no-op.
620 Note that if this is called in a non-main thread, it turns into a no-op.
621
621
622 Args:
622 Args:
623 warn: A callable which takes no arguments, and returns True if the
623 warn: A callable which takes no arguments, and returns True if the
624 previous signal handling should be restored.
624 previous signal handling should be restored.
625 """
625 """
626
626
627 oldsiginthandler = [signal.getsignal(signal.SIGINT)]
627 oldsiginthandler = [signal.getsignal(signal.SIGINT)]
628 shouldbail = []
628 shouldbail = []
629
629
630 def disabledsiginthandler(*args):
630 def disabledsiginthandler(*args):
631 if warn():
631 if warn():
632 signal.signal(signal.SIGINT, oldsiginthandler[0])
632 signal.signal(signal.SIGINT, oldsiginthandler[0])
633 del oldsiginthandler[0]
633 del oldsiginthandler[0]
634 shouldbail.append(True)
634 shouldbail.append(True)
635
635
636 try:
636 try:
637 try:
637 try:
638 signal.signal(signal.SIGINT, disabledsiginthandler)
638 signal.signal(signal.SIGINT, disabledsiginthandler)
639 except ValueError:
639 except ValueError:
640 # wrong thread, oh well, we tried
640 # wrong thread, oh well, we tried
641 del oldsiginthandler[0]
641 del oldsiginthandler[0]
642 yield
642 yield
643 finally:
643 finally:
644 if oldsiginthandler:
644 if oldsiginthandler:
645 signal.signal(signal.SIGINT, oldsiginthandler[0])
645 signal.signal(signal.SIGINT, oldsiginthandler[0])
646 if shouldbail:
646 if shouldbail:
647 raise KeyboardInterrupt
647 raise KeyboardInterrupt
648
648
649
649
650 if pycompat.iswindows:
650 if pycompat.iswindows:
651 # no fork on Windows, but we can create a detached process
651 # no fork on Windows, but we can create a detached process
652 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx
652 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx
653 # No stdlib constant exists for this value
653 # No stdlib constant exists for this value
654 DETACHED_PROCESS = 0x00000008
654 DETACHED_PROCESS = 0x00000008
655 # Following creation flags might create a console GUI window.
655 # Following creation flags might create a console GUI window.
656 # Using subprocess.CREATE_NEW_CONSOLE might helps.
656 # Using subprocess.CREATE_NEW_CONSOLE might helps.
657 # See https://phab.mercurial-scm.org/D1701 for discussion
657 # See https://phab.mercurial-scm.org/D1701 for discussion
658 _creationflags = (
658 _creationflags = (
659 DETACHED_PROCESS
659 DETACHED_PROCESS
660 | subprocess.CREATE_NEW_PROCESS_GROUP # pytype: disable=module-attr
660 | subprocess.CREATE_NEW_PROCESS_GROUP # pytype: disable=module-attr
661 )
661 )
662
662
663 def runbgcommand(
663 def runbgcommand(
664 script,
664 script,
665 env,
665 env,
666 shell=False,
666 shell=False,
667 stdout=None,
667 stdout=None,
668 stderr=None,
668 stderr=None,
669 ensurestart=True,
669 ensurestart=True,
670 record_wait=None,
670 record_wait=None,
671 stdin_bytes=None,
671 stdin_bytes=None,
672 ):
672 ):
673 '''Spawn a command without waiting for it to finish.'''
673 '''Spawn a command without waiting for it to finish.'''
674 # we can't use close_fds *and* redirect stdin. I'm not sure that we
674 # we can't use close_fds *and* redirect stdin. I'm not sure that we
675 # need to because the detached process has no console connection.
675 # need to because the detached process has no console connection.
676
676
677 try:
677 try:
678 stdin = None
678 stdin = None
679 if stdin_bytes is not None:
679 if stdin_bytes is not None:
680 stdin = pycompat.unnamedtempfile()
680 stdin = pycompat.unnamedtempfile()
681 stdin.write(stdin_bytes)
681 stdin.write(stdin_bytes)
682 stdin.flush()
682 stdin.flush()
683 stdin.seek(0)
683 stdin.seek(0)
684
684
685 p = subprocess.Popen(
685 p = subprocess.Popen(
686 pycompat.rapply(tonativestr, script),
686 pycompat.rapply(tonativestr, script),
687 shell=shell,
687 shell=shell,
688 env=tonativeenv(env),
688 env=tonativeenv(env),
689 close_fds=True,
689 close_fds=True,
690 creationflags=_creationflags,
690 creationflags=_creationflags,
691 stdin=stdin,
691 stdin=stdin,
692 stdout=stdout,
692 stdout=stdout,
693 stderr=stderr,
693 stderr=stderr,
694 )
694 )
695 if record_wait is not None:
695 if record_wait is not None:
696 record_wait(p.wait)
696 record_wait(p.wait)
697 finally:
697 finally:
698 if stdin is not None:
698 if stdin is not None:
699 stdin.close()
699 stdin.close()
700
700
701
701
702 else:
702 else:
703
703
704 def runbgcommand(
704 def runbgcommand(
705 cmd,
705 cmd,
706 env,
706 env,
707 shell=False,
707 shell=False,
708 stdout=None,
708 stdout=None,
709 stderr=None,
709 stderr=None,
710 ensurestart=True,
710 ensurestart=True,
711 record_wait=None,
711 record_wait=None,
712 stdin_bytes=None,
712 stdin_bytes=None,
713 ):
713 ):
714 """Spawn a command without waiting for it to finish.
714 """Spawn a command without waiting for it to finish.
715
715
716
716
717 When `record_wait` is not None, the spawned process will not be fully
717 When `record_wait` is not None, the spawned process will not be fully
718 detached and the `record_wait` argument will be called with a the
718 detached and the `record_wait` argument will be called with a the
719 `Subprocess.wait` function for the spawned process. This is mostly
719 `Subprocess.wait` function for the spawned process. This is mostly
720 useful for developers that need to make sure the spawned process
720 useful for developers that need to make sure the spawned process
721 finished before a certain point. (eg: writing test)"""
721 finished before a certain point. (eg: writing test)"""
722 if pycompat.isdarwin:
722 if pycompat.isdarwin:
723 # avoid crash in CoreFoundation in case another thread
723 # avoid crash in CoreFoundation in case another thread
724 # calls gui() while we're calling fork().
724 # calls gui() while we're calling fork().
725 gui()
725 gui()
726
726
727 # double-fork to completely detach from the parent process
727 # double-fork to completely detach from the parent process
728 # based on http://code.activestate.com/recipes/278731
728 # based on http://code.activestate.com/recipes/278731
729 if record_wait is None:
729 if record_wait is None:
730 pid = os.fork()
730 pid = os.fork()
731 if pid:
731 if pid:
732 if not ensurestart:
732 if not ensurestart:
733 # Even though we're not waiting on the child process,
733 # Even though we're not waiting on the child process,
734 # we still must call waitpid() on it at some point so
734 # we still must call waitpid() on it at some point so
735 # it's not a zombie/defunct. This is especially relevant for
735 # it's not a zombie/defunct. This is especially relevant for
736 # chg since the parent process won't die anytime soon.
736 # chg since the parent process won't die anytime soon.
737 # We use a thread to make the overhead tiny.
737 # We use a thread to make the overhead tiny.
738 def _do_wait():
738 def _do_wait():
739 os.waitpid(pid, 0)
739 os.waitpid(pid, 0)
740
740
741 t = threading.Thread(target=_do_wait)
741 t = threading.Thread(target=_do_wait)
742 t.daemon = True
742 t.daemon = True
743 t.start()
743 t.start()
744 return
744 return
745 # Parent process
745 # Parent process
746 (_pid, status) = os.waitpid(pid, 0)
746 (_pid, status) = os.waitpid(pid, 0)
747 if os.WIFEXITED(status):
747 if os.WIFEXITED(status):
748 returncode = os.WEXITSTATUS(status)
748 returncode = os.WEXITSTATUS(status)
749 else:
749 else:
750 returncode = -(os.WTERMSIG(status))
750 returncode = -(os.WTERMSIG(status))
751 if returncode != 0:
751 if returncode != 0:
752 # The child process's return code is 0 on success, an errno
752 # The child process's return code is 0 on success, an errno
753 # value on failure, or 255 if we don't have a valid errno
753 # value on failure, or 255 if we don't have a valid errno
754 # value.
754 # value.
755 #
755 #
756 # (It would be slightly nicer to return the full exception info
756 # (It would be slightly nicer to return the full exception info
757 # over a pipe as the subprocess module does. For now it
757 # over a pipe as the subprocess module does. For now it
758 # doesn't seem worth adding that complexity here, though.)
758 # doesn't seem worth adding that complexity here, though.)
759 if returncode == 255:
759 if returncode == 255:
760 returncode = errno.EINVAL
760 returncode = errno.EINVAL
761 raise OSError(
761 raise OSError(
762 returncode,
762 returncode,
763 b'error running %r: %s'
763 b'error running %r: %s'
764 % (cmd, os.strerror(returncode)),
764 % (cmd, os.strerror(returncode)),
765 )
765 )
766 return
766 return
767
767
768 returncode = 255
768 returncode = 255
769 try:
769 try:
770 if record_wait is None:
770 if record_wait is None:
771 # Start a new session
771 # Start a new session
772 os.setsid()
772 os.setsid()
773 # connect stdin to devnull to make sure the subprocess can't
773 # connect stdin to devnull to make sure the subprocess can't
774 # muck up that stream for mercurial.
774 # muck up that stream for mercurial.
775 if stdin_bytes is None:
775 if stdin_bytes is None:
776 stdin = open(os.devnull, b'r')
776 stdin = open(os.devnull, b'r')
777 else:
777 else:
778 stdin = pycompat.unnamedtempfile()
778 stdin = pycompat.unnamedtempfile()
779 stdin.write(stdin_bytes)
779 stdin.write(stdin_bytes)
780 stdin.flush()
780 stdin.flush()
781 stdin.seek(0)
781 stdin.seek(0)
782
782
783 if stdout is None:
783 if stdout is None:
784 stdout = open(os.devnull, b'w')
784 stdout = open(os.devnull, b'w')
785 if stderr is None:
785 if stderr is None:
786 stderr = open(os.devnull, b'w')
786 stderr = open(os.devnull, b'w')
787
787
788 p = subprocess.Popen(
788 p = subprocess.Popen(
789 cmd,
789 cmd,
790 shell=shell,
790 shell=shell,
791 env=env,
791 env=env,
792 close_fds=True,
792 close_fds=True,
793 stdin=stdin,
793 stdin=stdin,
794 stdout=stdout,
794 stdout=stdout,
795 stderr=stderr,
795 stderr=stderr,
796 )
796 )
797 if record_wait is not None:
797 if record_wait is not None:
798 record_wait(p.wait)
798 record_wait(p.wait)
799 returncode = 0
799 returncode = 0
800 except EnvironmentError as ex:
800 except EnvironmentError as ex:
801 returncode = ex.errno & 0xFF
801 returncode = ex.errno & 0xFF
802 if returncode == 0:
802 if returncode == 0:
803 # This shouldn't happen, but just in case make sure the
803 # This shouldn't happen, but just in case make sure the
804 # return code is never 0 here.
804 # return code is never 0 here.
805 returncode = 255
805 returncode = 255
806 except Exception:
806 except Exception:
807 returncode = 255
807 returncode = 255
808 finally:
808 finally:
809 # mission accomplished, this child needs to exit and not
809 # mission accomplished, this child needs to exit and not
810 # continue the hg process here.
810 # continue the hg process here.
811 stdin.close()
811 stdin.close()
812 if record_wait is None:
812 if record_wait is None:
813 os._exit(returncode)
813 os._exit(returncode)
@@ -1,756 +1,766 b''
1 # win32.py - utility functions that use win32 API
1 # win32.py - utility functions that use win32 API
2 #
2 #
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from __future__ import absolute_import
8 from __future__ import absolute_import
9
9
10 import ctypes
10 import ctypes
11 import ctypes.wintypes as wintypes
11 import ctypes.wintypes as wintypes
12 import errno
12 import errno
13 import msvcrt
13 import msvcrt
14 import os
14 import os
15 import random
15 import random
16 import subprocess
16 import subprocess
17
17
18 from . import (
18 from . import (
19 encoding,
19 encoding,
20 pycompat,
20 pycompat,
21 )
21 )
22
22
23 # pytype: disable=module-attr
23 _kernel32 = ctypes.windll.kernel32
24 _kernel32 = ctypes.windll.kernel32
24 _advapi32 = ctypes.windll.advapi32
25 _advapi32 = ctypes.windll.advapi32
25 _user32 = ctypes.windll.user32
26 _user32 = ctypes.windll.user32
26 _crypt32 = ctypes.windll.crypt32
27 _crypt32 = ctypes.windll.crypt32
28 # pytype: enable=module-attr
27
29
28 _BOOL = ctypes.c_long
30 _BOOL = ctypes.c_long
29 _WORD = ctypes.c_ushort
31 _WORD = ctypes.c_ushort
30 _DWORD = ctypes.c_ulong
32 _DWORD = ctypes.c_ulong
31 _UINT = ctypes.c_uint
33 _UINT = ctypes.c_uint
32 _LONG = ctypes.c_long
34 _LONG = ctypes.c_long
33 _LPCSTR = _LPSTR = ctypes.c_char_p
35 _LPCSTR = _LPSTR = ctypes.c_char_p
34 _HANDLE = ctypes.c_void_p
36 _HANDLE = ctypes.c_void_p
35 _HWND = _HANDLE
37 _HWND = _HANDLE
36 _PCCERT_CONTEXT = ctypes.c_void_p
38 _PCCERT_CONTEXT = ctypes.c_void_p
37 _MAX_PATH = wintypes.MAX_PATH
39 _MAX_PATH = wintypes.MAX_PATH
38
40
39 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
41 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
40
42
41 # GetLastError
43 # GetLastError
42 _ERROR_SUCCESS = 0
44 _ERROR_SUCCESS = 0
43 _ERROR_NO_MORE_FILES = 18
45 _ERROR_NO_MORE_FILES = 18
44 _ERROR_INVALID_PARAMETER = 87
46 _ERROR_INVALID_PARAMETER = 87
45 _ERROR_BROKEN_PIPE = 109
47 _ERROR_BROKEN_PIPE = 109
46 _ERROR_INSUFFICIENT_BUFFER = 122
48 _ERROR_INSUFFICIENT_BUFFER = 122
47 _ERROR_NO_DATA = 232
49 _ERROR_NO_DATA = 232
48
50
49 # WPARAM is defined as UINT_PTR (unsigned type)
51 # WPARAM is defined as UINT_PTR (unsigned type)
50 # LPARAM is defined as LONG_PTR (signed type)
52 # LPARAM is defined as LONG_PTR (signed type)
51 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
53 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
52 _WPARAM = ctypes.c_ulong
54 _WPARAM = ctypes.c_ulong
53 _LPARAM = ctypes.c_long
55 _LPARAM = ctypes.c_long
54 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
56 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
55 _WPARAM = ctypes.c_ulonglong
57 _WPARAM = ctypes.c_ulonglong
56 _LPARAM = ctypes.c_longlong
58 _LPARAM = ctypes.c_longlong
57
59
58
60
59 class _FILETIME(ctypes.Structure):
61 class _FILETIME(ctypes.Structure):
60 _fields_ = [('dwLowDateTime', _DWORD), ('dwHighDateTime', _DWORD)]
62 _fields_ = [('dwLowDateTime', _DWORD), ('dwHighDateTime', _DWORD)]
61
63
62
64
63 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
65 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
64 _fields_ = [
66 _fields_ = [
65 ('dwFileAttributes', _DWORD),
67 ('dwFileAttributes', _DWORD),
66 ('ftCreationTime', _FILETIME),
68 ('ftCreationTime', _FILETIME),
67 ('ftLastAccessTime', _FILETIME),
69 ('ftLastAccessTime', _FILETIME),
68 ('ftLastWriteTime', _FILETIME),
70 ('ftLastWriteTime', _FILETIME),
69 ('dwVolumeSerialNumber', _DWORD),
71 ('dwVolumeSerialNumber', _DWORD),
70 ('nFileSizeHigh', _DWORD),
72 ('nFileSizeHigh', _DWORD),
71 ('nFileSizeLow', _DWORD),
73 ('nFileSizeLow', _DWORD),
72 ('nNumberOfLinks', _DWORD),
74 ('nNumberOfLinks', _DWORD),
73 ('nFileIndexHigh', _DWORD),
75 ('nFileIndexHigh', _DWORD),
74 ('nFileIndexLow', _DWORD),
76 ('nFileIndexLow', _DWORD),
75 ]
77 ]
76
78
77
79
78 # CreateFile
80 # CreateFile
79 _FILE_SHARE_READ = 0x00000001
81 _FILE_SHARE_READ = 0x00000001
80 _FILE_SHARE_WRITE = 0x00000002
82 _FILE_SHARE_WRITE = 0x00000002
81 _FILE_SHARE_DELETE = 0x00000004
83 _FILE_SHARE_DELETE = 0x00000004
82
84
83 _OPEN_EXISTING = 3
85 _OPEN_EXISTING = 3
84
86
85 _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
87 _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
86
88
87 # SetFileAttributes
89 # SetFileAttributes
88 _FILE_ATTRIBUTE_NORMAL = 0x80
90 _FILE_ATTRIBUTE_NORMAL = 0x80
89 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
91 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
90
92
91 # Process Security and Access Rights
93 # Process Security and Access Rights
92 _PROCESS_QUERY_INFORMATION = 0x0400
94 _PROCESS_QUERY_INFORMATION = 0x0400
93
95
94 # GetExitCodeProcess
96 # GetExitCodeProcess
95 _STILL_ACTIVE = 259
97 _STILL_ACTIVE = 259
96
98
97
99
98 class _STARTUPINFO(ctypes.Structure):
100 class _STARTUPINFO(ctypes.Structure):
99 _fields_ = [
101 _fields_ = [
100 ('cb', _DWORD),
102 ('cb', _DWORD),
101 ('lpReserved', _LPSTR),
103 ('lpReserved', _LPSTR),
102 ('lpDesktop', _LPSTR),
104 ('lpDesktop', _LPSTR),
103 ('lpTitle', _LPSTR),
105 ('lpTitle', _LPSTR),
104 ('dwX', _DWORD),
106 ('dwX', _DWORD),
105 ('dwY', _DWORD),
107 ('dwY', _DWORD),
106 ('dwXSize', _DWORD),
108 ('dwXSize', _DWORD),
107 ('dwYSize', _DWORD),
109 ('dwYSize', _DWORD),
108 ('dwXCountChars', _DWORD),
110 ('dwXCountChars', _DWORD),
109 ('dwYCountChars', _DWORD),
111 ('dwYCountChars', _DWORD),
110 ('dwFillAttribute', _DWORD),
112 ('dwFillAttribute', _DWORD),
111 ('dwFlags', _DWORD),
113 ('dwFlags', _DWORD),
112 ('wShowWindow', _WORD),
114 ('wShowWindow', _WORD),
113 ('cbReserved2', _WORD),
115 ('cbReserved2', _WORD),
114 ('lpReserved2', ctypes.c_char_p),
116 ('lpReserved2', ctypes.c_char_p),
115 ('hStdInput', _HANDLE),
117 ('hStdInput', _HANDLE),
116 ('hStdOutput', _HANDLE),
118 ('hStdOutput', _HANDLE),
117 ('hStdError', _HANDLE),
119 ('hStdError', _HANDLE),
118 ]
120 ]
119
121
120
122
121 class _PROCESS_INFORMATION(ctypes.Structure):
123 class _PROCESS_INFORMATION(ctypes.Structure):
122 _fields_ = [
124 _fields_ = [
123 ('hProcess', _HANDLE),
125 ('hProcess', _HANDLE),
124 ('hThread', _HANDLE),
126 ('hThread', _HANDLE),
125 ('dwProcessId', _DWORD),
127 ('dwProcessId', _DWORD),
126 ('dwThreadId', _DWORD),
128 ('dwThreadId', _DWORD),
127 ]
129 ]
128
130
129
131
130 _CREATE_NO_WINDOW = 0x08000000
132 _CREATE_NO_WINDOW = 0x08000000
131 _SW_HIDE = 0
133 _SW_HIDE = 0
132
134
133
135
134 class _COORD(ctypes.Structure):
136 class _COORD(ctypes.Structure):
135 _fields_ = [('X', ctypes.c_short), ('Y', ctypes.c_short)]
137 _fields_ = [('X', ctypes.c_short), ('Y', ctypes.c_short)]
136
138
137
139
138 class _SMALL_RECT(ctypes.Structure):
140 class _SMALL_RECT(ctypes.Structure):
139 _fields_ = [
141 _fields_ = [
140 ('Left', ctypes.c_short),
142 ('Left', ctypes.c_short),
141 ('Top', ctypes.c_short),
143 ('Top', ctypes.c_short),
142 ('Right', ctypes.c_short),
144 ('Right', ctypes.c_short),
143 ('Bottom', ctypes.c_short),
145 ('Bottom', ctypes.c_short),
144 ]
146 ]
145
147
146
148
147 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
149 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
148 _fields_ = [
150 _fields_ = [
149 ('dwSize', _COORD),
151 ('dwSize', _COORD),
150 ('dwCursorPosition', _COORD),
152 ('dwCursorPosition', _COORD),
151 ('wAttributes', _WORD),
153 ('wAttributes', _WORD),
152 ('srWindow', _SMALL_RECT),
154 ('srWindow', _SMALL_RECT),
153 ('dwMaximumWindowSize', _COORD),
155 ('dwMaximumWindowSize', _COORD),
154 ]
156 ]
155
157
156
158
157 _STD_OUTPUT_HANDLE = _DWORD(-11).value
159 _STD_OUTPUT_HANDLE = _DWORD(-11).value
158 _STD_ERROR_HANDLE = _DWORD(-12).value
160 _STD_ERROR_HANDLE = _DWORD(-12).value
159
161
160 # CERT_TRUST_STATUS dwErrorStatus
162 # CERT_TRUST_STATUS dwErrorStatus
161 CERT_TRUST_IS_PARTIAL_CHAIN = 0x10000
163 CERT_TRUST_IS_PARTIAL_CHAIN = 0x10000
162
164
163 # CertCreateCertificateContext encodings
165 # CertCreateCertificateContext encodings
164 X509_ASN_ENCODING = 0x00000001
166 X509_ASN_ENCODING = 0x00000001
165 PKCS_7_ASN_ENCODING = 0x00010000
167 PKCS_7_ASN_ENCODING = 0x00010000
166
168
167 # These structs are only complete enough to achieve what we need.
169 # These structs are only complete enough to achieve what we need.
168 class CERT_CHAIN_CONTEXT(ctypes.Structure):
170 class CERT_CHAIN_CONTEXT(ctypes.Structure):
169 _fields_ = (
171 _fields_ = (
170 ("cbSize", _DWORD),
172 ("cbSize", _DWORD),
171 # CERT_TRUST_STATUS struct
173 # CERT_TRUST_STATUS struct
172 ("dwErrorStatus", _DWORD),
174 ("dwErrorStatus", _DWORD),
173 ("dwInfoStatus", _DWORD),
175 ("dwInfoStatus", _DWORD),
174 ("cChain", _DWORD),
176 ("cChain", _DWORD),
175 ("rgpChain", ctypes.c_void_p),
177 ("rgpChain", ctypes.c_void_p),
176 ("cLowerQualityChainContext", _DWORD),
178 ("cLowerQualityChainContext", _DWORD),
177 ("rgpLowerQualityChainContext", ctypes.c_void_p),
179 ("rgpLowerQualityChainContext", ctypes.c_void_p),
178 ("fHasRevocationFreshnessTime", _BOOL),
180 ("fHasRevocationFreshnessTime", _BOOL),
179 ("dwRevocationFreshnessTime", _DWORD),
181 ("dwRevocationFreshnessTime", _DWORD),
180 )
182 )
181
183
182
184
183 class CERT_USAGE_MATCH(ctypes.Structure):
185 class CERT_USAGE_MATCH(ctypes.Structure):
184 _fields_ = (
186 _fields_ = (
185 ("dwType", _DWORD),
187 ("dwType", _DWORD),
186 # CERT_ENHKEY_USAGE struct
188 # CERT_ENHKEY_USAGE struct
187 ("cUsageIdentifier", _DWORD),
189 ("cUsageIdentifier", _DWORD),
188 ("rgpszUsageIdentifier", ctypes.c_void_p), # LPSTR *
190 ("rgpszUsageIdentifier", ctypes.c_void_p), # LPSTR *
189 )
191 )
190
192
191
193
192 class CERT_CHAIN_PARA(ctypes.Structure):
194 class CERT_CHAIN_PARA(ctypes.Structure):
193 _fields_ = (
195 _fields_ = (
194 ("cbSize", _DWORD),
196 ("cbSize", _DWORD),
195 ("RequestedUsage", CERT_USAGE_MATCH),
197 ("RequestedUsage", CERT_USAGE_MATCH),
196 ("RequestedIssuancePolicy", CERT_USAGE_MATCH),
198 ("RequestedIssuancePolicy", CERT_USAGE_MATCH),
197 ("dwUrlRetrievalTimeout", _DWORD),
199 ("dwUrlRetrievalTimeout", _DWORD),
198 ("fCheckRevocationFreshnessTime", _BOOL),
200 ("fCheckRevocationFreshnessTime", _BOOL),
199 ("dwRevocationFreshnessTime", _DWORD),
201 ("dwRevocationFreshnessTime", _DWORD),
200 ("pftCacheResync", ctypes.c_void_p), # LPFILETIME
202 ("pftCacheResync", ctypes.c_void_p), # LPFILETIME
201 ("pStrongSignPara", ctypes.c_void_p), # PCCERT_STRONG_SIGN_PARA
203 ("pStrongSignPara", ctypes.c_void_p), # PCCERT_STRONG_SIGN_PARA
202 ("dwStrongSignFlags", _DWORD),
204 ("dwStrongSignFlags", _DWORD),
203 )
205 )
204
206
205
207
206 # types of parameters of C functions used (required by pypy)
208 # types of parameters of C functions used (required by pypy)
207
209
208 _crypt32.CertCreateCertificateContext.argtypes = [
210 _crypt32.CertCreateCertificateContext.argtypes = [
209 _DWORD, # cert encoding
211 _DWORD, # cert encoding
210 ctypes.c_char_p, # cert
212 ctypes.c_char_p, # cert
211 _DWORD,
213 _DWORD,
212 ] # cert size
214 ] # cert size
213 _crypt32.CertCreateCertificateContext.restype = _PCCERT_CONTEXT
215 _crypt32.CertCreateCertificateContext.restype = _PCCERT_CONTEXT
214
216
215 _crypt32.CertGetCertificateChain.argtypes = [
217 _crypt32.CertGetCertificateChain.argtypes = [
216 ctypes.c_void_p, # HCERTCHAINENGINE
218 ctypes.c_void_p, # HCERTCHAINENGINE
217 _PCCERT_CONTEXT,
219 _PCCERT_CONTEXT,
218 ctypes.c_void_p, # LPFILETIME
220 ctypes.c_void_p, # LPFILETIME
219 ctypes.c_void_p, # HCERTSTORE
221 ctypes.c_void_p, # HCERTSTORE
220 ctypes.c_void_p, # PCERT_CHAIN_PARA
222 ctypes.c_void_p, # PCERT_CHAIN_PARA
221 _DWORD,
223 _DWORD,
222 ctypes.c_void_p, # LPVOID
224 ctypes.c_void_p, # LPVOID
223 ctypes.c_void_p, # PCCERT_CHAIN_CONTEXT *
225 ctypes.c_void_p, # PCCERT_CHAIN_CONTEXT *
224 ]
226 ]
225 _crypt32.CertGetCertificateChain.restype = _BOOL
227 _crypt32.CertGetCertificateChain.restype = _BOOL
226
228
227 _crypt32.CertFreeCertificateContext.argtypes = [_PCCERT_CONTEXT]
229 _crypt32.CertFreeCertificateContext.argtypes = [_PCCERT_CONTEXT]
228 _crypt32.CertFreeCertificateContext.restype = _BOOL
230 _crypt32.CertFreeCertificateContext.restype = _BOOL
229
231
230 _kernel32.CreateFileA.argtypes = [
232 _kernel32.CreateFileA.argtypes = [
231 _LPCSTR,
233 _LPCSTR,
232 _DWORD,
234 _DWORD,
233 _DWORD,
235 _DWORD,
234 ctypes.c_void_p,
236 ctypes.c_void_p,
235 _DWORD,
237 _DWORD,
236 _DWORD,
238 _DWORD,
237 _HANDLE,
239 _HANDLE,
238 ]
240 ]
239 _kernel32.CreateFileA.restype = _HANDLE
241 _kernel32.CreateFileA.restype = _HANDLE
240
242
241 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
243 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
242 _kernel32.GetFileInformationByHandle.restype = _BOOL
244 _kernel32.GetFileInformationByHandle.restype = _BOOL
243
245
244 _kernel32.CloseHandle.argtypes = [_HANDLE]
246 _kernel32.CloseHandle.argtypes = [_HANDLE]
245 _kernel32.CloseHandle.restype = _BOOL
247 _kernel32.CloseHandle.restype = _BOOL
246
248
247 try:
249 try:
248 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
250 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
249 _kernel32.CreateHardLinkA.restype = _BOOL
251 _kernel32.CreateHardLinkA.restype = _BOOL
250 except AttributeError:
252 except AttributeError:
251 pass
253 pass
252
254
253 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
255 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
254 _kernel32.SetFileAttributesA.restype = _BOOL
256 _kernel32.SetFileAttributesA.restype = _BOOL
255
257
256 _DRIVE_UNKNOWN = 0
258 _DRIVE_UNKNOWN = 0
257 _DRIVE_NO_ROOT_DIR = 1
259 _DRIVE_NO_ROOT_DIR = 1
258 _DRIVE_REMOVABLE = 2
260 _DRIVE_REMOVABLE = 2
259 _DRIVE_FIXED = 3
261 _DRIVE_FIXED = 3
260 _DRIVE_REMOTE = 4
262 _DRIVE_REMOTE = 4
261 _DRIVE_CDROM = 5
263 _DRIVE_CDROM = 5
262 _DRIVE_RAMDISK = 6
264 _DRIVE_RAMDISK = 6
263
265
264 _kernel32.GetDriveTypeA.argtypes = [_LPCSTR]
266 _kernel32.GetDriveTypeA.argtypes = [_LPCSTR]
265 _kernel32.GetDriveTypeA.restype = _UINT
267 _kernel32.GetDriveTypeA.restype = _UINT
266
268
267 _kernel32.GetVolumeInformationA.argtypes = [
269 _kernel32.GetVolumeInformationA.argtypes = [
268 _LPCSTR,
270 _LPCSTR,
269 ctypes.c_void_p,
271 ctypes.c_void_p,
270 _DWORD,
272 _DWORD,
271 ctypes.c_void_p,
273 ctypes.c_void_p,
272 ctypes.c_void_p,
274 ctypes.c_void_p,
273 ctypes.c_void_p,
275 ctypes.c_void_p,
274 ctypes.c_void_p,
276 ctypes.c_void_p,
275 _DWORD,
277 _DWORD,
276 ]
278 ]
277 _kernel32.GetVolumeInformationA.restype = _BOOL
279 _kernel32.GetVolumeInformationA.restype = _BOOL
278
280
279 _kernel32.GetVolumePathNameA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD]
281 _kernel32.GetVolumePathNameA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD]
280 _kernel32.GetVolumePathNameA.restype = _BOOL
282 _kernel32.GetVolumePathNameA.restype = _BOOL
281
283
282 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
284 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
283 _kernel32.OpenProcess.restype = _HANDLE
285 _kernel32.OpenProcess.restype = _HANDLE
284
286
285 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
287 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
286 _kernel32.GetExitCodeProcess.restype = _BOOL
288 _kernel32.GetExitCodeProcess.restype = _BOOL
287
289
288 _kernel32.GetLastError.argtypes = []
290 _kernel32.GetLastError.argtypes = []
289 _kernel32.GetLastError.restype = _DWORD
291 _kernel32.GetLastError.restype = _DWORD
290
292
291 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
293 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
292 _kernel32.GetModuleFileNameA.restype = _DWORD
294 _kernel32.GetModuleFileNameA.restype = _DWORD
293
295
294 _kernel32.CreateProcessA.argtypes = [
296 _kernel32.CreateProcessA.argtypes = [
295 _LPCSTR,
297 _LPCSTR,
296 _LPCSTR,
298 _LPCSTR,
297 ctypes.c_void_p,
299 ctypes.c_void_p,
298 ctypes.c_void_p,
300 ctypes.c_void_p,
299 _BOOL,
301 _BOOL,
300 _DWORD,
302 _DWORD,
301 ctypes.c_void_p,
303 ctypes.c_void_p,
302 _LPCSTR,
304 _LPCSTR,
303 ctypes.c_void_p,
305 ctypes.c_void_p,
304 ctypes.c_void_p,
306 ctypes.c_void_p,
305 ]
307 ]
306 _kernel32.CreateProcessA.restype = _BOOL
308 _kernel32.CreateProcessA.restype = _BOOL
307
309
308 _kernel32.ExitProcess.argtypes = [_UINT]
310 _kernel32.ExitProcess.argtypes = [_UINT]
309 _kernel32.ExitProcess.restype = None
311 _kernel32.ExitProcess.restype = None
310
312
311 _kernel32.GetCurrentProcessId.argtypes = []
313 _kernel32.GetCurrentProcessId.argtypes = []
312 _kernel32.GetCurrentProcessId.restype = _DWORD
314 _kernel32.GetCurrentProcessId.restype = _DWORD
313
315
316 # pytype: disable=module-attr
314 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
317 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
318 # pytype: enable=module-attr
315 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
319 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
316 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
320 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
317
321
318 _kernel32.SetConsoleMode.argtypes = [_HANDLE, _DWORD]
322 _kernel32.SetConsoleMode.argtypes = [_HANDLE, _DWORD]
319 _kernel32.SetConsoleMode.restype = _BOOL
323 _kernel32.SetConsoleMode.restype = _BOOL
320
324
321 _kernel32.GetConsoleMode.argtypes = [_HANDLE, ctypes.c_void_p]
325 _kernel32.GetConsoleMode.argtypes = [_HANDLE, ctypes.c_void_p]
322 _kernel32.GetConsoleMode.restype = _BOOL
326 _kernel32.GetConsoleMode.restype = _BOOL
323
327
324 _kernel32.GetStdHandle.argtypes = [_DWORD]
328 _kernel32.GetStdHandle.argtypes = [_DWORD]
325 _kernel32.GetStdHandle.restype = _HANDLE
329 _kernel32.GetStdHandle.restype = _HANDLE
326
330
327 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
331 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
328 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
332 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
329
333
330 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
334 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
331 _advapi32.GetUserNameA.restype = _BOOL
335 _advapi32.GetUserNameA.restype = _BOOL
332
336
333 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
337 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
334 _user32.GetWindowThreadProcessId.restype = _DWORD
338 _user32.GetWindowThreadProcessId.restype = _DWORD
335
339
336 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
340 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
337 _user32.ShowWindow.restype = _BOOL
341 _user32.ShowWindow.restype = _BOOL
338
342
343 # pytype: disable=module-attr
339 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
344 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
345 # pytype: enable=module-attr
340 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
346 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
341 _user32.EnumWindows.restype = _BOOL
347 _user32.EnumWindows.restype = _BOOL
342
348
343 _kernel32.PeekNamedPipe.argtypes = [
349 _kernel32.PeekNamedPipe.argtypes = [
344 _HANDLE,
350 _HANDLE,
345 ctypes.c_void_p,
351 ctypes.c_void_p,
346 _DWORD,
352 _DWORD,
347 ctypes.c_void_p,
353 ctypes.c_void_p,
348 ctypes.c_void_p,
354 ctypes.c_void_p,
349 ctypes.c_void_p,
355 ctypes.c_void_p,
350 ]
356 ]
351 _kernel32.PeekNamedPipe.restype = _BOOL
357 _kernel32.PeekNamedPipe.restype = _BOOL
352
358
353
359
354 def _raiseoserror(name):
360 def _raiseoserror(name):
355 # Force the code to a signed int to avoid an 'int too large' error.
361 # Force the code to a signed int to avoid an 'int too large' error.
356 # See https://bugs.python.org/issue28474
362 # See https://bugs.python.org/issue28474
357 code = _kernel32.GetLastError()
363 code = _kernel32.GetLastError()
358 if code > 0x7FFFFFFF:
364 if code > 0x7FFFFFFF:
359 code -= 2 ** 32
365 code -= 2 ** 32
360 err = ctypes.WinError(code=code)
366 err = ctypes.WinError(code=code) # pytype: disable=module-attr
361 raise OSError(
367 raise OSError(
362 err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror)
368 err.errno, '%s: %s' % (encoding.strfromlocal(name), err.strerror)
363 )
369 )
364
370
365
371
366 def _getfileinfo(name):
372 def _getfileinfo(name):
367 fh = _kernel32.CreateFileA(
373 fh = _kernel32.CreateFileA(
368 name,
374 name,
369 0,
375 0,
370 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
376 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
371 None,
377 None,
372 _OPEN_EXISTING,
378 _OPEN_EXISTING,
373 _FILE_FLAG_BACKUP_SEMANTICS,
379 _FILE_FLAG_BACKUP_SEMANTICS,
374 None,
380 None,
375 )
381 )
376 if fh == _INVALID_HANDLE_VALUE:
382 if fh == _INVALID_HANDLE_VALUE:
377 _raiseoserror(name)
383 _raiseoserror(name)
378 try:
384 try:
379 fi = _BY_HANDLE_FILE_INFORMATION()
385 fi = _BY_HANDLE_FILE_INFORMATION()
380 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
386 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
381 _raiseoserror(name)
387 _raiseoserror(name)
382 return fi
388 return fi
383 finally:
389 finally:
384 _kernel32.CloseHandle(fh)
390 _kernel32.CloseHandle(fh)
385
391
386
392
387 def checkcertificatechain(cert, build=True):
393 def checkcertificatechain(cert, build=True):
388 """Tests the given certificate to see if there is a complete chain to a
394 """Tests the given certificate to see if there is a complete chain to a
389 trusted root certificate. As a side effect, missing certificates are
395 trusted root certificate. As a side effect, missing certificates are
390 downloaded and installed unless ``build=False``. True is returned if a
396 downloaded and installed unless ``build=False``. True is returned if a
391 chain to a trusted root exists (even if built on the fly), otherwise
397 chain to a trusted root exists (even if built on the fly), otherwise
392 False. NB: A chain to a trusted root does NOT imply that the certificate
398 False. NB: A chain to a trusted root does NOT imply that the certificate
393 is valid.
399 is valid.
394 """
400 """
395
401
396 chainctxptr = ctypes.POINTER(CERT_CHAIN_CONTEXT)
402 chainctxptr = ctypes.POINTER(CERT_CHAIN_CONTEXT)
397
403
398 pchainctx = chainctxptr()
404 pchainctx = chainctxptr()
399 chainpara = CERT_CHAIN_PARA(
405 chainpara = CERT_CHAIN_PARA(
400 cbSize=ctypes.sizeof(CERT_CHAIN_PARA), RequestedUsage=CERT_USAGE_MATCH()
406 cbSize=ctypes.sizeof(CERT_CHAIN_PARA), RequestedUsage=CERT_USAGE_MATCH()
401 )
407 )
402
408
403 certctx = _crypt32.CertCreateCertificateContext(
409 certctx = _crypt32.CertCreateCertificateContext(
404 X509_ASN_ENCODING, cert, len(cert)
410 X509_ASN_ENCODING, cert, len(cert)
405 )
411 )
406 if certctx is None:
412 if certctx is None:
407 _raiseoserror(b'CertCreateCertificateContext')
413 _raiseoserror(b'CertCreateCertificateContext')
408
414
409 flags = 0
415 flags = 0
410
416
411 if not build:
417 if not build:
412 flags |= 0x100 # CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE
418 flags |= 0x100 # CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE
413
419
414 try:
420 try:
415 # Building the certificate chain will update root certs as necessary.
421 # Building the certificate chain will update root certs as necessary.
416 if not _crypt32.CertGetCertificateChain(
422 if not _crypt32.CertGetCertificateChain(
417 None, # hChainEngine
423 None, # hChainEngine
418 certctx, # pCertContext
424 certctx, # pCertContext
419 None, # pTime
425 None, # pTime
420 None, # hAdditionalStore
426 None, # hAdditionalStore
421 ctypes.byref(chainpara),
427 ctypes.byref(chainpara),
422 flags,
428 flags,
423 None, # pvReserved
429 None, # pvReserved
424 ctypes.byref(pchainctx),
430 ctypes.byref(pchainctx),
425 ):
431 ):
426 _raiseoserror(b'CertGetCertificateChain')
432 _raiseoserror(b'CertGetCertificateChain')
427
433
428 chainctx = pchainctx.contents
434 chainctx = pchainctx.contents
429
435
430 return chainctx.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN == 0
436 return chainctx.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN == 0
431 finally:
437 finally:
432 if pchainctx:
438 if pchainctx:
433 _crypt32.CertFreeCertificateChain(pchainctx)
439 _crypt32.CertFreeCertificateChain(pchainctx)
434 _crypt32.CertFreeCertificateContext(certctx)
440 _crypt32.CertFreeCertificateContext(certctx)
435
441
436
442
437 def oslink(src, dst):
443 def oslink(src, dst):
438 try:
444 try:
439 if not _kernel32.CreateHardLinkA(dst, src, None):
445 if not _kernel32.CreateHardLinkA(dst, src, None):
440 _raiseoserror(src)
446 _raiseoserror(src)
441 except AttributeError: # Wine doesn't support this function
447 except AttributeError: # Wine doesn't support this function
442 _raiseoserror(src)
448 _raiseoserror(src)
443
449
444
450
445 def nlinks(name):
451 def nlinks(name):
446 '''return number of hardlinks for the given file'''
452 '''return number of hardlinks for the given file'''
447 return _getfileinfo(name).nNumberOfLinks
453 return _getfileinfo(name).nNumberOfLinks
448
454
449
455
450 def samefile(path1, path2):
456 def samefile(path1, path2):
451 '''Returns whether path1 and path2 refer to the same file or directory.'''
457 '''Returns whether path1 and path2 refer to the same file or directory.'''
452 res1 = _getfileinfo(path1)
458 res1 = _getfileinfo(path1)
453 res2 = _getfileinfo(path2)
459 res2 = _getfileinfo(path2)
454 return (
460 return (
455 res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
461 res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
456 and res1.nFileIndexHigh == res2.nFileIndexHigh
462 and res1.nFileIndexHigh == res2.nFileIndexHigh
457 and res1.nFileIndexLow == res2.nFileIndexLow
463 and res1.nFileIndexLow == res2.nFileIndexLow
458 )
464 )
459
465
460
466
461 def samedevice(path1, path2):
467 def samedevice(path1, path2):
462 '''Returns whether path1 and path2 are on the same device.'''
468 '''Returns whether path1 and path2 are on the same device.'''
463 res1 = _getfileinfo(path1)
469 res1 = _getfileinfo(path1)
464 res2 = _getfileinfo(path2)
470 res2 = _getfileinfo(path2)
465 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
471 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
466
472
467
473
468 def peekpipe(pipe):
474 def peekpipe(pipe):
469 handle = msvcrt.get_osfhandle(pipe.fileno())
475 handle = msvcrt.get_osfhandle(pipe.fileno()) # pytype: disable=module-attr
470 avail = _DWORD()
476 avail = _DWORD()
471
477
472 if not _kernel32.PeekNamedPipe(
478 if not _kernel32.PeekNamedPipe(
473 handle, None, 0, None, ctypes.byref(avail), None
479 handle, None, 0, None, ctypes.byref(avail), None
474 ):
480 ):
475 err = _kernel32.GetLastError()
481 err = _kernel32.GetLastError()
476 if err == _ERROR_BROKEN_PIPE:
482 if err == _ERROR_BROKEN_PIPE:
477 return 0
483 return 0
478 raise ctypes.WinError(err)
484 raise ctypes.WinError(err) # pytype: disable=module-attr
479
485
480 return avail.value
486 return avail.value
481
487
482
488
483 def lasterrorwaspipeerror(err):
489 def lasterrorwaspipeerror(err):
484 if err.errno != errno.EINVAL:
490 if err.errno != errno.EINVAL:
485 return False
491 return False
486 err = _kernel32.GetLastError()
492 err = _kernel32.GetLastError()
487 return err == _ERROR_BROKEN_PIPE or err == _ERROR_NO_DATA
493 return err == _ERROR_BROKEN_PIPE or err == _ERROR_NO_DATA
488
494
489
495
490 def testpid(pid):
496 def testpid(pid):
491 """return True if pid is still running or unable to
497 """return True if pid is still running or unable to
492 determine, False otherwise"""
498 determine, False otherwise"""
493 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
499 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
494 if h:
500 if h:
495 try:
501 try:
496 status = _DWORD()
502 status = _DWORD()
497 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
503 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
498 return status.value == _STILL_ACTIVE
504 return status.value == _STILL_ACTIVE
499 finally:
505 finally:
500 _kernel32.CloseHandle(h)
506 _kernel32.CloseHandle(h)
501 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
507 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
502
508
503
509
504 def executablepath():
510 def executablepath():
505 '''return full path of hg.exe'''
511 '''return full path of hg.exe'''
506 size = 600
512 size = 600
507 buf = ctypes.create_string_buffer(size + 1)
513 buf = ctypes.create_string_buffer(size + 1)
508 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
514 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
515 # pytype: disable=module-attr
509 if len == 0:
516 if len == 0:
510 raise ctypes.WinError() # Note: WinError is a function
517 raise ctypes.WinError() # Note: WinError is a function
511 elif len == size:
518 elif len == size:
512 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
519 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
520 # pytype: enable=module-attr
513 return buf.value
521 return buf.value
514
522
515
523
516 def getvolumename(path):
524 def getvolumename(path):
517 """Get the mount point of the filesystem from a directory or file
525 """Get the mount point of the filesystem from a directory or file
518 (best-effort)
526 (best-effort)
519
527
520 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
528 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
521 """
529 """
522 # realpath() calls GetFullPathName()
530 # realpath() calls GetFullPathName()
523 realpath = os.path.realpath(path)
531 realpath = os.path.realpath(path)
524
532
525 # allocate at least MAX_PATH long since GetVolumePathName('c:\\', buf, 4)
533 # allocate at least MAX_PATH long since GetVolumePathName('c:\\', buf, 4)
526 # somehow fails on Windows XP
534 # somehow fails on Windows XP
527 size = max(len(realpath), _MAX_PATH) + 1
535 size = max(len(realpath), _MAX_PATH) + 1
528 buf = ctypes.create_string_buffer(size)
536 buf = ctypes.create_string_buffer(size)
529
537
530 if not _kernel32.GetVolumePathNameA(realpath, ctypes.byref(buf), size):
538 if not _kernel32.GetVolumePathNameA(realpath, ctypes.byref(buf), size):
531 raise ctypes.WinError() # Note: WinError is a function
539 # Note: WinError is a function
540 raise ctypes.WinError() # pytype: disable=module-attr
532
541
533 return buf.value
542 return buf.value
534
543
535
544
536 def getfstype(path):
545 def getfstype(path):
537 """Get the filesystem type name from a directory or file (best-effort)
546 """Get the filesystem type name from a directory or file (best-effort)
538
547
539 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
548 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
540 """
549 """
541 volume = getvolumename(path)
550 volume = getvolumename(path)
542
551
543 t = _kernel32.GetDriveTypeA(volume)
552 t = _kernel32.GetDriveTypeA(volume)
544
553
545 if t == _DRIVE_REMOTE:
554 if t == _DRIVE_REMOTE:
546 return b'cifs'
555 return b'cifs'
547 elif t not in (
556 elif t not in (
548 _DRIVE_REMOVABLE,
557 _DRIVE_REMOVABLE,
549 _DRIVE_FIXED,
558 _DRIVE_FIXED,
550 _DRIVE_CDROM,
559 _DRIVE_CDROM,
551 _DRIVE_RAMDISK,
560 _DRIVE_RAMDISK,
552 ):
561 ):
553 return None
562 return None
554
563
555 size = _MAX_PATH + 1
564 size = _MAX_PATH + 1
556 name = ctypes.create_string_buffer(size)
565 name = ctypes.create_string_buffer(size)
557
566
558 if not _kernel32.GetVolumeInformationA(
567 if not _kernel32.GetVolumeInformationA(
559 volume, None, 0, None, None, None, ctypes.byref(name), size
568 volume, None, 0, None, None, None, ctypes.byref(name), size
560 ):
569 ):
561 raise ctypes.WinError() # Note: WinError is a function
570 # Note: WinError is a function
571 raise ctypes.WinError() # pytype: disable=module-attr
562
572
563 return name.value
573 return name.value
564
574
565
575
566 def getuser():
576 def getuser():
567 '''return name of current user'''
577 '''return name of current user'''
568 size = _DWORD(300)
578 size = _DWORD(300)
569 buf = ctypes.create_string_buffer(size.value + 1)
579 buf = ctypes.create_string_buffer(size.value + 1)
570 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
580 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
571 raise ctypes.WinError()
581 raise ctypes.WinError() # pytype: disable=module-attr
572 return buf.value
582 return buf.value
573
583
574
584
575 _signalhandler = []
585 _signalhandler = []
576
586
577
587
578 def setsignalhandler():
588 def setsignalhandler():
579 """Register a termination handler for console events including
589 """Register a termination handler for console events including
580 CTRL+C. python signal handlers do not work well with socket
590 CTRL+C. python signal handlers do not work well with socket
581 operations.
591 operations.
582 """
592 """
583
593
584 def handler(event):
594 def handler(event):
585 _kernel32.ExitProcess(1)
595 _kernel32.ExitProcess(1)
586
596
587 if _signalhandler:
597 if _signalhandler:
588 return # already registered
598 return # already registered
589 h = _SIGNAL_HANDLER(handler)
599 h = _SIGNAL_HANDLER(handler)
590 _signalhandler.append(h) # needed to prevent garbage collection
600 _signalhandler.append(h) # needed to prevent garbage collection
591 if not _kernel32.SetConsoleCtrlHandler(h, True):
601 if not _kernel32.SetConsoleCtrlHandler(h, True):
592 raise ctypes.WinError()
602 raise ctypes.WinError() # pytype: disable=module-attr
593
603
594
604
595 def hidewindow():
605 def hidewindow():
596 def callback(hwnd, pid):
606 def callback(hwnd, pid):
597 wpid = _DWORD()
607 wpid = _DWORD()
598 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
608 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
599 if pid == wpid.value:
609 if pid == wpid.value:
600 _user32.ShowWindow(hwnd, _SW_HIDE)
610 _user32.ShowWindow(hwnd, _SW_HIDE)
601 return False # stop enumerating windows
611 return False # stop enumerating windows
602 return True
612 return True
603
613
604 pid = _kernel32.GetCurrentProcessId()
614 pid = _kernel32.GetCurrentProcessId()
605 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
615 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
606
616
607
617
608 def termsize():
618 def termsize():
609 # cmd.exe does not handle CR like a unix console, the CR is
619 # cmd.exe does not handle CR like a unix console, the CR is
610 # counted in the line length. On 80 columns consoles, if 80
620 # counted in the line length. On 80 columns consoles, if 80
611 # characters are written, the following CR won't apply on the
621 # characters are written, the following CR won't apply on the
612 # current line but on the new one. Keep room for it.
622 # current line but on the new one. Keep room for it.
613 width = 80 - 1
623 width = 80 - 1
614 height = 25
624 height = 25
615 # Query stderr to avoid problems with redirections
625 # Query stderr to avoid problems with redirections
616 screenbuf = _kernel32.GetStdHandle(
626 screenbuf = _kernel32.GetStdHandle(
617 _STD_ERROR_HANDLE
627 _STD_ERROR_HANDLE
618 ) # don't close the handle returned
628 ) # don't close the handle returned
619 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
629 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
620 return width, height
630 return width, height
621 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
631 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
622 if not _kernel32.GetConsoleScreenBufferInfo(screenbuf, ctypes.byref(csbi)):
632 if not _kernel32.GetConsoleScreenBufferInfo(screenbuf, ctypes.byref(csbi)):
623 return width, height
633 return width, height
624 width = csbi.srWindow.Right - csbi.srWindow.Left # don't '+ 1'
634 width = csbi.srWindow.Right - csbi.srWindow.Left # don't '+ 1'
625 height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1
635 height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1
626 return width, height
636 return width, height
627
637
628
638
629 def enablevtmode():
639 def enablevtmode():
630 """Enable virtual terminal mode for the associated console. Return True if
640 """Enable virtual terminal mode for the associated console. Return True if
631 enabled, else False."""
641 enabled, else False."""
632
642
633 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
643 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
634
644
635 handle = _kernel32.GetStdHandle(
645 handle = _kernel32.GetStdHandle(
636 _STD_OUTPUT_HANDLE
646 _STD_OUTPUT_HANDLE
637 ) # don't close the handle
647 ) # don't close the handle
638 if handle == _INVALID_HANDLE_VALUE:
648 if handle == _INVALID_HANDLE_VALUE:
639 return False
649 return False
640
650
641 mode = _DWORD(0)
651 mode = _DWORD(0)
642
652
643 if not _kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
653 if not _kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
644 return False
654 return False
645
655
646 if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
656 if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
647 mode.value |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
657 mode.value |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
648
658
649 if not _kernel32.SetConsoleMode(handle, mode):
659 if not _kernel32.SetConsoleMode(handle, mode):
650 return False
660 return False
651
661
652 return True
662 return True
653
663
654
664
655 def spawndetached(args):
665 def spawndetached(args):
656 # No standard library function really spawns a fully detached
666 # No standard library function really spawns a fully detached
657 # process under win32 because they allocate pipes or other objects
667 # process under win32 because they allocate pipes or other objects
658 # to handle standard streams communications. Passing these objects
668 # to handle standard streams communications. Passing these objects
659 # to the child process requires handle inheritance to be enabled
669 # to the child process requires handle inheritance to be enabled
660 # which makes really detached processes impossible.
670 # which makes really detached processes impossible.
661 si = _STARTUPINFO()
671 si = _STARTUPINFO()
662 si.cb = ctypes.sizeof(_STARTUPINFO)
672 si.cb = ctypes.sizeof(_STARTUPINFO)
663
673
664 pi = _PROCESS_INFORMATION()
674 pi = _PROCESS_INFORMATION()
665
675
666 env = b''
676 env = b''
667 for k in encoding.environ:
677 for k in encoding.environ:
668 env += b"%s=%s\0" % (k, encoding.environ[k])
678 env += b"%s=%s\0" % (k, encoding.environ[k])
669 if not env:
679 if not env:
670 env = b'\0'
680 env = b'\0'
671 env += b'\0'
681 env += b'\0'
672
682
673 args = subprocess.list2cmdline(pycompat.rapply(encoding.strfromlocal, args))
683 args = subprocess.list2cmdline(pycompat.rapply(encoding.strfromlocal, args))
674
684
675 # TODO: CreateProcessW on py3?
685 # TODO: CreateProcessW on py3?
676 res = _kernel32.CreateProcessA(
686 res = _kernel32.CreateProcessA(
677 None,
687 None,
678 encoding.strtolocal(args),
688 encoding.strtolocal(args),
679 None,
689 None,
680 None,
690 None,
681 False,
691 False,
682 _CREATE_NO_WINDOW,
692 _CREATE_NO_WINDOW,
683 env,
693 env,
684 encoding.getcwd(),
694 encoding.getcwd(),
685 ctypes.byref(si),
695 ctypes.byref(si),
686 ctypes.byref(pi),
696 ctypes.byref(pi),
687 )
697 )
688 if not res:
698 if not res:
689 raise ctypes.WinError()
699 raise ctypes.WinError() # pytype: disable=module-attr
690
700
691 _kernel32.CloseHandle(pi.hProcess)
701 _kernel32.CloseHandle(pi.hProcess)
692 _kernel32.CloseHandle(pi.hThread)
702 _kernel32.CloseHandle(pi.hThread)
693
703
694 return pi.dwProcessId
704 return pi.dwProcessId
695
705
696
706
697 def unlink(f):
707 def unlink(f):
698 '''try to implement POSIX' unlink semantics on Windows'''
708 '''try to implement POSIX' unlink semantics on Windows'''
699
709
700 if os.path.isdir(f):
710 if os.path.isdir(f):
701 # use EPERM because it is POSIX prescribed value, even though
711 # use EPERM because it is POSIX prescribed value, even though
702 # unlink(2) on directories returns EISDIR on Linux
712 # unlink(2) on directories returns EISDIR on Linux
703 raise IOError(
713 raise IOError(
704 errno.EPERM,
714 errno.EPERM,
705 r"Unlinking directory not permitted: '%s'"
715 r"Unlinking directory not permitted: '%s'"
706 % encoding.strfromlocal(f),
716 % encoding.strfromlocal(f),
707 )
717 )
708
718
709 # POSIX allows to unlink and rename open files. Windows has serious
719 # POSIX allows to unlink and rename open files. Windows has serious
710 # problems with doing that:
720 # problems with doing that:
711 # - Calling os.unlink (or os.rename) on a file f fails if f or any
721 # - Calling os.unlink (or os.rename) on a file f fails if f or any
712 # hardlinked copy of f has been opened with Python's open(). There is no
722 # hardlinked copy of f has been opened with Python's open(). There is no
713 # way such a file can be deleted or renamed on Windows (other than
723 # way such a file can be deleted or renamed on Windows (other than
714 # scheduling the delete or rename for the next reboot).
724 # scheduling the delete or rename for the next reboot).
715 # - Calling os.unlink on a file that has been opened with Mercurial's
725 # - Calling os.unlink on a file that has been opened with Mercurial's
716 # posixfile (or comparable methods) will delay the actual deletion of
726 # posixfile (or comparable methods) will delay the actual deletion of
717 # the file for as long as the file is held open. The filename is blocked
727 # the file for as long as the file is held open. The filename is blocked
718 # during that time and cannot be used for recreating a new file under
728 # during that time and cannot be used for recreating a new file under
719 # that same name ("zombie file"). Directories containing such zombie files
729 # that same name ("zombie file"). Directories containing such zombie files
720 # cannot be removed or moved.
730 # cannot be removed or moved.
721 # A file that has been opened with posixfile can be renamed, so we rename
731 # A file that has been opened with posixfile can be renamed, so we rename
722 # f to a random temporary name before calling os.unlink on it. This allows
732 # f to a random temporary name before calling os.unlink on it. This allows
723 # callers to recreate f immediately while having other readers do their
733 # callers to recreate f immediately while having other readers do their
724 # implicit zombie filename blocking on a temporary name.
734 # implicit zombie filename blocking on a temporary name.
725
735
726 for tries in pycompat.xrange(10):
736 for tries in pycompat.xrange(10):
727 temp = b'%s-%08x' % (f, random.randint(0, 0xFFFFFFFF))
737 temp = b'%s-%08x' % (f, random.randint(0, 0xFFFFFFFF))
728 try:
738 try:
729 os.rename(f, temp) # raises OSError EEXIST if temp exists
739 os.rename(f, temp) # raises OSError EEXIST if temp exists
730 break
740 break
731 except OSError as e:
741 except OSError as e:
732 if e.errno != errno.EEXIST:
742 if e.errno != errno.EEXIST:
733 raise
743 raise
734 else:
744 else:
735 raise IOError(errno.EEXIST, "No usable temporary filename found")
745 raise IOError(errno.EEXIST, "No usable temporary filename found")
736
746
737 try:
747 try:
738 os.unlink(temp)
748 os.unlink(temp)
739 except OSError:
749 except OSError:
740 # The unlink might have failed because the READONLY attribute may heave
750 # The unlink might have failed because the READONLY attribute may heave
741 # been set on the original file. Rename works fine with READONLY set,
751 # been set on the original file. Rename works fine with READONLY set,
742 # but not os.unlink. Reset all attributes and try again.
752 # but not os.unlink. Reset all attributes and try again.
743 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
753 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
744 try:
754 try:
745 os.unlink(temp)
755 os.unlink(temp)
746 except OSError:
756 except OSError:
747 # The unlink might have failed due to some very rude AV-Scanners.
757 # The unlink might have failed due to some very rude AV-Scanners.
748 # Leaking a tempfile is the lesser evil than aborting here and
758 # Leaking a tempfile is the lesser evil than aborting here and
749 # leaving some potentially serious inconsistencies.
759 # leaving some potentially serious inconsistencies.
750 pass
760 pass
751
761
752
762
753 def makedir(path, notindexed):
763 def makedir(path, notindexed):
754 os.mkdir(path)
764 os.mkdir(path)
755 if notindexed:
765 if notindexed:
756 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
766 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
General Comments 0
You need to be logged in to leave comments. Login now