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 29 import os 30 import SocketServer 31 import stat 32 import subprocess 33 import threading 34 35 from . import compression 36 from . import constants 37 from . import signatures 38 from ..network import endpoint 39 from ..objects import workpacket 40 41 42 class 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 145 class 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