#!/bin/sh
# IBM(c) 2007 EPL license http://www.eclipse.org/legal/epl-v10.html

#-------------------------------------------------------------------------------
#=head1  otherpkgs  - only runs on Linux
#=head2  It gets the extra rpms and install/update them.
#        The environment variable OTHERPKGS contains the rpms to be installed/updated.
#        On MN, You need to:
#        1. put rpms under /install/post/otherpkgs/os/arch directory where 'os' and 'arch'
#           can be found in the nodetype table.
#        2. put the name of the packages to /opt/xcat/share/xcat/netboot(install)/platform
#           directory. The file name is one of the following:
#              profile.os.arch.otherpkgs.pkglist  
#              profile.os.otherpkgs.pkglist   
#              profile.arch.otherpkgs.pkglist  
#              profile.otherpkgs.pkglist
#   The install/deployment process will pick up the rpms and install them on the nodes. 
#   However, if the nodes have already installed and up and running, you can run the following
#   command to have the extra rpms installed:
#       updatenode noderange otherpkgs 
#   
#=cut
#-------------------------------------------------------------------------------

# pmatch determines if 1st argument string is matched by 2nd argument pattern

pmatch ()
{
  case $1 in
    $2) return 0;;  # zero return code means string matched by pattern
  esac

  return 1          # non-zero return code means string not matched by pattern
}

##
##  The following routines implement the notion of an array.
##  The POSIX shell does not support arrays.
##  With these routines, an array conceptually called "my_array" is implmented using the following series
##  of variables:
##
##      my_array__ARRAY_HIGH_INDEX  - holds the highest index used in the array
##      my_array__0   - value for element 0 of my_array: my_array[0] in standard array notation
##      my_array__1   - value for element 1 of my_array: my_array[1] in standard array notation
##         .                             .
##         .                             .
##         .                             .
##

#
#  array_empty - make the array whose name is given by $1 empty (with no elements).
#
#    sample call:  array_empty my_array
#

array_empty ()
{
    local array_name="$1"
    local high_ndx_varname="${array_name}__ARRAY_HIGH_INDEX"
    local elem_varname
    local high_ndx
    local ndx

    # Determine current element count

    eval "high_ndx=\$${high_ndx_varname}"

    if [ -z "$high_ndx" ]; then
        return
    fi

    # Unset all previously defined element variables and the high index variable

    ndx=0

    while [ $ndx -le $high_ndx ]; do
        elem_varname="${array_name}__${ndx}"

        eval "unset ${elem_varname}"

        ndx=$(expr $ndx + 1)
    done

    eval "unset ${high_ndx_varname}"
}

#
#  array_get_size - return the size of the array whose name is given by $1.
#                   The size, which is the highest index plus one is written to standard output
#
#    sample call:  size=$(array_get_size my_array)
#

array_get_size ()
{
    local array_name="$1"

    local high_ndx_varname="${array_name}__ARRAY_HIGH_INDEX"
    local high_ndx

    eval "high_ndx=\$${high_ndx_varname}"

    if [ -z "$high_ndx" ]; then
        high_ndx=-1
    fi

    echo $(expr $high_ndx + 1)
}

#
#  array_set_element - set an element to a value.  $1 is the array name, $2 is the element index,
#                      $3 is the element value.
#
#    sample call:  array_set_element my_array index "the new element value"
#

array_set_element ()
{
    local array_name="$1"
    local ndx="$2"
    local elem_value="$3"
    local high_ndx_varname="${array_name}__ARRAY_HIGH_INDEX"
    local elem_varname
    local high_ndx

    # Set specified element to specified value

    elem_varname="${array_name}__${ndx}"

    eval "${elem_varname}=\"${elem_value}\""

    # Adjust high index

    eval "high_ndx=\$${high_ndx_varname}"

    if [ -z "$high_ndx" ]; then
        high_ndx=-1
    fi

    if [ $ndx -gt $high_ndx ]; then
        eval "${high_ndx_varname}=${ndx}"
    fi
}

