sigpipe-remote.py
155 lines
| 4.2 KiB
| text/x-python
|
PythonLexer
r48352 | #!/usr/bin/env python3 | |||
from __future__ import print_function | ||||
r48417 | import io | |||
r48352 | import os | |||
import subprocess | ||||
import sys | ||||
import time | ||||
r48436 | # we cannot use mercurial.testing as long as python2 is not dropped as the test | |||
# will only install the mercurial module for python2 in python2 run | ||||
if sys.version_info[0] < 3: | ||||
r48437 | ver = '.'.join(str(x) for x in sys.version_info) | |||
exe = sys.executable | ||||
r48436 | print('SIGPIPE-HELPER: script should run with Python 3', file=sys.stderr) | |||
r48437 | print('SIGPIPE-HELPER: %s is running %s' % (exe, ver), file=sys.stderr) | |||
r48436 | sys.exit(255) | |||
r48352 | ||||
r48417 | if isinstance(sys.stdout.buffer, io.BufferedWriter): | |||
print('SIGPIPE-HELPER: script need unbuffered output', file=sys.stderr) | ||||
sys.exit(255) | ||||
r48416 | DEBUG_FILE = os.environ.get('SIGPIPE_REMOTE_DEBUG_FILE') | |||
if DEBUG_FILE is None: | ||||
debug_stream = sys.stderr.buffer | ||||
else: | ||||
debug_stream = open(DEBUG_FILE, 'bw', buffering=0) | ||||
SYNCFILE1 = os.environ.get('SYNCFILE1') | ||||
SYNCFILE2 = os.environ.get('SYNCFILE2') | ||||
if SYNCFILE1 is None: | ||||
print('SIGPIPE-HELPER: missing variable $SYNCFILE1', file=sys.stderr) | ||||
sys.exit(255) | ||||
if SYNCFILE2 is None: | ||||
print('SIGPIPE-HELPER: missing variable $SYNCFILE2', file=sys.stderr) | ||||
sys.exit(255) | ||||
r48352 | ||||
def _timeout_factor(): | ||||
"""return the current modification to timeout""" | ||||
default = int(os.environ.get('HGTEST_TIMEOUT_DEFAULT', 360)) | ||||
current = int(os.environ.get('HGTEST_TIMEOUT', default)) | ||||
if current == 0: | ||||
return 1 | ||||
return current / float(default) | ||||
def wait_file(path, timeout=10): | ||||
timeout *= _timeout_factor() | ||||
start = time.time() | ||||
while not os.path.exists(path): | ||||
if (time.time() - start) > timeout: | ||||
raise RuntimeError(b"timed out waiting for file: %s" % path) | ||||
time.sleep(0.01) | ||||
def write_file(path, content=b''): | ||||
with open(path, 'wb') as f: | ||||
f.write(content) | ||||
# end of mercurial.testing content | ||||
def sysbytes(s): | ||||
return s.encode('utf-8') | ||||
def sysstr(s): | ||||
return s.decode('latin-1') | ||||
debug_stream.write(b'SIGPIPE-HELPER: Starting\n') | ||||
TESTLIB_DIR = os.path.dirname(sys.argv[0]) | ||||
WAIT_SCRIPT = os.path.join(TESTLIB_DIR, 'wait-on-file') | ||||
hooks_cmd = '%s 10 %s %s' | ||||
hooks_cmd %= ( | ||||
WAIT_SCRIPT, | ||||
SYNCFILE2, | ||||
SYNCFILE1, | ||||
) | ||||
r48418 | try: | |||
cmd = ['hg'] | ||||
cmd += sys.argv[1:] | ||||
sub = subprocess.Popen( | ||||
cmd, | ||||
bufsize=0, | ||||
close_fds=True, | ||||
stdin=sys.stdin, | ||||
stdout=subprocess.PIPE, | ||||
stderr=subprocess.PIPE, | ||||
) | ||||
r48352 | ||||
r48418 | basedir = os.path.dirname(sys.argv[0]) | |||
worker = os.path.join(basedir, 'sigpipe-worker.py') | ||||
r48352 | ||||
r48418 | cmd = [sys.executable, worker] | |||
r48352 | ||||
r48418 | stdout_worker = subprocess.Popen( | |||
cmd, | ||||
bufsize=0, | ||||
close_fds=True, | ||||
stdin=sub.stdout, | ||||
stdout=sys.stdout, | ||||
stderr=sys.stderr, | ||||
) | ||||
r48352 | ||||
r48418 | stderr_worker = subprocess.Popen( | |||
cmd, | ||||
bufsize=0, | ||||
close_fds=True, | ||||
stdin=sub.stderr, | ||||
stdout=sys.stderr, | ||||
stderr=sys.stderr, | ||||
) | ||||
r48352 | debug_stream.write(b'SIGPIPE-HELPER: Redirection in place\n') | |||
r48418 | os.close(sub.stdout.fileno()) | |||
os.close(sub.stderr.fileno()) | ||||
debug_stream.write(b'SIGPIPE-HELPER: pipes closed in main\n') | ||||
r48352 | ||||
try: | ||||
wait_file(sysbytes(SYNCFILE1)) | ||||
except RuntimeError as exc: | ||||
msg = sysbytes(str(exc)) | ||||
debug_stream.write(b'SIGPIPE-HELPER: wait failed: %s\n' % msg) | ||||
else: | ||||
debug_stream.write(b'SIGPIPE-HELPER: SYNCFILE1 detected\n') | ||||
r48418 | stdout_worker.kill() | |||
stderr_worker.kill() | ||||
stdout_worker.wait(10) | ||||
stderr_worker.wait(10) | ||||
debug_stream.write(b'SIGPIPE-HELPER: worker killed\n') | ||||
r48352 | debug_stream.write(b'SIGPIPE-HELPER: creating SYNCFILE2\n') | |||
write_file(sysbytes(SYNCFILE2)) | ||||
finally: | ||||
debug_stream.write(b'SIGPIPE-HELPER: Shutting down\n') | ||||
if not sys.stdin.closed: | ||||
sys.stdin.close() | ||||
try: | ||||
sub.wait(timeout=30) | ||||
except subprocess.TimeoutExpired: | ||||
msg = b'SIGPIPE-HELPER: Server process failed to terminate\n' | ||||
debug_stream.write(msg) | ||||
r48418 | sub.kill() | |||
sub.wait() | ||||
msg = b'SIGPIPE-HELPER: Server process killed\n' | ||||
r48352 | else: | |||
r48418 | msg = b'SIGPIPE-HELPER: Server process terminated with status %d\n' | |||
msg %= sub.returncode | ||||
debug_stream.write(msg) | ||||
r48352 | debug_stream.write(b'SIGPIPE-HELPER: Shut down\n') | |||