From d61991b2acdb48faa4e6882250b642a6ba150cd5 Mon Sep 17 00:00:00 2001 From: "Kyle J. McKay" Date: Wed, 29 Nov 2017 02:34:47 -0800 Subject: [PATCH] update-all-config: convert from .sh to .pl The POSIX sh version is very POSIXy and also VERY SLOW. The shell is just not capable of doing the needed checks efficiently -- lots and lots of processes get spawned. The new perl version runs much, much faster. It still spawns out for find results (there doesn't seem to be an obviously faster way to get those), but now all the mode and group checking and changing is fully internal and so is the config checking (although changing still spawns git config). Signed-off-by: Kyle J. McKay --- toolbox/update-all-config.pl | 667 +++++++++++++++++++++++++++++++++++++++++ toolbox/update-all-config.sh | 384 +----------------------- toolbox/update-all-projects.sh | 4 +- 3 files changed, 676 insertions(+), 379 deletions(-) create mode 100755 toolbox/update-all-config.pl rewrite toolbox/update-all-config.sh (99%) diff --git a/toolbox/update-all-config.pl b/toolbox/update-all-config.pl new file mode 100755 index 0000000..1895114 --- /dev/null +++ b/toolbox/update-all-config.pl @@ -0,0 +1,667 @@ +#!/usr/bin/perl + +# update-all-config.pl - Update all out-of-date config + +use strict; +use warnings; +use vars qw($VERSION); +BEGIN {*VERSION = \'2.0'} +use File::Basename; +use Cwd qw(realpath); +use Getopt::Long; +use Pod::Usage; +use lib "__BASEDIR__"; +use Girocco::Config; +use Girocco::Util; +use Girocco::CLIUtil; +use Girocco::Project; + +my $shbin; +BEGIN { + $shbin = $Girocco::Config::posix_sh_bin; + defined($shbin) && $shbin ne "" or $shbin = "/bin/sh"; +} + +exit(&main(@ARGV)||0); + +my ($dryrun, $force, $quiet); + +sub die_usage { + pod2usage(-exitval => 2); +} + +sub do_help { + pod2usage(-verbose => 2, -exitval => 0); +} + +sub do_version { + print basename($0), " version ", $VERSION, "\n"; + exit 0; +} + +my ($dmode, $dperm, $drwxmode, $fmode, $fmodeoct, $fperm, $wall); +BEGIN { + $dmode=02775; + $dperm='drwxrwsr-x'; + $drwxmode='ug+rwx,o+rx'; + $fmode=0664; + $fmodeoct='0664'; + $fperm='-rw-rw-r--'; + $wall=0; +} + +my $owning_group_id; + +sub main { + local *ARGV = \@_; + my ($help, $version); + + umask 002; + close(DATA) if fileno(DATA); + Getopt::Long::Configure('bundling'); + GetOptions( + 'help|h' => sub {do_help}, + 'version|V' => sub {do_version}, + 'dry-run|n' => \$dryrun, + 'quiet|q' => \$quiet, + 'force|f' => \$force, + ) or die_usage; + $dryrun and $quiet = 0; + + -f jailed_file("/etc/group") or + die "Girocco group file not found: " . jailed_file("/etc/group") . "\n"; + + if (!defined($Girocco::Config::owning_group) || $Girocco::Config::owning_group eq "") { + die "\$Girocco::Config::owning_group unset, refusing to run without --force\n" unless $force; + $dmode=02777; + $dperm='drwxrwsrwx'; + $drwxmode='a+rwx'; + $fmode=0666; + $fmodeoct='0666'; + $fperm='-rw-rw-rw-'; + $wall=1; + warn "Mode 666 in effect\n" unless $quiet; + } elsif (($owning_group_id = scalar(getgrnam($Girocco::Config::owning_group))) !~ /^\d+$/) { + die "\$Girocco::Config::owning_group invalid ($Girocco::Config::owning_group), refusing to run\n"; + } + + my @allprojs = Girocco::Project::get_full_list; + my @projects = (); + + my $root = $Girocco::Config::reporoot; + $root or die "\$Girocco::Config::reporoot is invalid\n"; + $root =~ s,/+$,,; + $root ne "" or $root = "/"; + $root = realpath($root); + if (@ARGV) { + my %projnames = map {($_ => 1)} @allprojs; + foreach (@ARGV) { + s,/+$,,; + $_ or $_ = "/"; + -d $_ and $_ = realpath($_); + s,^\Q$root\E/,,; + s,\.git$,,; + if (!exists($projnames{$_})) { + warn "$_: unknown to Girocco (not in etc/group)\n" + unless $quiet; + next; + } + push(@projects, $_); + } + } else { + @projects = sort {lc($a) cmp lc($b)} @allprojs; + } + + my $bad = 0; + foreach (@projects) { + my $projdir = "$root/$_.git"; + if (! -d "$projdir") { + warn "$_: does not exist -- skipping\n" unless $quiet; + next; + } + if (!is_git_dir($projdir)) { + warn "$_: is not a .git directory -- skipping\n" unless $quiet; + next; + } + if (-e "$projdir/.noconfig") { + warn "$_: found .noconfig -- skipping\n" unless $quiet; + next; + } + process_one_project($_, $projdir) or $bad = 1; + } + + return $bad ? 1 : 0; +} + +my (@mkdirs, @mkfiles); +my (@fixdpermsdirs, @fixdpermsrwx, @fixfpermsfiles, @fixfpermsdirs); +BEGIN { + @mkdirs = qw(refs info hooks ctags htmlcache bundles reflogs objects objects/info); + @mkfiles = qw(config info/lastactivity); + @fixdpermsdirs = qw(. refs info ctags htmlcache bundles reflogs objects objects/info); + @fixdpermsrwx = qw(refs objects); + @fixfpermsfiles = qw(HEAD config description packed-refs README.html info/lastactivity + info/alternates info/http-alternates info/packs); + @fixfpermsdirs = qw(ctags); +} + +my (@boolvars, @falsevars, @false0vars, @truevars); +BEGIN { + @boolvars = qw(gitweb.statusupdates); + @falsevars = qw(core.ignorecase receive.denynonfastforwards); + @false0vars = qw(gc.auto receive.autogc); + @truevars = qw(receive.updateserverinfo repack.writebitmaps transfer.fsckobjects); +} + +my $hdr; + +sub defval($$) { + return defined($_[0]) ? $_[0] : $_[1]; +} + +sub process_one_project +{ + my ($proj, $projdir) = @_; + my $bad = 0; + my $reallybad = 0; + $hdr = 0; + do { + if (! -d "$projdir/$_") { + if (-e "$projdir/$_") { + warn "$proj: bypassing project, exists but not directory: $_\n" unless $quiet; + $reallybad = $bad = 1; + last; + } else { + do_mkdir($proj, $projdir, $_) or $bad = 1, last; + } + } + } foreach (@mkdirs); + return 0 if $reallybad; + + $ENV{PROJDIR} = $projdir; + -d "$projdir/$_" && check_dperm($proj, $projdir, $_) or $bad = 1 foreach (@fixdpermsdirs); + my @dirs = split(/\n+/, qx(cd "\$PROJDIR" && + exec find @fixdpermsrwx -xdev -type d \\( ! -path "objects/??" -o -prune \\) ! -perm -$drwxmode -print 2>/dev/null + )||""); + change_dpermrwx($proj, $projdir, $_) or $bad = 1 foreach (@dirs); + @dirs = split(/\n+/, qx(cd "\$PROJDIR" && + exec find . -xdev -type d \\( ! -path "./objects/??" -o -prune \\) ! -perm -a+rx -print + )||""); + change_dpermrx($proj, $projdir, $_) or $bad = 1 foreach (@dirs); + + do { + if (-e "$projdir/$_") { + if (! -f "$projdir/$_") { + warn "$proj: bypassing project, exists but not file: $_\n" unless $quiet; + $reallybad = $bad = 1; + last; + } + } else { + my $result = "(dryrun)"; + if (!$dryrun) { + $result = ""; + my $tf; + open($tf, '>', "$projdir/$_") && close ($tf) or $result = "FAILED", $bad = 1; + } + pmsg($proj, "$_: created", $result) unless $quiet; + } + } foreach(@mkfiles); + return 0 if $reallybad; + + $dryrun || check_fperm config or $bad = 1; + my $config = read_config_file_hash("$projdir/config", !$quiet); + if (!defined($config)) { + warn "$proj: could not read config file -- skipping\n" unless $quiet; + return 0; + } + + my $do_config = sub { + my ($item, $val) = @_; + my $oldval = defval($config->{$item},""); + my $result = "(dryrun)"; + if (!$dryrun) { + $result = ""; + system($Girocco::Config::git_bin, "config", "--file", "$projdir/config", "--replace-all", $item, $val) == 0 or + $result = "FAILED", $bad = 1; + } + if (!exists($config->{$item})) { + pmsg($proj, "config $item: created \"$val\"", $result) unless $quiet; + } else { + pmsg($proj, "config $item: \"$oldval\" -> \"$val\"", $result) unless $quiet; + } + }; + my $do_config_unset = sub { + my ($item, $msg) = @_; + defined($msg) or $msg = ""; + $msg eq "" or $msg = " " . $msg; + my $oldval = defval($config->{$item},""); + my $result = "(dryrun)"; + if (!$dryrun) { + $result = ""; + system($Girocco::Config::git_bin, "config", "--file", "$projdir/config", "--unset-all", $item) == 0 or + $result = "FAILED", $bad = 1; + } + pmsg($proj, "config $item: removed$msg \"$oldval\"", $result) unless $quiet; + }; + + my $cmplvl = defval($config->{'core.compression'},""); + if ($cmplvl !~ /^-?\d+$/ || $cmplvl < -1 || $cmplvl > 9 || "" . (0 + $cmplvl) ne "" . $cmplvl) { + pmsg($proj, "WARNING: replacing invalid core.compression value: \"$cmplvl\"") unless $cmplvl eq "" || $quiet; + $cmplvl = ""; + } elsif ($cmplvl != 5) { + pmsg($proj, "WARNING: suboptimal core.compression value left unchanged: \"$cmplvl\"") unless $quiet; + } + $cmplvl ne "" or &$do_config('core.compression', 5); + my $grpshr = defval($config->{'core.sharedrepository'},""); + if ($grpshr eq "" || (valid_bool($grpshr) && !git_bool($grpshr))) { + &$do_config('core.sharedrepository', 1); + } elsif (!(valid_bool($grpshr) && git_bool($grpshr))) { + pmsg($proj, "WARNING: odd core.sharedrepository value left unchanged: \"$grpshr\""); + } + if (git_bool($config->{'core.bare'})) { + my $setlaru = 1; + my $laru = $config->{'core.logallrefupdates'}; + if (defined($laru)) { + if (valid_bool($laru)) { + $setlaru = 0; + if (git_bool($laru)) { + pmsg($proj, "WARNING: core.logallrefupdates is true (left unchanged)") unless $quiet; + } + } else { + pmsg($proj, "WARNING: replacing non-boolean core.logallrefupdates value") unless $quiet; + } + } + !$setlaru or &$do_config('core.logallrefupdates', 'false'); + } else { + pmsg($proj, "WARNING: core.bare is not true (left unchanged)") unless $quiet; + } + defval($config->{'transfer.unpacklimit'},"") eq "1" or &$do_config('transfer.unpacklimit', 1); + lc(defval($config->{'receive.denydeletecurrent'},"")) eq "warn" or &$do_config('receive.denydeletecurrent', 'warn'); + do { + !exists($config->{$_}) || valid_bool(defval($config->{$_},"")) or &$do_config_unset($_, "(not a boolean)"); + } foreach (@boolvars); + do { + (valid_bool(defval($config->{$_},"")) && !git_bool($config->{$_})) or &$do_config($_, "false"); + } foreach (@falsevars); + do { + (valid_bool(defval($config->{$_},"")) && !git_bool($config->{$_})) or &$do_config($_, 0); + } foreach (@false0vars); + do { + (valid_bool(defval($config->{$_},"")) && git_bool($config->{$_})) or &$do_config($_, "true"); + } foreach (@truevars); + + if (defined($Girocco::Config::owning_group) && $Girocco::Config::owning_group ne "") { + my @items = split(/\n+/, qx(cd "\$PROJDIR" && + exec find . -xdev \\( -type d -o -type f \\) ! -group $Girocco::Config::owning_group -print + )||""); + change_group($proj, $projdir, $_) or $bad = 1 foreach (@items); + } + foreach (@fixfpermsfiles) { + if (-e "$projdir/$_") { + if (! -f "$projdir/$_") { + warn "$proj: bypassing project, exists but not file: $_\n" unless $quiet; + $reallybad = $bad = 1; + last; + } + check_fperm($proj, $projdir, $_) or $bad = 1; + } + } + return 0 if $reallybad; + + my @files = split(/\n+/, qx(cd "\$PROJDIR" && + exec find @fixfpermsdirs -xdev -type f ! -perm $fmodeoct -print 2>/dev/null + )||""); + check_fperm($proj, $projdir, $_) or $bad = 1 foreach (@files); + @files = split(/\n+/, qx(cd "\$PROJDIR" && + exec find . -xdev -type f ! -perm -a+r -print + )||""); + check_fpermr($proj, $projdir, $_) or $bad = 1 foreach (@files); + @files = split(/\n+/, qx(cd "\$PROJDIR" && + exec find . -xdev -type d \\( -path ./hooks -o -path ./mob/hooks \\) -prune -o -type f -perm +a+x -print + )||""); + check_fpermnox($proj, $projdir, $_) or $bad = 1 foreach (@files); + + my $bu = defval($config->{'gitweb.baseurl'},""); + if (-e "$projdir/.nofetch") { + $bu eq "" or pmsg($proj, "WARNING: .nofetch exists but gitweb.baseurl is not empty ($bu)") unless $quiet; + } else { + $bu ne "" or pmsg($proj, "WARNING: gitweb.baseurl is empty and .nofetch does not exist") unless $quiet; + } + + return !$bad; +} + +sub do_mkdir +{ + my ($proj, $projdir, $subdir) = @_; + my $result = ""; + if (!$dryrun) { + mkdir("$projdir/$subdir") && -d "$projdir/$subdir" or $result = "FAILED"; + } else { + $result = "(dryrun)"; + } + pmsg($proj, "$subdir/: created", $result); + return $result ne "FAILED"; +} + +sub check_dperm { + my ($proj, $projdir, $subdir) = @_; + my $oldmode = (stat("$projdir/$subdir"))[2]; + if (!defined($oldmode) || $oldmode eq "") { + warn "chmod: $projdir/$subdir: No such file or directory\n" unless $quiet; + return 0; + } + my $newmode = ($oldmode & ~07777) | $dmode; + $newmode == $oldmode and return 1; + my $result = ""; + if (!$dryrun) { + if (!chmod($newmode & 07777, "$projdir/$subdir")) { + $result = "FAILED"; + warn "chmod: $projdir/$subdir: $!\n" unless $quiet; + } + } else { + $result = "(dryrun)"; + } + pmsg($proj, "$subdir/:", get_mode_perm($oldmode), '->', get_mode_perm($newmode), $result); + return $result ne "FAILED"; +} + +sub change_dpermrwx { + my ($proj, $projdir, $subdir) = @_; + my $oldmode = (stat("$projdir/$subdir"))[2]; + if (!defined($oldmode) || $oldmode eq "") { + warn "chmod: $projdir/$subdir: No such file or directory\n" unless $quiet; + return 0; + } + my $newmode = $oldmode | ($wall ? 0777 : 0775); + $newmode == $oldmode and return 1; + my $result = ""; + if (!$dryrun) { + if (!chmod($newmode & 07777, "$projdir/$subdir")) { + $result = "FAILED"; + warn "chmod: $projdir/$subdir: $!\n" unless $quiet; + } + } else { + $result = "(dryrun)"; + } + pmsg($proj, "$subdir/:", get_mode_perm($oldmode), '->', get_mode_perm($newmode), $result); + return $result ne "FAILED"; +} + +sub change_dpermrx { + my ($proj, $projdir, $subdir) = @_; + my $oldmode = (stat("$projdir/$subdir"))[2]; + if (!defined($oldmode) || $oldmode eq "") { + warn "chmod: $projdir/$subdir: No such file or directory\n" unless $quiet; + return 0; + } + my $newmode = $oldmode | 0555; + $newmode == $oldmode and return 1; + my $result = ""; + if (!$dryrun) { + if (!chmod($newmode & 07777, "$projdir/$subdir")) { + $result = "FAILED"; + warn "chmod: $projdir/$subdir: $!\n" unless $quiet; + } + } else { + $result = "(dryrun)"; + } + pmsg($proj, "$subdir/:", get_mode_perm($oldmode), '->', get_mode_perm($newmode), $result); + return $result ne "FAILED"; +} + +sub check_fperm { + my ($proj, $projdir, $file) = @_; + my $oldmode = (stat("$projdir/$file"))[2]; + if (!defined($oldmode) || $oldmode eq "") { + warn "chmod: $projdir/$file: No such file or directory\n" unless $quiet; + return 0; + } + my $newmode = ($oldmode & ~07777) | $fmode; + $newmode == $oldmode and return 1; + my $result = ""; + if (!$dryrun) { + if (!chmod($newmode & 07777, "$projdir/$file")) { + $result = "FAILED"; + warn "chmod: $projdir/$file: $!\n" unless $quiet; + } + } else { + $result = "(dryrun)"; + } + pmsg($proj, "$file:", get_mode_perm($oldmode), '->', get_mode_perm($newmode), $result); + return $result ne "FAILED"; +} + +sub check_fpermr { + my ($proj, $projdir, $file) = @_; + my $oldmode = (stat("$projdir/$file"))[2]; + if (!defined($oldmode) || $oldmode eq "") { + warn "chmod: $projdir/$file: No such file or directory\n" unless $quiet; + return 0; + } + my $newmode = $oldmode | 0444; + $newmode == $oldmode and return 1; + my $result = ""; + if (!$dryrun) { + if (!chmod($newmode & 07777, "$projdir/$file")) { + $result = "FAILED"; + warn "chmod: $projdir/$file: $!\n" unless $quiet; + } + } else { + $result = "(dryrun)"; + } + pmsg($proj, "$file:", get_mode_perm($oldmode), '->', get_mode_perm($newmode), $result); + return $result ne "FAILED"; +} + +sub check_fpermnox { + my ($proj, $projdir, $file) = @_; + my $oldmode = (stat("$projdir/$file"))[2]; + if (!defined($oldmode) || $oldmode eq "") { + warn "chmod: $projdir/$file: No such file or directory\n" unless $quiet; + return 0; + } + my $newmode = $oldmode & ~0111; + $newmode == $oldmode and return 1; + my $result = ""; + if (!$dryrun) { + if (!chmod($newmode & 07777, "$projdir/$file")) { + $result = "FAILED"; + warn "chmod: $projdir/$file: $!\n" unless $quiet; + } + } else { + $result = "(dryrun)"; + } + pmsg($proj, "$file:", get_mode_perm($oldmode), '->', get_mode_perm($newmode), $result); + return $result ne "FAILED"; +} + +sub change_group { + my ($proj, $projdir, $item) = @_; + my @info = stat("$projdir/$item"); + if (@info < 6 || $info[2] eq "" || $info[4] eq "" || $info[5] eq "") { + warn "chgrp: $projdir/$item: No such file or directory\n" unless $quiet; + return 0; + } + $info[5] == $owning_group_id and return 1; + my $result = ""; + if (!$dryrun) { + if (!chown($info[4], $owning_group_id, "$projdir/$item")) { + $result = "FAILED"; + warn "chgrp: $projdir/$item: $!\n" unless $quiet; + } elsif (!chmod($info[2] & 07777, "$projdir/$item")) { + $result = "FAILED"; + warn "chmod: $projdir/$item: $!\n" unless $quiet; + } + } else { + $result = "(dryrun)"; + } + my $isdir = ((($info[2] >> 12) & 017) == 004) ? '/' : ''; + pmsg($proj, "$item$isdir: group", get_grp_nam($info[5]), '->', $Girocco::Config::owning_group, $result); + return $result ne "FAILED"; +} + +my $wrote; BEGIN {$wrote = ""} +sub pmsg { + my $proj = shift; + my $msg = join(" ", @_); + $msg =~ s/\s+$//; + my $prefix = ""; + if (!$hdr) { + $prefix = $wrote . $proj . ":\n"; + $hdr = 1; + } + print $prefix, " ", join(' ', @_), "\n"; + $wrote = "\n"; +} + +my %ftypes; +BEGIN {%ftypes = ( + 000 => '?', + 001 => 'p', + 002 => 'c', + 003 => '?', + 004 => 'd', + 005 => '?', + 006 => 'b', + 007 => '?', + 010 => '-', + 011 => '?', + 012 => 'l', + 013 => '?', + 014 => 's', + 015 => '?', + 016 => 'w', + 017 => '?' +)} +my %fperms; +BEGIN {%fperms = ( + 0 => '---', + 1 => '--x', + 2 => '-w-', + 3 => '-wx', + 4 => 'r--', + 5 => 'r-x', + 6 => 'rw-', + 7 => 'rwx' +)} + +sub get_mode_perm { + my $mode = $_[0]; + my $str = $ftypes{($mode >> 12) & 017} . + $fperms{($mode >> 6) & 7} . + $fperms{($mode >> 3) & 7} . + $fperms{$mode & 7}; + substr($str,3,1) = ($mode & 0100) ? 's' : 'S' if $mode & 04000; + substr($str,6,1) = ($mode & 0010) ? 's' : 'S' if $mode & 02000; + substr($str,9,1) = ($mode & 0001) ? 't' : 'T' if $mode & 01000; + return $str; +} + +sub get_perm { + my $mode = (stat($_[0]))[2]; + defined($mode) or return '??????????'; + return get_mode_perm($mode); +} + +sub get_grp_nam { + my $grpid = $_[0]; + defined($grpid) or return '?'; + my $grpnm = scalar(getgrgid($grpid)); + return defined($grpnm) && $grpnm ne "" ? $grpnm : $grpid; +} + +sub get_grp { + my $grp = (stat($_[0]))[5]; + defined($grp) or return '?'; + return get_grp_nam($grp); +} + +__END__ + +=head1 NAME + +update-all-config.pl - Update all projects' config settings + +=head1 SYNOPSIS + +update-all-config.pl [] []... + + Options: + -h | --help detailed instructions + -V | --version show version + -n | --dry-run show what would be done but don't do it + -f | --force run without a Config.pm owning_group + -q | --quiet suppress change messages + + if given, only operate on these projects + +=head1 OPTIONS + +=over 8 + +=item B<-h>, B<--help> + +Print the full description of update-all-config.pl's options. + +=item B<-V>, B<--version> + +Print the version of update-all-config.pl. + +=item B<-n>, B<--dry-run> + +Do not actually make any changes, just show what would be done without +actually doing it. + +=item B<-q>, B<--quiet> + +Suppress the messages about what's actually being changed. This option +is ignored if B<--dry-run> is in effect. + +The warnings about missing and unknown-to-Girocco projects are also +suppressed by this option. + +=item B<-f>, B<--force> + +Allow running without a $Girocco::Config::owning_group set. This is not +recommended as it results in world-writable items being used (instead of +just world-readable). + +=item B<> + +If no project names are specified then I projects are processed. + +If one or more project names are specified then only those projects are +processed. Specifying non-existent projects produces a warning for them, +but the rest of the projects specified will still be processed. + +Each B may be either a full absolute path starting with +$Girocco::Config::reporoot or just the project name part with or without +a trailing C<.git>. + +Any explicitly specified projects that do exist but are not known to +Girocco will be skipped (with a warning). + +=back + +=head1 DESCRIPTION + +Inspect the C files of Girocco projects (i.e. $GIT_DIR/config) and +look for anomalies and out-of-date settings. + +Additionally check the existence and permissions on various files and +directories in the project. + +If an explicity specified project is located under $Girocco::Config::reporoot +but is not actually known to Girocco (i.e. it's not in the etc/group file) +then it will be skipped. + +By default, any anomalies or out-of-date settings will be corrected with a +message to that effect. However using B<--dry-run> will only show the +correction(s) which would be made without making them and B<--quiet> will make +the correction(s) without any messages. + +Any projects that have a C<$GIT_DIR/.noconfig> file are always skipped (with a +message unless B<--quiet> is used). + +=cut diff --git a/toolbox/update-all-config.sh b/toolbox/update-all-config.sh dissimilarity index 99% index 6b20993..5a415db 100755 --- a/toolbox/update-all-config.sh +++ b/toolbox/update-all-config.sh @@ -1,377 +1,7 @@ -#!/bin/sh - -# Update all out-of-date config in all current projects and add missing bits - -# If one or more project names are given, just update those instead - -# update-all-config [--dry-run] [projname]... - -set -e - -. @basedir@/shlib.sh - -force= -dryrun= -[ "$1" != "--force" ] && [ "$1" != "-f" ] || { force=1; shift; } -[ "$1" != "--dry-run" ] && [ "$1" != "-n" ] || { dryrun=1; shift; } -case "$1" in -*) echo "Invalid options: $1" >&2; exit 1;; esac - -if is_root; then - printf '%s\n' "$(basename "$0"): refusing to run as root -- bad things would happen" - exit 1 -fi - -umask 002 -dmode=02775 -dperm='drwxrwsr-x' -drwxmode='ug+rwx,o+rx' -fmode=0664 -fperm='-rw-rw-r--' -wall= -if [ -z "$cfg_owning_group" ]; then - if [ -z "$force" ]; then - echo "No owning_group set, refusing to run without --force" >&2 - exit 1 - fi - dmode=02777 - dperm='drwxrwsrwx' - fmode=0666 - fperm='-rw-rw-rw-' - drwxmode='a+rwx' - wall=1 -fi - -get_perm() { - ls -ldH "$1" | cut -d ' ' -f 1 -} - -get_grp() { - ls -ldH "$1" | awk '{print $4}' -} - -wrote= -pmsg() { - [ -n "$hdr" ] || { hdr=1; [ -z "$wrote" ] || echo ""; echo "$proj:"; } - printf ' %s\n' "$*" - wrote=1 -} - -change_dperm() { - result= - oldperm="$(get_perm "$1")" - [ -n "$dryrun" ] || chmod $dmode "$1" 2>/dev/null || result=$? - pmsg "$1/:" "$oldperm" '->' "$dperm" ${result:+FAILED!} ${dryrun:+(dryrun)} -} - -change_dpermrwx() { - result= - oldperm="$(get_perm "$1")" - if [ -n "$wall" ]; then - newperm="drwxrwxrwx" - else - scratch="${oldperm#????????}" - newperm="drwxrwxr${scratch%?}x" - fi - [ -n "$dryrun" ] || [ "$oldperm" = "$newperm" ] || chmod $drwxmode "$1" 2>/dev/null || result=$? - pmsg "$1/:" "$oldperm" '->' "$newperm" ${result:+FAILED!} ${dryrun:+(dryrun)} -} - -change_dpermrx() { - result= - oldperm="$(get_perm "$1")" - scratch="${oldperm#??}" - newperm="dr${scratch%???????}xr" - scratch="${scratch#???}" - newperm="$newperm${scratch%????}xr" - scratch="${scratch#???}" - newperm="$newperm${scratch%?}x" - [ -n "$dryrun" ] || [ "$oldperm" = "$newperm" ] || chmod a+rx "$1" 2>/dev/null || result=$? - pmsg "$1/:" "$oldperm" '->' "$newperm" ${result:+FAILED!} ${dryrun:+(dryrun)} -} - -change_group() { - result= - oldgrp="$(get_grp "$1")" - [ -n "$dryrun" ] || chgrp "$cfg_owning_group" "$1" 2>/dev/null || result=$? - isdir= - ! [ -d "$1" ] || isdir='/' - pmsg "$1$isdir:" group "$oldgrp" '->' "$cfg_owning_group" ${result:+FAILED!} ${dryrun:+(dryrun)} -} - -change_fperm() { - result= - oldperm="$(get_perm "$1")" - msg= - [ "$oldperm" = "$fperm" ] || { msg=1; [ -n "$dryrun" ]; } || chmod $fmode "$1" 2>/dev/null || result=$? - [ -z "$msg" ] || pmsg "$1:" "$oldperm" '->' "$fperm" ${result:+FAILED!} ${dryrun:+(dryrun)} -} - -change_fpermr() { - result= - oldperm="$(get_perm "$1")" - scratch="${oldperm#??}" - newperm="-r${scratch%??????}" - scratch="${scratch#???}" - newperm="${newperm}r${scratch%???}" - scratch="${scratch#???}" - newperm="${newperm}r$scratch" - [ -n "$dryrun" ] || [ "$oldperm" = "$newperm" ] || chmod a+r "$1" 2>/dev/null || result=$? - pmsg "$1:" "$oldperm" '->' "$newperm" ${result:+FAILED!} ${dryrun:+(dryrun)} -} - -change_fpermnox() { - result= - oldperm="$(get_perm "$1")" - newperm="${oldperm%???????}-" - scratch="${oldperm#????}" - newperm="${newperm}${scratch%????}-" - scratch="${scratch#???}" - newperm="${newperm}${scratch%?}-" - [ -n "$dryrun" ] || [ "$oldperm" = "$newperm" ] || chmod a-x "$1" 2>/dev/null || result=$? - pmsg "$1:" "$oldperm" '->' "$newperm" ${result:+FAILED!} ${dryrun:+(dryrun)} -} - -change_fpermx() { - result= - oldperm="$(get_perm "$1")" - newperm="${oldperm%???????}x" - scratch="${oldperm#????}" - newperm="${newperm}${scratch%????}x" - scratch="${scratch#???}" - newperm="${newperm}${scratch%?}x" - [ -n "$dryrun" ] || [ "$oldperm" = "$newperm" ] || chmod a+x "$1" 2>/dev/null || result=$? - pmsg "$1:" "$oldperm" '->' "$newperm" ${result:+FAILED!} ${dryrun:+(dryrun)} -} - -do_mkdir() { - result= - [ -n "$dryrun" ] || mkdir "$1" 2>/dev/null || [ -d "$1" ] || result=1 - pmsg "$1/:" created ${result:+FAILED!} ${dryrun:+(dryrun)} -} - -nl=' -' -readconfiglist() { - if [ -n "$dryrun" ] && ! [ -e config ]; then - configlist= - else - configlist="$(git config --list --file config)" - [ -z "$configlist" ] || configlist="$configlist$nl" - fi -} - -configitem() { - while read -r _item; do - case "$_item" in "$1"=*) - printf '%s' "${_item#*=}" - return 0 - esac - done <<-EOT - $configlist - EOT - return 1 -} - -isbooltrue() { - case "$1" in 1|[Tt][Rr][Uu][Ee]|[Yy][Ee][Ss]|[Oo][Nn]) return 0; esac - return 1 -} - -isboolfalse() { - case "$1" in 0|[Ff][Aa][Ll][Ss][Ee]|[Nn][Oo]|[Oo][Ff][Ff]) return 0; esac - return 1 -} - -isbool() { - isbooltrue "$1" || isboolfalse "$1" || return 1 -} - -do_config() { - unset= - msg= - [ "$1" != "--unset" ] || { unset=1; shift; _x="$1"; shift; msg="$*"; set -- "$_x"; } - item="$1"; shift - result= - existsnot= - oldval="$(configitem "$item")" || existsnot=1 - ccmd=unset - arg="$item" - [ -n "$existsnot" ] || [ -z "$unset" ] || [ $(printf '%s' "$configlist" | grep "^${item%%.*}\.[^.][^.]*=" | wc -l) -ne 1 ] || { ccmd="remove-section"; arg="${item%%.*}"; } - [ -n "$dryrun" ] || { [ -n "$unset" ] && [ -n "$existsnot" ]; } || git config --file config ${unset:+--$ccmd} "$arg" "$@" 2>/dev/null || result=1 - if [ -n "$existsnot" ]; then - [ -n "$unset" ] || pmsg "config $item:" created "\"$*\"" ${result:+FAILED!} ${dryrun:+(dryrun)} - else - if [ -n "$unset" ]; then - pmsg "config $item:" removed $msg "\"$oldval\"" ${result:+FAILED!} ${dryrun:+(dryrun)} - else - pmsg "config $item:" "\"$oldval\"" '->' "\"$*\"" ${result:+FAILED!} ${dryrun:+(dryrun)} - fi - fi -} - -mkdirs='refs info hooks ctags htmlcache bundles reflogs objects objects/info' -mkfiles='config info/lastactivity' -fixdpermsdirs='. refs info ctags htmlcache bundles reflogs objects objects/info' -fixdpermsrwx='refs objects' -fixfpermsfiles='HEAD config description packed-refs README.html info/lastactivity' -fixfpermsfiles="$fixfpermsfiles info/alternates info/http-alternates info/packs" -fixfpermsdirs='ctags' - -boolvars='gitweb.statusupdates' -falsevars='core.ignorecase receive.denynonfastforwards' -false0vars='gc.auto receive.autogc' -truevars='receive.updateserverinfo repack.writebitmaps transfer.fsckobjects' - -base="${cfg_reporoot%/}" -cmd='cut -d : -f 1 <"$cfg_chroot/etc/group" | grep -v ^_repo' -[ $# -eq 0 ] || cmd='printf "%s\n" "$@"' -eval "$cmd" | -( - while read -r proj; do - proj="${proj#$base/}" - proj="${proj%.git}" - projdir="$base/$proj.git" - [ -d "$projdir" ] || { echo "$proj: does not exist -- skipping"; continue; } - is_git_dir "$projdir" || { echo "$proj: is not a .git directory -- skipping"; continue; } - ! [ -e "$projdir/.noconfig" ] || { echo "$proj: found .noconfig -- skipping"; continue; } - cd "$projdir" - bad= - hdr= - for mdir in $mkdirs; do - if ! [ -d "$mdir" ]; then - if [ -e "$mdir" ]; then - echo "$proj: bypassing project, exists but not directory: $mdir" >&2 - bad=1 - break - fi - do_mkdir "$mdir" - fi - done - [ -z "$bad" ] || continue - while read -r fixdir; do - [ -z "$fixdir" ] || change_dperm "$fixdir" - done <<-EOT - $(find $fixdpermsdirs -xdev -maxdepth 0 -type d ! -perm $dmode -print 2>/dev/null) - EOT - while read -r fixdir; do - [ -z "$fixdir" ] || change_dpermrwx "$fixdir" - done <<-EOT - $(find $fixdpermsrwx -xdev -type d ! -perm -$drwxmode -print 2>/dev/null) - EOT - while read -r fixdir; do - [ -z "$fixdir" ] || change_dpermrx "$fixdir" - done <<-EOT - $(find . -xdev -type d ! -perm -a+rx -print) - EOT - for mkfile in $mkfiles; do - if [ -e "$mkfile" ]; then - if ! [ -f "$mkfile" ]; then - echo "$proj: bypassing project, exists but not file: $mkfile" >&2 - bad=1 - break - fi - else - result= - [ -n "$dryrun" ] || { >"$mkfile"; } 2>/dev/null || result=1 - pmsg "$mkfile:" created ${result:+FAILED!} ${dryrun:+(dryrun)} - fi - done - [ -z "$bad" ] || continue - [ -n "$dryrun" ] || change_fperm config - readconfiglist - cmplvl="$(configitem core.compression)" || : - case "$cmplvl" in - "5"|"") - ;; - "-1"|"0"|"1"|"2"|"3"|"4"|"6"|"7"|"8"|"9") - pmsg "WARNING: suboptimal core.compression value left unchanged: \"$cmplvl\"" - ;; - *) - pmsg "WARNING: replacing invalid core.compression value" - cmplvl= - ;; - esac - [ -n "$cmplvl" ] || do_config core.compression 5 - grpshr="$(configitem core.sharedrepository)" || : - if [ -z "$grpshr" ] || isboolfalse "$grpshr"; then - do_config core.sharedrepository 1 - elif ! isbooltrue "$grpshr"; then - pmsg "WARNING: odd core.sharedrepository value left unchanged: \"$grpshr\"" - fi - if isbooltrue "$(configitem core.bare || :)"; then - setlaru=1 - if laru="$(configitem core.logallrefupdates)"; then - if isbool "$laru"; then - setlaru= - isboolfalse "$laru" || - pmsg "WARNING: core.logallrefupdates is true (left unchanged)" - else - pmsg "WARNING: replacing non-boolean core.logallrefupdates value" - fi - fi - [ -z "$setlaru" ] || do_config core.logallrefupdates false - else - pmsg "WARNING: core.bare is not true (left unchanged)" - fi - [ "$(configitem transfer.unpacklimit || :)" = 1 ] || do_config transfer.unpacklimit 1 - case "$(configitem receive.denydeletecurrent || :)" in [Ww][Aa][Rr][Nn]) :;; *) - do_config receive.denydeletecurrent warn - esac - for bvar in $boolvars; do - ! old="$(configitem "$bvar")" || isbool "$old" || do_config --unset "$bvar" "(not a boolean)" - done - for fvar in $falsevars; do - old="$(configitem "$fvar")" || : - isboolfalse "$old" || do_config "$fvar" false - done - for fvar in $false0vars; do - old="$(configitem "$fvar")" || : - isboolfalse "$old" || do_config "$fvar" 0 - done - for tvar in $truevars; do - old="$(configitem "$tvar")" || : - isbooltrue "$old" || do_config "$tvar" true - done - if [ -n "$cfg_owning_group" ]; then - while read -r fixgrp; do - [ -z "$fixgrp" ] || change_group "$fixgrp" - done <<-EOT - $(find . -xdev \( -type d -o -type f \) ! -group $cfg_owning_group -print) - EOT - fi - for fixfile in $fixfpermsfiles; do - if [ -e "$fixfile" ]; then - if ! [ -f "$fixfile" ]; then - echo "$proj: bypassing project, exists but not file: $fixfile" >&2 - bad=1 - break - fi - change_fperm "$fixfile" - fi - done - [ -z "$bad" ] || continue - for fixfile in $(find $fixfpermsdirs -xdev -type f ! -perm $fmode -print 2>/dev/null); do - change_fperm "$fixfile" - done - while read -r fixfileread; do - [ -z "$fixfileread" ] || change_fpermr "$fixfileread" - done <<-EOT - $(find . -xdev -type f ! -perm -a+r -print) - EOT - while read -r fixfilenox; do - [ -z "$fixfilenox" ] || change_fpermnox "$fixfilenox" - done <<-EOT - $(find . -xdev -type d \( -path ./hooks -o -path ./mob/hooks \) -prune -o -type f -perm +a+x -print) - EOT - while read -r fixfilex; do - #case "$fixfilex" in ""|*.sample) :;; *) change_fpermx "$fixfilex"; esac - : - done <<-EOT - $(! [ -d hooks ] || find hooks -xdev -type f ! -perm -a+x -print - ! [ -d mob/hooks ] || find mob/hooks -xdev -type f ! -perm -a+x -print) - EOT - bu="$(configitem gitweb.baseurl)" || : - [ -n "$bu" ] || [ -e .nofetch ] || pmsg "WARNING: gitweb.baseurl is empty and .nofetch does not exist" - [ -z "$bu" ] || ! [ -e .nofetch ] || pmsg "WARNING: .nofetch exists but gitweb.baseurl is not empty" - done -) +#!/bin/sh +# +# update-all-config - Update all out-of-date config +# The old update-all-config.sh has been replaced by update-all-config.pl; +# it now simply runs that. + +exec @basedir@/toolbox/update-all-config.pl "$@" diff --git a/toolbox/update-all-projects.sh b/toolbox/update-all-projects.sh index 0d305e5..13555c4 100755 --- a/toolbox/update-all-projects.sh +++ b/toolbox/update-all-projects.sh @@ -26,7 +26,7 @@ if [ -z "$cfg_owning_group" ]; then fi mydir="$(cd "$(dirname "$0")" && pwd -P)" -required='update-all-config.sh update-all-hooks.sh' +required='update-all-config.pl update-all-hooks.sh' bad= for r in $required; do if ! [ -x "$mydir/$r" ]; then @@ -36,6 +36,6 @@ for r in $required; do done [ -z "$bad" ] || exit 1 echo "Running update-all-config..." -"$mydir/update-all-config.sh" ${force:+--force} ${dryrun:+--dry-run} "$@" +"$mydir/update-all-config.pl" ${force:+--force} ${dryrun:+--dry-run} "$@" echo "Running update-all-hooks..." "$mydir/update-all-hooks.sh" ${force:+--force} ${dryrun:+--dry-run} "$@" -- 2.11.4.GIT