blob: 54fa32f57391aa2abe3ffaed221ccdc911d384b9 [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
10import os
11import sys
12
13import hashlib
14import time
15import inspect
16from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
17
18import bb
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
98 def registerEventHandler(self, host, port):
99 """
100 Register a remote UI Event Handler
101 """
102 s, t = bb.server.xmlrpcclient._create_server(host, port)
103
104 # we don't allow connections if the cooker is running
105 if (self.server.cooker.state in [bb.cooker.state.parsing, bb.cooker.state.running]):
106 return None, "Cooker is busy: %s" % bb.cooker.state.get_name(self.server.cooker.state)
107
108 self.event_handle = bb.event.register_UIHhandler(s, True)
109 return self.event_handle, 'OK'
110
111 def unregisterEventHandler(self, handlerNum):
112 """
113 Unregister a remote UI Event Handler
114 """
115 ret = bb.event.unregister_UIHhandler(handlerNum, True)
116 self.event_handle = None
117 return ret
118
119 def runCommand(self, command):
120 """
121 Run a cooker command on the server
122 """
123 return self.server.cooker.command.runCommand(command, self.server.readonly)
124
125 def getEventHandle(self):
126 return self.event_handle
127
128 def terminateServer(self):
129 """
130 Trigger the server to quit
131 """
132 self.server.parent.quit = True
133 print("XMLRPC Server triggering exit")
134 return
135
136 def addClient(self):
137 if self.server.parent.haveui:
138 return None
139 token = hashlib.md5(str(time.time()).encode("utf-8")).hexdigest()
140 self.server.connection_token = token
141 self.server.parent.haveui = True
142 return token
143
144 def removeClient(self):
145 if self.server.parent.haveui:
146 self.server.connection_token = None
147 self.server.parent.haveui = False
148