summaryrefslogtreecommitdiffstats
path: root/src/lib/Gitolite/Rc.pm
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/Gitolite/Rc.pm')
-rw-r--r--src/lib/Gitolite/Rc.pm688
1 files changed, 688 insertions, 0 deletions
diff --git a/src/lib/Gitolite/Rc.pm b/src/lib/Gitolite/Rc.pm
new file mode 100644
index 0000000..41996fb
--- /dev/null
+++ b/src/lib/Gitolite/Rc.pm
@@ -0,0 +1,688 @@
+package Gitolite::Rc;
+
+# everything to do with 'rc'. Also defines some 'constants'
+# ----------------------------------------------------------------------
+
+@EXPORT = qw(
+ %rc
+ glrc
+ query_rc
+ version
+ greeting
+ trigger
+ _which
+
+ $REMOTE_COMMAND_PATT
+ $REF_OR_FILENAME_PATT
+ $REPONAME_PATT
+ $REPOPATT_PATT
+ $USERNAME_PATT
+ $UNSAFE_PATT
+);
+
+use Exporter 'import';
+
+use Gitolite::Common;
+
+# ----------------------------------------------------------------------
+
+our %rc;
+our $non_core;
+
+# ----------------------------------------------------------------------
+
+# pre-populate some important rc keys
+# ----------------------------------------------------------------------
+
+$rc{GL_BINDIR} = $ENV{GL_BINDIR};
+$rc{GL_LIBDIR} = $ENV{GL_LIBDIR};
+
+# these keys could be overridden by the rc file later
+$rc{GL_REPO_BASE} = "$ENV{HOME}/repositories";
+$rc{GL_ADMIN_BASE} = "$ENV{HOME}/.gitolite";
+$rc{LOG_TEMPLATE} = "$ENV{HOME}/.gitolite/logs/gitolite-%y-%m.log";
+
+# variables that should probably never be changed but someone will want to, I'll bet...
+# ----------------------------------------------------------------------
+
+#<<<
+$REMOTE_COMMAND_PATT = qr(^[-0-9a-zA-Z._\@/+ :,\%=]*$);
+$REF_OR_FILENAME_PATT = qr(^[0-9a-zA-Z][-0-9a-zA-Z._\@/+ :,]*$);
+$REPONAME_PATT = qr(^\@?[0-9a-zA-Z][-0-9a-zA-Z._\@/+]*$);
+$REPOPATT_PATT = qr(^\@?[[0-9a-zA-Z][-0-9a-zA-Z._\@/+\\^$|()[\]*?{},]*$);
+$USERNAME_PATT = qr(^\@?[0-9a-zA-Z][-0-9a-zA-Z._\@+]*$);
+
+$UNSAFE_PATT = qr([`~#\$\&()|;<>]);
+#>>>
+
+# ----------------------------------------------------------------------
+
+# find the rc file and 'do' it
+# ----------------------------------------------------------------------
+my $current_data_version = "3.2";
+
+my $rc = glrc('filename');
+if ( -r $rc and -s $rc ) {
+ do $rc or die $@;
+}
+if ( defined($GL_ADMINDIR) ) {
+ say2 "";
+ say2 "FATAL: '$rc' seems to be for older gitolite; please see\nhttp://gitolite.com/gitolite/migr.html";
+
+ exit 1;
+}
+
+# let values specified in rc file override our internal ones
+# ----------------------------------------------------------------------
+@rc{ keys %RC } = values %RC;
+
+# expand the non_core list into INPUT, PRE_GIT, etc using 'ENABLE' settings
+non_core_expand() if $rc{ENABLE};
+
+# add internal triggers
+# ----------------------------------------------------------------------
+
+# is the server/repo in a writable state (i.e., not down for maintenance etc)
+unshift @{ $rc{ACCESS_1} }, 'Writable::access_1';
+
+# (testing only) override the rc file silently
+# ----------------------------------------------------------------------
+# use an env var that is highly unlikely to appear in real life :)
+do $ENV{G3T_RC} if exists $ENV{G3T_RC} and -r $ENV{G3T_RC};
+
+# setup some perl/rc/env vars, plus umask
+# ----------------------------------------------------------------------
+
+umask ( $rc{UMASK} || 0077 );
+
+unshift @INC, "$rc{LOCAL_CODE}/lib" if $rc{LOCAL_CODE};
+
+$ENV{PATH} = "$ENV{GL_BINDIR}:$ENV{PATH}" unless $ENV{PATH} =~ /^$ENV{GL_BINDIR}:/;
+
+{
+ $rc{GL_TID} = $ENV{GL_TID} ||= $$;
+ # TID: loosely, transaction ID. The first PID at the entry point passes
+ # it down to all its children so you can track each access, across all the
+ # various commands it spawns and actions it generates.
+
+ $rc{GL_LOGFILE} = $ENV{GL_LOGFILE} ||= gen_lfn( $rc{LOG_TEMPLATE} );
+}
+
+# these two are meant to help externally written commands (see
+# src/commands/writable for an example)
+$ENV{GL_REPO_BASE} = $rc{GL_REPO_BASE};
+$ENV{GL_ADMIN_BASE} = $rc{GL_ADMIN_BASE};
+
+# ----------------------------------------------------------------------
+
+use strict;
+use warnings;
+
+# ----------------------------------------------------------------------
+
+my $glrc_default_text = '';
+{
+ local $/ = undef;
+ $glrc_default_text = <DATA>;
+}
+
+# ----------------------------------------------------------------------
+
+sub non_core_expand {
+ my %enable;
+
+ for my $e ( @{ $rc{ENABLE} } ) {
+ my ( $name, $arg ) = split ' ', $e, 2;
+ # store args as the hash value for the name
+ $enable{$name} = $arg || '';
+
+ # for now, we pretend everything is a command, because commands
+ # are the only thing that the non_core list does not contain
+ $rc{COMMANDS}{$name} = $arg || 1;
+ }
+
+ # bring in additional non-core specs from the rc file, if given
+ if ( my $nc2 = $rc{NON_CORE} ) {
+ for ( $non_core, $nc2 ) {
+ # beat 'em into shape :)
+ s/#.*//g;
+ s/[ \t]+/ /g; s/^ //mg; s/ $//mg;
+ s/\n+/\n/g;
+ }
+
+ for ( split "\n", $nc2 ) {
+ next unless /\S/;
+ my ( $name, $where, $module, $before, $name2 ) = split ' ', $_;
+ if ( not $before ) {
+ $non_core .= "$name $where $module\n";
+ next;
+ }
+ die if $before ne 'before';
+ $non_core =~ s(^(?=$name2 $where( |$)))($name $where $module\n)m;
+ }
+ }
+
+ my @data = split "\n", $non_core || '';
+ for (@data) {
+ next if /^\s*(#|$)/;
+ my ( $name, $where, $module ) = split ' ', $_;
+
+ # if it appears here, it's not a command, so delete it. At the end of
+ # this loop, what's left in $rc{COMMANDS} will be those names in the
+ # enable list that do not appear in the non_core list.
+ delete $rc{COMMANDS}{$name};
+
+ next unless exists $enable{$name};
+
+ # module to call is name if specified as "."
+ $module = $name if $module eq ".";
+
+ # module to call is "name::pre_git" or such if specified as "::"
+ ( $module = $name ) .= "::" . lc($where) if $module eq '::';
+
+ # append arguments, if supplied
+ $module .= " $enable{$name}" if $enable{$name};
+
+ push @{ $rc{$where} }, $module;
+ }
+
+ # finally, add in commands that were declared in the non-core list
+ map { /^(\S+)/; $rc{COMMANDS}{$1} = 1 } @{ $rc{COMMAND} };
+}
+
+# exported functions
+# ----------------------------------------------------------------------
+
+sub glrc {
+ my $cmd = shift;
+ if ( $cmd eq 'default-filename' ) {
+ return "$ENV{HOME}/.gitolite.rc";
+ } elsif ( $cmd eq 'default-text' ) {
+ return $glrc_default_text if $glrc_default_text;
+ _die "rc file default text not set; this should not happen!";
+ } elsif ( $cmd eq 'filename' ) {
+ # where is the rc file?
+
+ # search $HOME first
+ return "$ENV{HOME}/.gitolite.rc" if -f "$ENV{HOME}/.gitolite.rc";
+
+ return '';
+ } elsif ( $cmd eq 'current-data-version' ) {
+ return $current_data_version;
+ } else {
+ _die "unknown argument to glrc: '$cmd'";
+ }
+}
+
+my $all = 0;
+my $dump = 0;
+my $nonl = 0;
+my $quiet = 0;
+
+sub query_rc {
+
+ my @vars = args();
+
+ no strict 'refs';
+
+ if ($all) {
+ for my $e ( sort keys %rc ) {
+ print "$e=" . ( defined( $rc{$e} ) ? $rc{$e} : 'undef' ) . "\n";
+ }
+ exit 0;
+ }
+
+ if ($dump) {
+ require Data::Dumper;
+ $Data::Dumper::Sortkeys = 1;
+ print Data::Dumper::Dumper \%rc;
+ exit 0;
+ }
+
+ my $cv = \%rc; # current "value"
+ while (@vars) {
+ my $v = shift @vars;
+
+ # dig into the rc hash, using each var as a component
+ if ( not ref($cv) ) {
+ _warn "unused arguments...";
+ last;
+ } elsif ( ref($cv) eq 'HASH' ) {
+ $cv = $cv->{$v} || '';
+ } elsif ( ref($cv) eq 'ARRAY' ) {
+ $cv = $cv->[$v] || '';
+ } else {
+ _die "dont know what to do with " . ref($cv) . " item in the rc file";
+ }
+ }
+
+ # we've run out of arguments so $cv is what we have. If we're supposed to
+ # be quiet, we don't have to print anything so let's get that done first:
+ exit( $cv ? 0 : 1 ) if $quiet; # shell truth
+
+ # print values (notice we ignore the '-n' option if it's a ref)
+ if ( ref($cv) eq 'HASH' ) {
+ print join( "\n", sort keys %$cv ), "\n" if %$cv;
+ } elsif ( ref($cv) eq 'ARRAY' ) {
+ print join( "\n", @$cv ), "\n" if @$cv;
+ } else {
+ print $cv . ( $nonl ? '' : "\n" ) if $cv;
+ }
+ exit( $cv ? 0 : 1 ); # shell truth
+}
+
+sub version {
+ my $version = '';
+ $version = '(unknown)';
+ for ("$ENV{GL_BINDIR}/VERSION") {
+ $version = slurp($_) if -r $_;
+ }
+ chomp($version);
+ return $version;
+}
+
+sub greeting {
+ my $json = shift;
+
+ chomp( my $hn = `hostname -s 2>/dev/null || hostname` );
+ my $gv = substr( `git --version`, 12 );
+ my $gl_user = $ENV{GL_USER} || '';
+ $gl_user = " $gl_user" if $gl_user;
+
+ if ($json) {
+ $json->{GL_USER} = $ENV{GL_USER};
+ $json->{USER} = ( $ENV{USER} || "httpd" ) . "\@$hn";
+ $json->{gitolite_version} = version();
+ chomp( $json->{git_version} = $gv ); # this thing has a newline at the end
+ return;
+ }
+
+ # normal output
+ return "hello$gl_user, this is " . ( $ENV{USER} || "httpd" ) . "\@$hn running gitolite3 " . version() . " on git $gv\n";
+}
+
+sub trigger {
+ my $rc_section = shift;
+
+ # if arg-2 (now arg-1, due to the 'shift' above) exists, it is a repo
+ # name, so setup env from options
+ require Gitolite::Conf::Load;
+ Gitolite::Conf::Load->import('env_options');
+ env_options( $_[0] ) if $_[0];
+
+ if ( exists $rc{$rc_section} ) {
+ if ( ref( $rc{$rc_section} ) ne 'ARRAY' ) {
+ _die "'$rc_section' section in rc file is not a perl list";
+ } else {
+ for my $s ( @{ $rc{$rc_section} } ) {
+ my ( $pgm, @args ) = split ' ', $s;
+
+ if ( my ( $module, $sub ) = ( $pgm =~ /^(.*)::(\w+)$/ ) ) {
+
+ require Gitolite::Triggers;
+ trace( 2, 'trigger module', $module, $sub, @args, $rc_section, @_ );
+ Gitolite::Triggers::run( $module, $sub, @args, $rc_section, @_ );
+
+ } else {
+ $pgm = _which( "triggers/$pgm", 'x' );
+
+ _warn("skipped trigger '$s' (not found or not executable)"), next if not $pgm;
+ trace( 2, 'trigger command', $s );
+ _system( $pgm, @args, $rc_section, @_ ); # they better all return with 0 exit codes!
+ }
+ }
+ }
+ return;
+ }
+ trace( 3, "'$rc_section' not found in rc" );
+}
+
+sub _which {
+ # looks for a file in LOCAL_CODE or GL_BINDIR. Returns whichever exists
+ # (LOCAL_CODE preferred if defined) or 0 if not found.
+ my $file = shift;
+ my $mode = shift; # could be 'x' or 'r'
+
+ my @files = ("$rc{GL_BINDIR}/$file");
+ unshift @files, ("$rc{LOCAL_CODE}/$file") if $rc{LOCAL_CODE};
+
+ for my $f (@files) {
+ return $f if -x $f;
+ return $f if -r $f and $mode eq 'r';
+ }
+
+ return 0;
+}
+
+# ----------------------------------------------------------------------
+
+=for args
+Usage: gitolite query-rc -a
+ gitolite query-rc -d
+ gitolite query-rc [-n] [-q] rc-variable
+
+ -a print all variables and values (first level only)
+ -d dump the entire rc structure
+ -n do not append a newline if variable is scalar
+ -q exit code only (shell truth; 0 is success)
+
+Query the rc hash. Second and subsequent arguments dig deeper into the hash.
+The examples are for the default configuration; yours may be different.
+
+Single values:
+ gitolite query-rc GL_ADMIN_BASE # prints "/home/git/.gitolite" or similar
+ gitolite query-rc UMASK # prints "63" (that's 0077 in decimal!)
+
+Hashes:
+ gitolite query-rc COMMANDS
+ # prints "desc", "help", "info", "perms", "writable", one per line
+ gitolite query-rc COMMANDS help # prints 1
+ gitolite query-rc -q COMMANDS help # prints nothing; exit code is 0
+ gitolite query-rc COMMANDS fork # prints nothing; exit code is 1
+
+Arrays (somewhat less useful):
+ gitolite query-rc POST_GIT # prints nothing; exit code is 0
+ gitolite query-rc POST_COMPILE # prints 4 lines
+ gitolite query-rc POST_COMPILE 0 # prints the first of those 4 lines
+
+Explore:
+ gitolite query-rc -a
+ # prints all first level variables and values, one per line. Any that are
+ # listed as HASH or ARRAY can be explored further in subsequent commands.
+ gitolite query-rc -d # dump the entire rc structure
+=cut
+
+sub args {
+ my $help = 0;
+
+ require Getopt::Long;
+ Getopt::Long::GetOptions(
+ 'all|a' => \$all,
+ 'dump|d' => \$dump,
+ 'nonl|n' => \$nonl,
+ 'quiet|q' => \$quiet,
+ 'help|h' => \$help,
+ ) or usage();
+
+ _die("'-a' cannot be combined with other arguments or options; run with '-h' for usage") if $all and ( @ARGV or $dump or $nonl or $quiet );
+ usage() if not $all and not $dump and not @ARGV or $help;
+ return @ARGV;
+}
+
+# ----------------------------------------------------------------------
+
+BEGIN {
+ $non_core = "
+ # No user-servicable parts inside. Warranty void if seal broken. Refer
+ # servicing to authorised service center only.
+
+ continuation-lines SYNTACTIC_SUGAR .
+ keysubdirs-as-groups SYNTACTIC_SUGAR .
+ macros SYNTACTIC_SUGAR .
+ refex-expr SYNTACTIC_SUGAR .
+
+ renice PRE_GIT .
+
+ Kindergarten INPUT ::
+
+ CpuTime INPUT ::
+ CpuTime POST_GIT ::
+
+ Shell INPUT ::
+
+ Alias INPUT ::
+
+ Motd INPUT ::
+ Motd PRE_GIT ::
+ Motd COMMAND motd
+
+ Mirroring INPUT ::
+ Mirroring PRE_GIT ::
+ Mirroring POST_GIT ::
+
+ refex-expr ACCESS_2 RefexExpr::access_2
+
+ expand-deny-messages ACCESS_1 .
+ expand-deny-messages ACCESS_2 .
+
+ RepoUmask PRE_GIT ::
+ RepoUmask POST_CREATE ::
+
+ partial-copy PRE_GIT .
+
+ upstream PRE_GIT .
+
+ no-create-on-read PRE_CREATE AutoCreate::deny_R
+ no-auto-create PRE_CREATE AutoCreate::deny_RW
+
+ ssh-authkeys-split POST_COMPILE post-compile/ssh-authkeys-split
+ ssh-authkeys POST_COMPILE post-compile/ssh-authkeys
+ Shell POST_COMPILE post-compile/ssh-authkeys-shell-users
+
+ set-default-roles POST_CREATE .
+
+ git-config POST_COMPILE post-compile/update-git-configs
+ git-config POST_CREATE post-compile/update-git-configs
+
+ create-with-reference POST_CREATE post-compile/create-with-reference
+
+ gitweb POST_CREATE post-compile/update-gitweb-access-list
+ gitweb POST_COMPILE post-compile/update-gitweb-access-list
+
+ cgit POST_COMPILE post-compile/update-description-file
+
+ daemon POST_CREATE post-compile/update-git-daemon-access-list
+ daemon POST_COMPILE post-compile/update-git-daemon-access-list
+
+ repo-specific-hooks POST_COMPILE .
+ repo-specific-hooks POST_CREATE .
+";
+}
+
+1;
+
+# ----------------------------------------------------------------------
+
+__DATA__
+# configuration variables for gitolite
+
+# This file is in perl syntax. But you do NOT need to know perl to edit it --
+# just mind the commas, use single quotes unless you know what you're doing,
+# and make sure the brackets and braces stay matched up!
+
+# (Tip: perl allows a comma after the last item in a list also!)
+
+# HELP for commands can be had by running the command with "-h".
+
+# HELP for all the other FEATURES can be found in the documentation (look for
+# "list of non-core programs shipped with gitolite" in the master index) or
+# directly in the corresponding source file.
+
+%RC = (
+
+ # ------------------------------------------------------------------
+
+ # default umask gives you perms of '0700'; see the rc file docs for
+ # how/why you might change this
+ UMASK => 0077,
+
+ # look for "git-config" in the documentation
+ GIT_CONFIG_KEYS => '',
+
+ # comment out if you don't need all the extra detail in the logfile
+ LOG_EXTRA => 1,
+ # logging options
+ # 1. leave this section as is for 'normal' gitolite logging (default)
+ # 2. uncomment this line to log ONLY to syslog:
+ # LOG_DEST => 'syslog',
+ # 3. uncomment this line to log to syslog and the normal gitolite log:
+ # LOG_DEST => 'syslog,normal',
+ # 4. prefixing "repo-log," to any of the above will **also** log just the
+ # update records to "gl-log" in the bare repo directory:
+ # LOG_DEST => 'repo-log,normal',
+ # LOG_DEST => 'repo-log,syslog',
+ # LOG_DEST => 'repo-log,syslog,normal',
+ # syslog 'facility': defaults to 'local0', uncomment if needed. For example:
+ # LOG_FACILITY => 'local4',
+
+ # roles. add more roles (like MANAGER, TESTER, ...) here.
+ # WARNING: if you make changes to this hash, you MUST run 'gitolite
+ # compile' afterward, and possibly also 'gitolite trigger POST_COMPILE'
+ ROLES => {
+ READERS => 1,
+ WRITERS => 1,
+ },
+
+ # enable caching (currently only Redis). PLEASE RTFM BEFORE USING!!!
+ # CACHE => 'Redis',
+
+ # ------------------------------------------------------------------
+
+ # rc variables used by various features
+
+ # the 'info' command prints this as additional info, if it is set
+ # SITE_INFO => 'Please see http://blahblah/gitolite for more help',
+
+ # the CpuTime feature uses these
+ # display user, system, and elapsed times to user after each git operation
+ # DISPLAY_CPU_TIME => 1,
+ # display a warning if total CPU times (u, s, cu, cs) crosses this limit
+ # CPU_TIME_WARN_LIMIT => 0.1,
+
+ # the Mirroring feature needs this
+ # HOSTNAME => "foo",
+
+ # TTL for redis cache; PLEASE SEE DOCUMENTATION BEFORE UNCOMMENTING!
+ # CACHE_TTL => 600,
+
+ # ------------------------------------------------------------------
+
+ # suggested locations for site-local gitolite code (see cust.html)
+
+ # this one is managed directly on the server
+ # LOCAL_CODE => "$ENV{HOME}/local",
+
+ # or you can use this, which lets you put everything in a subdirectory
+ # called "local" in your gitolite-admin repo. For a SECURITY WARNING
+ # on this, see http://gitolite.com/gitolite/non-core.html#pushcode
+ # LOCAL_CODE => "$rc{GL_ADMIN_BASE}/local",
+
+ # ------------------------------------------------------------------
+
+ # List of commands and features to enable
+
+ ENABLE => [
+
+ # COMMANDS
+
+ # These are the commands enabled by default
+ 'help',
+ 'desc',
+ 'info',
+ 'perms',
+ 'writable',
+
+ # Uncomment or add new commands here.
+ # 'create',
+ # 'fork',
+ # 'mirror',
+ # 'readme',
+ # 'sskm',
+ # 'D',
+
+ # These FEATURES are enabled by default.
+
+ # essential (unless you're using smart-http mode)
+ 'ssh-authkeys',
+
+ # creates git-config entries from gitolite.conf file entries like 'config foo.bar = baz'
+ 'git-config',
+
+ # creates git-daemon-export-ok files; if you don't use git-daemon, comment this out
+ 'daemon',
+
+ # creates projects.list file; if you don't use gitweb, comment this out
+ 'gitweb',
+
+ # These FEATURES are disabled by default; uncomment to enable. If you
+ # need to add new ones, ask on the mailing list :-)
+
+ # user-visible behaviour
+
+ # prevent wild repos auto-create on fetch/clone
+ # 'no-create-on-read',
+ # no auto-create at all (don't forget to enable the 'create' command!)
+ # 'no-auto-create',
+
+ # access a repo by another (possibly legacy) name
+ # 'Alias',
+
+ # give some users direct shell access. See documentation in
+ # sts.html for details on the following two choices.
+ # "Shell $ENV{HOME}/.gitolite.shell-users",
+ # 'Shell alice bob',
+
+ # set default roles from lines like 'option default.roles-1 = ...', etc.
+ # 'set-default-roles',
+
+ # show more detailed messages on deny
+ # 'expand-deny-messages',
+
+ # show a message of the day
+ # 'Motd',
+
+ # system admin stuff
+
+ # enable mirroring (don't forget to set the HOSTNAME too!)
+ # 'Mirroring',
+
+ # allow people to submit pub files with more than one key in them
+ # 'ssh-authkeys-split',
+
+ # selective read control hack
+ # 'partial-copy',
+
+ # manage local, gitolite-controlled, copies of read-only upstream repos
+ # 'upstream',
+
+ # updates 'description' file instead of 'gitweb.description' config item
+ # 'cgit',
+
+ # allow repo-specific hooks to be added
+ # 'repo-specific-hooks',
+
+ # performance, logging, monitoring...
+
+ # be nice
+ # 'renice 10',
+
+ # log CPU times (user, system, cumulative user, cumulative system)
+ # 'CpuTime',
+
+ # syntactic_sugar for gitolite.conf and included files
+
+ # allow backslash-escaped continuation lines in gitolite.conf
+ # 'continuation-lines',
+
+ # create implicit user groups from directory names in keydir/
+ # 'keysubdirs-as-groups',
+
+ # allow simple line-oriented macros
+ # 'macros',
+
+ # Kindergarten mode
+
+ # disallow various things that sensible people shouldn't be doing anyway
+ # 'Kindergarten',
+ ],
+
+);
+
+# ------------------------------------------------------------------------------
+# per perl rules, this should be the last line in such a file:
+1;
+
+# Local variables:
+# mode: perl
+# End:
+# vim: set syn=perl: