3 # Copyright (c) 2006 Eric Wong
10 git instaweb [options] (--start | --stop | --restart)
12 l,local only bind on 127.0.0.1
13 p,port= the port to bind to
14 d,httpd= the command to launch
15 b,browser= the browser to launch
16 m,module-path= the module path (only needed for apache2)
18 stop stop the web server
19 start start the web server
20 restart restart the web server
27 local="$(git config --bool --get instaweb.local)"
28 httpd
="$(git config --get instaweb.httpd)"
29 root
="$(git config --get instaweb.gitwebdir)"
30 port
=$
(git config
--get instaweb.port
)
31 module_path
="$(git config --get instaweb.modulepath)"
34 conf
="$GIT_DIR/gitweb/httpd.conf"
38 # if installed, it doesn't need further configuration (module_path)
39 test -z "$httpd" && httpd
='lighttpd -f'
41 # Default is @@GITWEBDIR@@
42 test -z "$root" && root
='@@GITWEBDIR@@'
44 # any untaken local port will do...
45 test -z "$port" && port
=1234
47 resolve_full_httpd
() {
49 *apache2
*|
*lighttpd
*|
*httpd
*)
50 # 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" |
grep -- '-f *$' >/dev
/null
2>&1
58 # server is started by running via generated gitweb.psgi in $fqgitdir/gitweb
59 full_httpd
="$fqgitdir/gitweb/gitweb.psgi"
60 httpd_only
="${httpd%% *}" # cut on first space
64 # server is started by running via generated webrick.rb in
66 full_httpd
="$fqgitdir/gitweb/webrick.rb"
67 httpd_only
="${httpd%% *}" # cut on first space
71 # server is started by running via generated gitweb.py in
73 full_httpd
="$fqgitdir/gitweb/gitweb.py"
74 httpd_only
="${httpd%% *}" # cut on first space
79 httpd_only
="$(echo $httpd | cut -f1 -d' ')"
80 if case "$httpd_only" in /*) : ;; *) which $httpd_only >/dev
/null
2>&1;; esac
84 # many httpds are installed in /usr/sbin or /usr/local/sbin
85 # these days and those are not in most users $PATHs
86 # in addition, we may have generated a server script
87 # in $fqgitdir/gitweb.
88 for i
in /usr
/local
/sbin
/usr
/sbin
"$root" "$fqgitdir/gitweb"
90 if test -x "$i/$httpd_only"
97 echo >&2 "$httpd_only not found. Install $httpd_only or use" \
98 "--httpd to specify another httpd daemon."
104 if test -f "$fqgitdir/pid"; then
105 echo "Instance already running. Restarting..."
109 # here $httpd should have a meaningful value
111 mkdir
-p "$fqgitdir/gitweb/$httpd_only"
112 conf
="$fqgitdir/gitweb/$httpd_only.conf"
114 # generate correct config file if it doesn't exist
115 test -f "$conf" || configure_httpd
116 test -f "$fqgitdir/gitweb/gitweb_config.perl" || gitweb_conf
118 # don't quote $full_httpd, there can be arguments to it (-f)
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 #Save the pid before doing anything else (we'll print it later)
126 if test $?
!= 0; then
127 echo "Could not execute http daemon $httpd."
131 cat > "$fqgitdir/pid" <<EOF
137 if test $?
!= 0; then
138 echo "Could not execute http daemon $httpd."
146 test -f "$fqgitdir/pid" && kill $
(cat "$fqgitdir/pid")
147 rm -f "$fqgitdir/pid"
151 "$PERL" -MIO::Socket
::INET
-e "
152 local \$| = 1; # turn on autoflush
153 exit if (IO::Socket::INET->new('127.0.0.1:$port'));
154 print 'Waiting for \'$httpd\' to start ..';
158 } until (IO::Socket::INET->new('127.0.0.1:$port'));
203 mkdir
-p "$GIT_DIR/gitweb/tmp"
204 GIT_EXEC_PATH
="$(git --exec-path)"
206 GITWEB_CONFIG
="$fqgitdir/gitweb/gitweb_config.perl"
207 export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
210 # webrick seems to have no way of passing arbitrary environment
211 # variables to the underlying CGI executable, so we wrap the
212 # actual gitweb.cgi using a shell script to force it
213 wrapper
="$fqgitdir/gitweb/$httpd/wrapper.sh"
214 cat > "$wrapper" <<EOF
216 # we use this shell script wrapper around the real gitweb.cgi since
217 # there appears to be no other way to pass arbitrary environment variables
218 # into the CGI process
219 GIT_EXEC_PATH=$GIT_EXEC_PATH GIT_DIR=$GIT_DIR GITWEB_CONFIG=$GITWEB_CONFIG
220 export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
221 exec $root/gitweb.cgi
225 # This assumes _ruby_ is in the user's $PATH. that's _one_
226 # portable way to run ruby, which could be installed anywhere, really.
227 # generate a standalone server script in $fqgitdir/gitweb.
228 cat >"$fqgitdir/gitweb/$httpd.rb" <<EOF
234 :DocumentRoot => "$root",
235 :Logger => Logger.new('$fqgitdir/gitweb/error.log'),
237 [ Logger.new('$fqgitdir/gitweb/access.log'),
238 WEBrick::AccessLog::COMBINED_LOG_FORMAT ]
240 :DirectoryIndex => ["gitweb.cgi"],
241 :CGIInterpreter => "$wrapper",
242 :StartCallback => lambda do
243 File.open("$fqgitdir/pid", "w") { |f| f.puts Process.pid }
245 :ServerType => WEBrick::Daemon,
247 options[:BindAddress] = '127.0.0.1' if "$local" == "true"
248 server = WEBrick::HTTPServer.new(options)
249 ['INT', 'TERM'].each do |signal|
250 trap(signal) {server.shutdown}
254 chmod +x
"$fqgitdir/gitweb/$httpd.rb"
255 # configuration is embedded in server script file, webrick.rb
261 server.document-root = "$root"
263 server.modules = ( "mod_setenv", "mod_cgi" )
264 server.indexfiles = ( "gitweb.cgi" )
265 server.pid-file = "$fqgitdir/pid"
266 server.errorlog = "$fqgitdir/gitweb/$httpd_only/error.log"
268 # to enable, add "mod_access", "mod_accesslog" to server.modules
269 # variable above and uncomment this
270 #accesslog.filename = "$fqgitdir/gitweb/$httpd_only/access.log"
272 setenv.add-environment = ( "PATH" => env.PATH, "GITWEB_CONFIG" => env.GITWEB_CONFIG )
274 cgi.assign = ( ".cgi" => "" )
278 ".pdf" => "application/pdf",
279 ".sig" => "application/pgp-signature",
280 ".spl" => "application/futuresplash",
281 ".class" => "application/octet-stream",
282 ".ps" => "application/postscript",
283 ".torrent" => "application/x-bittorrent",
284 ".dvi" => "application/x-dvi",
285 ".gz" => "application/x-gzip",
286 ".pac" => "application/x-ns-proxy-autoconfig",
287 ".swf" => "application/x-shockwave-flash",
288 ".tar.gz" => "application/x-tgz",
289 ".tgz" => "application/x-tgz",
290 ".tar" => "application/x-tar",
291 ".zip" => "application/zip",
292 ".mp3" => "audio/mpeg",
293 ".m3u" => "audio/x-mpegurl",
294 ".wma" => "audio/x-ms-wma",
295 ".wax" => "audio/x-ms-wax",
296 ".ogg" => "application/ogg",
297 ".wav" => "audio/x-wav",
298 ".gif" => "image/gif",
299 ".jpg" => "image/jpeg",
300 ".jpeg" => "image/jpeg",
301 ".png" => "image/png",
302 ".xbm" => "image/x-xbitmap",
303 ".xpm" => "image/x-xpixmap",
304 ".xwd" => "image/x-xwindowdump",
305 ".css" => "text/css",
306 ".html" => "text/html",
307 ".htm" => "text/html",
308 ".js" => "text/javascript",
309 ".asc" => "text/plain",
310 ".c" => "text/plain",
311 ".cpp" => "text/plain",
312 ".log" => "text/plain",
313 ".conf" => "text/plain",
314 ".text" => "text/plain",
315 ".txt" => "text/plain",
316 ".dtd" => "text/xml",
317 ".xml" => "text/xml",
318 ".mpeg" => "video/mpeg",
319 ".mpg" => "video/mpeg",
320 ".mov" => "video/quicktime",
321 ".qt" => "video/quicktime",
322 ".avi" => "video/x-msvideo",
323 ".asf" => "video/x-ms-asf",
324 ".asx" => "video/x-ms-asf",
325 ".wmv" => "video/x-ms-wmv",
326 ".bz2" => "application/x-bzip",
327 ".tbz" => "application/x-bzip-compressed-tar",
328 ".tar.bz2" => "application/x-bzip-compressed-tar",
332 test x
"$local" = xtrue
&& echo 'server.bind = "127.0.0.1"' >> "$conf"
341 if test -d "$candidate/modules"
343 module_path
="$candidate/modules"
348 test x
"$local" = xtrue
&& bind='127.0.0.1:'
349 echo 'text/css css' > "$fqgitdir/mime.types"
351 ServerName "git-instaweb"
354 ErrorLog "$fqgitdir/gitweb/$httpd_only/error.log"
355 CustomLog "$fqgitdir/gitweb/$httpd_only/access.log" combined
356 PidFile "$fqgitdir/pid"
360 for mod
in mpm_event mpm_prefork mpm_worker
362 if test -e $module_path/mod_
${mod}.so
364 echo "LoadModule ${mod}_module " \
365 "$module_path/mod_${mod}.so" >> "$conf"
366 # only one mpm module permitted
370 for mod
in mime dir env log_config authz_core unixd
372 if test -e $module_path/mod_
${mod}.so
374 echo "LoadModule ${mod}_module " \
375 "$module_path/mod_${mod}.so" >> "$conf"
379 TypesConfig "$fqgitdir/mime.types"
380 DirectoryIndex gitweb.cgi
383 if test -f "$module_path/mod_perl.so"
385 # favor mod_perl if available
387 LoadModule perl_module $module_path/mod_perl.so
389 PerlPassEnv GIT_EXEC_PATH
390 PerlPassEnv GITWEB_CONFIG
391 <Location /gitweb.cgi>
392 SetHandler perl-script
393 PerlResponseHandler ModPerl::Registry
394 PerlOptions +ParseHeaders
401 list_mods
=$
(echo "$full_httpd" |
sed 's/-f$/-l/')
402 $list_mods |
grep 'mod_cgi\.c' >/dev
/null
2>&1 || \
403 if test -f "$module_path/mod_cgi.so"
405 echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
407 $list_mods |
grep 'mod_cgid\.c' >/dev
/null
2>&1 || \
408 if test -f "$module_path/mod_cgid.so"
410 echo "LoadModule cgid_module $module_path/mod_cgid.so" \
413 echo "You have no CGI support!"
416 echo "ScriptSock logs/gitweb.sock" >> "$conf"
420 PassEnv GIT_EXEC_PATH
421 PassEnv GITWEB_CONFIG
422 AddHandler cgi-script .cgi
423 <Location /gitweb.cgi>
432 # Mongoose web server configuration file.
433 # Lines starting with '#' and empty lines are ignored.
434 # For detailed description of every option, visit
435 # https://code.google.com/p/mongoose/wiki/MongooseManual
439 index_files gitweb.cgi
440 #ssl_cert $fqgitdir/gitweb/ssl_cert.pem
441 error_log $fqgitdir/gitweb/$httpd_only/error.log
442 access_log $fqgitdir/gitweb/$httpd_only/access.log
445 cgi_env PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH,GITWEB_CONFIG=$GITWEB_CONFIG
450 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
455 # generate a standalone 'plackup' server script in $fqgitdir/gitweb
456 # with embedded configuration; it does not use "$conf" file
457 cat > "$fqgitdir/gitweb/gitweb.psgi" <<EOF
460 # gitweb - simple web interface to track changes in git repositories
461 # PSGI wrapper and server starter (see https://plackperl.org)
468 use Plack::App::WrapCGI;
469 use CGI::Emulate::PSGI 0.07; # minimum version required to work with gitweb
471 # mimetype mapping (from lighttpd_conf)
472 Plack::MIME->add_type(
473 ".pdf" => "application/pdf",
474 ".sig" => "application/pgp-signature",
475 ".spl" => "application/futuresplash",
476 ".class" => "application/octet-stream",
477 ".ps" => "application/postscript",
478 ".torrent" => "application/x-bittorrent",
479 ".dvi" => "application/x-dvi",
480 ".gz" => "application/x-gzip",
481 ".pac" => "application/x-ns-proxy-autoconfig",
482 ".swf" => "application/x-shockwave-flash",
483 ".tar.gz" => "application/x-tgz",
484 ".tgz" => "application/x-tgz",
485 ".tar" => "application/x-tar",
486 ".zip" => "application/zip",
487 ".mp3" => "audio/mpeg",
488 ".m3u" => "audio/x-mpegurl",
489 ".wma" => "audio/x-ms-wma",
490 ".wax" => "audio/x-ms-wax",
491 ".ogg" => "application/ogg",
492 ".wav" => "audio/x-wav",
493 ".gif" => "image/gif",
494 ".jpg" => "image/jpeg",
495 ".jpeg" => "image/jpeg",
496 ".png" => "image/png",
497 ".xbm" => "image/x-xbitmap",
498 ".xpm" => "image/x-xpixmap",
499 ".xwd" => "image/x-xwindowdump",
500 ".css" => "text/css",
501 ".html" => "text/html",
502 ".htm" => "text/html",
503 ".js" => "text/javascript",
504 ".asc" => "text/plain",
505 ".c" => "text/plain",
506 ".cpp" => "text/plain",
507 ".log" => "text/plain",
508 ".conf" => "text/plain",
509 ".text" => "text/plain",
510 ".txt" => "text/plain",
511 ".dtd" => "text/xml",
512 ".xml" => "text/xml",
513 ".mpeg" => "video/mpeg",
514 ".mpg" => "video/mpeg",
515 ".mov" => "video/quicktime",
516 ".qt" => "video/quicktime",
517 ".avi" => "video/x-msvideo",
518 ".asf" => "video/x-ms-asf",
519 ".asx" => "video/x-ms-asf",
520 ".wmv" => "video/x-ms-wmv",
521 ".bz2" => "application/x-bzip",
522 ".tbz" => "application/x-bzip-compressed-tar",
523 ".tar.bz2" => "application/x-bzip-compressed-tar",
528 # to be able to override \$SIG{__WARN__} to log build time warnings
529 use CGI::Carp; # it sets \$SIG{__WARN__} itself
531 my \$logdir = "$fqgitdir/gitweb/$httpd_only";
532 open my \$access_log_fh, '>>', "\$logdir/access.log"
533 or die "Couldn't open access log '\$logdir/access.log': \$!";
534 open my \$error_log_fh, '>>', "\$logdir/error.log"
535 or die "Couldn't open error log '\$logdir/error.log': \$!";
537 \$access_log_fh->autoflush(1);
538 \$error_log_fh->autoflush(1);
540 # redirect build time warnings to error.log
541 \$SIG{'__WARN__'} = sub {
543 # timestamp warning like in CGI::Carp::warn
544 my \$stamp = CGI::Carp::stamp();
545 \$msg =~ s/^/\$stamp/gm;
546 print \$error_log_fh \$msg;
549 # write errors to error.log, access to access.log
551 format => "combined",
552 logger => sub { print \$access_log_fh @_; };
557 \$env->{'psgi.errors'} = \$error_log_fh;
561 # gitweb currently doesn't work with $SIG{CHLD} set to 'IGNORE',
562 # because it uses 'close $fd or die...' on piped filehandle $fh
563 # (which causes the parent process to wait for child to finish).
564 enable_if { \$SIG{'CHLD'} eq 'IGNORE' } sub {
568 local \$SIG{'CHLD'} = 'DEFAULT';
569 local \$SIG{'CLD'} = 'DEFAULT';
573 # serve static files, i.e. stylesheet, images, script
575 path => sub { m!\.(js|css|png)\$! && s!^/gitweb/!! },
577 encoding => 'utf-8'; # encoding for 'text/plain' files
578 # convert CGI application to PSGI app
579 Plack::App::WrapCGI->new(script => "$root/gitweb.cgi")->to_app;
582 # make it runnable as standalone app,
583 # like it would be run via 'plackup' utility
587 require Plack::Runner;
589 my \$runner = Plack::Runner->new();
590 \$runner->parse_options(qw(--env deployment --port $port),
591 "$local" ? qw(--host 127.0.0.1) : ());
592 \$runner->run(\$app);
597 chmod a
+x
"$fqgitdir/gitweb/gitweb.psgi"
598 # configuration is embedded in server script file, gitweb.psgi
603 # Python's builtin http.server and its CGI support is very limited.
604 # CGI handler is capable of running CGI script only from inside a directory.
605 # Trying to set cgi_directories=["/"] will add double slash to SCRIPT_NAME
606 # and that in turn breaks gitweb's relative link generation.
608 # create a simple web root where $fqgitdir/gitweb/$httpd_only is our root
609 mkdir
-p "$fqgitdir/gitweb/$httpd_only/cgi-bin"
610 # Python http.server follows the symlinks
611 ln -sf "$root/gitweb.cgi" "$fqgitdir/gitweb/$httpd_only/cgi-bin/gitweb.cgi"
612 ln -sf "$root/static" "$fqgitdir/gitweb/$httpd_only/"
614 # generate a standalone 'python http.server' script in $fqgitdir/gitweb
615 # This assumes that python is in user's $PATH
616 # This script is Python 2 and 3 compatible
617 cat > "$fqgitdir/gitweb/gitweb.py" <<EOF
618 #!/usr/bin/env python
622 # Open log file in line buffering mode
623 accesslogfile = open("$fqgitdir/gitweb/access.log", 'a', buffering=1)
624 errorlogfile = open("$fqgitdir/gitweb/error.log", 'a', buffering=1)
626 # and replace our stdout and stderr with log files
627 # also do a lowlevel duplicate of the logfile file descriptors so that
628 # our CGI child process writes any stderr warning also to the log file
629 _orig_stdout_fd = sys.stdout.fileno()
631 os.dup2(accesslogfile.fileno(), _orig_stdout_fd)
632 sys.stdout = accesslogfile
634 _orig_stderr_fd = sys.stderr.fileno()
636 os.dup2(errorlogfile.fileno(), _orig_stderr_fd)
637 sys.stderr = errorlogfile
639 from functools import partial
641 if sys.version_info < (3, 0): # Python 2
642 from CGIHTTPServer import CGIHTTPRequestHandler
643 from BaseHTTPServer import HTTPServer as ServerClass
645 from http.server import CGIHTTPRequestHandler
646 from http.server import HTTPServer as ServerClass
649 # Those environment variables will be passed to the cgi script
651 "GIT_EXEC_PATH": "$GIT_EXEC_PATH",
652 "GIT_DIR": "$GIT_DIR",
653 "GITWEB_CONFIG": "$GITWEB_CONFIG"
657 class GitWebRequestHandler(CGIHTTPRequestHandler):
659 def log_message(self, format, *args):
660 # Write access logs to stdout
661 sys.stdout.write("%s - - [%s] %s\n" %
662 (self.address_string(),
663 self.log_date_time_string(),
668 CGIHTTPRequestHandler.do_HEAD(self)
672 self.send_response(303, "See Other")
673 self.send_header("Location", "/cgi-bin/gitweb.cgi")
677 CGIHTTPRequestHandler.do_GET(self)
681 CGIHTTPRequestHandler.do_POST(self)
683 # rewrite path of every request that is not gitweb.cgi to out of cgi-bin
684 def redirect_path(self):
685 if not self.path.startswith("/cgi-bin/gitweb.cgi"):
686 self.path = self.path.replace("/cgi-bin/", "/")
688 # gitweb.cgi is the only thing that is ever going to be run here.
689 # Ignore everything else
692 if self.path.startswith('/cgi-bin/gitweb.cgi'):
693 result = CGIHTTPRequestHandler.is_cgi(self)
698 if "$local" == "true":
701 # Set our http root directory
702 # This is a work around for a missing directory argument in older Python versions
703 # as this was added to SimpleHTTPRequestHandler in Python 3.7
704 os.chdir("$fqgitdir/gitweb/$httpd_only/")
706 GitWebRequestHandler.protocol_version = "HTTP/1.0"
707 httpd = ServerClass((bind, $port), GitWebRequestHandler)
709 sa = httpd.socket.getsockname()
710 print("Serving HTTP on", sa[0], "port", sa[1], "...")
711 httpd.serve_forever()
714 chmod a
+x
"$fqgitdir/gitweb/gitweb.py"
718 cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
720 our \$projectroot = "$(dirname "$fqgitdir")";
721 our \$git_temp = "$fqgitdir/gitweb/tmp";
722 our \$projects_list = \$projectroot;
724 \$feature{'remote_heads'}{'default'} = [1];
749 echo "Unknown httpd specified: $httpd"
774 mkdir
-p "$fqgitdir/gitweb/$httpd_only"
775 conf
="$fqgitdir/gitweb/$httpd_only.conf"
780 url
=http
://127.0.0.1:$port
782 if test -n "$browser"; then
783 httpd_is_ready
&& git web--browse
-b "$browser" $url ||
echo $url
785 httpd_is_ready
&& git web--browse
-c "instaweb.browser" $url ||
echo $url