#!/usr/bin/perl 
# IBM_PROLOG_BEGIN_TAG 
# This is an automatically generated prolog. 
#  
#  
#  
# Licensed Materials - Property of IBM 
#  
# (C) COPYRIGHT International Business Machines Corp. 2006,2007 
# All Rights Reserved 
#  
# US Government Users Restricted Rights - Use, duplication or 
# disclosure restricted by GSA ADP Schedule Contract with IBM Corp. 
#  
# IBM_PROLOG_END_TAG 
#################################################
# @(#)26  1.9.1.8  src/csm/cfm/cmds/cfm_local.perl, cfm, csm_rfish, rfishs001b 2/8/07 10:36:58
#
# As of 1.6.0.0,  this routine can either be installed on the node
# or is in the /csminstall/csm directory for minmanaged nodes
# Need to set the path to messages and *.pm files to either the installed ones
# or the ones in the /csminstall/csm directory mounted from the management
# server.
# This routine replaces clocal in 1.6.0.0 which was part the csm.client pkg
# and is packaged in the csm.core pkg so that it can be copied into
# /csminstall/csm for minmanaged nodes.
# Also a new message map file had to be created for the move to csm.core.
#
# Get current directory
#
use strict;

BEGIN
{
    use File::Basename;
    ($::Bin) = dirname($0);    # get current directory
    if (-e "$::Bin/csmCFMlocal.cfm_local.map")
    {                          # if in the current directory, use it
                               # we are on minmanaged nodes and the
                               # code is mounted in /csminstall/csm
        $::csmpm               = $::Bin;
        $::MSGMAPPATH          = $::Bin;
        $NodeUtils::MSGMAPPATH = $::MSGMAPPATH;
    }
    else
    {                          # csm is installed on the node
        $::csmpm = $ENV{'CSM_PM'} ? $ENV{'CSM_PM'} : '/opt/csm/pm';
        $::MSGMAPPATH =
          $ENV{'CSM_MSGMAPS'} ? $ENV{'CSM_MSGMAPS'} : '/opt/csm/msgmaps';
        $NodeUtils::MSGMAPPATH = $::MSGMAPPATH;
    }
}
use locale;
use lib $::csmpm;
require NodeUtils;
require CSMDefs;
use NetworkUtils;
use MessageUtils;

#MSG Variables:
$::MSGCAT = 'csmCFMlocal.cat';
$::MSGSET = 'cfm_local';

$main::date = time();

my (
    $name,        $userpasswd, $uid,     $gid,       $userquota,
    $usercomment, $usergcos,   $userdir, $usershell, $userexpire
  )
  = getpwuid($<);

$main::user    = $name;
$main::useruid = $uid;
$main::usergid = $gid;

$::FILE = "/usr/bin/file";

#################################################
#Usage
#################################################
sub usage { MessageUtils->message('I', 'IMsgClocalUsage'); }

# Main#

my $logpath = "/var/log/csm";
$::CFM_LOCAL_LOG = "/var/log/csm/cfm_local.log";
$CFM::DEBUG      = $ENV{'CSM_CFM_DEBUG'};

#$CFM::DEBUG = 1;

# log dir may not exist, especially on minmanaged nodes
#
if (!(-e $logpath))
{
    mkdir($logpath, 0755);
}
my @command_line = ();
@command_line = @ARGV;
my $command_line = $0 . " " . join(" ", @command_line);
MessageUtils->start_logging($::CFM_LOCAL_LOG);
MessageUtils->message('I', 'IMsgCmd', $command_line);
$::PLTFRM = `uname`;  # get platform name
chomp $::PLTFRM;
%::OSDefs = ServerUtils->get_OSDefs($::PLTFRM);
#################################################
###Get command line options
#################################################
use Getopt::Std;
if (!getopts('ybhMcefn:u:g:')) { &usage; exit 1; }
if ($::opt_y && $::opt_c) { &usage; exit 1; }

if ($::opt_y)
{    #preserve younger destination files
    $main::younger = 1;
    undef $::opt_y;
}

if ($::opt_M)
{    #management server is an HMC
    $::MSisHMC = 1;
}
if ($::opt_b)
{    #save a copy of original files as "filename".OLD
    $main::BACKUP = 1;
    undef $::opt_b;
}
if ($::opt_c)
{
    $main::difflag = 1;
    srand(time | $$);
    undef $::opt_c;
}
if ($::opt_f)
{
    my $file = $::opt_f;
    push @::FILENAMES, $file;
    push @::FILENAMES, @ARGV;
    undef $::opt_f;
}
if ($::opt_n)
{
    $::hostname = $::opt_n;
    undef $::opt_n;
}
if ($::opt_u)
{
    if ($main::user ne 'root')
    {
        exit 1;
    }
    $main::USERS = $::opt_u;
    undef $::opt_u;
}
if ($::opt_g)
{
    if ($main::user ne 'root')
    {
        exit 1;
    }
    $main::GROUPS = $::opt_g;
    undef $::opt_g;
}
if ($::opt_e)
{
    $main::FORCE = 1;
    undef $::opt_e;
}
if ($::opt_h)
{
    &usage;
    undef $::opt_h;
    exit 0;
}

#################################################
#Set environment variables
#################################################
umask(0000);    #so that I can preserve the permissions of files

if (!$::hostname)
{
    &determineHostName;    # sets $::hostname and $::ip
} 

#################################################
#Call functions
#################################################
$main::GLOBAL_EXIT = 0;
$main::transferflag = 0;
$::VERBOSE         = 1;

