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