#!/usr/bin/sh

##  Copyright 2007-2025 Carnegie Mellon University
##  See license information in LICENSE.txt.

# yaf start/control script
#
# /etc/init.d/yaf
# chkconfig: - 19 96
# description:  Control yaf as a live capture daemon
# processname: yaf
### BEGIN INIT INFO
# Provides: yaf
# Required-Start: $local_fs $remote_fs $network $named $syslog $time
# Required-Stop: $local_fs $remote_fs $network $named $syslog $time
# Short-Description: start and stop YAF yaf daemon
# Description: Run YAF for live pcap collection and write either to a network
#              socket or rotating IPFIX files.
### END INIT INFO

PROGRAM_NAME="yaf"

# Extract an addendum from the filename. For example, an init script
# named 'yaf-secondary' would look for a config file named
# 'yaf'-secondary.conf  and produce log and pid files with
# '-secondary' in their names.

SCRIPT_BASE_NAME="$( basename -- "$0" | sed -e 's/\.init\.d$//' -e 's/\.sh$//' )"
INSTANCE_SUFFIX="$(
    echo "$SCRIPT_BASE_NAME" |
    sed -ne "/^${PROGRAM_NAME}-/ s/^${PROGRAM_NAME}// p"
)"

# The INSTANCE_SUFFIX is used as part of the filename of the config
# file, as well as the PID file and log file base name.

SCRIPT_CONFIG_FILENAME="${PROGRAM_NAME}${INSTANCE_SUFFIX}.conf"

### Above this from init.d/yaf_exec-init-header.sh.in


### Below this from init.d/yaf_exec-common.sh.in
### Loading of configuration file and argument checking

############################################################################

# This script requires the following configuration variables from the
# /etc/yaf.conf file:
#
# ENABLED               must be non-empty to start YAF
# YAF_USER              user to become after opening capture device (optional)
# YAF_CAP_TYPE          capture driver (pcap, others if available)
# YAF_CAP_IF            capture interface name
# YAF_IPFIX_PROTO       collector transport protocol
# YAF_IPFIX_HOST        collector hostname
# YAF_IPFIX_PORT        collector port (optional; default 4739)
# YAF_LOG               log file or syslog facility name (optional)
# YAF_EXTRAFLAGS        Additional YAF command-line flags (optional)
# YAF_PIDFILE           pidfile path (optional)
# YAF_ROTATE_LOCATION   location to write rotating IPFIX files (optional)
# YAF_ROTATE_TIME       seconds interval to rotate files (optional)
# YAF_STATEDIR          directory holding the log subdirectory (optional)
# PROGRAM_PATH          path to yaf executable (optional)

############################################################################

# SHELL FUNCTIONS

# check_executable PROGRAM
#
#   Verifies that PROGRAM is executable.  Prints an error message and
#   exits the script if PROGRAM is not executable.
#
check_executable()
{
    if [ ! -x "$1" ] ; then
        echo "$0: '$1' is not executable or not found."
        exit 1
    fi
}

# check_varempty VARNAME VALUE
#
#   Verifies that VARNAME has a value.  If the second argument (value
#   of VARNAME) is empty, prints an error message that VARNAME is not
#   set and exits the script.
#
check_varempty()
{
    if [ "x$2" = "x" ] ; then
        echo "$0: the '$1' parameter in '${SCRIPT_CONFIG}' is not set."
        exit 1
    fi
}

############################################################################

# set the loadable library path to pick up both default plugin
# libraries and the application labeler libraries
for dir in /usr/lib64 /usr/lib /usr/lib64 ; do
    if [ -d "$dir"/yaf ] ; then
        export LTDL_LIBRARY_PATH="${dir}${LTDL_LIBRARY_PATH:+$LTDL_LIBRARY_PATH}"
        break
    fi
done

############################################################################

# PROGRAM_NAME and SCRIPT_CONFIG_FILENAME should be set before this point.

# PROGRAM_NAME is the name of the executable program being managed by
# this script.

# SCRIPT_CONFIG_FILENAME is the name of the configuration file from
# which to read environment variables. Typically some variation on
# "${PROGRAM_NAME}.conf".

# SCRIPT_CONFIG_LOCATION is the directory where the
# ${SCRIPT_CONFIG_FILENAME} config file is located. It can be set via an
# environment variable. If the envar is not set, then it defaults to
# /etc

if [ -z "${SCRIPT_CONFIG_LOCATION}" ] ; then
    SCRIPT_CONFIG_LOCATION="/etc"
fi

SCRIPT_CONFIG="${SCRIPT_CONFIG_LOCATION}/${SCRIPT_CONFIG_FILENAME}"

