#!/bin/sh
#****************************************************************************
#
# (C) Copyright 2004 Novell, Inc.
# All Rights Reserved.
#
# This program is an unpublished copyrighted work which is proprietary
# to Novell, Inc. and contains confidential information that is not
# to be reproduced or disclosed to any other person or entity without
# prior written consent from Novell, Inc. in each and every instance.
#
# WARNING:  Unauthorized reproduction of this program as well as
# unauthorized preparation of derivative works based upon the
# program or distribution of copies by sale, rental, lease or
# lending are violations of federal copyright laws and state trade
# secret laws, punishable by civil and criminal penalties.
#
#    Author:    Brad Rupp
#  Workfile:    ncsfuncs
#  Contents:    Bash helper functions for NCS
#
#****************************************************************************

NCSVAR_OCF_DIR=/usr/lib/ocf/resource.d/heartbeat
PATH="$PATH:/opt/novell/afptcpd/bin/:/opt/novell/bin:/opt/novell/ncs/bin"
export script_name=$0
set -x

#**********************************************************************
# Name:         exit_on_error
# Description:  Executes a command and exits the script if any errors occur
# Parameters:   <command> - The command to execute, including its parameters
# Return:       0 on success
# Notes:
#**********************************************************************
exit_on_error()
{
    eval $*
    local rc=$?
    date

    if [ "$rc" -ne 0 ]
    then
        exit $rc
    fi

    return 0

} # exit_on_error

#**********************************************************************
# Name:         ignore_error
# Description:  Executes a command and ignores any errors that may occur
# Parameters:   <command> - The command to execute, including its parameters
# Return:       Always returns 0
# Notes:
#**********************************************************************
ignore_error()
{
    check_nss_ncp_volume_dismount $script_name "$*"
    eval $*
    local rc=$?
    date
    return 0

} # ignore_error

#**********************************************************************
# Name:         del_secondary_ipv6
# Description:  Deletes a secondary IPV6 address from this node in the cluster
# Parameters:   <ip address> - Secondary IP address to delete (required)
# Return:       0 on success
# Notes:
#**********************************************************************
del_secondary_ipv6()
{
    local ip=$1; shift

    local other_options="$*"
    local dev_name=$(expr match "$other_options" '.*\(\<dev\s\+\S\+\).*')

    if [ -z "$dev_name" ]
    then
        local i=0

        while [ "$i" -lt 3 ]
        do
            dev_name=$(ip addr show to $(expr match $ip '^\([0-9a-f]*\(:[0-9a-f]*\)\+\)\(\/[0-9]\+\)\?$') 2>/dev/null | grep -o "^[0-9]\+:\s\+[^:@]\+[:@]")
            dev_name=$(expr match "$dev_name" '[0-9]\+:\s\+\([^:@]\+\)[:@]')
            if [ -n "$dev_name" ]
            then
                dev_name="dev $dev_name"
                break;
            fi
            sleep 2
            i=$(($i+1))
        done
    else
        other_options=${other_options/$dev_name/}
    fi

    if [ -z "$(expr match $ip '.*\(\/[0-9]\+\)$')" ]
    then
        local mask_width=$(ip addr show $dev_name 2>/dev/null | grep -m1 -o "^[[:space:]]*inet6[[:space:]]\+[^[:space:]]\+" | grep -o "\/[0-9]\+")
        ip="$ip$mask_width"
    fi

    if [ -n "$dev_name" ]
    then
        ip -f inet6 addr del $ip $other_options $dev_name
    fi
    return $?
}

#**********************************************************************
# Name:         del_secondary_ipaddress
# Description:  Deletes a secondary IP address from this node in the cluster
# Parameters:   <ip address> - Secondary IP address to delete (required)
# Return:       0 on success
# Notes:
#**********************************************************************
del_secondary_ipaddress()
{
    local ip=$1; shift

    if [ -n "$1" ] && [ "$1" == "-np" ]
    then
        shift
    fi

    if [ -n "$(expr match $ip '^\([0-9a-f]*\(:[0-9a-f]*\)\+\)\(\/[0-9]\+\)\?$')" ]
    then
        del_secondary_ipv6 $ip $*
        return $?
    fi

    local other_options="$*"
    local dev_name=$(expr match "$other_options" '.*\(\<dev\s\+\S\+\).*')

    if [ -z "$dev_name" ]
    then
        local i=0

        while [ "$i" -lt 3 ]
        do
            dev_name=$(ip addr show to $(expr match $ip '\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*') 2>/dev/null | grep -o "^[0-9]\+:\s\+[^:@]\+[:@]")
            dev_name=$(expr match "$dev_name" '[0-9]\+:\s\+\([^:@]\+\)[:@]')
            if [ -n "$dev_name" ]
            then
                dev_name="dev $dev_name"
                break;
            fi
            sleep 2
            i=$(($i+1))
        done
    else
        other_options=${other_options/$dev_name/}
    fi

    if [ -z "$(expr match $ip '.*\(\/[0-9]\+\)')" ]
    then
        local mask_width=$(ip addr show $dev_name 2>/dev/null | grep -m1 -o "^[[:space:]]*inet[[:space:]]\+[^[:space:]]\+" | grep -o "\/[0-9]\+")
        ip="$ip$mask_width"
    fi

    if [ -n "$dev_name" ]
    then
        ip -f inet addr del $ip $other_options $dev_name
    fi
} # del_secondary_ipaddress

