#!/bin/bash
##
## This is the fwcontrol script.  It executes the proper sequences for stopping
## And Starting the FileWave components for version 3.0.2, with 10.5 compatibility.
##
## Created by Steve Lind 10/29/2007
##
## First, get the version of OSX. Returns the major version, e.g. "4" or "5"

FW_ROOT="/usr/local/filewave"
PYTHON="$FW_ROOT/python/bin/python"
MANAGE="$FW_ROOT/django/manage.pyc"
USER_SESSIONS=$(ps auwwx |grep [l]oginwindow|grep -v root|awk '{ print $1","$2 }')
PGBOUNCER_PID=/usr/local/filewave/log/pgbouncer.pid

# Check that $LANG is set. Django 1.4 will complain if it is not the case.
if [ -z $LANG ]; then
        if [ `whoami` == "root" ]; then
		export LANG="`sudo -u $USER defaults read NSGlobalDomain AppleLocale 2>/dev/null  || echo en_US | cut -d@ -f1`.UTF-8"
        else
		export LANG="`(defaults read NSGlobalDomain AppleLocale 2>/dev/null  || echo en_US) | cut -d@ -f1`.UTF-8"
	fi
	if [ -z `locale -ck LC_CTYPE | grep "UTF-8"` ]; then
		export LANG='en_US.UTF-8'
	fi
	echo "No LANG set. Using '$LANG' as the default." >&2
fi

###############
###Functions###
###############
function execForAllUsers
{
    local osMajorVersion=$(uname -r |cut -d '.' -f 1)
	for user_pid in $USER_SESSIONS; do
		current_user=$(echo $user_pid |cut -d ',' -f 1)
		loginwindow_pid=$(echo $user_pid |cut -d ',' -f 2)
		# Change to launchctl call from El Capitan onwards.
		# Note that quoting differs too.
		if [ "$osMajorVersion" -ge 15 ]
		then /bin/launchctl asuser $(id -u "$current_user") $*
		else /bin/launchctl bsexec "$loginwindow_pid" sudo -u "$current_user" $*
		fi
	done
}


function syntaxErr
{
	/bin/echo ""
	/bin/echo "|----------------------|"
	/bin/echo "| Welcome to fwcontrol |"
	/bin/echo "|----------------------|"
	/bin/echo ""
	/bin/echo "Usage: (sudo) fwcontrol <component> <command>"
	/bin/echo ""
	/bin/echo "Examples:"
	/bin/echo ""
	/bin/echo "sudo fwcontrol server stop"
	/bin/echo "sudo fwcontrol client start"
	/bin/echo "sudo fwcontrol booster restart"
	/bin/echo "sudo fwcontrol booster connectedclients"
	/bin/echo "sudo fwcontrol client version"
	/bin/echo "fwcontrol fwgui showKiosk"
    /bin/echo "fwcontrol dashboard start"
	/bin/echo ""
	/bin/echo "MDM:"
	/bin/echo ""
	/bin/echo "sudo fwcontrol mdm adduser [new_user_name]"
	/bin/echo "sudo fwcontrol mdm adddeuser [new_user_name] [realm (default is \"Enroll IOS Device\")]"
	/bin/echo "sudo fwcontrol mdm adddepuser [new_user_name] [realm (default is \"Enroll IOS Device\")]"
	/bin/echo "sudo fwcontrol mdm addadminuser"
	/bin/echo ""
	/bin/echo "Additional Commands:"
	/bin/echo ""
	/bin/echo "sudo fwcontrol server dbcompact"
	/bin/echo "sudo fwcontrol server restore [version]"
	/bin/echo "sudo fwcontrol server resetinventory"
	/bin/echo "sudo fwcontrol server generateSelfSignedCert --create --cn=fqdn [--country COUNTRY] [--state STATE] [--locality LOCALITY] [--organization ORGANIZATION] [--ou ORGANIZATIONAL_UNIT] [--email EMAIL] [--ignore_name_mismatch]"
    /bin/echo "sudo fwcontrol server generateSelfSignedCert --show-pending"
	/bin/echo "sudo fwcontrol server generateSelfSignedCert --install"
	/bin/echo "sudo fwcontrol client status"
	/bin/echo "sudo fwcontrol apache start|stop|restart"
	/bin/echo "sudo fwcontrol postgres start|stop|restart"
	/bin/echo "sudo fwcontrol redis start|stop|restart"
	/bin/echo "sudo fwcontrol nats start|stop|restart"
	/bin/echo "sudo fwcontrol scheduler start|stop|restart"
	/bin/echo "sudo fwcontrol client_monitor start|stop|restart"
	if [ "$($FW_ROOT/python/bin/python $FW_ROOT/django/check_model_update_version.pyc)" = 2 ]; then
		/bin/echo "sudo fwcontrol model_update_service start|stop|restart"
	fi
	/bin/echo ""
}

function launchOrStart()
{
	LABEL="$1"
	PLIST="$2"
	# if the LABEL item is already loaded in launchctl, then attempt to simply start it
	launchctl list | grep -F "$LABEL" >/dev/null && RES=0 || RES=1
	if [ "$RES" -eq 1 ]; then
		# grep didnt find the $LABEL in this list, to try to load this into launchd
		launchctl load "$PLIST"
	else
		# grep found it, so simply use the start command
		launchctl start "$LABEL"
	fi
}

function valid_ip()
{
    local  ip=$1
    local  stat=1

    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        OIFS=$IFS
        IFS='.'
        ip=($ip)
        IFS=$OIFS
        [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
            && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
        stat=$?
    fi
    return $stat
}

##Server

#
# the following functions 'echo' their output, rather than returning it - since
# PID values can be greater than 128 (the limit of values sent via 'return')
#

function serverIsRunning
{
	echo `ps -ax | /usr/bin/grep -i /usr/local/sbin/fwxserver | egrep -v 'grep' | wc -l`
}

function serverLdapIsRunning
{
	echo `ps -ax | /usr/bin/grep -i /usr/local/sbin/fwldap | egrep -v 'grep' | wc -l`
}

function redisIsRunning
{
	echo `ps -ax | /usr/bin/grep -i /usr/local/sbin/redis-server | egrep -v 'grep' | wc -l`
}

function serverApacheIsRunning
{
	echo `ps -ax | /usr/bin/grep $FW_ROOT/apache/bin/httpd | egrep -v 'grep' | wc -l`
}

function clientMonitorIsRunning
{
	echo `ps -ax | /usr/bin/grep -i "$FW_ROOT/python/bin/python /usr/local/filewave/django/manage.pyc run_monitor" | egrep -v 'grep' | wc -l`
}

function modelUpdateServiceIsRunning
{
	echo `ps -ax | /usr/bin/grep -i "$FW_ROOT/python/bin/python /usr/local/filewave/django/manage.pyc run_model_update_service" | egrep -v 'grep' | wc -l`
}

function schedulerIsRunning
{
	echo `ps -ax | /usr/bin/grep -i "$FW_ROOT/python/bin/python /usr/local/filewave/django/manage.pyc run_huey" | egrep -v 'grep' | wc -l`
}

function grafanaIsRunning
{
	echo `ps -ax | /usr/bin/grep -i "/usr/local/sbin/grafana-server" | egrep -v 'grep' | wc -l`
}

function natsIsRunning
{
	echo `ps -ax | /usr/bin/egrep -i "/usr/local/sbin/nats-server|/usr/local/filewave/nats/nats-server" | egrep -v 'grep' | wc -l`
}

function dashboardIsRunning
{
    echo `ps -ax | /usr/bin/grep -i "/usr/local/sbin/grafana-server\|/usr/local/sbin/prometheus\|/usr/local/sbin/node_exporter\|/usr/local/sbin/postgres_exporter\|/usr/local/sbin/alertmanager\|/usr/local/sbin/apache_exporter\|/usr/local/sbin/redis_exporter\|/usr/local/sbin/prometheus-nats-exporter" | egrep -v 'grep' | wc -l`
}

function prometheusIsRunning
{
	echo `ps -ax | /usr/bin/grep -i "/usr/local/sbin/prometheus" | egrep -v 'grep' | wc -l`
}

function getBoosterPid
{
	fpid=`ps -ax | grep /usr/local/sbin/fwbooster | /usr/bin/grep -v grep | awk '{print $1}'`
	if [ -z "$fpid" ]; then
		echo "0"
	fi
	echo "$fpid"
}

function boosterIsRunning
{
	 booster_Pid=`ps -ax | grep /usr/local/sbin/fwbooster | /usr/bin/grep -v grep | awk '{print $1}'`
        if [ -z "$booster_Pid" ]; then
                echo "not running"
        else
        	echo "running"
        fi

}

function getConnectedClients
{
	netstat -an | grep 20014
}

function getClientPid
{
    	fpid=`ps -ax | /usr/bin/grep /MacOS/fwcld | /usr/bin/grep -v grep | awk '{print $1}'`

	if [ -z "$fpid" ]; then
		echo "0"
	fi

	echo "$fpid"
}

function numfwGUIProcesses
{
	# the form: -o pid,comm is a Tiger only thing, command work on 10.3.9 as well
	NUM=`ps -ax -o pid,command | /usr/bin/grep -v grep | /usr/bin/grep -i 'MacOS/fwGUI' | wc -l`
	echo $NUM
}


function createPostgresUserAccount()
{
    FOUND=`/usr/bin/dscl . -search /Users RecordName postgres`
 	if [ "$FOUND" ]; then
 		return
 	fi
 	printf "Creating new user account postgres as alias to _postgres\n"
    /usr/bin/dscl . -merge Users/_postgres RecordName postgres
}

function startPostgres
{
	if [ ! -e "/usr/local/filewave/fwxserver/DB/pg_data/postmaster.pid" ]; then
	    createPostgresUserAccount
		printf "Starting PostgreSQL database manager"
		launchOrStart com.filewave.postgresql /Library/LaunchDaemons/com.filewave.postgresql.plist
	else
		printf "Postgres already started."
	fi
	for i in {1..30}; do
		$FW_ROOT/postgresql/bin/pg_isready -d mdm -U django -q && break
		printf "."
		sleep .5
	done
	echo ""
	if [ ! -e "/usr/local/filewave/fwxserver/DB/pg_data/postmaster.pid" ]; then
	    echo "Could not start postgres." && exit 3
    fi

    if [[ -e $PGBOUNCER_PID ]] && kill -0 $(cat $PGBOUNCER_PID) 2> /dev/null; then
	echo "pgBouncer is already running."
    else
        Echo "Starting pgBouncer"
        launchOrStart com.filewave.dbpooler /Library/LaunchDaemons/com.filewave.dbpooler.plist
	for i in {1..30}; do
		PGPORT=9433 $FW_ROOT/postgresql/bin/pg_isready -d mdm -U django -q && break
		printf "."
		sleep .5
	done
    fi
}

function stopPostgres
{
    if [ ! -z $1 ]; then
        PGDIR="$1/bin"
    else
        PGDIR="/usr/local/filewave/postgresql/bin"
    fi

	if [ -e "/usr/local/filewave/fwxserver/DB/pg_data/postmaster.pid" ]; then
	    printf "Stopping PostgreSQL database manager"
	    # first we kill all client connections that are left
        # wait maximum 1 min for all queries to stop
        for try in {1..60}; do
            success=$(/usr/local/filewave/postgresql/bin/psql -U postgres -d mdm -qtAX -c "SELECT DISTINCT pg_cancel_backend(pid) from pg_stat_activity where pid <> pg_backend_pid() and usename is not NULL;" 2> /dev/null) || true
            # either if no connection is open, or all closed successfully
            if [[ $success == t ]] || [[ -z $success ]]; then break; fi
            printf "."
            sleep .5
        done
        echo ""
        while true; do
		        /bin/launchctl unload /Library/LaunchDaemons/com.filewave.postgresql.plist &> /dev/null
		        sudo -u postgres $PGDIR/pg_ctl stop -w -D /usr/local/filewave/fwxserver/DB/pg_data -m fast 2> /dev/null || true
                # check if postmaster.pid file exists and its process is actually running
                if [[ -e "/usr/local/filewave/fwxserver/DB/pg_data/postmaster.pid" ]] && [[ ! -z $(ps -p `head -1 /usr/local/filewave/fwxserver/DB/pg_data/postmaster.pid` | grep postmaster) ]]; then
                    printf "."
		            sleep .5
                else
                    # it is safe to remove postmaster.pid cause the process doesn't exist anyway
                    rm -f /usr/local/filewave/fwxserver/DB/pg_data/postmaster.pid
                    break
                fi
        done
        echo ""
	else
	    /bin/echo "No PostgreSQL found running"
	fi

    if [[ -e $PGBOUNCER_PID ]] && kill -0 $(cat $PGBOUNCER_PID) 2> /dev/null; then
        echo "Stopping pgBouncer"
        while [[ -e "$PGBOUNCER_PID" ]]; do
	    /bin/launchctl unload /Library/LaunchDaemons/com.filewave.dbpooler.plist &> /dev/null || true
            # check if postmaster.pid file exists and its process is actually running
            if [[ -e $PGBOUNCER_PID ]] && kill -0 $(cat $PGBOUNCER_PID) 2> /dev/null; then
                printf "."
                sleep .5
                kill `cat $PGBOUNCER_PID` 2> /dev/null || true
            else
                # it is safe to remove postmaster.pid cause the process doesn't exist anyway
                rm -f $PGBOUNCER_PID
            fi
        done
    else
        Echo "pgBouncer is already stopped"
    fi
}

function restartPostgres
{
	stopPostgres
	startPostgres
}

startFwxserver()
{
	WASRUNNING=1
	NUMPROCS=`serverIsRunning`
	if [ $NUMPROCS -eq 0 ]; then
		/bin/echo "Starting Server Daemon"
		WASRUNNING=0
		launchOrStart com.filewave.fwxserver-admin /Library/LaunchDaemons/com.filewave.fwxserver-admin.plist
		launchOrStart com.filewave.fwxserver-server /Library/LaunchDaemons/com.filewave.fwxserver-server.plist
	fi
	NUMPROCS=`serverLdapIsRunning`
	if [ $NUMPROCS -eq 0 ]; then
		WASRUNNING=0
		/bin/echo "Starting LDAP Extraction Daemon"
		launchOrStart com.filewave.fwxserver-ldap /Library/LaunchDaemons/com.filewave.fwxserver-ldap.plist
	fi
	return $WASRUNNING
}

startMDM()
{
	WASRUNNING=1
	startClientMonitor
	NUMPROCS=`serverApacheIsRunning`
	if [ $NUMPROCS -eq 0  -a -f /usr/local/filewave/apache/bin/httpd ]; then
		WASRUNNING=0
		printf "Starting Web Service"
		launchOrStart com.filewave.fwxserver-apache /Library/LaunchDaemons/com.filewave.fwxserver-apache.plist
		for i in {1..30}; do
			if [ ! -e "/usr/local/filewave/apache/logs/httpd.pid" ]; then
				printf "."
				sleep .5
			else
				break
			fi
		done
		printf "\n"
		if [ ! -e "/usr/local/filewave/apache/logs/httpd.pid" ]; then
			launchOrStart com.filewave.fwxserver-apache /Library/LaunchDaemons/com.filewave.fwxserver-apache.plist
		fi
	fi
	return $WASRUNNING
}

function startClientMonitor()
{
	NUMPROCS=`clientMonitorIsRunning`
	if [ $NUMPROCS -eq 0 ]; then
		/bin/echo "Starting Client Monitor Daemon"
		WASRUNNING=0
		launchOrStart com.filewave.client-monitor-v2 /Library/LaunchDaemons/com.filewave.client-monitor-v2.plist
	fi
}

function stopClientMonitor()
{
	NUMPROCS=`clientMonitorIsRunning`
	if [ $NUMPROCS -ne 0 ]; then
		/bin/echo "Stopping Client Monitor Daemon"
		/bin/launchctl unload /Library/LaunchDaemons/com.filewave.client-monitor-v2.plist &>/dev/null
		RUNNING=1
	fi
}

function restartClientMonitor()
{
	stopClientMonitor
	startClientMonitor
}

function startModelUpdateService()
{
if [ "$($FW_ROOT/python/bin/python $FW_ROOT/django/check_model_update_version.pyc)" = 2 ]; then
	WASRUNNING=1
	NUMPROCS=`modelUpdateServiceIsRunning`
	if [ $NUMPROCS -eq 0 ]; then
		/bin/echo "Starting Model Update Daemon"
		WASRUNNING=0
		launchOrStart com.filewave.model-update-service /Library/LaunchDaemons/com.filewave.model-update-service.plist
	else
    	printf "Model Update daemon already running.\n"
  	fi

	return $WASRUNNING
fi
}

function stopModelUpdateService()
{
	WASRUNNING=0
	NUMPROCS=`modelUpdateServiceIsRunning`
	if [ $NUMPROCS -ne 0 ]; then
		/bin/echo "Stopping Model Update Daemon"
		/bin/launchctl unload /Library/LaunchDaemons/com.filewave.model-update-service.plist &>/dev/null
		WASRUNNING=1
	elif [ "$($FW_ROOT/python/bin/python $FW_ROOT/django/check_model_update_version.pyc)" = 2 ]; then
		/bin/echo "Model Update daemon already stopped."
	fi

	return $WASRUNNING
}

function restartModelUpdateService()
{
	stopModelUpdateService
	startModelUpdateService
}

function startNats()
{
    NUMPROCS=`natsIsRunning`
    if [ $NUMPROCS -eq 0 ]; then
        /bin/echo "Starting NATS Daemon"
        WASRUNNING=0
        if [ -e /Library/LaunchDaemons/com.filewave.nats-server.plist ]; then
            launchOrStart com.filewave.nats-server /Library/LaunchDaemons/com.filewave.nats-server.plist
        fi
        if [ -e /Library/LaunchDaemons/com.filewave.nats-server-jwt.plist ]; then
            launchOrStart com.filewave.nats-server-jwt /Library/LaunchDaemons/com.filewave.nats-server-jwt.plist
        fi
        if [ -e /Library/LaunchDaemons/com.filewave.nats-booster.plist ]; then
            launchOrStart com.filewave.nats-booster /Library/LaunchDaemons/com.filewave.nats-booster.plist
        fi
    fi
}

function stopNats()
{
    NUMPROCS=`natsIsRunning`
    if [ $NUMPROCS -ne 0 ]; then
        /bin/echo "Stopping NATS Daemon"
        if [ -e /Library/LaunchDaemons/com.filewave.nats-server-jwt.plist ]; then
            /bin/launchctl unload /Library/LaunchDaemons/com.filewave.nats-server-jwt.plist &>/dev/null
        fi
        if [ -e /Library/LaunchDaemons/com.filewave.nats-server.plist ]; then
            /bin/launchctl unload /Library/LaunchDaemons/com.filewave.nats-server.plist &>/dev/null
        fi
        if [ -e /Library/LaunchDaemons/com.filewave.nats-booster.plist ]; then
            /bin/launchctl unload /Library/LaunchDaemons/com.filewave.nats-booster.plist &>/dev/null
        fi
    fi
}

function restartNats()
{
	stopNats
	startNats
}


function startScheduler()
{
	WASRUNNING=1
	NUMPROCS=`schedulerIsRunning`
	if [ $NUMPROCS -eq 0 ]; then
		/bin/echo "Starting Scheduler Daemon"
		WASRUNNING=0
		launchOrStart com.filewave.scheduler-v2 /Library/LaunchDaemons/com.filewave.scheduler-v2.plist
	fi
	return $WASRUNNING
}

function stopScheduler()
{
	WASRUNNING=0
	NUMPROCS=`schedulerIsRunning`
	if [ $NUMPROCS -ne 0 ]; then
		/bin/echo "Stopping Scheduler Daemon"
		/bin/launchctl kill SIGINT system/com.filewave.scheduler-v2 &>/dev/null
		sleep 1
		# wait for the scheduler to stop gracefully
		for i in {1..20}; do
			NUMPROCS=`schedulerIsRunning`
			if [ $NUMPROCS -eq 0 ]; then
				break
			else
				printf "."
				sleep 1
			fi
		done
		printf "\n"
		/bin/launchctl unload /Library/LaunchDaemons/com.filewave.scheduler-v2.plist &>/dev/null
		WASRUNNING=1
	fi
	return $WASRUNNING
}

function restartScheduler()
{
	stopScheduler
	startScheduler
}

function startServer
{
	if [ $EUID -ne 0 ]; then
		echo "Superuser credentials needed. Try with sudo. FileWave Server not started."
		return
	fi
	/bin/echo "Starting the FileWave Server..."
	WASRUNNING1=0
	WASRUNNING2=0

	startNats
	startRedis
	startPostgres
	startMDM || WASRUNNING1=$?
	startFwxserver || WASRUNNING2=$?

	if [ $WASRUNNING1 -ne 0 -o $WASRUNNING2 -ne 0 ]; then
		/bin/echo "The Server is already running."
	fi

	startScheduler
	startModelUpdateService
	startPrometheus
	startDashboard
}

stopFwxserver()
{
	RUNNING=0
	NUMPROCS=`serverLdapIsRunning`
	if [ $NUMPROCS -ne 0 ]; then
		/bin/echo "Stopping Server LDAP Extraction Daemon"
		/bin/launchctl unload /Library/LaunchDaemons/com.filewave.fwxserver-ldap.plist &>/dev/null
		RUNNING=1
	fi
	NUMPROCS=`serverIsRunning`
	if [ $NUMPROCS -ne 0 ]; then
		/bin/echo "Stopping Server Daemon"
		/bin/launchctl unload /Library/LaunchDaemons/com.filewave.fwxserver-server.plist &>/dev/null
		/bin/launchctl unload /Library/LaunchDaemons/com.filewave.fwxserver-admin.plist &>/dev/null
		RUNNING=1
	fi
	return $RUNNING
}

function startRedis()
{
    if [ $(redisIsRunning) -eq 0 ]; then
        printf "Starting Redis Daemon"
        launchOrStart com.filewave.redis /Library/LaunchDaemons/com.filewave.redis.plist
        for i in {1..30}; do
            if [ $(redisIsRunning) -eq 0 ]; then
                printf "."
                sleep .5
            else
                break
            fi
        done
        printf "\n"
        if [ $(redisIsRunning) -eq 0 ]; then
            printf "Unable to start Redis in a timely fashion.\n"
        fi
    else
        printf "Redis daemon already running.\n"
    fi
}

function stopRedis()
{
    if [ $(redisIsRunning) -ne 0 ]; then
        printf "Stopping Redis Daemon"
        unloadService /Library/LaunchDaemons/com.filewave.redis.plist
        for i in {1..20}; do
            if [ $(redisIsRunning) -ne 0 ]; then
                printf "."
                sleep .5
            else
                break
            fi
        done

        # kill redis since it is still running
        for pid in $(ps -ax | /usr/bin/grep -i /usr/local/sbin/redis-server | egrep -v 'grep|sudo' | awk '{print $1}'); do
        	kill -9 $pid || true
        done
        printf "\n"
    else
        printf "Redis daemon already stopped.\n"
    fi
}

function startPrometheus()
{
	if [ $(prometheusIsRunning) -eq 0 ]; then
		printf "Starting Prometheus Daemon"
		launchOrStart com.filewave.prometheus /Library/LaunchDaemons/com.filewave.prometheus.plist
		launchOrStart com.filewave.prometheus-alertmanager /Library/LaunchDaemons/com.filewave.prometheus-alertmanager.plist
		for i in {1..10}; do
			if [ $(prometheusIsRunning) -eq 0 ]; then
				printf "."
				sleep .5
			else
				break
			fi
		done
		printf "\n"
		if [ $(prometheusIsRunning) -eq 0 ]; then
			printf "Unable to start Prometheus in a timely fashion.\n"
		fi
	else
		printf "Prometheus daemon already running.\n"
	fi
}

function stopPrometheus()
{
	if [ $(prometheusIsRunning) -ne 0 ]; then
        printf "Stopping Prometheus Daemon"
		unloadService /Library/LaunchDaemons/com.filewave.prometheus.plist
		unloadService /Library/LaunchDaemons/com.filewave.prometheus-alertmanager.plist
	fi
}

stopMDM()
{
	RUNNING=0
	stopClientMonitor
	NUMPROCS=`serverApacheIsRunning`
	if [ $NUMPROCS -ne 0 -a -f /usr/local/filewave/apache/bin/httpd ]; then
		/bin/echo -n "Stopping Server Web Service"
		if [ -x /usr/local/filewave/apache/bin/apachectl ]; then
			PID_HTTPD=$(<${FW_ROOT}/apache/logs/httpd.pid)
			/usr/local/filewave/apache/bin/apachectl stop &>/dev/null || true
			WAIT4FIN=20 # 8 seconds should be enough (20 * 0.4)
			while [ $WAIT4FIN -gt 0 ] && kill -0 ${PID_HTTPD} &>/dev/null; do printf '.'; sleep 0.4; let WAIT4FIN-=1; done
			if [ ${WAIT4FIN} -lt 1 ]; then /bin/echo ;/bin/echo -n "Could not stop Apache in a timely fashion."; fi
		fi
		/bin/echo
		/bin/launchctl unload /Library/LaunchDaemons/com.filewave.fwxserver-apache.plist &>/dev/null
		RUNNING=1
	fi
	return $RUNNING
}

function stopServer
{
	/bin/echo "Stopping the FileWave Server..."
	stopDashboard
	stopScheduler


    # clean up shared memory in case of fwxserver crashing or being SIGKILLed
    # (note: unix-based systems have the great feature that shm needs to be explicitly marked for removal;
    #  in case of crashed or SIGKILLed fwxservers we end up with dangling shm, which we take care of here;
    #  unluckily we can't mark the shm for removal at creation time in code (QSharedMemory::create) because the
    #  second fwxserver process trying to attach would create a new shm rather than attach to an existing one)
    fwxserverpids_space_delimited=$(ps ax | egrep 'fwxserver -(a|s)' | awk '{ print $1 }')
    if [ -n "$fwxserverpids_space_delimited" ]; then
        fwxserverpids_pipe_delimited=$(echo $fwxserverpids_space_delimited | tr ' ' '|')
        # note: `ipcs -mp' outputs: T ID KEY MODE OWNER GROUP CPID LPID
        # explanation: we're testing whether one of cpid or lpid matches the list of fwxserver pids, then forward the shmid to ipcrm
        ipcs -mp | awk -v regex=^\($fwxserverpids_pipe_delimited\)$ '$7 ~ regex || $8 ~ regex { print $2 }' | xargs -ISHMID ipcrm -m SHMID
    fi
    stopFwxserver && RUNNING1=0 || RUNNING1=$?
	if [ $RUNNING1 -eq 1 ]; then
		/bin/sleep 3
		/usr/bin/killall -TERM fwxserver >/dev/null 2>&1
		/usr/bin/killall -TERM fwldap >/dev/null 2>&1
	fi
	stopMDM && RUNNING2=0 || RUNNING2=$?

	if [ $RUNNING1 -eq 0 -a $RUNNING2 -eq 0 ]; then
		/bin/echo "The FileWave Server is not currently running."
	fi

	if [ -f /private/var/run/fwxserveradmin.pid ]; then
		rm /private/var/run/fwxserveradmin.pid
	fi
	if [ -f /private/var/run/fwxserver.pid ]; then
		rm /private/var/run/fwxserver.pid
	fi
	if [ -f /private/var/run/fwldap.pid ]; then
		rm /private/var/run/fwldap.pid
	fi

	stopModelUpdateService
	stopPostgres
	stopRedis
	stopNats
	stopPrometheus
}

function restartServer
{
	stopServer
    if [ "$(/usr/local/filewave/python/bin/python /usr/local/filewave/django/check_permission_fix_disabled.pyc)" = "False" ]; then
        echo -n "Setting permissions and ownership for FileWave Server"
        /usr/local/filewave/python/bin/python /usr/local/filewave/django/manage.pyc set_fwxserver_permissions_and_ownership
    fi
	startServer
}

function restartRedis
{
	stopRedis
	startRedis
}

function dbCompact
{
    export PGUSER=postgres
    export PGPASSWORD=postgres
    sudo -E -u postgres /usr/local/filewave/postgresql/bin/vacuumdb --all --full --analyze
}

function dbRestore
{
    /usr/local/sbin/fwxserver -restore $1
}

dbResetInventory()
{
    /usr/local/sbin/fwxserver -resetInventory
}

function dbMigrate
{
	stopServer
	startPostgres

	/bin/echo "Syncing the Django DB..."
	"$PYTHON" "$MANAGE" migrate --noinput
	/bin/echo "Migrating Server DB..."
	/usr/local/sbin/fwxserver -m

	startServer
}

function versionServer
{
	/usr/local/sbin/fwxserver -V
}



## Booster

startBooster()
{
	if [ $EUID -ne 0 ]; then
		echo "Superuser credentials needed. Try with sudo. FileWave Booster not started."
		return
	fi
	/bin/echo "Starting FileWave Booster..."

	if [ -e /private/var/FWBooster/Data\ Folder ]; then
		fpid=`getBoosterPid`
    	if [ $fpid -eq 0 ]; then
			launchOrStart com.filewave.fwbooster /Library/LaunchDaemons/com.filewave.fwbooster.plist
			launchOrStart com.filewave.pushprox-client /Library/LaunchDaemons/com.filewave.pushprox-client.plist
			startNats
			SERVERPROCS=`serverIsRunning`
			if [[ $SERVERPROCS -eq 0 ]]; then
				for i in {1..50}; do
					if [ -f "/Library/Preferences/com.filewave.Booster.plist" ]; then
						launchOrStart com.filewave.node-exporter /Library/LaunchDaemons/com.filewave.node-exporter.plist
					fi
				done
			fi
		else
    		/bin/echo "FileWave Booster is already running"
    	fi
	else
		/bin/echo "Error: No \"/private/var/FWBooster/Data Folder\". FileWave Booster did not start"
	fi
}

stopFwBooster()
{
	RUNNING=0
	fpid=`getBoosterPid`
	if [ $fpid -ne 0 ]; then
		/bin/launchctl unload /Library/LaunchDaemons/com.filewave.fwbooster.plist &>/dev/null
		/bin/launchctl unload /Library/LaunchDaemons/com.filewave.pushprox-client.plist &>/dev/null
		stopNats
		# if booster is running along with server, don't stop node-exporter
		SERVERPROCS=`serverIsRunning`
		if [[ $SERVERPROCS -eq 0 ]]; then
			/bin/launchctl unload /Library/LaunchDaemons/com.filewave.node-exporter.plist &>/dev/null
		fi
		RUNNING=1
	fi
	return $RUNNING
}

stopBooster()
{
	/bin/echo "Stopping FileWave Booster ..."

	stopFwBooster && RUNNING=0 || RUNNING=1
	if [ $RUNNING -eq 1 ]; then
		/bin/sleep 3
		/usr/bin/killall -TERM fwbooster >/dev/null 2>&1
	else
		/bin/echo "FileWave Booster is not running"
	fi
	if [ -f /private/var/run/fwbooster.pid ]; then
		rm /private/var/run/fwbooster.pid
	fi
}

function restartBooster()
{
	/bin/echo "Restarting FileWave Booster"
	stopBooster
	/bin/sleep 3
	startBooster
}

function versionBooster
{
	/usr/local/sbin/fwbooster -V
}

function boosterRunning
{
	boosterIsRunning
}

function connectedclients
{
	getConnectedClients
}

## fwgui
function restartFWGUI
{
	# KeepAlive is true, the process will be restarted automatically by the OS
	killall "FileWave Kiosk"
	#as fwGUI has KeepAlive set to true, we just need to kill the process and they will be restarted automatically
	killall fwGUI
	sleep 3
	if [ `numfwGUIProcesses` -eq 0 ]; then
		# it seems it as unable to start the fwGUI processes again, so let's restart them manually
		execForAllUsers /bin/launchctl unload -S Aqua /Library/LaunchAgents/com.filewave.fwGUI.plist
		execForAllUsers /bin/launchctl load -S Aqua /Library/LaunchAgents/com.filewave.fwGUI.plist
	fi
}

function showKiosk
{
	if [ $EUID -eq 0 ]; then
		/bin/echo "This command cannot be run with the root user; try again without sudo."
	elif [ `numfwGUIProcesses` -eq 0 ]; then
		/bin/echo "The FileWave GUI process is not running, cannot show the Kiosk."
	else
		/usr/local/sbin/FileWave.app/Contents/Resources/fwGUI.app/Contents/MacOS/fwGUI -showKiosk &
	fi
}

## Client

function startClient
{
	fpid=`getClientPid`

	if [ $fpid -eq 0 ]; then
	    /bin/echo "Starting FileWave Client"

		launchOrStart com.filewave.fwcld /Library/LaunchDaemons/com.filewave.fwcld.plist

    else
        /bin/echo "FileWave Client is already running"
    fi
}

function stopClient
{
	fpid=`getClientPid`

	if [ $fpid -ne 0 ]; then

		/bin/echo "Stopping FileWave Client"

		/bin/launchctl unload /Library/LaunchDaemons/com.filewave.fwcld.plist
		/bin/sleep 2

		/usr/bin/killall fwcld >/dev/null 2>&1
		fpid=`getClientPid`
		if [ $fpid -ne 0 ]; then
			/bin/sleep 3
			fpid=`getClientPid`
			if [ $fpid -ne 0 ]; then
				/bin/echo "The FileWave Client Daemon could not be shut down gracefully in a timely manner.  Aborting fwcld..."
				/bin/kill -9 "$fpid" >/dev/null 2>&1
			fi
		fi
	fi
}

function restartClient
{
	stopClient
	/bin/sleep 3
	startClient
}

function versionClient
{
	/usr/local/sbin/FileWave.app/Contents/MacOS/fwcld -V
}

function clientStatus
{
	/usr/local/sbin/FileWave.app/Contents/MacOS/fwcld -s
}



# MDM

function addMDMUser()
{
    if [ ! -e '/usr/local/filewave/apache/passwd/passwords' ]; then
        touch '/usr/local/filewave/apache/passwd/passwords'
    fi

	/usr/local/filewave/apache/bin/htpasswd /usr/local/filewave/apache/passwd/passwords $1
}

function addDEUser()
{
	[ -e '/usr/local/filewave/apache/passwd/passwords.digest' ] || PARAMS="-c"

	REALM=$2
	[ ! -z $REALM ] || REALM="Enroll IOS Device" #default value

	/usr/local/filewave/apache/bin/htdigest $PARAMS /usr/local/filewave/apache/passwd/passwords.digest "$REALM" $1
}

function addDjangoUser()
{
	"$PYTHON" "$MANAGE" createsuperuser
}

function restartMDMGracefully()
{
	/usr/local/filewave/apache/bin/apachectl graceful
	restartClientMonitor
}

function generateSelfSignedCert()
{
	$PYTHON $MANAGE create_mdm_self_signed_cert "$@"
	RESULT=$?
	if [[ $RESULT -eq 0 && $@ == *"--install"* ]];
	then
      echo "running restart apache command"
	  /usr/local/filewave/apache/bin/apachectl restart
	  restartClientMonitor
	fi
	# take care of permissions - certs and logs that may have been changed by sudo python
	chown _www:_www /usr/local/filewave/certs/* /usr/local/filewave/log/*
}

function startDashboard()
{
    WASRUNNING=1
    NUMPROCS=`dashboardIsRunning`
    /bin/echo "Starting FileWave Dashboard..."

		# we need to setup oauth configs before Grafana is up
		/usr/local/filewave/python/bin/python /usr/local/filewave/django/manage.pyc setup_grafana_and_prometheus --setup-grafana-oauth
    if [ $NUMPROCS -ne 4 ]; then
        launchOrStart com.filewave.node-exporter /Library/LaunchDaemons/com.filewave.node-exporter.plist
        launchOrStart com.filewave.nats-exporter /Library/LaunchDaemons/com.filewave.nats-exporter.plist
        launchOrStart com.filewave.nats-exporter-jwt /Library/LaunchDaemons/com.filewave.nats-exporter-jwt.plist
        launchOrStart com.filewave.postgres-exporter /Library/LaunchDaemons/com.filewave.postgres-exporter.plist
		launchOrStart com.filewave.pushprox-proxy /Library/LaunchDaemons/com.filewave.pushprox-proxy.plist
        launchOrStart com.filewave.apache-exporter /Library/LaunchDaemons/com.filewave.apache-exporter.plist
        launchOrStart com.filewave.redis-exporter /Library/LaunchDaemons/com.filewave.redis-exporter.plist
        launchOrStart com.filewave.mtail /Library/LaunchDaemons/com.filewave.mtail.plist
        launchOrStart com.filewave.grafana /Library/LaunchDaemons/com.filewave.grafana.plist
        WASRUNNING=0

		# Kick off a script that'll wait for grafana to start, and then update the
		# default admin users organization's dashboard to be 'FileWave'
		# It can take 'username' 'pwd' as args, but by default will use 'admin' 'admin' (the grafana default)
		/usr/local/filewave/python/bin/python /usr/local/filewave/django/manage.pyc setup_grafana_and_prometheus
    else
        /bin/echo "dashboard already running"
    fi
    return $WASRUNNING
}

function unloadService()
{
  PLIST=$1
  /bin/launchctl unload "$PLIST" &> /dev/null
  EXIT_CODE=$?
  if [ $EXIT_CODE -ne 0 ]; then
    echo "launchctl unload failed for: $PLIST, error code: $EXIT_CODE"
    /bin/launchctl error $EXIT_CODE
  fi
}

function stopDashboard()
{
    NUMPROCS=`dashboardIsRunning`
    /bin/echo "Stopping FileWave Dashboard..."
    if [ $NUMPROCS -ne 0 ]; then
        unloadService /Library/LaunchDaemons/com.filewave.grafana.plist
        unloadService /Library/LaunchDaemons/com.filewave.postgres-exporter.plist
		unloadService /Library/LaunchDaemons/com.filewave.pushprox-proxy.plist
        unloadService /Library/LaunchDaemons/com.filewave.apache-exporter.plist
        unloadService /Library/LaunchDaemons/com.filewave.redis-exporter.plist
        unloadService /Library/LaunchDaemons/com.filewave.mtail.plist
        unloadService /Library/LaunchDaemons/com.filewave.nats-exporter.plist
        unloadService /Library/LaunchDaemons/com.filewave.nats-exporter-jwt.plist
        unloadService /Library/LaunchDaemons/com.filewave.node-exporter.plist
    else
        /bin/echo "dashboard already stopped"
    fi
}

function restartDashboard()
{
    /bin/echo "Restarting FileWave Dashboard"
    stopDashboard
    startDashboard
}

function startGrafana()
{
	echo "running start grafana command"
	NUMPROCS=`grafanaIsRunning`
	if [ $NUMPROCS -eq 0 ]; then
		/usr/local/filewave/python/bin/python /usr/local/filewave/django/manage.pyc setup_grafana_and_prometheus --setup-grafana-oauth
		launchOrStart com.filewave.grafana /Library/LaunchDaemons/com.filewave.grafana.plist
		/usr/local/filewave/python/bin/python /usr/local/filewave/django/manage.pyc setup_grafana_and_prometheus
	else
		echo "Grafana already running"
	fi
}

function stopGrafana()
{
	echo "running stop grafana command"
	NUMPROCS=`grafanaIsRunning`
	if [ $NUMPROCS -ne 0 ]; then
		unloadService /Library/LaunchDaemons/com.filewave.grafana.plist
	else
		echo "Grafana already stopped"
	fi
}

function restartGrafana()
{
    /bin/echo "Restarting Grafana"
    stopGrafana
    startGrafana
}

##########################
##Command Interpretation##
##########################
if [ "$1" = "fwgui" ]; then
	case "$2"
	in
		"restart")
			echo "running restart GUI command"
			restartFWGUI
			;;
		"showKiosk")
			echo "attempting to show the FileWave Kiosk"
			showKiosk
			;;
		*) syntaxErr;;
	esac
elif [ "$1" = "apache" ]; then
	case "$2"
	in
		"start")
			echo "running start apache command"
			/usr/local/filewave/apache/bin/apachectl start
			;;
		"stop")
			echo "running stop apache command"
			/usr/local/filewave/apache/bin/apachectl stop
			;;
		"restart")
			echo "running restart apache command"
			/usr/local/filewave/apache/bin/apachectl restart
			;;
			*) syntaxErr
			;;
	esac
elif [ "$1" = "postgres" ]; then
	case "$2"
	in
		"start")
			echo "running start postgres command"
			startPostgres
			;;
		"stop")
			echo "running stop postgres command"
			stopPostgres $3
			;;
		"restart")
			echo "running restart postgres command"
			restartPostgres
			;;
			*) syntaxErr
			;;
	esac
elif [ "$1" = "redis" ]; then
	case "$2" in
		start)   startRedis ;;
		stop)    stopRedis ;;
		restart) restartRedis ;;
		*) syntaxErr ;;
	esac
elif [ "$1" = "nats" ]; then
	case "$2" in
		start)   startNats ;;
		stop)    stopNats ;;
		restart) restartNats ;;
		*) syntaxErr ;;
	esac
elif [ "$1" = "scheduler" ]; then
	if [ "$2" = "start" ]; then
		startScheduler
	elif [ "$2" = "stop" ]; then
		stopScheduler
	elif [ "$2" = "restart" ]; then
		restartScheduler
	else
		syntaxErr
	fi

elif [ "$1" = "client_monitor" ]; then
	if [ "$2" = "start" ]; then
		startClientMonitor
	elif [ "$2" = "stop" ]; then
		stopClientMonitor
	elif [ "$2" = "restart" ]; then
		restartClientMonitor
	else
		syntaxErr
	fi
elif [[ "$1" = "model_update_service" ]] && [ "$($FW_ROOT/python/bin/python $FW_ROOT/django/check_model_update_version.pyc)" = 2 ]; then
	if [ "$2" = "start" ]; then
		startModelUpdateService
	elif [ "$2" = "stop" ]; then
		stopModelUpdateService
	elif [ "$2" = "restart" ]; then
		restartModelUpdateService
	else
		syntaxErr
	fi

elif [ "$1" = "server" ]; then

	if [ "$2" = "start" ]; then
		startServer
	elif [ "$2" = "stop" ]; then
		stopServer
	elif [ "$2" = "restart" ]; then
		restartServer
	elif [ "$2" = "version" ]; then
		versionServer
	elif [ "$2" = "dbcompact" ]; then
		dbCompact
	elif [ "$2" = "migrate" ]; then
		dbMigrate
	elif [ "$2" = "restore" -a ! -z "$3" ]; then
		dbRestore $3
	elif [ "$2" = "resetinventory" ]; then
		dbResetInventory
        elif [ "$2" = "generateSelfSignedCert" ]; then
        shift; shift;
        generateSelfSignedCert "$@"
	else
		syntaxErr
	fi
elif [ "$1" = "booster" ]; then

	if [ "$2" = "start" ]; then
		startBooster
	elif [ "$2" = "stop" ]; then
		stopBooster
	elif [ "$2" = "restart" ]; then
		restartBooster
	elif [ "$2" = "version" ]; then
		versionBooster
	elif [ "$2" = "running" ]; then
		boosterRunning
	elif [ "$2" = "connectedclients" ]; then
		connectedclients
	else
		syntaxErr
	fi

elif [ "$1" = "client" ]; then

	if [ "$2" = "start" ]; then
		startClient
	elif [ "$2" = "stop" ]; then
		stopClient
	elif [ "$2" = "restart" ]; then
		restartClient
	elif [ "$2" = "version" ]; then
		versionClient
	elif [ "$2" = "status" ]; then
		clientStatus
	else
		syntaxErr
	fi
elif [ "$1" = "mdm" ]; then

	if [ "$2" = "adduser" -a ! -z "$3" ]; then
		addMDMUser $3
	elif [ "$2" = "adddeuser" -a ! -z "$3" ]; then
		addDEUser $3 $4
	elif [ "$2" = "adddepuser" -a ! -z "$3" ]; then
		addDEUser $3 $4
	elif [ "$2" = "addadminuser" ]; then
		addDjangoUser $3
	elif [ "$2" = "graceful" ]; then
		restartMDMGracefully
	else
		syntaxErr
	fi
elif [ "$1" = "dashboard" ]; then
    case "$2"
    in
        "start")
            startDashboard
            ;;
        "stop")
            stopDashboard
            ;;
        "restart")
            restartDashboard
            ;;
        *) syntaxErr
            ;;
        esac
elif [ "$1" = "grafana" ]; then
	case "$2"
	in
		"start")
			startGrafana
			;;
		"stop")
			stopGrafana
			;;
		"restart")
			restartGrafana
			;;
			*) syntaxErr
			;;
	esac
else syntaxErr

fi
