From 6c476e790ce56a207f4f76ecc7b1de9a7c00ff35 Mon Sep 17 00:00:00 2001
From: "Kyle J. McKay" <mackyle@gmail.com>
Date: Wed, 27 Jan 2021 23:25:22 -0700
Subject: [PATCH] bin/gitweb-gc.sh: add gitweb FastCGI utility script

The FastCGI specification can still be found at:

  https://web.archive.org/web/20160310000121/http://www.fastcgi.com/devkit/doc/fcgi-spec.html

or alternatively archived here:

  https://fast-cgi.github.io/

Section "7. Errors" states:

> A FastCGI application exits with zero status to indicate that it
> terminated on purpose, e.g. in order to perform a crude form of
> garbage collection. A FastCGI application that exits with nonzero
> status is assumed to have crashed.

The gitweb.cgi application running in FastCGI mode falls into the
category of "application exits with zero status to indicate that
it terminated on purpose, e.g. in order to perform a crude form of
garbage collection."

The standard gitweb.cgi when running in FastCGI mode always exits with
"zero status" after serving 100 requests.

While Apache's mod_fcgid can easily be configured to expect this
behavior (via the `FcgidCmdOptions /gitweb.cgi MaxRequestsPerProcess 100`
configuration line), mod_fcgid handles unexpected "zero status" exits
just fine as well and simply restarts the cgi as needed.

Some other web servers that provide FastCGI support are not nearly as
forgiving (they do not handle the unexpected "zero status" exit) or as
accomodating (they do not have the equivalent of the `MaxRequestsPerProcess`
configuration option).

For example, lighttpd behaves very badly when gitweb exits with the
unexpected "zero status" after 100 requests and then proceeds to drop and
fail any outstanding requests to that FastCGI application before it manages
to restart it.

This causes a bad user experience as every so often requests will fail,
but then succeed when retried.

To accomodate these web servers that poorly implement the FastCGI
specification while allowing gitweb.cgi running in FastCGI mode to work
without intermittent failures, provide an intermediary "helper" script
that conceals the "zero status" (aka "crude form of garbage collection")
exits from the web server and seemlessly restarts a new copy of gitweb.cgi.

This allows use of gitweb.cgi in FastCGI mode reliably with a greater variety
of web server software than would otherwise be possible.

The new script, "gitweb-gc.sh" gets installed in the
"$Girocco::Config::basedir/bin" directory after performing an install of
Girocco.

When configuring a web server that does not properly handle FastCGI
"zero status [...] crude form of garbage collection" exits, instead of
configuring it to execute "$Gircco::Config::cgiroot/gitweb.cgi" directly,
it must be configured to execute "$Girocco::Config::basedir/bin/gitweb-gc.sh"
instead.

This will allow the deficient web server software to provide gitweb
services via FastCGI (much, much, much, much more responsive than spawning
a new gitweb instance for each request) while avoiding sporadic intermittent
failures as gitweb exits after serving 100 requests to perform a "crude form
of garbage collection."

Signed-off-by: Kyle J. McKay <mackyle@gmail.com>
---
 bin/gitweb-gc.sh | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
 create mode 100755 bin/gitweb-gc.sh

diff --git a/bin/gitweb-gc.sh b/bin/gitweb-gc.sh
new file mode 100755
index 0000000..d869ecf
--- /dev/null
+++ b/bin/gitweb-gc.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# Allow gitweb.cgi to be run from a fastcgi server that does
+# not really support periodic "garbage collection" exits
+# (even on an exit status of 0) without losing fastcgi requests.
+# Signals are forwarded in both directions.
+cgiroot=@cgiroot@
+bg=
+handle_sig() { [ -z "$bg" ] || kill -$1 "$bg" 2>/dev/null; }
+# backgrounding the cgi with '&' causes it to ignore SIGINT and SIGQUIT
+# therefore we redirect INT to TERM and QUIT to ABRT when forwarding those
+trap 'handle_sig HUP' HUP
+trap 'handle_sig ABRT' ABRT QUIT
+trap 'handle_sig PIPE' PIPE
+trap 'handle_sig ALRM' ALRM
+trap 'handle_sig TERM' TERM INT
+trap 'handle_sig USR1' USR1
+trap 'handle_sig USR2' USR2
+while :; do
+	exec 3<&0
+	(exec 0<&3 3<&-; exec "$cgiroot/gitweb.cgi" "$@")&
+	bg="$!"
+	exec 3<&-
+	while :; do
+		ec=0
+		wait "$bg" || ec="$?"
+		kill -0 "$bg" 2>/dev/null || break
+		# was a signal to self that's now been forwarded
+		# go around the loop again
+	done
+	bg=
+	[ "${ec:-0}" = "0" ] || break
+	# was a gitweb "garbage collection" exit
+	# go around the loop again and restart gitweb
+done
+trap - HUP INT QUIT ABRT PIPE ALRM TERM USR1 USR2
+if [ "${ec:-0}" -gt 128 ] && [ "${ec:-0}" -lt 160 ]; then
+	# self terminate
+	kill -$(($ec-128)) $$
+fi
+exit "$ec"
-- 
2.11.4.GIT