#!/bin/bash
# 
# netdump	This starts, stops, and reloads the netconsole
#		and netcrashdump facility
#
# chkconfig: - 50 50
# description: Initialize console side of netconsole and netcrashdump facility
# config: /etc/sysconfig/netdump
#

# Copyright 2002 Red Hat, Inc.
#
# Based in part on a shell script by
# Andreas Dilger <adilger@turbolinux.com>  Sep 26, 2001

PATH=/sbin:/usr/sbin:$PATH
RETVAL=0
SERVER_ADDRESS_RESOLUTION=
prog=netdump

# Check that networking is up.
. /etc/sysconfig/network
if [ ${NETWORKING} = "no" ]
then
    exit 0
fi

# Source function library.
. /etc/rc.d/init.d/functions

# Default values
LOCALPORT=6666
DEV=eth0
NETDUMPADDR=
NETDUMPMACADDR=
NETDUMPPORT=6666
IDLETIMEOUT=
NETDUMPKEYEXCHANGE=

SYSLOGADDR=
SYSLOGPORT=514
SYSLOGMACADDR=

NETLOGADDR=
NETLOGMACADDR=
NETLOGPORT=

kernel=`uname -r | cut -d. -f1-2`

[ -f /etc/sysconfig/netdump ] || exit 0
. /etc/sysconfig/netdump
[ -n "$NETDUMPADDR" ] || [ -n "$SYSLOGADDR" ] || [ -n "$NETLOGADDR" ] || {
    echo "Server address not specified in /etc/sysconfig/netdump" 1>&2
    exit 1
}


usage ()
{
    echo "Usage: service netdump {start|stop|status|restart|condrestart|propagate}" 1>&2
    RETVAL=1
}

dquad_to_hex ()
{
    echo $1 | sed -e "s/[()]//g" -e "s/\./ /g" | while read I0 I1 I2 I3 ; do
	printf "0x%02X%02X%02X%02X" $I0 $I1 $I2 $I3
    done
}

print_address_info ()
{
    # fill the arp cache with needed data and print info for host
    # usage: print_address_info host
    local host=$1
    local ping_output line host_ip trc_output mac_ip
    local arp_output hostname ipaddr at mac iftype on iface

    # use ping to make sure the netdump server is reachable; also,
    # ensure that the IP address is numeric
    ping_output="$(ping -c 1 -I $DEV $host 2> /dev/null)"
    [ $? -ne 0 ] && echo "$prog: cannot ping $host" 1>&2 && exit 1
    ping_output="$(echo $ping_output | grep '^PING ' | awk '{print $3}' | \
      sed 's/^(//' | sed 's/)$//')"

    for line in $ping_output; do host_ip=$line; done

    # the needed MAC address is directly associated with the host
    # IP address only if client and server are on the same subnet
    # if not, the needed MAC address is that of the gateway port;
    # either way, this will be the first IP address from traceroute
    trc_output="$(traceroute -i $DEV -n -m 1 $host_ip 2> /dev/null)"
    [ $? -ne 0 ] && echo "$prog: cannot traceroute $host_ip" 1>&2
    trc_output="$(echo $trc_output | grep '^1 ' | awk '{print $2}')"

    for line in $trc_output; do mac_ip=$line; done

    arping -c 1 -I $DEV $mac_ip &> /dev/null
    [ $? -ne 0 ] && echo "$prog: cannot arp $mac_ip" 1>&2

    # output from arp -a of the form:
    # good: host.domain (A.B.C.D) at 00:50:BF:06:48:C1 [ether] on eth0
    #           1           2      3         4            5     6   7
    # bad:  ? (A.B.C.D) at <incomplete> on eth0
    arp_output="$(LC_ALL=C arp -a | grep -v incomplete)"
    echo "$arp_output" | ( while read hostname ipaddr at mac iftype on iface;
    do
	: echo hostname=$hostname ipaddr=$ipaddr at=$at mac=$mac iftype=$iftype
	: echo on=$on iface=$iface
        if [ "$ipaddr" = "($mac_ip)" ] || expr "$hostname" : "$host" &>/dev/null;
	then
            echo HOSTNAME=$host IPADDR=$host_ip AT=$at MAC=$mac \
                 TYPE=$iftype ON=$on IFACE=$iface
        fi
    done )
}

random_hex_int ()
{
    dd if=/dev/urandom bs=4 count=1 2>/dev/null | od -x | awk '/0000000/ {print $2$3}'
}

ip_of_device ()
{
    LC_ALL=C /sbin/ifconfig $1 | sed 's/:/ /' | awk '/inet addr/ {print $3}'
}

netdump_failure ()
{
    echo -n "$1"
    failure
    echo
    exit 1
}

