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 |