#!/bin/sh

# This program is Copyright (c) 2018 VividCortex, Inc. All rights reserved.

# This is a placeholder that is populated with the service version that the
# script is deployed with. It is here for information purposes only - DO NOT USE
VERSION=1.0.24

# Placeholder that is populated with the base URI for the download server - DO NOT USE
baseuri="https://download.vividcortex.com"

serviceName="security-gateway" # files, dirs
txtServiceName="Security Gateway"
fullTxtServiceName="VividCortex ${txtServiceName}"

# To-do: using '-' in unit/file names is probably a bad idea, at least for FreeBSD

# To-do: support non-root installs

# To-do: ability to install SG as read-only / write-only.

set -u
POSIXLY_CORRECT=1
export POSIXLY_CORRECT
TERM=dumb
export TERM

unalias -a >/dev/null 2>&1 || true

configFilename="${serviceName}.conf"
defaultconfdir="/etc/vividcortex/${serviceName}"
confdir="${defaultconfdir}"
logdir="/var/log/vividcortex/${serviceName}"
runvdir="/var/run/vividcortex/${serviceName}"
servicePidFile="${runvdir}/${serviceName}.pid"
tempdir=""
socks5=""
SKIPCERTS=""
LOADCERTS=""
DB_DSN=""
DB_SCHEMA="vividcortex"
HASH_SALT=""

EXISTS="command -v"
${EXISTS} ls >/dev/null 2>&1 || EXISTS=which
if ! ${EXISTS} ls >/dev/null 2>&1 ; then
	echo "command/which failed" >&2
	exit 1
fi

# testing
test "${baseuri}" = "unknown" && baseuri="https://download.vividcortex.com"

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

initscript_systemd() {
	nl='
'
	while IFS="${nl}" read -r line; do
		echo "${line}${nl}"
	done <<\HEREDOC
# This program is Copyright (c) 2018 VividCortex, Inc. All rights reserved.

[Unit]
Description=VividCortex Security Gateway
After=local-fs.target syslog.target network.target time-sync.target nss-lookup.target

[Service]
Type=simple
User=root
ExecStart=/usr/local/bin/security-gateway
ProtectSystem=strict
PrivateTmp=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectKernelModules=true
LockPersonality=true
MemoryDenyWriteExecute=true
SystemCallArchitectures=x86-64
NoNewPrivileges=true
Restart=on-abort
RestartPreventExitStatus=1
RestartSec=1
KillMode=control-group
KillSignal=SIGTERM
TimeoutStopSec=3

[Install]
WantedBy=multi-user.target
HEREDOC
}

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

reset_tempdir()
{
	test -n "${tempdir}" && test -d "${tempdir}" && rm -Rf "${tempdir}" >/dev/null 2>&1
	tempdir=""
}

abort()
{
	if [ $# -gt 0 ]; then
		echo >&2
		echo "$1" >&2
		echo >&2
	fi
	reset_tempdir
	exit 1
}

create_tempdir()
{
	tempdir=$(mktemp -d "/tmp/vividcortex.${serviceName}.XXXXXX" 2>/dev/null)
	if [ $? -ne 0 ] || [ -z "${tempdir}" ] || [ ! -d "${tempdir}" ]; then
		tempdir=""
		abort "Unable to create temp folder under /tmp"
	fi
}

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

createdir()
{
	mkdir -p "$1" 2>/dev/null || abort "Unable to create directory $1"
}

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

getfile()
{
	failed=0
	if ${EXISTS} curl >/dev/null 2>&1 ; then
		if [ -z "${socks5}" ]; then
			curl -f -s "$1" > "$2" 2>/dev/null && return 0
			test -n "${SKIPCERTS}" && curl -f -s -k "$1" > "$2" 2>/dev/null && return 0
		else
			curl -f -s --socks5-hostname "${socks5}" "$1" > "$2" 2>/dev/null && return 0
			curl -f -s --socks5 "${socks5}" "$1" > "$2" 2>/dev/null && return 0
			curl -f -s -x "socks5://${socks5}" "$1" > "$2" 2>/dev/null && return 0
			test -n "${SKIPCERTS}" && curl -f -s -k --socks5-hostname "${socks5}" "$1" > "$2" 2>/dev/null && return 0
			test -n "${SKIPCERTS}" && curl -f -s -k --socks5 "${socks5}" "$1" > "$2" 2>/dev/null && return 0
			test -n "${SKIPCERTS}" && curl -f -s -k -x "socks5://${socks5}" "$1" > "$2" 2>/dev/null && return 0
		fi
		failed=1
	fi
	if ${EXISTS} wget >/dev/null 2>&1 ; then
		# depending on version, uses env vars
		wget -q -O - "$1" > "$2" 2>/dev/null && return 0
		test -n "${SKIPCERTS}" && wget -q --no-check-certificate -O - "$1" > "$2" 2>/dev/null && return 0
		failed=1
	fi
	if ${EXISTS} fetch >/dev/null 2>&1 ; then
		# fetch uses env vars and does not support socks5
		fetch -q -o - "$1" > "$2" 2>/dev/null && return 0
		test -n "${SKIPCERTS}" && fetch -q --no-verify-peer -o - "$1" > "$2" 2>/dev/null && return 0
		failed=1
	fi

	test "${failed}" = "0" && abort "No suitable tool for file download found"

	test "$#" -gt 2 && test "$3" = "IGNORE" && return 1

	dlHelp="Unable to retrieve $1

Please verify proxy/firewall and install or update CA Root Certificates"

	case "${initver}" in
		FreeBSD)
			abort "${dlHelp} - e.g. pkg install security/ca_root_nss"
			;;
		Redhat)
			if [ "${distro}" = "Slackware" ]; then
				abort "${dlHelp} - e.g. update-ca-certificates"
			else
				abort "${dlHelp} - e.g. yum update ca-certificates"
			fi
			;;
		Debian|OpenRC|systemd)
			if [ "${distro}" = "SuSE" ]; then
				abort "${dlHelp} - e.g. zypper install ca-certificates ; update-ca-certificates"
			else
				abort "${dlHelp} - e.g. update-ca-certificates"
			fi
			;;
		*)
			abort "${dlHelp}."
			;;
	esac
}

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

