#! /bin/bash
#
# chkconfig: 2345 80 20
# description: start and stop ISDN services
#

if [ -f /etc/init.d/functions ]; then
    . /etc/init.d/functions
elif [ -f /etc/rc.d/init.d/functions ] ; then
    . /etc/rc.d/init.d/functions
else
    exit 0
fi

# Source configuration
. /etc/sysconfig/network

test -f /etc/sysconfig/isdncard || exit 0
. /etc/sysconfig/isdncard

if [ -z "$MODULE" -o -z "$RESOURCES" ]; then
    exit 0
fi        

# disable debug as default
DEBUG=off
IBOD=no
RETVAL=0
sync_devices=0
raw_devices=0
CARDCTL=/sbin/cardctl
PCMCIA=no

# check PCMCIA
if [ "$RESOURCES" = "NONE" -a -f /proc/bus/pccard/drivers ] ; then
    PCMCIA=yes
fi

if [ -f /var/log/isdn.log ] ; then
    touch /var/log/isdn.log
    chmod 644 /var/log/isdn.log
fi

function log_echo()
{
    if [ "$DEBUG" = "on" ] ; then
        echo $"$*"
    fi
}

function log_isdnctrl()
{
    if [ "$DEBUG" = "on" ]; then
        echo "/usr/sbin/isdnctrl $*" >>/var/log/isdn.log 2>&1
    fi
    /usr/sbin/isdnctrl $* >>/var/log/isdn.log 2>&1
}

function clear_env()
{
    PROVIDER=; USER=; PASSWORD=; ENCAP=; DIALMODE=; SECURE=; MSN=; PHONE_IN=
    PREFIX=; AREACODE=; PHONE_OUT=; CITY=; BUNDLING=; LOCAL_IP=; REMOTE_IP=
    NETWORK=; BROADCAST=; HUPTIMEOUT=; DNS=; ISDN_HOSTNAME=; ISDN_DOMAIN=; L2_PROT=
    L3_PROT=; CALLBACK=; CHARGEHUP=; CHARGEINT=; CBHUP=; CBDELAY=; DIALMAX=
    AUTH=; IHUP=; IP_FORWARD=; SLAVE_DELAY=; SLAVE_TRIGGER=; FIRMWARE=
    VJ=; VJCCOMP=; AC=; PC=; BSDCOMP=; MTU=; MRU=; DRIVERTYPE=
    SLAVE_DIALMODE=; SLAVE_MSN=; SLAVE_PHONE_IN=; SLAVE_PHONE_OUT=
    SLAVE_HUPTIMEOUT=; SLAVE_CALLBACK=; SLAVE_CHARGEHUP=; SLAVE_CHARGEINT=
    SLAVE_CBHUP=; SLAVE_CBDELAY=; SLAVE_DIALMAX=; SLAVE_IHUP=; DELDEFAULTROUTE=
    CBCP=; DIALIN=
}

function create_option_file()
{
    umask 066
    echo "$1" > /etc/ppp/ioption-secret-$2
    umask 022
}

function set_channel_bundling()
{
    if [ "$BUNDLING" = "yes" -o "$BUNDLING" = "on" ]; then
        IBOD="yes"
        if [ "$ENCAP" = "syncppp" ]; then
            if [ -z "$SLAVE_MSN" ]; then
                SLAVE_MSN="$MSN"
            fi
            if [ -z "$SLAVE_PHONE_OUT" ]; then
                SLAVE_PHONE_OUT="$PHONE_OUT"
            fi
            if [ -z "$SLAVE_PHONE_IN" ]; then
                SLAVE_PHONE_IN="$PHONE_IN"
            fi
            if [ -z "$SLAVE_HUPTIMEOUT" ]; then
                SLAVE_HUPTIMEOUT="$HUPTIMEOUT"
            fi
            if [ -z "$SLAVE_CHARGEHUP" ]; then
                SLAVE_CHARGEHUP="$CHARGEHUP"
            fi
            if [ -z "$SLAVE_CHARGEINT" ]; then
                SLAVE_CHARGEINT="$CHARGEINT"
            fi
            if [ -z "$SLAVE_CBHUP" ]; then
                SLAVE_CBHUP="$CBHUP"
            fi
            if [ -z "$SLAVE_IHUP" ]; then
                SLAVE_IHUP="$IHUP"
            fi
            if [ -z "$SLAVE_DIALMAX" ]; then
                SLAVE_DIALMAX="$DIALMAX"
            fi
           if [ -z "$SLAVE_CALLBACK" ]; then
                SLAVE_CALLBACK="$CALLBACK"
            fi
            if [ -z "$SLAVE_CBDELAY" ]; then
                SLAVE_CBDELAY="$CBDELAY"
            fi
            if [ -z "$SLAVE_DIALMODE" ]; then
                SLAVE_DIALMODE="auto"
            fi
        else
            log_echo " Error: $1 channel bundling only supported with syncppp - slave interface disabled"
            BUNDLING=
        fi
    fi
}

