#!/bin/sh # # Enable a newly installed package to run or enable an existing package # to run after a kernal upgrade. # init_I18N() { type -P gettext.sh > /dev/null 2>&1 && \ type -p gettext >/dev/null 2>&1 if [ $? -ne 0 ]; then gettext() { printf "$*" } eval_gettext() { eval echo "$*" } else . gettext.sh TEXTDOMAIN=EMCpower export TEXTDOMAIN TEXTDOMAINDIR=$PPBASE/i18n/catalog export TEXTDOMAINDIR export RPM_INSTALL_PREFIX fi } report_error() { eval_gettext "$*" echo } error_exit() { exit 1 } ######## ### DO NOT REMOVE THE FOLLOWING LINE ##################################################################### ### Start of Global variable section. ##################################################################### # Temp directory used by the %pre section. (Before $PP_I_DIR exists.) # PRE_TMP=/var/tmp # # Default installation directory for emcpower # PP_I_DIR=/etc/opt/emcpower # # The file name of the device file used to control the power path drivers # # created by sysfs in 2.6. #POWER_DEV=/dev/emcpower # # Installation log file # POWER_LOG=$PP_I_DIR/log # # Installation temporary file directory. # POWER_TMP_DIR=$PP_I_DIR/.tmp # # Backup archives used in case an installation is aborted in mid-stream # POWER_BACKUP_ARCHIVE=$PP_I_DIR/.archive.tar POWER_BACKUP_ARCHIVE_NEW=$PP_I_DIR/.archive_NEW.tar archive_dir=$RPM_INSTALL_PREFIX/etc/emc/archive # # Patch log file in case of patch errors # PATCH_LOG=$PP_I_DIR/patch.log # # local modprobe file modprobe_file=/etc/modprobe.conf.pp ##################################################################### ### End of Global variable section. ##################################################################### ######## #----------------------------------------------------------------------- # Restore system to the state that existed before this script ran. # First, execute the commands in the undo list in reverse order, then # untar archived files. #----------------------------------------------------------------------- rollback() { awk '{print NR,$0}' $POWER_TMP_DIR/undolist | \ sort -nr | \ cut -d' ' -f2- > $POWER_TMP_DIR/undo.sh sh $POWER_TMP_DIR/undo.sh tar -x --absolute-names --file $POWER_TMP_DIR/rollbackfiles.tar } #----------------------------------------------------------------------- # Delete the files containing rollback information #----------------------------------------------------------------------- rollback_reset() { rm -f $POWER_TMP_DIR/rollbackfiles.tar rm -f $POWER_TMP_DIR/undolist rm -f $POWER_TMP_DIR/undo.sh } #----------------------------------------------------------------------- # Save a directory and contained files in the rollback archive #----------------------------------------------------------------------- save_dirtree_for_rollback() { local pathname case "$1" in /*) pathname=$1 ;; *) pathname=`pwd`/$1 esac tar --append --preserve-permissions --absolute-names \ --file $POWER_TMP_DIR/rollbackfiles.tar $pathname } #----------------------------------------------------------------------- # Save a file or directory in the rollback archive #----------------------------------------------------------------------- save_for_rollback() { local pathname case "$1" in /*) pathname=$1 ;; *) pathname=`pwd`/$1 esac tar --append --preserve-permissions --absolute-names \ --no-recursion --file $POWER_TMP_DIR/rollbackfiles.tar $pathname } #----------------------------------------------------------------------- # Add a shell command, for execution during rollback, to the undo list #----------------------------------------------------------------------- add_to_undolist() { echo "cd `pwd`; $*" >> $POWER_TMP_DIR/undolist } #----------------------------------------------------------------------- # Move a file in such a way that the original source and destination # files are restored by rollback. #----------------------------------------------------------------------- move_file() { save_for_rollback $1 local dest if [ -d "$2" ]; then dest=$2/`basename $1` else dest=$2 fi if [ -f "$dest" ]; then save_for_rollback $dest else add_to_undolist rm -f "$dest" fi mv -f $1 $dest } #----------------------------------------------------------------------- # Copy a new file over an old one,in such a way that the old file # can be restored during rollback. #----------------------------------------------------------------------- copy_file() { local dest if [ -d "$2" ]; then dest=$2/`basename $1` else dest=$2 fi if [ -f "$dest" ]; then save_for_rollback $dest else add_to_undolist rm -f "$dest" fi cp -pf $1 $2 } #----------------------------------------------------------------------- # Save a file in the rollback archive and then delete it. #----------------------------------------------------------------------- remove_file() { if [ -f "$1" ]; then save_for_rollback $1 rm -f $1 fi } #----------------------------------------------------------------------- # Save a directory and contained files in the rollback archive and then # delete them. #----------------------------------------------------------------------- remove_dirtree() { if [ -d "$1" ]; then save_dirtree_for_rollback $1 rm -rf $1 fi } #----------------------------------------------------------------------- # Make a directory in such a way that it will be removed during rollback. # All non-existent directories in the given path are created. If the # directory already exists, do nothing. #----------------------------------------------------------------------- make_dir() { if [ ! -d "$1" ]; then make_dir2 $1 && chmod 755 $1 && chown root:root $1 fi } make_dir2() { if test ! -d "$1" && make_dir2 `dirname $1`; then add_to_undolist rmdir $1 mkdir $1 fi } #----------------------------------------------------------------------- # Copy a kernel module and set file pwermissions, group and owner #----------------------------------------------------------------------- copy_mod() { copy_file "$1" "$2" && chmod 644 "$2" && chown root:root "$2" } #----------------------------------------------------------------------- # Compare the vermagic string of the pp base driver to the vermagic string # of the scsi_mod driver. # input: path to PowerPath base driver, in package area. # return 0 for matching strings. #----------------------------------------------------------------------- verify_driver_vermagic() { local pp_drv_path=$1 local ref_driver local ref_mod_version local pp_mod_version ref_driver=`lsmod | cut -d' ' -f1 | tail -1` ref_mod_version=`modinfo $ref_driver | \ sed -n '/^vermagic:/s/^vermagic: *//p'` pp_mod_version=`modinfo $pp_drv_path | \ sed -n '/^vermagic:/s/^vermagic: *//p'` ref_mod_version=`echo $ref_mod_version | cut -d' ' -f2-` pp_mod_version=`echo $pp_mod_version | cut -d' ' -f2-` if [ "$pp_mod_version" = "$ref_mod_version" ] then true else false fi } #----------------------------------------------------------------------- # Recursively load all modules on the command line, in the order given. # If any module fails to load, all previously loaded modules are unloaded # and a non-zero status is returned. After all modules are loaded, emcpioc # is unloaded. #----------------------------------------------------------------------- load_all_modules() { local mod=`basename "$1" .ko.gz` if [ "$1" ]; then shift 1 fi if [ -z "$mod" ] then if lsmod | grep emcpioc > /dev/null then modprobe -r emcpioc fi return 0 fi if modprobe -q $mod then load_all_modules $* if [ $? -ne 0 ] then modprobe -r $mod return 1 fi else report_error 'PowerPath could not load module $mod' return 1 fi } #----------------------------------------------------------------------- # Run "preinstall" checks to make sure the running system is compatible # with this package. #----------------------------------------------------------------------- validate_install_target() { ## Check - Require effective UID of root if [ $(id -u) != 0 ]; then report_error "You must be root to install PowerPath." error_exit fi ## Check - Only install on a 2.6 kernel expr `uname -r` : '2\.6' > /dev/null if [ $? -ne 0 ]; then report_error "This PowerPath package does not support this kernel." error_exit fi ## Check vendor if expr \( "$EXPECTED_VENDOR" : ".*$VENDOR" \) = 0 >/dev/null; then report_error 'This package requires a $VENDOR_NAME Platform.' error_exit fi ## Check vendor rev. check_vendor_rev $EXPECTED_OS_REV if [ $? != 0 ]; then report_error 'This package requires $VENDOR_NAME $VENDOR_OS_NAME.' error_exit fi ## Check hardware platform if [ "`uname -m`" != "$EXPECTED_ISA" ]; then report_error \ 'This package requires the $VENDOR_NAME $EXPECTED_ISA kernel.' error_exit fi ## Check - Make sure no devices are in use. if [ "`/sbin/lsmod | grep -w emcp`" != "" ]; then /sbin/powermt save > /dev/null 2>&1 /sbin/powermt remove dev=all > /dev/null 2>&1 if [ "`powermt display dev=all 2>&1 | grep "not found"`" = "" ]; then report_error \ "Unable to remove devices from the PowerPath configuration." report_error \ "Please make sure no PowerPath devices are in use and retry." /sbin/powermt config > /dev/null 2>&1 /sbin/powermt load > /dev/null 2>&1 error_exit fi /sbin/powermt config > /dev/null 2>&1 /sbin/powermt load > /dev/null 2>&1 fi ## Check driver vermagic verify_driver_vermagic /lib/modules/$(uname -r)/kernel/drivers/block/emcp.ko.gz if [ $? != 0 ]; then report_error 'PowerPath driver version magic mismatch' error_exit fi } #----------------------------------------------------------------------- # Compare the version recorded in the saved powermt.custom file with the # version in $1. If the two versions match, return true. #----------------------------------------------------------------------- matching_saved_config_version() { local saved local Mmp if [ -f $archive_dir/powermt.custom.saved ]; then Mmp=`expr "$1" : '\([0-9]\.[0-9]\.[0-9]\)'` saved=`head -1 $archive_dir/powermt.custom.saved | cut -d: -f3` if [ "$saved" = "$Mmp" ]; then /bin/true else /bin/false fi else /bin/false fi } #----------------------------------------------------------------------- # Move files from the PowerPath archive directory to their normal # installed locations. #----------------------------------------------------------------------- restore_configuration_files() { for file in /etc/emc/mpaa.lams \ /etc/emc/mpaa.excluded \ /etc/emcp_registration do if [ -f $archive_dir/`basename $file`.saved ]; then move_file $archive_dir/`basename $file`.saved $file fi done if test "$PP_VERSION" || \ matching_saved_config_version $NEW_PP_VERSION; then for file in /etc/powermt.custom \ /etc/emcp_devicesDB.dat \ /etc/emcp_devicesDB.idx do if [ -f $archive_dir/`basename $file`.saved ]; then move_file $archive_dir/`basename $file`.saved $file fi done if [ -f $archive_dir/*.FCS ]; then for file in $archive_dir/*.FCS do move_file $file /etc done fi fi } #----------------------------------------------------------------------- # tps_managed_classes - Return a true status if third party software # is installed to manage the array named in $1. Otherwise return a # false status. #----------------------------------------------------------------------- tps_managed() { case "$1" in hitachi) test -d /opt/DynamicLinkManager/bin ;; hpxp) test -d /usr/src/Autopath ;; ess) test -d /opt/IBMsdd/bin ;; hphsx) test -d /etc/CPQswsp/modules ;; *) /bin/false ;; esac } #----------------------------------------------------------------------- # prev_unmanaged - Return a true status if the array named in $2 # is marked "unmanaged" in the mpaa.lams file named in $1 #----------------------------------------------------------------------- prev_unmanaged() { grep -w $2 $1 2>/dev/null | grep -w unmanaged > /dev/null 2>&1 } #----------------------------------------------------------------------- # If no mpaa.lams file exists, create one. Otherwise create a temporary # mpaa.lams file, compare it to the existing file and replace the # existing file with the temporary one if the two files differ. Also, # create an empty mpaa.excluded if one doesn't exist #----------------------------------------------------------------------- update_lams_files() { local lams_file="/etc/emc/mpaa.lams" local lams_excluded_file="/etc/emc/mpaa.excluded" local lams_new if [ -f "$lams_file" ]; then lam_ref=$lams_file lams_new=${lams_file}.tmp else lam_ref=/dev/null lams_new=$lams_file fi echo "global:version:$PP_VERSION" > $lams_new for array in symm clariion hpxp hitachi hphsx ess invista do if tps_managed $array || prev_unmanaged $lam_ref $array then echo "unmanaged:$array" >> $lams_new else echo "managed:$array" >> $lams_new fi done if [ "$lams_new" = "${lams_file}.tmp" ]; then if /usr/bin/diff -q $lams_file $lams_new >/dev/null 2>&1; then rm -f $lams_new else mv -f $lams_new $lams_file fi else add_to_undolist rm -f $lams_file fi if [ ! -f "$lams_excluded_file" ]; then echo "global:version:$PP_VERSION" > $lams_excluded_file add_to_undolist rm -f $lams_excluded_file fi } #----------------------------------------------------------------------- # Add lines to /etc/modprobe.conf.pp to coordinate filter driver, hba # driver and emcp loading #----------------------------------------------------------------------- modprobe_add_pp_lines() { local begin_tag='#begin-hba-' local end_tag='#end-hba-' local install_hba if test "$1"; then modprobe_hba=' /sbin/modprobe pp_hba;' install_hba= for hba in $* do install_hba="$install_hba /sbin/modprobe $hba;" done fi if grep "^${begin_tag}$1" $modprobe_file > /dev/null; then return # Lines for this hba are already in place else # Lines indented with tab!! cat <<-EOF >> $modprobe_file ${begin_tag}${hba} install emcp /sbin/modprobe pp_hba; /sbin/modprobe emcp --ignore-install install pp_hba ${install_hba} ${end_tag}${hba} EOF fi } #----------------------------------------------------------------------- # Create dependencies between HBA drivers and power path drivers. #----------------------------------------------------------------------- create_hba_dependencies() { local added_dependency=FALSE local searchDir=/lib/modules/`uname -r` local qlaHbaName local emulexHbaName qlaHbaName=`find /proc/scsi -name qla2xxx | sed -e 's/\/proc\/scsi\///'` emulexHbaName=`find /proc/scsi -name lpfc* | sed -e 's/\/proc\/scsi\///'` if [ "$qlaHbaName" ]; then qla_hbas=`lsmod | cut -d' ' -f1 | grep qla | grep -v qla2xxx` modprobe_add_pp_lines $qla_hbas added_dependency=TRUE fi # We no longer support mixed vendor types, so we will not add the # emulex driver if the qla driver exists. if [ "$emulexHbaName" != "" -a "$added_dependency" = FALSE ]; then emulexHbaName=lpfcdd modloc=`find $searchDir -name ${emulexHbaName}.ko -print` if [ "$modloc" != "" ]; then modprobe_add_pp_lines $emulexHbaName fi fi } #----------------------------------------------------------------------- # Add PowerPath parameters to modprobe.conf.pp file #----------------------------------------------------------------------- update_driver_parameters() { local lams_file="/etc/emc/mpaa.lams" local lams_excluded_file="/etc/emc/mpaa.excluded" ## Attach the PP file to modprobe.conf grep '^##*BEGINPP' /etc/modprobe.conf >/dev/null 2>&1 if [ $? -ne 0 ]; then save_for_rollback /etc/modprobe.conf cat <<-EOF >> /etc/modprobe.conf ## Lines indented with tabs!!! ###BEGINPP include /etc/modprobe.conf.pp ###ENDPP EOF fi copy_file /etc/modprobe.d/$(uname -r)/EMCpower.conf $modprobe_file ## Update the managedclass parameter line mng_class=`awk -F: '$1 ~ /^managed$/ {print $2}' $lams_file | \ xargs | \ sed -e 's/ /,/g' \ -e 's/^/options emcp managedclass=/'` grep "options emcp managedclass" $modprobe_file > /dev/null 2>&1 if test $? -ne 0 ; then echo "$mng_class" >> $modprobe_file else sed -i "s/^options emcp managedclass=.*/${mng_class}/" \ $modprobe_file fi ## Update the excludedvolumes parameter line. if [ `wc -l $lams_excluded_file | awk '{print $1}'` -gt 1 ] ; then excluded=`awk -F: '{print $2","}' $lams_excluded_file |\ grep -v version |\ sed '$s/.$//' |\ tr -d '\012'` if [ "$excluded" ] ; then if grep "options emcp excludedvolumes" \ $modprobe_file > /dev/null 2>&1 ; then sed -i "/excludedvolumes/c\ options emcp excludedvolumes=$excluded" $modprobe_file else echo "options emcp excludedvolumes=$excluded" >> $modprobe_file fi fi fi ## Link hba drivers and powerpath drivers create_hba_dependencies } #----------------------------------------------------------------------- # update_boot_logic - Tie PowerPath start into system startup logic. #----------------------------------------------------------------------- update_boot_logic() { case "$VENDOR" in suse) MDFILE=/etc/init.d/boot.md grep boot.powerpath $MDFILE >> /dev/null 2>>/dev/null if [ $? -eq 1 ]; then save_for_rollback $MDFILE string=`grep "# Required-Start:" $MDFILE` sed --in-place -e "s/$string/$string boot.powerpath/" $MDFILE if [ -x /sbin/insserv ]; then /sbin/insserv fi fi ;; redhat|asianux) ETCFILE=/etc/rc.d/rc.sysinit grep BEGINPP $ETCFILE >> /dev/null 2>>/dev/null if [ $? -eq 1 ]; then save_for_rollback $ETCFILE sed --in-place -e '/remount,rw \//a \ ###BEGINPP\ # Configure and initialize PowerPath.\ if [ -f /etc/init.d/PowerPath ]; then\ /etc/init.d/PowerPath start\ fi\ ###ENDPP' $ETCFILE fi ;; esac } #----------------------------------------------------------------------- # Delete old modules in all /lib/modules subdirectories, then install # the new modules. #----------------------------------------------------------------------- install_driver_modules() { return # already cames from rpm package for os in /lib/modules/* do case $os in /lib/modules/`uname -r`) for modpath in $os/powerpath/* do remove_file $modpath done ;; *) remove_dirtree $os/powerpath ;; esac for modpath in $os/extra/emcp* do remove_file $modpath done done ## Install new modules. MODULE_BASE=/lib/modules/`uname -r` test -d $MODULE_BASE/powerpath || make_dir $MODULE_BASE/powerpath for mod in `ls $PPBASE/bin/driver/$TAG` do copy_mod $PPBASE/bin/driver/$TAG/$mod $MODULE_BASE/powerpath/$mod done depmod } ##-------------------------------- Main ------------------------------## PATH=/sbin:/usr/sbin:/bin:/usr/bin; export PATH cmd_path=`dirname $0` PPBASE=`(cd $cmd_path;pwd)` test -f $PP_I_DIR/.pp_version && PP_VERSION=`cat $PP_I_DIR/.pp_version` test -f $PP_I_DIR/.new_pp_version && \ NEW_PP_VERSION=`cat $PP_I_DIR/.new_pp_version` test -f $PP_I_DIR/.os_version && OS_VERSION=`cat $PP_I_DIR/.os_version` VENDOR=`rpm --showrc | awk '$2 == "_vendor" {print $3}'` ### ### DO NOT REMOVE THE FOLLOWING LINE # SuSE x86_64 specific: # EXPECTED_VENDOR=pld VENDOR_NAME=PLD VENDOR_OS_NAME=SLES10SP1 EXPECTED_ISA=x86_64 EXPECTED_OS_REV=ac MIN_UPGRADE_REV=5.0.0 check_vendor_rev() { if [ "`awk '/Ac/{print 1}' /etc/pld-release`" == "1" ] then sles_version=$EXPECTED_OS_REV else sles_version="" fi test "$1" = "$sles_version" } #----------------------------------------------------------------------- # identify_platform - # Determine the set of drivers files compatible with the /lib/modules # subdirectory passed as an argument and echo the tag for that set of # drivers to stdout. #----------------------------------------------------------------------- identify_platform() { local platform= case "$1" in *-smp) platform=sles${EXPECTED_OS_REV}smp_x8664 ;; *-xen) platform=sles${EXPECTED_OS_REV}xensmp_x8664 ;; *) if [ "$1" = `uname -r` ] then pre_error "This PowerPath package only supports the SuSE SLES x86_64 smp kernel" cleanup_error_exit fi ;; esac test "$platform" && echo $platform } ### init_I18N rollback_reset validate_install_target make_dir /etc/emc/ppme test "$NEW_PP_VERSION" && restore_configuration_files update_lams_files update_driver_parameters install_driver_modules ## Load new modules. Emcplib and emcp first, emcpdm and emcpioc last ## with everything else in between. load_all_modules `cd /lib/modules/$(uname -r)/kernel/drivers/block/; ls emc* | \ awk '/^emcplib.ko.gz$/ {order[$1]=1; next} /^emcp.ko.gz$/ {order[$1]=2; next} /^emcpdm.ko.gz$/ {order[$1]=4; next} /^emcpioc.ko.gz$/ {order[$1]=5; next} /^emcp/ {order[$1]=3; next} END { for (mod in order) print order[mod],mod;}' |\ sort -n | \ cut -d' ' -f2` ## If error loading modules, roll back everything done to this point and ## return an error status. if [ $? -ne 0 ] then report_error "Error loading PowerPath kernel modules" rollback rollback_reset depmod error_exit fi ## If kernel has changed make sure PowerPath will still start at boot time if [ "$OS_VERSION" != `uname -r` ]; then update_boot_logic fi if [ $? -ne 0 ] then report_error "Error installing PowerPath" rollback rollback_reset error_exit fi ## Update state info in /etc/opt/emcpower rm -f $PP_I_DIR/.os_version uname -r > $PP_I_DIR/.os_version rm -f $PP_I_DIR/.prev_pp_version if [ -f $PP_I_DIR/.new_pp_version ] then test -f $PP_I_DIR/.pp_version && \ mv -f $PP_I_DIR/.pp_version $PP_I_DIR/.prev_pp_version mv $PP_I_DIR/.new_pp_version $PP_I_DIR/.pp_version fi