usage()
{
	abort "Usage: install [options]  ...

Full list of options:

  --help              Display this help information
  --autostart         Configure system to start the ${txtServiceName} at boot
  --no-autostart      Do not configure system to start the ${txtServiceName} at boot
  --start-now         Start the ${txtServiceName} after installation
  --db-dsn            The Data Source Name for the samples database
  --db-schema         The schema to connect to in the target database. Defaults to 'vividcortex'
  --hash-salt         The salt to use when hashing samples to produce an id. Required.
                      Changing the salt invalidates your samples ids.
  --cdn-uri 'URI'     Use custom CDN endpoint 'URI'
  --proxy 'URI/auto/dyn/no' Configure HTTP/HTTPS or SOCKS5 proxy to 'URI',
      'auto' [default] sets whatever proxy (or no proxy) is detected in environment vars,
      'dyn' makes ${txtServiceName} detect proxies on startup.
  --no-proxy          Do not configure a proxy, alias of --proxy=no
  --bin-dir 'DIR'     Use custom install directory 'DIR'
  --cfg-dir 'DIR'     Use custom configuration files directory 'DIR'
  --uninstall         Stop & uninstall the ${txtServiceName}
  --batch             Non-interactive mode, suppresses prompts
  --init [-i] 'INIT'  Linux only. Override init detection and use the
                      specified init setup [Debian, None, OpenRC, Redhat].
  --version 'TAG'     Install ${txtServiceName} version 'TAG' (latest otherwise)"
}

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

if [ "$(id -u)" -ne 0 ]; then
	abort "This script must be run as root"
fi

test -d /usr || abort "/usr not mounted"

# Note that I am using the external getopt(1) here rather than the bash-
# internal getopts() because /bin/sh on some operating systems (e.g. FreeBSD)
# is not bash.
(getopt -T) >/dev/null 2>&1
exitcode=$?
if [ ${exitcode} -eq 4 ]; then
	# GNU version, supports long options
	GETARGS=$(getopt -o "" -l "help,autostart,no-autostart,start-now,load-certs,skip-certs,batch,uninstall,debug,static-ip,no-proxy,proxy:,version:,init:,cdn-uri:,bin-dir:,cfg-dir:,db-dsn:,db-schema:,hash-salt:" -n "$0" -- "$@") 2>/dev/null
	test $? -eq 0 || usage
	eval set -- "${GETARGS}"
else
	# BSD, cannot validate
	eval set -- "$*"
fi

AUTOSTART=""
BATCH=""
UNINSTALL=""
DEBUG=""
NOPROXY=""
PROXY="auto"
STARTNOW=""
STATICIP=""
CDNURI=""
FORCE_INIT=""
INSTALL_VERSION=current
defaultvcbindir="/usr/local/bin"
vcbindir="${defaultvcbindir}"

if [ -n "${SSL_NO_VERIFY_PEER:-}" ]; then
	SKIPCERTS=1
	LOADCERTS=1
fi

ORIG_ARGS="$@"

while [ $# -ne 0 ]; do
	case "$1" in
		--autostart)
			shift
			AUTOSTART=1
			;;
		--no-autostart)
			shift
			AUTOSTART=0
			;;
		--batch)
			shift
			BATCH=1
			;;
		--start-now)
			shift
			STARTNOW=1
			;;
		--load-certs)
			shift
			LOADCERTS=1
			;;
		--skip-certs)
			shift
			SKIPCERTS=1
			LOADCERTS=1
			;;
		--no-proxy)
			shift
			NOPROXY=1
			PROXY=""
			;;
		--proxy)
			shift
			if [ -n "$1" ]; then
				NOPROXY=""
				PROXY="$1"
				shift
				if [ "${PROXY}" = "no" ] || [ "${PROXY}" = "none" ] || [ "${PROXY}" = "false" ] || [ "${PROXY}" = "off" ]; then
					NOPROXY=1
					PROXY=""
				fi
			fi
			;;
		--init)
			shift
			FORCE_INIT="$1"
			if [ "${FORCE_INIT}" != "Debian" ] && [ "${FORCE_INIT}" != "None" ] && [ "${FORCE_INIT}" != "OpenRC" ] && [ "${FORCE_INIT}" != "Redhat" ]; then
				abort "init must be Debian, None, OpenRC or Redhat"
			fi
			shift
			;;
		--debug)
			shift
			DEBUG="${DEBUG}1"
			;;
		--static-ip)
			shift
			STATICIP=1
			CDNURI="https://static-download.vividcortex.com"
			baseuri="${CDNURI}"
			;;
		--cdn-uri)
			shift
			test -z "$1" && abort "--cdn-uri requires a value"
			CDNURI="$1"
			baseuri="$1"
			echo "${CDNURI}" | grep '^http' >/dev/null 2>&1 || abort "--cdn-uri must be http or https"
			shift
			;;
		--bin-dir)
			shift
			test -z "$1" && abort "--bin-dir requires a value"
			vcbindir="$1"
			echo "${vcbindir}" | grep '^/[^ ]\+[^/]$' >/dev/null 2>&1 || abort "invalid directory: ${vcbindir}"
			test -d "${vcbindir}" || abort "${vcbindir} is not a directory"
			shift
			;;
		--cfg-dir)
			shift
			test -z "$1" && abort "--cfg-dir requires a value"
			confdir="$1"
			echo "${confdir}" | grep '^/[^ ]\+[^/]$' >/dev/null 2>&1 || abort "invalid directory: ${confdir}"
			test -d "${confdir}" || abort "${confdir} is not a directory"
			shift
			;;
		--uninstall)
			shift
			UNINSTALL=1
			;;
		--version)
			shift
			if [ -n "$1" ]; then
				INSTALL_VERSION="$1"
				shift
			fi
			;;
		--db-dsn)
			shift
			test -z "$1" && abort "--db-dsn requires a value"
			DB_DSN="$1"
			shift
			;;
		--db-schema)
			shift
			test -z "$1" && abort "--db-schema requires a value"
			DB_SCHEMA="$1"
			shift
			;;
		--hash-salt)
			shift
			test -z "$1" && abort "--hash-salt requires a value"
			HASH_SALT="$1"
			shift
			;;
		--help)
			shift
			usage
			;;
		--)
			shift
			break
			;;
		*)
			shift
			usage
			;;
	esac