function addprovider()
{
    clear_env

    # load provider config
    . /etc/sysconfig/provider/conf-$1

    options=

    # Do some checks
    if [ -z "$MSN" ]; then
        log_echo "Error: $1: MSN not set"
        return 1
    fi
    if [ -z "$PHONE_OUT" ]; then
        log_echo "Error: $1: no outgoing phone number set"
        return 1
    fi
    if [ -z "$ENCAP" ]; then
        log_echo "Error: $1: Encapsulation not set"
        return 1
    fi
    if [ -z "$DIALMODE" ]; then
        log_echo "Error: $1: Dialmode not set"
        return 1
    fi
    if [ -z "$AUTH" ]; then
        AUTH="+pap -chap"
    fi
    if [ -z "$DIALIN" ]; then
        DIALIN="off"
    fi
    # Check for channel bundling
    set_channel_bundling

    # some settings
    if [ -z "$L2_PROT" ]; then
        L2_PROT="hdlc"
    fi
    if [ -z "$L3_PROT" ]; then
        L3_PROT="trans"
    fi

    # checks for syncppp
    if [ "$ENCAP" = "syncppp" ]; then
        device=ippp$sync_devices
        sync_devices=$[$sync_devices+1]

        # 1=delete default route, if exists
        if [ "$DELDEFAULTROUTE" = "enabled" -o -z "$DELDEFAULTROUTE" ]; then
            options="$options deldefaultroute"
        fi
	
        # we use authentication with pap and/or chap
        _auth=`echo "$AUTH" | sed 's/[a-z -]*//g'`
        if [ -n "$_auth" ]; then
            if [ -z "$USER" ]; then
                log_echo " Error: $1 (syncppp) user is not set"
                return 1
            fi
            # we should hide the user name, so i add user name to option file.
            if [ "$AUTH" = "-pap +chap" ]; then
                #options="$options name $USER"
                create_option_file "name \"$USER\"" "$1"
            else
                #options="$options user $USER"
                create_option_file "user \"$USER\"" "$1"
            fi
            options="$options file /etc/ppp/ioption-secret-$1"

            # authentication options:
            # +pap and/or +chap does not work correct by dialout - remove
            # them if it's configured as dialout
            [ "$DIALIN" = "on" ] || AUTH=`echo "$AUTH" | sed 's/+[a-z]*//g'`
        fi
    fi

    # checks for rawip
    if [ "$ENCAP" = "rawip" ]; then
        device=isdn$raw_devices
        raw_devices=$[$raw_devices+1]
        if [ -z "$LOCAL_IP" -o -z "$REMOTE_IP" ]; then
            log_echo " Error: $1 (rawip) local or remote ip not set"
            return 1
        fi
    fi

    # did we got a device
    if [ -z "$device" ]; then
        log_echo " Error: $1: cannot determine device"
        return 1
    fi

    log_isdnctrl addif $device
    log_isdnctrl eaz $device $MSN

    for i in $PHONE_OUT; do
        log_isdnctrl addphone $device out $PREFIX$AREACODE$i
    done

    for i in $PHONE_IN; do
        log_isdnctrl addphone $device in $i
    done

    log_isdnctrl l2_prot $device $L2_PROT
    log_isdnctrl l3_prot $device $L3_PROT
    log_isdnctrl encap $device $ENCAP
    log_isdnctrl dialmode $device $DIALMODE

    if [ -n "$SECURE" ]; then
        log_isdnctrl secure $device $SECURE
    fi
    if [ -n "$HUPTIMEOUT" ]; then
        log_isdnctrl huptimeout $device $HUPTIMEOUT
    fi
    if [ -n "$CHARGEHUP" ]; then
        log_isdnctrl chargehup $device $CHARGEHUP
    fi
    if [ -n "$CHARGEINT" ]; then
        log_isdnctrl chargeint $device $CHARGEINT
    fi
    if [ -n "$CBHUP" ]; then
        log_isdnctrl cbhup $device $CBHUP
    fi
    if [ -n "$IHUP" ]; then
        log_isdnctrl ihup $device $IHUP
    fi
    if [ -n "$DIALMAX" ]; then
        log_isdnctrl dialmax $device $DIALMAX
    fi
    if [ "$CALLBACK" = "out" ]; then
        log_isdnctrl callback $device $CALLBACK
    else
        log_isdnctrl callback $device off
    fi
    if [ -n "$CBDELAY" ]; then
        log_isdnctrl cbdelay $device $CBDELAY
    fi

    if [ "$ENCAP" = "syncppp" ]; then
        # set config options for /etc/sysconfig/network-scripts/ifcfg-$device
        ifcfg_file="/etc/sysconfig/network-scripts/ifcfg-$device"
        echo "DEVICE=$device" > $ifcfg_file
        options="$options ipparam $device"
        dyn_local=
        log_isdnctrl pppbind $device
        if [ -z "$LOCAL_IP" ]; then
            dyn_local=1
            LOCAL_IP="0.0.0.0"
            options="$options ipcp-accept-local"
        else
            options="$options noipdefault"
        fi
        # Add device
        options="$options /dev/$device"

        # Create slave device if bundling enabled
        if [ "$BUNDLING" = "yes" -o "$BUNDLING" = "on" ]; then
            slave=ippp$sync_devices
            sync_devices=$[$sync_devices+1]

            options="$options /dev/$slave +mp"

            # Create slave and set options
            log_isdnctrl addslave $device $slave
            log_isdnctrl eaz $slave $SLAVE_MSN
            for i in $SLAVE_PHONE_OUT; do
                log_isdnctrl addphone $slave out $PREFIX$AREACODE$i
            done
            for i in $SLAVE_PHONE_IN; do
                log_isdnctrl addphone $slave in $i
            done
            log_isdnctrl l2_prot $slave $L2_PROT
            log_isdnctrl l3_prot $slave $L3_PROT
            log_isdnctrl encap $slave $ENCAP
            log_isdnctrl dialmode $slave $SLAVE_DIALMODE
            if [ -n "$SECURE" ]; then
                log_isdnctrl secure $slave $SECURE
            fi
            if [ -n "$SLAVE_HUPTIMEOUT" ]; then
                log_isdnctrl huptimeout $slave $SLAVE_HUPTIMEOUT
            fi
            if [ -n "$SLAVE_CHARGEHUP" ]; then
                log_isdnctrl chargehup $slave $SLAVE_CHARGEHUP
            fi
            if [ -n "$SLAVE_CHARGEINT" ]; then
                log_isdnctrl chargeint $slave $SLAVE_CHARGEINT
            fi
            if [ -n "$SLAVE_CBHUP" ]; then
                log_isdnctrl cbhup $slave $SLAVE_CBHUP
            fi
            if [ -n "$SLAVE_IHUP" ]; then
                log_isdnctrl ihup $slave $SLAVE_IHUP
            fi
            if [ -n "$SLAVE_DIALMAX" ]; then
                log_isdnctrl dialmax $slave $SLAVE_DIALMAX
            fi
            if [ -n "$SLAVE_CALLBACK" ]; then
                log_isdnctrl callback $slave $SLAVE_CALLBACK
            else
                log_isdnctrl callback $slave off
            fi
            if [ -n "$SLAVE_CBDELAY" ]; then
                log_isdnctrl cbdelay $device $SLAVE_CBDELAY
            fi

            # Options for master device
            if [ -n "$SLAVE_DELAY" ]; then
                log_isdnctrl sdelay $device $SLAVE_DELAY
            fi
            if [ -n "$SLAVE_TRIGGER" ]; then
                log_isdnctrl trigger $device $SLAVE_TRIGGER
            fi
        fi

        if [ -z "$REMOTE_IP" ]; then
            options="$options ipcp-accept-remote"
            # Activate network interface
            ifconfig $device $LOCAL_IP up >/dev/null 2>&1
        else
            # Activate network interface
            ifconfig $device $LOCAL_IP pointopoint $REMOTE_IP up >/dev/null 2>&1
        fi

        # do we have all dynamically?
        if [ -n "$dyn_local" ]; then 
            if [ -z "$REMOTE_IP" ]; then
       	        # Set the local and/or remote interface IP addresses
                options="$LOCAL_IP:0.0.0.0 $options"
            else
                # Set the local and/or remote interface IP addresses
                options="$LOCAL_IP:$REMOTE_IP $options"
            fi
        else
            options="$options $LOCAL_IP:$REMOTE_IP"
        fi

        # Add default route while connection
        options="$options defaultroute"

        # configure Van Jacobson style TCP/IP header compression and
        # VJ connection-ID compression
        [ "$VJ" = "off" ] && options="$options -vj"
        [ "$VJCCOMP" = "off" ] && options="$options -vjccomp"

        # configure Address/Control compression, protocol field compression and
        # BSD-Compression scheme
        [ "$AC" = "off" ] && options="$options -ac"
        [ "$PC" = "off" ] && options="$options -pc"
        [ "$BSDCOMP" = "off" ] && options="$options -bsdcomp"

        # Set max receive and max transmit units
        [ -n "$MRU" ] && options="$options mru $MRU"
        [ -n "$MTU" ] && options="$options mtu $MTU"

        # set CBCP protocoll
        [ "$CBCP" = "on" ] && options="$options callback 6"

        # set CCP protocoll
        [ "$CCP" = "off" ] && options="$options noccp"

        # set host name
        if [ -n "$ISDN_HOSTNAME" ]; then
            options="$options hostname $ISDN_HOSTNAME"
        fi

        # set domain name
        if [ -n "$ISDN_DOMAIN" ]; then
            options="$options domain $ISDN_DOMAIN"
            echo "DOMAIN=\"$ISDN_DOMAIN\"" >> $ifcfg_file
        fi

        # Set authentication options
        for i in $AUTH; do
            options="$options $i"
        done

        # check dns entry
        if [ -z "$DNS" ]; then
            options="$options ms-get-dns"
            if [ "$RESOLV_MODS" != "no" ]; then
                echo "RESOLV_MODS=\"yes\"" >> $ifcfg_file
            fi
        else
            k=1
            for i in $DNS; do
                options="$options ms-dns $i"
                echo "DNS$k=$i" >> $ifcfg_file
                k=$[$k+1]
            done
        fi

        if [ -f /etc/ppp/options-$1 ]; then
            options="$options `cat /etc/ppp/options-$1`"
        fi

        # Start ipppd
        if [ "$DEBUG" = "on" ]; then
            options="-d $options"
        fi
        /usr/sbin/ipppd $options

        if [ -n "$dyn_local" ]; then
            echo 1 > /proc/sys/net/ipv4/ip_dynaddr
        fi
    fi

    if [ "$ENCAP" = "rawip" ]; then
        ifconfig $device $LOCAL_IP pointopoint $REMOTE_IP up >/dev/null 2>&1
    fi

    if [ "$DIALMODE" = "auto" ]; then
        route del default >/dev/null 2>&1
        if [ -z "$REMOTE_IP" ]; then
            route add default $device >/dev/null 2>&1
        else
            route add default gw $REMOTE_IP >/dev/null 2>&1
        fi
    fi

    ip_forwarding

    echo "$device $1" >> /var/run/isdn
    touch /var/lock/subsys/isdn
}