if (($main::USERS) || ($main::GROUPS))
{
    &checkUsers();
}
$main::userFile = "/var/opt/csm/" . $::user . "CFMfileinfo";
NodeUtils->runcmd(
               "/bin/cp -fp /var/opt/csm/cfmlocal/.runclocal $::userFile 2>&1");

NodeUtils->runcmd("/bin/rm -f /var/opt/csm/cfmlocal/.runclocal 2>&1");

if ($::user ne 'root')
{
    &checkUserFiles($main::user, $main::userFile);
}
else
{
    &checkRootFiles($main::userFile);
}

&removeNonRealFiles('/var/opt/csm/cfmlocal');
&getfilelist('/var/opt/csm/cfmlocal');
if ($::MSisHMC)
{    #management server is an HMC
    my $filename = "/etc/passwd";    # check to see if userid hscroot
                                     # or group hmc is on the node
                                     # not allowed with HMC MS
    NodeUtils->runcmd("$::CAT $filename | $::GREP hscroot", -1);
    if ($::RUNCMD_RC == 0)
    {                                # hscroot id  exists not good
        MessageUtils->message('E', 'EMsgHMCUserErr');
    }
    $filename = "/etc/group";        # check to see if group hmc
    NodeUtils->runcmd("$::CAT $filename | $::GREP hmc", -1);
    if ($::RUNCMD_RC == 0)
    {                                # hmc group  exists not good
        MessageUtils->message('E', 'EMsgHMCUserErr');
    }
}

#
# Warning the below IMsgFile* message must be the last
# Output from cfm_local for cfmupdatenode to process
# and display the correct state for the node.
#
if ($main::transferflag == 0)
{
    MessageUtils->message('I', 'IMsgFileNoTrans');
}
else
{
    MessageUtils->message('I', 'IMsgFileTrans');
}

END
{
    MessageUtils->stop_logging($::CFM_LOCAL_LOG);
    `/bin/rm -f /var/opt/csm/cfmlocal/.runclocal 2>&1`;
    `/bin/rm -f $::userFile 2>&1`;
    $? = $main::GLOBAL_EXIT;
}

#################################################
#removeNonRealFiles
#################################################
sub removeNonRealFiles
{
    my $path = $_[0];
    my @subdirs;
    my $fh;
    my $file;
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Enter removenonRealFiles\n";
    opendir($fh, $path)
      or MessageUtils->message("E2", 'EMsgCANT_OPEN_DIR', $path);
  FILE: while ($file = readdir($fh))
    {

        if ($file ne '.' and $file ne '..')
        {    #make sure its not . or ..
            my $wholename = "$path/$file";
            if (!(-d $wholename))
            {
                if (!$::distFiles{$wholename})
                {
                    $CFM::DEBUG
                      && print $::LOG_FILE_HANDLE "Calling removeFile 1\n";
                    &removeFile($wholename);
                }
            }
            else
            {
                push @subdirs, $wholename;
            }
        }
    }
    closedir $fh or MessageUtils->message("E", 'EMsgPATH_ERROR', $path);
    foreach my $dir (@subdirs)
    {
        &removeNonRealFiles($dir);
    }
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Exit removenonRealFiles\n";
}

