2012-03-08 09:00:13 +01:00
|
|
|
package Gitolite::Common;
|
|
|
|
|
|
|
|
# common (non-gitolite-specific) functions
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
#<<<
|
|
|
|
@EXPORT = qw(
|
|
|
|
print2 dbg _mkdir _open ln_sf tsh_rc sort_u
|
2012-03-08 14:50:00 +01:00
|
|
|
say _warn _chdir _print tsh_text list_phy_repos
|
2012-03-10 14:26:29 +01:00
|
|
|
say2 _die _system slurp tsh_lines
|
2012-03-09 16:53:16 +01:00
|
|
|
trace cleanup_conf_line tsh_try
|
2012-03-08 09:00:13 +01:00
|
|
|
usage tsh_run
|
2012-03-21 11:53:50 +01:00
|
|
|
gen_lfn
|
2012-03-17 16:43:28 +01:00
|
|
|
gl_log
|
2012-03-08 09:00:13 +01:00
|
|
|
);
|
|
|
|
#>>>
|
|
|
|
use Exporter 'import';
|
|
|
|
use File::Path qw(mkpath);
|
|
|
|
use Carp qw(carp cluck croak confess);
|
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
sub print2 {
|
|
|
|
local $/ = "\n";
|
|
|
|
print STDERR @_;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub say {
|
|
|
|
local $/ = "\n";
|
|
|
|
print @_, "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
sub say2 {
|
|
|
|
local $/ = "\n";
|
|
|
|
print STDERR @_, "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
sub trace {
|
2012-03-30 02:41:06 +02:00
|
|
|
gl_log( "\t" . join( ",", @_[ 1 .. $#_ ] ) ) if $_[0] <= 1 and defined $Gitolite::Rc::rc{LOG_EXTRA};
|
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
return unless defined( $ENV{D} );
|
|
|
|
|
2012-03-11 04:56:12 +01:00
|
|
|
my $level = shift; return if $ENV{D} < $level;
|
2012-03-12 16:24:30 +01:00
|
|
|
my $args = ''; $args = join( ", ", @_ ) if @_;
|
2012-03-19 03:01:09 +01:00
|
|
|
my $sub = ( caller 1 )[3] || ''; $sub =~ s/.*://;
|
2012-03-19 03:19:01 +01:00
|
|
|
if ( not $sub ) {
|
|
|
|
$sub = (caller)[1];
|
2012-03-19 03:01:09 +01:00
|
|
|
$sub =~ s(.*/(.*))(($1));
|
|
|
|
}
|
|
|
|
$sub .= ' ' x ( 32 - length($sub) );
|
2012-03-12 16:24:30 +01:00
|
|
|
say2 "TRACE $level $sub", ( @_ ? shift : () );
|
|
|
|
say2( "TRACE $level " . ( " " x 32 ), $_ ) for @_;
|
2012-03-08 09:00:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub dbg {
|
|
|
|
use Data::Dumper;
|
|
|
|
return unless defined( $ENV{D} );
|
|
|
|
for my $i (@_) {
|
|
|
|
print STDERR "DBG: " . Dumper($i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _warn {
|
2012-04-16 13:44:03 +02:00
|
|
|
gl_log( 'warn', @_ );
|
2012-03-08 09:00:13 +01:00
|
|
|
if ( $ENV{D} and $ENV{D} >= 3 ) {
|
|
|
|
cluck "WARNING: ", @_, "\n";
|
|
|
|
} elsif ( defined( $ENV{D} ) ) {
|
|
|
|
carp "WARNING: ", @_, "\n";
|
|
|
|
} else {
|
|
|
|
warn "WARNING: ", @_, "\n";
|
|
|
|
}
|
|
|
|
}
|
2012-04-16 13:44:03 +02:00
|
|
|
$SIG{__WARN__} = \&_warn;
|
2012-03-08 09:00:13 +01:00
|
|
|
|
|
|
|
sub _die {
|
2012-03-21 11:53:50 +01:00
|
|
|
gl_log( 'die', @_ );
|
2012-03-08 09:00:13 +01:00
|
|
|
if ( $ENV{D} and $ENV{D} >= 3 ) {
|
|
|
|
confess "FATAL: " . join( ",", @_ ) . "\n" if defined( $ENV{D} );
|
|
|
|
} elsif ( defined( $ENV{D} ) ) {
|
|
|
|
croak "FATAL: " . join( ",", @_ ) . "\n";
|
|
|
|
} else {
|
|
|
|
die "FATAL: " . join( ",", @_ ) . "\n";
|
|
|
|
}
|
|
|
|
}
|
2012-04-16 13:44:03 +02:00
|
|
|
$SIG{__DIE__} = \&_die;
|
2012-03-08 09:00:13 +01:00
|
|
|
|
|
|
|
sub usage {
|
2012-03-12 16:24:30 +01:00
|
|
|
_warn(shift) if @_;
|
2012-03-19 17:04:07 +01:00
|
|
|
my $script = (caller)[1];
|
|
|
|
my $function = ( ( ( caller(1) )[3] ) || ( ( caller(0) )[3] ) );
|
2012-03-12 16:24:30 +01:00
|
|
|
$function =~ s/.*:://;
|
|
|
|
my $code = slurp($script);
|
2012-03-19 17:04:07 +01:00
|
|
|
$code =~ /^=for $function\b(.*?)^=cut/sm;
|
2012-03-12 16:24:30 +01:00
|
|
|
say2( $1 ? $1 : "...no usage message in $script" );
|
2012-03-08 09:00:13 +01:00
|
|
|
exit 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _mkdir {
|
|
|
|
# it's not an error if the directory exists, but it is an error if it
|
|
|
|
# doesn't exist and we can't create it
|
|
|
|
my $dir = shift;
|
|
|
|
my $perm = shift; # optional
|
|
|
|
return if -d $dir;
|
|
|
|
mkpath($dir);
|
|
|
|
chmod $perm, $dir if $perm;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _chdir {
|
|
|
|
chdir( $_[0] || $ENV{HOME} ) or _die "chdir $_[0] failed: $!\n";
|
|
|
|
}
|
|
|
|
|
2012-03-10 14:26:29 +01:00
|
|
|
sub _system {
|
2012-03-15 04:45:02 +01:00
|
|
|
# run system(), catch errors. Be verbose only if $ENV{D} exists. If not,
|
|
|
|
# exit with <rc of system()> if it applies, else just "exit 1".
|
2012-03-30 02:41:06 +02:00
|
|
|
trace( 1, 'system', @_ );
|
2012-03-10 14:26:29 +01:00
|
|
|
if ( system(@_) != 0 ) {
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 1, "system() failed", @_, "-> $?" );
|
2012-03-10 14:26:29 +01:00
|
|
|
if ( $? == -1 ) {
|
2012-03-15 04:45:02 +01:00
|
|
|
die "failed to execute: $!\n" if $ENV{D};
|
2012-03-10 14:26:29 +01:00
|
|
|
} elsif ( $? & 127 ) {
|
2012-03-15 04:45:02 +01:00
|
|
|
die "child died with signal " . ( $? & 127 ) . "\n" if $ENV{D};
|
2012-03-10 14:26:29 +01:00
|
|
|
} else {
|
2012-03-15 04:45:02 +01:00
|
|
|
die "child exited with value " . ( $? >> 8 ) . "\n" if $ENV{D};
|
2012-03-15 15:34:30 +01:00
|
|
|
exit( $? >> 8 );
|
2012-03-10 14:26:29 +01:00
|
|
|
}
|
2012-03-15 04:45:02 +01:00
|
|
|
exit 1;
|
2012-03-10 14:26:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
sub _open {
|
|
|
|
open( my $fh, $_[0], $_[1] ) or _die "open $_[1] failed: $!\n";
|
|
|
|
return $fh;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub _print {
|
|
|
|
my ( $file, @text ) = @_;
|
|
|
|
my $fh = _open( ">", "$file.$$" );
|
|
|
|
print $fh @text;
|
|
|
|
close($fh) or _die "close $file failed: $! at ", (caller)[1], " line ", (caller)[2], "\n";
|
|
|
|
my $oldmode = ( ( stat $file )[2] );
|
|
|
|
rename "$file.$$", $file;
|
|
|
|
chmod $oldmode, $file if $oldmode;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub slurp {
|
2012-03-11 16:16:31 +01:00
|
|
|
return unless defined wantarray;
|
|
|
|
local $/ = undef unless wantarray;
|
2012-03-08 09:00:13 +01:00
|
|
|
my $fh = _open( "<", $_[0] );
|
|
|
|
return <$fh>;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub dos2unix {
|
|
|
|
# WARNING: when calling this, make sure you supply a list context
|
|
|
|
s/\r\n/\n/g for @_;
|
|
|
|
return @_;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub ln_sf {
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 3, @_ );
|
2012-03-08 09:00:13 +01:00
|
|
|
my ( $srcdir, $glob, $dstdir ) = @_;
|
|
|
|
for my $hook ( glob("$srcdir/$glob") ) {
|
|
|
|
$hook =~ s/$srcdir\///;
|
|
|
|
unlink "$dstdir/$hook";
|
|
|
|
symlink "$srcdir/$hook", "$dstdir/$hook" or croak "could not symlink $srcdir/$hook to $dstdir\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sub sort_u {
|
|
|
|
my %uniq;
|
|
|
|
my $listref = shift;
|
2012-03-12 16:24:30 +01:00
|
|
|
return [] unless @{$listref};
|
|
|
|
undef @uniq{ @{$listref} }; # expect a listref
|
2012-03-08 09:00:13 +01:00
|
|
|
my @sort_u = sort keys %uniq;
|
|
|
|
return \@sort_u;
|
|
|
|
}
|
2012-03-08 14:50:00 +01:00
|
|
|
|
2012-03-09 16:53:16 +01:00
|
|
|
sub cleanup_conf_line {
|
|
|
|
my $line = shift;
|
|
|
|
|
|
|
|
# kill comments, but take care of "#" inside *simple* strings
|
|
|
|
$line =~ s/^((".*?"|[^#"])*)#.*/$1/;
|
|
|
|
# normalise whitespace; keeps later regexes very simple
|
|
|
|
$line =~ s/=/ = /;
|
|
|
|
$line =~ s/\s+/ /g;
|
|
|
|
$line =~ s/^ //;
|
|
|
|
$line =~ s/ $//;
|
|
|
|
return $line;
|
|
|
|
}
|
|
|
|
|
2012-03-08 14:50:00 +01:00
|
|
|
{
|
|
|
|
my @phy_repos = ();
|
|
|
|
|
|
|
|
sub list_phy_repos {
|
|
|
|
# use cached value only if it exists *and* no arg was received (i.e.,
|
|
|
|
# receiving *any* arg invalidates cache)
|
|
|
|
return \@phy_repos if ( @phy_repos and not @_ );
|
|
|
|
|
|
|
|
for my $repo (`find . -name "*.git" -prune`) {
|
|
|
|
chomp($repo);
|
|
|
|
$repo =~ s(\./(.*)\.git$)($1);
|
|
|
|
push @phy_repos, $repo;
|
|
|
|
}
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 2, scalar(@phy_repos) . " physical repos found" );
|
2012-03-12 16:24:30 +01:00
|
|
|
return sort_u( \@phy_repos );
|
2012-03-08 14:50:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-21 11:53:50 +01:00
|
|
|
# generate a timestamp
|
|
|
|
sub gen_ts {
|
2012-03-19 03:19:01 +01:00
|
|
|
my ( $s, $min, $h, $d, $m, $y ) = (localtime)[ 0 .. 5 ];
|
|
|
|
$y += 1900; $m++; # usual adjustments
|
|
|
|
for ( $s, $min, $h, $d, $m ) {
|
2012-03-17 16:43:28 +01:00
|
|
|
$_ = "0$_" if $_ < 10;
|
|
|
|
}
|
|
|
|
my $ts = "$y-$m-$d.$h:$min:$s";
|
|
|
|
|
2012-03-21 11:53:50 +01:00
|
|
|
return $ts;
|
|
|
|
}
|
|
|
|
|
|
|
|
# generate a log file name
|
|
|
|
sub gen_lfn {
|
|
|
|
my ( $s, $min, $h, $d, $m, $y ) = (localtime)[ 0 .. 5 ];
|
|
|
|
$y += 1900; $m++; # usual adjustments
|
|
|
|
for ( $s, $min, $h, $d, $m ) {
|
|
|
|
$_ = "0$_" if $_ < 10;
|
|
|
|
}
|
2012-03-17 16:43:28 +01:00
|
|
|
|
2012-03-19 03:19:01 +01:00
|
|
|
my ($template) = shift;
|
2012-03-17 16:43:28 +01:00
|
|
|
# substitute template parameters and set the logfile name
|
|
|
|
$template =~ s/%y/$y/g;
|
|
|
|
$template =~ s/%m/$m/g;
|
|
|
|
$template =~ s/%d/$d/g;
|
|
|
|
|
2012-03-21 11:53:50 +01:00
|
|
|
return $template;
|
2012-03-17 16:43:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
sub gl_log {
|
|
|
|
# the log filename and the timestamp come from the environment. If we get
|
|
|
|
# called even before they are set, we have no choice but to dump to STDERR
|
|
|
|
# (and probably call "logger").
|
2012-03-30 02:41:06 +02:00
|
|
|
|
|
|
|
# tab sep if there's more than one field
|
2012-03-19 03:19:01 +01:00
|
|
|
my $msg = join( "\t", @_ );
|
2012-03-21 11:53:50 +01:00
|
|
|
$msg =~ s/[\n\r]+/<<newline>>/g;
|
2012-03-17 16:43:28 +01:00
|
|
|
|
2012-03-30 02:41:06 +02:00
|
|
|
my $ts = gen_ts();
|
2012-03-21 11:53:50 +01:00
|
|
|
my $tid = $ENV{GL_TID} ||= $$;
|
2012-03-17 16:43:28 +01:00
|
|
|
|
|
|
|
my $fh;
|
2012-06-17 04:25:12 +02:00
|
|
|
logger_plus_stderr( "errors found before logging could be setup", "$msg" ) if not $ENV{GL_LOGFILE};
|
|
|
|
open my $lfh, ">>", $ENV{GL_LOGFILE}
|
|
|
|
or logger_plus_stderr( "errors found before logfile could be created", "$msg" );
|
2012-03-21 11:53:50 +01:00
|
|
|
print $lfh "$ts\t$tid\t$msg\n";
|
2012-03-17 16:43:28 +01:00
|
|
|
close $lfh;
|
|
|
|
}
|
|
|
|
|
|
|
|
sub logger_plus_stderr {
|
|
|
|
open my $fh, "|-", "logger" or confess "it's really not my day is it...?\n";
|
2012-06-17 04:25:12 +02:00
|
|
|
for ( @_ ) {
|
|
|
|
print STDERR "FATAL: $_\n";
|
|
|
|
print $fh "FATAL: $_\n";
|
2012-03-17 16:43:28 +01:00
|
|
|
}
|
|
|
|
exit 1;
|
|
|
|
}
|
|
|
|
|
2012-03-08 09:00:13 +01:00
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
# bare-minimum subset of 'Tsh' (see github.com/sitaramc/tsh)
|
|
|
|
{
|
|
|
|
my ( $rc, $text );
|
|
|
|
sub tsh_rc { return $rc || 0; }
|
|
|
|
sub tsh_text { return $text || ''; }
|
|
|
|
sub tsh_lines { return split /\n/, $text; }
|
|
|
|
|
|
|
|
sub tsh_try {
|
|
|
|
my $cmd = shift; die "try: expects only one argument" if @_;
|
2012-04-18 08:35:21 +02:00
|
|
|
$text = `( $cmd ) 2>&1; printf RC=\$?`;
|
2012-03-08 09:00:13 +01:00
|
|
|
if ( $text =~ s/RC=(\d+)$// ) {
|
|
|
|
$rc = $1;
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 3, $text );
|
2012-03-08 09:00:13 +01:00
|
|
|
return ( not $rc );
|
|
|
|
}
|
|
|
|
die "couldnt find RC= in result; this should not happen:\n$text\n\n...\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
sub tsh_run {
|
|
|
|
open( my $fh, "-|", @_ ) or die "popen failed: $!";
|
|
|
|
local $/ = undef; $text = <$fh>;
|
|
|
|
close $fh; warn "pclose failed: $!" if $!;
|
|
|
|
$rc = ( $? >> 8 );
|
2012-03-15 16:30:39 +01:00
|
|
|
trace( 3, $text );
|
2012-03-08 09:00:13 +01:00
|
|
|
return $text;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
1;
|