blob: 2e65dc34a938c8b875ceef8b9258c63ca9827f78 [file] [log] [blame]
Brad Bishopd7bf8c12018-02-25 22:55:05 -05001#
2# BitBake XMLRPC Server Interface
3#
4# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
5# Copyright (C) 2006 - 2008 Richard Purdie
6#
Brad Bishopc342db32019-05-15 21:57:59 -04007# SPDX-License-Identifier: GPL-2.0-only
Brad Bishopd7bf8c12018-02-25 22:55:05 -05008#
Brad Bishopd7bf8c12018-02-25 22:55:05 -05009
Brad Bishopd7bf8c12018-02-25 22:55:05 -050010import hashlib
11import time
12import inspect
13from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
Andrew Geissler9aee5002022-03-30 16:27:02 +000014import bb.server.xmlrpcclient
Brad Bishopd7bf8c12018-02-25 22:55:05 -050015
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 """
Andrew Geissler517393d2023-01-13 08:55:19 -0600121 return self.server.cooker.command.runCommand(command, self.server, self.server.readonly)
Brad Bishopd7bf8c12018-02-25 22:55:05 -0500122
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