#
#  array_get_element - get an element's value.  $1 is the array name, $2 is the element index.
#
#    sample call:  value=$(array_get_element my_array index)
#

array_get_element ()
{
    local array_name="$1"
    local ndx="$2"

    eval "echo \"\$${array_name}__${ndx}\""
}

##
##  End of set routines.
##

##
## Begin the means to update apt's view of Ubuntu repo's if necessary.
##

# *** IMPORTANT *** IMPORTANT *** IMPORTANT *** IMPORTANT *** IMPORTANT ***
# Call apt_get_update_if_repos_changed before ALL apt-* calls. Examples:
#
# apt_get_update_if_repos_changed $REPOFILE
# apt-get install $PACKAGES
#
# apt_get_update_if_repos_changed $REPOFILE
# apt-get -y remove $repo_pkgs_postremove
# *** IMPORTANT *** IMPORTANT *** IMPORTANT *** IMPORTANT *** IMPORTANT ***

prev_ubuntu_repo_lastmod=

# required argument: REPOFILE
apt_get_update_if_repos_changed()
{
	# Obtain file last modification timestamp. Ignore stderr because file
	# non-existence is not an error, but just another indication of modification.
	# It's okay if REPOFILE isn't set because that is interpreted as acceptable
	# file non-existence.
	curr_ubuntu_repo_lastmod=`stat -c "%y" $1 2>/dev/null`

	if [ "$prev_ubuntu_repo_lastmod" != "$curr_ubuntu_repo_lastmod" ];then
		apt-get -y update 1>/dev/null 2>/dev/null
		prev_ubuntu_repo_lastmod=$curr_ubuntu_repo_lastmod
	fi
}

##
## End the means to update apt's view of Ubuntu repo's if necessary.
##
# Main - start of other pkgs
#do nothing for diskless deployment case because it is done in the image already

if [ "$(uname -s)" = "AIX" ]; then
      logger -p local4.info -t xcat "otherpkgs not support on AIX, exiting "
      exit 0
  else
      logger -p local4.info -t xcat "Running otherpkgs "
fi
if [ -z "$UPDATENODE" ] || [ $UPDATENODE -ne 1 ]; then
  if [ "$NODESETSTATE" = "netboot" -o \
     "$NODESETSTATE" = "statelite" -o \
     "$NODESETSTATE" = "diskless" -o \
     "$NODESETSTATE" = "dataless" ] 
  then
      echo "  Did not install any extra rpms."
      exit 0
  fi
fi 

if [ -z "$OTHERPKGS_INDEX" ]; then 
  echo "$0: no extra rpms to install"
  exit 0
fi

if [ -z "$NFSSERVER" ]; then
    NFSSERVER=$MASTER
fi

if [ -z "$INSTALLDIR" ]; then
    INSTALLDIR="/install"
fi

#check if /install is mounted on the server
mounted=0;
result=`mount |grep " $INSTALLDIR " |grep $NFSSERVER`
if [ $? -eq 0 ]; then
    mounted=1 
fi



#OTHERPKGDIR is set only when the provmethod is the os image name
#when it is not set, we need to figure it out here
if [ -z "$OTHERPKGDIR" ]; then
    if [ $mounted -eq 0 ]; then
        OTHERPKGDIR="$NFSSERVER$INSTALLDIR/post/otherpkgs/$OSVER/$ARCH"
    else
        OTHERPKGDIR="$INSTALLDIR/post/otherpkgs/$OSVER/$ARCH"
    fi
else
    if [ $mounted -eq 0 ]; then
	OTHERPKGDIR=${NFSSERVER}${OTHERPKGDIR}
    fi
fi

echo NFSSERVER=$NFSSERVER
logger -p local4.info -t xcat "NFSSERVER=$NFSSERVER"
echo OTHERPKGDIR=$OTHERPKGDIR
logger -p local4.info -t xcat "OTHERPKGDIR=$OTHERPKGDIR"

#if [ -x "/sbin/dhcpcd" ]; then
#    dhcpcd -n $PRIMARYNIC
#fi

#check if the node has yum or zypper installed, it will try yum first, then zypper and last rpm 
# for rpm based machines, or check if apt is installed, then it will use apt then dpkg 

hasrpm=0
hasyum=0  
haszypper=0        
hasapt=0
hasdpkg=0
supdatecommand="rpm -Uvh --replacepkgs"
sremovecommand="rpm -ev"

result=`which rpm`
if [ $? -eq 0 ]; then
    hasrpm=1
    result=`rpm -q yum`
    if [ $? -eq 0 ]; then 
        hasyum=1
    else
        result=`rpm -q zypper`
        if [ "$?" = "0" ]; then 
	    haszypper=1
        fi
    fi 
else
    result=`which dpkg`
    if [ $? -eq 0 ]; then
        hasdpkg=1
	supdatecommand="dpkg -i"
	sremovecommand="dpkg -r"
	result=`dpkg -l apt`
	if [ $? -eq 0 ]; then
	    hasapt=1
	fi
    fi
fi


# To support the #NEW_INSTALL_LIST# entry in otherpkgs.pkglist files,
# multiple lists of packages are provided to this script in the form:
#  OTHERPKGS1, OTHERPKGS2, ... OTHERPKSn where n=OTHERPKGS_INDEX
# Each sublist will be installed in a separate call (separate pass
#  through this loop)
op_index=1
#echo "OTHERPKGS_INDEX = $OTHERPKGS_INDEX"
while [ $op_index -le $OTHERPKGS_INDEX ]; do 
    eval pkglist=\$OTHERPKGS$op_index
    eval envlist=\$ENVLIST$op_index
    #echo "pkglist = $pkglist"
    if [ $hasyum -eq 1 ]; then 
        mkdir -p /etc/yum.repos.d
        result=`rm /etc/yum.repos.d/xCAT-otherpkgs*.repo 2>&1`
        result=`yum clean all`
        repo_base="/etc/yum.repos.d"
    elif [ $haszypper -eq 1 ]; then
        #remove old repo
        old_repo=`zypper sl |grep xcat-otherpkgs | cut -f2 -d '|'`
        for x in $old_repo
        do
            result=`zypper sd $x`
        done
	zypper --non-interactive refresh
        repo_base="/tmp"
    elif [ $hasapt -eq 1 ] ; then
        mkdir -p /etc/apt/sources.list.d
	result=`rm /etc/apt/sources.list.d/xCAT-otherpkgs*.list 2>&1`
	repo_base="/etc/apt/sources.list.d"
    fi

    array_empty repo_path
    repo_pkgs=""
    repo_pkgs_preremove=""
    repo_pkgs_postremove=""
    plain_pkgs=""
    plain_pkgs_preremove=""
    plain_pkgs_postremove=""
    array_empty handled_path
    for x in `echo "$pkglist" | tr "," "\n"`
    do
        #check if the file name starts with -- or -. 
        #If it is start with -,  then the rpm must be removed before installing other packages
        #If it is start with --, then the rpm will be removed after  installing other packages
        string_type=0;  #nornmal rpm
        pos=`expr index  $x -`
        if [ $pos -eq 1 ]; then 
	    x=`echo ${x#-}`
	    pos=`expr index  $x -`
	    if [ $pos -eq 1 ]; then
	        x=`echo ${x#-}`
	        string_type=1  #start with --
	    else
	        string_type=-1 #start with -
	    fi
        fi
	    
        if [ $hasyum -eq 0 ] && [ $haszypper -eq 0 ] && [ $hasapt -eq 0 ]; then
	    if [  $string_type -eq -1 ]; then
	        plain_pkgs_preremove="$plain_pkgs_preremove $x"
            elif [  $string_type -eq 1 ]; then
	        plain_pkgs_postremove="$plain_pkgs_postremove $x"
	    else
	        plain_pkgs="$plain_pkgs $x*"
	    fi
	    continue
        fi

        if [  $string_type -eq -1 ]; then
	    repo_pkgs_preremove="$repo_pkgs_preremove $x"
        elif [  $string_type -eq 1 ]; then
	    repo_pkgs_postremove="$repo_pkgs_postremove $x"
        else
	    fn=`basename $x`
	    path=`dirname $x`
	    whole_path=$OTHERPKGDIR/$path
	    #whole_path=$OTHERPKGDIR
	
            #find out if this path has already handled
	    try_repo=1
	    rc=1
	    i=0
	    while [ $i -lt $(array_get_size handled_path) ]; do
	        if [ $(array_get_element handled_path $i) = $path ]; then
		    try_repo=0
		    j=0
		    while [ $j -lt $(array_get_size repo_path) ]; do
		        if [ $(array_get_element repo_path $j) = $path ]; then
			    rc=0
			    break
		        fi
		        j=$((j+1))
		    done
		    break 
	        fi
	        i=$((i+1))
	    done
	
	
            #try to add the path to the repo
	    if [ $try_repo -eq 1 ]; then
	        index=$(array_get_size repo_path)
		if [ $hasyum -eq 1 ] || [ $haszypper -eq 1 ] ; then
	            REPOFILE="$repo_base/xCAT-otherpkgs$index.repo"
	            echo "[xcat-otherpkgs$index]" > $REPOFILE
	            echo "name=xcat-otherpkgs$index" >> $REPOFILE
	            if [ $mounted -eq 0 ]; then
		        echo "baseurl=http://$whole_path" >> $REPOFILE
	            else
		        echo "baseurl=file://$whole_path" >> $REPOFILE
	            fi
	            echo "enabled=1" >> $REPOFILE
	            echo "gpgcheck=0" >> $REPOFILE
		elif [ $hasapt -eq 1 ] ; then
		    REPOFILE="$repo_base/xCAT-otherpkgs$index.list"
		    
	            if [ $mounted -eq 0 ]; then
		        type=http
	            else
		        type=file
	            fi

			echo "deb $type://$whole_path /"  > $REPOFILE
		fi 
	        if [ $hasyum -eq 1 ]; then 
	            #use yum
		    result=`yum list $fn 2>&1`
		    if [ $? -eq 0 ]; then  
		        rc=0
		        array_set_element repo_path $index $path
		    else
		        rm $REPOFILE
		    fi
	        elif [ $haszypper -eq 1 ]; then
                    #use zypper
		    if ( pmatch "$OSVER" "sles11*" ); then
		        result=`zypper ar -c $REPOFILE`
		    else
		        result=`zypper sa -c $REPOFILE` 
		    fi	
		
		    result=`zypper --non-interactive refresh xcat-otherpkgs$index 2>&1`
		    if [ $? -eq 0 ]; then  
		        rc=0
		        array_set_element repo_path $index $path
		    else
		        result=`zypper sd xcat-otherpkgs$index`
		    fi
		elif [ $hasapt -eq 1 ]; then 
		    #use apt
			apt_get_update_if_repos_changed $REPOFILE
			result=`apt-cache show $fn 2>&1`

		    if [ $? -eq 0 ]; then  
		        rc=0
				array_set_element repo_path $index $path
		    else
		        rm $REPOFILE
		    fi

	        fi
	    fi
	
	    if [ $rc -eq 0 ]; then
	        repo_pkgs="$repo_pkgs $fn"  
	    else
                #now no hope we have to use rpm command
	        plain_pkgs="$plain_pkgs $x*"
	    fi
	    array_set_element handled_path $(array_get_size handled_path) $path
        fi
    done  

    #now update all the existing rpms
    if [ $hasyum -eq 1 ]; then 
        echo "yum -y upgrade"
        result=`yum -y upgrade 2>&1`
        logger -p local4.info -t xcat "$result"
        if [ $? -ne 0 ]; then 
	    logger -p local4.info -t xcat "otherpkgs: $result"
        fi
        echo "$result"
    elif [ $haszypper -eq 1 ]; then
        echo "zypper --non-interactive update --auto-agree-with-license"
	result=`zypper --non-interactive update --auto-agree-with-license 2>&1`
        logger -p local4.info -t xcat "$result"
	if [ $? -ne 0 ]; then 
            logger -p local4.info -t xcat "otherpkgs: $result"
	fi
        echo "$result"
	elif [ $hasapt -eq 1 ]; then
		apt_get_update_if_repos_changed $REPOFILE
		echo "apt-get -y upgrade"
		result=`apt-get -y upgrade 2>&1`
		logger -p local4.info -t xcat "$result"
		if [ $? -ne 0 ]; then 
			logger -p local4.info -t xcat "otherpkgs: $result"
		fi
		echo "$result"
    fi     

    #echo "repo_pkgs=$repo_pkgs,\nrepo_pkgs_preremove=$repo_pkgs_preremove,\nrepo_pkgs_postremove=$repo_pkgs_postremove"
    #echo "plain_pkgs=$plain_pkgs,\nplain_pkgs_preremove=$plain_pkgs_preremove,\nplain_pkgs_postremove=$plain_pkgs_postremove"


    #Now we have parsed the input, let's remove rpms if is specified with -
    if [ "$repo_pkgs_preremove" != "" ]; then
        if [ $hasyum -eq 1 ]; then 
            echo "$envlist yum -y remove $repo_pkgs_preremove"
            result=`eval $envlist yum -y remove $repo_pkgs_preremove 2>&1`
            logger -p local4.info -t xcat "$result"
            if [ $? -ne 0 ]; then 
	        logger -p local4.info -t xcat "otherpkgs: $result"
            fi
            echo "$result"
        elif [ $haszypper -eq 1 ]; then
            echo "$envlist zypper remove -y $repo_pkgs_preremove"
	    result=`eval $envlist zypper remove -y $repo_pkgs_preremove 2>&1`
            logger -p local4.info -t xcat "$result"
	    if [ $? -ne 0 ]; then 
                logger -p local4.info -t xcat "otherpkgs: $result"
	    fi
            echo "$result"
		elif [ $hasapt -eq 1 ]; then
			apt_get_update_if_repos_changed $REPOFILE
			echo "apt-get -y remove $repo_pkgs_preremove"
			result=`apt-get -y remove $repo_pkgs_preremove 2>&1`
			logger -p local4.info -t xcat "$result"
			if [ $? -ne 0 ]; then 
				logger -p local4.info -t xcat "otherpkgs: $result"
			fi
			echo "$result"
        fi
    fi 

    if [ "$plain_pkgs_preremove" != "" ]; then 
        echo "$envlist $sremovecommand $plain_pkgs_preremove" 
        result=`eval $envlist $sremovecommand $plain_pkgs_preremove 2>&1`
        logger -p local4.info -t xcat "$result"
        if [ $? -ne 0 ]; then 
	    logger -p local4.info -t xcat "otherpkgs $result"
        fi
        echo "$result"
    fi


    #installation using yum or zypper     
    if [ "$repo_pkgs" != "" ]; then
        if [ $hasyum -eq 1 ]; then 
            echo "$envlist yum -y install $repo_pkgs"
            result=`eval $envlist yum -y install $repo_pkgs 2>&1`
	    logger -p local4.info -t xcat "$result"
            if [ $? -ne 0 ]; then 
                logger -p local4.info -t xcat "otherpkgs: $result"
            fi
            echo "$result"
        elif [ $haszypper -eq 1 ]; then
            echo "$envlist zypper install -y $repo_pkgs"
	    result=`eval $envlist zypper install -y $repo_pkgs 2>&1`
            logger -p local4.info -t xcat "$result"
	    if [ $? -ne 0 ]; then 
                logger -p local4.info -t xcat "otherpkgs: $result"
	    fi
            echo "$result"
            #remove the repos
            #old_repo=`zypper lr -u |grep xcat-otherpkgs | cut -f2 -d '|'`
            #for x in $old_repo
            #do
            #    result=`zypper sd $x`
            #done
	elif [ $hasapt -eq 1 ]; then
		apt_get_update_if_repos_changed $REPOFILE
	    echo "$envlist apt-get -q -y --force-yes install $repo_pkgs"
	    result=`eval $envlist apt-get -q -y --force-yes install $repo_pkgs 2>&1`
	    logger -p local4.info -t xcat "$result"
	    if [ $? -ne 0 ]; then 
            logger -p local4.info -t xcat "otherpkgs: $result"
	    fi
            echo "$result"
        fi
    fi 

    #Handle the rest with rpm
    if [ "$plain_pkgs" != "" ]; then 
        echo "Warning: the packages $plain_pkgs could not be found in the repository, falling back to rpm command, did you forget to run createrepo?"
        logger -p local4.info -t xcat "Warning: the packages $plain_pkgs could not be found in the repository, falling back to rpm command, did you forget to run createrepo?"
        if [ $mounted -eq 0 ]; then
	    dir_no_ftproot=${OTHERPKGDIR#*$INSTALLDIR/}
            mkdir -p /xcatpost/$dir_no_ftproot 
            rm -f -R /xcatpost/$dir_no_ftproot/*
            mkdir -p /tmp/postage/
            rm -f -R /tmp/postage/*
            cd /tmp/postage
    
	    wget -l inf -N -r --waitretry=10 --random-wait --retry-connrefused -e robots=off -t 0 -T 60 --reject "index.html*" --no-parent http://$OTHERPKGDIR/ 2> /tmp/wget.log  
    
            cd /tmp/postage/$NFSSERVER$INSTALLDIR
            mv $dir_no_ftproot/* /xcatpost/$dir_no_ftproot; 
            rm -rf $dir_no_ftproot
            cd /xcatpost/$dir_no_ftproot
        else
            cd $OTHERPKGDIR
        fi

        echo "$envlist $supdatecommand $plain_pkgs" 
        result=`eval $envlist $supdatecommand $plain_pkgs 2>&1`
        logger -p local4.info -t xcat "$result"
        if [ $? -ne 0 ]; then 
	    logger -p local4.info -t xcat "otherpkgs $result"
        fi
        echo "$result"
    
        if [ $mounted -eq 0 ]; then
	    cd /xcatpost
	    dir_no_ftproot=${OTHERPKGDIR#*$INSTALLDIR/}
            dir_no_ftproot=${dir_no_ftproot%%/*}
            rm -f -R $dir_no_ftproot
        fi
    fi

    #remove more rpms if specified with --
    if [ "$repo_pkgs_postremove" != "" ]; then
        if [ $hasyum -eq 1 ]; then 
            echo "$envlist yum -y remove $repo_pkgs_postremove"
            result=`eval $envlist yum -y remove $repo_pkgs_postremove 2>&1`
            logger -p local4.info -t xcat "$result"
            if [ $? -ne 0 ]; then 
                logger -p local4.info -t xcat "otherpkgs: $result"
            fi
            echo "$result"
        elif [ $haszypper -eq 1 ]; then
            echo "$envlist zypper remove -y $repo_pkgs_postremove"
	    result=`eval $envlist zypper remove -y $repo_pkgs_postremove 2>&1`
            logger -p local4.info -t xcat "$result"
	    if [ $? -ne 0 ]; then 
                logger -p local4.info -t xcat "otherpkgs: $result"
    	    fi
            echo "$result"
        elif [ $hasapt -eq 1 ]; then 
			apt_get_update_if_repos_changed $REPOFILE
            echo "$envlist apt-get -y remove $repo_pkgs_postremove"
            result=`eval $envlist apt-get -y remove $repo_pkgs_postremove 2>&1`
            logger -p local4.info  -t xcat "$result"
            if [ $? -ne 0 ]; then 
                logger -p local4.info -t xcat "otherpkgs: $result"
            fi
            echo "$result"
        fi
    fi 

    if [ "$plain_pkgs_postremove" != "" ]; then 
        echo "$envlist $sremovecommand $plain_pkgs_postremove" 
        result=`eval $envlist $sremovecommand $plain_pkgs_postremove 2>&1`
        logger -p local4.info -t xcat "$result"
        if [ $? -ne 0 ]; then 
       	    logger -p local4.info -t xcat "otherpkgs $result"
        fi
        echo "$result"
    fi

    op_index=$((op_index+1))
done

exit 0