/
gitolite_env.pm
157 lines (131 loc) · 5.87 KB
/
gitolite_env.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# stuff that detects or sets up the runtime environment
package gitolite_env;
use Exporter 'import';
@EXPORT = qw(
setup_environment
simulate_ssh_connection
get_logfilename
);
use strict;
use warnings;
# ----------------------------------------------------------------------------
# find the rc file, then pull the libraries
# ----------------------------------------------------------------------------
BEGIN {
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
}
use lib $ENV{GL_BINDIR};
use gitolite_rc;
use gitolite;
# ----------------------------------------------------------------------------
# start
# ----------------------------------------------------------------------------
# firstly, the following function, 'setup_environment', is not only about env
# vars; it does other stuff too (like umask, nice...)
# a lot of stuff gets carried around in env vars primarily for 2 reasons. One
# is that git calls the hooks, so they're not in the same 'process' as the
# 'gl-auth-command' that probably started things off.
# Granted; we could write the same 'discovery' within the hook code, but
# that's needless code duplication, plus in some cases a good amount of
# inefficiency.
# Even more important, we do *not* want to burden the ADCs (admin defined
# commands) with all this discovery, because those are written by the users
# themselves (my 'user' == some gitolite 'admin' somewhere; I don't mean
# 'gitolite user')
# think of it OS-supported memo-ization :-)
sub setup_environment {
$ENV{GL_ADMINDIR} = $GL_ADMINDIR;
$ENV{GL_LOG} = get_logfilename($GL_LOGT);
$ENV{PATH} = "$GIT_PATH:$ENV{PATH}" if $GIT_PATH;
# set default permission of wildcard repositories
$ENV{GL_WILDREPOS_DEFPERMS} = $GL_WILDREPOS_DEFPERMS if $GL_WILDREPOS_DEFPERMS;
# this is used in so many places, inside and outside gitolite by external
# hooks and ADCs, it isn't even funny...
$ENV{GL_REPO_BASE_ABS} = ( $REPO_BASE =~ m(^/) ? $REPO_BASE : "$ENV{HOME}/$REPO_BASE" );
# be nice if asked. If you want me to pull in BSD::Resource to get rid of
# the first '0', feel free to send me a patch that does everything needed
# from within my own installer, does not require internet access (don't
# ask!), and doesn't require a C compiler or the perl-devel (or eqvt
# named) packages. Heck in some cases it's not even Linux...
setpriority(0, 0, $GL_NICE_VALUE) if $GL_NICE_VALUE and $GL_NICE_VALUE > 0;
umask($REPO_UMASK);
set_up_http_death() if $ENV{GITOLITE_HTTP_HOME};
}
sub simulate_ssh_connection {
# these patterns indicate normal git usage; see "services[]" in
# http-backend.c for how I got that. Also note that "info" is overloaded;
# git uses "info/refs...", while gitolite uses "info" or "info?...". So
# there's a "/" after info in the list below
if ($ENV{PATH_INFO} =~ m(^/(.*)/(HEAD$|info/refs$|objects/|git-(?:upload|receive)-pack$))) {
my $repo = $1;
my $verb = ($ENV{REQUEST_URI} =~ /git-receive-pack/) ? 'git-receive-pack' : 'git-upload-pack';
$ENV{SSH_ORIGINAL_COMMAND} = "$verb '$repo'";
} else {
# this is one of our custom commands; could be anything really,
# because of the adc feature
my ($verb) = ($ENV{PATH_INFO} =~ m(^/(\S+)));
my $args = $ENV{QUERY_STRING};
$args =~ s/\+/ /g;
$ENV{SSH_ORIGINAL_COMMAND} = $verb;
$ENV{SSH_ORIGINAL_COMMAND} .= " $args" if $args;
print_http_headers(); # in preparation for the eventual output!
}
$ENV{SSH_CONNECTION} = "$ENV{REMOTE_ADDR} $ENV{REMOTE_PORT} $ENV{SERVER_ADDR} $ENV{SERVER_PORT}";
}
# a plain "die" was fine for ssh but http has all that extra gunk it needs.
# So we need to, in effect, create a "death handler".
sub set_up_http_death
{
$SIG{__DIE__} = sub {
my $service = ($ENV{SSH_ORIGINAL_COMMAND} =~ /git-receive-pack/ ? 'git-receive-pack' : 'git-upload-pack');
my $message = shift; chomp($message);
print STDERR "$message\n";
# format the service response, then the message. With initial
# help from Ilari and then a more detailed email from Shawn...
$service = "# service=$service\n"; $message = "ERR $message\n";
$service = sprintf("%04X", length($service)+4) . "$service"; # no CRLF on this one
$message = sprintf("%04X", length($message)+4) . "$message";
print_http_headers();
print $service;
print "0000"; # flush-pkt, apparently
print $message;
print STDERR $service;
print STDERR $message;
exit 0; # if it's ok for die_webcgi in git.git/http-backend.c, it's ok for me ;-)
}
}
# ----------------------------------------------------------------------------
# helpers
# ----------------------------------------------------------------------------
my $http_headers_printed = 0;
sub print_http_headers {
my($code, $text) = @_;
return if $http_headers_printed++;
$code ||= 200;
$text ||= "OK - gitolite";
$|++;
print "Status: $code $text\r\n";
print "Expires: Fri, 01 Jan 1980 00:00:00 GMT\r\n";
print "Pragma: no-cache\r\n";
print "Cache-Control: no-cache, max-age=0, must-revalidate\r\n";
print "\r\n";
}
sub get_logfilename {
# this sub has a wee little side-effect; it sets $ENV{GL_TS}
my($template) = shift;
my ($s, $min, $h, $d, $m, $y) = (localtime)[0..5];
$y += 1900; $m++; # usual adjustments
for ($s, $min, $h, $d, $m) {
$_ = "0$_" if $_ < 10;
}
$ENV{GL_TS} = "$y-$m-$d.$h:$min:$s";
# substitute template parameters and set the logfile name
$template =~ s/%y/$y/g;
$template =~ s/%m/$m/g;
$template =~ s/%d/$d/g;
return ($template);
}
# ------------------------------------------------------------------------------
# per perl rules, this should be the last line in such a file:
1;