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