#!/usr/bin/perl -w ########################################################################### # TODO ########################################################################### # o tbd ########################################################################### use strict; use App::Options ( options => [qw(prefix remote remote_prefix remote_user remote_host op verbose)], option => { prefix => { description => "the directory path to be administered (generally an 'application root' directory)", }, remote => { description => "run the command remotely using 'ssh' rather than locally as the logged in user", }, remote_host => { description => "(for --remote option) which server(s) to run the prefixadmin on. Comma delimited list. (optionally user\@host:/pr/ef/ix)", }, remote_prefix => { description => "directory on remote machine to administer", }, remote_user => { description => "the username to run the remote command", }, op => { description => "list of operations to perform [fix] (check,fix)", }, username => { description => "the user name that a prefix should be shared as", }, group => { description => "the group name that a prefix should be shared as", }, verbose => { default => 0, description => "level of detail to print", }, }, ); $| = 1; { my (@op); if ($App::options{op}) { @op = split(/,/,$App::options{op}); } elsif ($#ARGV > -1) { @op = @ARGV; } else { @op = ("fix"); } if ($#op > -1) { my $admin = App::Options::PrefixAdmin->new(); foreach my $op (@op) { if ($op eq "fix") { $admin->fix(\%App::options); } else { print "Unknown operation [$op]\n"; } } } else { print "No operations specified\n"; } } package App::Options::PrefixAdmin; use File::Find; use Date::Format; use Fcntl ':mode'; sub new { my ($this) = @_; my $class = ref($this) || $this; my $self = {}; bless $self, $class; return($self); } sub fix { my ($self, $options) = @_; my ($path, $file, $cwd); my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks); my $verbose = $options->{verbose} || 0; my $prefix = $options->{prefix} || die "prefix not specified"; die "$prefix is not a directory" if (! -d $prefix); chdir($prefix) || die "Could not change directory to $prefix"; $path = "."; ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($path); if ($verbose >= 2) { printf("%3d %8d %10s %2d %5d %5d %6d %15d [%17s] %s\n", $dev, $ino, $self->format_mode($mode), $nlink, $uid, $gid, $rdev, $size, time2str("%Y-%m-%d %H:%M:%S", $mtime), $path); } my ($u_name, $u_pass, $u_uid, $u_gid, $u_quota, $u_comment, $u_gcos, $u_dir, $u_shell, $u_expire) = getpwuid($uid); print "Uname: $u_name UID: $u_uid\n" if ($verbose >= 2); my ($grp_name, $grp_passwd, $grp_gid, $grp_members) = getgrgid($gid); print "Gname: $grp_name GID: $grp_gid Members: $grp_members\n" if ($verbose >= 2); my ($shgrp_name,$shgrp_passwd,$shgrp_gid,$shgrp_members); my $shared_group = $options->{group}; if ($shared_group) { ($shgrp_name,$shgrp_passwd,$shgrp_gid,$shgrp_members) = getgrnam($shared_group); print "Shared Gname: $shgrp_name GID: $shgrp_gid Members: $shgrp_members\n" if ($verbose >= 2); } else { # if --group is not given on the command line, use the GID of the top level directory ($shgrp_name,$shgrp_passwd,$shgrp_gid,$shgrp_members) = getgrgid($gid); print "Shared Gname: $shgrp_name GID: $shgrp_gid Members: $shgrp_members\n" if ($verbose >= 2); } #print STDERR " searching $prefix\n" if ($verbose >= 2); find( sub { $file = $_; $path = $File::Find::name; $path =~ s!^\.\/!!; $cwd = $File::Find::dir; $cwd =~ s!^\.\/!!; my ($err_msg); ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat($file); if (!defined $mode) { print ">>> $file\n" if ($verbose); } else { printf("%3d %8d %07o:%10s %2d %5d %5d %6d %15d [%17s] %s\n", $dev, $ino, $mode, $self->format_mode($mode), $nlink, $uid, $gid, $rdev, $size, time2str("%Y-%m-%d %H:%M:%S", $mtime), $path) if ($verbose); if ($shgrp_gid) { $err_msg = $self->_share_file($file, $options, $shgrp_gid, $mode, $uid, $gid); } } return(0); }, "." ); } # S_IRWXU S_IRUSR S_IWUSR S_IXUSR # S_IRWXG S_IRGRP S_IWGRP S_IXGRP # S_IRWXO S_IROTH S_IWOTH S_IXOTH # # # Setuid/Setgid/Stickiness/SaveText. # # Note that the exact meaning of these is system dependent. # # S_ISUID S_ISGID S_ISVTX S_ISTXT sub format_mode { my ($self, $mode) = @_; my $fmt_mode = ($mode & S_IFREG) ? "-" : (($mode & S_IFDIR) ? "d" : (($mode & S_IFLNK) ? "l" : "?")); $fmt_mode .= ($mode & S_IRUSR) ? "r" : "-"; $fmt_mode .= ($mode & S_IWUSR) ? "w" : "-"; $fmt_mode .= ($mode & S_IXUSR) ? (($mode & S_ISUID) ? "s" : "x") : (($mode & S_ISUID) ? "S" : "-"); $fmt_mode .= ($mode & S_IRGRP) ? "r" : "-"; $fmt_mode .= ($mode & S_IWGRP) ? "w" : "-"; $fmt_mode .= ($mode & S_IXGRP) ? (($mode & S_ISGID) ? "s" : "x") : (($mode & S_ISGID) ? "S" : "-"); $fmt_mode .= ($mode & S_IROTH) ? "r" : "-"; $fmt_mode .= ($mode & S_IWOTH) ? "w" : "-"; $fmt_mode .= ($mode & S_IXOTH) ? (($mode & S_ISVTX) ? "t" : "x") : (($mode & S_ISVTX) ? "T" : "-"); return($fmt_mode); } # 1. $cnt = chmod 0755, 'foo', 'bar'; # 2. chmod 0755, @executables; # 3. $mode = '0644'; chmod $mode, 'foo'; # !!! sets mode to # 4. # --w----r-T # 5. $mode = '0644'; chmod oct($mode), 'foo'; # this is better # 6. $mode = 0644; chmod $mode, 'foo'; # this is best # 1. $cnt = chown $uid, $gid, 'foo', 'bar'; # 2. chown $uid, $gid, @filenames; sub _share_file { my ($self, $file, $options, $shgrp_gid, $mode, $uid, $gid) = @_; my $verbose = $options->{verbose}; my $err_msg = ""; my ($retval); if ($shgrp_gid) { if ($gid != $shgrp_gid) { $retval = chown($uid, $shgrp_gid, $file); if ($verbose) { print ">>> chown($uid, $shgrp_gid, $file) = [$retval]\n"; } } my $share_mode = $mode & 07777; my $mode_needs_fix = 0; if ($mode & S_IFDIR) { if ($mode & S_ISGID) { # do nothing } else { $share_mode |= S_ISGID; $mode_needs_fix = 1; } } else { if ($mode & S_ISGID) { $share_mode ^= S_ISGID; $mode_needs_fix = 1; } else { # do nothing } } if ($mode & S_ISUID) { $share_mode ^= S_ISUID; $mode_needs_fix = 1; } if ($mode & S_ISVTX) { $share_mode ^= S_ISVTX; $mode_needs_fix = 1; } if ($mode & S_IRUSR) { if ($mode & S_IRGRP) { # do nothing } else { $share_mode |= S_IRGRP; $mode_needs_fix = 1; } } if ($mode & S_IWUSR) { if ($mode & S_IWGRP) { # do nothing } else { $share_mode |= S_IWGRP; $mode_needs_fix = 1; } } if ($mode & S_IXUSR) { if ($mode & S_IXGRP) { # do nothing } else { $share_mode |= S_IXGRP; $mode_needs_fix = 1; } } if ($mode_needs_fix) { $retval = chmod($share_mode, $file); if ($verbose) { printf(">>> chmod(%06o, $file) = [$retval]\n", $share_mode); } } } return($err_msg); } 1;