done

if [ "${DEBUG}" = "1" ]; then # cmdline includes a single --debug
	# find ourselves
	SELF=$(readlink -f "$0" 2>/dev/null)
	test -n "${SELF}" && head "${SELF}" | grep 'VividCortex' >/dev/null 2>&1 || abort "cannot find script"
	# create a temp install script patched with verbose flags
	create_tempdir
	sed 's/>\/dev\/null 2>&1//' "${SELF}" |
		sed 's/2>\/dev\/null//' |
		sed 's/curl -f -s/curl -f -v/' |
		sed 's/wget -q/wget -v/' |
		sed 's/fetch -q/fetch -v/' > "${tempdir}/vcinstall"
	# run it in trace mode
	echo
	echo -n "running install in batch mode, please wait..."
	/bin/sh -x "${tempdir}/vcinstall" --batch --debug ${ORIG_ARGS} >"${tempdir}/vcoutput" 2>&1
	cat "${tempdir}/vcoutput" | tr -s '\r' '\n' >/tmp/vividcortex.log
	reset_tempdir
	echo
	echo
	echo "Install trace copied to /tmp/vividcortex.log"
	echo
	exit 0
elif [ -n "${DEBUG}" ]; then
	# dump diagnostics
	uname -a
	sestatus
	getenforce
	cat /etc/lsb-release
	grep VERSION /etc/os-release
	set | grep -i '\(proxy\|socks\)'
	ls -l "${logdir}/"
	tail -n 30 "${logdir}/${serviceName}.log"
fi

platform=$(uname -s | tr A-Z a-z)
distro=""
arch=""
initver=""
initdir=""
initname="${serviceName}"
initperm=755
startcmd=""
stopcmd=""
preinstallcmd="true"
installcmd=""
uninstallcmd="true"

longbit=$(getconf LONG_BIT 2>/dev/null)
if [ -z "${longbit}" ]; then
	if [ -x /usr/bin/file ] && [ -x /bin/cat ]; then
		if /usr/bin/file /bin/cat | grep 64.bit >/dev/null 2>&1 ; then
			longbit=64
		fi
	else
		if uname -a | grep "\(amd\|x86_\)64" >/dev/null 2>&1 ; then
			longbit=64
		fi
	fi
fi

case "${longbit}" in
	64)
		arch="amd64"
		;;
	32)
		abort "${fullTxtServiceName} isn't supported on 32-bit systems"
		;;
	*)
		abort "Could not determine system configuration.
Please contact VividCortex for installation support.
Additional information: $(uname -a)"
		;;
esac

