Commit b78aa8c2 authored by Iustin Pop's avatar Iustin Pop
Browse files

Change daemon.GenericMain/utils.Daemonize workflow



This patch copies the pipe-based error reporting functionality from
utils.StartDaemon (I gave up for now on tryin to merge the two).

This patch will fix two longstanding bugs:

- if we fork, we lose all error reporting from the child to the original
  parent
- if we fork, the original parent exits before the child is ready to
  "work" (whatever the work might be)

Both these are fixed once the users of daemon.GenericMain are converted
to the three-state setup, as we'll get error reporting via the pipe and
also not exit until the PrepFn is done.
Signed-off-by: default avatarIustin Pop <iustin@google.com>
Reviewed-by: default avatarMichael Hanselmann <hansmi@google.com>
parent b42ea9ed
......@@ -624,22 +624,34 @@ def GenericMain(daemon_name, optionparser,
if options.fork:
utils.CloseFDs()
utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
wpipe = utils.Daemonize(logfile=constants.DAEMONS_LOGFILES[daemon_name])
else:
wpipe = None
utils.WritePidFile(utils.DaemonPidFileName(daemon_name))
try:
utils.SetupLogging(logfile=constants.DAEMONS_LOGFILES[daemon_name],
debug=options.debug,
stderr_logging=not options.fork,
multithreaded=multithreaded,
program=daemon_name,
syslog=options.syslog,
console_logging=console_logging)
logging.info("%s daemon startup", daemon_name)
if callable(prepare_fn):
prep_results = prepare_fn(options, args)
else:
prep_results = None
try:
utils.SetupLogging(logfile=constants.DAEMONS_LOGFILES[daemon_name],
debug=options.debug,
stderr_logging=not options.fork,
multithreaded=multithreaded,
program=daemon_name,
syslog=options.syslog,
console_logging=console_logging)
if callable(prepare_fn):
prep_results = prepare_fn(options, args)
else:
prep_results = None
logging.info("%s daemon startup", daemon_name)
except Exception, err:
if wpipe is not None:
os.write(wpipe, str(err))
raise
if wpipe is not None:
# we're done with the preparation phase, we close the pipe to
# let the parent know it's safe to exit
os.close(wpipe)
exec_fn(options, args, prep_results)
finally:
......
......@@ -348,8 +348,8 @@ def StartDaemon(cmd, env=None, cwd="/", output=None, output_fd=None,
finally:
_CloseFDNoErr(errpipe_write)
# Wait for daemon to be started (or an error message to arrive) and read
# up to 100 KB as an error message
# Wait for daemon to be started (or an error message to
# arrive) and read up to 100 KB as an error message
errormsg = RetryOnSignal(os.read, errpipe_read, 100 * 1024)
finally:
_CloseFDNoErr(errpipe_read)
......@@ -2146,6 +2146,12 @@ def Daemonize(logfile):
# pylint: disable-msg=W0212
# yes, we really want os._exit
# TODO: do another attempt to merge Daemonize and StartDaemon, or at
# least abstract the pipe functionality between them
# Create pipe for sending error messages
(rpipe, wpipe) = os.pipe()
# this might fail
pid = os.fork()
if (pid == 0): # The first child.
......@@ -2154,15 +2160,24 @@ def Daemonize(logfile):
# this might fail
pid = os.fork() # Fork a second child.
if (pid == 0): # The second child.
pass # nothing special to do in the child
_CloseFDNoErr(rpipe)
else:
# exit() or _exit()? See below.
os._exit(0) # Exit parent (the first child) of the second child.
else:
os._exit(0) # Exit parent of the first child.
_CloseFDNoErr(wpipe)
# Wait for daemon to be started (or an error message to
# arrive) and read up to 100 KB as an error message
errormsg = RetryOnSignal(os.read, rpipe, 100 * 1024)
if errormsg:
sys.stderr.write("Error when starting daemon process: %r\n" % errormsg)
rcode = 1
else:
rcode = 0
os._exit(rcode) # Exit parent of the first child.
SetupDaemonFDs(logfile, None)
return 0
return wpipe
def DaemonPidFileName(name):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment