mirror of
https://git.yoctoproject.org/poky
synced 2026-02-10 02:33:02 +01:00
There were various problems in the server startup loggin: a) stdout/stderr were not being flushed before forking which could potentially duplicate output b) there were separate buffers for stdout/stderr leading to confusing logs where the entries could be reordered. This was particularly confusing due to the separator the logs use to idendify new messages c) an fd wasn't being closed during server startup meaning if the server failed to start, the closed fd wasn't detected as it was held open by the other reference d) If the pipe was detected as being closed, the code incorrectly retried server startup e) The event code would remap stdout/stderr without flushing them, leading to lose log messages (Bitbake rev: 0594faa0b52ce5dbd948d836d88617d38d9862d1) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
94 lines
3.6 KiB
Python
94 lines
3.6 KiB
Python
"""
|
|
Python Daemonizing helper
|
|
|
|
Originally based on code Copyright (C) 2005 Chad J. Schroeder but now heavily modified
|
|
to allow a function to be daemonized and return for bitbake use by Richard Purdie
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import io
|
|
import traceback
|
|
|
|
def createDaemon(function, logfile):
|
|
"""
|
|
Detach a process from the controlling terminal and run it in the
|
|
background as a daemon, returning control to the caller.
|
|
"""
|
|
|
|
# Ensure stdout/stderror are flushed before forking to avoid duplicate output
|
|
sys.stdout.flush()
|
|
sys.stderr.flush()
|
|
|
|
try:
|
|
# Fork a child process so the parent can exit. This returns control to
|
|
# the command-line or shell. It also guarantees that the child will not
|
|
# be a process group leader, since the child receives a new process ID
|
|
# and inherits the parent's process group ID. This step is required
|
|
# to insure that the next call to os.setsid is successful.
|
|
pid = os.fork()
|
|
except OSError as e:
|
|
raise Exception("%s [%d]" % (e.strerror, e.errno))
|
|
|
|
if (pid == 0): # The first child.
|
|
# To become the session leader of this new session and the process group
|
|
# leader of the new process group, we call os.setsid(). The process is
|
|
# also guaranteed not to have a controlling terminal.
|
|
os.setsid()
|
|
try:
|
|
# Fork a second child and exit immediately to prevent zombies. This
|
|
# causes the second child process to be orphaned, making the init
|
|
# process responsible for its cleanup. And, since the first child is
|
|
# a session leader without a controlling terminal, it's possible for
|
|
# it to acquire one by opening a terminal in the future (System V-
|
|
# based systems). This second fork guarantees that the child is no
|
|
# longer a session leader, preventing the daemon from ever acquiring
|
|
# a controlling terminal.
|
|
pid = os.fork() # Fork a second child.
|
|
except OSError as e:
|
|
raise Exception("%s [%d]" % (e.strerror, e.errno))
|
|
|
|
if (pid != 0):
|
|
# Parent (the first child) of the second child.
|
|
# exit() or _exit()?
|
|
# _exit is like exit(), but it doesn't call any functions registered
|
|
# with atexit (and on_exit) or any registered signal handlers. It also
|
|
# closes any open file descriptors, but doesn't flush any buffered output.
|
|
# Using exit() may cause all any temporary files to be unexpectedly
|
|
# removed. It's therefore recommended that child branches of a fork()
|
|
# and the parent branch(es) of a daemon use _exit().
|
|
os._exit(0)
|
|
else:
|
|
os.waitpid(pid, 0)
|
|
return
|
|
|
|
# The second child.
|
|
|
|
# Replace standard fds with our own
|
|
si = open('/dev/null', 'r')
|
|
os.dup2(si.fileno(), sys.stdin.fileno())
|
|
|
|
try:
|
|
so = open(logfile, 'a+')
|
|
os.dup2(so.fileno(), sys.stdout.fileno())
|
|
os.dup2(so.fileno(), sys.stderr.fileno())
|
|
except io.UnsupportedOperation:
|
|
sys.stdout = open(logfile, 'a+')
|
|
|
|
# Have stdout and stderr be the same so log output matches chronologically
|
|
# and there aren't two seperate buffers
|
|
sys.stderr = sys.stdout
|
|
|
|
try:
|
|
function()
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
finally:
|
|
bb.event.print_ui_queue()
|
|
# os._exit() doesn't flush open files like os.exit() does. Manually flush
|
|
# stdout and stderr so that any logging output will be seen, particularly
|
|
# exception tracebacks.
|
|
sys.stdout.flush()
|
|
sys.stderr.flush()
|
|
os._exit(0)
|