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