mirror of
https://github.com/AdaCore/git-hooks.git
synced 2026-02-12 12:43:11 -08:00
For some reason that I was unable to determine, trying to cover
the case failure to fork during the second fork causes an error
when running the testsuite with coverage, such as:
$ ./coverage.sh -j16 --enable-color tests/LC28-010__daemon_utest
We get a crash during the execution of the at_exit handling when
the coverage framework tries to create a file in the root ("/")
directory, presumably to save the coverage information. I couldn't
figure out why this was happening, nor could I find a fix. Since
the code we're trying is not expected to change much over time,
and the second fork error handling is only about damage control,
I gave up on the idea. Instead, I commented out the corresponding
testing code, so as for it to be available should we try to solve
that mystery again, and I added a "pragma nocover" in run_in_daemon.
This had the side-effect of dropping one call to syslog that was
contributing to full coverage. This commit simply extends the unit
test to include a call to that function as well.
TN: U627-007
Change-Id: Iaacfd174747ff43a921f5d17e3a74900ad96a78e
98 lines
2.7 KiB
Python
98 lines
2.7 KiB
Python
"""A module to handle daemonization...
|
|
"""
|
|
|
|
from __future__ import print_function
|
|
import os
|
|
import sys
|
|
from syslog import syslog
|
|
|
|
|
|
def daemonize(output_fd=None):
|
|
"""Create a daemon process.
|
|
|
|
PARAMETERS
|
|
output_fd: If not none, a file descriptor where stdout and
|
|
stderr should be redirected.
|
|
|
|
RETURN VALUE
|
|
This function returns True for the child (daemon) process,
|
|
while it returns False for the parent process.
|
|
"""
|
|
# Flush the output a first time to make sure the child processes
|
|
# do not inherit some buffered output from the parent, causing
|
|
# some output generated by the parent to be printed multiple times
|
|
# due to both parent and child printing it at flush time.
|
|
for f in sys.stdout, sys.stderr:
|
|
f.flush()
|
|
|
|
# Perform the first fork.
|
|
try:
|
|
pid = os.fork()
|
|
if pid > 0:
|
|
# In the parent. We can now return.
|
|
return False
|
|
except OSError as e:
|
|
sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
|
|
return False
|
|
|
|
# Decouple ourselves from the parent environment.
|
|
os.chdir("/")
|
|
os.umask(0)
|
|
os.setsid()
|
|
|
|
# Perform the second fork, to daemonize ourselves.
|
|
try:
|
|
pid = os.fork()
|
|
if pid > 0:
|
|
# In the second parent. Exit.
|
|
sys.exit(0)
|
|
except OSError as e: # pragma: no cover (really hard to cover -- see U627-007)
|
|
syslog("git-hooks: fork #2 failed: (%d) %s" % (e.errno, e.strerror))
|
|
sys.exit(1)
|
|
|
|
# Flush the output...
|
|
for f in sys.stdout, sys.stderr:
|
|
f.flush()
|
|
|
|
# Perform the input/output redirection.
|
|
|
|
os.close(0)
|
|
os.dup2(os.open("/dev/null", os.O_RDONLY), 0)
|
|
|
|
if output_fd is None: # pragma: no cover (never true in testsuite mode)
|
|
output_fd = os.open("/dev/null", os.O_WRONLY)
|
|
os.close(1)
|
|
os.dup2(output_fd, 1)
|
|
os.close(2)
|
|
os.dup2(output_fd, 2)
|
|
|
|
return True
|
|
|
|
|
|
def run_in_daemon(fun):
|
|
"""Run the given callbable in a daemon process.
|
|
|
|
In GIT_HOOKS_TESTSUITE_MODE, the function's stdout and stderr
|
|
is redirected to a pipe and then re-printed on our stdout.
|
|
But this is only for testing purposes. In normal mode,
|
|
the function's stdout/stderr, as well as stdin are redirected
|
|
to /dev/null.
|
|
|
|
PARAMETERS
|
|
fun: A callable.
|
|
"""
|
|
daemon_pipe = (None, None)
|
|
if "GIT_HOOKS_TESTSUITE_MODE" in os.environ:
|
|
daemon_pipe = os.pipe()
|
|
|
|
in_daemon = daemonize(daemon_pipe[1])
|
|
if in_daemon:
|
|
fun()
|
|
sys.exit(0)
|
|
else:
|
|
if daemon_pipe[0] is not None:
|
|
os.close(daemon_pipe[1])
|
|
daemon_stdout = os.fdopen(daemon_pipe[0])
|
|
print(daemon_stdout.read(), file=sys.stderr, end="")
|
|
daemon_stdout.close()
|