#! /usr/bin/env bash
#
# crash-diag <dir>
#
# <dir> is the node's working directory.

. `dirname $0`/broctl-config.sh

if [ $# -ne 1 ]; then
    echo "crash-diag: wrong number of arguments"
    exit 1
fi

if [ ! -d "$1" ]; then
    echo "No work dir found"
    exit 0
fi

cd "$1"
if [ $? -ne 0 ]; then
    exit 1
fi

(

echo

# Identify all core files in the current directory.  We assume these
# filenames contain the word "core" and do not end in ".log".
core=`ls -t *core* 2> /dev/null | grep -v '\.log$'`

gdb=`which gdb 2> /dev/null`

# If no backtrace can be generated, then output an explanation.  However,
# skip this if there's no .status file, because in that case Bro didn't run.
if [ -f .status ]; then
    if [ -n "$gdb" ]; then
        if [ -z "$core" ]; then
            # Check if the system allows setting core file size to unlimited,
            # because this is what the run-bro script does when it starts bro.
            ulimit -c unlimited 2> /dev/null
            coresize=`ulimit -c`

            if [ "$coresize" != "unlimited" ]; then
                echo "No core file found.  You may need to change your system settings to"
                echo "allow core files."
            else
                echo "No core file found."
            fi

            echo
        fi
    else
        if [ -n "$core" ]; then
            echo "Unable to output a backtrace because gdb is not installed."
            echo "It is recommended to install gdb so that BroControl can output a"
            echo "backtrace the next time Bro crashes."
        else
            echo "No core file found and gdb is not installed.  It is recommended to"
            echo "install gdb so that BroControl can output a backtrace if Bro crashes."
        fi
        echo
    fi
fi

if [ -f "${bro}" ];then
    bro_version=`"${bro}" -v 2>&1 >/dev/null | awk '{print $3}'`
else
    bro_version="(file not found: ${bro})"
fi

echo Bro $bro_version
uname -sr
echo

broplugins=`"${bro}" -N | grep -v "(built-in)"`
if [ -z "$broplugins" ]; then
    echo "Bro plugins: (none found)"
else
    echo "Bro plugins:"
    echo "$broplugins"
fi

# Output a backtrace, if possible.
if [ -n "$gdb" ]; then
    if [ -n "$core" ]; then
        echo "thread apply all bt" >.gdb_cmds
        for c in "$core"; do
            if [ -f "$c" ]; then
                echo
                # Note: broctl looks for this string in order to determine
                # if a backtrace was output.
                echo "Core file: $c"
                gdb --batch -x .gdb_cmds "${bro}" "$c" 2>/dev/null
            fi
        done
        rm -f .gdb_cmds
    fi
fi

# Usage:
#   show_log <filename> <num>
# Output the last <num> lines of <filename>.  If <num> is -1, then output the
# entire file.
function show_log() {
    filename=$1
    num=$2

    echo

    if [ -f $filename ]; then
       echo ==== $filename
       if [ $num -ge 0 ]; then
           tail -$num $filename
       else
           cat $filename
       fi
    else
       echo ==== No $filename
    fi
}

show_log reporter.log 10
show_log stderr.log 10
show_log stdout.log 10
show_log .cmdline 30
show_log .env_vars 30
show_log .status 10
show_log prof.log 10
show_log packet_filter.log 30
show_log loaded_scripts.log -1

) >.crash-diag.out

cat .crash-diag.out
