mirror of
https://git.yoctoproject.org/poky
synced 2026-05-30 12:29:55 +00:00
bitbake: prserv: Extract daemonization from PRServer class
The code to start the prservice process as a daemon is extracted out of the PRServer class and simplified. This makes the PRServer class easier to modernise as it no longer needs to worry about process management. (Bitbake rev: 39c7c158c52157b18f5ccbbd673e3298e6402f52) Signed-off-by: Paul Barker <pbarker@konsulko.com> Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
committed by
Richard Purdie
parent
802024fd2a
commit
d66a1d83f5
+72
-103
@@ -34,7 +34,7 @@ singleton = None
|
|||||||
|
|
||||||
|
|
||||||
class PRServer(SimpleXMLRPCServer):
|
class PRServer(SimpleXMLRPCServer):
|
||||||
def __init__(self, dbfile, logfile, interface, daemon=True):
|
def __init__(self, dbfile, logfile, interface):
|
||||||
''' constructor '''
|
''' constructor '''
|
||||||
try:
|
try:
|
||||||
SimpleXMLRPCServer.__init__(self, interface,
|
SimpleXMLRPCServer.__init__(self, interface,
|
||||||
@@ -47,7 +47,6 @@ class PRServer(SimpleXMLRPCServer):
|
|||||||
raise PRServiceConfigError
|
raise PRServiceConfigError
|
||||||
|
|
||||||
self.dbfile=dbfile
|
self.dbfile=dbfile
|
||||||
self.daemon=daemon
|
|
||||||
self.logfile=logfile
|
self.logfile=logfile
|
||||||
self.working_thread=None
|
self.working_thread=None
|
||||||
self.host, self.port = self.socket.getsockname()
|
self.host, self.port = self.socket.getsockname()
|
||||||
@@ -176,106 +175,6 @@ class PRServer(SimpleXMLRPCServer):
|
|||||||
os.close(self.quitpipein)
|
os.close(self.quitpipein)
|
||||||
return
|
return
|
||||||
|
|
||||||
def start(self):
|
|
||||||
if self.daemon:
|
|
||||||
pid = self.daemonize()
|
|
||||||
else:
|
|
||||||
pid = self.fork()
|
|
||||||
self.pid = pid
|
|
||||||
|
|
||||||
# Ensure both the parent sees this and the child from the work_forever log entry above
|
|
||||||
logger.info("Started PRServer with DBfile: %s, IP: %s, PORT: %s, PID: %s" %
|
|
||||||
(self.dbfile, self.host, self.port, str(pid)))
|
|
||||||
|
|
||||||
def delpid(self):
|
|
||||||
os.remove(self.pidfile)
|
|
||||||
|
|
||||||
def daemonize(self):
|
|
||||||
"""
|
|
||||||
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))
|
|
||||||
|
|
||||||
self.cleanup_handles()
|
|
||||||
os._exit(0)
|
|
||||||
|
|
||||||
def fork(self):
|
|
||||||
try:
|
|
||||||
pid = os.fork()
|
|
||||||
if pid > 0:
|
|
||||||
self.socket.close() # avoid ResourceWarning in parent
|
|
||||||
return pid
|
|
||||||
except OSError as e:
|
|
||||||
raise Exception("%s [%d]" % (e.strerror, e.errno))
|
|
||||||
|
|
||||||
bb.utils.signal_on_parent_exit("SIGTERM")
|
|
||||||
self.cleanup_handles()
|
|
||||||
os._exit(0)
|
|
||||||
|
|
||||||
def cleanup_handles(self):
|
|
||||||
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(self.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(self.pidfile, 'w') as pf:
|
|
||||||
pf.write("%s\n" % pid)
|
|
||||||
|
|
||||||
self.work_forever()
|
|
||||||
self.delpid()
|
|
||||||
|
|
||||||
class PRServSingleton(object):
|
class PRServSingleton(object):
|
||||||
def __init__(self, dbfile, logfile, interface):
|
def __init__(self, dbfile, logfile, interface):
|
||||||
self.dbfile = dbfile
|
self.dbfile = dbfile
|
||||||
@@ -324,6 +223,76 @@ class PRServerConnection(object):
|
|||||||
def getinfo(self):
|
def getinfo(self):
|
||||||
return self.host, self.port
|
return self.host, self.port
|
||||||
|
|
||||||
|
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):
|
def start_daemon(dbfile, host, port, logfile):
|
||||||
ip = socket.gethostbyname(host)
|
ip = socket.gethostbyname(host)
|
||||||
pidfile = PIDPREFIX % (ip, port)
|
pidfile = PIDPREFIX % (ip, port)
|
||||||
@@ -339,7 +308,7 @@ def start_daemon(dbfile, host, port, logfile):
|
|||||||
return 1
|
return 1
|
||||||
|
|
||||||
server = PRServer(os.path.abspath(dbfile), os.path.abspath(logfile), (ip,port))
|
server = PRServer(os.path.abspath(dbfile), os.path.abspath(logfile), (ip,port))
|
||||||
server.start()
|
run_as_daemon(server.work_forever, pidfile, os.path.abspath(logfile))
|
||||||
|
|
||||||
# Sometimes, the port (i.e. localhost:0) indicated by the user does not match with
|
# Sometimes, the port (i.e. localhost:0) indicated by the user does not match with
|
||||||
# the one the server actually is listening, so at least warn the user about it
|
# the one the server actually is listening, so at least warn the user about it
|
||||||
|
|||||||
Reference in New Issue
Block a user