git-instaweb.sh 21.8 KB
Newer Older
1 2 3 4
#!/bin/sh
#
# Copyright (c) 2006 Eric Wong
#
5

6
PERL='@@PERL@@'
7
OPTIONS_KEEPDASHDASH=
8
OPTIONS_STUCKLONG=
9
OPTIONS_SPEC="\
10
git instaweb [options] (--start | --stop | --restart)
11 12 13 14 15 16 17 18 19 20 21
--
l,local        only bind on 127.0.0.1
p,port=        the port to bind to
d,httpd=       the command to launch
b,browser=     the browser to launch
m,module-path= the module path (only needed for apache2)
 Action
stop           stop the web server
start          start the web server
restart        restart the web server
"
22

23
SUBDIRECTORY_OK=Yes
24 25
. git-sh-setup

26
fqgitdir="$GIT_DIR"
27 28
local="$(git config --bool --get instaweb.local)"
httpd="$(git config --get instaweb.httpd)"
29
root="$(git config --get instaweb.gitwebdir)"
30 31
port=$(git config --get instaweb.port)
module_path="$(git config --get instaweb.modulepath)"
32
action="browse"
33

34
conf="$GIT_DIR/gitweb/httpd.conf"
35 36 37

# Defaults:

38
# if installed, it doesn't need further configuration (module_path)
39 40
test -z "$httpd" && httpd='lighttpd -f'

41 42 43
# Default is @@GITWEBDIR@@
test -z "$root" && root='@@GITWEBDIR@@'

44 45 46
# any untaken local port will do...
test -z "$port" && port=1234

47 48
resolve_full_httpd () {
	case "$httpd" in
49 50
	*apache2*|*lighttpd*|*httpd*)
		# yes, *httpd* covers *lighttpd* above, but it is there for clarity
51
		# ensure that the apache2/lighttpd command ends with "-f"
52
		if ! echo "$httpd" | sane_grep -- '-f *$' >/dev/null 2>&1
53 54 55 56
		then
			httpd="$httpd -f"
		fi
		;;
57 58 59 60 61 62
	*plackup*)
		# server is started by running via generated gitweb.psgi in $fqgitdir/gitweb
		full_httpd="$fqgitdir/gitweb/gitweb.psgi"
		httpd_only="${httpd%% *}" # cut on first space
		return
		;;
63
	*webrick*)
64
		# server is started by running via generated webrick.rb in
65
		# $fqgitdir/gitweb
66
		full_httpd="$fqgitdir/gitweb/webrick.rb"
67 68 69
		httpd_only="${httpd%% *}" # cut on first space
		return
		;;
70 71 72 73 74 75 76
	*python*)
		# server is started by running via generated gitweb.py in
		# $fqgitdir/gitweb
		full_httpd="$fqgitdir/gitweb/gitweb.py"
		httpd_only="${httpd%% *}" # cut on first space
		return
		;;
77 78 79
	esac

	httpd_only="$(echo $httpd | cut -f1 -d' ')"
80
	if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev/null 2>&1;; esac
81
	then
82
		full_httpd=$httpd
83 84 85
	else
		# many httpds are installed in /usr/sbin or /usr/local/sbin
		# these days and those are not in most users $PATHs
86 87
		# in addition, we may have generated a server script
		# in $fqgitdir/gitweb.
88
		for i in /usr/local/sbin /usr/sbin "$root" "$fqgitdir/gitweb"
89 90 91
		do
			if test -x "$i/$httpd_only"
			then
92
				full_httpd=$i/$httpd
93 94 95
				return
			fi
		done
96 97 98

		echo >&2 "$httpd_only not found. Install $httpd_only or use" \
		     "--httpd to specify another httpd daemon."
99
		exit 1
100
	fi
101 102 103
}

start_httpd () {
104 105 106 107 108
	if test -f "$fqgitdir/pid"; then
		say "Instance already running. Restarting..."
		stop_httpd
	fi

109 110
	# here $httpd should have a meaningful value
	resolve_full_httpd
111 112 113 114 115 116
	mkdir -p "$fqgitdir/gitweb/$httpd_only"
	conf="$fqgitdir/gitweb/$httpd_only.conf"

	# generate correct config file if it doesn't exist
	test -f "$conf" || configure_httpd
	test -f "$fqgitdir/gitweb/gitweb_config.perl" || gitweb_conf
117 118

	# don't quote $full_httpd, there can be arguments to it (-f)
119
	case "$httpd" in
120
	*mongoose*|*plackup*|*python*)
121
		#These servers don't have a daemon mode so we'll have to fork it
122
		$full_httpd "$conf" &
123 124 125 126 127 128 129 130 131 132 133 134 135
		#Save the pid before doing anything else (we'll print it later)
		pid=$!

		if test $? != 0; then
			echo "Could not execute http daemon $httpd."
			exit 1
		fi

		cat > "$fqgitdir/pid" <<EOF
$pid
EOF
		;;
	*)
136
		$full_httpd "$conf"
137 138 139 140 141 142
		if test $? != 0; then
			echo "Could not execute http daemon $httpd."
			exit 1
		fi
		;;
	esac
143 144 145
}

stop_httpd () {
146
	test -f "$fqgitdir/pid" && kill $(cat "$fqgitdir/pid")
147
	rm -f "$fqgitdir/pid"
148 149
}

150 151 152 153 154 155 156 157 158 159 160 161 162
httpd_is_ready () {
	"$PERL" -MIO::Socket::INET -e "
local \$| = 1; # turn on autoflush
exit if (IO::Socket::INET->new('127.0.0.1:$port'));
print 'Waiting for \'$httpd\' to start ..';
do {
	print '.';
	sleep(1);
} until (IO::Socket::INET->new('127.0.0.1:$port'));
print qq! (done)\n!;
"
}

163
while test $# != 0
164 165 166
do
	case "$1" in
	--stop|stop)
167
		action="stop"
168 169
		;;
	--start|start)
170
		action="start"
171 172
		;;
	--restart|restart)
173
		action="restart"
174
		;;
175
	-l|--local)
176 177
		local=true
		;;
178 179 180 181 182 183 184
	-d|--httpd)
		shift
		httpd="$1"
		;;
	-b|--browser)
		shift
		browser="$1"
185
		;;
186 187 188
	-p|--port)
		shift
		port="$1"
189
		;;
190 191 192
	-m|--module-path)
		shift
		module_path="$1"
193
		;;
194
	--)
195 196 197 198 199 200 201 202 203
		;;
	*)
		usage
		;;
	esac
	shift
done

mkdir -p "$GIT_DIR/gitweb/tmp"
204
GIT_EXEC_PATH="$(git --exec-path)"
205
GIT_DIR="$fqgitdir"
206 207
GITWEB_CONFIG="$fqgitdir/gitweb/gitweb_config.perl"
export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
208

209
webrick_conf () {
210 211 212 213 214
	# webrick seems to have no way of passing arbitrary environment
	# variables to the underlying CGI executable, so we wrap the
	# actual gitweb.cgi using a shell script to force it
  wrapper="$fqgitdir/gitweb/$httpd/wrapper.sh"
	cat > "$wrapper" <<EOF
215
#!@SHELL_PATH@
216 217 218 219 220 221 222 223 224 225 226
# we use this shell script wrapper around the real gitweb.cgi since
# there appears to be no other way to pass arbitrary environment variables
# into the CGI process
GIT_EXEC_PATH=$GIT_EXEC_PATH GIT_DIR=$GIT_DIR GITWEB_CONFIG=$GITWEB_CONFIG
export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
exec $root/gitweb.cgi
EOF
	chmod +x "$wrapper"

	# This assumes _ruby_ is in the user's $PATH. that's _one_
	# portable way to run ruby, which could be installed anywhere, really.
227 228
	# generate a standalone server script in $fqgitdir/gitweb.
	cat >"$fqgitdir/gitweb/$httpd.rb" <<EOF
229
#!/usr/bin/env ruby
230
require 'webrick'
231
require 'logger'
232 233 234
options = {
  :Port => $port,
  :DocumentRoot => "$root",
235 236 237 238 239
  :Logger => Logger.new('$fqgitdir/gitweb/error.log'),
  :AccessLog => [
    [ Logger.new('$fqgitdir/gitweb/access.log'),
      WEBrick::AccessLog::COMBINED_LOG_FORMAT ]
  ],
240 241 242 243 244 245 246 247
  :DirectoryIndex => ["gitweb.cgi"],
  :CGIInterpreter => "$wrapper",
  :StartCallback => lambda do
    File.open("$fqgitdir/pid", "w") { |f| f.puts Process.pid }
  end,
  :ServerType => WEBrick::Daemon,
}
options[:BindAddress] = '127.0.0.1' if "$local" == "true"
248 249 250 251 252 253
server = WEBrick::HTTPServer.new(options)
['INT', 'TERM'].each do |signal|
  trap(signal) {server.shutdown}
end
server.start
EOF
254 255 256
	chmod +x "$fqgitdir/gitweb/$httpd.rb"
	# configuration is embedded in server script file, webrick.rb
	rm -f "$conf"
257 258
}

