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:
20
sources/poky/bitbake/lib/prserv/__init__.py
Normal file
20
sources/poky/bitbake/lib/prserv/__init__.py
Normal file
@@ -0,0 +1,20 @@
|
||||
#
|
||||
# Copyright BitBake Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
|
||||
__version__ = "1.0.0"
|
||||
|
||||
import os, time
|
||||
import sys, logging
|
||||
|
||||
def init_logger(logfile, loglevel):
|
||||
numeric_level = getattr(logging, loglevel.upper(), None)
|
||||
if not isinstance(numeric_level, int):
|
||||
raise ValueError("Invalid log level: %s" % loglevel)
|
||||
FORMAT = "%(asctime)-15s %(message)s"
|
||||
logging.basicConfig(level=numeric_level, filename=logfile, format=FORMAT)
|
||||
|
||||
class NotFoundError(Exception):
|
||||
pass
|
||||
71
sources/poky/bitbake/lib/prserv/client.py
Normal file
71
sources/poky/bitbake/lib/prserv/client.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#
|
||||
# Copyright BitBake Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
|
||||
import logging
|
||||
import bb.asyncrpc
|
||||
|
||||
logger = logging.getLogger("BitBake.PRserv")
|
||||
|
||||
class PRAsyncClient(bb.asyncrpc.AsyncClient):
|
||||
def __init__(self):
|
||||
super().__init__("PRSERVICE", "1.0", logger)
|
||||
|
||||
async def getPR(self, version, pkgarch, checksum):
|
||||
response = await self.invoke(
|
||||
{"get-pr": {"version": version, "pkgarch": pkgarch, "checksum": checksum}}
|
||||
)
|
||||
if response:
|
||||
return response["value"]
|
||||
|
||||
async def test_pr(self, version, pkgarch, checksum):
|
||||
response = await self.invoke(
|
||||
{"test-pr": {"version": version, "pkgarch": pkgarch, "checksum": checksum}}
|
||||
)
|
||||
if response:
|
||||
return response["value"]
|
||||
|
||||
async def test_package(self, version, pkgarch):
|
||||
response = await self.invoke(
|
||||
{"test-package": {"version": version, "pkgarch": pkgarch}}
|
||||
)
|
||||
if response:
|
||||
return response["value"]
|
||||
|
||||
async def max_package_pr(self, version, pkgarch):
|
||||
response = await self.invoke(
|
||||
{"max-package-pr": {"version": version, "pkgarch": pkgarch}}
|
||||
)
|
||||
if response:
|
||||
return response["value"]
|
||||
|
||||
async def importone(self, version, pkgarch, checksum, value):
|
||||
response = await self.invoke(
|
||||
{"import-one": {"version": version, "pkgarch": pkgarch, "checksum": checksum, "value": value}}
|
||||
)
|
||||
if response:
|
||||
return response["value"]
|
||||
|
||||
async def export(self, version, pkgarch, checksum, colinfo):
|
||||
response = await self.invoke(
|
||||
{"export": {"version": version, "pkgarch": pkgarch, "checksum": checksum, "colinfo": colinfo}}
|
||||
)
|
||||
if response:
|
||||
return (response["metainfo"], response["datainfo"])
|
||||
|
||||
async def is_readonly(self):
|
||||
response = await self.invoke(
|
||||
{"is-readonly": {}}
|
||||
)
|
||||
if response:
|
||||
return response["readonly"]
|
||||
|
||||
class PRClient(bb.asyncrpc.Client):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self._add_methods("getPR", "test_pr", "test_package", "importone", "export", "is_readonly")
|
||||
|
||||
def _get_async_client(self):
|
||||
return PRAsyncClient()
|
||||
361
sources/poky/bitbake/lib/prserv/db.py
Normal file
361
sources/poky/bitbake/lib/prserv/db.py
Normal file
@@ -0,0 +1,361 @@
|
||||
#
|
||||
# Copyright BitBake Contributors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
|
||||
import logging
|
||||
import os.path
|
||||
import errno
|
||||
import prserv
|
||||
import time
|
||||
|
||||
try:
|
||||
import sqlite3
|
||||
except ImportError:
|
||||
from pysqlite2 import dbapi2 as sqlite3
|
||||
|
||||
logger = logging.getLogger("BitBake.PRserv")
|
||||
|
||||
sqlversion = sqlite3.sqlite_version_info
|
||||
if sqlversion[0] < 3 or (sqlversion[0] == 3 and sqlversion[1] < 3):
|
||||
raise Exception("sqlite3 version 3.3.0 or later is required.")
|
||||
|
||||
#
|
||||
# "No History" mode - for a given query tuple (version, pkgarch, checksum),
|
||||
# the returned value will be the largest among all the values of the same
|
||||
# (version, pkgarch). This means the PR value returned can NOT be decremented.
|
||||
#
|
||||
# "History" mode - Return a new higher value for previously unseen query
|
||||
# tuple (version, pkgarch, checksum), otherwise return historical value.
|
||||
# Value can decrement if returning to a previous build.
|
||||
#
|
||||
|
||||
class PRTable(object):
|
||||
def __init__(self, conn, table, nohist, read_only):
|
||||
self.conn = conn
|
||||
self.nohist = nohist
|
||||
self.read_only = read_only
|
||||
self.dirty = False
|
||||
if nohist:
|
||||
self.table = "%s_nohist" % table
|
||||
else:
|
||||
self.table = "%s_hist" % table
|
||||
|
||||
if self.read_only:
|
||||
table_exists = self._execute(
|
||||
"SELECT count(*) FROM sqlite_master \
|
||||
WHERE type='table' AND name='%s'" % (self.table))
|
||||
if not table_exists:
|
||||
raise prserv.NotFoundError
|
||||
else:
|
||||
self._execute("CREATE TABLE IF NOT EXISTS %s \
|
||||
(version TEXT NOT NULL, \
|
||||
pkgarch TEXT NOT NULL, \
|
||||
checksum TEXT NOT NULL, \
|
||||
value INTEGER, \
|
||||
PRIMARY KEY (version, pkgarch, checksum));" % self.table)
|
||||
|
||||
def _execute(self, *query):
|
||||
"""Execute a query, waiting to acquire a lock if necessary"""
|
||||
start = time.time()
|
||||
end = start + 20
|
||||
while True:
|
||||
try:
|
||||
return self.conn.execute(*query)
|
||||
except sqlite3.OperationalError as exc:
|
||||
if "is locked" in str(exc) and end > time.time():
|
||||
continue
|
||||
raise exc
|
||||
|
||||
def sync(self):
|
||||
if not self.read_only:
|
||||
self.conn.commit()
|
||||
self._execute("BEGIN EXCLUSIVE TRANSACTION")
|
||||
|
||||
def sync_if_dirty(self):
|
||||
if self.dirty:
|
||||
self.sync()
|
||||
self.dirty = False
|
||||
|
||||
def test_package(self, version, pkgarch):
|
||||
"""Returns whether the specified package version is found in the database for the specified architecture"""
|
||||
|
||||
# Just returns the value if found or None otherwise
|
||||
data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=?;" % self.table,
|
||||
(version, pkgarch))
|
||||
row=data.fetchone()
|
||||
if row is not None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def test_value(self, version, pkgarch, value):
|
||||
"""Returns whether the specified value is found in the database for the specified package and architecture"""
|
||||
|
||||
# Just returns the value if found or None otherwise
|
||||
data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? and value=?;" % self.table,
|
||||
(version, pkgarch, value))
|
||||
row=data.fetchone()
|
||||
if row is not None:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def find_value(self, version, pkgarch, checksum):
|
||||
"""Returns the value for the specified checksum if found or None otherwise."""
|
||||
|
||||
data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table,
|
||||
(version, pkgarch, checksum))
|
||||
row=data.fetchone()
|
||||
if row is not None:
|
||||
return row[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def find_max_value(self, version, pkgarch):
|
||||
"""Returns the greatest value for (version, pkgarch), or None if not found. Doesn't create a new value"""
|
||||
|
||||
data = self._execute("SELECT max(value) FROM %s where version=? AND pkgarch=?;" % (self.table),
|
||||
(version, pkgarch))
|
||||
row = data.fetchone()
|
||||
if row is not None:
|
||||
return row[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def _get_value_hist(self, version, pkgarch, checksum):
|
||||
data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table,
|
||||
(version, pkgarch, checksum))
|
||||
row=data.fetchone()
|
||||
if row is not None:
|
||||
return row[0]
|
||||
else:
|
||||
#no value found, try to insert
|
||||
if self.read_only:
|
||||
data = self._execute("SELECT ifnull(max(value)+1, 0) FROM %s where version=? AND pkgarch=?;" % (self.table),
|
||||
(version, pkgarch))
|
||||
row = data.fetchone()
|
||||
if row is not None:
|
||||
return row[0]
|
||||
else:
|
||||
return 0
|
||||
|
||||
try:
|
||||
self._execute("INSERT INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1, 0) from %s where version=? AND pkgarch=?));"
|
||||
% (self.table, self.table),
|
||||
(version, pkgarch, checksum, version, pkgarch))
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
|
||||
self.dirty = True
|
||||
|
||||
data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table,
|
||||
(version, pkgarch, checksum))
|
||||
row=data.fetchone()
|
||||
if row is not None:
|
||||
return row[0]
|
||||
else:
|
||||
raise prserv.NotFoundError
|
||||
|
||||
def _get_value_no_hist(self, version, pkgarch, checksum):
|
||||
data=self._execute("SELECT value FROM %s \
|
||||
WHERE version=? AND pkgarch=? AND checksum=? AND \
|
||||
value >= (select max(value) from %s where version=? AND pkgarch=?);"
|
||||
% (self.table, self.table),
|
||||
(version, pkgarch, checksum, version, pkgarch))
|
||||
row=data.fetchone()
|
||||
if row is not None:
|
||||
return row[0]
|
||||
else:
|
||||
#no value found, try to insert
|
||||
if self.read_only:
|
||||
data = self._execute("SELECT ifnull(max(value)+1, 0) FROM %s where version=? AND pkgarch=?;" % (self.table),
|
||||
(version, pkgarch))
|
||||
return data.fetchone()[0]
|
||||
|
||||
try:
|
||||
self._execute("INSERT OR REPLACE INTO %s VALUES (?, ?, ?, (select ifnull(max(value)+1, 0) from %s where version=? AND pkgarch=?));"
|
||||
% (self.table, self.table),
|
||||
(version, pkgarch, checksum, version, pkgarch))
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
self.conn.rollback()
|
||||
|
||||
self.dirty = True
|
||||
|
||||
data=self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table,
|
||||
(version, pkgarch, checksum))
|
||||
row=data.fetchone()
|
||||
if row is not None:
|
||||
return row[0]
|
||||
else:
|
||||
raise prserv.NotFoundError
|
||||
|
||||
def get_value(self, version, pkgarch, checksum):
|
||||
if self.nohist:
|
||||
return self._get_value_no_hist(version, pkgarch, checksum)
|
||||
else:
|
||||
return self._get_value_hist(version, pkgarch, checksum)
|
||||
|
||||
def _import_hist(self, version, pkgarch, checksum, value):
|
||||
if self.read_only:
|
||||
return None
|
||||
|
||||
val = None
|
||||
data = self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table,
|
||||
(version, pkgarch, checksum))
|
||||
row = data.fetchone()
|
||||
if row is not None:
|
||||
val=row[0]
|
||||
else:
|
||||
#no value found, try to insert
|
||||
try:
|
||||
self._execute("INSERT INTO %s VALUES (?, ?, ?, ?);" % (self.table),
|
||||
(version, pkgarch, checksum, value))
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
|
||||
self.dirty = True
|
||||
|
||||
data = self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=?;" % self.table,
|
||||
(version, pkgarch, checksum))
|
||||
row = data.fetchone()
|
||||
if row is not None:
|
||||
val = row[0]
|
||||
return val
|
||||
|
||||
def _import_no_hist(self, version, pkgarch, checksum, value):
|
||||
if self.read_only:
|
||||
return None
|
||||
|
||||
try:
|
||||
#try to insert
|
||||
self._execute("INSERT INTO %s VALUES (?, ?, ?, ?);" % (self.table),
|
||||
(version, pkgarch, checksum, value))
|
||||
except sqlite3.IntegrityError as exc:
|
||||
#already have the record, try to update
|
||||
try:
|
||||
self._execute("UPDATE %s SET value=? WHERE version=? AND pkgarch=? AND checksum=? AND value<?"
|
||||
% (self.table),
|
||||
(value, version, pkgarch, checksum, value))
|
||||
except sqlite3.IntegrityError as exc:
|
||||
logger.error(str(exc))
|
||||
|
||||
self.dirty = True
|
||||
|
||||
data = self._execute("SELECT value FROM %s WHERE version=? AND pkgarch=? AND checksum=? AND value>=?;" % self.table,
|
||||
(version, pkgarch, checksum, value))
|
||||
row=data.fetchone()
|
||||
if row is not None:
|
||||
return row[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def importone(self, version, pkgarch, checksum, value):
|
||||
if self.nohist:
|
||||
return self._import_no_hist(version, pkgarch, checksum, value)
|
||||
else:
|
||||
return self._import_hist(version, pkgarch, checksum, value)
|
||||
|
||||
def export(self, version, pkgarch, checksum, colinfo):
|
||||
metainfo = {}
|
||||
#column info
|
||||
if colinfo:
|
||||
metainfo["tbl_name"] = self.table
|
||||
metainfo["core_ver"] = prserv.__version__
|
||||
metainfo["col_info"] = []
|
||||
data = self._execute("PRAGMA table_info(%s);" % self.table)
|
||||
for row in data:
|
||||
col = {}
|
||||
col["name"] = row["name"]
|
||||
col["type"] = row["type"]
|
||||
col["notnull"] = row["notnull"]
|
||||
col["dflt_value"] = row["dflt_value"]
|
||||
col["pk"] = row["pk"]
|
||||
metainfo["col_info"].append(col)
|
||||
|
||||
#data info
|
||||
datainfo = []
|
||||
|
||||
if self.nohist:
|
||||
sqlstmt = "SELECT T1.version, T1.pkgarch, T1.checksum, T1.value FROM %s as T1, \
|
||||
(SELECT version, pkgarch, max(value) as maxvalue FROM %s GROUP BY version, pkgarch) as T2 \
|
||||
WHERE T1.version=T2.version AND T1.pkgarch=T2.pkgarch AND T1.value=T2.maxvalue " % (self.table, self.table)
|
||||
else:
|
||||
sqlstmt = "SELECT * FROM %s as T1 WHERE 1=1 " % self.table
|
||||
sqlarg = []
|
||||
where = ""
|
||||
if version:
|
||||
where += "AND T1.version=? "
|
||||
sqlarg.append(str(version))
|
||||
if pkgarch:
|
||||
where += "AND T1.pkgarch=? "
|
||||
sqlarg.append(str(pkgarch))
|
||||
if checksum:
|
||||
where += "AND T1.checksum=? "
|
||||
sqlarg.append(str(checksum))
|
||||
|
||||
sqlstmt += where + ";"
|
||||
|
||||
if len(sqlarg):
|
||||
data = self._execute(sqlstmt, tuple(sqlarg))
|
||||
else:
|
||||
data = self._execute(sqlstmt)
|
||||
for row in data:
|
||||
if row["version"]:
|
||||
col = {}
|
||||
col["version"] = row["version"]
|
||||
col["pkgarch"] = row["pkgarch"]
|
||||
col["checksum"] = row["checksum"]
|
||||
col["value"] = row["value"]
|
||||
datainfo.append(col)
|
||||
return (metainfo, datainfo)
|
||||
|
||||
def dump_db(self, fd):
|
||||
writeCount = 0
|
||||
for line in self.conn.iterdump():
|
||||
writeCount = writeCount + len(line) + 1
|
||||
fd.write(line)
|
||||
fd.write("\n")
|
||||
return writeCount
|
||||
|
||||
class PRData(object):
|
||||
"""Object representing the PR database"""
|
||||
def __init__(self, filename, nohist=True, read_only=False):
|
||||
self.filename=os.path.abspath(filename)
|
||||
self.nohist=nohist
|
||||
self.read_only = read_only
|
||||
#build directory hierarchy
|
||||
try:
|
||||
os.makedirs(os.path.dirname(self.filename))
|
||||
except OSError as e:
|
||||
if e.errno != errno.EEXIST:
|
||||
raise e
|
||||
uri = "file:%s%s" % (self.filename, "?mode=ro" if self.read_only else "")
|
||||
logger.debug("Opening PRServ database '%s'" % (uri))
|
||||
self.connection=sqlite3.connect(uri, uri=True, isolation_level="EXCLUSIVE", check_same_thread = False)
|
||||
self.connection.row_factory=sqlite3.Row
|
||||
if not self.read_only:
|
||||
self.connection.execute("pragma synchronous = off;")
|
||||
self.connection.execute("PRAGMA journal_mode = MEMORY;")
|
||||
self._tables={}
|
||||
|
||||
def disconnect(self):
|
||||
self.connection.close()
|
||||
|
||||
def __getitem__(self, tblname):
|
||||
if not isinstance(tblname, str):
|
||||
raise TypeError("tblname argument must be a string, not '%s'" %
|
||||
type(tblname))
|
||||
if tblname in self._tables:
|
||||
return self._tables[tblname]
|
||||
else:
|
||||
tableobj = self._tables[tblname] = PRTable(self.connection, tblname, self.nohist, self.read_only)
|
||||
return tableobj
|
||||
|
||||
def __delitem__(self, tblname):
|
||||
if tblname in self._tables:
|
||||
del self._tables[tblname]
|
||||
logger.info("drop table %s" % (tblname))
|
||||
self.connection.execute("DROP TABLE IF EXISTS %s;" % tblname)
|
||||
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