1# 2# BitBake XMLRPC Server Interface 3# 4# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer 5# Copyright (C) 2006 - 2008 Richard Purdie 6# 7# SPDX-License-Identifier: GPL-2.0-only 8# 9 10import hashlib 11import time 12import inspect 13from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler 14import bb.server.xmlrpcclient 15 16import bb 17import bb.cooker 18import bb.event 19 20# This request handler checks if the request has a "Bitbake-token" header 21# field (this comes from the client side) and compares it with its internal 22# "Bitbake-token" field (this comes from the server). If the two are not 23# equal, it is assumed that a client is trying to connect to the server 24# while another client is connected to the server. In this case, a 503 error 25# ("service unavailable") is returned to the client. 26class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): 27 def __init__(self, request, client_address, server): 28 self.server = server 29 SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server) 30 31 def do_POST(self): 32 try: 33 remote_token = self.headers["Bitbake-token"] 34 except: 35 remote_token = None 36 if 0 and remote_token != self.server.connection_token and remote_token != "observer": 37 self.report_503() 38 else: 39 if remote_token == "observer": 40 self.server.readonly = True 41 else: 42 self.server.readonly = False 43 SimpleXMLRPCRequestHandler.do_POST(self) 44 45 def report_503(self): 46 self.send_response(503) 47 response = 'No more client allowed' 48 self.send_header("Content-type", "text/plain") 49 self.send_header("Content-length", str(len(response))) 50 self.end_headers() 51 self.wfile.write(bytes(response, 'utf-8')) 52 53class BitBakeXMLRPCServer(SimpleXMLRPCServer): 54 # remove this when you're done with debugging 55 # allow_reuse_address = True 56 57 def __init__(self, interface, cooker, parent): 58 # Use auto port configuration 59 if interface[1] == -1: 60 interface = (interface[0], 0) 61 SimpleXMLRPCServer.__init__(self, interface, 62 requestHandler=BitBakeXMLRPCRequestHandler, 63 logRequests=False, allow_none=True) 64 self.host, self.port = self.socket.getsockname() 65 self.interface = interface 66 67 self.connection_token = None 68 self.commands = BitBakeXMLRPCServerCommands(self) 69 self.register_functions(self.commands, "") 70 71 self.cooker = cooker 72 self.parent = parent 73 74 75 def register_functions(self, context, prefix): 76 """ 77 Convenience method for registering all functions in the scope 78 of this class that start with a common prefix 79 """ 80 methodlist = inspect.getmembers(context, inspect.ismethod) 81 for name, method in methodlist: 82 if name.startswith(prefix): 83 self.register_function(method, name[len(prefix):]) 84 85 def get_timeout(self, delay): 86 socktimeout = self.socket.gettimeout() or delay 87 return min(socktimeout, delay) 88 89 def handle_requests(self): 90 self._handle_request_noblock() 91 92class BitBakeXMLRPCServerCommands: 93 94 def __init__(self, server): 95 self.server = server 96 self.has_client = False 97 self.event_handle = None 98 99 def registerEventHandler(self, host, port): 100 """ 101 Register a remote UI Event Handler 102 """ 103 s, t = bb.server.xmlrpcclient._create_server(host, port) 104 105 # we don't allow connections if the cooker is running 106 if self.server.cooker.state in [bb.cooker.State.PARSING, bb.cooker.State.RUNNING]: 107 return None, f"Cooker is busy: {self.server.cooker.state.name}" 108 109 self.event_handle = bb.event.register_UIHhandler(s, True) 110 return self.event_handle, 'OK' 111 112 def unregisterEventHandler(self, handlerNum): 113 """ 114 Unregister a remote UI Event Handler 115 """ 116 ret = bb.event.unregister_UIHhandler(handlerNum, True) 117 self.event_handle = None 118 return ret 119 120 def runCommand(self, command): 121 """ 122 Run a cooker command on the server 123 """ 124 return self.server.cooker.command.runCommand(command, self.server.parent, self.server.readonly) 125 126 def getEventHandle(self): 127 return self.event_handle 128 129 def terminateServer(self): 130 """ 131 Trigger the server to quit 132 """ 133 self.server.parent.quit = True 134 print("XMLRPC Server triggering exit") 135 return 136 137 def addClient(self): 138 if self.server.parent.haveui: 139 return None 140 token = hashlib.md5(str(time.time()).encode("utf-8")).hexdigest() 141 self.server.connection_token = token 142 self.server.parent.haveui = True 143 return token 144 145 def removeClient(self): 146 if self.server.parent.haveui: 147 self.server.connection_token = None 148 self.server.parent.haveui = False 149 150