Forward logs from nginx to stderr.

Much thanks to @dzuelke for the template, via the PHP buildpack.
parent adcb0b34
#!/bin/bash -e
set -o pipefail
HERE=$(dirname "$0")
# Reify the environment into an nginx config file, since nginx doesn't support
# environment variables particularly well
"${HERE}/config/make-config"
"${HERE}/nginx" -p . -c config/nginx.conf
# make a shared pipe; we'll write the name of the process that exits to it once
# that happens, and wait for that event below this particular call works on
# Linux and Mac OS (will create a literal ".XXXXXX" on Mac, but that doesn't
# matter).
wait_pipe=$(mktemp -t "heroku.waitpipe-$PORT.XXXXXX" -u)
rm -f $wait_pipe
mkfifo $wait_pipe
exec 3<> $wait_pipe
pids=()
# trap SIGQUIT (ctrl+\ on the console), SIGTERM (when we get killed) and EXIT
# (upon failure of any command due to set -e, or because of the exit 1 at the
# very end), we then 1) restore the trap so it doesn't fire again a loop due to
# the exit at the end (if we're handling SIGQUIT or SIGTERM) or another signal
# 2) remove our FIFO from above 3) kill all the subshells we've spawned - they
# in turn have their own traps to kill their respective subprocesses 3a) send
# STDERR to /dev/null so we don't see "no such process" errors - after all, one
# of the subshells may be gone 3b) || true so that set -e doesn't cause a mess
# if the kill returns 1 on "no such process" cases (which is likely) 4) exit in
# case we're handling SIGQUIT or SIGTERM
trap 'trap - QUIT TERM EXIT; echo "Going down, terminating child processes..." >&2; rm -f ${wait_pipe} || true; kill -TERM "${pids[@]}" 2> /dev/null || true; exit' QUIT TERM EXIT
# if FD 1 is a TTY (that's the -t 1 check), trap SIGINT/Ctrl+C
# 1) restore the INT trap so it doesn't fire in a loop due to 2)
# 2) be nice to the caller and send SIGINT to ourselves (http://mywiki.wooledge.org/SignalTrap#Special_Note_On_SIGINT)
# 3) *do* exit after all to run the cleanup code from above (avoids duplication)
if [[ -t 1 ]]; then
trap 'trap - INT; kill -INT $$; exit' INT;
# if FD 1 is not a TTY (e.g. when we're run through 'foreman start'), do nothing
# on SIGINT; the assumption is that the parent will send us a SIGTERM or
# something when this happens. With the trap above, Ctrl+C-ing out of a 'foreman
# start' run would trigger the INT trap both in Foreman and here (because Ctrl+C
# sends SIGINT to the entire process group, but there is no way to tell the two
# cases apart), and while the trap is still doing its shutdown work triggered by
# the SIGTERM from the Ctrl+C, Foreman would then send a SIGTERM because that's
# what it does when it receives a SIGINT itself.
else
trap '' INT;
fi
# we are now launching a subshell for each of the tasks (log tail, web server)
# 1) each subshell has a trap on EXIT that echos the command name to FD 3 (see the FIFO set up above)
# 1a) a 'read' at the end of the script will block on reading from that FD and then trigger the exit trap above, which does the cleanup
# 2) each subshell also has a trap on TERM that
# 2a) kills $! (the last process executed)
# 2b) ... which in turn will unblock the 'wait' in 4)
# 3) execute the command in the background
# 4) 'wait' on the command (wait is interrupted by an incoming TERM to the subshell, whereas running 3) in the foreground would wait for that 3) to finish before triggering the trap)
# 5) add the PID of the subshell to the array that the EXIT trap further above uses to clean everything up
echo "Starting log redirection..." >&2
(
trap 'echo "logs" >&3;' EXIT
trap 'kill -TERM $! 2> /dev/null' TERM
mkdir -p logs
rm -f logs/access.log
mkfifo "logs/access.log"
cat "logs/access.log" 1>&2 &
wait
) & pids+=($!)
echo "Starting nginx..." >&2
(
trap 'echo "nginx" >&3;' EXIT
trap 'kill -TERM $! 2> /dev/null' TERM
"${HERE}/nginx" -p . -c config/nginx.conf &
wait
) & pids+=($!)
# wait for something to come from the FIFO attached to FD 3, which means that
# the given process was killed or has failed this will be interrupted by a
# SIGTERM or SIGINT in the traps further up if the pipe unblocks and this
# executes, then we won't read it again, so if the traps further up kill the
# remaining subshells above, their writing to FD 3 will have no effect
read exitproc <&3
# we'll only reach this if one of the processes above has terminated
echo "Process exited unexpectedly: $exitproc" >&2
# this will trigger the EXIT trap further up and kill all remaining children
exit 1
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