##// END OF EJS Templates
win32: close the handles associated with a spawned child process...
Matt Harbison -
r40964:576474ba default
parent child Browse files
Show More
@@ -1,649 +1,652 b''
1 1 # win32.py - utility functions that use win32 API
2 2 #
3 3 # Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import
9 9
10 10 import ctypes
11 11 import ctypes.wintypes as wintypes
12 12 import errno
13 13 import msvcrt
14 14 import os
15 15 import random
16 16 import subprocess
17 17
18 18 from . import (
19 19 encoding,
20 20 pycompat,
21 21 )
22 22
23 23 _kernel32 = ctypes.windll.kernel32
24 24 _advapi32 = ctypes.windll.advapi32
25 25 _user32 = ctypes.windll.user32
26 26 _crypt32 = ctypes.windll.crypt32
27 27
28 28 _BOOL = ctypes.c_long
29 29 _WORD = ctypes.c_ushort
30 30 _DWORD = ctypes.c_ulong
31 31 _UINT = ctypes.c_uint
32 32 _LONG = ctypes.c_long
33 33 _LPCSTR = _LPSTR = ctypes.c_char_p
34 34 _HANDLE = ctypes.c_void_p
35 35 _HWND = _HANDLE
36 36 _PCCERT_CONTEXT = ctypes.c_void_p
37 37 _MAX_PATH = wintypes.MAX_PATH
38 38
39 39 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
40 40
41 41 # GetLastError
42 42 _ERROR_SUCCESS = 0
43 43 _ERROR_NO_MORE_FILES = 18
44 44 _ERROR_INVALID_PARAMETER = 87
45 45 _ERROR_BROKEN_PIPE = 109
46 46 _ERROR_INSUFFICIENT_BUFFER = 122
47 47 _ERROR_NO_DATA = 232
48 48
49 49 # WPARAM is defined as UINT_PTR (unsigned type)
50 50 # LPARAM is defined as LONG_PTR (signed type)
51 51 if ctypes.sizeof(ctypes.c_long) == ctypes.sizeof(ctypes.c_void_p):
52 52 _WPARAM = ctypes.c_ulong
53 53 _LPARAM = ctypes.c_long
54 54 elif ctypes.sizeof(ctypes.c_longlong) == ctypes.sizeof(ctypes.c_void_p):
55 55 _WPARAM = ctypes.c_ulonglong
56 56 _LPARAM = ctypes.c_longlong
57 57
58 58 class _FILETIME(ctypes.Structure):
59 59 _fields_ = [(r'dwLowDateTime', _DWORD),
60 60 (r'dwHighDateTime', _DWORD)]
61 61
62 62 class _BY_HANDLE_FILE_INFORMATION(ctypes.Structure):
63 63 _fields_ = [(r'dwFileAttributes', _DWORD),
64 64 (r'ftCreationTime', _FILETIME),
65 65 (r'ftLastAccessTime', _FILETIME),
66 66 (r'ftLastWriteTime', _FILETIME),
67 67 (r'dwVolumeSerialNumber', _DWORD),
68 68 (r'nFileSizeHigh', _DWORD),
69 69 (r'nFileSizeLow', _DWORD),
70 70 (r'nNumberOfLinks', _DWORD),
71 71 (r'nFileIndexHigh', _DWORD),
72 72 (r'nFileIndexLow', _DWORD)]
73 73
74 74 # CreateFile
75 75 _FILE_SHARE_READ = 0x00000001
76 76 _FILE_SHARE_WRITE = 0x00000002
77 77 _FILE_SHARE_DELETE = 0x00000004
78 78
79 79 _OPEN_EXISTING = 3
80 80
81 81 _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
82 82
83 83 # SetFileAttributes
84 84 _FILE_ATTRIBUTE_NORMAL = 0x80
85 85 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
86 86
87 87 # Process Security and Access Rights
88 88 _PROCESS_QUERY_INFORMATION = 0x0400
89 89
90 90 # GetExitCodeProcess
91 91 _STILL_ACTIVE = 259
92 92
93 93 class _STARTUPINFO(ctypes.Structure):
94 94 _fields_ = [(r'cb', _DWORD),
95 95 (r'lpReserved', _LPSTR),
96 96 (r'lpDesktop', _LPSTR),
97 97 (r'lpTitle', _LPSTR),
98 98 (r'dwX', _DWORD),
99 99 (r'dwY', _DWORD),
100 100 (r'dwXSize', _DWORD),
101 101 (r'dwYSize', _DWORD),
102 102 (r'dwXCountChars', _DWORD),
103 103 (r'dwYCountChars', _DWORD),
104 104 (r'dwFillAttribute', _DWORD),
105 105 (r'dwFlags', _DWORD),
106 106 (r'wShowWindow', _WORD),
107 107 (r'cbReserved2', _WORD),
108 108 (r'lpReserved2', ctypes.c_char_p),
109 109 (r'hStdInput', _HANDLE),
110 110 (r'hStdOutput', _HANDLE),
111 111 (r'hStdError', _HANDLE)]
112 112
113 113 class _PROCESS_INFORMATION(ctypes.Structure):
114 114 _fields_ = [(r'hProcess', _HANDLE),
115 115 (r'hThread', _HANDLE),
116 116 (r'dwProcessId', _DWORD),
117 117 (r'dwThreadId', _DWORD)]
118 118
119 119 _CREATE_NO_WINDOW = 0x08000000
120 120 _SW_HIDE = 0
121 121
122 122 class _COORD(ctypes.Structure):
123 123 _fields_ = [(r'X', ctypes.c_short),
124 124 (r'Y', ctypes.c_short)]
125 125
126 126 class _SMALL_RECT(ctypes.Structure):
127 127 _fields_ = [(r'Left', ctypes.c_short),
128 128 (r'Top', ctypes.c_short),
129 129 (r'Right', ctypes.c_short),
130 130 (r'Bottom', ctypes.c_short)]
131 131
132 132 class _CONSOLE_SCREEN_BUFFER_INFO(ctypes.Structure):
133 133 _fields_ = [(r'dwSize', _COORD),
134 134 (r'dwCursorPosition', _COORD),
135 135 (r'wAttributes', _WORD),
136 136 (r'srWindow', _SMALL_RECT),
137 137 (r'dwMaximumWindowSize', _COORD)]
138 138
139 139 _STD_OUTPUT_HANDLE = _DWORD(-11).value
140 140 _STD_ERROR_HANDLE = _DWORD(-12).value
141 141
142 142 # CERT_TRUST_STATUS dwErrorStatus
143 143 CERT_TRUST_IS_PARTIAL_CHAIN = 0x10000
144 144
145 145 # CertCreateCertificateContext encodings
146 146 X509_ASN_ENCODING = 0x00000001
147 147 PKCS_7_ASN_ENCODING = 0x00010000
148 148
149 149 # These structs are only complete enough to achieve what we need.
150 150 class CERT_CHAIN_CONTEXT(ctypes.Structure):
151 151 _fields_ = (
152 152 (r"cbSize", _DWORD),
153 153
154 154 # CERT_TRUST_STATUS struct
155 155 (r"dwErrorStatus", _DWORD),
156 156 (r"dwInfoStatus", _DWORD),
157 157
158 158 (r"cChain", _DWORD),
159 159 (r"rgpChain", ctypes.c_void_p),
160 160 (r"cLowerQualityChainContext", _DWORD),
161 161 (r"rgpLowerQualityChainContext", ctypes.c_void_p),
162 162 (r"fHasRevocationFreshnessTime", _BOOL),
163 163 (r"dwRevocationFreshnessTime", _DWORD),
164 164 )
165 165
166 166 class CERT_USAGE_MATCH(ctypes.Structure):
167 167 _fields_ = (
168 168 (r"dwType", _DWORD),
169 169
170 170 # CERT_ENHKEY_USAGE struct
171 171 (r"cUsageIdentifier", _DWORD),
172 172 (r"rgpszUsageIdentifier", ctypes.c_void_p), # LPSTR *
173 173 )
174 174
175 175 class CERT_CHAIN_PARA(ctypes.Structure):
176 176 _fields_ = (
177 177 (r"cbSize", _DWORD),
178 178 (r"RequestedUsage", CERT_USAGE_MATCH),
179 179 (r"RequestedIssuancePolicy", CERT_USAGE_MATCH),
180 180 (r"dwUrlRetrievalTimeout", _DWORD),
181 181 (r"fCheckRevocationFreshnessTime", _BOOL),
182 182 (r"dwRevocationFreshnessTime", _DWORD),
183 183 (r"pftCacheResync", ctypes.c_void_p), # LPFILETIME
184 184 (r"pStrongSignPara", ctypes.c_void_p), # PCCERT_STRONG_SIGN_PARA
185 185 (r"dwStrongSignFlags", _DWORD),
186 186 )
187 187
188 188 # types of parameters of C functions used (required by pypy)
189 189
190 190 _crypt32.CertCreateCertificateContext.argtypes = [_DWORD, # cert encoding
191 191 ctypes.c_char_p, # cert
192 192 _DWORD] # cert size
193 193 _crypt32.CertCreateCertificateContext.restype = _PCCERT_CONTEXT
194 194
195 195 _crypt32.CertGetCertificateChain.argtypes = [
196 196 ctypes.c_void_p, # HCERTCHAINENGINE
197 197 _PCCERT_CONTEXT,
198 198 ctypes.c_void_p, # LPFILETIME
199 199 ctypes.c_void_p, # HCERTSTORE
200 200 ctypes.c_void_p, # PCERT_CHAIN_PARA
201 201 _DWORD,
202 202 ctypes.c_void_p, # LPVOID
203 203 ctypes.c_void_p # PCCERT_CHAIN_CONTEXT *
204 204 ]
205 205 _crypt32.CertGetCertificateChain.restype = _BOOL
206 206
207 207 _crypt32.CertFreeCertificateContext.argtypes = [_PCCERT_CONTEXT]
208 208 _crypt32.CertFreeCertificateContext.restype = _BOOL
209 209
210 210 _kernel32.CreateFileA.argtypes = [_LPCSTR, _DWORD, _DWORD, ctypes.c_void_p,
211 211 _DWORD, _DWORD, _HANDLE]
212 212 _kernel32.CreateFileA.restype = _HANDLE
213 213
214 214 _kernel32.GetFileInformationByHandle.argtypes = [_HANDLE, ctypes.c_void_p]
215 215 _kernel32.GetFileInformationByHandle.restype = _BOOL
216 216
217 217 _kernel32.CloseHandle.argtypes = [_HANDLE]
218 218 _kernel32.CloseHandle.restype = _BOOL
219 219
220 220 try:
221 221 _kernel32.CreateHardLinkA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p]
222 222 _kernel32.CreateHardLinkA.restype = _BOOL
223 223 except AttributeError:
224 224 pass
225 225
226 226 _kernel32.SetFileAttributesA.argtypes = [_LPCSTR, _DWORD]
227 227 _kernel32.SetFileAttributesA.restype = _BOOL
228 228
229 229 _DRIVE_UNKNOWN = 0
230 230 _DRIVE_NO_ROOT_DIR = 1
231 231 _DRIVE_REMOVABLE = 2
232 232 _DRIVE_FIXED = 3
233 233 _DRIVE_REMOTE = 4
234 234 _DRIVE_CDROM = 5
235 235 _DRIVE_RAMDISK = 6
236 236
237 237 _kernel32.GetDriveTypeA.argtypes = [_LPCSTR]
238 238 _kernel32.GetDriveTypeA.restype = _UINT
239 239
240 240 _kernel32.GetVolumeInformationA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD,
241 241 ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p, _DWORD]
242 242 _kernel32.GetVolumeInformationA.restype = _BOOL
243 243
244 244 _kernel32.GetVolumePathNameA.argtypes = [_LPCSTR, ctypes.c_void_p, _DWORD]
245 245 _kernel32.GetVolumePathNameA.restype = _BOOL
246 246
247 247 _kernel32.OpenProcess.argtypes = [_DWORD, _BOOL, _DWORD]
248 248 _kernel32.OpenProcess.restype = _HANDLE
249 249
250 250 _kernel32.GetExitCodeProcess.argtypes = [_HANDLE, ctypes.c_void_p]
251 251 _kernel32.GetExitCodeProcess.restype = _BOOL
252 252
253 253 _kernel32.GetLastError.argtypes = []
254 254 _kernel32.GetLastError.restype = _DWORD
255 255
256 256 _kernel32.GetModuleFileNameA.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD]
257 257 _kernel32.GetModuleFileNameA.restype = _DWORD
258 258
259 259 _kernel32.CreateProcessA.argtypes = [_LPCSTR, _LPCSTR, ctypes.c_void_p,
260 260 ctypes.c_void_p, _BOOL, _DWORD, ctypes.c_void_p, _LPCSTR, ctypes.c_void_p,
261 261 ctypes.c_void_p]
262 262 _kernel32.CreateProcessA.restype = _BOOL
263 263
264 264 _kernel32.ExitProcess.argtypes = [_UINT]
265 265 _kernel32.ExitProcess.restype = None
266 266
267 267 _kernel32.GetCurrentProcessId.argtypes = []
268 268 _kernel32.GetCurrentProcessId.restype = _DWORD
269 269
270 270 _SIGNAL_HANDLER = ctypes.WINFUNCTYPE(_BOOL, _DWORD)
271 271 _kernel32.SetConsoleCtrlHandler.argtypes = [_SIGNAL_HANDLER, _BOOL]
272 272 _kernel32.SetConsoleCtrlHandler.restype = _BOOL
273 273
274 274 _kernel32.SetConsoleMode.argtypes = [_HANDLE, _DWORD]
275 275 _kernel32.SetConsoleMode.restype = _BOOL
276 276
277 277 _kernel32.GetConsoleMode.argtypes = [_HANDLE, ctypes.c_void_p]
278 278 _kernel32.GetConsoleMode.restype = _BOOL
279 279
280 280 _kernel32.GetStdHandle.argtypes = [_DWORD]
281 281 _kernel32.GetStdHandle.restype = _HANDLE
282 282
283 283 _kernel32.GetConsoleScreenBufferInfo.argtypes = [_HANDLE, ctypes.c_void_p]
284 284 _kernel32.GetConsoleScreenBufferInfo.restype = _BOOL
285 285
286 286 _advapi32.GetUserNameA.argtypes = [ctypes.c_void_p, ctypes.c_void_p]
287 287 _advapi32.GetUserNameA.restype = _BOOL
288 288
289 289 _user32.GetWindowThreadProcessId.argtypes = [_HANDLE, ctypes.c_void_p]
290 290 _user32.GetWindowThreadProcessId.restype = _DWORD
291 291
292 292 _user32.ShowWindow.argtypes = [_HANDLE, ctypes.c_int]
293 293 _user32.ShowWindow.restype = _BOOL
294 294
295 295 _WNDENUMPROC = ctypes.WINFUNCTYPE(_BOOL, _HWND, _LPARAM)
296 296 _user32.EnumWindows.argtypes = [_WNDENUMPROC, _LPARAM]
297 297 _user32.EnumWindows.restype = _BOOL
298 298
299 299 _kernel32.PeekNamedPipe.argtypes = [_HANDLE, ctypes.c_void_p, _DWORD,
300 300 ctypes.c_void_p, ctypes.c_void_p, ctypes.c_void_p]
301 301 _kernel32.PeekNamedPipe.restype = _BOOL
302 302
303 303 def _raiseoserror(name):
304 304 # Force the code to a signed int to avoid an 'int too large' error.
305 305 # See https://bugs.python.org/issue28474
306 306 code = _kernel32.GetLastError()
307 307 if code > 0x7fffffff:
308 308 code -= 2**32
309 309 err = ctypes.WinError(code=code)
310 310 raise OSError(err.errno, r'%s: %s' % (encoding.strfromlocal(name),
311 311 err.strerror))
312 312
313 313 def _getfileinfo(name):
314 314 fh = _kernel32.CreateFileA(name, 0,
315 315 _FILE_SHARE_READ | _FILE_SHARE_WRITE | _FILE_SHARE_DELETE,
316 316 None, _OPEN_EXISTING, _FILE_FLAG_BACKUP_SEMANTICS, None)
317 317 if fh == _INVALID_HANDLE_VALUE:
318 318 _raiseoserror(name)
319 319 try:
320 320 fi = _BY_HANDLE_FILE_INFORMATION()
321 321 if not _kernel32.GetFileInformationByHandle(fh, ctypes.byref(fi)):
322 322 _raiseoserror(name)
323 323 return fi
324 324 finally:
325 325 _kernel32.CloseHandle(fh)
326 326
327 327 def checkcertificatechain(cert, build=True):
328 328 '''Tests the given certificate to see if there is a complete chain to a
329 329 trusted root certificate. As a side effect, missing certificates are
330 330 downloaded and installed unless ``build=False``. True is returned if a
331 331 chain to a trusted root exists (even if built on the fly), otherwise
332 332 False. NB: A chain to a trusted root does NOT imply that the certificate
333 333 is valid.
334 334 '''
335 335
336 336 chainctxptr = ctypes.POINTER(CERT_CHAIN_CONTEXT)
337 337
338 338 pchainctx = chainctxptr()
339 339 chainpara = CERT_CHAIN_PARA(cbSize=ctypes.sizeof(CERT_CHAIN_PARA),
340 340 RequestedUsage=CERT_USAGE_MATCH())
341 341
342 342 certctx = _crypt32.CertCreateCertificateContext(X509_ASN_ENCODING, cert,
343 343 len(cert))
344 344 if certctx is None:
345 345 _raiseoserror('CertCreateCertificateContext')
346 346
347 347 flags = 0
348 348
349 349 if not build:
350 350 flags |= 0x100 # CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE
351 351
352 352 try:
353 353 # Building the certificate chain will update root certs as necessary.
354 354 if not _crypt32.CertGetCertificateChain(None, # hChainEngine
355 355 certctx, # pCertContext
356 356 None, # pTime
357 357 None, # hAdditionalStore
358 358 ctypes.byref(chainpara),
359 359 flags,
360 360 None, # pvReserved
361 361 ctypes.byref(pchainctx)):
362 362 _raiseoserror('CertGetCertificateChain')
363 363
364 364 chainctx = pchainctx.contents
365 365
366 366 return chainctx.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN == 0
367 367 finally:
368 368 if pchainctx:
369 369 _crypt32.CertFreeCertificateChain(pchainctx)
370 370 _crypt32.CertFreeCertificateContext(certctx)
371 371
372 372 def oslink(src, dst):
373 373 try:
374 374 if not _kernel32.CreateHardLinkA(dst, src, None):
375 375 _raiseoserror(src)
376 376 except AttributeError: # Wine doesn't support this function
377 377 _raiseoserror(src)
378 378
379 379 def nlinks(name):
380 380 '''return number of hardlinks for the given file'''
381 381 return _getfileinfo(name).nNumberOfLinks
382 382
383 383 def samefile(path1, path2):
384 384 '''Returns whether path1 and path2 refer to the same file or directory.'''
385 385 res1 = _getfileinfo(path1)
386 386 res2 = _getfileinfo(path2)
387 387 return (res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
388 388 and res1.nFileIndexHigh == res2.nFileIndexHigh
389 389 and res1.nFileIndexLow == res2.nFileIndexLow)
390 390
391 391 def samedevice(path1, path2):
392 392 '''Returns whether path1 and path2 are on the same device.'''
393 393 res1 = _getfileinfo(path1)
394 394 res2 = _getfileinfo(path2)
395 395 return res1.dwVolumeSerialNumber == res2.dwVolumeSerialNumber
396 396
397 397 def peekpipe(pipe):
398 398 handle = msvcrt.get_osfhandle(pipe.fileno())
399 399 avail = _DWORD()
400 400
401 401 if not _kernel32.PeekNamedPipe(handle, None, 0, None, ctypes.byref(avail),
402 402 None):
403 403 err = _kernel32.GetLastError()
404 404 if err == _ERROR_BROKEN_PIPE:
405 405 return 0
406 406 raise ctypes.WinError(err)
407 407
408 408 return avail.value
409 409
410 410 def lasterrorwaspipeerror(err):
411 411 if err.errno != errno.EINVAL:
412 412 return False
413 413 err = _kernel32.GetLastError()
414 414 return err == _ERROR_BROKEN_PIPE or err == _ERROR_NO_DATA
415 415
416 416 def testpid(pid):
417 417 '''return True if pid is still running or unable to
418 418 determine, False otherwise'''
419 419 h = _kernel32.OpenProcess(_PROCESS_QUERY_INFORMATION, False, pid)
420 420 if h:
421 421 try:
422 422 status = _DWORD()
423 423 if _kernel32.GetExitCodeProcess(h, ctypes.byref(status)):
424 424 return status.value == _STILL_ACTIVE
425 425 finally:
426 426 _kernel32.CloseHandle(h)
427 427 return _kernel32.GetLastError() != _ERROR_INVALID_PARAMETER
428 428
429 429 def executablepath():
430 430 '''return full path of hg.exe'''
431 431 size = 600
432 432 buf = ctypes.create_string_buffer(size + 1)
433 433 len = _kernel32.GetModuleFileNameA(None, ctypes.byref(buf), size)
434 434 if len == 0:
435 435 raise ctypes.WinError() # Note: WinError is a function
436 436 elif len == size:
437 437 raise ctypes.WinError(_ERROR_INSUFFICIENT_BUFFER)
438 438 return buf.value
439 439
440 440 def getvolumename(path):
441 441 """Get the mount point of the filesystem from a directory or file
442 442 (best-effort)
443 443
444 444 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
445 445 """
446 446 # realpath() calls GetFullPathName()
447 447 realpath = os.path.realpath(path)
448 448
449 449 # allocate at least MAX_PATH long since GetVolumePathName('c:\\', buf, 4)
450 450 # somehow fails on Windows XP
451 451 size = max(len(realpath), _MAX_PATH) + 1
452 452 buf = ctypes.create_string_buffer(size)
453 453
454 454 if not _kernel32.GetVolumePathNameA(realpath, ctypes.byref(buf), size):
455 455 raise ctypes.WinError() # Note: WinError is a function
456 456
457 457 return buf.value
458 458
459 459 def getfstype(path):
460 460 """Get the filesystem type name from a directory or file (best-effort)
461 461
462 462 Returns None if we are unsure. Raises OSError on ENOENT, EPERM, etc.
463 463 """
464 464 volume = getvolumename(path)
465 465
466 466 t = _kernel32.GetDriveTypeA(volume)
467 467
468 468 if t == _DRIVE_REMOTE:
469 469 return 'cifs'
470 470 elif t not in (_DRIVE_REMOVABLE, _DRIVE_FIXED, _DRIVE_CDROM,
471 471 _DRIVE_RAMDISK):
472 472 return None
473 473
474 474 size = _MAX_PATH + 1
475 475 name = ctypes.create_string_buffer(size)
476 476
477 477 if not _kernel32.GetVolumeInformationA(volume, None, 0, None, None, None,
478 478 ctypes.byref(name), size):
479 479 raise ctypes.WinError() # Note: WinError is a function
480 480
481 481 return name.value
482 482
483 483 def getuser():
484 484 '''return name of current user'''
485 485 size = _DWORD(300)
486 486 buf = ctypes.create_string_buffer(size.value + 1)
487 487 if not _advapi32.GetUserNameA(ctypes.byref(buf), ctypes.byref(size)):
488 488 raise ctypes.WinError()
489 489 return buf.value
490 490
491 491 _signalhandler = []
492 492
493 493 def setsignalhandler():
494 494 '''Register a termination handler for console events including
495 495 CTRL+C. python signal handlers do not work well with socket
496 496 operations.
497 497 '''
498 498 def handler(event):
499 499 _kernel32.ExitProcess(1)
500 500
501 501 if _signalhandler:
502 502 return # already registered
503 503 h = _SIGNAL_HANDLER(handler)
504 504 _signalhandler.append(h) # needed to prevent garbage collection
505 505 if not _kernel32.SetConsoleCtrlHandler(h, True):
506 506 raise ctypes.WinError()
507 507
508 508 def hidewindow():
509 509
510 510 def callback(hwnd, pid):
511 511 wpid = _DWORD()
512 512 _user32.GetWindowThreadProcessId(hwnd, ctypes.byref(wpid))
513 513 if pid == wpid.value:
514 514 _user32.ShowWindow(hwnd, _SW_HIDE)
515 515 return False # stop enumerating windows
516 516 return True
517 517
518 518 pid = _kernel32.GetCurrentProcessId()
519 519 _user32.EnumWindows(_WNDENUMPROC(callback), pid)
520 520
521 521 def termsize():
522 522 # cmd.exe does not handle CR like a unix console, the CR is
523 523 # counted in the line length. On 80 columns consoles, if 80
524 524 # characters are written, the following CR won't apply on the
525 525 # current line but on the new one. Keep room for it.
526 526 width = 80 - 1
527 527 height = 25
528 528 # Query stderr to avoid problems with redirections
529 529 screenbuf = _kernel32.GetStdHandle(
530 530 _STD_ERROR_HANDLE) # don't close the handle returned
531 531 if screenbuf is None or screenbuf == _INVALID_HANDLE_VALUE:
532 532 return width, height
533 533 csbi = _CONSOLE_SCREEN_BUFFER_INFO()
534 534 if not _kernel32.GetConsoleScreenBufferInfo(
535 535 screenbuf, ctypes.byref(csbi)):
536 536 return width, height
537 537 width = csbi.srWindow.Right - csbi.srWindow.Left # don't '+ 1'
538 538 height = csbi.srWindow.Bottom - csbi.srWindow.Top + 1
539 539 return width, height
540 540
541 541 def enablevtmode():
542 542 '''Enable virtual terminal mode for the associated console. Return True if
543 543 enabled, else False.'''
544 544
545 545 ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4
546 546
547 547 handle = _kernel32.GetStdHandle(_STD_OUTPUT_HANDLE) # don't close the handle
548 548 if handle == _INVALID_HANDLE_VALUE:
549 549 return False
550 550
551 551 mode = _DWORD(0)
552 552
553 553 if not _kernel32.GetConsoleMode(handle, ctypes.byref(mode)):
554 554 return False
555 555
556 556 if (mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING) == 0:
557 557 mode.value |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
558 558
559 559 if not _kernel32.SetConsoleMode(handle, mode):
560 560 return False
561 561
562 562 return True
563 563
564 564 def spawndetached(args):
565 565 # No standard library function really spawns a fully detached
566 566 # process under win32 because they allocate pipes or other objects
567 567 # to handle standard streams communications. Passing these objects
568 568 # to the child process requires handle inheritance to be enabled
569 569 # which makes really detached processes impossible.
570 570 si = _STARTUPINFO()
571 571 si.cb = ctypes.sizeof(_STARTUPINFO)
572 572
573 573 pi = _PROCESS_INFORMATION()
574 574
575 575 env = ''
576 576 for k in encoding.environ:
577 577 env += "%s=%s\0" % (k, encoding.environ[k])
578 578 if not env:
579 579 env = '\0'
580 580 env += '\0'
581 581
582 582 args = subprocess.list2cmdline(pycompat.rapply(encoding.strfromlocal, args))
583 583
584 584 # TODO: CreateProcessW on py3?
585 585 res = _kernel32.CreateProcessA(
586 586 None, encoding.strtolocal(args), None, None, False, _CREATE_NO_WINDOW,
587 587 env, encoding.getcwd(), ctypes.byref(si), ctypes.byref(pi))
588 588 if not res:
589 589 raise ctypes.WinError()
590 590
591 _kernel32.CloseHandle(pi.hProcess)
592 _kernel32.CloseHandle(pi.hThread)
593
591 594 return pi.dwProcessId
592 595
593 596 def unlink(f):
594 597 '''try to implement POSIX' unlink semantics on Windows'''
595 598
596 599 if os.path.isdir(f):
597 600 # use EPERM because it is POSIX prescribed value, even though
598 601 # unlink(2) on directories returns EISDIR on Linux
599 602 raise IOError(errno.EPERM,
600 603 r"Unlinking directory not permitted: '%s'"
601 604 % encoding.strfromlocal(f))
602 605
603 606 # POSIX allows to unlink and rename open files. Windows has serious
604 607 # problems with doing that:
605 608 # - Calling os.unlink (or os.rename) on a file f fails if f or any
606 609 # hardlinked copy of f has been opened with Python's open(). There is no
607 610 # way such a file can be deleted or renamed on Windows (other than
608 611 # scheduling the delete or rename for the next reboot).
609 612 # - Calling os.unlink on a file that has been opened with Mercurial's
610 613 # posixfile (or comparable methods) will delay the actual deletion of
611 614 # the file for as long as the file is held open. The filename is blocked
612 615 # during that time and cannot be used for recreating a new file under
613 616 # that same name ("zombie file"). Directories containing such zombie files
614 617 # cannot be removed or moved.
615 618 # A file that has been opened with posixfile can be renamed, so we rename
616 619 # f to a random temporary name before calling os.unlink on it. This allows
617 620 # callers to recreate f immediately while having other readers do their
618 621 # implicit zombie filename blocking on a temporary name.
619 622
620 623 for tries in pycompat.xrange(10):
621 624 temp = '%s-%08x' % (f, random.randint(0, 0xffffffff))
622 625 try:
623 626 os.rename(f, temp) # raises OSError EEXIST if temp exists
624 627 break
625 628 except OSError as e:
626 629 if e.errno != errno.EEXIST:
627 630 raise
628 631 else:
629 632 raise IOError(errno.EEXIST, r"No usable temporary filename found")
630 633
631 634 try:
632 635 os.unlink(temp)
633 636 except OSError:
634 637 # The unlink might have failed because the READONLY attribute may heave
635 638 # been set on the original file. Rename works fine with READONLY set,
636 639 # but not os.unlink. Reset all attributes and try again.
637 640 _kernel32.SetFileAttributesA(temp, _FILE_ATTRIBUTE_NORMAL)
638 641 try:
639 642 os.unlink(temp)
640 643 except OSError:
641 644 # The unlink might have failed due to some very rude AV-Scanners.
642 645 # Leaking a tempfile is the lesser evil than aborting here and
643 646 # leaving some potentially serious inconsistencies.
644 647 pass
645 648
646 649 def makedir(path, notindexed):
647 650 os.mkdir(path)
648 651 if notindexed:
649 652 _kernel32.SetFileAttributesA(path, _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
General Comments 0
You need to be logged in to leave comments. Login now