# HG changeset patch # User Atul Varma # Date 1275588333 25200 # Node ID 4ba34e1cf3108d067b157e0faa24e2f51009c1a4 # Parent 88da4618d578d02699b983087d29e8bec5222e29 untrusted hg commands now only have 60 seconds to execute. diff -r 88da4618d578 -r 4ba34e1cf310 bzezpatch/hg.py --- a/bzezpatch/hg.py Thu Jun 03 09:41:11 2010 -0700 +++ b/bzezpatch/hg.py Thu Jun 03 11:05:33 2010 -0700 @@ -4,9 +4,16 @@ import tempfile import shutil +# Only give untrusted hg commands this long to execute. +DEFAULT_TIMEOUT = 60 + class Hg(object): - def __init__(self, **kwargs): - self.__dict__.update(kwargs) + def __init__(self, canonical_repo, hg='hg', timeout=DEFAULT_TIMEOUT): + self.canonical_repo = canonical_repo + self.hg = hg + self.timeout = timeout + self.timelimiter = os.path.join(os.path.dirname(__file__), + 'timelimiter.py') def trypatch(self, pull_url, out=sys.stdout, privout=sys.stdout): temp_dir = tempfile.mkdtemp(prefix='hg-trypatch-') @@ -18,15 +25,19 @@ privout.flush() def log(msg): + privlog(msg) out.flush() out.write('%s\n' % msg) out.flush() + hg = [sys.executable, self.timelimiter, str(self.timeout), self.hg] + privhg = [self.hg] + try: # Step 1: Pull and update local canonical repo. privlog('pulling and updating local canonical repo') - rv = subprocess.call([self.hg, '-R', self.canonical_repo, - 'pull', '-u'], + rv = subprocess.call(privhg + ['-R', self.canonical_repo, + 'pull', '-u'], stdout=privout, stderr=privout) if rv: log('Warning: server was unable to pull the latest version ' @@ -37,7 +48,8 @@ # Step 2: Figure out tip changeset so we can diff against # it later. - popen = subprocess.Popen([self.hg, '-R', self.canonical_repo, + popen = subprocess.Popen(privhg + + ['-R', self.canonical_repo, 'tip', '--template', '{node}'], stdout=subprocess.PIPE) tip_revision, _ = popen.communicate() @@ -47,25 +59,25 @@ # Step 3: Create a temporary clone to apply the patch to. privlog('creating temporary clone at %s' % temp_repo) - subprocess.check_call([self.hg, 'clone', self.canonical_repo, - temp_repo], + subprocess.check_call(privhg + ['clone', self.canonical_repo, + temp_repo], stdout=privout, stderr=privout) # Step 4: Apply the patch by pulling from the foreign repo. log('pulling from %s' % pull_url) - subprocess.check_call([self.hg, '-R', temp_repo, 'pull', - '--rebase', pull_url], + subprocess.check_call(hg + ['-R', temp_repo, 'pull', + '--rebase', pull_url], stdout=out, stderr=out) - subprocess.check_call([self.hg, '-R', temp_repo, 'pull', - '--update', pull_url], + subprocess.check_call(hg + ['-R', temp_repo, 'pull', + '--update', pull_url], stdout=out, stderr=out) # Step 5: Generate a patch by diffing the tip of the - # foreign repo against - popen = subprocess.Popen([self.hg, '-R', temp_repo, - 'diff', '-r', tip_revision, - '-r', 'tip', - '--git', '--unified', '8'], + # foreign repo against the tip of the canonical one. + popen = subprocess.Popen(hg + ['-R', temp_repo, + 'diff', '-r', tip_revision, + '-r', 'tip', + '--git', '--unified', '8'], stdout=subprocess.PIPE) patch, _ = popen.communicate() if popen.returncode: diff -r 88da4618d578 -r 4ba34e1cf310 bzezpatch/timelimiter.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bzezpatch/timelimiter.py Thu Jun 03 11:05:33 2010 -0700 @@ -0,0 +1,38 @@ +import os +import sys +import subprocess +import threading +import time +import signal + +def killer(pid, deadline, now=time.time, sleep=time.sleep, + sig=signal.SIGINT, kill=os.kill, stderr=sys.stderr): + while now() < deadline: + sleep(0.10) + stderr.flush() + stderr.write('process is taking too long to execute, killing it.\n') + stderr.flush() + kill(pid, sig) + +if __name__ == '__main__': + if len(sys.argv) < 3: + print "usage: %s " % sys.argv[0] + sys.exit(1) + + try: + timeout = int(sys.argv[1]) + except ValueError: + print "error: '%s' is not a valid timeout in seconds" % sys.argv[1] + sys.exit(1) + + cmdline = sys.argv[2:] + popen = subprocess.Popen(cmdline) + watchdog = threading.Thread( + target=killer, + kwargs=dict(pid=popen.pid, + deadline=time.time() + timeout) + ) + watchdog.setDaemon(True) + watchdog.start() + + sys.exit(popen.wait())