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