159 lines
5.4 KiB
Python
159 lines
5.4 KiB
Python
|
#
|
||
|
# BitBake XMLRPC Server Interface
|
||
|
#
|
||
|
# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
|
||
|
# Copyright (C) 2006 - 2008 Richard Purdie
|
||
|
#
|
||
|
# This program is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License version 2 as
|
||
|
# published by the Free Software Foundation.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License along
|
||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
|
||
|
import os
|
||
|
import sys
|
||
|
|
||
|
import hashlib
|
||
|
import time
|
||
|
import inspect
|
||
|
from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
||
|
|
||
|
import bb
|
||
|
|
||
|
# This request handler checks if the request has a "Bitbake-token" header
|
||
|
# field (this comes from the client side) and compares it with its internal
|
||
|
# "Bitbake-token" field (this comes from the server). If the two are not
|
||
|
# equal, it is assumed that a client is trying to connect to the server
|
||
|
# while another client is connected to the server. In this case, a 503 error
|
||
|
# ("service unavailable") is returned to the client.
|
||
|
class BitBakeXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||
|
def __init__(self, request, client_address, server):
|
||
|
self.server = server
|
||
|
SimpleXMLRPCRequestHandler.__init__(self, request, client_address, server)
|
||
|
|
||
|
def do_POST(self):
|
||
|
try:
|
||
|
remote_token = self.headers["Bitbake-token"]
|
||
|
except:
|
||
|
remote_token = None
|
||
|
if 0 and remote_token != self.server.connection_token and remote_token != "observer":
|
||
|
self.report_503()
|
||
|
else:
|
||
|
if remote_token == "observer":
|
||
|
self.server.readonly = True
|
||
|
else:
|
||
|
self.server.readonly = False
|
||
|
SimpleXMLRPCRequestHandler.do_POST(self)
|
||
|
|
||
|
def report_503(self):
|
||
|
self.send_response(503)
|
||
|
response = 'No more client allowed'
|
||
|
self.send_header("Content-type", "text/plain")
|
||
|
self.send_header("Content-length", str(len(response)))
|
||
|
self.end_headers()
|
||
|
self.wfile.write(bytes(response, 'utf-8'))
|
||
|
|
||
|
class BitBakeXMLRPCServer(SimpleXMLRPCServer):
|
||
|
# remove this when you're done with debugging
|
||
|
# allow_reuse_address = True
|
||
|
|
||
|
def __init__(self, interface, cooker, parent):
|
||
|
# Use auto port configuration
|
||
|
if (interface[1] == -1):
|
||
|
interface = (interface[0], 0)
|
||
|
SimpleXMLRPCServer.__init__(self, interface,
|
||
|
requestHandler=BitBakeXMLRPCRequestHandler,
|
||
|
logRequests=False, allow_none=True)
|
||
|
self.host, self.port = self.socket.getsockname()
|
||
|
self.interface = interface
|
||
|
|
||
|
self.connection_token = None
|
||
|
self.commands = BitBakeXMLRPCServerCommands(self)
|
||
|
self.register_functions(self.commands, "")
|
||
|
|
||
|
self.cooker = cooker
|
||
|
self.parent = parent
|
||
|
|
||
|
|
||
|
def register_functions(self, context, prefix):
|
||
|
"""
|
||
|
Convenience method for registering all functions in the scope
|
||
|
of this class that start with a common prefix
|
||
|
"""
|
||
|
methodlist = inspect.getmembers(context, inspect.ismethod)
|
||
|
for name, method in methodlist:
|
||
|
if name.startswith(prefix):
|
||
|
self.register_function(method, name[len(prefix):])
|
||
|
|
||
|
def get_timeout(self, delay):
|
||
|
socktimeout = self.socket.gettimeout() or delay
|
||
|
return min(socktimeout, delay)
|
||
|
|
||
|
def handle_requests(self):
|
||
|
self._handle_request_noblock()
|
||
|
|
||
|
class BitBakeXMLRPCServerCommands():
|
||
|
|
||
|
def __init__(self, server):
|
||
|
self.server = server
|
||
|
self.has_client = False
|
||
|
|
||
|
def registerEventHandler(self, host, port):
|
||
|
"""
|
||
|
Register a remote UI Event Handler
|
||
|
"""
|
||
|
s, t = bb.server.xmlrpcclient._create_server(host, port)
|
||
|
|
||
|
# we don't allow connections if the cooker is running
|
||
|
if (self.server.cooker.state in [bb.cooker.state.parsing, bb.cooker.state.running]):
|
||
|
return None, "Cooker is busy: %s" % bb.cooker.state.get_name(self.server.cooker.state)
|
||
|
|
||
|
self.event_handle = bb.event.register_UIHhandler(s, True)
|
||
|
return self.event_handle, 'OK'
|
||
|
|
||
|
def unregisterEventHandler(self, handlerNum):
|
||
|
"""
|
||
|
Unregister a remote UI Event Handler
|
||
|
"""
|
||
|
ret = bb.event.unregister_UIHhandler(handlerNum, True)
|
||
|
self.event_handle = None
|
||
|
return ret
|
||
|
|
||
|
def runCommand(self, command):
|
||
|
"""
|
||
|
Run a cooker command on the server
|
||
|
"""
|
||
|
return self.server.cooker.command.runCommand(command, self.server.readonly)
|
||
|
|
||
|
def getEventHandle(self):
|
||
|
return self.event_handle
|
||
|
|
||
|
def terminateServer(self):
|
||
|
"""
|
||
|
Trigger the server to quit
|
||
|
"""
|
||
|
self.server.parent.quit = True
|
||
|
print("XMLRPC Server triggering exit")
|
||
|
return
|
||
|
|
||
|
def addClient(self):
|
||
|
if self.server.parent.haveui:
|
||
|
return None
|
||
|
token = hashlib.md5(str(time.time()).encode("utf-8")).hexdigest()
|
||
|
self.server.connection_token = token
|
||
|
self.server.parent.haveui = True
|
||
|
return token
|
||
|
|
||
|
def removeClient(self):
|
||
|
if self.server.parent.haveui:
|
||
|
self.server.connection_token = None
|
||
|
self.server.parent.haveui = False
|
||
|
|