259 260
lighttpd_conf () {
	cat > "$conf" <<EOF
261
server.document-root = "$root"
262
server.port = $port
263
server.modules = ( "mod_setenv", "mod_cgi" )
264 265
server.indexfiles = ( "gitweb.cgi" )
server.pid-file = "$fqgitdir/pid"
266
server.errorlog = "$fqgitdir/gitweb/$httpd_only/error.log"
267 268 269

# to enable, add "mod_access", "mod_accesslog" to server.modules
# variable above and uncomment this
270
#accesslog.filename = "$fqgitdir/gitweb/$httpd_only/access.log"
271

272
setenv.add-environment = ( "PATH" => env.PATH, "GITWEB_CONFIG" => env.GITWEB_CONFIG )
273

274
cgi.assign = ( ".cgi" => "" )
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330

# mimetype mapping
mimetype.assign             = (
  ".pdf"          =>      "application/pdf",
  ".sig"          =>      "application/pgp-signature",
  ".spl"          =>      "application/futuresplash",
  ".class"        =>      "application/octet-stream",
  ".ps"           =>      "application/postscript",
  ".torrent"      =>      "application/x-bittorrent",
  ".dvi"          =>      "application/x-dvi",
  ".gz"           =>      "application/x-gzip",
  ".pac"          =>      "application/x-ns-proxy-autoconfig",
  ".swf"          =>      "application/x-shockwave-flash",
  ".tar.gz"       =>      "application/x-tgz",
  ".tgz"          =>      "application/x-tgz",
  ".tar"          =>      "application/x-tar",
  ".zip"          =>      "application/zip",
  ".mp3"          =>      "audio/mpeg",
  ".m3u"          =>      "audio/x-mpegurl",
  ".wma"          =>      "audio/x-ms-wma",
  ".wax"          =>      "audio/x-ms-wax",
  ".ogg"          =>      "application/ogg",
  ".wav"          =>      "audio/x-wav",
  ".gif"          =>      "image/gif",
  ".jpg"          =>      "image/jpeg",
  ".jpeg"         =>      "image/jpeg",
  ".png"          =>      "image/png",
  ".xbm"          =>      "image/x-xbitmap",
  ".xpm"          =>      "image/x-xpixmap",
  ".xwd"          =>      "image/x-xwindowdump",
  ".css"          =>      "text/css",
  ".html"         =>      "text/html",
  ".htm"          =>      "text/html",
  ".js"           =>      "text/javascript",
  ".asc"          =>      "text/plain",
  ".c"            =>      "text/plain",
  ".cpp"          =>      "text/plain",
  ".log"          =>      "text/plain",
  ".conf"         =>      "text/plain",
  ".text"         =>      "text/plain",
  ".txt"          =>      "text/plain",
  ".dtd"          =>      "text/xml",
  ".xml"          =>      "text/xml",
  ".mpeg"         =>      "video/mpeg",
  ".mpg"          =>      "video/mpeg",
  ".mov"          =>      "video/quicktime",
  ".qt"           =>      "video/quicktime",
  ".avi"          =>      "video/x-msvideo",
  ".asf"          =>      "video/x-ms-asf",
  ".asx"          =>      "video/x-ms-asf",
  ".wmv"          =>      "video/x-ms-wmv",
  ".bz2"          =>      "application/x-bzip",
  ".tbz"          =>      "application/x-bzip-compressed-tar",
  ".tar.bz2"      =>      "application/x-bzip-compressed-tar",
  ""              =>      "text/plain"
 )
331
EOF
332
	test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf"
333 334 335
}