# aborts or sets (distro) or (distro,initdir,initver)
case "${platform}" in
	linux)
		initdir="/etc/init.d"

		if [ -n "${FORCE_INIT}" ]; then
			initver="${FORCE_INIT}"
			if [ "${initver}" = "None" ]; then
				initdir="/tmp"
				initname=""
			else
				test -d "${initdir}" || initdir="/etc/rc.d"
				test -d "${initdir}" || abort "Cannot find directory for init scripts"
			fi
			distro="Unknown-${FORCE_INIT}"
		elif grep -i ubuntu /etc/*-release >/dev/null 2>&1 ; then
			distro="Ubuntu"
			initver="Debian"
		elif grep -i debian /etc/*-release >/dev/null 2>&1 ; then
			distro="Debian"
			initver="Debian"
		elif grep -i amazon /etc/*-release >/dev/null 2>&1 ; then
			distro="Amazon"
			initver="Redhat"
		elif grep -i centos /etc/*-release >/dev/null 2>&1 ; then
			distro="CentOS"
			initver="Redhat"
		elif grep -i red.hat.enterprise /etc/*-release >/dev/null 2>&1 ; then
			distro="RHEL"
			initver="Redhat"
		elif grep -i fedora /etc/*-release >/dev/null 2>&1 ; then
			distro="Fedora"
			initver="Redhat"
		elif grep 'ID=coreos' /etc/os-release >/dev/null 2>&1 ; then
			distro="CoreOS"
			#systemd
		elif grep 'ID=alpine' /etc/os-release >/dev/null 2>&1 ; then
			distro="Alpine"
			if ${EXISTS} rc-update >/dev/null 2>&1; then
				# host
				initver="OpenRC"
			else
				# container
				initdir="/tmp"
				initver="None"
				initname=""
				SKIPCERTS=1
				LOADCERTS=1
			fi
		elif grep 'ID=slackware' /etc/os-release >/dev/null 2>&1 ; then
			distro="Slackware"
			initdir="/etc/rc.d"
			initver="Redhat"
		elif grep 'ID=rancheros' /etc/os-release >/dev/null 2>&1 ; then
			distro="RancherOS"
			initdir="/tmp"
			initver="None"
			initname=""
		elif grep 'ID=arch' /etc/os-release >/dev/null 2>&1 ; then
			distro="Arch"
			#systemd
		elif grep 'ID=devuan' /etc/os-release >/dev/null 2>&1 ; then
			distro="Devuan"
			initver="Debian"
		elif [ -e /etc/redhat-release ]; then
			distro="Redhat"
			initver="Redhat"
		elif [ -e /etc/debian_version ]; then
			distro="Debian"
			initver="Debian"
		elif [ -e /etc/gentoo-release ]; then
			distro="Gentoo"
			initver="OpenRC"
		elif [ -e /etc/SuSE-release ]; then
			distro="SuSE"
			initver="Debian"
		else
			distro="Unknown"
			# don't abort, it may be an unknown systemd-based distro
		fi
		;;
	freebsd)
		initdir="/etc/rc.d"
		test -d /usr/local/etc/rc.d && grep '/usr/local/etc/rc.d' /etc/defaults/rc.conf >/dev/null 2>&1 && initdir="/usr/local/etc/rc.d"
		test -d /usr/local/etc && test "${confdir}" = "${defaultconfdir}" && confdir="/usr/local/etc/vividcortex/${serviceName}"
		distro="FreeBSD"
		initver="FreeBSD"
		;;
	*)
		abort "Sorry, this install script doesn't support the '${platform}' platform.
Please contact VividCortex for installation support.
Additional information: $(uname -a)"
		;;
esac

if [ "${UNINSTALL}" = "" ]; then
	if [ "${HASH_SALT}" = "" ]; then
		abort "--hash-salt required"
	fi
	if [ "${DB_DSN}" = "" ]; then
		abort "--db-dsn required"
	fi
fi

# linux: do we need to use systemd instead of legacy initver?
if [ "${platform}" = "linux" ] && [ "${FORCE_INIT}" = "" ]; then
	if [ -d /etc/systemd/system ]; then
		if ${EXISTS} systemctl >/dev/null 2>&1 ; then
			# installed; enabled?
			export SYSTEMD_PAGER=
			if systemctl --version >/dev/null 2>&1 ; then
				# enabled; in use?
				if systemctl | grep -q "\.service" ; then
					initver="systemd"
					# CoreOS mounts /usr read-only. Use /vividcortex/* unless user overrides it.
					test "${distro}" = "CoreOS" && test "${vcbindir}" = "${defaultvcbindir}" && vcbindir="/vividcortex/${serviceName}"
				fi
			fi
		fi
	fi
fi

test "${initver}" = "systemd" || abort "only systemd is supported at this time"

# verify initver dependencies and set startcmd/stopcmd/installcmd
case "${initver}" in
	Redhat)
		startcmd="${initdir}/${serviceName} start"
		stopcmd="${initdir}/${serviceName} stop"
		if [ "${distro}" = "Slackware" ]; then
			installcmd="ln -s -f ${initdir}/${serviceName} /etc/rc.d/rc3.d/S${serviceName}"
			uninstallcmd="rm -f /etc/rc.d/rc?.d/?${serviceName} && rm -f ${initdir}/${serviceName}"
		else
			installcmd="/sbin/chkconfig --add ${serviceName}"
			uninstallcmd="/sbin/chkconfig --del ${serviceName}"
			${EXISTS} /sbin/chkconfig >/dev/null 2>&1 || initver=""
		fi
		;;
	Debian)
		if ${EXISTS} update-rc.d >/dev/null 2>&1 ; then
			startcmd="${initdir}/${serviceName} start"
			stopcmd="${initdir}/${serviceName} stop"
			installcmd="update-rc.d ${serviceName} defaults"
			uninstallcmd="rm -f ${initdir}/${serviceName} && update-rc.d ${serviceName} remove"
		elif ${EXISTS} /sbin/chkconfig >/dev/null 2>&1 ; then
			startcmd="${initdir}/${serviceName} start"
			stopcmd="${initdir}/${serviceName} stop"
			installcmd="/sbin/chkconfig --add ${serviceName}"
			uninstallcmd="/sbin/chkconfig --del ${serviceName}"
		else
			initver=""
		fi
		;;
	OpenRC)
		startcmd="rc-service ${serviceName} start"
		stopcmd="rc-service ${serviceName} stop"
		installcmd="rc-update add ${serviceName}"
		uninstallcmd="rc-update delete ${serviceName}"
		${EXISTS} rc-update >/dev/null 2>&1 || initver=""
		${EXISTS} rc-service >/dev/null 2>&1 || initver=""
		;;
	systemd)
		startcmd="systemctl start ${serviceName}.service"
		stopcmd="systemctl stop ${serviceName}.service"
		preinstallcmd="systemctl daemon-reload"
		installcmd="systemctl enable ${serviceName}.service"
		uninstallcmd="systemctl disable ${serviceName}.service"
		initdir="/etc/systemd/system"
		initname="${serviceName}.service"
		initperm=644
		;;
	FreeBSD)
		startcmd="${initdir}/${serviceName} onestart"
		stopcmd="${initdir}/${serviceName} stop"
		installcmd="true"
		if [ "${UNINSTALL}" = "" ]; then
			srvEnabled=""
			grep '^[[:space:]]*'${serviceName}'_enable="\?\(FALSE\|false\|NO\|no\|0\)' /etc/rc.conf >/dev/null 2>&1 && srvEnabled=0
			grep '^[[:space:]]*'${serviceName}'_enable="\?\(TRUE\|true\|YES\|yes\|1\)' /etc/rc.conf >/dev/null 2>&1 && srvEnabled=1
			if [ "${AUTOSTART}" = "0" ]; then
				test "${srvEnabled}" = "1" && abort "Please change ${serviceName}_enable to FALSE in /etc/rc.conf"
				test "${srvEnabled}" = "0" || abort "Please add ${serviceName}_enable=FALSE to /etc/rc.conf"
			elif [ "${AUTOSTART}" = "1" ]; then
				# compatibility: init script starts by default when ${serviceName}_enable not present
				test "${srvEnabled}" = "0" && abort "${serviceName}_enable=FALSE found in /etc/rc.conf, change it to TRUE to autostart"
			elif [ "${srvEnabled}" = "0" ]; then
				AUTOSTART=0
			elif [ "${srvEnabled}" = "1" ]; then
				AUTOSTART=1
			else
				abort "Please set ${serviceName}_enable to FALSE or TRUE in /etc/rc.conf"
			fi
		fi
		;;
	None)
		startcmd="${vcbindir}/${serviceName}"
		test "${confdir}" = "${defaultconfdir}" || startcmd="${startcmd} -config-file=${confdir}/${configFilename}"
		stopcmd="test -f ${servicePidFile} && kill \$(cat ${servicePidFile}) || rm -f ${servicePidFile}"
		test "${AUTOSTART}" = "1" && abort "Autostarting unsupported, please remove option --autostart"
		AUTOSTART=0
		;;
	*)
		initver=""
		;;
esac

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

delete_binary()
{
	test $# -ne 1 && abort "delete_binary: expected 1 arg, got $#"
	srvBinDir="$1"
	echo "${srvBinDir}" | grep '^/[^?*" ]\+[^/?*" .]$' >/dev/null 2>&1 || abort "delete_binary: invalid dir ${srvBinDir}"
	delspec="${srvBinDir}/${serviceName}"
	for f in ${delspec}; do
		test -f "${f}" && grep -i 'Copyright (c) [0-9-]\+ VividCortex, Inc' "${f}" >/dev/null 2>&1 && rm -f "${f}" && echo "deleted $f"
	done
}

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

stop_service()
{
	echo -n "Stopping ${fullTxtServiceName}... "
	# stop - assumes running service use current init system
	if [ -n "${stopcmd}" ]; then
		${stopcmd} >/dev/null 2>&1
	fi
	# stop - generic linux/freebsd
	${EXISTS} service >/dev/null 2>&1 && service ${serviceName} stop >/dev/null 2>&1
	${EXISTS} rc-service >/dev/null 2>&1 && rc-service ${serviceName} stop >/dev/null 2>&1
	${EXISTS} systemctl >/dev/null 2>&1 && systemctl stop ${serviceName}.service >/dev/null 2>&1
	test -x /etc/init.d/${serviceName} && /etc/init.d/${serviceName} stop >/dev/null 2>&1
	test -x /etc/rc.d/${serviceName} && /etc/rc.d/${serviceName} stop >/dev/null 2>&1
	test -x /usr/local/etc/rc.d/${serviceName} && /usr/local/etc/rc.d/${serviceName} stop >/dev/null 2>&1
	if [ -f "${servicePidFile}" ]; then
		# ${stopcmd} fails on BusyBox's ash
		sleep 7
		if [ -f "${servicePidFile}" ]; then
			kill $(cat "${servicePidFile}")
			rm -f "${servicePidFile}" >/dev/null 2>&1
		fi
	fi
	rm -f "/var/lock/subsys/${serviceName}" >/dev/null 2>&1
	echo "OK"
}

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

delete_init()
{
	echo -n "Removing ${txtServiceName} init script... "
	# assumes running service use current init system
	${uninstallcmd} >/dev/null 2>&1
	# generic linux/freebsd
	/sbin/chkconfig --del ${serviceName} >/dev/null 2>&1
	${EXISTS} systemctl >/dev/null 2>&1 && systemctl disable ${serviceName}.service >/dev/null 2>&1
	${EXISTS} rc-update >/dev/null 2>&1 && rc-update delete ${serviceName} >/dev/null 2>&1
	rm -f /etc/init.d/${serviceName} >/dev/null 2>&1
	${EXISTS} update-rc.d >/dev/null 2>&1 && update-rc.d ${serviceName} remove >/dev/null 2>&1 # after deleting init.d script
	rm -f /etc/rc.d/${serviceName} >/dev/null 2>&1
	rm -f /etc/rc.d/rc?.d/?${serviceName} /etc/rc.d/rc?.d/?[0-9]${serviceName} /etc/rc.d/rc?.d/?[0-9][0-9]${serviceName} >/dev/null 2>&1
	rm -f /usr/local/etc/rc.d/${serviceName} >/dev/null 2>&1
	rm -f /etc/systemd/system/${serviceName}.service >/dev/null 2>&1
	rm -f /etc/systemd/system/*/${serviceName}.service >/dev/null 2>&1
	echo "OK"
}

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

