#!/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($value)) || ($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 = getgrgid(0);}
die "$value is not a valid group: $!\n" unless ((getgrnam($value)) || (($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 "dir") || ($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-group") || ($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 "
\n\tmode $nice_mode\n\towner $pwuid\n\tgroup $grgid\n\n";
}
elsif (-f $_)
{
print CONFIG "\n\tmode $nice_mode\n\towner $pwuid\n\tgroup $grgid\n\n";
}
elsif (-l $_)
{
print CONFIG "\n\tmode $nice_mode\n\towner $pwuid\n\tgroup $grgid\n\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 $the_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_value\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_value\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 have 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_own $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{$test}{$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});
}
}
}
}
}
}
####
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
perms 0700
owner shane
group shane
perms 0744
owner joe
group baz
perms 0777
owner follow
group me