apache2_conf () {
336 337 338 339 340 341 342 343 344 345 346
	for candidate in \
		/etc/httpd \
		/usr/lib/apache2 \
		/usr/lib/httpd ;
	do
		if test -d "$candidate/modules"
		then
			module_path="$candidate/modules"
			break
		fi
	done
347
	bind=
348
	test x"$local" = xtrue && bind='127.0.0.1:'
349
	echo 'text/css css' > "$fqgitdir/mime.types"
350
	cat > "$conf" <<EOF
351
ServerName "git-instaweb"
352 353
ServerRoot "$root"
DocumentRoot "$root"
354 355
ErrorLog "$fqgitdir/gitweb/$httpd_only/error.log"
CustomLog "$fqgitdir/gitweb/$httpd_only/access.log" combined
356 357
PidFile "$fqgitdir/pid"
Listen $bind$port
358 359
EOF

360 361 362 363 364 365 366 367 368 369
	for mod in mpm_event mpm_prefork mpm_worker
	do
		if test -e $module_path/mod_${mod}.so
		then
			echo "LoadModule ${mod}_module " \
			     "$module_path/mod_${mod}.so" >> "$conf"
			# only one mpm module permitted
			break
		fi
	done
370
	for mod in mime dir env log_config authz_core unixd
371 372 373
	do
		if test -e $module_path/mod_${mod}.so
		then
374 375 376 377 378
			echo "LoadModule ${mod}_module " \
			     "$module_path/mod_${mod}.so" >> "$conf"
		fi
	done
	cat >> "$conf" <<EOF
379
TypesConfig "$fqgitdir/mime.types"
380 381 382 383 384
DirectoryIndex gitweb.cgi
EOF

	# check to see if Dennis Stosberg's mod_perl compatibility patch
	# (<20060621130708.Gcbc6e5c@leonov.stosberg.net>) has been applied
385
	if test -f "$module_path/mod_perl.so" &&
386
	   sane_grep 'MOD_PERL' "$root/gitweb.cgi" >/dev/null
387 388 389 390 391
	then
		# favor mod_perl if available
		cat >> "$conf" <<EOF
LoadModule perl_module $module_path/mod_perl.so
PerlPassEnv GIT_DIR
392
PerlPassEnv GIT_EXEC_PATH
393
PerlPassEnv GITWEB_CONFIG
394 395 396 397 398 399 400 401 402
<Location /gitweb.cgi>
	SetHandler perl-script
	PerlResponseHandler ModPerl::Registry
	PerlOptions +ParseHeaders
	Options +ExecCGI
</Location>
EOF
	else
		# plain-old CGI
403
		resolve_full_httpd
404
		list_mods=$(echo "$full_httpd" | sed 's/-f$/-l/')
405
		$list_mods | sane_grep 'mod_cgi\.c' >/dev/null 2>&1 || \
406 407 408 409 410 411 412 413 414 415 416 417 418 419 420
		if test -f "$module_path/mod_cgi.so"
		then
			echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
		else
			$list_mods | grep 'mod_cgid\.c' >/dev/null 2>&1 || \
			if test -f "$module_path/mod_cgid.so"
			then
				echo "LoadModule cgid_module $module_path/mod_cgid.so" \
					>> "$conf"
			else
				echo "You have no CGI support!"
				exit 2
			fi
			echo "ScriptSock logs/gitweb.sock" >> "$conf"
		fi
421
		cat >> "$conf" <<EOF
422 423 424
PassEnv GIT_DIR
PassEnv GIT_EXEC_PATH
PassEnv GITWEB_CONFIG
425 426 427 428 429 430 431 432
AddHandler cgi-script .cgi
<Location /gitweb.cgi>
	Options +ExecCGI
</Location>
EOF
	fi
}

