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