#**********************************************************************
# Name:         add_secondary_ipv6
# Description:  Adds a secondary IPV6 address to this node in the cluster
# Parameters:   <ip address> - Secondary IP address to add (required)
#               -np - Skip the initial ping test (optional)
# Return:       0 on success
# Notes:
#**********************************************************************
add_secondary_ipv6()
{
    local ip=$1; shift
    destination=$(expr match $ip '^\([0-9a-f]*\(:[0-9a-f]*\)\+\)\(\/[0-9]\+\)\?$')

    if [ "" == "$destination" ]
    then
        return 11
    fi

    if [ -n "$1" ] && [ "$1" == "-np" ]
    then
        shift
    else
        if ping6 -c  1 -q -n $destination
        then
            sleep 5
            if ping6 -c  1 -q -n $destination
            then
                return 12
            fi
        fi
    fi

    local other_options="$*"
    local dev_name=$(expr match "$other_options" '.*\(\<dev\s\+\S\+\).*')

    if [ -z "$dev_name" ]
    then
        dev_name=$(ip route get $ip 2>/dev/null | grep -o '\<dev \S\+')
    else
        other_options=${other_options/$dev_name/}
    fi

    if [ -z "$(expr match $ip '.*\(\/[0-9]\+\)$')" ]
    then
        local mask_width=$(ip addr show $dev_name 2>/dev/null | grep -m1 -o "^[[:space:]]*inet6[[:space:]]\+[^[:space:]]\+" | grep -o "\/[0-9]\+")
        ip="$ip$mask_width"
    fi

    ip -f inet6 addr add $ip $other_options $dev_name
    return $?
}