function remove_provider()
{
    if [ -f /var/run/isdn ]; then
        # kill ipppd if running
        prog="ipppd"
        echo -n $"Shutting down $prog"
        killproc ipppd
        echo
 
        while read device provider ; do
            if /sbin/ifconfig $device >& /dev/null ; then
                action $"Removing provider '$provider' on device '$device'" /sbin/ifconfig $device down 2>/dev/null
                log_isdnctrl delif $device >/dev/null 2>&1
                [ -f /etc/ppp/ioption-secret-$provider ] && /bin/rm -f /etc/ppp/ioption-secret-$provider
            fi
            /bin/rm -f /etc/sysconfig/network-scripts/ifcfg-$device
        done < /var/run/isdn
 
        /bin/rm -f /var/run/isdn /var/lock/subsys/isdn
    fi
}

function load_modules()
{
    if [ "$PCMCIA" = "no" ] && /sbin/lsmod | grep "^$MODULE" >& /dev/null ; then
        return
    fi
    
    [ "$PCMCIA" = "no" ] || RESOURCES=""
    /sbin/modprobe $MODULE $RESOURCES >& /dev/null

    if [ "$PCMCIA" = "yes" ] ; then
        cat /proc/bus/pccard/drivers | grep "^$MODULE" | grep "0 1" >& /dev/null
    else
        /sbin/lsmod | grep "^$MODULE" >& /dev/null
    fi

    if [ "$?" = "0" ] ; then
        action $"Loading ISDN modules" /bin/true
    else
        action $"Loading ISDN modules" /bin/false
        /sbin/modprobe -r $MODULE >& /dev/null
        return 1
    fi
}