433 434 435 436 437 438 439
mongoose_conf() {
	cat > "$conf" <<EOF
# Mongoose web server configuration file.
# Lines starting with '#' and empty lines are ignored.
# For detailed description of every option, visit
# http://code.google.com/p/mongoose/wiki/MongooseManual

440
root		$root
441 442 443
ports		$port
index_files	gitweb.cgi
#ssl_cert	$fqgitdir/gitweb/ssl_cert.pem
444 445
error_log	$fqgitdir/gitweb/$httpd_only/error.log
access_log	$fqgitdir/gitweb/$httpd_only/access.log
446 447

#cgi setup
448
cgi_env		PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH,GITWEB_CONFIG=$GITWEB_CONFIG
449 450 451 452 453 454 455 456
cgi_interp	$PERL
cgi_ext		cgi,pl

# mimetype mapping
mime_types	.gz=application/x-gzip,.tar.gz=application/x-tgz,.tgz=application/x-tgz,.tar=application/x-tar,.zip=application/zip,.gif=image/gif,.jpg=image/jpeg,.jpeg=image/jpeg,.png=image/png,.css=text/css,.html=text/html,.htm=text/html,.js=text/javascript,.c=text/plain,.cpp=text/plain,.log=text/plain,.conf=text/plain,.text=text/plain,.txt=text/plain,.dtd=text/xml,.bz2=application/x-bzip,.tbz=application/x-bzip-compressed-tar,.tar.bz2=application/x-bzip-compressed-tar
EOF
}

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
plackup_conf () {
	# generate a standalone 'plackup' server script in $fqgitdir/gitweb
	# with embedded configuration; it does not use "$conf" file
	cat > "$fqgitdir/gitweb/gitweb.psgi" <<EOF
#!$PERL

# gitweb - simple web interface to track changes in git repositories
#          PSGI wrapper and server starter (see http://plackperl.org)

use strict;

use IO::Handle;
use Plack::MIME;
use Plack::Builder;
use Plack::App::WrapCGI;
use CGI::Emulate::PSGI 0.07; # minimum version required to work with gitweb

# mimetype mapping (from lighttpd_conf)
Plack::MIME->add_type(
	".pdf"          =>      "application/pdf",
	".sig"          =>      "application/pgp-signature",
	".spl"          =>      "application/futuresplash",
	".class"        =>      "application/octet-stream",
	".ps"           =>      "application/postscript",
	".torrent"      =>      "application/x-bittorrent",
	".dvi"          =>      "application/x-dvi",
	".gz"           =>      "application/x-gzip",
	".pac"          =>      "application/x-ns-proxy-autoconfig",
	".swf"          =>      "application/x-shockwave-flash",
	".tar.gz"       =>      "application/x-tgz",
	".tgz"          =>      "application/x-tgz",
	".tar"          =>      "application/x-tar",
	".zip"          =>      "application/zip",
	".mp3"          =>      "audio/mpeg",
	".m3u"          =>      "audio/x-mpegurl",
	".wma"          =>      "audio/x-ms-wma",
	".wax"          =>      "audio/x-ms-wax",
	".ogg"          =>      "application/ogg",
	".wav"          =>      "audio/x-wav",
	".gif"          =>      "image/gif",
	".jpg"          =>      "image/jpeg",
	".jpeg"         =>      "image/jpeg",
	".png"          =>      "image/png",
	".xbm"          =>      "image/x-xbitmap",
	".xpm"          =>      "image/x-xpixmap",
	".xwd"          =>      "image/x-xwindowdump",
	".css"          =>      "text/css",
	".html"         =>      "text/html",
	".htm"          =>      "text/html",
	".js"           =>      "text/javascript",
	".asc"          =>      "text/plain",
	".c"            =>      "text/plain",
	".cpp"          =>      "text/plain",
	".log"          =>      "text/plain",
	".conf"         =>      "text/plain",
	".text"         =>      "text/plain",
	".txt"          =>      "text/plain",
	".dtd"          =>      "text/xml",
	".xml"          =>      "text/xml",
	".mpeg"         =>      "video/mpeg",
	".mpg"          =>      "video/mpeg",
	".mov"          =>      "video/quicktime",
	".qt"           =>      "video/quicktime",
	".avi"          =>      "video/x-msvideo",
	".asf"          =>      "video/x-ms-asf",
	".asx"          =>      "video/x-ms-asf",
	".wmv"          =>      "video/x-ms-wmv",
	".bz2"          =>      "application/x-bzip",
	".tbz"          =>      "application/x-bzip-compressed-tar",
	".tar.bz2"      =>      "application/x-bzip-compressed-tar",
	""              =>      "text/plain"
);

my \$app = builder {
	# to be able to override \$SIG{__WARN__} to log build time warnings
	use CGI::Carp; # it sets \$SIG{__WARN__} itself

	my \$logdir = "$fqgitdir/gitweb/$httpd_only";
	open my \$access_log_fh, '>>', "\$logdir/access.log"
		or die "Couldn't open access log '\$logdir/access.log': \$!";
	open my \$error_log_fh,  '>>', "\$logdir/error.log"
		or die "Couldn't open error log '\$logdir/error.log': \$!";

	\$access_log_fh->autoflush(1);
	\$error_log_fh->autoflush(1);

	# redirect build time warnings to error.log
	\$SIG{'__WARN__'} = sub {
		my \$msg = shift;
		# timestamp warning like in CGI::Carp::warn
		my \$stamp = CGI::Carp::stamp();
		\$msg =~ s/^/\$stamp/gm;
		print \$error_log_fh \$msg;
	};

	# write errors to error.log, access to access.log
	enable 'AccessLog',
		format => "combined",
		logger => sub { print \$access_log_fh @_; };
	enable sub {
		my \$app = shift;
		sub {
			my \$env = shift;
			\$env->{'psgi.errors'} = \$error_log_fh;
			\$app->(\$env);
		}
	};
	# gitweb currently doesn't work with $SIG{CHLD} set to 'IGNORE',
	# because it uses 'close $fd or die...' on piped filehandle $fh
	# (which causes the parent process to wait for child to finish).
	enable_if { \$SIG{'CHLD'} eq 'IGNORE' } sub {
		my \$app = shift;
		sub {
			my \$env = shift;
			local \$SIG{'CHLD'} = 'DEFAULT';
			local \$SIG{'CLD'}  = 'DEFAULT';
			\$app->(\$env);
		}
	};
	# serve static files, i.e. stylesheet, images, script
	enable 'Static',
		path => sub { m!\.(js|css|png)\$! && s!^/gitweb/!! },
		root => "$root/",
		encoding => 'utf-8'; # encoding for 'text/plain' files
	# convert CGI application to PSGI app
	Plack::App::WrapCGI->new(script => "$root/gitweb.cgi")->to_app;
};

# make it runnable as standalone app,
# like it would be run via 'plackup' utility
587 588 589
if (caller) {
	return \$app;
} else {
590 591 592 593
	require Plack::Runner;

	my \$runner = Plack::Runner->new();
	\$runner->parse_options(qw(--env deployment --port $port),
594
				"$local" ? qw(--host 127.0.0.1) : ());
595 596 597 598 599 600 601 602 603 604
	\$runner->run(\$app);
}
__END__
EOF

	chmod a+x "$fqgitdir/gitweb/gitweb.psgi"
	# configuration is embedded in server script file, gitweb.psgi
	rm -f "$conf"
}