#**********************************************************************
# Name:         add_secondary_ipaddress
# Description:  Adds a secondary IP address to this node in the cluster
# Parameters:   <ip address> - Secondary IP address to add (required)
#               -np - Skip the initial ping test (optional)
# Return:       0 on success
# Notes:
#**********************************************************************
add_secondary_ipaddress()
{
    local ip=$1; shift
    local destination=$(expr match $ip '\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*')

    if [ "" == "$destination" ]
    then
        add_secondary_ipv6 $ip $*
        return $?
    fi

    if [ -n "$1" ] && [ "$1" == "-np" ]
    then
        shift
    else
        if fping -q -c1 -t30 $destination
        then
            sleep 5
            if fping -q -c1 -t30 $destination
            then
                return 1
            fi
        fi
    fi

    local other_options="$*"
    local dev_name=$(expr match "$other_options" '.*\(\<dev\s\+\S\+\).*')

    if [ -z "$dev_name" ]
    then
        dev_name=$(ip route get $ip 2>/dev/null | grep -o '\<dev \S\+')
    else
        other_options=${other_options/$dev_name/}
    fi

    if [ -z "$(expr match $ip '.*\(\/[0-9]\+\)')" ]
    then
        local mask_width=$(ip addr show $dev_name 2>/dev/null | grep -m1 -o "^[[:space:]]*inet[[:space:]]\+[^[:space:]]\+" | grep -o "\/[0-9]\+")
        ip="$ip$mask_width"
    fi

    local brd_addr=$(expr match "$other_options" '.*\(\<brd\s\+\S\+\).*')
    if [ -z "$brd_addr" ]
    then
        brd_addr=$(expr match "$other_options" '.*\(\<broadcast\s\+\S\+\).*')
    fi

    if [ -z "$brd_addr" ]
    then
        brd_addr="brd +"
    else
        other_options=${other_options/$brd_addr/}
    fi

    ip -f inet addr add $ip $brd_addr $other_options $dev_name
    local rc=$?

    if [ 0 -eq "$rc" ]
    then
        arping -c 1 -A -I ${dev_name#dev} $destination
        arping -c 1 -U -I ${dev_name#dev} $destination
    fi

    return $rc
} # add_secondary_ipaddress

#**********************************************************************
# Name:         status_secondary_ipv6
# Description:  Is IP address bound on this node?
# Parameters:   <ip address> - Secondary IPV6 address to check (required)
#               -np - Skip the ping test (optional)
# Return:       0 on success
# Notes:
#**********************************************************************
status_secondary_ipv6()
{
    destination=$(expr match $1 '^\([0-9a-f]*\(:[0-9a-f]*\)\+\)\(\/[0-9]\+\)\?$')

    if [ "" == "$destination" ]
    then
        return 11
    fi

    ip addr show to $destination 2>/dev/null | grep "^[0-9]\+:\s\+\S\+:"
    local status=$?

    if [ 0 -eq "$status" ]
    then
        if [ -z "$2" ] || [ "$2" != "-np" ]
        then
            for(( i = 0; i < 5; i++ ))
            do
                ping6 -c 1 -q -n $destination
                status=$?
                if [ 0 -eq "$status" ]
                then
                    break
                fi
                sleep 2
            done
        fi
    fi

    return $status
}

#**********************************************************************
# Name:         status_secondary_ipaddress
# Description:  Is IP address bound on this node?
# Parameters:   <ip address> - Secondary IP address to check (required)
#               -np - Skip the ping test (optional)
# Return:       0 on success
# Notes:
#**********************************************************************
status_secondary_ipaddress()
{
    local destination=$(expr match $1 '\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*')
    if [ -z "$destination" ]
    then
        status_secondary_ipv6 $*
        return $?
    fi

    ip addr show to $destination 2>/dev/null | grep "^[0-9]\+:\s\+\S\+:"
    local status=$?

    if [ 0 -eq "$status" ]
    then
        if [ -z "$2" ] || [ "$2" != "-np" ]
        then
            for(( i = 0; i < 5; i++ ))
            do
                ping -c 1 -q -n $destination
                status=$?
                if [ 0 -eq "$status" ]
                then
                    break
                fi
                sleep 2
            done
        fi
    fi

    return $status
} # status_secondary_ipaddress

#**********************************************************************
# Name:         mount_fs
# Description:  Mount a filesystem
# Parameters:   <device> - Device file system is on (required)
#               <location> - Directory where to mount (required)
#               <file system type> - Type of filesystem (required)
#               <options> - Options to pass to mount (optional)
# Return:       0 on success
# Notes:
#**********************************************************************
mount_fs()
{
    local dev="`readlink -e -q -n $1`"; shift
    local loc="`readlink -m -q -n $1`"; shift
    local type="$1"; shift
    local options="$*"

    if [ -z "$dev" ] || [ -z "$loc" ]
    then
        echo "Invalid device or mount point."
        return 2
    fi

    if [ "$dev" == "$(readlink -eqn $(eval grep \" $loc \" /etc/mtab | grep -o '^[^ ]\+') 2>/dev/null)" ] || [ "$dev" == "$(readlink -eqn $(mount | eval grep \" $loc \" | grep -o '^[^ ]\+') 2>/dev/null)" ]
    then
        echo "Mount point $loc is already moounted.\n"
        return 1
    fi

    mkdir -p $loc
    mount -t $type $options $dev $loc

    return $?
} # mount_fs

check_mnt()
{
    local dev="`readlink -e -q -n $1`"
    local loc="`readlink -e -q -n $2`"

    if [ "$dev" != "$(readlink -eqn $(eval grep \" $loc \" /etc/mtab | grep -o '^[^ ]\+') 2>/dev/null)" ]
    then
        if [ "$dev" != "$(readlink -eqn $(mount | eval grep \" $loc \" | grep -o '^[^ ]\+') 2>/dev/null)" ]
        then
            return 1;
        fi
    fi

    return 0
}

#**********************************************************************
# Name:         status_fs
# Description:  Check that the mount is correct
# Parameters:   <device> - Device file system is on (required)
#               <location> - Directory where mounted (required)
#               <file system type> - Type of filesystem (required)
# Return:       0 on success
# Notes:
#**********************************************************************
status_fs()
{
    check_mnt $*
    local rc=$?

    if [ 0 -ne "$rc" ]
    then
        if [ "/evms/" == "${1:4:6}" ]
        then
            local alt_dev=${1/\/evms\///pool/}
            check_mnt $alt_dev $2 $3
            rc=$?
        fi
    fi

    if [ 0 -ne "$rc" ]
    then
        echo "Device $1 is not mounted at $2, or file system type ($3) is wrong.\n"
    fi

    return $rc
} # status_fs

#**********************************************************************
# Name:         umount_fs
# Description:  Unmount a filesystem
# Parameters:   <device> - Device file system is on (required)
#               <location> - Directory where mounted (required)
#               <file system type> - Type of filesystem (required)
# Return:       0 on success
# Notes:
#**********************************************************************
umount_fs()
{
    local dev="`readlink -e -q -n $1`"
    local loc="`readlink -e -q -n $2`"

    if [ "$dev" == "$(readlink -eqn $(eval grep \" $loc \" /etc/mtab | grep -o '^[^ ]\+') 2>/dev/null)" ] || [ "$dev" == "$(readlink -eqn $(mount | eval grep \" $loc \" | grep -o '^[^ ]\+') 2>/dev/null)" ]
    then
        umount $loc
    fi

    return $?
} # status_fs

#**********************************************************************
# Name:         status_lv
# Description:  check if a logical volume is available
# Parameters:   location of the logical volume (required)
# Return:       0 on success
# Notes:
#**********************************************************************
status_lv()
{
    lvdisplay $* | grep "^[[:space:]]\+LV Status[[:space:]]\+available$"
    return $?
}

#**********************************************************************
# Name:        ncs_dismount - deprecated - use umount_fs
# Description: forcefully dismount a volume, killing all accessing processes
# Parameters:  mount_point
# Return:      0 - successful, error otherwise
# Notes:       it depends on the output format of 'df'
#**********************************************************************
ncs_dismount()
{
    if [ -n "$1" ]; then
        if df | grep -e "[[:space:]]$1$"
        then
                ignore_error fuser -km $1
                sleep 8
                # umount the file system
                exit_on_error umount $1
        fi
    fi

    return 0
}

#**********************************************************************
# Name:         ocf_script
# Description:          start an ocf resource
# Parameters: [action] [service] [params for service]
# Return:       0 on success
# Notes:
#**********************************************************************
ocf_script()
{
    if [ $# -lt 2 ]
    then
        echo "Missing Parameter"
        return 1
    fi

    action=$1
    shift
    service=$1
    shift
    $NCSVAR_OCF_DIR/$service $action $@
} # ocf_script

#**********************************************************************
# Name:         ocf_start
# Description:          start an ocf resource
# Parameters:   [service] [params for service]
# Return:       0 on success
# Notes:
#**********************************************************************
ocf_start()
{
    if [ $# -eq 0 ]
    then
        echo "Missing Service Name"
        return 1
    fi

    ocf_script start $@
} # ocf_start

#**********************************************************************
# Name:         ocf_stop
# Description:          stop an ocf resource
# Parameters:   [service] [params for service]
# Return:       0 on success
# Notes:
#**********************************************************************
ocf_stop()
{
    if [ $# -eq 0 ]
    then
        echo "Missing Service Name"
        return 1
    fi

    ocf_script stop $@
} # ocf_stop

#**********************************************************************
# Name:         ocf_status
# Description:          status an ocf resource
# Parameters:   [service] [params for service]
# Return:       0 on success
# Notes:
#**********************************************************************
ocf_status()
{
    if [ $# -eq 0 ]
    then
        echo "Missing Service Name"
        return 1
    fi

    ocf_script status $@
} # ocf_status

#**********************************************************************
# Name:         ocf_monitor
# Description:          monitor value of an ocf resource
# Parameters:   [service] [params for service]
# Return:       0 on success
# Notes:
#**********************************************************************
ocf_monitor()
{
    if [ $# -eq 0 ]
    then
        echo "Missing Service Name"
        return 1
    fi

    ocf_script monitor $@
} # ocf_monitor

#**********************************************************************
# Name:         ocf_migrate_to
# Description:          Beginning of a migrate. Use in the unload script
# Parameters:   [service] [params for service]
# Return:       0 on success
# Notes:
#**********************************************************************
ocf_migrate_to()
{
    if [ $# -eq 0 ]
    then
        echo "Missing Service Name"
        return 1
    fi

    ocf_script migrate_to $@
} # ocf_migrate_to

#**********************************************************************
# Name:         ocf_migrate_from
# Description:          End of a migrate. Use in the load script
# Parameters:   [service] [params for service]
# Return:       0 on success
# Notes:
#**********************************************************************
ocf_migrate_from()
{
    if [ $# -eq 0 ]
    then
        echo "Missing Service Name"
        return 1
    fi

    ocf_script migrate_from $@
} # ocf_migrate_from