function remove_modules()
{
    # Unload hisax modules
    if /sbin/lsmod | grep "^$MODULE" >/dev/null 2>&1 ; then
        action $"Unloading ISDN modules" /sbin/modprobe -r $MODULE
    fi
}

function load_firmware()
{
    # loading firmware
    $FIRMWARE >/dev/null 2>&1

    # setup isdn device, only work with hisax
    [ "$MODULE" = "hisax" ] && hisaxctrl HiSax 1 0x3ff >/dev/null 2>&1
}

function start_isdnlog()
{
    # don't start isdnlog, if the ISDN card requires the firmware
    if [ -z "$FIRMWARE" ] ; then
        prog="isdnlog"
        echo -n $"Starting $prog"
        if [ -f /etc/isdn/isdnlog.option ] ; then
            daemon isdnlog /dev/isdnctrl0 -D -f /etc/isdn/isdnlog.option
        else
            daemon isdnlog /dev/isdnctrl0 -x0x3fff -M -w2 -S -D -s -O+/var/log/isdnctrl
        fi
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && touch /var/lock/subsys/isdnlog
    fi
}

function stop_isdnlog()
{
    if [ -f /var/lock/subsys/isdnlog ] ; then
        prog="isdnlog"
        echo -n $"Shutting down $prog"
        killproc isdnlog
        RETVAL=$?
        echo
        [ $RETVAL -eq 0 ] && /bin/rm -f /var/lock/subsys/isdnlog
    fi
}