start ()
{
    # netdump/netconsole server
    NETDUMPOPTS=
    if [ -n "$NETDUMPADDR" ]
    then
        eval $(print_address_info $NETDUMPADDR)
        if [ "$HOSTNAME" = "?" -a -z "$MAC" ] 
        then
    	    echo "$prog: can't resolve $NETDUMPADDR MAC address" 1>&2
	    netdump_failure "netdump server address resolution"
        fi
    
        [ -z "$NETDUMPMACADDR" ] && NETDUMPMACADDR=$MAC
        [ -z "$DEV" ] && DEV=$IFACE
        if [ "$DEV" = "$IFACE" -a "$TYPE" != "[ether]" ] 
        then
    	    echo "$prog: $DEV must be an ethernet interface" 1>&2
    	    netdump_failure "netdump $DEV configuration"
        fi
    
        # Now we are ready to tell the netdump server how to talk to us
        MAGIC1=$(random_hex_int)
        MAGIC2=$(random_hex_int)
        LOCALADDR=$(ip_of_device $DEV)
    
        case "$NETDUMPKEYEXCHANGE" in
        none)
            # magic can be anything (except 0) if security is disabled
    	    NETDUMPOPTS="magic1=0x11111111 magic2=0x11111111 "
    	    ;;
        *)
    	    ssh -x -i /etc/sysconfig/netdump_id_dsa netdump@$NETDUMPADDR echo "$MAGIC2$MAGIC1" \> /var/crash/magic/$LOCALADDR
    	    if [ $? -ne 0 ]; then
    		echo "$prog: could not ssh to server $NETDUMPADDR"
    		netdump_failure "netdump server ssh key exchange"
    	    fi
    	    NETDUMPOPTS="magic1=0x$MAGIC1 magic2=0x$MAGIC2 "
    	    ;;
        esac
    
        IPHEX=`dquad_to_hex $IPADDR`
        eval $(echo $NETDUMPMACADDR | sed "s/:/ /g" | ( read M0 M1 M2 M3 M4 M5;
               echo M0=$M0\; M1=$M1\; M2=$M2\; M3=$M3\; M4=$M4\; M5=$M5\; ))
        TGTMAC="netdump_target_eth_byte0=0x$M0 netdump_target_eth_byte1=0x$M1 \
    	    netdump_target_eth_byte2=0x$M2 netdump_target_eth_byte3=0x$M3 \
    	    netdump_target_eth_byte4=0x$M4 netdump_target_eth_byte5=0x$M5 \
    	    netlog_target_eth_byte0=0x$M0 netlog_target_eth_byte1=0x$M1 \
    	    netlog_target_eth_byte2=0x$M2 netlog_target_eth_byte3=0x$M3 \
    	    netlog_target_eth_byte4=0x$M4 netlog_target_eth_byte5=0x$M5"
        MHZ="mhz=$(awk '/cpu MHz/ { print int($4) ; exit }' < /proc/cpuinfo)"
        if [ "$MHZ" == 0 ] ; then
    	    # something went wrong; make some reasonable guess
    	    MHZ=1000
        fi
        if [ -n "$IDLETIMEOUT" ] ; then
      	    IDLETIMEOUT="idle_timeout=$IDLETIMEOUT"
        fi

	if [ $kernel = 2.4 ]; then
	    NETDUMPOPTS=$NETDUMPOPTS"\
	    dev=$DEV netdump_target_ip=$IPHEX netlog_target_ip=$IPHEX \
	    source_port=$LOCALPORT netdump_target_port=$NETDUMPPORT \
	    netlog_target_port=$NETDUMPPORT \
	    $TGTMAC $MHZ $IDLETIMEOUT"
	else
	    NETDUMPOPTS=$NETDUMPOPTS"\
	    netdump=@$LOCALADDR/$DEV,$NETDUMPPORT@$IPADDR/$NETDUMPMACADDR"
	fi
    else
	# The netdump subsystem of the netconsole module is not configured.
	# However, the netconsole module minimally needs the following 
        # options in order for the syslog subsystem to run alone.
	if [ $kernel = 2.4 ]; then
	    NETDUMPOPTS="magic1=0x11111111 magic2=0x11111111 dev=$DEV source_port=$LOCALPORT"
	else
	    NETDUMPOPTS="magic1=0x11111111 magic2=0x11111111"
	fi
    fi

    # This section must come after NETDUMPOPTS, as it inherits IP address
    # from it.
    if [ $kernel = 2.6 ]; then
	# Make these the same as netdump opts
	if [ -z $NETLOGADDR ]; then
	    NETLOGOPTS="netlog=@$LOCALADDR/$DEV,$NETDUMPPORT@$IPADDR/$NETDUMPMACADDR"
	elif [ $NETLOGADDR = "NONE" ]; then
	    NETLOGOPTS=
	elif [ -n "$NETLOGADDR" ]; then
	    eval $(print_address_info $NETLOGADDR)
            [ "$HOSTNAME" = "?" -a -z "$MAC" ] &&
		netdump_failure "netlog server address resolution"
	    [ -z "$NETLOGMACADDR" ] && NETLOGMACADDR=$MAC
	    NETLOGOPTS="netlog=@$LOCALDDR/$DEV,$NETLOGPORT@$IPADDR/$NETLOGMACADDR "
	fi
    fi

    SYSLOGOPTS=
    # syslogd server, if any
    if [ -n "$SYSLOGADDR" ] ; then
	eval $(print_address_info $SYSLOGADDR)
        [ "$SERVER_ADDRESS_RESOLUTION" = "unresolved" ] &&
            netdump_failure "syslog server address resolution"
	[ -z "$SYSLOGMACADDR" ] && SYSLOGMACADDR=$MAC
	SYSLOGIPHEX=`dquad_to_hex $IPADDR`
	eval $(echo $SYSLOGMACADDR | sed "s/:/ /g" | ( read M0 M1 M2 M3 M4 M5;
	       echo M0=$M0\; M1=$M1\; M2=$M2\; M3=$M3\; M4=$M4\; M5=$M5\; ))
	SYSLOGMAC="syslog_target_eth_byte0=0x$M0 syslog_target_eth_byte1=0x$M1 \
		syslog_target_eth_byte2=0x$M2 syslog_target_eth_byte3=0x$M3 \
		syslog_target_eth_byte4=0x$M4 syslog_target_eth_byte5=0x$M5"
	if [ $kernel = 2.4 ]; then
	    SYSLOGOPTS="syslog_target_ip=$SYSLOGIPHEX syslog_target_port=$SYSLOGPORT $SYSLOGMAC"
	else
	    SYSLOGOPTS="netconsole=@$LOCALADDR/$DEV,$SYSLOGPORT@$SYSLOGADDR/$SYSLOGMACADDR "
	fi
    fi

    logger -p daemon.info -t netdump: inserting netconsole module with arguments \
	$NETDUMPOPTS $SYSLOGOPTS
    if [ $kernel = 2.4 ]; then
	action $"initializing netdump" modprobe netconsole \
	    $NETDUMPOPTS $SYSLOGOPTS
    else
	if [ -n "$NETDUMPADDR" ]; then
	    action $"initializing netdump" modprobe netdump \
		$NETDUMPOPTS
	fi
	if [ -n "$SYSLOGOPTS" ] || [ -n "$NETLOGOPTS" ] ; then
	    action $"initializing netconsole" modprobe netconsole \
		$SYSLOGOPTS $NETLOGOPTS
	fi
    fi
    touch /var/lock/subsys/netdump
}