get_init()
{
	if [ -n "${initname}" ]; then
		"initscript_${initver}" > "${tempdir}/initscript"

		if [ "${vcbindir}" != "${defaultvcbindir}" ]; then
			escFrom=`echo "${defaultvcbindir}" | sed 's/\//\\\&/g'`
			escTo=`echo "${vcbindir}" | sed 's/\//\\\&/g'`
			sed "s/${escFrom}/${escTo}/g" "${tempdir}/initscript" >"${tempdir}/initscript.new" || abort "Failed to replace vcbindir in init script"
			mv -f "${tempdir}/initscript.new" "${tempdir}/initscript" || abort "Failed to move file after replacing vcbindir in init script"
		fi
		if [ "${confdir}" != "${defaultconfdir}" ]; then
			escFrom=`echo "${defaultconfdir}" | sed 's/\//\\\&/g'`
			escTo=`echo "${confdir}" | sed 's/\//\\\&/g'`
			sed "s/${escFrom}/${escTo}/g" "${tempdir}/initscript" >"${tempdir}/initscript.new" || abort "Failed to replace confdir in init script"
			sed "s/set_conf_file=0/set_conf_file=1/" "${tempdir}/initscript.new" >"${tempdir}/initscript" || abort "Failed to replace set_conf_file in init script"
			if [ "${initver}" = "systemd" ]; then
				mv -f "${tempdir}/initscript" "${tempdir}/initscript.new" || abort "Unable to move init script"
				sed "s/\(^ExecStart=.\+$\)/\1 -config-file=${escTo}\/${configFilename}/" "${tempdir}/initscript.new" > "${tempdir}/initscript" || abort "Failed to patch ExecStart in init script"
			fi
			rm -f "${tempdir}/initscript.new"
		fi
	fi
}

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