function start_ibod()
{
    if [ "$IBOD" = "yes" -a -f /etc/isdn/ibod.cf -a -x /usr/sbin/ibod ] ; then
        /usr/sbin/ibod &
        pid=`pidofproc ibod`
        if [ -n "$pid" ] ; then
            prog="ibod"
            action $"Starting $prog" /bin/true
            touch /var/lock/subsys/ibod
        else
            action $"Starting $prog" /bin/false
        fi
    fi
}

# stopping ibod daemon for channel bundling
function stop_ibod()
{
    if  [ -f /var/lock/subsys/ibod ] ; then
        prog="ibod"
        echo -n $"Shutting down $prog"
        killproc /usr/sbin/ibod
        RETVAL=$?
        [ $RETVAL -eq 0 ] && /bin/rm -f /var/lock/subsys/ibod
        echo
    fi
}

function static_routes()
{
    [ -f /etc/sysconfig/static-routes ] || return
 
    # note the trailing space in the grep gets rid of aliases
    grep "^$1 " /etc/sysconfig/static-routes | while read device args; do
        /sbin/route add -$args $device
    done
    grep "^any " /etc/sysconfig/static-routes | while read ignore type net netmask mask gw dev ; do
        _dev="$(echo $dev | cut -c-4)"
        # set static route if dev is isdn device
        [ "$_dev" = "ippp" -o "$_dev" = "isdn" ] || break
        if [ "$dev" = "$1" ]; then
           /sbin/route add -$type $net $netmask $mask $dev
        fi
    done
}

function ip_forwarding()
{
    if [ "$IP_FORWARD" = 1 ]; then
        echo 1 >/proc/sys/net/ipv4/ip_forward
    fi
}

function start()
{
    # don't start again if it's already started
    [ ! -f /var/lock/subsys/isdn ] || exit 0

    # load modules
    load_modules || exit 0
 
    # load firmware and setup isdn device driver
    load_firmware

    # start isdn logging
    start_isdnlog

    # don't add provider if /etc/sysconfig/activeisdn does not exist or is empty
    [ -s /etc/sysconfig/activeisdn ] || exit 0

    ACTION_STATUS=/bin/true

    # start provider configurations
    for prov in `cat /etc/sysconfig/activeisdn` ; do
        if [ -f /etc/sysconfig/provider/conf-$prov ]; then
            # execute function in sub-shell to have clear environment
            addprovider $prov || ACTION_STATUS=/bin/false
        fi
        action $"Adding provider '$prov' on device '$device'" $ACTION_STATUS
        static_routes $device
    done

    # start ibod daemon for channel bundling
    start_ibod
}

function stop()
{
    stop_ibod
    stop_isdnlog
    remove_provider
    remove_modules
}

function restart()
{
    stop
    start
}

function status()
{
    if [ ! -f /var/run/isdn ] ; then
        echo $"$0: Link is down"
    else
        cat /var/run/isdn | while read dev pn; do
            echo $"$pn is attached to $dev"
        done
    fi
}

function condrestart()
{
    if [ -f /var/lock/subsys/isdn ] ; then
        restart
    fi
}

case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    condrestart)
       condrestart
       ;;
    status)
       status
       ;;
    *)
       echo $"Usage: $0 {start|stop|restart|status|condrestart}"
       exit 1
esac

exit 0
