Complete Yocto mirror with license table for TQMa6UL (2038-compliance)
- 264 license table entries with exact download URLs (224/264 resolved) - Complete sources/ directory with all BitBake recipes - Build configuration: tqma6ul-multi-mba6ulx, spaetzle (musl) - Full traceability for Softwarefreigabeantrag - GCC 13.4.0, Linux 6.6.102, U-Boot 2023.04, musl 1.2.4 - License distribution: GPL-2.0 (24), MIT (23), GPL-2.0+ (18), BSD-3 (16)
This commit is contained in:
392
sources/poky/bitbake/lib/prserv/serv.py
Normal file
392
sources/poky/bitbake/lib/prserv/serv.py
Normal file
@@ -0,0 +1,392 @@
|
||||
#
|
||||
# Copyright BitBake Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
|
||||
import os,sys,logging
|
||||
import signal, time
|
||||
import socket
|
||||
import io
|
||||
import sqlite3
|
||||
import prserv
|
||||
import prserv.db
|
||||
import errno
|
||||
import bb.asyncrpc
|
||||
|
||||
logger = logging.getLogger("BitBake.PRserv")
|
||||
|
||||
PIDPREFIX = "/tmp/PRServer_%s_%s.pid"
|
||||
singleton = None
|
||||
|
||||
class PRServerClient(bb.asyncrpc.AsyncServerConnection):
|
||||
def __init__(self, socket, server):
|
||||
super().__init__(socket, "PRSERVICE", server.logger)
|
||||
self.server = server
|
||||
|
||||
self.handlers.update({
|
||||
"get-pr": self.handle_get_pr,
|
||||
"test-pr": self.handle_test_pr,
|
||||
"test-package": self.handle_test_package,
|
||||
"max-package-pr": self.handle_max_package_pr,
|
||||
"import-one": self.handle_import_one,
|
||||
"export": self.handle_export,
|
||||
"is-readonly": self.handle_is_readonly,
|
||||
})
|
||||
|
||||
def validate_proto_version(self):
|
||||
return (self.proto_version == (1, 0))
|
||||
|
||||
async def dispatch_message(self, msg):
|
||||
try:
|
||||
return await super().dispatch_message(msg)
|
||||
except:
|
||||
self.server.table.sync()
|
||||
raise
|
||||
else:
|
||||
self.server.table.sync_if_dirty()
|
||||
|
||||
async def handle_test_pr(self, request):
|
||||
'''Finds the PR value corresponding to the request. If not found, returns None and doesn't insert a new value'''
|
||||
version = request["version"]
|
||||
pkgarch = request["pkgarch"]
|
||||
checksum = request["checksum"]
|
||||
|
||||
value = self.server.table.find_value(version, pkgarch, checksum)
|
||||
return {"value": value}
|
||||
|
||||
async def handle_test_package(self, request):
|
||||
'''Tells whether there are entries for (version, pkgarch) in the db. Returns True or False'''
|
||||
version = request["version"]
|
||||
pkgarch = request["pkgarch"]
|
||||
|
||||
value = self.server.table.test_package(version, pkgarch)
|
||||
return {"value": value}
|
||||
|
||||
async def handle_max_package_pr(self, request):
|
||||
'''Finds the greatest PR value for (version, pkgarch) in the db. Returns None if no entry was found'''
|
||||
version = request["version"]
|
||||
pkgarch = request["pkgarch"]
|
||||
|
||||
value = self.server.table.find_max_value(version, pkgarch)
|
||||
return {"value": value}
|
||||
|
||||
async def handle_get_pr(self, request):
|
||||
version = request["version"]
|
||||
pkgarch = request["pkgarch"]
|
||||
checksum = request["checksum"]
|
||||
|
||||
response = None
|
||||
try:
|
||||
value = self.server.table.get_value(version, pkgarch, checksum)
|
||||
response = {"value": value}
|
||||
except prserv.NotFoundError:
|
||||
self.logger.error("failure storing value in database for (%s, %s)",version, checksum)
|
||||
|
||||
return response
|
||||
|
||||
async def handle_import_one(self, request):
|
||||
response = None
|
||||
if not self.server.read_only:
|
||||
version = request["version"]
|
||||
pkgarch = request["pkgarch"]
|
||||
checksum = request["checksum"]
|
||||
value = request["value"]
|
||||
|
||||
value = self.server.table.importone(version, pkgarch, checksum, value)
|
||||
if value is not None:
|
||||
response = {"value": value}
|
||||
|
||||
return response
|
||||
|
||||
async def handle_export(self, request):
|
||||
version = request["version"]
|
||||
pkgarch = request["pkgarch"]
|
||||
checksum = request["checksum"]
|
||||
colinfo = request["colinfo"]
|
||||
|
||||
try:
|
||||
(metainfo, datainfo) = self.server.table.export(version, pkgarch, checksum, colinfo)
|
||||
except sqlite3.Error as exc:
|
||||
self.logger.error(str(exc))
|
||||
metainfo = datainfo = None
|
||||
|
||||
return {"metainfo": metainfo, "datainfo": datainfo}
|
||||
|
||||
async def handle_is_readonly(self, request):
|
||||
return {"readonly": self.server.read_only}
|
||||
|
||||
class PRServer(bb.asyncrpc.AsyncServer):
|
||||
def __init__(self, dbfile, read_only=False):
|
||||
super().__init__(logger)
|
||||
self.dbfile = dbfile
|
||||
self.table = None
|
||||
self.read_only = read_only
|
||||
|
||||
def accept_client(self, socket):
|
||||
return PRServerClient(socket, self)
|
||||
|
||||
def start(self):
|
||||
tasks = super().start()
|
||||
self.db = prserv.db.PRData(self.dbfile, read_only=self.read_only)
|
||||
self.table = self.db["PRMAIN"]
|
||||
|
||||
self.logger.info("Started PRServer with DBfile: %s, Address: %s, PID: %s" %
|
||||
(self.dbfile, self.address, str(os.getpid())))
|
||||
|
||||
return tasks
|
||||
|
||||
async def stop(self):
|
||||
self.table.sync_if_dirty()
|
||||
self.db.disconnect()
|
||||
await super().stop()
|
||||
|
||||
def signal_handler(self):
|
||||
super().signal_handler()
|
||||
if self.table:
|
||||
self.table.sync()
|
||||
|
||||
class PRServSingleton(object):
|
||||
def __init__(self, dbfile, logfile, host, port):
|
||||
self.dbfile = dbfile
|
||||
self.logfile = logfile
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
def start(self):
|
||||
self.prserv = PRServer(self.dbfile)
|
||||
self.prserv.start_tcp_server(socket.gethostbyname(self.host), self.port)
|
||||
self.process = self.prserv.serve_as_process(log_level=logging.WARNING)
|
||||
|
||||
if not self.prserv.address:
|
||||
raise PRServiceConfigError
|
||||
if not self.port:
|
||||
self.port = int(self.prserv.address.rsplit(":", 1)[1])
|
||||
|
||||
def run_as_daemon(func, pidfile, logfile):
|
||||
"""
|
||||
See Advanced Programming in the UNIX, Sec 13.3
|
||||
"""
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0:
|
||||
os.waitpid(pid, 0)
|
||||
#parent return instead of exit to give control
|
||||
return pid
|
||||
except OSError as e:
|
||||
raise Exception("%s [%d]" % (e.strerror, e.errno))
|
||||
|
||||
os.setsid()
|
||||
"""
|
||||
fork again to make sure the daemon is not session leader,
|
||||
which prevents it from acquiring controlling terminal
|
||||
"""
|
||||
try:
|
||||
pid = os.fork()
|
||||
if pid > 0: #parent
|
||||
os._exit(0)
|
||||
except OSError as e:
|
||||
raise Exception("%s [%d]" % (e.strerror, e.errno))
|
||||
|
||||
os.chdir("/")
|
||||
|
||||
sys.stdout.flush()
|
||||
sys.stderr.flush()
|
||||
|
||||
# We could be called from a python thread with io.StringIO as
|
||||
# stdout/stderr or it could be 'real' unix fd forking where we need
|
||||
# to physically close the fds to prevent the program launching us from
|
||||
# potentially hanging on a pipe. Handle both cases.
|
||||
si = open("/dev/null", "r")
|
||||
try:
|
||||
os.dup2(si.fileno(), sys.stdin.fileno())
|
||||
except (AttributeError, io.UnsupportedOperation):
|
||||
sys.stdin = si
|
||||
so = open(logfile, "a+")
|
||||
try:
|
||||
os.dup2(so.fileno(), sys.stdout.fileno())
|
||||
except (AttributeError, io.UnsupportedOperation):
|
||||
sys.stdout = so
|
||||
try:
|
||||
os.dup2(so.fileno(), sys.stderr.fileno())
|
||||
except (AttributeError, io.UnsupportedOperation):
|
||||
sys.stderr = so
|
||||
|
||||
# Clear out all log handlers prior to the fork() to avoid calling
|
||||
# event handlers not part of the PRserver
|
||||
for logger_iter in logging.Logger.manager.loggerDict.keys():
|
||||
logging.getLogger(logger_iter).handlers = []
|
||||
|
||||
# Ensure logging makes it to the logfile
|
||||
streamhandler = logging.StreamHandler()
|
||||
streamhandler.setLevel(logging.DEBUG)
|
||||
formatter = bb.msg.BBLogFormatter("%(levelname)s: %(message)s")
|
||||
streamhandler.setFormatter(formatter)
|
||||
logger.addHandler(streamhandler)
|
||||
|
||||
# write pidfile
|
||||
pid = str(os.getpid())
|
||||
with open(pidfile, "w") as pf:
|
||||
pf.write("%s\n" % pid)
|
||||
|
||||
func()
|
||||
os.remove(pidfile)
|
||||
os._exit(0)
|
||||
|
||||
def start_daemon(dbfile, host, port, logfile, read_only=False):
|
||||
ip = socket.gethostbyname(host)
|
||||
pidfile = PIDPREFIX % (ip, port)
|
||||
try:
|
||||
with open(pidfile) as pf:
|
||||
pid = int(pf.readline().strip())
|
||||
except IOError:
|
||||
pid = None
|
||||
|
||||
if pid:
|
||||
sys.stderr.write("pidfile %s already exist. Daemon already running?\n"
|
||||
% pidfile)
|
||||
return 1
|
||||
|
||||
dbfile = os.path.abspath(dbfile)
|
||||
def daemon_main():
|
||||
server = PRServer(dbfile, read_only=read_only)
|
||||
server.start_tcp_server(ip, port)
|
||||
server.serve_forever()
|
||||
|
||||
run_as_daemon(daemon_main, pidfile, os.path.abspath(logfile))
|
||||
return 0
|
||||
|
||||
def stop_daemon(host, port):
|
||||
import glob
|
||||
ip = socket.gethostbyname(host)
|
||||
pidfile = PIDPREFIX % (ip, port)
|
||||
try:
|
||||
with open(pidfile) as pf:
|
||||
pid = int(pf.readline().strip())
|
||||
except IOError:
|
||||
pid = None
|
||||
|
||||
if not pid:
|
||||
# when server starts at port=0 (i.e. localhost:0), server actually takes another port,
|
||||
# so at least advise the user which ports the corresponding server is listening
|
||||
ports = []
|
||||
portstr = ""
|
||||
for pf in glob.glob(PIDPREFIX % (ip, "*")):
|
||||
bn = os.path.basename(pf)
|
||||
root, _ = os.path.splitext(bn)
|
||||
ports.append(root.split("_")[-1])
|
||||
if len(ports):
|
||||
portstr = "Wrong port? Other ports listening at %s: %s" % (host, " ".join(ports))
|
||||
|
||||
sys.stderr.write("pidfile %s does not exist. Daemon not running? %s\n"
|
||||
% (pidfile, portstr))
|
||||
return 1
|
||||
|
||||
try:
|
||||
if is_running(pid):
|
||||
print("Sending SIGTERM to pr-server.")
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
time.sleep(0.1)
|
||||
|
||||
try:
|
||||
os.remove(pidfile)
|
||||
except FileNotFoundError:
|
||||
# The PID file might have been removed by the exiting process
|
||||
pass
|
||||
|
||||
except OSError as e:
|
||||
err = str(e)
|
||||
if err.find("No such process") <= 0:
|
||||
raise e
|
||||
|
||||
return 0
|
||||
|
||||
def is_running(pid):
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
except OSError as err:
|
||||
if err.errno == errno.ESRCH:
|
||||
return False
|
||||
return True
|
||||
|
||||
def is_local_special(host, port):
|
||||
if (host == "localhost" or host == "127.0.0.1") and not port:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
class PRServiceConfigError(Exception):
|
||||
pass
|
||||
|
||||
def auto_start(d):
|
||||
global singleton
|
||||
|
||||
host_params = list(filter(None, (d.getVar("PRSERV_HOST") or "").split(":")))
|
||||
if not host_params:
|
||||
# Shutdown any existing PR Server
|
||||
auto_shutdown()
|
||||
return None
|
||||
|
||||
if len(host_params) != 2:
|
||||
# Shutdown any existing PR Server
|
||||
auto_shutdown()
|
||||
logger.critical("\n".join(["PRSERV_HOST: incorrect format",
|
||||
'Usage: PRSERV_HOST = "<hostname>:<port>"']))
|
||||
raise PRServiceConfigError
|
||||
|
||||
host = host_params[0].strip().lower()
|
||||
port = int(host_params[1])
|
||||
if is_local_special(host, port):
|
||||
import bb.utils
|
||||
cachedir = (d.getVar("PERSISTENT_DIR") or d.getVar("CACHE"))
|
||||
if not cachedir:
|
||||
logger.critical("Please set the 'PERSISTENT_DIR' or 'CACHE' variable")
|
||||
raise PRServiceConfigError
|
||||
dbfile = os.path.join(cachedir, "prserv.sqlite3")
|
||||
logfile = os.path.join(cachedir, "prserv.log")
|
||||
if singleton:
|
||||
if singleton.dbfile != dbfile:
|
||||
# Shutdown any existing PR Server as doesn't match config
|
||||
auto_shutdown()
|
||||
if not singleton:
|
||||
bb.utils.mkdirhier(cachedir)
|
||||
singleton = PRServSingleton(os.path.abspath(dbfile), os.path.abspath(logfile), host, port)
|
||||
singleton.start()
|
||||
if singleton:
|
||||
host = singleton.host
|
||||
port = singleton.port
|
||||
|
||||
try:
|
||||
ping(host, port)
|
||||
return str(host) + ":" + str(port)
|
||||
|
||||
except Exception:
|
||||
logger.critical("PRservice %s:%d not available" % (host, port))
|
||||
raise PRServiceConfigError
|
||||
|
||||
def auto_shutdown():
|
||||
global singleton
|
||||
if singleton and singleton.process:
|
||||
singleton.process.terminate()
|
||||
singleton.process.join()
|
||||
singleton = None
|
||||
|
||||
def ping(host, port):
|
||||
from . import client
|
||||
|
||||
with client.PRClient() as conn:
|
||||
conn.connect_tcp(host, port)
|
||||
return conn.ping()
|
||||
|
||||
def connect(host, port):
|
||||
from . import client
|
||||
|
||||
global singleton
|
||||
|
||||
if host.strip().lower() == "localhost" and not port:
|
||||
host = "localhost"
|
||||
port = singleton.port
|
||||
|
||||
conn = client.PRClient()
|
||||
conn.connect_tcp(host, port)
|
||||
return conn
|
||||
Reference in New Issue
Block a user