git-instaweb.shon commit rebase: fix a memory leak (7372eae)
   1#!/bin/sh
   2#
   3# Copyright (c) 2006 Eric Wong
   4#
   5
   6PERL='@@PERL@@'
   7OPTIONS_KEEPDASHDASH=
   8OPTIONS_STUCKLONG=
   9OPTIONS_SPEC="\
  10git instaweb [options] (--start | --stop | --restart)
  11--
  12l,local        only bind on 127.0.0.1
  13p,port=        the port to bind to
  14d,httpd=       the command to launch
  15b,browser=     the browser to launch
  16m,module-path= the module path (only needed for apache2)
  17 Action
  18stop           stop the web server
  19start          start the web server
  20restart        restart the web server
  21"
  22
  23SUBDIRECTORY_OK=Yes
  24. git-sh-setup
  25
  26fqgitdir="$GIT_DIR"
  27local="$(git config --bool --get instaweb.local)"
  28httpd="$(git config --get instaweb.httpd)"
  29root="$(git config --get instaweb.gitwebdir)"
  30port=$(git config --get instaweb.port)
  31module_path="$(git config --get instaweb.modulepath)"
  32action="browse"
  33
  34conf="$GIT_DIR/gitweb/httpd.conf"
  35
  36# Defaults:
  37
  38# if installed, it doesn't need further configuration (module_path)
  39test -z "$httpd" && httpd='lighttpd -f'
  40
  41# Default is @@GITWEBDIR@@
  42test -z "$root" && root='@@GITWEBDIR@@'
  43
  44# any untaken local port will do...
  45test -z "$port" && port=1234
  46
  47resolve_full_httpd () {
  48        case "$httpd" in
  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" | sane_grep -- '-f *$' >/dev/null 2>&1
  53                then
  54                        httpd="$httpd -f"
  55                fi
  56                ;;
  57        *plackup*)
  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
  61                return
  62                ;;
  63        *webrick*)
  64                # server is started by running via generated webrick.rb in
  65                # $fqgitdir/gitweb
  66                full_httpd="$fqgitdir/gitweb/webrick.rb"
  67                httpd_only="${httpd%% *}" # cut on first space
  68                return
  69                ;;
  70        *python*)
  71                # server is started by running via generated gitweb.py in
  72                # $fqgitdir/gitweb
  73                full_httpd="$fqgitdir/gitweb/gitweb.py"
  74                httpd_only="${httpd%% *}" # cut on first space
  75                return
  76                ;;
  77        esac
  78
  79        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        else
  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"
  89                do
  90                        if test -x "$i/$httpd_only"
  91                        then
  92                                full_httpd=$i/$httpd
  93                                return
  94                        fi
  95                done
  96
  97                echo >&2 "$httpd_only not found. Install $httpd_only or use" \
  98                     "--httpd to specify another httpd daemon."
  99                exit 1
 100        fi
 101}
 102
 103start_httpd () {
 104        if test -f "$fqgitdir/pid"; then
 105                say "Instance already running. Restarting..."
 106                stop_httpd
 107        fi
 108
 109        # here $httpd should have a meaningful value
 110        resolve_full_httpd
 111        mkdir -p "$fqgitdir/gitweb/$httpd_only"
 112        conf="$fqgitdir/gitweb/$httpd_only.conf"
 113
 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
 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                #Save the pid before doing anything else (we'll print it later)
 124                pid=$!
 125
 126                if test $? != 0; then
 127                        echo "Could not execute http daemon $httpd."
 128                        exit 1
 129                fi
 130
 131                cat > "$fqgitdir/pid" <<EOF
 132$pid
 133EOF
 134                ;;
 135        *)
 136                $full_httpd "$conf"
 137                if test $? != 0; then
 138                        echo "Could not execute http daemon $httpd."
 139                        exit 1
 140                fi
 141                ;;
 142        esac
 143}
 144
 145stop_httpd () {
 146        test -f "$fqgitdir/pid" && kill $(cat "$fqgitdir/pid")
 147        rm -f "$fqgitdir/pid"
 148}
 149
 150httpd_is_ready () {
 151        "$PERL" -MIO::Socket::INET -e "
 152local \$| = 1; # turn on autoflush
 153exit if (IO::Socket::INET->new('127.0.0.1:$port'));
 154print 'Waiting for \'$httpd\' to start ..';
 155do {
 156        print '.';
 157        sleep(1);
 158} until (IO::Socket::INET->new('127.0.0.1:$port'));
 159print qq! (done)\n!;
 160"
 161}
 162
 163while test $# != 0
 164do
 165        case "$1" in
 166        --stop|stop)
 167                action="stop"
 168                ;;
 169        --start|start)
 170                action="start"
 171                ;;
 172        --restart|restart)
 173                action="restart"
 174                ;;
 175        -l|--local)
 176                local=true
 177                ;;
 178        -d|--httpd)
 179                shift
 180                httpd="$1"
 181                ;;
 182        -b|--browser)
 183                shift
 184                browser="$1"
 185                ;;
 186        -p|--port)
 187                shift
 188                port="$1"
 189                ;;
 190        -m|--module-path)
 191                shift
 192                module_path="$1"
 193                ;;
 194        --)
 195                ;;
 196        *)
 197                usage
 198                ;;
 199        esac
 200        shift
 201done
 202
 203mkdir -p "$GIT_DIR/gitweb/tmp"
 204GIT_EXEC_PATH="$(git --exec-path)"
 205GIT_DIR="$fqgitdir"
 206GITWEB_CONFIG="$fqgitdir/gitweb/gitweb_config.perl"
 207export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
 208
 209webrick_conf () {
 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
 215#!@SHELL_PATH@
 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
 219GIT_EXEC_PATH=$GIT_EXEC_PATH GIT_DIR=$GIT_DIR GITWEB_CONFIG=$GITWEB_CONFIG
 220export GIT_EXEC_PATH GIT_DIR GITWEB_CONFIG
 221exec $root/gitweb.cgi
 222EOF
 223        chmod +x "$wrapper"
 224
 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
 229#!/usr/bin/env ruby
 230require 'webrick'
 231require 'logger'
 232options = {
 233  :Port => $port,
 234  :DocumentRoot => "$root",
 235  :Logger => Logger.new('$fqgitdir/gitweb/error.log'),
 236  :AccessLog => [
 237    [ Logger.new('$fqgitdir/gitweb/access.log'),
 238      WEBrick::AccessLog::COMBINED_LOG_FORMAT ]
 239  ],
 240  :DirectoryIndex => ["gitweb.cgi"],
 241  :CGIInterpreter => "$wrapper",
 242  :StartCallback => lambda do
 243    File.open("$fqgitdir/pid", "w") { |f| f.puts Process.pid }
 244  end,
 245  :ServerType => WEBrick::Daemon,
 246}
 247options[:BindAddress] = '127.0.0.1' if "$local" == "true"
 248server = WEBrick::HTTPServer.new(options)
 249['INT', 'TERM'].each do |signal|
 250  trap(signal) {server.shutdown}
 251end
 252server.start
 253EOF
 254        chmod +x "$fqgitdir/gitweb/$httpd.rb"
 255        # configuration is embedded in server script file, webrick.rb
 256        rm -f "$conf"
 257}
 258
 259lighttpd_conf () {
 260        cat > "$conf" <<EOF
 261server.document-root = "$root"
 262server.port = $port
 263server.modules = ( "mod_setenv", "mod_cgi" )
 264server.indexfiles = ( "gitweb.cgi" )
 265server.pid-file = "$fqgitdir/pid"
 266server.errorlog = "$fqgitdir/gitweb/$httpd_only/error.log"
 267
 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"
 271
 272setenv.add-environment = ( "PATH" => env.PATH, "GITWEB_CONFIG" => env.GITWEB_CONFIG )
 273
 274cgi.assign = ( ".cgi" => "" )
 275
 276# mimetype mapping
 277mimetype.assign             = (
 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",
 329  ""              =>      "text/plain"
 330 )
 331EOF
 332        test x"$local" = xtrue && echo 'server.bind = "127.0.0.1"' >> "$conf"
 333}
 334
 335apache2_conf () {
 336        for candidate in \
 337                /etc/httpd \
 338                /usr/lib/apache2 \
 339                /usr/lib/httpd ;
 340        do
 341                if test -d "$candidate/modules"
 342                then
 343                        module_path="$candidate/modules"
 344                        break
 345                fi
 346        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
 351ServerName "git-instaweb"
 352ServerRoot "$root"
 353DocumentRoot "$root"
 354ErrorLog "$fqgitdir/gitweb/$httpd_only/error.log"
 355CustomLog "$fqgitdir/gitweb/$httpd_only/access.log" combined
 356PidFile "$fqgitdir/pid"
 357Listen $bind$port
 358EOF
 359
 360        for mod in mpm_event mpm_prefork mpm_worker
 361        do
 362                if test -e $module_path/mod_${mod}.so
 363                then
 364                        echo "LoadModule ${mod}_module " \
 365                             "$module_path/mod_${mod}.so" >> "$conf"
 366                        # only one mpm module permitted
 367                        break
 368                fi
 369        done
 370        for mod in mime dir env log_config authz_core unixd
 371        do
 372                if test -e $module_path/mod_${mod}.so
 373                then
 374                        echo "LoadModule ${mod}_module " \
 375                             "$module_path/mod_${mod}.so" >> "$conf"
 376                fi
 377        done
 378        cat >> "$conf" <<EOF
 379TypesConfig "$fqgitdir/mime.types"
 380DirectoryIndex gitweb.cgi
 381EOF
 382
 383        # check to see if Dennis Stosberg's mod_perl compatibility patch
 384        # (<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        then
 388                # favor mod_perl if available
 389                cat >> "$conf" <<EOF
 390LoadModule perl_module $module_path/mod_perl.so
 391PerlPassEnv GIT_DIR
 392PerlPassEnv GIT_EXEC_PATH
 393PerlPassEnv GITWEB_CONFIG
 394<Location /gitweb.cgi>
 395        SetHandler perl-script
 396        PerlResponseHandler ModPerl::Registry
 397        PerlOptions +ParseHeaders
 398        Options +ExecCGI
 399</Location>
 400EOF
 401        else
 402                # 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                if test -f "$module_path/mod_cgi.so"
 407                then
 408                        echo "LoadModule cgi_module $module_path/mod_cgi.so" >> "$conf"
 409                else
 410                        $list_mods | grep 'mod_cgid\.c' >/dev/null 2>&1 || \
 411                        if test -f "$module_path/mod_cgid.so"
 412                        then
 413                                echo "LoadModule cgid_module $module_path/mod_cgid.so" \
 414                                        >> "$conf"
 415                        else
 416                                echo "You have no CGI support!"
 417                                exit 2
 418                        fi
 419                        echo "ScriptSock logs/gitweb.sock" >> "$conf"
 420                fi
 421                cat >> "$conf" <<EOF
 422PassEnv GIT_DIR
 423PassEnv GIT_EXEC_PATH
 424PassEnv GITWEB_CONFIG
 425AddHandler cgi-script .cgi
 426<Location /gitweb.cgi>
 427        Options +ExecCGI
 428</Location>
 429EOF
 430        fi
 431}
 432
 433mongoose_conf() {
 434        cat > "$conf" <<EOF
 435# Mongoose web server configuration file.
 436# Lines starting with '#' and empty lines are ignored.
 437# For detailed description of every option, visit
 438# http://code.google.com/p/mongoose/wiki/MongooseManual
 439
 440root            $root
 441ports           $port
 442index_files     gitweb.cgi
 443#ssl_cert       $fqgitdir/gitweb/ssl_cert.pem
 444error_log       $fqgitdir/gitweb/$httpd_only/error.log
 445access_log      $fqgitdir/gitweb/$httpd_only/access.log
 446
 447#cgi setup
 448cgi_env         PATH=$PATH,GIT_DIR=$GIT_DIR,GIT_EXEC_PATH=$GIT_EXEC_PATH,GITWEB_CONFIG=$GITWEB_CONFIG
 449cgi_interp      $PERL
 450cgi_ext         cgi,pl
 451
 452# mimetype mapping
 453mime_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
 454EOF
 455}
 456
 457plackup_conf () {
 458        # generate a standalone 'plackup' server script in $fqgitdir/gitweb
 459        # with embedded configuration; it does not use "$conf" file
 460        cat > "$fqgitdir/gitweb/gitweb.psgi" <<EOF
 461#!$PERL
 462
 463# gitweb - simple web interface to track changes in git repositories
 464#          PSGI wrapper and server starter (see http://plackperl.org)
 465
 466use strict;
 467
 468use IO::Handle;
 469use Plack::MIME;
 470use Plack::Builder;
 471use Plack::App::WrapCGI;
 472use CGI::Emulate::PSGI 0.07; # minimum version required to work with gitweb
 473
 474# mimetype mapping (from lighttpd_conf)
 475Plack::MIME->add_type(
 476        ".pdf"          =>      "application/pdf",
 477        ".sig"          =>      "application/pgp-signature",
 478        ".spl"          =>      "application/futuresplash",
 479        ".class"        =>      "application/octet-stream",
 480        ".ps"           =>      "application/postscript",
 481        ".torrent"      =>      "application/x-bittorrent",
 482        ".dvi"          =>      "application/x-dvi",
 483        ".gz"           =>      "application/x-gzip",
 484        ".pac"          =>      "application/x-ns-proxy-autoconfig",
 485        ".swf"          =>      "application/x-shockwave-flash",
 486        ".tar.gz"       =>      "application/x-tgz",
 487        ".tgz"          =>      "application/x-tgz",
 488        ".tar"          =>      "application/x-tar",
 489        ".zip"          =>      "application/zip",
 490        ".mp3"          =>      "audio/mpeg",
 491        ".m3u"          =>      "audio/x-mpegurl",
 492        ".wma"          =>      "audio/x-ms-wma",
 493        ".wax"          =>      "audio/x-ms-wax",
 494        ".ogg"          =>      "application/ogg",
 495        ".wav"          =>      "audio/x-wav",
 496        ".gif"          =>      "image/gif",
 497        ".jpg"          =>      "image/jpeg",
 498        ".jpeg"         =>      "image/jpeg",
 499        ".png"          =>      "image/png",
 500        ".xbm"          =>      "image/x-xbitmap",
 501        ".xpm"          =>      "image/x-xpixmap",
 502        ".xwd"          =>      "image/x-xwindowdump",
 503        ".css"          =>      "text/css",
 504        ".html"         =>      "text/html",
 505        ".htm"          =>      "text/html",
 506        ".js"           =>      "text/javascript",
 507        ".asc"          =>      "text/plain",
 508        ".c"            =>      "text/plain",
 509        ".cpp"          =>      "text/plain",
 510        ".log"          =>      "text/plain",
 511        ".conf"         =>      "text/plain",
 512        ".text"         =>      "text/plain",
 513        ".txt"          =>      "text/plain",
 514        ".dtd"          =>      "text/xml",
 515        ".xml"          =>      "text/xml",
 516        ".mpeg"         =>      "video/mpeg",
 517        ".mpg"          =>      "video/mpeg",
 518        ".mov"          =>      "video/quicktime",
 519        ".qt"           =>      "video/quicktime",
 520        ".avi"          =>      "video/x-msvideo",
 521        ".asf"          =>      "video/x-ms-asf",
 522        ".asx"          =>      "video/x-ms-asf",
 523        ".wmv"          =>      "video/x-ms-wmv",
 524        ".bz2"          =>      "application/x-bzip",
 525        ".tbz"          =>      "application/x-bzip-compressed-tar",
 526        ".tar.bz2"      =>      "application/x-bzip-compressed-tar",
 527        ""              =>      "text/plain"
 528);
 529
 530my \$app = builder {
 531        # to be able to override \$SIG{__WARN__} to log build time warnings
 532        use CGI::Carp; # it sets \$SIG{__WARN__} itself
 533
 534        my \$logdir = "$fqgitdir/gitweb/$httpd_only";
 535        open my \$access_log_fh, '>>', "\$logdir/access.log"
 536                or die "Couldn't open access log '\$logdir/access.log': \$!";
 537        open my \$error_log_fh,  '>>', "\$logdir/error.log"
 538                or die "Couldn't open error log '\$logdir/error.log': \$!";
 539
 540        \$access_log_fh->autoflush(1);
 541        \$error_log_fh->autoflush(1);
 542
 543        # redirect build time warnings to error.log
 544        \$SIG{'__WARN__'} = sub {
 545                my \$msg = shift;
 546                # timestamp warning like in CGI::Carp::warn
 547                my \$stamp = CGI::Carp::stamp();
 548                \$msg =~ s/^/\$stamp/gm;
 549                print \$error_log_fh \$msg;
 550        };
 551
 552        # write errors to error.log, access to access.log
 553        enable 'AccessLog',
 554                format => "combined",
 555                logger => sub { print \$access_log_fh @_; };
 556        enable sub {
 557                my \$app = shift;
 558                sub {
 559                        my \$env = shift;
 560                        \$env->{'psgi.errors'} = \$error_log_fh;
 561                        \$app->(\$env);
 562                }
 563        };
 564        # gitweb currently doesn't work with $SIG{CHLD} set to 'IGNORE',
 565        # because it uses 'close $fd or die...' on piped filehandle $fh
 566        # (which causes the parent process to wait for child to finish).
 567        enable_if { \$SIG{'CHLD'} eq 'IGNORE' } sub {
 568                my \$app = shift;
 569                sub {
 570                        my \$env = shift;
 571                        local \$SIG{'CHLD'} = 'DEFAULT';
 572                        local \$SIG{'CLD'}  = 'DEFAULT';
 573                        \$app->(\$env);
 574                }
 575        };
 576        # serve static files, i.e. stylesheet, images, script
 577        enable 'Static',
 578                path => sub { m!\.(js|css|png)\$! && s!^/gitweb/!! },
 579                root => "$root/",
 580                encoding => 'utf-8'; # encoding for 'text/plain' files
 581        # convert CGI application to PSGI app
 582        Plack::App::WrapCGI->new(script => "$root/gitweb.cgi")->to_app;
 583};
 584
 585# make it runnable as standalone app,
 586# like it would be run via 'plackup' utility
 587if (caller) {
 588        return \$app;
 589} else {
 590        require Plack::Runner;
 591
 592        my \$runner = Plack::Runner->new();
 593        \$runner->parse_options(qw(--env deployment --port $port),
 594                                "$local" ? qw(--host 127.0.0.1) : ());
 595        \$runner->run(\$app);
 596}
 597__END__
 598EOF
 599
 600        chmod a+x "$fqgitdir/gitweb/gitweb.psgi"
 601        # configuration is embedded in server script file, gitweb.psgi
 602        rm -f "$conf"
 603}
 604
 605python_conf() {
 606        # Python's builtin http.server and its CGI support is very limited.
 607        # CGI handler is capable of running CGI script only from inside a directory.
 608        # Trying to set cgi_directories=["/"] will add double slash to SCRIPT_NAME
 609        # and that in turn breaks gitweb's relative link generation.
 610
 611        # create a simple web root where $fqgitdir/gitweb/$httpd_only is our root
 612        mkdir -p "$fqgitdir/gitweb/$httpd_only/cgi-bin"
 613        # Python http.server follows the symlinks
 614        ln -sf "$root/gitweb.cgi" "$fqgitdir/gitweb/$httpd_only/cgi-bin/gitweb.cgi"
 615        ln -sf "$root/static" "$fqgitdir/gitweb/$httpd_only/"
 616
 617        # generate a standalone 'python http.server' script in $fqgitdir/gitweb
 618        # This asumes that python is in user's $PATH
 619        # This script is Python 2 and 3 compatible
 620        cat > "$fqgitdir/gitweb/gitweb.py" <<EOF
 621#!/usr/bin/env python
 622import os
 623import sys
 624
 625# Open log file in line buffering mode
 626accesslogfile = open("$fqgitdir/gitweb/access.log", 'a', buffering=1)
 627errorlogfile = open("$fqgitdir/gitweb/error.log", 'a', buffering=1)
 628
 629# and replace our stdout and stderr with log files
 630# also do a lowlevel duplicate of the logfile file descriptors so that
 631# our CGI child process writes any stderr warning also to the log file
 632_orig_stdout_fd = sys.stdout.fileno()
 633sys.stdout.close()
 634os.dup2(accesslogfile.fileno(), _orig_stdout_fd)
 635sys.stdout = accesslogfile
 636
 637_orig_stderr_fd = sys.stderr.fileno()
 638sys.stderr.close()
 639os.dup2(errorlogfile.fileno(), _orig_stderr_fd)
 640sys.stderr = errorlogfile
 641
 642from functools import partial
 643
 644if sys.version_info < (3, 0):  # Python 2
 645        from CGIHTTPServer import CGIHTTPRequestHandler
 646        from BaseHTTPServer import HTTPServer as ServerClass
 647else:  # Python 3
 648        from http.server import CGIHTTPRequestHandler
 649        from http.server import HTTPServer as ServerClass
 650
 651
 652# Those environment variables will be passed to the cgi script
 653os.environ.update({
 654        "GIT_EXEC_PATH": "$GIT_EXEC_PATH",
 655        "GIT_DIR": "$GIT_DIR",
 656        "GITWEB_CONFIG": "$GITWEB_CONFIG"
 657})
 658
 659
 660class GitWebRequestHandler(CGIHTTPRequestHandler):
 661
 662        def log_message(self, format, *args):
 663                # Write access logs to stdout
 664                sys.stdout.write("%s - - [%s] %s\n" %
 665                                (self.address_string(),
 666                                self.log_date_time_string(),
 667                                format%args))
 668
 669        def do_HEAD(self):
 670                self.redirect_path()
 671                CGIHTTPRequestHandler.do_HEAD(self)
 672
 673        def do_GET(self):
 674                if self.path == "/":
 675                        self.send_response(303, "See Other")
 676                        self.send_header("Location", "/cgi-bin/gitweb.cgi")
 677                        self.end_headers()
 678                        return
 679                self.redirect_path()
 680                CGIHTTPRequestHandler.do_GET(self)
 681
 682        def do_POST(self):
 683                self.redirect_path()
 684                CGIHTTPRequestHandler.do_POST(self)
 685
 686        # rewrite path of every request that is not gitweb.cgi to out of cgi-bin
 687        def redirect_path(self):
 688                if not self.path.startswith("/cgi-bin/gitweb.cgi"):
 689                        self.path = self.path.replace("/cgi-bin/", "/")
 690
 691        # gitweb.cgi is the only thing that is ever going to be run here.
 692        # Ignore everything else
 693        def is_cgi(self):
 694                result = False
 695                if self.path.startswith('/cgi-bin/gitweb.cgi'):
 696                        result = CGIHTTPRequestHandler.is_cgi(self)
 697                return result
 698
 699
 700bind = "127.0.0.1"
 701if "$local" == "true":
 702        bind = "0.0.0.0"
 703
 704# Set our http root directory
 705# This is a work around for a missing directory argument in older Python versions
 706# as this was added to SimpleHTTPRequestHandler in Python 3.7
 707os.chdir("$fqgitdir/gitweb/$httpd_only/")
 708
 709GitWebRequestHandler.protocol_version = "HTTP/1.0"
 710httpd = ServerClass((bind, $port), GitWebRequestHandler)
 711
 712sa = httpd.socket.getsockname()
 713print("Serving HTTP on", sa[0], "port", sa[1], "...")
 714httpd.serve_forever()
 715EOF
 716
 717        chmod a+x "$fqgitdir/gitweb/gitweb.py"
 718}
 719
 720gitweb_conf() {
 721        cat > "$fqgitdir/gitweb/gitweb_config.perl" <<EOF
 722#!@@PERL@@
 723our \$projectroot = "$(dirname "$fqgitdir")";
 724our \$git_temp = "$fqgitdir/gitweb/tmp";
 725our \$projects_list = \$projectroot;
 726
 727\$feature{'remote_heads'}{'default'} = [1];
 728EOF
 729}
 730
 731configure_httpd() {
 732        case "$httpd" in
 733        *lighttpd*)
 734                lighttpd_conf
 735                ;;
 736        *apache2*|*httpd*)
 737                apache2_conf
 738                ;;
 739        webrick)
 740                webrick_conf
 741                ;;
 742        *mongoose*)
 743                mongoose_conf
 744                ;;
 745        *plackup*)
 746                plackup_conf
 747                ;;
 748        *python*)
 749                python_conf
 750                ;;
 751        *)
 752                echo "Unknown httpd specified: $httpd"
 753                exit 1
 754                ;;
 755        esac
 756}
 757
 758case "$action" in
 759stop)
 760        stop_httpd
 761        exit 0
 762        ;;
 763start)
 764        start_httpd
 765        exit 0
 766        ;;
 767restart)
 768        stop_httpd
 769        start_httpd
 770        exit 0
 771        ;;
 772esac
 773
 774gitweb_conf
 775
 776resolve_full_httpd
 777mkdir -p "$fqgitdir/gitweb/$httpd_only"
 778conf="$fqgitdir/gitweb/$httpd_only.conf"
 779
 780configure_httpd
 781
 782start_httpd
 783url=http://127.0.0.1:$port
 784
 785if test -n "$browser"; then
 786        httpd_is_ready && git web--browse -b "$browser" $url || echo $url
 787else
 788        httpd_is_ready && git web--browse -c "instaweb.browser" $url || echo $url
 789fi