install_init()
{
	if [ -n "${initname}" ]; then
		mv -f "${tempdir}/initscript" "${initdir}/${initname}" || abort "Unable to install ${txtServiceName} init script"
		chmod ${initperm} "${initdir}/${initname}" || abort "Unable to set ${initdir}/${initname} permissions"
	fi
}

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

set_proxy()
{
	# detect proxy
	detProxy=${HTTPS_PROXY:-''}
	test -n "${detProxy}" && echo "${detProxy}" | grep -v '://' >/dev/null 2>&1 && detProxy="://${detProxy}"
	echo "${detProxy}" | grep '://' >/dev/null 2>&1 || detProxy=${https_proxy:-''}
	test -n "${detProxy}" && echo "${detProxy}" | grep -v '://' >/dev/null 2>&1 && detProxy="://${detProxy}"
	echo "${detProxy}" | grep '://' >/dev/null 2>&1 || detProxy=${HTTP_PROXY:-''}
	test -n "${detProxy}" && echo "${detProxy}" | grep -v '://' >/dev/null 2>&1 && detProxy="://${detProxy}"
	echo "${detProxy}" | grep '://' >/dev/null 2>&1 || detProxy=${http_proxy:-''}
	test -n "${detProxy}" && echo "${detProxy}" | grep -v '://' >/dev/null 2>&1 && detProxy="://${detProxy}"
	echo "${detProxy}" | grep '://' >/dev/null 2>&1 || detProxy=${SOCKS_SERVER:-''}
	test -n "${detProxy}" && echo "${detProxy}" | grep -v '://' >/dev/null 2>&1 && detProxy="socks5://${detProxy}"
	echo "${detProxy}" | grep '://' >/dev/null 2>&1 || detProxy=${ALL_PROXY:-''}
	test -n "${detProxy}" && echo "${detProxy}" | grep -v '://' >/dev/null 2>&1 && detProxy="socks5://${detProxy}"
	echo "${detProxy}" | grep '://' >/dev/null 2>&1 || detProxy=${all_proxy:-''}
	test -n "${detProxy}" && echo "${detProxy}" | grep -v '://' >/dev/null 2>&1 && detProxy="socks5://${detProxy}"
	echo "${detProxy}" | grep '://' >/dev/null 2>&1 || detProxy=""

	unset ALL_PROXY all_proxy HTTP_PROXY http_proxy HTTP_PROXY_AUTH HTTPS_PROXY https_proxy SOCKS_SERVER SOCKS_VERSION SSL_NO_VERIFY_PEER

	# scheme-less proxy, detect
	if echo "${detProxy}" | grep "^://" >/dev/null 2>&1 ; then
		export HTTP_PROXY="http${detProxy}"
		export http_proxy="http${detProxy}"
		export HTTPS_PROXY="http${detProxy}"
		export https_proxy="http${detProxy}"
		if getfile "${baseuri}/install" "/dev/null" IGNORE ; then
			detProxy="http${detProxy}"
		else
			export HTTP_PROXY="https${detProxy}"
			export http_proxy="https${detProxy}"
			export HTTPS_PROXY="https${detProxy}"
			export https_proxy="https${detProxy}"
			if getfile "${baseuri}/install" "/dev/null" IGNORE ; then
				detProxy="https${detProxy}"
			else
				detProxy=""
			fi
		fi
		unset HTTP_PROXY http_proxy HTTPS_PROXY https_proxy
	fi

	proxy=""
	dynmsg=""

	if [ -n "${NOPROXY}" ]; then
		test -n "${detProxy}" && dynmsg=" because of --no-proxy. If install fails, consider removing
that option as ${detProxy} may be a valid proxy."
	elif [ "${PROXY}" = "auto" ]; then
		proxy="${detProxy}"
		dynmsg="  (auto-detected)"
	elif [ "${PROXY}" = "dyn" ]; then
		proxy="${detProxy}"
		dynmsg="  (will re-detect every time the service starts)"
	elif [ -n "${PROXY}" ]; then
		proxy="${PROXY}"
	elif [ "${BATCH}" = "1" ]; then
		test -n "${detProxy}" && dynmsg=" because of --batch and lack of --proxy. If install fails,
consider --proxy=auto as ${detProxy} may be a valid proxy."
	else
		resp=""
		echo
		echo -n "Do you need to set up a proxy? [y/N] "
		if read -r resp && [ "${resp}" = "Y" ] || [ "${resp}" = "y" ]; then
			if [ -n "${detProxy}" ]; then
				echo
				echo "Environment vars suggest this may be a valid proxy: ${detProxy}"
			fi
			echo
			echo -n "Please input your proxy URI: "
			read -r proxy
			test -z "${proxy}" && abort "Blank proxy; retry install"
		fi
	fi

	if [ -n "${proxy}" ]; then
		if echo "${proxy}" | grep "^\(socks5\|socks\)://" >/dev/null 2>&1 ; then
			# socks5 proxy
			socks5=$(echo "${proxy}" | sed 's/[^:]\+:\/\///')
			export SOCKS_SERVER="${socks5}"
			export SOCKS_VERSION=5
			export ALL_PROXY="${proxy}"
			export all_proxy="${proxy}"
		elif echo "${proxy}" | grep "^\(https\|http\)://" >/dev/null 2>&1 ; then
			# https/http proxy
			export HTTP_PROXY="${proxy}"
			export http_proxy="${proxy}"
			export HTTPS_PROXY="${proxy}"
			export https_proxy="${proxy}"
		else
			abort "Unsupported proxy URI: ${proxy}
Proxy URI must begin with 'https://', 'http://', 'socks5://' or 'socks://'.
E.g.: socks5://user:pass@127.0.0.1:1080"
		fi
		echo
		echo "Using proxy: ${proxy}${dynmsg}"
	else
		# try harder to not use a proxy
		export NO_PROXY="$(echo "${baseuri}" | sed 's/.*:\/\///' | sed 's/\/.*$//')"
		export no_proxy="${NO_PROXY}"
		echo
		echo "Not using a proxy${dynmsg}"
	fi
	test "${PROXY}" = "dyn" && proxy="auto"
}

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

