#!/bin/bash
#
# For RedHat-style chkconfig
#
# ds_agent    Trend Micro Deep Security Agent
#
# chkconfig:   2345 13 87
# description: Trend Micro Deep Security Agent
# processname: ds_agent
# config:      /var/opt/ds_agent/core/ds_agent.config
# config:      /var/opt/ds_agent/core/ds_agent.crt
# config:      /var/opt/ds_agent/core/ds_agent_dsm.crt
# pidfile:     /var/run/ds_agent.pid
#
#
# For SuSE-style chkconfig/insserv
#
### BEGIN INIT INFO
# Provides:          ds_agent
# Required-Start:    $network $syslog
# Required-Stop:     $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Trend Micro Deep Security Agent
# Description:       Trend Micro Deep Security Agent
#	The ds_agent program communicates with the Trend Micro
#	Deep Security Manager and controls the local firewall
#	and content filtering rules.
### END INIT INFO

exec="/opt/ds_agent/ds_agent"
prog=`basename $exec`
opts='-w /var/opt/ds_agent -b -i -e /opt/ds_agent/ext'
lockfile=/var/lock/subsys/$prog
lockdir=$(dirname "$lockfile")
pidfile=/var/run/ds_agent.pid
servicefile=/usr/lib/systemd/system/$prog.service

LD_LIBRARY_PATH="/opt/ds_agent/lib"
LOG_TAG=`basename $0`

# Error code
SUCCESS=0
ERROR_INVALID_ARGUMENT=1
ERROR_NO_NETWORK=2

# jemalloc will break OOM of Linux agent, use it for agent-less only
test -f "/etc/dsva-bld" && test -f "/opt/ds_agent/lib/libjemalloc.so" && export LD_PRELOAD="/opt/ds_agent/lib/libjemalloc.so"
test -e /etc/sysconfig/$prog && . /etc/sysconfig/$prog
test -e /etc/default/$prog && . /etc/default/$prog

# Source function library - RedHat only
if [ -r /etc/init.d/functions ]; then
    # this is redhat
    . /etc/init.d/functions
    os_daemon_start="daemon /usr/bin/sg tm_dsa -c \"$exec $opts\""
    os_daemon_stop=killproc
    rc_status() {
        local ret=$?
        echo
        return ${ret}
    }
elif [ -r /etc/rc.status ]; then
    # this is SuSE
    . /etc/rc.status
    os_daemon_start=do_start_daemon
    os_daemon_stop=do_stop_daemon
    do_start_daemon() {
        if type start_daemon >/dev/null 2>&1; then
            start_daemon -g tm_dsa $@
        else
            # Oops, no start_daemon? Run command directly
            $@
        fi
    }
    do_stop_daemon() {
        if type killproc >/dev/null 2>&1; then
            killproc -t60 $@
        else
            local pid=$(cat /var/run/$1.pid 2>/dev/null)
            local app=$(readlink /proc/$pid/exe 2>/dev/null)
            pkill -x $1
            [ -z "$pid" ] && return 0
            local x
            for ((x=0;x<600;x++)) ; do
                local running=$(readlink /proc/$pid/exe 2>/dev/null)
                test "$running" != "$app" && return 0
                sleep 0.1
                test $(($x % 10)) -eq 0 && echo -n .
                test $x -eq 300 && kill -9 $pid
            done
            return 1
        fi
    }
    status() {
        if [ -f "$servicefile" ]; then
            /usr/bin/systemctl status $prog.service
        else
            checkproc $1
            rc_status -v
        fi
    }
elif [ -r /lib/lsb/init-functions ] ;then
    # this is debian
    . /lib/lsb/init-functions
    os_daemon_start=start_daemon
    os_daemon_stop=stop_daemon
    start_daemon() {
        local exec=$1 ; shift
        local opts=$@
        /sbin/start-stop-daemon --start --quiet --pidfile $pidfile -g tm_dsa \
        --exec $exec -- $opts
    }
    stop_daemon() {
        local pid=$(cat $pidfile 2>/dev/null)
        /sbin/start-stop-daemon --stop --quiet --pidfile $pidfile \
        --oknodo --exec $exec
        local x
        [ -z "$pid" ] && return 0
        for ((x=0;x<100;x++)) ; do
            local running="$(readlink /proc/$pid/exe 2>/dev/null)"
            test "$running" != "$exec" && return 0
            sleep 0.1
            test $(($x % 10)) -eq 0 && echo -n .
            test $x -eq 50 && kill -9 $pid
        done
        return 1
    }
    action() {
        echo -n $1
        shift
        $*
        rc_status -v
    }
    rc_status() {
        local ret=$?
        if [ "$ret" = 0 ]; then 
            echo [OK] 
        else
            echo [ERROR]
        fi
        return ${ret}
    }
    status() {
        local pid=$(cat $pidfile 2>/dev/null)
        status_of_proc -p $pidfile $1 "ds_agent (pid $pid) process" && exit 0 || exit $?
    }
elif [ -r /opt/ds_agent/dsa-initfunctions.sh ]; then
    # this is distro that using the functions library in the DSA binary directory
    . /opt/ds_agent/dsa-initfunctions.sh

    # this is definition for RHEL
    os_daemon_start="daemon /usr/bin/sg tm_dsa -c \"$exec $opts\""
    os_daemon_stop=killproc
    rc_status() {
        local ret=$?
        echo
        return ${ret}
    }
else
    echo "Unsupported distribution"
    exit $ERROR_INVALID_ARGUMENT
fi

# Source networking configuration
if [ -f /etc/sysconfig/network ]; then
    # Check that networking is up.
    if grep -q "^NETWORKING=no" /etc/sysconfig/network; then
        echo "Networking is down. Exiting script."
        logger -t $LOG_TAG "Networking is down. Exiting script."
        exit $ERROR_NO_NETWORK
    fi
fi

clearArpCache() {
    local ip
    local flags
    cat /proc/net/arp | grep -v ^IP | while read ip type flags hw mask device ; do
        if [ "$(( $flags & 0x4))" = 0 ]; then
            /sbin/arp -d "${ip}" >/dev/null 2>&1
        fi
    done
}

start() {
    local prog=$(basename $exec)
    sp_pre_start

    if [ -f $pidfile ]; then
        pidFromFile=$(cat $pidfile)
        for pid in $(pgrep -x ${prog}); do
            if [ "${pid}" -eq "${pidFromFile}" ]; then
                echo -n $"$prog already running"
                rc_status -v
                return 0
            fi
        done
        # If we get down here, the pid from the file didn't match a running process.
        rm -f $pidfile
    fi

    if [ ! $(/usr/bin/getent group tm_dsa) ]; then
        /usr/sbin/groupadd -f tm_dsa
    fi

    echo -n $"Starting $prog: "
    if [ -f /etc/dsva-bld ]; then
        echo -n " DSVA "
        (cd `dirname $exec` && ulimit -S -c 0 && ulimit -n 8192 >/dev/null 2>&1 && $os_daemon_start $exec $opts)
        if [ -d /opt/ds_agent/dsva/modules/`uname -r` ]; then
            # in case of vmtoolsd service and necessary ko not ready, we try recovery here
            MOD_DIR="/lib/modules/`uname -r`/misc"
            declare -a vmware_mods=("vmci.ko" "vsock.ko" "vmxnet.ko")
            for mod in "${vmware_mods[@]}"; do
                if [ ! -e $MOD_DIR/$mod ]; then
                    logger -t $LOG_TAG "warning: $MOD_DIR/$mod not exist, recoverying..."
                    mkdir -p $MOD_DIR
                    cp -p /opt/ds_agent/dsva/modules/`uname -r`/misc/$mod $MOD_DIR
                fi
            done
        fi
        if ! pgrep -x "vmtoolsd"; then
            logger -t $LOG_TAG "warning: vmware-tools service not running, recoverying..."
            rm -f /var/lock/subsys/vmware-tools > /dev/null 2>&1
            /etc/vmware-tools/services.sh start
        fi
    else
        (cd `dirname $exec` && ulimit -S -c 0 >/dev/null 2>&1 && $os_daemon_start $exec $opts)
    fi

    # Prevent dsva vmware tool upgrade unintentionally
    if [ -f /etc/dsva-bld ]; then
        if [ -f /usr/bin/vmware-config-tools.pl ]; then
            chmod 444 /usr/bin/vmware-config-tools.pl >/dev/null 2>&1
        fi  
        if [ -f /usr/bin/vmware-config-tools.pl ]; then
            chmod 444 /usr/bin/vmware-uninstall-tools.pl >/dev/null 2>&1
        fi  
    fi 

    rc_status -v
    retval=$?
    [ ! -d "$lockdir" ] && mkdir -p "$lockdir"
    if [ $retval -eq 0 ] ; then
        touch $lockfile
        # Clear ARP cache during agent installation. This can be avoided by creating /tmp/ignore_clearing_arp_cache
        if [ ! -f /tmp/ignore_clearing_arp_cache ]; then
            clearArpCache
        fi
    fi
    return $retval
}

stop() {
    sp_pre_stop

    quiet=${1:-"0"}
    rm -f $lockfile
    [ "${quiet}" -ne 0  -a  ! -f "$pidfile" ] && return 0
    echo -n $"Stopping $prog: "
    $os_daemon_stop $prog
    rc_status -v
    retval=$?
    rm -f $pidfile

    local x agentPids
    for ((x=0;x<10;x++)) ; do
        agentPids=`pgrep -x -f "$exec $opts"`
        if [ $? -ne 0 ]; then
            break
        fi
        sleep 1
    done
    if [ -n "$agentPids" ]; then
        echo "Stop remaining processes"
        kill -s KILL $agentPids
    fi

    # network filter driver
    [ -x /opt/ds_agent/Linux.init ] && (lsmod | grep -m 1 dsa_ > /dev/null) && /opt/ds_agent/Linux.init stop
    
    # dsva dvfilter
    (initctl status ds_filter 2> /dev/null | grep start > /dev/null) && initctl stop ds_filter
    (systemctl status ds_dvfilter.service 2> /dev/null | head -n 10 | grep \(running\) > /dev/null) && systemctl stop ds_dvfilter.service

    return $retval
}

reset() {
    /opt/ds_agent/dsa_control -r
}

restart() {
    stop
    start
}

reload() {
    restart
}

fdrstatus() {
    status $exec
}

sp_pre_stop() {
    local SP_DUMP_PATH="/proc/driver/bmhook/self_protection/dump"
    if [ -f "$SP_DUMP_PATH" ]; then
        val=`grep Enable "$SP_DUMP_PATH" | awk '{print $2}'`

        if [ "$val" == "1" ]; then
            rm $pidfile > /dev/null 2>&1
            echo "Please stop self-protection before start/stop ds_agent service"
            exit $SUCCESS
        fi
    fi
}

sp_pre_start() {
    if [ -f $pidfile ]; then
        return
    fi

    ds_pid=`pgrep -o -P 1 -f "/opt/ds_agent/ds_agent\s"`
    if [ "$ds_pid" != "" ]; then
        echo "$ds_pid" > $pidfile
    fi
}

case "$1" in
    start|stop|reset|restart|reload)
        $1
        ;;
    stopquiet)
        stop 1
        ;;
    status)
        fdrstatus
        ;;
    condrestart)
        [ -f $lockfile ] && restart || :
        ;;
    *)
        echo $"Usage: $0 {start|stop|status|reset|restart|condrestart|reload}"
        exit $ERROR_INVALID_ARGUMENT
esac
