mirror of
https://github.com/zeek/zeek.git
synced 2025-10-02 06:38:20 +00:00
1098 lines
29 KiB
Bash
Executable file
1098 lines
29 KiB
Bash
Executable file
#!/bin/sh
|
|
|
|
# $Id: bro.rc.in 4620 2007-07-09 21:23:52Z vern $
|
|
#
|
|
# Start script for running Bro.
|
|
#
|
|
# This will run one instance of Bro. If there is a need to run more than one
|
|
# instance at a time then copy this script to a new file and change the name
|
|
# to something else.
|
|
#
|
|
# chkconfig: - 57 30
|
|
#
|
|
# description: Bro is a highly customizable intrusion detection system \
|
|
# developed at Lawrence Berkeley National Labs.
|
|
# processname: bro
|
|
# pidfile: /var/run/bro-lite/pid
|
|
# config: /etc/sysconfig/bro-lite
|
|
#
|
|
# Written by Roger Winslow rwinslow@lbl.gov
|
|
|
|
# Variables which are exported on a startup and needed by Bro
|
|
# This variable used to be called BRO_ID
|
|
# BRO_LOG_SUFFIX
|
|
|
|
# For tasks to complete before and after Bro starts please edit the following
|
|
# scripts to suit your needs. For those of you familiar with dhclient this
|
|
# uses the same idea.
|
|
# Before Bro starts $BROHOME/etc/bro.rc-hooks.sh
|
|
|
|
# See the bottom of this script for an explanation of how this all works.
|
|
# I'll try my best to be clear....
|
|
|
|
prog="bro.rc"
|
|
|
|
RETVAL=0
|
|
|
|
# picked up from configure at install time
|
|
BROHOME="@prefix@"
|
|
export BROHOME
|
|
|
|
# Set the environment.
|
|
source_config="${BROHOME}/etc/bro.cfg"
|
|
|
|
# Location of bro-hooks.sh script
|
|
bro_hooks="${BROHOME}/etc/bro.rc-hooks.sh"
|
|
|
|
# Set the full path to this script as called
|
|
if [ `echo ${0} | grep -E "^/"` ]; then
|
|
this_script="${0}"
|
|
else
|
|
this_script="`pwd`/${0}"
|
|
fi
|
|
|
|
# Set the args as passed to this script
|
|
cur_args="$*"
|
|
|
|
# Load the source config
|
|
if ! [ -f ${source_config} ] || ! . "${source_config}"; then
|
|
echo "${prog}: Unable to load the source config file at ${source_config}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Source the bro_hooks script if exists and readable
|
|
if ! [ -f "${bro_hooks}" ] || ! . "${bro_hooks}"; then
|
|
echo "${prog}: Unable to source the bro.rc-hooks.sh script at ${bro_hooks}" >&2
|
|
fi
|
|
|
|
syslog_cmd="logger"
|
|
run_dir="${BRO_RUNTIME_DIR}"
|
|
pidfile="${run_dir}/pid"
|
|
start_time_file="${run_dir}/start_time"
|
|
active_log_file="${BROLOGS}/active_log"
|
|
autorestart_file="${run_dir}/autorestart"
|
|
alternate_user_id=${BRO_USER_ID}
|
|
renice_checkpoint_level=15
|
|
time_between_restarts=2
|
|
minimum_mortality_time=60
|
|
|
|
# Setting DEBUG to 1 will prevent the following
|
|
# forking of the Bro process into the background
|
|
# any autorestart features
|
|
# any bro.rc-hooks.sh functions
|
|
# writing of data to $BRO_RUNTIME_DIR
|
|
# redirection of STDERR and STDOUT to the info file
|
|
DEBUG=0
|
|
|
|
# Export a few items that Bro needs to run
|
|
export BROLOGS
|
|
export BROPATH
|
|
export BROHOME
|
|
export PATH="${BROHOME}/bro/bin:${BROHOME}/bro/scripts:/usr/local/bin:/usr/local/sbin:${PATH}"
|
|
|
|
# Make sure that the $BRO_RUNTIME_DIR exists and is writtable
|
|
if [ ! -d "${BRO_RUNTIME_DIR}" ]; then
|
|
mkdir "${BRO_RUNTIME_DIR}"
|
|
if [ "$?" != "0" ]; then
|
|
echo "${prog}: Failed to create the runtime directory at ${BRO_RUNTIME_DIR}" >&2
|
|
echo "${prog}: Unable to continue" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ "${alternate_user_id}x" != "x" ] && [ "${alternate_user_id}" != "${USER}" ]; then
|
|
chown ${alternate_user_id} "${BRO_RUNTIME_DIR}"
|
|
if [ "$?" != "0" ]; then
|
|
echo "${prog}: Failed to change owner on runtime directory ${BRO_RUNTIME_DIR}" >&2
|
|
echo "${prog}: Unable to continue" >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
start() {
|
|
# Make a few sanity checks
|
|
# Make sure the BROLOGS directory is writeable
|
|
if [ ! -d ${BROLOGS} ] || [ ! -w ${BROLOGS} ]; then
|
|
echo "${prog}: BROLOGS directory at ${BROLOGS} is not writable." >&2
|
|
echo "${prog}: Unable to continue" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Check to make sure that the Bro executable is at least --x
|
|
if [ ! -x "${BRO}" ]; then
|
|
echo "${prog}: Unable to execute the bro binary at ${BRO}" >&2
|
|
echo "${prog}: Unable to continue" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Check to make sure that bro is not already running
|
|
local pid
|
|
if [ -f "${pidfile}" ]; then
|
|
pid=`cat "${pidfile}"`
|
|
|
|
if [ "${pid}x" = "x" ]; then
|
|
cleanup
|
|
else
|
|
local _is_running
|
|
pidisrunning ${pid} "${BRO}"
|
|
_is_running=$?
|
|
if [ "${_is_running}" != "0" ]; then
|
|
cleanup
|
|
else
|
|
echo "${prog}: already running with a pid of ${pid}"
|
|
# errno code for EEXIST = 17
|
|
return 17
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Check to make sure that a start policy has been specified
|
|
if [ "${BRO_START_POLICY}x" = "x" ]; then
|
|
echo "${prog}: No start policy file specified." >&2
|
|
echo "${prog}: Unable to continue" >&2
|
|
return 1
|
|
fi
|
|
|
|
# Change to the BROLOGS directory
|
|
local _did_cd
|
|
cd "${BROLOGS}"
|
|
_did_cd=$?
|
|
|
|
if [ "${_did_cd}" != "0" ]; then
|
|
echo "${prog}: Failed to change to BROLOGS directory at ${BROLOGS}" >&2
|
|
echo "${prog}: Unable to continue" >&2
|
|
return 1
|
|
fi
|
|
|
|
# Start Bro
|
|
local _start_result
|
|
echo -n "${prog}: Starting "
|
|
|
|
# What type of start should be performed
|
|
if [ "${DEBUG}" = '1' ]; then
|
|
startbro
|
|
return
|
|
elif [ "${AUTO_RESTART}" = 't' ]; then
|
|
autorestart &
|
|
else
|
|
startbro &
|
|
fi
|
|
|
|
sleep 1
|
|
_start_result=$?
|
|
|
|
# Check the return code to see if it started
|
|
local _failed_check_count
|
|
_failed_check_count=0
|
|
if [ "${_start_result}" = '0' ] || [ "${_start_result}x" = 'x' ]; then
|
|
local _loop_count
|
|
_loop_count=0
|
|
while [ ${_loop_count} -lt 12 ]
|
|
do
|
|
_loop_count=$(( ${_loop_count} + 1 ))
|
|
echo -n '.'
|
|
|
|
# Sometimes the result from the fork takes a few seconds
|
|
# Check if was not set yet
|
|
if [ "${_start_result}x" = 'x' ]; then
|
|
_start_result=$?
|
|
|
|
if [ "${_start_result}x" != 'x' ] && [ "${_start_result}" != '0' ]; then
|
|
break
|
|
fi
|
|
else
|
|
# Use the status function to see if Bro is still up
|
|
local _status_result
|
|
status 2>/dev/null >/dev/null
|
|
_status_result=$?
|
|
|
|
if [ "${_status_result}" != '0' ] || [ "${_start_result}" != '0' ]; then
|
|
_failed_check_count=$(( ${_failed_check_count} + 1 ))
|
|
if [ ${_failed_check_count} -gt 11 ]; then
|
|
_start_result=1
|
|
break
|
|
fi
|
|
fi
|
|
fi
|
|
sleep 1
|
|
done
|
|
fi
|
|
|
|
if [ "${_start_result}" = '0' ]; then
|
|
echo ". SUCCESS"
|
|
else
|
|
echo ". FAILED"
|
|
fi
|
|
|
|
return ${_start_result}
|
|
}
|
|
|
|
startbro() {
|
|
|
|
local cur_date
|
|
local _already_running
|
|
local trace_file
|
|
local cmd_opts
|
|
local basic_cmd_opts
|
|
cur_date=`date +%y-%m-%d_%H.%M.%S`
|
|
BRO_LOG_SUFFIX="${BRO_HOSTNAME}.${cur_date}"
|
|
export BRO_LOG_SUFFIX
|
|
trace_file="${BROLOGS}/trace.${BRO_LOG_SUFFIX}"
|
|
info_log="${BROLOGS}/info.${BRO_LOG_SUFFIX}"
|
|
cmd_opts="${BRO_OPTS}"
|
|
|
|
# Check if Bro is already running
|
|
status 2>/dev/null >/dev/null
|
|
_already_running=$?
|
|
if [ "${_already_running}" = '0' ]; then
|
|
return 17
|
|
fi
|
|
|
|
# Build the command line.
|
|
# Should a trace file be created
|
|
if [ "${BRO_CREATE_TRACE_FILE}" = 'YES' ] || [ "${BRO_CREATE_TRACE_FILE}" = 'yes' ]; then
|
|
cmd_opts="${cmd_opts} -w ${trace_file}"
|
|
fi
|
|
|
|
# If a specific interface to capture on is specified then add it in
|
|
# This is a space delimited list
|
|
if [ "${BRO_CAPTURE_INTERFACE}x" != 'x' ]; then
|
|
for _intf in ${BRO_CAPTURE_INTERFACE}
|
|
do
|
|
cmd_opts="${cmd_opts} -i ${_intf}"
|
|
done
|
|
fi
|
|
|
|
# Append the Bro policy which to start
|
|
if [ "${BRO_START_POLICY}x" != 'x' ]; then
|
|
cmd_opts="${cmd_opts} ${BRO_START_POLICY}"
|
|
else
|
|
echo "${prog}: No start policy file specified." >&2
|
|
fi
|
|
|
|
local new_pid
|
|
local bro_result
|
|
cd "${BROLOGS}"
|
|
|
|
# Check whether DEBUG is set. If so then keep Bro attached to the shell
|
|
# and don't fork.
|
|
if [ "${DEBUG}" != '0' ]; then
|
|
"${BRO}" ${cmd_opts}
|
|
return
|
|
# Run bro. Redirect STDERR and STDOUT to $info_log
|
|
else
|
|
"${BRO}" ${cmd_opts} 2>>"${info_log}" >>"${info_log}" &
|
|
fi
|
|
|
|
bro_result=$?
|
|
new_pid=$!
|
|
|
|
# Check the exit code returned by Bro
|
|
if [ "${bro_result}" = '0' ] || [ -z "${bro_result}" ]; then
|
|
local _loop_count
|
|
_loop_count=0
|
|
while [ ${_loop_count} -lt 10 ]
|
|
do
|
|
_loop_count=$(( ${_loop_count} + 1 ))
|
|
if [ -f "${info_log}" ]; then
|
|
if [ "`grep -E '^listening on' ${info_log}`x" != 'x' ]; then
|
|
break
|
|
fi
|
|
fi
|
|
|
|
# break now if the process returned a non-zero value
|
|
if [ "${bro_result}x" != 'x' ] && [ "${bro_result}" != '0' ]; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
fi
|
|
|
|
# check to make sure that Bro actually started
|
|
if [ "${new_pid}x" = 'x' ] || [ "${bro_result}" != '0' ]; then
|
|
false
|
|
else
|
|
pidisrunning ${new_pid} "${BRO}"
|
|
_is_running=$?
|
|
if [ "${_is_running}" != '0' ]; then
|
|
bro_result=1
|
|
fi
|
|
fi
|
|
|
|
if [ "${bro_result}" != '0' ]; then
|
|
echo "${prog}: Failed to start Bro"
|
|
if [ -f "${info_log}" ] && [ -s "${info_log}" ]; then
|
|
cat "${info_log}"
|
|
ERROR_MESSAGE=`cat "${info_log}"`
|
|
else
|
|
ERROR_MESSAGE='Unknown error, no messages recieved on STDERR or STDOUT'
|
|
fi
|
|
|
|
# Call the post_start_hook function
|
|
LOG_SUFFIX="${BRO_LOG_SUFFIX}"
|
|
EXIT_CODE=${bro_result}
|
|
|
|
"${syslog_cmd}" -t "${prog}" "Bro has failed to start. ${ERROR_MESSAGE}"
|
|
|
|
post_start_hook
|
|
|
|
return ${bro_result}
|
|
else
|
|
"${syslog_cmd}" -t "${prog}" "Bro process (${new_pid}) has started"
|
|
fi
|
|
|
|
# Write the pid out to file
|
|
echo ${new_pid} > "${pidfile}"
|
|
|
|
# Write the active log suffix out to file
|
|
echo "${BRO_LOG_SUFFIX}" > "${active_log_file}"
|
|
|
|
# Write the start date out to file
|
|
local _start_time
|
|
_start_time=`date`
|
|
echo "${_start_time}" > "${start_time_file}"
|
|
|
|
# Write the auto-restart status out to file
|
|
if [ "${AUTO_RESTART}" = 't' ]; then
|
|
echo "ON" > "${autorestart_file}"
|
|
else
|
|
echo "OFF" > "${autorestart_file}"
|
|
fi
|
|
|
|
echo "Bro Version: `\"${BRO}\" -v 2>&1 | awk ' { print $3 } '`" >> "${info_log}"
|
|
echo "Started with the following command line options: ${cmd_opts}" >> "${info_log}"
|
|
|
|
# Get the BPF capture filter
|
|
local _print_filter
|
|
local _print_filter_result
|
|
# Strip out the trace file parameter if it exists
|
|
basic_cmd_opts=`removetraceopt "${cmd_opts}"`
|
|
_print_filter=`capturefilter "${basic_cmd_opts}"`
|
|
_print_filter_result=$?
|
|
if [ "${_print_filter_result}" = '0' ]; then
|
|
echo "Capture filter: ${_print_filter}" >> "${info_log}"
|
|
else
|
|
echo 'Capture filter: <not available>' >> "${info_log}"
|
|
fi
|
|
|
|
# Call the post_start_hook function
|
|
LOG_SUFFIX="${BRO_LOG_SUFFIX}"
|
|
PID=${new_pid}
|
|
EXIT_CODE=""
|
|
ERROR_MESSAGE=""
|
|
START_TIME="${_start_time}"
|
|
END_TIME=""
|
|
|
|
post_start_hook
|
|
|
|
local _exitwatch_result
|
|
exitwatch ${new_pid}
|
|
_exitwatch_result=$?
|
|
|
|
return ${_exitwatch_result}
|
|
}
|
|
|
|
autorestart() {
|
|
|
|
local _cur_restart_count
|
|
local _max_restart_count
|
|
local _called_from # name of function that called autorestart if any
|
|
local _failed_first_start
|
|
local _start_res
|
|
local _minimum_life
|
|
_minimum_life=$(( `epochtime` + ${minimum_mortality_time} ))
|
|
|
|
_called_from="${1}"
|
|
_cur_restart_count=0
|
|
_max_restart_count=${BRO_MAX_RESTART_ATTEMPTS}
|
|
|
|
# If _max_restart_count is not defined or is less than 1 then
|
|
# this equats to no restart limit.
|
|
if [ "${_max_restart_count}x" = 'x' ] || [ ${_max_restart_count} -lt '1' ]; then
|
|
_cur_restart_count=-2
|
|
_max_restart_count=-1
|
|
fi
|
|
|
|
# loop and restart bro until told to exit
|
|
while [ ${_cur_restart_count} -lt ${_max_restart_count} ]
|
|
do
|
|
local _cur_epoch_time
|
|
# startbro will block until Bro exits
|
|
startbro
|
|
_start_res=$?
|
|
|
|
_cur_epoch_time="`epochtime`"
|
|
|
|
if [ "${_start_res}" = '0' ]; then
|
|
# Bro exited normally due to a planned stop
|
|
break
|
|
elif [ "${_start_res}" = '17' ]; then
|
|
echo "${prog}: Unable to begin autorestart. Stop the running instance of Bro first." >&2
|
|
echo "${prog}: Unable to continue" >&2
|
|
break
|
|
elif [ ${_cur_epoch_time} -lt ${_minimum_life} ]; then
|
|
_failed_first_start=1
|
|
break
|
|
fi
|
|
|
|
# If _max_restart_count is greater than 0 increment _cur_restart_count
|
|
# otherwise don't because no restart limit has been set.
|
|
if [ ${_max_restart_count} -gt '0' ]; then
|
|
_cur_restart_count=$(( ${_cur_restart_count} + 1 ))
|
|
fi
|
|
|
|
sleep ${time_between_restarts}
|
|
"${syslog_cmd}" -t "${prog}" "Autorestarting Bro due to unexpected exit"
|
|
done
|
|
|
|
# Check why the loop exited
|
|
if ! [ ${_cur_restart_count} -lt ${_max_restart_count} ]; then
|
|
# Stop Bro, this will force a cleanup of the run-time directory
|
|
stopbro 2> /dev/null 1> /dev/null
|
|
EXIT_CODE=${_start_res}
|
|
ERROR_MESSAGE="Exceeded maximum autorestart count of ${BRO_MAX_RESTART_ATTEMPTS}. No further attempts will be made to restart Bro"
|
|
END_TIME=`date`
|
|
|
|
# Call the post_exit_hook to report the error
|
|
post_exit_hook
|
|
|
|
return 1
|
|
elif [ "${_failed_first_start}" = '1' ]; then
|
|
# Bro failed on the very first start and this was not during a checkpoint.
|
|
# No further restarts will be attempted.
|
|
"${syslog_cmd}" -t "${prog}" "Bro process failed on first start attempt. No further restart attempts will be made."
|
|
EXIT_CODE=""
|
|
ERROR_MESSAGE="Failed on first start attempt. No further restart attempts will be made."
|
|
|
|
# Call the post_exit_hook to report the error
|
|
post_exit_hook
|
|
|
|
return ${_start_res}
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
stopbro() {
|
|
# Check to see if bro is running
|
|
local _pid
|
|
local _bro_is_running
|
|
local _status_result
|
|
|
|
status 2>/dev/null >/dev/null
|
|
_status_result=$?
|
|
|
|
if [ "${_status_result}" = '0' ]; then
|
|
# try and stop it
|
|
local _kill_result
|
|
echo -n "${prog}: Stopping "
|
|
_pid=`cat "${pidfile}"`
|
|
_bro_is_running="t"
|
|
|
|
cleanup
|
|
for _i in 1 2; do
|
|
echo -n '.'
|
|
sleep 1
|
|
done
|
|
|
|
kill -TERM ${_pid}
|
|
_kill_result=$?
|
|
if [ "${_kill_result}" = "0" ]; then
|
|
true
|
|
else
|
|
echo "${prog}: Failed to stop process with pid ${_pid}"
|
|
return 1
|
|
fi
|
|
else
|
|
_bro_is_running="f"
|
|
if [ -f "${pidfile}" ]; then
|
|
cleanup
|
|
fi
|
|
fi
|
|
|
|
if [ "${_bro_is_running}" = "f" ]; then
|
|
echo "${prog}: No running process to stop"
|
|
return 0
|
|
fi
|
|
|
|
# Now see if the process is still hanging around.
|
|
# Bro can stick around a long time as it writes out logs and writes it's
|
|
# state info to disk
|
|
local _is_dead
|
|
for _i in 1 2 3 4 5 6 7 8 9 10; do
|
|
_is_dead=""
|
|
pidisrunning ${_pid} "${BRO}"
|
|
_is_dead=$?
|
|
echo -n "."
|
|
if [ "${_is_dead}" != "0" ]; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
if [ "${_is_dead}" != "0" ]; then
|
|
echo " SUCCESS !"
|
|
|
|
else
|
|
echo "${prog}: Process (${_pid}) has still not exited."
|
|
echo "${prog}: If it has not exited after ${BRO_MAX_SHUTDOWN_TIME} seconds from now it will be forced to exit."
|
|
# Create a backgound process to watch the terminated Bro
|
|
# If the process still exists after the BRO_MAX_SHUTDOWN_TIME
|
|
# then send it a SIG_KILL
|
|
stopwatch ${_pid} "${BRO}" &
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
checkpoint() {
|
|
# Check to make sure bro is already running
|
|
local _old_pid=""
|
|
local _old_bro_running
|
|
local _status_result
|
|
|
|
echo "${prog}: Beginning the checkpoint process"
|
|
status 2>/dev/null >/dev/null
|
|
_status_result=$?
|
|
|
|
if [ "${_status_result}" = '0' ]; then
|
|
_old_pid=`cat "${pidfile}"`
|
|
_old_bro_running="t"
|
|
else
|
|
_old_bro_running="f"
|
|
fi
|
|
|
|
if [ "${_old_bro_running}" = "f" ]; then
|
|
cleanup
|
|
echo "${prog}: No current instance of Bro is running."
|
|
return 1
|
|
fi
|
|
|
|
echo "${prog}: Starting a new Bro instance."
|
|
if [ "${_old_bro_running}" = "t" ]; then
|
|
echo "${prog}: Will wait for ${BRO_CHECKPOINT_OVERLAP_TIME} seconds before stopping the old Bro instance."
|
|
fi
|
|
|
|
# start a new Bro
|
|
"${syslog_cmd}" -t "${prog}" "Starting the checkpoint process (${_old_pid})"
|
|
cleanup
|
|
${this_script} start > /dev/null &
|
|
|
|
# Create a background process to stop the old Bro process if
|
|
# it is still running
|
|
if [ "${_old_bro_running}" = "t" ]; then
|
|
{
|
|
# Renice the old process so it doesn't compete with the new active Bro
|
|
renice ${renice_checkpoint_level} ${_old_pid} 2> /dev/null 1> /dev/null
|
|
|
|
# Sleep for BRO_CHECKPOINT_OVERLAP_TIME
|
|
sleep ${BRO_CHECKPOINT_OVERLAP_TIME}
|
|
|
|
# kill the old bro process
|
|
kill -TERM ${_old_pid}
|
|
|
|
# call the stopwatch function to make sure the old bro process exits
|
|
# before BRO_MAX_SHUTDOWN_TIME
|
|
stopwatch ${_old_pid} "${BRO}"
|
|
"${syslog_cmd}" -t "${prog}" "Checkpoint process complete. Bro process (${_old_pid}) is done"
|
|
} &
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
status() {
|
|
local _pid
|
|
local _ret_val
|
|
if [ -f "${pidfile}" ] && [ -f "${start_time_file}" ] && [ "${active_log_file}" ]; then
|
|
_pid=`cat "${pidfile}" | awk '{ print $1 }'`
|
|
pidisrunning ${_pid}
|
|
_ret_val=$?
|
|
if [ "${_ret_val}" = "0" ]; then
|
|
true
|
|
else
|
|
echo "${prog}: Bro is not running"
|
|
return 1
|
|
fi
|
|
else
|
|
echo "${prog}: Bro is not running"
|
|
return 1
|
|
fi
|
|
|
|
local _start_time
|
|
local _log_suffix
|
|
local _bro_ver
|
|
local _autorestart_status
|
|
|
|
_start_time=`cat "${start_time_file}"`
|
|
_log_suffix=`activelogsuffix`
|
|
_bro_ver=`"${BRO}" -v 2>&1 | awk ' { print $3 } '`
|
|
_autorestart_status=`cat "${autorestart_file}"`
|
|
echo "Bro is running (pid: ${_pid})"
|
|
echo "Autorestart: ${_autorestart_status}"
|
|
echo "Running since: ${_start_time}"
|
|
echo "Bro Version: ${_bro_ver}"
|
|
echo "Active log suffix: ${_log_suffix}"
|
|
|
|
return 0
|
|
}
|
|
|
|
activelogsuffix() {
|
|
local _log_suffix
|
|
|
|
if [ -f "${active_log_file}" ] && [ -r "${active_log_file}" ]; then
|
|
# ok file exists
|
|
_log_suffix=`cat "${active_log_file}"`
|
|
echo -n "${_log_suffix}"
|
|
else
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
exitwatch() {
|
|
# pid number to wait for
|
|
PID=${1}
|
|
|
|
if [ -z ${PID} ]; then
|
|
echo "${prog}: No pid number passed to function exitwatch" > 2
|
|
echo "${prog}: Unable to continue" > 2
|
|
exit 1
|
|
fi
|
|
|
|
# trap some signals
|
|
# Ignore HUP, QUIT, INT signals
|
|
trap '' SIGHUP SIGINT SIGQUIT
|
|
# Call stop if a TERM signal is sent to this ($$) process
|
|
trap 'stopbro 2>/dev/null >/dev/null; true' SIGTERM
|
|
|
|
wait ${PID} 2> /dev/null
|
|
|
|
EXIT_CODE=$?
|
|
END_TIME=`date`
|
|
|
|
# Currently Bro does not always exit with the right exit codes
|
|
# Need to figure out if something besides 0 really happened
|
|
if [ "${EXIT_CODE}x" = 'x' ]; then
|
|
# Something evil happened!
|
|
EXIT_CODE=1
|
|
false
|
|
# The following exit codes are expected for a successful exit:
|
|
# 0, 130, 145
|
|
# 143 (SIGTERM) and 130 (SIGINT) are returned when wait is interrupted by
|
|
# a trapped signal.
|
|
elif [ "${EXIT_CODE}" = '0' ] || \
|
|
[ "${EXIT_CODE}" = '143' ] || \
|
|
[ "${EXIT_CODE}" = '130' ]; then
|
|
if ! [ -f "${pidfile}" ]; then
|
|
# A normal exit occurred as the pid file has been cleaned up
|
|
EXIT_CODE=0
|
|
true
|
|
else
|
|
check_pid=`cat "${pidfile}"`
|
|
if [ "${check_pid}" != "${PID}" ]; then
|
|
# The PID has changed which means either a restart was forced
|
|
# or a checkpoint has occurred. In either case it is
|
|
# likely that Bro exited correctly
|
|
true
|
|
else
|
|
# Something went wrong with Bro because it exited before the
|
|
# temporal files were cleaned up
|
|
EXIT_CODE=1
|
|
cleanup
|
|
false
|
|
fi
|
|
fi
|
|
elif [ ! -f "${pidfile}" ] && [ ! -f "${start_time_file}" ]; then
|
|
# Bro exited with a non-zero value but the exit was planned.
|
|
# Log the an error message but return a zero exit status as this
|
|
# was a planned exit.
|
|
"${syslog_cmd}" -t "${prog}" "Bro process (${PID}) returned a non-zero exit status but the exit was planned."
|
|
EXIT_CODE=0
|
|
else
|
|
# An unexpected exit code was returned by Bro
|
|
false
|
|
fi
|
|
|
|
# Set the ERROR_MESSAGE to something useful if there was an error
|
|
if [ "${EXIT_CODE}" != '0' ]; then
|
|
if [ -f "${info_log}" ] && [ -s "${info_log}" ]; then
|
|
ERROR_MESSAGE=`tail -2 "${info_log}"`
|
|
else
|
|
ERROR_MESSAGE="Unknown error occurred"
|
|
fi
|
|
|
|
"${syslog_cmd}" -t "${prog}" "Bro process (${PID}) exited with a code of ${EXIT_CODE}"
|
|
else
|
|
"${syslog_cmd}" -t "${prog}" "Bro process (${PID}) stopped"
|
|
fi
|
|
|
|
# Call the post_exit_hook function
|
|
post_exit_hook
|
|
|
|
# renamed logfiles now that file writing is done.
|
|
# turned off till sc04 is over
|
|
# rename_logfiles "${BRO_LOG_SUFFIX}"
|
|
|
|
return ${EXIT_CODE}
|
|
}
|
|
|
|
stopwatch() {
|
|
# This function is used to run in the background and watch the pid
|
|
# and command line as returned by ps. This function should be
|
|
# forked off as a background process to monitor a terminating Bro
|
|
# process to make sure that it ends by BRO_MAX_SHUTDOWN_TIME
|
|
|
|
local _pid
|
|
local _cmd_line
|
|
_pid=${1}
|
|
_cmd_line="${2}"
|
|
|
|
# Make sure that _pid has a value
|
|
if [ -z ${_pid} ]; then
|
|
_pid=`cat "${pidfile}"`
|
|
if [ -z ${_pid} ]; then
|
|
echo "${prog}: Unable to find a process number to monitor for stopwatch" >&2
|
|
fi
|
|
echo "First argument passed to function stopwatch must be a PID greater than zero." >&2
|
|
return 1
|
|
fi
|
|
|
|
# Make sure that _pid has a value larger than 0
|
|
if [ "${_pid}" -lt 1 ]; then
|
|
echo "First argument passed to function stopwatch must be a PID greater than zero." >&2
|
|
return 1
|
|
fi
|
|
|
|
# Make sure that _cmd_line has a value
|
|
if [ -z ${_cmd_line} ]; then
|
|
echo "Second option passed to function stopwatch must be the program name as" >&2
|
|
echo "specified on the command line when first started." >&2
|
|
fi
|
|
|
|
local _cur_wait
|
|
local _max_wait
|
|
local _sleep_time
|
|
_cur_wait=0
|
|
_max_wait=${BRO_MAX_SHUTDOWN_TIME}
|
|
_sleep_time=10
|
|
while [ "${_cur_wait}" -lt "${_max_wait}" ]
|
|
do
|
|
local _res
|
|
pidisrunning ${_pid} "${_cmd_line}"
|
|
local _ret_val=$?
|
|
if [ "${_ret_val}" != "0" ]; then
|
|
break
|
|
fi
|
|
_cur_wait=$(( ${_cur_wait} + ${_sleep_time} ))
|
|
sleep ${_sleep_time}
|
|
done
|
|
|
|
# Check if the process died on it's own
|
|
if [ "${_cur_wait}" -ge "${_max_wait}" ]; then
|
|
# Nope, didn't exit on it's own
|
|
kill -KILL ${_pid}
|
|
"${syslog_cmd}" -t "${prog}" "Bro process (${_pid}) exceeded the maximum shutdown time and was forcibly terminated"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
runasnonroot() {
|
|
|
|
# Check on whether to start as the current user or a different user
|
|
#
|
|
# Check if the alternate user has been specified
|
|
if [ "${alternate_user_id}x" != "x" ]; then
|
|
# Check if the current username is the same as that in $alternate_user_id
|
|
if [ "${USER}" = "${alternate_user_id}" ]; then
|
|
# The alternate is the same as the current, no need to su
|
|
alternate_user_id=''
|
|
return 254
|
|
else
|
|
# Allow only users with root privelages to use su -
|
|
if [ -w /etc/passwd ]; then
|
|
echo "${prog}: Running as non-root user ${alternate_user_id}" >&2
|
|
su - ${alternate_user_id} -c "${this_script} ${cur_args} < /dev/null"
|
|
else
|
|
# Nope, no root privelage
|
|
alternate_user_id=''
|
|
echo "$prog: Must have root privileges in order to run as a non-root user" >&2
|
|
echo "FAILED!" >&2
|
|
return 1
|
|
fi
|
|
fi
|
|
else
|
|
# No alternate user specified
|
|
return 254
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
pidisrunning() {
|
|
|
|
local _pid
|
|
local _cmd_line
|
|
local _running_pid
|
|
|
|
_pid=${1}
|
|
_cmd_line="${2}" # optional
|
|
|
|
if [ `uname | grep "CYGWIN"` ]; then
|
|
# cygwin ps command
|
|
if [ -z ${_cmd_line} ]; then
|
|
_running_pid=`ps -as | awk ' { print $1 } ' | grep ${_pid}`
|
|
else
|
|
_running_pid=`ps -as | grep "${_pid}.*${_cmd_line}"`
|
|
fi
|
|
else
|
|
# the rest of *NIX
|
|
_running_pid=`ps ax -o "pid,command" | grep "${_pid}.*${_cmd_line}" | grep -v "grep ${_pid}.*${_cmd_line}"`
|
|
fi
|
|
|
|
if [ "${_running_pid}x" = 'x' ]; then
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
|
|
}
|
|
|
|
removetraceopt() {
|
|
local _cmd_opts
|
|
local _ret_string
|
|
local _sed_patt
|
|
|
|
_cmd_opts="${1}"
|
|
|
|
_sed_patt='/-w/ {
|
|
/-w *\"/ {
|
|
s/ -w *\"[^"]\{1,\}\" */ /
|
|
t
|
|
s/^-w *[^" ]\{1,\} */ /
|
|
}
|
|
t
|
|
/-w *[^ ]\{1,\}/ {
|
|
s/ -w *[^ ]\{1,\} */ /
|
|
t
|
|
s/^-w *[^ ]\{1,\} */ /
|
|
}
|
|
}'
|
|
|
|
_ret_string=`echo -n "${cmd_opts}" | sed "${_sed_patt}"`
|
|
|
|
echo -n "${_ret_string}"
|
|
return 0
|
|
}
|
|
|
|
capturefilter() {
|
|
local _cmd_opts
|
|
local _ret_string
|
|
local _dummy_dir
|
|
local _cur_log_suffix
|
|
|
|
_cmd_opts="${1}"
|
|
_cmd_opts="${_cmd_opts} print-filter.bro"
|
|
_dummy_dir="${BROLOGS}/.print-filter"
|
|
|
|
mkdir "${_dummy_dir}" 1>/dev/null 2>/dev/null
|
|
cd "${_dummy_dir}"
|
|
|
|
_ret_string=`"${BRO}" ${_cmd_opts} 2>/dev/null`
|
|
|
|
cd "${BROLOGS}"
|
|
rm -rf "${_dummy_dir}"
|
|
|
|
if [ "${_ret_string}x" = 'x' ]; then
|
|
return 1
|
|
else
|
|
echo "${_ret_string}"
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
cleanup() {
|
|
|
|
rm -f "${pidfile}"
|
|
rm -f "${active_log_file}"
|
|
rm -f "${start_time_file}"
|
|
rm -f "${autorestart_file}"
|
|
return 0
|
|
}
|
|
|
|
epochtime() {
|
|
echo -n "`date +'%s'`"
|
|
}
|
|
|
|
# Need something more comprehensive but this does fine for now.
|
|
rename_logfiles() {
|
|
# rename the closed log file passed in as arg ${1} and add
|
|
# the stop time. The format of the files will now be:
|
|
# file.host.YY-MM-DD_HH.MM.SS-YY-MM-DD_HH.MM.SS, where the first date
|
|
# is the start time, and the end date is the time bro was shutdown
|
|
local _end_time
|
|
local _cur_log_suffix
|
|
|
|
# The start time log suffix is passed in as an argument
|
|
_cur_log_suffix="${1}"
|
|
|
|
# change directory to where we need to do our work
|
|
cd "${BROLOGS}"
|
|
|
|
# If _cur_log_suffix has no value then don't do anything
|
|
if [ "${_cur_log_suffix}x" = 'x' ]; then
|
|
#echo "No logs to rename"
|
|
return 1
|
|
fi
|
|
# format the end time once, and use for all the logs
|
|
_end_time=`date "+%y-%m-%d_%H.%M.%S"`
|
|
|
|
# grok out the logs to change, and change them, hope we have lots of disk space
|
|
filelist=`ls *.${_cur_log_suffix}`
|
|
for name in ${filelist}; do
|
|
#echo "Moving ${name} to ${name}-${_end_time}"
|
|
mv "${name}" "${name}-${_end_time}"
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# See how we were called.
|
|
case "$1" in
|
|
status|--status)
|
|
status
|
|
;;
|
|
start|--start|autorestart|--autorestart|stop|--stop|stopwatch|--stopwatch|checkpoint|--checkpoint)
|
|
# Check on whether to run as non-root
|
|
runasnonroot
|
|
_run_non_root_res=$?
|
|
if [ "${_run_non_root_res}" = "0" ]; then
|
|
true
|
|
elif [ "${_run_non_root_res}" = "1" ]; then
|
|
false
|
|
else
|
|
# If it gets here then the script is either running as root or it has
|
|
# already done an su - to the non-root user.
|
|
case "$1" in
|
|
start|--start)
|
|
# One more command line option is available
|
|
second_opt="${2}"
|
|
|
|
# enable autorestart?
|
|
AUTO_RESTART="f"
|
|
if [ "${BRO_ENABLE_AUTORESTART}" = "YES" ] || \
|
|
[ "${BRO_ENABLE_AUTORESTART}" = "yes" ] || \
|
|
[ "${second_opt}" = "autorestart" ]; then
|
|
AUTO_RESTART="t"
|
|
fi
|
|
|
|
# An explicit noautorestart will always disable it
|
|
if [ "${second_opt}" = "noautorestart" ]; then
|
|
AUTO_RESTART="f"
|
|
fi
|
|
|
|
start
|
|
;;
|
|
startbro|--startbro)
|
|
startbro
|
|
;;
|
|
stop|--stop)
|
|
stopbro
|
|
;;
|
|
# stopwatch|--stopwatch)
|
|
# stopwatch ${2}
|
|
# ;;
|
|
checkpoint|--checkpoint)
|
|
checkpoint
|
|
esac
|
|
fi
|
|
;;
|
|
*)
|
|
echo "Usage: ${prog} {start|stop|checkpoint|status}"
|
|
echo "start has the additional option which can be used to disable/enable "
|
|
echo "autorestart: "
|
|
echo " start [no]autorestart"
|
|
echo ""
|
|
exit 1
|
|
esac
|
|
|
|
exit $?
|
|
|
|
|
|
# So how does this thing work and why is it always running when Bro is running?
|
|
|
|
# bro.rc is the init script which starts, stops, checkpoints, and monitors a
|
|
# running instance of Bro.
|
|
# bro.rc logs it's actions to syslog via the logger command.
|
|
# bro.rc offers users an interface into the starting and stopping of a Bro
|
|
# process via the file $BROHOME/etc/bro.rc-hooks.rc. This allows for
|
|
# actions to be sent to any custom monitoring or alerting programs the
|
|
# user may wish to use.
|
|
|
|
|
|
# There are four command line options available
|
|
# to the user. Each is covered below with an explanation.
|
|
# 1) start
|
|
# Start has two additional commands of autorestart and noautorestart which
|
|
# can be used to enable and disable the autorestart feature. This feature
|
|
# is also controlled by setting the variable BRO_ENABLE_AUTORESTART in
|
|
# to 'YES' or 'NO' bro.cfg. Any autorestart option given to start on the
|
|
# command line will override the bro.cfg setting in BRO_ENABLE_AUTORESTART.
|
|
#
|
|
# 'start' does just as it's name implies, it starts a new Bro process.
|
|
# When autorestart is enabled bro.rc will monitor the Bro process and
|
|
# restart it if it fails unexpectedly for any reason. Bro will be restarted
|
|
# a maximum count as set by BRO_MAX_RESTART_ATTEMPTS in bro.cfg. If the
|
|
# BRO_MAX_RESTART_ATTEMPTS count is exceeded then no further restart
|
|
# attempts will be made and the error will be sent to syslog.
|
|
#
|
|
# After Bro has started either successfully or unsuccessfully the function
|
|
# post_start_hook will be called. (see bro.rc-hooks.sh for more info).
|
|
#
|
|
# If start is called and Bro is already running this is an error and
|
|
# the script will return 1.
|
|
#
|
|
# If Bro starts successfully bro.rc will return 0.
|
|
#
|
|
# 2) stop
|
|
# Self explantory, stops a running Bro process. Stop will cleanup the
|
|
# temporal files such as pid, start_time, etc, send a SIGTERM to the
|
|
# running instance of Bro, and then if needed call another process to
|
|
# to watch the exiting process. If the process has not terminated by
|
|
# BRO_MAX_SHUTDOWN_TIME seconds then a SIGKILL will be sent to the process
|
|
# and a message will be sent to syslog.
|
|
#
|
|
# 3) checkpoint
|
|
# A checkpoint is where a new instance of Bro is started and then the old
|
|
# one is terminated. (see documentation on when and why this is done).
|
|
# Essentially what happens is that temporal files are cleaned up, a
|
|
# new instance of Bro is started, bro.rc thens waits
|
|
# BRO_CHECKPOINT_OVERLAP_TIME seconds before sending the old Bro process
|
|
# a SIGTERM. An exit code of 1 will be returned in the event checkpoint
|
|
# is called and there is no running Bro process.
|
|
#
|
|
# 4) status
|
|
# Used to check if Bro is running. Also outputs the Bro version, the start
|
|
# time, the current log suffix, and whether autorestart is enabled or not.
|
|
# If Bro is running the command will return 0 and if Bro is not running the
|
|
# command will return 1.
|
|
#
|
|
# The detection of when Bro exits is done through the use of the shell
|
|
# builtin 'wait'. The function exitwatch will wait on the Bro child
|
|
# pif until it exits. A few tests are run after Bro returns to determine
|
|
# If the exit was expected or unexpected. An unexpected exit is anytime
|
|
# Bro exits or terminates outside the control of bro.rc. After the Bro
|
|
# process returns and before bro.rc exits the post_exit_hook will be called.
|
|
#
|
|
# bro.rc traps SIGTERM and ignores SIGHUP. If SIGTERM is sent to the
|
|
# bro.rc process this will also cause bro.rc to stop the running Bro process
|
|
# it monitors as well. This is to provide proper behavior in the event of
|
|
# a system reboot or halt.
|