stop ()
{
    if [ $kernel = 2.4 ]; then
	if /sbin/lsmod | grep netconsole >/dev/null 2>&1 ; then
	    action $"disabling netdump" rmmod netconsole
	fi
    else
	if /sbin/lsmod | grep netconsole >/dev/null 2>&1 ; then
	    action $"disabling netconsole" rmmod netconsole;
	fi

	if /sbin/lsmod | grep netdump >/dev/null 2>&1 ; then
	    action $"disabling netdump" rmmod netdump
	fi
    fi

    rm -f /var/lock/subsys/netdump
}

status ()
{
    if [ $kernel = 2.6 ]; then
    	if /sbin/lsmod | grep netdump >/dev/null 2>&1 ; then
	    echo "netdump module loaded"
    	else
	    echo "netdump module not loaded"
        fi
    fi
    if /sbin/lsmod | grep netconsole >/dev/null 2>&1 ; then
	echo "netconsole module loaded"
    else
	echo "netconsole module not loaded"
    fi
}


restart ()
{
    stop
    start
}

condrestart ()
{
    [ -e /var/lock/subsys/netdump ] && restart
}

propagate ()
{
    # propagate netdump ssh public key to the crashdump server
    cat /etc/sysconfig/netdump_id_dsa.pub | \
	ssh -x netdump@$NETDUMPADDR cat '>>' /var/crash/.ssh/authorized_keys2
}

case "$1" in
    stop) stop ;;
    status) status ;;
    start|restart|reload) restart ;;
    condrestart) condrestart ;;
    propagate) propagate ;;
    *) usage ;;
esac

exit $RETVAL


syntax highlighted by Code2HTML, v. 0.9.1