mercurial/utils/procutil.py
changeset 43873 5606e1cb4685
parent 43807 be8552f25cab
parent 43456 15a6c6783060
child 43918 a89381e04c58
equal deleted inserted replaced
43872:aac921f54554 43873:5606e1cb4685
   538         DETACHED_PROCESS
   538         DETACHED_PROCESS
   539         | subprocess.CREATE_NEW_PROCESS_GROUP  # pytype: disable=module-attr
   539         | subprocess.CREATE_NEW_PROCESS_GROUP  # pytype: disable=module-attr
   540     )
   540     )
   541 
   541 
   542     def runbgcommand(
   542     def runbgcommand(
   543         script, env, shell=False, stdout=None, stderr=None, ensurestart=True
   543         script,
       
   544         env,
       
   545         shell=False,
       
   546         stdout=None,
       
   547         stderr=None,
       
   548         ensurestart=True,
       
   549         record_wait=None,
   544     ):
   550     ):
   545         '''Spawn a command without waiting for it to finish.'''
   551         '''Spawn a command without waiting for it to finish.'''
   546         # we can't use close_fds *and* redirect stdin. I'm not sure that we
   552         # we can't use close_fds *and* redirect stdin. I'm not sure that we
   547         # need to because the detached process has no console connection.
   553         # need to because the detached process has no console connection.
   548         subprocess.Popen(
   554         p = subprocess.Popen(
   549             tonativestr(script),
   555             tonativestr(script),
   550             shell=shell,
   556             shell=shell,
   551             env=tonativeenv(env),
   557             env=tonativeenv(env),
   552             close_fds=True,
   558             close_fds=True,
   553             creationflags=_creationflags,
   559             creationflags=_creationflags,
   554             stdout=stdout,
   560             stdout=stdout,
   555             stderr=stderr,
   561             stderr=stderr,
   556         )
   562         )
       
   563         if record_wait is not None:
       
   564             record_wait(p.wait)
   557 
   565 
   558 
   566 
   559 else:
   567 else:
   560 
   568 
   561     def runbgcommand(
   569     def runbgcommand(
   562         cmd, env, shell=False, stdout=None, stderr=None, ensurestart=True
   570         cmd,
       
   571         env,
       
   572         shell=False,
       
   573         stdout=None,
       
   574         stderr=None,
       
   575         ensurestart=True,
       
   576         record_wait=None,
   563     ):
   577     ):
   564         '''Spawn a command without waiting for it to finish.'''
   578         '''Spawn a command without waiting for it to finish.
       
   579 
       
   580 
       
   581         When `record_wait` is not None, the spawned process will not be fully
       
   582         detached and the `record_wait` argument will be called with a the
       
   583         `Subprocess.wait` function for the spawned process.  This is mostly
       
   584         useful for developers that need to make sure the spawned process
       
   585         finished before a certain point. (eg: writing test)'''
   565         # double-fork to completely detach from the parent process
   586         # double-fork to completely detach from the parent process
   566         # based on http://code.activestate.com/recipes/278731
   587         # based on http://code.activestate.com/recipes/278731
   567         pid = os.fork()
   588         if record_wait is None:
   568         if pid:
   589             pid = os.fork()
   569             if not ensurestart:
   590             if pid:
       
   591                 if not ensurestart:
       
   592                     return
       
   593                 # Parent process
       
   594                 (_pid, status) = os.waitpid(pid, 0)
       
   595                 if os.WIFEXITED(status):
       
   596                     returncode = os.WEXITSTATUS(status)
       
   597                 else:
       
   598                     returncode = -(os.WTERMSIG(status))
       
   599                 if returncode != 0:
       
   600                     # The child process's return code is 0 on success, an errno
       
   601                     # value on failure, or 255 if we don't have a valid errno
       
   602                     # value.
       
   603                     #
       
   604                     # (It would be slightly nicer to return the full exception info
       
   605                     # over a pipe as the subprocess module does.  For now it
       
   606                     # doesn't seem worth adding that complexity here, though.)
       
   607                     if returncode == 255:
       
   608                         returncode = errno.EINVAL
       
   609                     raise OSError(
       
   610                         returncode,
       
   611                         b'error running %r: %s'
       
   612                         % (cmd, os.strerror(returncode)),
       
   613                     )
   570                 return
   614                 return
   571             # Parent process
       
   572             (_pid, status) = os.waitpid(pid, 0)
       
   573             if os.WIFEXITED(status):
       
   574                 returncode = os.WEXITSTATUS(status)
       
   575             else:
       
   576                 returncode = -(os.WTERMSIG(status))
       
   577             if returncode != 0:
       
   578                 # The child process's return code is 0 on success, an errno
       
   579                 # value on failure, or 255 if we don't have a valid errno
       
   580                 # value.
       
   581                 #
       
   582                 # (It would be slightly nicer to return the full exception info
       
   583                 # over a pipe as the subprocess module does.  For now it
       
   584                 # doesn't seem worth adding that complexity here, though.)
       
   585                 if returncode == 255:
       
   586                     returncode = errno.EINVAL
       
   587                 raise OSError(
       
   588                     returncode,
       
   589                     b'error running %r: %s' % (cmd, os.strerror(returncode)),
       
   590                 )
       
   591             return
       
   592 
   615 
   593         returncode = 255
   616         returncode = 255
   594         try:
   617         try:
   595             # Start a new session
   618             if record_wait is None:
   596             os.setsid()
   619                 # Start a new session
       
   620                 os.setsid()
   597 
   621 
   598             stdin = open(os.devnull, b'r')
   622             stdin = open(os.devnull, b'r')
   599             if stdout is None:
   623             if stdout is None:
   600                 stdout = open(os.devnull, b'w')
   624                 stdout = open(os.devnull, b'w')
   601             if stderr is None:
   625             if stderr is None:
   602                 stderr = open(os.devnull, b'w')
   626                 stderr = open(os.devnull, b'w')
   603 
   627 
   604             # connect stdin to devnull to make sure the subprocess can't
   628             # connect stdin to devnull to make sure the subprocess can't
   605             # muck up that stream for mercurial.
   629             # muck up that stream for mercurial.
   606             subprocess.Popen(
   630             p = subprocess.Popen(
   607                 cmd,
   631                 cmd,
   608                 shell=shell,
   632                 shell=shell,
   609                 env=env,
   633                 env=env,
   610                 close_fds=True,
   634                 close_fds=True,
   611                 stdin=stdin,
   635                 stdin=stdin,
   612                 stdout=stdout,
   636                 stdout=stdout,
   613                 stderr=stderr,
   637                 stderr=stderr,
   614             )
   638             )
       
   639             if record_wait is not None:
       
   640                 record_wait(p.wait)
   615             returncode = 0
   641             returncode = 0
   616         except EnvironmentError as ex:
   642         except EnvironmentError as ex:
   617             returncode = ex.errno & 0xFF
   643             returncode = ex.errno & 0xFF
   618             if returncode == 0:
   644             if returncode == 0:
   619                 # This shouldn't happen, but just in case make sure the
   645                 # This shouldn't happen, but just in case make sure the
   622         except Exception:
   648         except Exception:
   623             returncode = 255
   649             returncode = 255
   624         finally:
   650         finally:
   625             # mission accomplished, this child needs to exit and not
   651             # mission accomplished, this child needs to exit and not
   626             # continue the hg process here.
   652             # continue the hg process here.
   627             os._exit(returncode)
   653             if record_wait is None:
       
   654                 os._exit(returncode)