xref: /openbmc/openbmc/poky/bitbake/lib/bb/server/xmlrpcserver.py (revision 8460358c3d24c71d9d38fd126c745854a6301564)
1#
2# BitBake XMLRPC Server Interface
3#
4# Copyright (C) 2006 - 2007  Michael 'Mickey' Lauer
5# Copyright (C) 2006 - 2008  Richard Purdie
6#
7# SPDX-License-Identifier: GPL-2.0-only
8#
9
10import hashlib
11import time
12import inspect
13from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
14import bb.server.xmlrpcclient
15
16import bb
17import bb.cooker
18import bb.event
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        self.event_handle = None
98
99    def registerEventHandler(self, host, port):
100        """
101        Register a remote UI Event Handler
102        """
103        s, t = bb.server.xmlrpcclient._create_server(host, port)
104
105        # we don't allow connections if the cooker is running
106        if self.server.cooker.state in [bb.cooker.State.PARSING, bb.cooker.State.RUNNING]:
107            return None, f"Cooker is busy: {self.server.cooker.state.name}"
108
109        self.event_handle = bb.event.register_UIHhandler(s, True)
110        return self.event_handle, 'OK'
111
112    def unregisterEventHandler(self, handlerNum):
113        """
114        Unregister a remote UI Event Handler
115        """
116        ret = bb.event.unregister_UIHhandler(handlerNum, True)
117        self.event_handle = None
118        return ret
119
120    def runCommand(self, command):
121        """
122        Run a cooker command on the server
123        """
124        return self.server.cooker.command.runCommand(command, self.server.parent, self.server.readonly)
125
126    def getEventHandle(self):
127        return self.event_handle
128
129    def terminateServer(self):
130        """
131        Trigger the server to quit
132        """
133        self.server.parent.quit = True
134        print("XMLRPC Server triggering exit")
135        return
136
137    def addClient(self):
138        if self.server.parent.haveui:
139            return None
140        token = hashlib.md5(str(time.time()).encode("utf-8")).hexdigest()
141        self.server.connection_token = token
142        self.server.parent.haveui = True
143        return token
144
145    def removeClient(self):
146        if self.server.parent.haveui:
147            self.server.connection_token = None
148            self.server.parent.haveui = False
149
150