if [ ! -f "${SCRIPT_CONFIG}" ] ; then
    echo "$0: Configuration file '${SCRIPT_CONFIG}' does not exist."
    exit 0
fi

# Load the configuration

. "${SCRIPT_CONFIG}"

# Check enabled state

if [ "x$ENABLED" = "x" ] ; then
    exit 0
fi

# Verify we have a runnable YAF

if [ -z "${PROGRAM_PATH}" ] ; then
    PROGRAM_PATH="/usr/bin/${PROGRAM_NAME}"
fi

check_executable ${PROGRAM_PATH}

# verify we have a good configuration

check_varempty  "YAF_CAP_TYPE"      ${YAF_CAP_TYPE}
check_varempty  "YAF_CAP_IF"        ${YAF_CAP_IF}

if [ "x$YAF_USER" != "x" ] ; then
    YAFARGS="$YAFARGS --become-user $YAF_USER"
fi

YAFARGS="$YAFARGS --live $YAF_CAP_TYPE --in $YAF_CAP_IF"

if [ "x$YAF_IPFIX_PROTO" != "x" ] ; then
    # Network Export
    check_varempty  "YAF_IPFIX_HOST"    ${YAF_IPFIX_HOST}
    YAFARGS="$YAFARGS --ipfix $YAF_IPFIX_PROTO --out $YAF_IPFIX_HOST"
    if [ "x$YAF_IPFIX_PORT" != "x" ] ; then
        YAFARGS="$YAFARGS --ipfix-port $YAF_IPFIX_PORT"
    fi

elif [ "x$YAF_ROTATE_LOCATION" != "x" ] ; then
    # Rotating File Export
    YAFARGS="$YAFARGS --out $YAF_ROTATE_LOCATION --lock"
    if [ "x$YAF_ROTATE_TIME" != "x" ] ; then
        YAFARGS="$YAFARGS --rotate $YAF_ROTATE_TIME"
    else
        YAFARGS="$YAFARGS --rotate 120"
    fi

else
    # Force an error
    check_varempty  "YAF_IPFIX_PROTO"   ${YAF_IPFIX_PROTO}
    check_varempty  "YAF_IPFIX_HOST"    ${YAF_IPFIX_HOST}
fi


if [ "x$YAF_STATEDIR" = "x" ] ; then
    YAF_STATEDIR="/var"
fi

if [ "x$YAF_LOG" = "x" ] ; then
    if [ -d "$YAF_STATEDIR/log" ] ; then
        YAF_LOG="${YAF_STATEDIR}/log/${PROGRAM_NAME}${INSTANCE_SUFFIX}.log"
    elif [ -d "/var/log" ] ; then
        YAF_LOG="/var/log/${PROGRAM_NAME}${INSTANCE_SUFFIX}.log"
    else
        echo "$0: YAF_LOG is not set and the '$YAF_STATEDIR/log' directory does not exist."
        exit 1
    fi
fi

YAFARGS="$YAFARGS --log $YAF_LOG --verbose"

YAFARGS="$YAFARGS $YAF_EXTRAFLAGS"

### Above this from init.d/yaf_exec-common.sh.in


### Below this from init.d/yaf_exec-init-footer.sh.in
### Init startup script footer: Final checks and handling arguments

if [ "x$YAF_PIDFILE" = "x" ]; then
    if [ -d "$YAF_STATEDIR/run" ] ; then
         YAF_PIDFILE="$YAF_STATEDIR/run/${PROGRAM_NAME}${INSTANCE_SUFFIX}.pid"
    elif [ -d "/var/run" ] ; then
         YAF_PIDFILE="$YAF_STATEDIR/run/${PROGRAM_NAME}${INSTANCE_SUFFIX}.pid"
    else
         echo "$0: YAF_PIDFILE not set and the '$YAF_STATEDIR/run' directory does not exist."
         exit 1
    fi
fi

YAFARGS="$YAFARGS --pidfile $YAF_PIDFILE"

# Run as a daemon
YAFARGS="$YAFARGS --daemonize"

checkpid() {
    kill -0 $1 >/dev/null 2>&1 && return 0
    return 1
}

getPid() {
    RETVAL=1
    if [ -f "$YAF_PIDFILE" ] ; then
        RETVAL=2
        read pid < ${YAF_PIDFILE}
        if [ "X$pid" != "X" ] ; then
            RETVAL=3
            # Found a pid
            if checkpid $pid ; then
                echo $pid
                RETVAL=0
            fi
        fi
    fi
    echo ""
    return $RETVAL
}

start() {
    /bin/echo -n "Starting ${PROGRAM_NAME}:	"
    PID=`getPid`
    if [ "x${PID}" != "x" ]; then
       echo ""
       echo "${PROGRAM_NAME} is already running.  PID: ${PID}"
       return 0
    fi

    $PROGRAM_PATH $YAFARGS
    RETVAL=$?
    if [ "$RETVAL" -ne "0" ] ; then
        echo "[Failed]"
    else
        sleep 1
        PID=`getPid`
        if [ "x$PID" = "x" ] ; then
            echo "[Failed]"
            RETVAL=1
        else
            if [ -d /var/lock/subsys ] ; then
                touch $lockfile
            fi
            echo '[OK]'
        fi
    fi
    return $RETVAL
}

stop() {
    PID=`getPid`
    if [ "x${PID}" = "x" ] ; then
        echo "${PROGRAM_NAME} not running"
        return 0
    fi
    /bin/echo -n "Stopping ${PROGRAM_NAME}:	"
    /bin/kill -s INT $PID
    i=0
    while [ -e /proc/$PID ]; do
        if [ $i = 1800 ]; then break; fi
        sleep 0.1
        i=$(( i+1 ))
    done
    (checkpid $PID)
    RETVAL=$?
    if [ "$RETVAL" -eq "1" ]
    then
        echo '[OK]'
        RETVAL=0
    else
        echo '[Failed]'
        RETVAL=1
    fi
# yaf removes pidfile upon closing
#    /bin/rm -f ${YAF_PIDFILE} 2> /dev/null
    rm -f $lockfile
    return $RETVAL
}

restart() {
    stop
    start
}

reload() {
    restart
}

status() {
    if [ $# -gt 0 ] ; then
        doEcho=0
    else
        doEcho=1
    fi

    # first check if the process is running
    PID=`getPid`
    RETVAL=$?

    if [ $doEcho -eq 1 ] ; then
        case "$RETVAL" in
          0)
            echo "${PROGRAM_NAME} is running with pid $PID"
            ;;
          1)
            echo "${PROGRAM_NAME} is stopped"
            ;;
          *)
            echo "${PROGRAM_NAME} is dead but ${YAF_PIDFILE} exists"
            ;;
        esac
    fi
    return $RETVAL
}

dumpstats() {
    PID=`getPid`
    if [ "X${PID}" = "X" ] ; then
        echo "${PROGRAM_NAME} not running"
        return 1
    fi
    /bin/kill -s USR1 $PID
    RETVAL=$?
    if [ "$RETVAL" -eq 0 ]; then
        echo "Dumped ${PROGRAM_NAME} statistics to ${YAF_LOG}"
    else
        echo "Could not dump ${PROGRAM_NAME} statistics"
    fi
    return $RETVAL
}

# See how we were called.
case "$1" in
    start)
        start
        RETVAL=$?
        ;;
    stop)
        stop
        RETVAL=$?
        ;;
    restart)
        restart
        RETVAL=$?
        ;;
    status)
        status
        RETVAL=$?
        ;;
    dumpstats)
        dumpstats
        RETVAL=$?
        ;;
    *)
        echo "Usage: $0 {start|stop|restart|status|dumpstats}"
        RETVAL=1
        ;;
esac

exit $RETVAL


##  ----------------------------------------------------------------------
##  @DISTRIBUTION_STATEMENT_BEGIN@
##  YAF 2.18
##
##  Copyright 2025 Carnegie Mellon University.
##
##  NO WARRANTY. THIS CARNEGIE MELLON UNIVERSITY AND SOFTWARE ENGINEERING
##  INSTITUTE MATERIAL IS FURNISHED ON AN "AS-IS" BASIS. CARNEGIE MELLON
##  UNIVERSITY MAKES NO WARRANTIES OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
##  AS TO ANY MATTER INCLUDING, BUT NOT LIMITED TO, WARRANTY OF FITNESS FOR
##  PURPOSE OR MERCHANTABILITY, EXCLUSIVITY, OR RESULTS OBTAINED FROM USE OF
##  THE MATERIAL. CARNEGIE MELLON UNIVERSITY DOES NOT MAKE ANY WARRANTY OF
##  ANY KIND WITH RESPECT TO FREEDOM FROM PATENT, TRADEMARK, OR COPYRIGHT
##  INFRINGEMENT.
##
##  Licensed under a GNU GPL 2.0-style license, please see LICENSE.txt or
##  contact permission@sei.cmu.edu for full terms.
##
##  [DISTRIBUTION STATEMENT A] This material has been approved for public
##  release and unlimited distribution.  Please see Copyright notice for
##  non-US Government use and distribution.
##
##  This Software includes and/or makes use of Third-Party Software each
##  subject to its own license.
##
##  DM25-1281
##  @DISTRIBUTION_STATEMENT_END@
##  ----------------------------------------------------------------------
