comparison mercurial/utils/procutil.py @ 40497:3fbfbc8c9f82

remotefilelog: transplant runbgcommand to procutil While cleaning up the deprecated runshellcommand I noticed a near-clone of this in logtoprocess, so I'm standardizing on what appears to be the newer one by moving it to procutil. Differential Revision: https://phab.mercurial-scm.org/D4938
author Augie Fackler <augie@google.com>
date Wed, 03 Oct 2018 14:01:04 -0400
parents a9f56e4501c1
children 8fab95aa5280
comparison
equal deleted inserted replaced
40496:60eb35b0c11c 40497:3fbfbc8c9f82
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 imp 14 import imp
14 import io 15 import io
15 import os 16 import os
16 import signal 17 import signal
17 import subprocess 18 import subprocess
465 finally: 466 finally:
466 if oldsiginthandler: 467 if oldsiginthandler:
467 signal.signal(signal.SIGINT, oldsiginthandler[0]) 468 signal.signal(signal.SIGINT, oldsiginthandler[0])
468 if shouldbail: 469 if shouldbail:
469 raise KeyboardInterrupt 470 raise KeyboardInterrupt
471
472 if pycompat.iswindows:
473 # no fork on Windows, but we can create a detached process
474 # https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863.aspx
475 # No stdlib constant exists for this value
476 DETACHED_PROCESS = 0x00000008
477 _creationflags = DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP
478
479 def runbgcommand(script, env, shell=False, stdout=None, stderr=None):
480 '''Spawn a command without waiting for it to finish.'''
481 # we can't use close_fds *and* redirect stdin. I'm not sure that we
482 # need to because the detached process has no console connection.
483 subprocess.Popen(
484 script, shell=shell, env=env, close_fds=True,
485 creationflags=_creationflags, stdout=stdout, stderr=stderr)
486 else:
487 def runbgcommand(cmd, env, shell=False, stdout=None, stderr=None):
488 '''Spawn a command without waiting for it to finish.'''
489 # double-fork to completely detach from the parent process
490 # based on http://code.activestate.com/recipes/278731
491 pid = os.fork()
492 if pid:
493 # Parent process
494 (_pid, status) = os.waitpid(pid, 0)
495 if os.WIFEXITED(status):
496 returncode = os.WEXITSTATUS(status)
497 else:
498 returncode = -os.WTERMSIG(status)
499 if returncode != 0:
500 # The child process's return code is 0 on success, an errno
501 # value on failure, or 255 if we don't have a valid errno
502 # value.
503 #
504 # (It would be slightly nicer to return the full exception info
505 # over a pipe as the subprocess module does. For now it
506 # doesn't seem worth adding that complexity here, though.)
507 if returncode == 255:
508 returncode = errno.EINVAL
509 raise OSError(returncode, 'error running %r: %s' %
510 (cmd, os.strerror(returncode)))
511 return
512
513 returncode = 255
514 try:
515 # Start a new session
516 os.setsid()
517
518 stdin = open(os.devnull, 'r')
519 if stdout is None:
520 stdout = open(os.devnull, 'w')
521 if stderr is None:
522 stderr = open(os.devnull, 'w')
523
524 # connect stdin to devnull to make sure the subprocess can't
525 # muck up that stream for mercurial.
526 subprocess.Popen(
527 cmd, shell=shell, env=env, close_fds=True,
528 stdin=stdin, stdout=stdout, stderr=stderr)
529 returncode = 0
530 except EnvironmentError as ex:
531 returncode = (ex.errno & 0xff)
532 if returncode == 0:
533 # This shouldn't happen, but just in case make sure the
534 # return code is never 0 here.
535 returncode = 255
536 except Exception:
537 returncode = 255
538 finally:
539 # mission accomplished, this child needs to exit and not
540 # continue the hg process here.
541 os._exit(returncode)