# # BitBake XMLRPC Server Interface # # Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer # Copyright (C) 2006 - 2008 Richard Purdie # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2 as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. import os import sys import hashlib import time import inspect from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler import bb # This request handler checks if the request has a "Bitbake-token" header # field (this comes from the client side) and compares it with its internal # "Bitbake-token" field (this comes from the server). If the two are not # equal, it is assumed that a client is trying to connect to the server # while another client is connected to the server. In this case, a 503 error # ("service unavailable") is returned to the client. class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): def __init__(self, request, client_address, server): self.server = server SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server) def do_POST(self): try: remote_token = self.headers["Bitbake-token"] except: remote_token = None if 0 and remote_token != self.server.connection_token and remote_token != "observer": self.report_503() else: if remote_token == "observer": self.server.readonly = True else: self.server.readonly = False SimpleXMLRPCRequestHandler.do_POST(self) def report_503(self): self.send_response(503) response = 'No more client allowed' self.send_header("Content-type", "text/plain") self.send_header("Content-length", str(len(response))) self.end_headers() self.wfile.write(bytes(response, 'utf-8')) class BitBakeXMLRPCServer(SimpleXMLRPCServer): # remove this when you're done with debugging # allow_reuse_address = True def __init__(self, interface, cooker, parent): # Use auto port configuration if (interface[1] == -1): interface = (interface[0], 0) SimpleXMLRPCServer.__init__(self, interface, requestHandler=BitBakeXMLRPCRequestHandler, logRequests=False, allow_none=True) self.host, self.port = self.socket.getsockname() self.interface = interface self.connection_token = None self.commands = BitBakeXMLRPCServerCommands(self) self.register_functions(self.commands, "") self.cooker = cooker self.parent = parent def register_functions(self, context, prefix): """ Convenience method for registering all functions in the scope of this class that start with a common prefix """ methodlist = inspect.getmembers(context, inspect.ismethod) for name, method in methodlist: if name.startswith(prefix): self.register_function(method, name[len(prefix):]) def get_timeout(self, delay): socktimeout = self.socket.gettimeout() or delay return min(socktimeout, delay) def handle_requests(self): self._handle_request_noblock() class BitBakeXMLRPCServerCommands(): def __init__(self, server): self.server = server self.has_client = False def registerEventHandler(self, host, port): """ Register a remote UI Event Handler """ s, t = bb.server.xmlrpcclient._create_server(host, port) # we don't allow connections if the cooker is running if (self.server.cooker.state in [bb.cooker.state.parsing, bb.cooker.state.running]): return None, "Cooker is busy: %s" % bb.cooker.state.get_name(self.server.cooker.state) self.event_handle = bb.event.register_UIHhandler(s, True) return self.event_handle, 'OK' def unregisterEventHandler(self, handlerNum): """ Unregister a remote UI Event Handler """ ret = bb.event.unregister_UIHhandler(handlerNum, True) self.event_handle = None return ret def runCommand(self, command): """ Run a cooker command on the server """ return self.server.cooker.command.runCommand(command, self.server.readonly) def getEventHandle(self): return self.event_handle def terminateServer(self): """ Trigger the server to quit """ self.server.parent.quit = True print("XMLRPC Server triggering exit") return def addClient(self): if self.server.parent.haveui: return None token = hashlib.md5(str(time.time()).encode("utf-8")).hexdigest() self.server.connection_token = token self.server.parent.haveui = True return token def removeClient(self): if self.server.parent.haveui: self.server.connection_token = None self.server.parent.haveui = False