mirror of
https://git.yoctoproject.org/poky
synced 2026-02-06 00:38:45 +01:00
When bitbake is off running heavier "idle" commands, it doesn't service it's command socket which means stopping/interrupting it is hard. It also means we can't "ping" from the UI to know if it is still alive. For those reasons, split idle command execution into it's own thread. The commands are generally already self containted so this is easier than expected. We do have to be careful to only handle inotify poll() from a single thread at a time. It also means we always have to use a thread lock when sending events since both the idle thread and the command thread may generate log messages (and hence events). The patch depends on previous fixes to the builtins locking in event.py and the heartbeat enable/disable changes as well as other locking additions. We use a condition to signal from the idle thread when other sections of code can continue, thanks to Joshua Watt for the review and tweaks squashed into this patch. We do have some sync points where we need to ensure any currently executing commands have finished before we can start a new async command for example. (Bitbake rev: 67dd9a5e84811df8869a82da6a37a41ee8fe94e2) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
147 lines
4.9 KiB
Python
147 lines
4.9 KiB
Python
#
|
|
# BitBake XMLRPC Server Interface
|
|
#
|
|
# Copyright (C) 2006 - 2007 Michael 'Mickey' Lauer
|
|
# Copyright (C) 2006 - 2008 Richard Purdie
|
|
#
|
|
# SPDX-License-Identifier: GPL-2.0-only
|
|
#
|
|
|
|
import hashlib
|
|
import time
|
|
import inspect
|
|
from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
|
|
import bb.server.xmlrpcclient
|
|
|
|
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, 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
|
|
|