605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
python_conf() {
	# Python's builtin http.server and its CGI support is very limited.
	# CGI handler is capable of running CGI script only from inside a directory.
	# Trying to set cgi_directories=["/"] will add double slash to SCRIPT_NAME
	# and that in turn breaks gitweb's relative link generation.

	# create a simple web root where $fqgitdir/gitweb/$httpd_only is our root
	mkdir -p "$fqgitdir/gitweb/$httpd_only/cgi-bin"
	# Python http.server follows the symlinks
	ln -sf "$root/gitweb.cgi" "$fqgitdir/gitweb/$httpd_only/cgi-bin/gitweb.cgi"
	ln -sf "$root/static" "$fqgitdir/gitweb/$httpd_only/"

	# generate a standalone 'python http.server' script in $fqgitdir/gitweb
	# This asumes that python is in user's $PATH
	# This script is Python 2 and 3 compatible
	cat > "$fqgitdir/gitweb/gitweb.py" <<EOF
#!/usr/bin/env python
import os
import sys

# Open log file in line buffering mode
accesslogfile = open("$fqgitdir/gitweb/access.log", 'a', buffering=1)
errorlogfile = open("$fqgitdir/gitweb/error.log", 'a', buffering=1)

# and replace our stdout and stderr with log files
# also do a lowlevel duplicate of the logfile file descriptors so that
# our CGI child process writes any stderr warning also to the log file
_orig_stdout_fd = sys.stdout.fileno()
sys.stdout.close()
os.dup2(accesslogfile.fileno(), _orig_stdout_fd)
sys.stdout = accesslogfile

_orig_stderr_fd = sys.stderr.fileno()
sys.stderr.close()
os.dup2(errorlogfile.fileno(), _orig_stderr_fd)
sys.stderr = errorlogfile

from functools import partial

if sys.version_info < (3, 0):  # Python 2
	from CGIHTTPServer import CGIHTTPRequestHandler
	from BaseHTTPServer import HTTPServer as ServerClass
else:  # Python 3
	from http.server import CGIHTTPRequestHandler
	from http.server import HTTPServer as ServerClass


# Those environment variables will be passed to the cgi script
os.environ.update({
	"GIT_EXEC_PATH": "$GIT_EXEC_PATH",
	"GIT_DIR": "$GIT_DIR",
	"GITWEB_CONFIG": "$GITWEB_CONFIG"
})


class GitWebRequestHandler(CGIHTTPRequestHandler):

	def log_message(self, format, *args):
		# Write access logs to stdout
		sys.stdout.write("%s - - [%s] %s\n" %
				(self.address_string(),
				self.log_date_time_string(),
				format%args))

	def do_HEAD(self):
		self.redirect_path()
		CGIHTTPRequestHandler.do_HEAD(self)

	def do_GET(self):
		if self.path == "/":
			self.send_response(303, "See Other")
			self.send_header("Location", "/cgi-bin/gitweb.cgi")
			self.end_headers()
			return
		self.redirect_path()
		CGIHTTPRequestHandler.do_GET(self)

	def do_POST(self):
		self.redirect_path()
		CGIHTTPRequestHandler.do_POST(self)

	# rewrite path of every request that is not gitweb.cgi to out of cgi-bin
	def redirect_path(self):
		if not self.path.startswith("/cgi-bin/gitweb.cgi"):
			self.path = self.path.replace("/cgi-bin/", "/")

	# gitweb.cgi is the only thing that is ever going to be run here.
	# Ignore everything else
	def is_cgi(self):
		result = False
		if self.path.startswith('/cgi-bin/gitweb.cgi'):
			result = CGIHTTPRequestHandler.is_cgi(self)
		return result


bind = "127.0.0.1"
if "$local" == "true":
	bind = "0.0.0.0"

# Set our http root directory
# This is a work around for a missing directory argument in older Python versions
# as this was added to SimpleHTTPRequestHandler in Python 3.7
os.chdir("$fqgitdir/gitweb/$httpd_only/")

GitWebRequestHandler.protocol_version = "HTTP/1.0"
httpd = ServerClass((bind, $port), GitWebRequestHandler)

sa = httpd.socket.getsockname()
print("Serving HTTP on", sa[0], "port", sa[1], "...")
httpd.serve_forever()
EOF

	chmod a+x "$fqgitdir/gitweb/gitweb.py"
}

