1# 2# BitBake XMLRPC Client 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 socket 11import http.client 12import xmlrpc.client 13 14import bb 15from bb.ui import uievent 16 17class BBTransport(xmlrpc.client.Transport): 18 def __init__(self, timeout): 19 self.timeout = timeout 20 self.connection_token = None 21 xmlrpc.client.Transport.__init__(self) 22 23 # Modified from default to pass timeout to HTTPConnection 24 def make_connection(self, host): 25 #return an existing connection if possible. This allows 26 #HTTP/1.1 keep-alive. 27 if self._connection and host == self._connection[0]: 28 return self._connection[1] 29 30 # create a HTTP connection object from a host descriptor 31 chost, self._extra_headers, x509 = self.get_host_info(host) 32 #store the host argument along with the connection object 33 self._connection = host, http.client.HTTPConnection(chost, timeout=self.timeout) 34 return self._connection[1] 35 36 def set_connection_token(self, token): 37 self.connection_token = token 38 39 def send_content(self, h, body): 40 if self.connection_token: 41 h.putheader("Bitbake-token", self.connection_token) 42 xmlrpc.client.Transport.send_content(self, h, body) 43 44def _create_server(host, port, timeout = 60): 45 t = BBTransport(timeout) 46 s = xmlrpc.client.ServerProxy("http://%s:%d/" % (host, port), transport=t, allow_none=True, use_builtin_types=True) 47 return s, t 48 49def check_connection(remote, timeout): 50 try: 51 host, port = remote.split(":") 52 port = int(port) 53 except Exception as e: 54 bb.warn("Failed to read remote definition (%s)" % str(e)) 55 raise e 56 57 server, _transport = _create_server(host, port, timeout) 58 try: 59 ret, err = server.runCommand(['getVariable', 'TOPDIR']) 60 if err or not ret: 61 return False 62 except ConnectionError: 63 return False 64 return True 65 66class BitBakeXMLRPCServerConnection(object): 67 def __init__(self, host, port, clientinfo=("localhost", 0), observer_only = False, featureset = None): 68 self.connection, self.transport = _create_server(host, port) 69 self.clientinfo = clientinfo 70 self.observer_only = observer_only 71 if featureset: 72 self.featureset = featureset 73 else: 74 self.featureset = [] 75 76 self.events = uievent.BBUIEventQueue(self.connection, self.clientinfo) 77 78 _, error = self.connection.runCommand(["setFeatures", self.featureset]) 79 if error: 80 # disconnect the client, we can't make the setFeature work 81 self.connection.removeClient() 82 # no need to log it here, the error shall be sent to the client 83 raise BaseException(error) 84 85 def connect(self, token = None): 86 if token is None: 87 if self.observer_only: 88 token = "observer" 89 else: 90 token = self.connection.addClient() 91 92 if token is None: 93 return None 94 95 self.transport.set_connection_token(token) 96 return self 97 98 def removeClient(self): 99 if not self.observer_only: 100 self.connection.removeClient() 101 102 def terminate(self): 103 # Don't wait for server indefinitely 104 socket.setdefaulttimeout(2) 105 try: 106 self.events.system_quit() 107 except: 108 pass 109 try: 110 self.connection.removeClient() 111 except: 112 pass 113 114def connectXMLRPC(remote, featureset, observer_only = False, token = None): 115 # The format of "remote" must be "server:port" 116 try: 117 [host, port] = remote.split(":") 118 port = int(port) 119 except Exception as e: 120 bb.warn("Failed to parse remote definition %s (%s)" % (remote, str(e))) 121 raise e 122 123 # We need our IP for the server connection. We get the IP 124 # by trying to connect with the server 125 try: 126 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 127 s.connect((host, port)) 128 ip = s.getsockname()[0] 129 s.close() 130 except Exception as e: 131 bb.warn("Could not create socket for %s:%s (%s)" % (host, port, str(e))) 132 raise e 133 try: 134 connection = BitBakeXMLRPCServerConnection(host, port, (ip, 0), observer_only, featureset) 135 return connection.connect(token) 136 except Exception as e: 137 bb.warn("Could not connect to server at %s:%s (%s)" % (host, port, str(e))) 138 raise e 139 140 141 142