BuddhaNature has asked for the wisdom of the Perl Monks concerning the following question:
My main drive for this was so that I would put something like /etc into Subversion and then do "svn update"s on other machines to maintain consistency across sytems' /etc's. But there are a number of files which have to have very specific permissions so that they work correctly, but Subversion does not keep permissions info, so I wrote this script "permmy."
This is one of my first large-ish scripts and would like any constructive criticism I could get on it regarding errors and streamlining, especially errors (screwing up permissons on /etc can be a BadThing(tm)...). If you are interested check out the readmore...
#!/usr/bin/perl -w ### LAME BUG ALERT - program will barf if there ### are repeated elements in the conf file ### so be careful you don't "add" the same directory ### to a conf file that already has it in it use strict; use diagnostics; use File::Find (); use Getopt::Std; #use Sys::Syslog; use Config::General; die "Usage: permmy -[itcad] [directory] -f config-file\n" unless @ARGV >= 3; use vars qw/ $opt_t $opt_c $opt_d $opt_i $opt_f $opt_a/; getopts('tcdi:f:a:') or die "Usable options are -tcifa: $!\n"; my $dir_tree = $opt_i; my $add_tree = $opt_a; my $perms_config_file = $opt_f; use vars qw/ $conf %config/; if ($opt_t || $opt_d || $opt_c) { $conf = new Config::General( -ConfigFile => "$perms_config_file", -UseApacheInclude => "yes", -InterPolateVars => "yes" ); %config = $conf->getall; } sub return_mode { sprintf "%04o", (shift) & 07777; } ## checks for validity. Takes a type argument (m for mode, u for user ## and g for group) and the test value sub validator ($$) { my($type, $value) = @_; if ($type =~ /.*mode/) { die "$value is not a valid mode: $!\n" unless ((oct($value)) & +& (oct($value) <= 4095)); } elsif ($type =~ /.*owner/) { die "$value is not a valid user: $!\n" unless ((getpwnam($valu +e)) || ($value eq "root")); } elsif ($type =~ /.*group/) { if (($value eq "root") && ($^O =~ /bsd|darwin/)) {print "There + is no $value group on $^O, fix your config file!\n";} if (($value eq "wheel") && ($^O =~ /linux/)) {print "There is +no $value group in $^O, fix your config file!\n";} if (($value eq "root") || ($value eq "wheel")) {$value = getgr +gid(0);} die "$value is not a valid group: $!\n" unless ((getgrnam($val +ue)) || (($value eq "root") || ($value eq "wheel"))); } else { die "Validator complaint: something wrong with type $type and/ +or value $value\n"; } } sub configurator ($$$$) { my($conf_type, $conf_file, $directory, $wanted_ref) = @_; if ($conf_type eq "i") { open CONFIG, ">$conf_file" or die "Could not open config file $conf_file for writing: + $!\n"; } elsif ($conf_type eq "a") { open CONFIG, ">>$conf_file" or die "Could not open config file $conf_file for writing: + $!\n"; } use vars qw/*name *dir *prune/; *name = *File::Find::name; *dir = *File::Find::dir; *prune = *File::Find::prune; # Traverse desired filesystems File::Find::find({wanted => $wanted_ref}, $directory); exit; close CONFIG or die "Could not close config file $conf_file: $!\n" +; } sub configtest { foreach my $pri ( keys %config ) { die "$pri is not a valid descriptor: $!\n" unless (($pri eq "d +ir") || ($pri eq "file") || ($pri eq "link") || ($pri eq "area")); if ($pri eq "area") { foreach my $sec ( keys %{ $config{$pri} } ) { die "$sec does not exist: $!\n" unless (-e $sec); foreach my $ter (keys %{ $config{$pri}{$sec} } ) { die "$ter is not a valid type: $!\n" unless (($ter + eq "dir-mode") || ($ter eq "dir-owner") || ($ter eq "dir-group") || +($ter eq "file-mode") || ($ter eq "file-owner") || ($ter eq "file-gro +up") || ($ter eq "link-mode") || ($ter eq "link-owner") || ($ter eq " +link-group")); &validator($ter, $config{$pri}{$sec}{$ter}); } } } else { foreach my $sec (keys %{ $config{$pri} } ) { die "$sec does not exist: $!\n" unless (-e $sec); foreach my $ter (keys %{ $config{$pri}{$sec} } ) { die "$ter is not a valid type: $!\n" unless (($ter + eq "mode") || ($ter eq "owner") || ($ter eq "group")); &validator($ter, $config{$pri}{$sec}{$ter}); } } } } print "Syntax OK\n"; } sub wanted_ia { ((my $dev, my $ino, my $mode, my $nlink, my $uid, my $gid) = lstat +($_)); my $pwuid = getpwuid $uid; my $grgid = getgrgid $gid; my $nice_mode = &return_mode($mode); if (-d $_) { print CONFIG "<dir $name>\n\tmode $nice_mode\n\towner $pwuid\n +\tgroup $grgid\n</dir>\n"; } elsif (-f $_) { print CONFIG "<file $name>\n\tmode $nice_mode\n\towner $pwuid\ +n\tgroup $grgid\n</file>\n"; } elsif (-l $_) { print CONFIG "<link $name>\n\tmode $nice_mode\n\towner $pwuid\ +n\tgroup $grgid\n</link>\n"; } } sub change_mode ($$) { my($the_file, $change_value) = @_; ((my $dev, my $ino, my $mode, my $nlink, my $uid, my $gid) = lstat +($the_file)); my $c_nice_mode = &return_mode($mode); if (($change_value) && (oct($change_value) != oct($c_nice_mode))) { my $o_mode = oct($change_value); chmod($o_mode, $the_file) or warn "Could not change mode of $t +he_file: $!\n"; print "Changed mode of $the_file from $c_nice_mode to $change_ +value\n"; } } sub change_owner ($$) { my($the_file, $change_value) = @_; ((my $dev, my $ino, my $mode, my $nlink, my $uid, my $gid) = lstat +($the_file)); my $c_pwuid = getpwuid($uid); if (($change_value) && ($change_value ne $c_pwuid)) { my $num_uid = getpwnam($change_value); chown($num_uid, -1, $the_file) or warn "Could not change owner + of $the_file: $!\n"; print "Changed owner of $the_file from $c_pwuid to $change_val +ue\n"; } } sub change_group ($$) { my($the_file, $change_value) = @_; ((my $dev, my $ino, my $mode, my $nlink, my $uid, my $gid) = lstat +($the_file)); my $c_grgid = getgrgid($gid); if (($change_value) && ($change_value ne $c_grgid)) { if (($change_value eq "root") || ($change_value eq "wheel")) { +$change_value = getgrgid(0)} my $num_gid = getgrnam($change_value); chown(-1, $num_gid, $the_file) or warn "Could not change group + of $the_file: $!\n"; print "Changed group of $the_file from $c_grgid to $change_val +ue\n"; } } sub test_mode ($$) { my($the_file, $test_value) = @_; ((my $dev, my $ino, my $mode, my $nlink, my $uid, my $gid) = lstat +($the_file)); my $c_nice_mode = return_mode($mode); print "The file $the_file has a mode of $c_nice_mode but should ha +ve a mode of $test_value\n" unless ($c_nice_mode eq $test_value); } sub test_owner ($$) { my($the_file, $test_value) = @_; ((my $dev, my $ino, my $mode, my $nlink, my $uid, my $gid) = lstat +($the_file)); my $c_pwuid = getpwuid($uid); print "The file $the_file has an owner of $c_pwuid but should have + an owner of $test_value\n" unless ($c_pwuid eq $test_value); } sub test_group ($$) { my($the_file, $test_value) = @_; ((my $dev, my $ino, my $mode, my $nlink, my $uid, my $gid) = lstat +($the_file)); my $c_grgid = getgrgid($gid); print "The file $the_file has a group of $c_grgid but should have +a group of $test_value\n" unless ($c_grgid eq $test_value); } if ($opt_d) { configtest(); } elsif ($opt_i) { configurator("i", $perms_config_file, $dir_tree, \&wanted_ia); } elsif ($opt_a) { configurator("a", $perms_config_file, $add_tree, \&wanted_ia); } elsif ($opt_t) { configtest(); foreach my $test ( keys %config ) { foreach my $tester ( keys %{ $config{$test} } ) { if (!-e $tester) { print "$tester does not seem to exist any more\n"; next; } foreach my $testy ( keys %{ $config{$test}{$tester} } ) { if ($testy eq "mode") { test_mode($tester, $config{$test}{$tester}{$testy} +); } elsif ($testy eq "owner") { test_owner($tester, $config{$test}{$tester}{$testy +}); } elsif ($testy eq "group") { test_group($tester, $config{$test}{$tester}{$testy +}); } } } } } elsif ($opt_c) { configtest(); foreach my $test ( keys %config ) { if ($test eq "area") { foreach my $tester ( keys %{ $config{$test} } ) { if (!-e $tester) { print "$tester does not seem to exist any more\n"; next; } use vars qw/$d_mode $f_mode $l_mode $d_own $f_own $l_o +wn $d_grp $f_grp $l_grp/; $d_mode=$f_mode=$l_mode=$d_own=$f_own=$l_own=$d_grp=$f +_grp=$l_grp=0; foreach my $testy ( keys %{ $config{$test}{$tester} } +) { if ($testy eq "dir-mode") { $d_mode = $config{$tes +t}{$tester}{$testy} } elsif ($testy eq "file-mode") { $f_mode = $config{ +$test}{$tester}{$testy} } elsif ($testy eq "link-mode") { $l_mode = $config{ +$test}{$tester}{$testy} } elsif ($testy eq "dir-owner") { $d_own = $config{$ +test}{$tester}{$testy} } elsif ($testy eq "file-owner") { $f_own = $config{ +$test}{$tester}{$testy} } elsif ($testy eq "link-owner") { $l_own = $config{ +$test}{$tester}{$testy} } elsif ($testy eq "dir-group") { $d_grp = $config{$ +test}{$tester}{$testy} } elsif ($testy eq "file-group") { $f_grp = $config{ +$test}{$te\ster}{$testy} } elsif ($testy eq "link-group") { $l_grp = $config{ +$test}{$tester}{$testy} } } use vars qw/*name *dir *prune/; *name = *File::Find::name; *dir = *File::Find::dir; *prune = *File::Find::prune; # Traverse desired filesystems File::Find::find({wanted => \&wanted_c}, $tester); exit; sub wanted_c { if ((-d $_) && ($d_mode)) { change_mode($_, $d_mode); } elsif ((-d $_) && ($d_own)) { change_owner($_, $d_own); } elsif ((-d $_) && ($d_grp)) { change_group($_, $d_grp); } elsif ((-f $_) && ($f_mode)) { change_mode($_, $f_mode); } elsif ((-f $_) && ($f_own)) { change_owner($_, $f_own); } elsif ((-f $_) && ($f_grp)) { change_group($_, $f_grp); } elsif ((-l $_) && ($l_mode)) { change_mode($_, $l_mode); } elsif ((-l $_) && ($l_own)) { change_owner($_, $l_own); } elsif ((-l $_) && ($l_grp)) { change_group($_, $l_grp); } } } } else { foreach my $tester ( keys %{ $config{$test} } ) { if (!-e $tester) { print "$tester does not seem to exist any more\n"; next; } foreach my $testy2 ( keys %{ $config{$test}{$tester} } + ) elsif ((-l $_) && ($l_mode)) { change_mode($_, $l_mode); } elsif ((-l $_) && ($l_own)) { change_owner($_, $l_own); } elsif ((-l $_) && ($l_grp)) { change_group($_, $l_grp); } } } } else { foreach my $tester ( keys %{ $config{$test} } ) { if (!-e $tester) { print "$tester does not seem to exist any more\n"; next; } foreach my $testy2 ( keys %{ $config{$test}{$tester} } + ) { if ($testy2 eq "mode") { change_mode($tester, $config{$test}{$tester}{$ +testy2}); } elsif ($testy2 eq "owner") { change_owner($tester, $config{$test}{$tester}{ +$testy2}); } elsif ($testy2 eq "group") { change_group($tester, $config{$test}{$tester}{ +$testy2}); } } } } } }
Below is a sample config file, showing all the options:
<area /etc> dir-perms 0755 file-perms 0644 link-perms 0777 dir-owner root file-owner root link-owner root dir-group root file-group root link-group root </area> <dir /etc/foo> perms 0700 owner shane group shane </dir> <file /etc/bar.cfg> perms 0744 owner joe group baz </file> <link /etc/baz> perms 0777 owner follow group me </link>
The one kinda "weird" part is the "area" tag - this basically just means to give default permissions (recursively) to the directory and its contents.
Thanks for any help.
|
|---|