#################################################
#getfilelist
# input a directory to process
# calls intself recursively
#################################################
sub getfilelist
{
    my ($path, $file, $destfile, $mode, $fh);
    $path = $_[0];    # directory to process
    my $numfiles = 0;
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Enter getfilelist dir=$path\n";
    opendir($fh, $path)
      or MessageUtils->message("E2", 'EMsgCANT_OPEN_DIR', $path);
    my @dirs = sort readdir($fh);    # sort alphabetically

    #
    #  check for directory .pre script and execute, if it exists
    #
    my $rc = &check_run_dir_pre_post_script($path, "pre");
    if ($rc != 0)
    {                                # error occurred
        return;                      # process no more files in this directory
    }

    #
    # go through the input directory and filter out files that do
    # not have correct uids and perms
    #
  FILE: foreach $file (@dirs)
    {
        if ($file ne '.' and $file ne '..')
        {                            #make sure its not . or ..
            my $wholename = "$path/$file";
            if (!(-d $wholename))    # not a directory
            {                        #don't worry about it if its a directory
                    #
                    # check uid and gid to see if permissions are correct
                    # that we can copy this file
                    #
                my $rc = &processfile($wholename);
                if ($rc == 2)
                {    #skip this file
                    next FILE;
                }
            }
            $file = join "/", $path, $file;
            $destfile = $file;
            $destfile =~ s/var\/opt\/csm\/cfmlocal\///;    #remove cfmlocal part
            if (-d "$file")
            {                                              #DIRECTORY
                if (-e "$destfile")
                {    #if destination directory EXISTS
                    if (-d "$destfile")
                    {    #it is a directory as well
                        my $notempty =
                          &getfilelist($file); #transfer files to this directory
                        if (!$notempty)
                        {
                            $CFM::DEBUG
                              && print $::LOG_FILE_HANDLE
                              "Calling removeFile 2\n";
                            &removeFile($wholename);
                        }                      # empty
                        else { $numfiles = 1; }
                    }
                    else
                    {                          #its the wrong file type
                        MessageUtils->message("E", 'EMsgWRONG_DEST_TYPE',
                                              $destfile, $destfile);
                        $main::GLOBAL_EXIT = 2;
                    }
                    next FILE;
                }
                else
                {    #destination directory DOES NOT EXIST
                    if ($::user eq 'root')
                    {

                        #  create the directory
                        my $rc = &createrootdir($destfile, $file, $wholename);
                        if ($rc == 2)
                        {    # skip this file, problem
                                # creating the directory
                            next FILE;
                        }

                    }
                    else        # not root
                    {

                        #  create the directory
                        my $rc =
                          &createnonrootdir($destfile, $file, $wholename);
                        if ($rc == 2)
                        {       # skip this file, problem
                                # creating the directory
                            next FILE;
                        }
                        else
                        {
                            next FILE;
                        }
                    }
                    &getfilelist($file);    #transfer files to this directory
                    next FILE;
                }
            }
            else                            # it is a file
            {
                $numfiles = 1;

                #Everything else is a FILE
                if ($::FILENAMES)
                {                           #we are only transfering one file
                    if (!grep /$file/, @::FILENAMES)
                    {                       #if this isn't one of the files
                        next FILE;          #go on to the next file
                    }
                }
                if (-e $destfile)
                {
                    my $do_metasub  = 0;
                    my @type_output = `$::FILE $file 2>&1`;
                    foreach my $line (@type_output)
                    {
                        if ($line =~ m/\Wtext\W/ || $line =~ m/\Wscript\W/)
                        {
                            $do_metasub = 1;
                        }
                    }
                    if ($main::difflag == 1)    # if -c flag chosen
                                                # which means diff the files
                                                # and replace if different
                    {
                        my $rc = &diff_file($file, $do_metasub, $destfile);
                    }
                    else                        #  not main::DIFF

                    {
                        if (((stat($file))[9] != (stat($destfile))[9]))
                        {

                            #Destination file either doesn't exist
                            #or doesnt match mtime
                            #check for .pre script
                            if ($main::younger)    # check -y flag
                            {
                                my $diff =
                                  (stat($destfile))[9] - (stat($file))[9];
                                if ($diff < 0)
                                {                  #destination file is older
                                    &transfer($file, $do_metasub);
                                }
                            }
                            else
                            {
                                &transfer($file, $do_metasub)
                                  ;    #update file through transfer function
                            }
                        }
                    }    # end stat
                }       # end $main::DIFF
                else    # $destfile does not exist
                {       #first time transfer
                    my $do_metasub  = 0;
                    my @type_output = `$::FILE $file 2>&1`;
                    foreach my $line (@type_output)
                    {
                        if ($line =~ m/\Wtext\W/ || $line =~ m/\Wscript\W/)
                        {
                            $do_metasub = 1;
                        }
                    }
                    &transfer($file, $do_metasub)
                      ;    #update file through transfer function
                }
            }
        }
    }
    closedir $fh or MessageUtils->message("E", 'EMsgPATH_ERROR', $path);

    #
    #  check for directory .post script and execute, if it exists
    #
    $rc = &check_run_dir_pre_post_script($path, "post");
    if ($rc != 0)
    {                      # error occurred
        return;            # process no more files in this directory
    }

    $CFM::DEBUG
      && print $::LOG_FILE_HANDLE "Exit getfilelist numberfiles = $numfiles\n";

    return $numfiles;
}