720 721
gitweb_conf() {
	cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
722
#!@@PERL@@
723 724 725
our \$projectroot = "$(dirname "$fqgitdir")";
our \$git_temp = "$fqgitdir/gitweb/tmp";
our \$projects_list = \$projectroot;
726 727

\$feature{'remote_heads'}{'default'} = [1];
728
EOF
729 730
}

731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747
configure_httpd() {
	case "$httpd" in
	*lighttpd*)
		lighttpd_conf
		;;
	*apache2*|*httpd*)
		apache2_conf
		;;
	webrick)
		webrick_conf
		;;
	*mongoose*)
		mongoose_conf
		;;
	*plackup*)
		plackup_conf
		;;
748 749 750
	*python*)
		python_conf
		;;
751 752 753 754 755 756 757
	*)
		echo "Unknown httpd specified: $httpd"
		exit 1
		;;
	esac
}

758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773
case "$action" in
stop)
	stop_httpd
	exit 0
	;;
start)
	start_httpd
	exit 0
	;;
restart)
	stop_httpd
	start_httpd
	exit 0
	;;
esac

774
gitweb_conf
775

776 777
resolve_full_httpd
mkdir -p "$fqgitdir/gitweb/$httpd_only"
778
conf="$fqgitdir/gitweb/$httpd_only.conf"
779

780
configure_httpd
781 782

start_httpd
783
url=http://127.0.0.1:$port
784 785

if test -n "$browser"; then
786
	httpd_is_ready && git web--browse -b "$browser" $url || echo $url
787
else
788
	httpd_is_ready && git web--browse -c "instaweb.browser" $url || echo $url
789
fi