create_config()
{
	rm -f "${globalconf}.tmp" >/dev/null 2>&1
	test -e "${globalconf}.tmp" && abort "Unable to remove ${globalconf}.tmp"

	cat - > "${globalconf}.tmp" <<EOF
{
    EXTRA_CONFIG
EOF

	# testing
	grep 'EXTRA_' "${globalconf}.tmp" >/dev/null 2>&1 && echo '{' > "${globalconf}.tmp"

	if [ -n "${LOADCERTS}" ]; then
		cat - >> "${globalconf}.tmp" <<EOF
    "override-os-certs": "true",
EOF
	fi

	if [ -n "${SKIPCERTS}" ]; then
		cat - >> "${globalconf}.tmp" <<EOF
    "skip-certs": "true",
EOF
	fi

        cat - >> "${globalconf}.tmp" <<EOF
    "db-dsn": "${DB_DSN}",
    "db-schema": "${DB_SCHEMA}",
    "hash-salt": "${HASH_SALT}",
    "log-type": "file",
    "proxy-uri": "${proxy}"
}
EOF

	mv -f "${globalconf}.tmp" "${globalconf}" >/dev/null 2>&1 || abort "Unable to move ${globalconf}.tmp to ${globalconf}"
	test -s "${globalconf}" || abort "Creation of ${globalconf} failed"
	chmod 600 "${globalconf}" >/dev/null 2>&1 || abort "Unable to chmod ${globalconf}"
}

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

start_service()
{
	resp=""
	if [ -z "${AUTOSTART}" ] && [ "${BATCH}" != "1" ] && [ "${installcmd}" != "" ]; then
		echo -n "Would you like to schedule automatic service startup? [Y/n] "
		read -r resp
		echo
		test -z "${resp}" && resp="Y"
	fi

	${preinstallcmd} >/dev/null 2>&1 || abort "Running pre-install action '${preinstallcmd}' failed"

	if [ "${resp}" = "Y" ] || [ "${resp}" = "y" ] || [ "${AUTOSTART}" = "1" ]; then
		if [ "${installcmd}" = "" ]; then
			abort "Sorry, don't know how to enable init scripts for '${initver}'.
Please enable the '${serviceName}' init script manually.
Please also tell VividCortex how to improve this script to do that."
		else
			${installcmd} >/dev/null 2>&1 || abort "Unable to install init script using '${installcmd}'"
			AUTOSTART=1
		fi
		echo "${txtServiceName} init script scheduled for automatic start/stop"
	else
		echo "${txtServiceName} init script is NOT scheduled for automatic start/stop"
	fi

	echo

	if [ "${STARTNOW}" = "1" ]; then
		echo "Starting ${fullTxtServiceName}"
		rm -f "${servicePidFile}" >/dev/null 2>&1
		started=0
		if ${startcmd} >/dev/null 2>&1 ; then
			sleep 3
			test -f "${servicePidFile}" && started=1
		fi

		if [ "${started}" != "1" ]; then
			abort "${fullTxtServiceName} service startup may have failed.

It can be started by running:

    ${startcmd}

and stopped by running:

    ${stopcmd}"
		elif [ "${AUTOSTART}" != "1" ]; then
			echo
			echo "Started successfully.  ${txtServiceName} is NOT configured to start at boot because"
			echo "install was run without --autostart.  It can be started by running:"
			echo
			echo "    ${startcmd}"
			echo
			echo "and stopped by running:"
			echo
			echo "    ${stopcmd}"
		fi
	else
		echo "${txtServiceName} can be started by running:"
		echo
		echo "    ${startcmd}"
		echo
		echo "and stopped by running:"
		echo
		echo "    ${stopcmd}"
	fi
}