#################################################
#check_run_dir_pre_post_script
# check to see if directory.pre  or directory.post script exists
# if it exists and is executable, run it
# input directory and pre or post option
#################################################
sub check_run_dir_pre_post_script
{
    my ($dir, $option) = @_;
    my $file       = $dir . "." . $option; # create the dir.pre or dir.post name
    my $dirprepost = $file;
    if ((-e "$dirprepost") && (!(-d "$dirprepost"))) # it exists  
													 # and not a directory
    {    #this this is a .pre or .postfile for the directory
        if (!(-x $dirprepost))
        {    # if not executable
            if (!($::MSisHMC))
            {    # MS is not an HMC, then error
                $main::GLOBAL_EXIT = 11;
                MessageUtils->message("E", 'EMsgPrePostNotExe_ERROR',
                                      "$dirprepost");
                return 1 if !$main::FORCE;
            }
            else
            {    # MS is an HMC,  force it executable
                my $chmod_command = "/bin/chmod u+x $dirprepost 2>&1";
                NodeUtils->runcmd($chmod_command, 0);
                if ($::RUNCMD_RC != 0)
                {
                    $main::GLOBAL_EXIT = 2;
                }

                # remove write permission  for group and other
                my $chmod_command = "/bin/chmod go-w $dirprepost 2>&1";
                NodeUtils->runcmd($chmod_command, 0);
                if ($::RUNCMD_RC != 0)
                {
                    $main::GLOBAL_EXIT = 2;
                }

            }
        }
        my @dirprepost_results = `$dirprepost 2>&1`;    # execute it
        MessageUtils->message('I', 'IMsgRAN_FILE_PRE', $dirprepost, $dir);
        if (($? >> 8) != 0)    # if there was an error, report
        {
            $main::GLOBAL_EXIT = 2;
            my $dirprepost_return = join '', @dirprepost_results;
            $dirprepost_return ||= 'unknown';
            my $error = $? >> 8;
            MessageUtils->message(
                                  "E",           'EMsgCOMMAND_ERROR',
                                  "$dirprepost", "$error",
                                  "$dirprepost_return"
                                  );
            return -1 if !$main::FORCE;    # return, unless force chosen
        }
        elsif ($::VERBOSE)    # no error, running the .pre or .post file
        {
            my @name = split '/', $dirprepost;
            my $shortname = $name[$#name];
            foreach my $line (@dirprepost_results)
            {
                print "$shortname: $line";
            }
        }
    }
    return 0;
}

#################################################
#check_run_dir_post_script
# check to see if directory.post script exists
# if it exists and is executable, run it
#################################################
sub check_run_dir_post_script
{
    my ($dir) = @_;

    #
    # is this  .pre or .post file
    #
    if ($dir =~ m/\.post$/)
    {
        my $check_real = $dir;
        $check_real =~ s/\.post$//;
        if (-e "$check_real")
        {    #this this is a .pre file
                # skip for now until we read it's file
        }
    }

    return 0;
}
#################################################
#################################################
# process file, check uids and gids
# returns 0  OK
# returns 2  means skip to the next file, it has a gid or uid problem
#################################################
sub processfile
{
    my ($wholename) = @_;
    my @file_info   = stat($wholename);
    my $uid         = $file_info[4];
    my $gid         = $file_info[5];
    my $rc          = 0;
    $CFM::DEBUG
      && print $::LOG_FILE_HANDLE "Enter processfile  file=$wholename\n";
    if ($main::user eq 'root')
    {

        #  check user and group
        if ($::distFiles{$wholename})
        {
            if (   ($uid != $::distFiles{$wholename}{'user'})
                || ($gid != $::distFiles{$wholename}{'group'}))
            {

                #if the file was originally owned by root,
                # and still is, then don't worry about the group
                # if file is owned by root and the new one is not
                # root, that is a problem
                if (!($uid eq "0" && $::distFiles{$wholename}{'user'} eq "0"))
                {
                    MessageUtils->message('E', 'EMsgIncorrectUser', $wholename);
                    $CFM::DEBUG
                      && print $::LOG_FILE_HANDLE "Calling removeFile 3\n";
                    &removeFile($wholename);
                    return 2;    # go to next file
                    $CFM::DEBUG
                      && print $::LOG_FILE_HANDLE
                      "Exit processfile  file=$wholename\n";
                }
            }
        }
        else
        {
            $CFM::DEBUG && print $::LOG_FILE_HANDLE "Calling removeFile 4\n";
            &removeFile($wholename);
            $CFM::DEBUG
              && print $::LOG_FILE_HANDLE "Exit processfile return=2\n";
            return 2;    # go to next file
        }
    }
    else                 # not root
    {

        # do uid and gid agree
        if (($uid == $::useruid) && ($gid == $::usergid))
        {

            #file should be in %::distFiles
            if (!$::distFiles{$wholename})
            {

                #its not on the MS, so delete it
                `/bin/rm -f $wholename 2>&1`;
            }
        }
        else
        {    #not owned by me, on to the next
            $CFM::DEBUG
              && print $::LOG_FILE_HANDLE "Exit processfile return=2\n";
            return 2;    # go to next file

        }
    }
    $CFM::DEBUG
      && print $::LOG_FILE_HANDLE "Exit processfile return=0\n";
    return 0;
}
#################################################
# create root directory - determines if the directory
# needs to be created before copying the file
# return 0  OK
# return 2  Go to next file
#################################################
sub createrootdir
{
    my ($destfile, $file, $wholename) = @_;
    my @files  = keys %::distFiles;
    my $create = 0;
    my $mode;
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Enter createrootdir\n";
    while (@files && !$create)
    {
        my $f = pop @files;
        if ($f =~ m/^\Q$file\E/)
        {    # the \Q and \E mean between these,
                # any variables should not be treated
                # like regexp chars
            $create = 1;
        }
    }
    if ($create)
    {

        #$mode= (stat($file))[2] & 07777;
        #get mode from orig, remove file type bits
        if ($destfile =~ m/\w/)
        {
            $mode = $::distDirs{"/cfmroot$destfile"}{'mode'} & 07777;
            if (!(mkdir "$destfile", $mode))
            {    #MAKE destination directory
                MessageUtils->message("E", 'EMsgDEST_DIR_ERROR', $destfile);
                $main::GLOBAL_EXIT = 2;
            }
            chown $::distDirs{"/cfmroot$destfile"}{'user'},
              $::distDirs{"/cfmroot$destfile"}{'group'}, $destfile;
        }
    }
    else
    {
        $CFM::DEBUG && print $::LOG_FILE_HANDLE "Calling removeFile 5\n";
        &removeFile($wholename);
        $CFM::DEBUG && print $::LOG_FILE_HANDLE "Exit createrootdir return=2\n";
        return 2;    # go to next file
    }
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Exit createrootdir return=0\n";
    return 0;
}
#################################################
# create non root directory - determines if the directory
# needs to be created before copying the file
# return 0  OK
# return 2  Go to next file
#################################################
sub createnonrootdir
{
    my ($destfile, $file, $wholename) = @_;
    my @files  = keys %::distFiles;
    my $create = 0;
    my $mode;
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Enter createnonrootdir\n";
    if (($uid == $::useruid) && ($gid == $::usergid))
    {

        #file should be in %::distFiles
        if (!$::distFiles{$wholename})
        {

            #its not on the MS, so skip it
            next FILE;
        }
        else
        {

            #$mode= (stat($file))[2] & 07777;
            #get mode from orig, remove file type bits
            if ($destfile =~ m/\w/)
            {
                $mode = $::distDirs{"/cfmroot$destfile"}{'mode'} & 07777;
                if (!(mkdir "$destfile", $mode))
                {    #MAKE destination directory
                    MessageUtils->message("E", 'EMsgDEST_DIR_ERROR', $destfile);
                    $main::GLOBAL_EXIT = 2;
                }
                chown $::distDirs{"/cfmroot$destfile"}{'user'},
                  $::distDirs{"/cfmroot$destfile"}{'group'}, $destfile;
            }
        }
    }
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Exit createrootdir return=0\n";
    return 0;
}
#################################################
# Option -c chosen
# diff the file with previous version and
# replace if different
# return 0  OK
#################################################
sub diff_file
{
    my ($file, $do_metasub, $destfile) = @_;
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Enter diff_file file=$file\n";
    my $new_name = &CreateRandomName($file);
    while (-e $new_name)
    {
        $new_name = &CreateRandomName($file);
    }
    my $copy_command;
    my $cmd;
    if ($do_metasub)
    {
        $copy_command = sprintf
          "/usr/bin/perl -p -e 's/\%%ip\%/$::ip/g; s/\%%hostname\%/$::hostname/g' %s > %s",
          $file, $new_name;
        $cmd =
          "/usr/bin/perl -p -e 's/\%ip\%/$::ip/g; s/\%hostname\%/$::hostname/g' $file > $new_name";
    }
    else
    {
        $copy_command = "/bin/cp -fp $file $new_name";
        $cmd          = $copy_command;
    }

    my @copy      = `$cmd 2>&1`;
    my $copy_exit = $? >> 8;
    if ($copy_exit != 0)
    {
        $main::GLOBAL_EXIT = 2;
        my $copy_return = join '', @copy;
        MessageUtils->message(
                              "E",             'EMsgCOMMAND_ERROR',
                              "$copy_command", "$copy_exit",
                              "$copy_return"
                              );
    }
    my @diff_test = NodeUtils->runcmd("$::DIFF $new_name $destfile 2>&1", 0);
    if (@diff_test)
    {    #the files are different
        &transfer($file, $do_metasub);
    }
    my @rm = `/bin/rm -f $new_name`;
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Exit diff_file\n";

    return 0;
}
#################################################
#transfer
#################################################
sub transfer
{
    my ($file, $destfile, $checkpre, $checkpost, $made_backup, $copy_command,
        $msg_copy_command);
    $made_backup = 0;
    my ($file, $do_metasub) = @_;
    my @file_info = stat($file);
    my $uid       = $file_info[4];
    my $gid       = $file_info[5];
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Enter transfer file=$file\n";
    $destfile = $file;
    $destfile =~ s/var\/opt\/csm\/cfmlocal\///;

    #
    # is this  .pre or .post file
    #
    if (($file =~ m/\.pre$/) || ($file =~ m/\.post$/))
    {
        my $check_real = $file;
        if ($file =~ m/\.pre$/)
        {
            $check_real =~ s/\.pre$//;
            if (-e "$check_real")
            {    #this this is a .pre file
                    # skip for now until we read it's file
                return 1;
            }
        }
        elsif ($file =~ m/\.post$/)
        {
            $check_real =~ s/\.post$//;
            if (-e "$check_real")
            {       #this this is a .pre file
                    # skip for now until we read it's file
                return 1;
            }
        }
    }

    #
    # is there a .pre or .post file for the file we are processing
    #
    $checkpre  = $file . ".pre";
    $checkpost = $file . ".post";

    #is there a .pre file? --what about .pre and ._GroupName
    if (-e $checkpre)
    {
        if (!(-x $checkpre))
        {    # if not executable
            if (!($::MSisHMC))
            {    # MS is not an HMC, then error
                $main::GLOBAL_EXIT = 11;
                MessageUtils->message("E", 'EMsgPrePostNotExe_ERROR',
                                      "$checkpre");
                return if !$main::FORCE;
            }
            else
            {    # MS is an HMC,  force it executable
                my $chmod_command = "/bin/chmod u+x $checkpre 2>&1";
                NodeUtils->runcmd($chmod_command, 0);
                if ($::RUNCMD_RC != 0)
                {
                    $main::GLOBAL_EXIT = 2;
                }

                # remove write permission  for group and other
                my $chmod_command = "/bin/chmod go-w $checkpre 2>&1";
                NodeUtils->runcmd($chmod_command, 0);
                if ($::RUNCMD_RC != 0)
                {
                    $main::GLOBAL_EXIT = 2;
                }

            }
        }

        #
        # Execute the .pre file for the file we are about to transfer
        # tell them we ran it
        #
        my $rc = &execute_pre_file($checkpre, $destfile);
        if ($rc != 0)
        {
            return;
        }

    }    # end there is a .pre file

    #
    #create directory if it does not exist for the file transfer
    # now copy the file to the proper place from
    # /var/opt/csm/cfmlocal

    my @parts = split "/", $destfile;
    pop @parts;
    my $dir = join "/", @parts;
    if (!-e "$dir" && $dir =~ m/\w/)    # need to make the directory
    {
        my $mode = $::distDirs{"/cfmroot$dir"}{'mode'} & 07777;
        if (!(mkdir "$dir", $mode))
        {                               #MAKE destination directory
            MessageUtils->message("E", 'EMsgDEST_DIR_ERROR', $dir);
            $main::GLOBAL_EXIT = 2;
        }
        chown $::distDirs{"/cfmroot$dir"}{'user'},
          $::distDirs{"/cfmroot$dir"}{'group'}, $dir;
    }

    my $mode = (stat($file))[2] & 07777;
    $mode = sprintf "%04o", $mode;
    my $mtime       = (stat($file))[9];
    my $atime       = (stat($destfile))[8];
    my $backup_file = "$destfile.OLD";
    if (-e $destfile)
    {

        #check if backup exists, update it with the new file
        # if post script fails we will have to recover from
        # backup
        if (-e $backup_file)
        {
            $backup_file .= ".$::date";
        }
        my $move_command = "/bin/mv $destfile $backup_file";
        my @move         = `$move_command 2>&1`;
        if (($? >> 8) != 0)
        {
            $main::GLOBAL_EXIT = 2;
            my $move_return = join '', @move;
            my $mv_exit = $? >> 8;
            MessageUtils->message(
                                  "E",             'EMsgCOMMAND_ERROR',
                                  "$move_command", "$mv_exit",
                                  "$move_return"
                                  );
        }
        else
        {
            $made_backup = 1;
        }
    }
    my $new_name = &CreateRandomName($file);
    while (-e $new_name)
    {
        $new_name = &CreateRandomName($file);
    }
    if ($do_metasub)
    {
        my $metasub_command = sprintf
          "/usr/bin/perl -p -e 's/\%%ip\%/$::ip/g; s/\%%hostname\%/$::hostname/g' %s > %s",
          $file, $new_name;
        my $metasub =
          `/usr/bin/perl -p -e 's/\%ip\%/$::ip/g; s/\%hostname\%/$::hostname/g' $file > $new_name`;
        my $metasub_exit = $? >> 8;
        if ($metasub_exit == 0)
        {    #it was a text file
            $copy_command = &CopyTextFile($file, $new_name, $mode, $destfile);
        }
        else
        {    #something went wrong, just do a normal copy
            $copy_command = "$::CP -fp $file $destfile";

            #msg copy command
            $msg_copy_command = $copy_command;
        }
    }
    else
    {        #it was a binary file
        $copy_command = "$::CP -fp $file $destfile";

        #msg copy command
        $msg_copy_command = $copy_command;
    }
    NodeUtils->runcmd("$copy_command 2>&1", 0);
    if ($::RUNCMD_RC != 0)
    {
        $main::GLOBAL_EXIT = 2;
        MessageUtils->message("E", 'EMsgCOPY_ERROR2', $msg_copy_command,
                              $::RUNCMD_RC);
        print "CR_FILE: /cfmroot$destfile\n";
        if ($made_backup)
        {
            MessageUtils->message('O', 'IMsgRestore', $destfile);
            my $move_command = "/bin/mv $backup_file $destfile";
            my @move         = `$move_command 2>&1`;
            if (($? >> 8) != 0)
            {
                my $move_return = join '', @move;
                my $mv_exit = $? >> 8;
                MessageUtils->message(
                                      "E",             'EMsgCOMMAND_ERROR',
                                      "$move_command", "$mv_exit",
                                      "$move_return"
                                      );
            }
        }
    }
    else
    {    #copy successful
        utime $atime, $mtime, $destfile;
        MessageUtils->message('I', 'IMsgTRANS_FILE', $file, $destfile);
        print "K*E*Y: $destfile\n";
    }
    `/bin/rm -f $new_name 2>&1`;

    #
    #is there a .post file?
    #
    if (-e $checkpost)
    {
        my $rc =
          &process_post_file($checkpost,   $checkpre, $destfile,
                             $backup_file, $made_backup);
        if ($rc == 1)
        {    # error
            return;
        }
    }
    else
    {        # there is no .post script
        &DealwithSuccessAndForce($destfile, $backup_file, $made_backup);
    }
    $main::transferflag = 1;
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Exit transfer file=$file\n";
}
#################################################
# execute_pre_file
##################################################
sub execute_pre_file
{
    my ($checkpre, $destfile) = @_;
    my @checkpre_results = `$checkpre 2>&1`;
    MessageUtils->message('I', 'IMsgRAN_FILE_PRE', $checkpre, $destfile);
    if (($? >> 8) != 0)    # if there was an error, report
    {
        $main::GLOBAL_EXIT = 2;
        my $checkpre_return = join '', @checkpre_results;
        $checkpre_return ||= 'unknown';
        my $ch_exit = $? >> 8;
        MessageUtils->message(
                              "E",         'EMsgCOMMAND_ERROR',
                              "$checkpre", "$ch_exit",
                              "$checkpre_return"
                              );
        return -1 if !$main::FORCE;    # return, unless force chosen
    }
    elsif ($::VERBOSE)                 # no error, running the .pre file
    {
        my @name = split '/', $checkpre;
        my $shortname = $name[$#name];
        foreach my $line (@checkpre_results)
        {
            print "$shortname: $line";
        }
    }
    return 0;
}
#################################################
#process_post_file
##################################################
sub process_post_file
{
    my ($checkpost, $checkpre, $destfile, $backup_file, $made_backup) = @_;
    if (!(-x $checkpost))
    {    # not executable
        if (!($::MSisHMC))
        {    # MS is not an HMC
            $main::GLOBAL_EXIT = 11;
            MessageUtils->message("E", 'EMsgPrePostNotExe_ERROR', "$checkpre");
            return 1 if !$main::FORCE;
        }
        else
        {    # MS is HMC, make executable
            my $chmod_command = "/bin/chmod u+x $checkpost 2>&1";
            NodeUtils->runcmd($chmod_command, 0);
            if ($::RUNCMD_RC != 0)
            {
                $main::GLOBAL_EXIT = 2;
            }

            # remove write permission  for group and other
            my $chmod_command = "/bin/chmod go-w $checkpost 2>&1";
            NodeUtils->runcmd($chmod_command, 0);
            if ($::RUNCMD_RC != 0)
            {
                $main::GLOBAL_EXIT = 2;
            }

        }
    }
    my @checkpost_results = `$checkpost 2>&1`;
    MessageUtils->message('I', 'IMsgRAN_FILE_POST', $checkpost, $destfile);
    if (($? >> 8) != 0)
    {
        $main::GLOBAL_EXIT = 2;
        my $checkpost_return = join '', @checkpost_results;
        $checkpost_return ||= 'unknown';
        my $p_exit = $? >> 8;
        MessageUtils->message(
                              "E",          'EMsgCOMMAND_ERROR',
                              "$checkpost", "$p_exit",
                              "$checkpost_return"
                              );
        if (!$main::FORCE)
        {
            MessageUtils->message('I', 'IMsgRecoverForPostScript', $checkpost,
                                  $destfile);
            my $recover_command;    # error post, recover old file
            $recover_command =
              $made_backup
              ? "/bin/mv $backup_file $destfile"
              : "/bin/rm -f $destfile";
            my @recover_result = `$recover_command`;
            return 1;
        }
        else
        {                           #force mode
            &DealwithSuccessAndForce($destfile, $backup_file, $made_backup);
        }
    }
    else
    {                               #.post is sucessful
        &DealwithSuccessAndForce($destfile, $backup_file, $made_backup);
        if ($::VERBOSE)
        {
            my @name = split '/', $checkpost;
            my $shortname = $name[$#name];
            foreach my $line (@checkpost_results)
            {
                print "$shortname: $line";
            }
        }
    }
    return 0;
}
#################################################
#CopyTextFile
##################################################
sub CopyTextFile
{
    my ($file, $new_name, $mode, $destfile, $copy_command) = @_;
    my $chmod_command = sprintf "/bin/chmod %s %s", $mode, $new_name;
    my @chmod = `/bin/chmod $mode $new_name 2>&1`;
    if (($? >> 8) != 0)
    {
        $main::GLOBAL_EXIT = 2;
        my $chmod_return = join '', @chmod;
        my $ch_exit = $? >> 8;
        MessageUtils->message(
                              "E",              'EMsgCOMMAND_ERROR',
                              "$chmod_command", "$ch_exit",
                              "$chmod_return"
                              );
    }
    my $chown_command = sprintf "/bin/chown %s:%s %s", $uid, $gid, $new_name;
    my @chown = `$chown_command 2>&1`;
    if (($? >> 8) != 0)
    {
        $main::GLOBAL_EXIT = 2;
        my $chown_return = join '', @chown;
        my $ch_exit = $? >> 8;
        MessageUtils->message(
                              "E",              'EMsgCOMMAND_ERROR',
                              "$chown_command", "$ch_exit",
                              "$chown_return"
                              );
    }
    $copy_command = " $::CP -fp $new_name $destfile ";

    #msg copy command
    return $copy_command;
}
#################################################
# DealwithSuccessAndForce
##################################################
sub DealwithSuccessAndForce
{
    my ($destfile, $backup_file, $made_backup) = @_;
    if (!$main::BACKUP && ($made_backup == 1))
    {
        my $remove_command = "/bin/rm -f $backup_file";
        my @remove         = `$remove_command 2>&1`;
        if (($? >> 8) != 0)
        {
            $main::GLOBAL_EXIT = 2;
            my $remove_return = join '', @remove;
            my $rm_exit = $? >> 8;
            MessageUtils->message(
                                  "E",               'EMsgCOMMAND_ERROR',
                                  "$remove_command", "$rm_exit",
                                  "$remove_return"
                                  );
        }
    }
    if ($::BACKUP && ($backup_file ne "$destfile.OLD"))
    {
        MessageUtils->message("I", 'IMsgOLDExists', "$destfile.OLD", $destfile,
                              $backup_file);
    }
}

sub CreateRandomName
{
    my ($name) = @_;
    my $nI;
    for ($nI = 0 ; $nI < 8 ; $nI++)
    {
        my $char = ('a' .. 'z', 'A' .. 'Z')[int(rand(52)) + 1];
        $name .= $char;
    }
    $name;
}

#################################################
# checkUsers
#################################################
sub checkUsers
{
    my %badUsers  = ();
    my %badGroups = ();
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Enter checkUsers\n";
    if ($main::USERS)
    {
        my @pairs = split ',', $main::USERS;
        foreach my $entry (@pairs)
        {
            my ($num, $name) = split '\.', $entry;
            my (
                $username,  $userpasswd,  $useruid,  $usergid,
                $userquota, $usercomment, $usergcos, $userdir,
                $usershell, $userexpire
              )
              = getpwnam($name);
            if ($useruid != $num)
            {
                $badUsers{$entry} = 1;
            }
        }
    }
    if ($main::GROUPS)
    {
        my @pairs = split ',', $main::GROUPS;
        foreach my $entry (@pairs)
        {
            my ($num, $name) = split '\.', $entry;
            my ($goupname, $grouppasswd, $groupgid, $groupmembers) =
              getgrnam($name);
            if ($groupgid != $num)
            {
                $badGroups{$entry} = 1;
            }
        }
    }
    my $fine = 0;
    if (keys %badUsers)
    {
        $fine = 1;
        print "USERS ";
        foreach my $entry (keys %badUsers)
        {
            print "$entry,";
        }
        print "\n";
    }
    if (keys %badGroups)
    {
        $fine = 1;
        print "GROUPS ";
        foreach my $entry (keys %badGroups)
        {
            print "$entry,";
        }
        print "\n";
    }
    if (!$fine)
    {
        MessageUtils->message('O', 'IMsgExist');
    }
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Exit checkUsers\n";
    exit;
}

sub checkUserFiles
{
    my ($user, $file) = @_;
    my $line;
    %main::distFiles;
    $CFM::DEBUG
      && print $::LOG_FILE_HANDLE "Enter checkUserFiles\n";
    open(USERFILE, "<$file");
    while ($line = <USERFILE>)
    {
        chomp $line;
        my ($filename, @nodes) = split ' ', $line;
        my $isNode = 0;
        foreach my $n (@nodes)
        {
            if (($n eq $::hostname) || ($n eq $::ip))    # match name or ipaddr
            {
                $isNode = 1;
            }
        }
        if ($isNode)
        {
            $filename =~ s/^\/cfmroot/\/var\/opt\/csm\/cfmlocal/g;
            $::distFiles{$filename} = 1;
        }
    }
    close USERFILE;
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Exit checkUserFiles\n";
}

sub checkRootFiles
{
    my ($file) = @_;
    my $line;
    %main::distFiles;
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Enter checkRootFiles\n";
    my $isNode = 0;
    open(ROOTFILE, "<$file");
    while ($line = <ROOTFILE>)
    {
        chomp $line;
        my ($filename, $user, $group, @nodes) = split ' ', $line;
        if ($nodes[0] =~ m/CFM_MODE_CFM=/)
        {    #maybe a directory
            my ($text, $mode) = split /=/, $nodes[0];
            if ($mode =~ m/\d+/)
            {    #its a directory
                $::distDirs{$filename}{'mode'}  = $mode;
                $::distDirs{$filename}{'user'}  = $user;
                $::distDirs{$filename}{'group'} = $group;
            }
        }
        else
        {
            foreach my $n (@nodes)
            {
                $CFM::DEBUG
                  && print $::LOG_FILE_HANDLE "Check $n eq $::hostname\n";
                $CFM::DEBUG && print $::LOG_FILE_HANDLE "Check $n eq $::ip\n";

                if (($n eq $::hostname) || ($n eq $::ip)) # match name or ipaddr
                {
                    $isNode = 1;
                }
            }
        }
        if ($isNode)
        {
            $filename =~ s/^\/cfmroot/\/var\/opt\/csm\/cfmlocal/g;
            $::distFiles{$filename}{'user'}  = $user;
            $::distFiles{$filename}{'group'} = $group;
        }
    }
    close ROOTFILE;
    if ($isNode == 0)
    {    # if did not find the hostname in the .runclocal
            # we will not distribute the files to
            # this node
        MessageUtils->message('E', 'EMsgHostnameNotValid');
    }
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Exit checkRootFiles\n";

}

sub removeFile
{
    my $file = $_[0];
    $CFM::DEBUG && print $::LOG_FILE_HANDLE "Enter removeFiles file=$file\n";
    if (!(-d $file))
    {
        $CFM::DEBUG && print $::LOG_FILE_HANDLE "removing file=$file\n";
        my @results = `/bin/rm -f $file 2>&1`;
        my $rm_exit = $? >> 8;
        if ($rm_exit != 0)
        {
            MessageUtils->message(
                                  'E255',             'EMsgCOMMAND_ERROR',
                                  "/bin/rm -f $file", "$rm_exit",
                                  "@results $!"
                                  );
            $CFM::DEBUG
              && print $::LOG_FILE_HANDLE "Exit removeFiles file=$file\n";
            return 1;
        }
        else
        {
            $CFM::DEBUG
              && print $::LOG_FILE_HANDLE "Exit removeFiles file=$file\n";
            return 0;
        }
    }
    else
    {

        #print "since we arent transfering this directory $file, we are also not transfering any sub files.\n";
        my @results = `/bin/rm -rf $file/* 2>&1`;
        my $rm_exit = $? >> 8;
        if ($rm_exit != 0)
        {
            MessageUtils->message(
                                  'E255',                'EMsgCOMMAND_ERROR',
                                  "/bin/rm -rf $file/*", "$rm_exit",
                                  "@results $!"
                                  );
            $CFM::DEBUG
              && print $::LOG_FILE_HANDLE "Exit removeFiles file=$file\n";
            return 1;
        }
        else
        {
            rmdir($file);
            $CFM::DEBUG
              && print $::LOG_FILE_HANDLE "Exit removeFiles file=$file\n";
            return 0;
        }
    }
}

sub determineHostName
{
    # if there exist lsrsrc-api then
    #  try to read IBM.ManageServer Class to determine name
    if (-e "/usr/bin/lsrsrc-api")
    {    # is rsct installed
        my $CMD;
        if ($::MSisHMC == 1)
        {    # is the MS an HMC
            $CMD =
              "/usr/bin/lsrsrc-api -s IBM.ManagementServer::\"ManagerType IN ('HMC')\"::::LocalHostname";
        }
        else
        {
            $CMD =
              "/usr/bin/lsrsrc-api -s IBM.ManagementServer::\"ManagerType IN ('CSM')\"::::LocalHostname";
        }
        my @output = NodeUtils->runcmd($CMD, 0); # display if error but continue
        if ($::RUNCMD_RC == 0)
        {                                        # got the name
            $::hostname = $output[0];    # could be hostname or ip address
            $::ip       = $output[0];
        }
    }    # end rsct installed
    if (!$::hostname)
    {    #either no rsct or error
            # determine hostname  from the node
        my $hostname_cmd;
        if (NodeUtils->isAIX() == 1)
        {
            $hostname_cmd = "/usr/bin/hostname";
        }
        elsif (NodeUtils->isLinux() == 1)
        {
            $hostname_cmd = "/bin/hostname";
        }
        else
        {

            #just use hostname command that is in the path
            $hostname_cmd = "hostname";
        }
        my @thostname = NodeUtils->runcmd("$hostname_cmd");
        chomp($thostname[0]);
        $::hostname = $thostname[0];
        my ($hostname, $ip) = NetworkUtils->getHost($::hostname);
        $::hostname = $hostname;
        $::ip       = $ip;
    }
    return;

}
