1# Copyright 2012 the V8 project authors. All rights reserved. 2# Redistribution and use in source and binary forms, with or without 3# modification, are permitted provided that the following conditions are 4# met: 5# 6# * Redistributions of source code must retain the above copyright 7# notice, this list of conditions and the following disclaimer. 8# * Redistributions in binary form must reproduce the above 9# copyright notice, this list of conditions and the following 10# disclaimer in the documentation and/or other materials provided 11# with the distribution. 12# * Neither the name of Google Inc. nor the names of its 13# contributors may be used to endorse or promote products derived 14# from this software without specific prior written permission. 15# 16# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 29import os 30import SocketServer 31import stat 32import subprocess 33import threading 34 35from . import compression 36from . import constants 37from . import signatures 38from ..network import endpoint 39from ..objects import workpacket 40 41 42class WorkHandler(SocketServer.BaseRequestHandler): 43 44 def handle(self): 45 rec = compression.Receiver(self.request) 46 while not rec.IsDone(): 47 data = rec.Current() 48 with self.server.job_lock: 49 self._WorkOnWorkPacket(data) 50 rec.Advance() 51 52 def _WorkOnWorkPacket(self, data): 53 server_root = self.server.daemon.root 54 v8_root = os.path.join(server_root, "v8") 55 os.chdir(v8_root) 56 packet = workpacket.WorkPacket.Unpack(data) 57 self.ctx = packet.context 58 self.ctx.shell_dir = os.path.join("out", 59 "%s.%s" % (self.ctx.arch, self.ctx.mode)) 60 if not os.path.isdir(self.ctx.shell_dir): 61 os.makedirs(self.ctx.shell_dir) 62 for binary in packet.binaries: 63 if not self._UnpackBinary(binary, packet.pubkey_fingerprint): 64 return 65 66 if not self._CheckoutRevision(packet.base_revision): 67 return 68 69 if not self._ApplyPatch(packet.patch): 70 return 71 72 tests = packet.tests 73 endpoint.Execute(v8_root, self.ctx, tests, self.request, self.server.daemon) 74 self._SendResponse() 75 76 def _SendResponse(self, error_message=None): 77 try: 78 if error_message: 79 compression.Send([[-1, error_message]], self.request) 80 compression.Send(constants.END_OF_STREAM, self.request) 81 return 82 except Exception, e: 83 pass # Peer is gone. There's nothing we can do. 84 # Clean up. 85 self._Call("git checkout -f") 86 self._Call("git clean -f -d") 87 self._Call("rm -rf %s" % self.ctx.shell_dir) 88 89 def _UnpackBinary(self, binary, pubkey_fingerprint): 90 binary_name = binary["name"] 91 if binary_name == "libv8.so": 92 libdir = os.path.join(self.ctx.shell_dir, "lib.target") 93 if not os.path.exists(libdir): os.makedirs(libdir) 94 target = os.path.join(libdir, binary_name) 95 else: 96 target = os.path.join(self.ctx.shell_dir, binary_name) 97 pubkeyfile = "../trusted/%s.pem" % pubkey_fingerprint 98 if not signatures.VerifySignature(target, binary["blob"], 99 binary["sign"], pubkeyfile): 100 self._SendResponse("Signature verification failed") 101 return False 102 os.chmod(target, stat.S_IRWXU) 103 return True 104 105 def _CheckoutRevision(self, base_svn_revision): 106 get_hash_cmd = ( 107 "git log -1 --format=%%H --remotes --grep='^git-svn-id:.*@%s'" % 108 base_svn_revision) 109 try: 110 base_revision = subprocess.check_output(get_hash_cmd, shell=True) 111 if not base_revision: raise ValueError 112 except: 113 self._Call("git fetch") 114 try: 115 base_revision = subprocess.check_output(get_hash_cmd, shell=True) 116 if not base_revision: raise ValueError 117 except: 118 self._SendResponse("Base revision not found.") 119 return False 120 code = self._Call("git checkout -f %s" % base_revision) 121 if code != 0: 122 self._SendResponse("Error trying to check out base revision.") 123 return False 124 code = self._Call("git clean -f -d") 125 if code != 0: 126 self._SendResponse("Failed to reset checkout") 127 return False 128 return True 129 130 def _ApplyPatch(self, patch): 131 if not patch: return True # Just skip if the patch is empty. 132 patchfilename = "_dtest_incoming_patch.patch" 133 with open(patchfilename, "w") as f: 134 f.write(patch) 135 code = self._Call("git apply %s" % patchfilename) 136 if code != 0: 137 self._SendResponse("Error applying patch.") 138 return False 139 return True 140 141 def _Call(self, cmd): 142 return subprocess.call(cmd, shell=True) 143 144 145class WorkSocketServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): 146 def __init__(self, daemon): 147 address = (daemon.ip, constants.PEER_PORT) 148 SocketServer.TCPServer.__init__(self, address, WorkHandler) 149 self.job_lock = threading.Lock() 150 self.daemon = daemon 151