globalconf="${confdir}/${configFilename}"

if [ "${UNINSTALL}" = "1" ]; then
	echo
	if [ "${BATCH}" != "1" ]; then
		echo -n "Are you sure you want to uninstall ${fullTxtServiceName}? [y/N] "
		read -r resp
		test "${resp}" != "y" && test "${resp}" != "Y" && abort "Uninstall aborted."
		echo
	fi

	stop_service
	delete_init
	echo -n "Cleaning up... "
	# clean-up
	rm -Rf "/etc/vividcortex/${serviceName}" >/dev/null 2>&1
	rm -Rf "/usr/local/etc/vividcortex/${serviceName}" >/dev/null 2>&1
	rm -Rf "${runvdir}" >/dev/null 2>&1
	rm -Rf "/var/lock/vividcortex/${serviceName}" >/dev/null 2>&1
	delete_binary "${defaultvcbindir}"
	delete_binary "${vcbindir}"
	if [ -d "${logdir}" ]; then
		echo "OK, deleted everything except logs at ${logdir}"
	else
		echo "OK"
	fi
	echo
	exit 0
fi

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

if [ "${initver}" = "" ] || [ "${initdir}" = "" ] || [ ! -d "${initdir}" ]; then
	abort "Sorry, install does not yet support your version or flavor of ${platform}/${distro}.

Additional information: $(uname -a)

Please contact VividCortex for installation support."
fi

# Download to temp folder in case we need to abort
create_tempdir

if [ -e "${globalconf}" ]; then
	abort "${globalconf} exists; stopping.
Remove and re-run if you want to replace an existing install."
fi

# agents are installed in ${vcbindir}; make sure it exists
if [ ! -d "${vcbindir}" ]; then
	if [ "${vcbindir}" = "/usr/local/bin" ] && [ ! -d /usr/local ]; then
		mkdir /usr/local >/dev/null 2>&1
		chmod 755 /usr/local >/dev/null 2>&1
	fi
	mkdir "${vcbindir}" >/dev/null 2>&1
	chmod 755 "${vcbindir}" >/dev/null 2>&1
	test -d "${vcbindir}" || abort "Cannot create ${vcbindir}"
fi

# customer consented a reinstall by removing globalconf; make sure service isn't running
if [ -z "${initname}" ] || [ -e "${initdir}/${initname}" ]; then
	stop_service >/dev/null
	sleep 1
	${uninstallcmd} >/dev/null 2>&1
fi

# remove service file
test -n "${initname}" && rm -f "${initdir}/${initname}"

echo
echo "Installing ${txtServiceName} for ${platform} ${distro} ${arch}."

set_proxy

if [ "${INSTALL_VERSION}" != "current" ]; then
	echo
	echo "Installing ${txtServiceName} version: ${INSTALL_VERSION}"
fi

if [ "${STATICIP}" != "" ]; then
	echo
	echo "Using static IP: make sure to allow connections to TCP port 443 of static-app.vividcortex.com"
fi

getfile "${baseuri}/${serviceName}/${platform}/${arch}/${INSTALL_VERSION}/${serviceName}" "${tempdir}/${serviceName}"
chmod 700 "${tempdir}/${serviceName}"

EXTRA_ARG=""
if [ -n "${LOADCERTS}" ]; then
	EXTRA_ARG="${EXTRA_ARG} -override-os-certs"
fi
if [ -n "${SKIPCERTS}" ]; then
	EXTRA_ARG="${EXTRA_ARG} -skip-certs"
fi

{ "${tempdir}/${serviceName}" -self-test -proxy-uri="${proxy}" -config-file="" -create-db-objects -db-dsn="${DB_DSN}" -db-schema="${DB_SCHEMA}" -hash-salt="${HASH_SALT}" -log-type=stderr ${EXTRA_ARG} ; } >"${tempdir}/${serviceName}.log" 2>&1
exitcode=$?
# 0 exitSuccess
if [ ${exitcode} -ne 0 ]; then
	abort "Installation failed. Please retry and, if the problem persists, contact VividCortex.

Self-test log:
$(cat "${tempdir}/${serviceName}.log")

Additional information: ${distro}/${arch} (exitcode=${exitcode}) $(uname -a)"
fi

mv -f "${tempdir}/${serviceName}" "${vcbindir}/" >/dev/null 2>&1 || abort "Unable to install ${serviceName}"
chmod 700 "${vcbindir}/${serviceName}" >/dev/null 2>&1 || abort "Unable to set ${vcbindir}/${serviceName} permissions"

get_init

createdir "${confdir}"
createdir "${logdir}"
createdir "${runvdir}"

install_init
reset_tempdir
create_config

echo
echo "${fullTxtServiceName} successfully installed"
echo

start_service

echo
exit 0
