#!/usr/bin/bash
# Atomicorp API: Agent
# Copyright Atomicorp 2023
# All Rights reserved

VERSION=2.9.6
DEBUG=0
HOSTNAME=$(hostname)

if [ ! -f /var/ossec/etc/client.keys ]; then
	echo "Error: Agent not registered"
	exit 1
fi


AGENT_ID=$(cat /var/ossec/etc/client.keys | awk '{print $1}')
AGENT_NAME=$(cat /var/ossec/etc/client.keys | awk '{print $2}')

###########################
# Logging
###########################
LOG_FILE="/var/ossec/logs/atomicorp-api.log"
REMOTE_UPGRADE_STATE="/var/ossec/tmp/awp-remote-upgrade.state"
UPGRADE_LOCK="/var/ossec/tmp/awp-remote-upgrade.lock"

###########################
#functions
###########################
function log_event () {
    MSG=$1
    echo "`date "+%b %d %H:%M:%S"` ${HOSTNAME} atomicorp-api: ${MSG}" >> ${LOG_FILE}
	logger -t atomicorp-api "${MSG} Agent: ${AGENT_NAME} ID: ${AGENT_ID} scanid:${SCANID}"
}

function detect_dist () {
	#log_event "detect_dist: start"
	if [ -f /etc/redhat-release ]; then
		RELEASE=/etc/redhat-release
	elif [ -f /etc/os-release ]; then
		RELEASE=/etc/os-release
	elif [ -f /etc/openvz-release ]; then
		RELEASE=/etc/openvz-release
	elif [ -f /etc/SuSE-release ]; then
		RELEASE=/etc/SuSE-release
	elif [ -f /etc/os-release ]; then
		RELEASE=/etc/os-release
	elif [ -f /etc/lsb-release ]; then
		RELEASE=/etc/lsb-release
	elif [ -f /etc/debian_version ]; then
		RELEASE=/etc/debian_version
	elif [ -f /etc/openvz-release ]; then
		RELEASE=/etc/openvz-release
	elif [ -f /etc/virtuozzo-release ]; then
		RELEASE=/etc/virtuozzo-release
	elif [[ $OSTYPE == "aix"* ]]; then
		PKG=aix
	else
		echo "Error: unable to identify operating system"
		exit 1
	fi

	if [[ $OSTYPE == "aix"* ]]; then
		PKG=aix
	elif egrep -q "(release 5)" $RELEASE ; then
		DIST="el5"
		DIR=centos/5
		PKG=rpm
	elif egrep -q "(release 6|release 2012)" $RELEASE ; then
		DIST="el6"
		DIR=centos/6
		PKG=rpm
	elif egrep -q "(release 7|release 2014)" $RELEASE ; then
		DIST="el7"
		DIR=centos/7
		PKG=rpm
	elif egrep -q "(release 8)" $RELEASE ; then
		DIST="el8"
		DIR=centos/8
		PKG=rpm
	elif egrep -q "Red Hat Enterprise Linux.* 7" $RELEASE ; then
		DIST="el7"
		DIR=redhat/7
		PKG=rpm
	elif egrep -q "Red Hat Enterprise Linux.* 8" $RELEASE ; then
		DIST="el8"
		DIR=redhat/8
		PKG=rpm
	elif egrep -q "(release 9)" $RELEASE ; then
		DIST="el9"
		DIR=rocky/9
		PKG=rpm
	elif egrep -q "(release 10)" $RELEASE ; then
		DIST="el10"
		DIR=rocky/10
		PKG=rpm
	elif egrep -q "(Amazon Linux 2)" $RELEASE; then
		DIST="amazon"
		DIR=amazon/2
		PKG=rpm
	elif egrep -q "(Amazon Linux AMI|Amazon)" $RELEASE ; then
		DIST="el6"
		DIR=centos/6
		PKG=rpm
	elif egrep -q "wheezy" $RELEASE ; then
		DIST="debian"
		DIR="wheezy"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "jessie" $RELEASE ; then
		DIST="debian"
		DIR="jessie"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "stretch" $RELEASE ; then
		DIST="debian"
		DIR="stretch/usr/bin/apt"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "lucid" $RELEASE ; then
		DIST="debian"
		DIR="lucid"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "precise" $RELEASE ; then
		DIST="debian"
		DIR="precise"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "Raring Ringtail" $RELEASE ; then
		DIST="debian"
		DIR="raring"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "Trusty Tahr" $RELEASE ; then
		DIST="ubuntu"
		DIR="trusty"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "Xenial" $RELEASE ; then
		DIST="ubuntu"
		DIR="xenial"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "Bionic" $RELEASE ; then
		DIST="ubuntu"
		DIR="bionic"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "Focal Fossa" $RELEASE; then
		DIST="ubuntu"
		DIR="focal"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "Jammy" $RELEASE; then
		DIST="ubuntu"
		DIR="jammy"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "Noble" $RELEASE; then
		DIST="ubuntu"
		DIR="noble"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "buster" $RELEASE ; then
		DIST="debian"
		DIR="buster"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
	elif egrep -q "bullseye" $RELEASE ; then
		DIST="debian"
		DIR="bullseye"
		PKG=deb
		ARCH=$(dpkg --print-architecture)
    elif egrep -q "bookworm" $RELEASE ; then
        DIST="debian"
        DIR="bookworm"
        PKG=deb
        ARCH=$(dpkg --print-architecture)
	elif egrep -q "openSUSE Leap" $RELEASE; then
		DIST="suse15"
		DIR="opensuse/15.1"
		PKG=zypper
	else
		echo "Error: Unable to determine distribution type. Please send the contents of $RELEASE to support@atomicorp.com"
		log_event "Error: Unable to determine distribution type. Please send the contents of $RELEASE to support@atomicorp.com"
		exit 1
	fi

	#log_event "detect_dist: DIST:($DIST) DIR:($DIR) PKG:($PKG)"


}

function check_repository() {
    if [[ $PKG == "rpm" ]]; then
        REPO_FILE="/etc/yum.repos.d/atomicorp-ossec.repo"
    elif [[ $PKG == "deb" ]]; then
        REPO_FILE="/etc/apt/sources.list.d/atomicorp-ossec.list"
    else
        return 0
    fi

    if [ ! -f "$REPO_FILE" ]; then
        log_event "ERROR: Repository missing ($REPO_FILE). Update aborted."
        echo "ERROR: Repository missing ($REPO_FILE). Update aborted."
        exit 1
    fi
}

function install_package () {
	check_repository
	PACKAGE=$1
	log_event "install: ${PACKAGE} install_package start"
	if [[ $PKG == "rpm" ]]; then
		yum -y install ${PACKAGE}
		if [ $? -eq 0 ]; then
			log_event "install: ${PACKAGE} install_package:success"
		else
			log_event "install: ${PACKAGE} install_package:failure"
			return 1
		fi
		
	elif [[ $PKG == "deb" ]]; then
		# Update data
		apt -y update
		if [ $? -ne 0 ]; then
			log_event "install: ${PACKAGE} install_package:apt update failed"
			return 1
		fi

		# Install
		DEBIAN_FRONTEND=noninteractive  apt install -o  Dpkg::Options::="--force-confmiss" -y ${PACKAGE}
		if [ $? -eq 0 ]; then
			log_event "install: ${PACKAGE} install_package:success"
		else
			log_event "install: ${PACKAGE} install_package:failure"
			return 1
		fi
	else
		echo "ERROR: $DIST Not supported"
		exit 1
	fi 
}

function write_upgrade_state() {
	local started_at="${1:-}"
	local detach_method="${2:-}"
	local package="${3:-}"
	local finished_at="${4:-}"
	local exit_code="${5:-}"

	{
		echo "started_at=${started_at}"
		echo "detach_method=${detach_method}"
		echo "package=${package}"
		echo "finished_at=${finished_at}"
		echo "exit_code=${exit_code}"
	} >"$REMOTE_UPGRADE_STATE"
}

function write_upgrade_state_scheduled() {
	local method=$1
	local pkg=$2
	local ts
	ts=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u)
	write_upgrade_state "$ts" "$method" "$pkg" "" ""
}

function finish_upgrade_state() {
	local code="${1:-0}"
	local ts
	local started_at="" detach_method="" package=""

	ts=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u)
	if [[ -f "$REMOTE_UPGRADE_STATE" ]]; then
		started_at=$(grep '^started_at=' "$REMOTE_UPGRADE_STATE" 2>/dev/null | cut -d= -f2-)
		detach_method=$(grep '^detach_method=' "$REMOTE_UPGRADE_STATE" 2>/dev/null | cut -d= -f2-)
		package=$(grep '^package=' "$REMOTE_UPGRADE_STATE" 2>/dev/null | cut -d= -f2-)
	fi
	[[ -z "$detach_method" ]] && detach_method="worker"
	write_upgrade_state "$started_at" "$detach_method" "$package" "$ts" "$code"
}

function rpm_upgrade_cli() {
	if command -v dnf >/dev/null 2>&1; then
		echo "dnf"
	elif command -v yum >/dev/null 2>&1; then
		echo "yum"
	elif command -v microdnf >/dev/null 2>&1; then
		echo "microdnf"
	else
		echo ""
	fi
}

function enable_and_restart_ossec_service() {
	if systemctl list-unit-files 2>/dev/null | grep -q "ossec-agent.service"; then
		systemctl enable ossec-agent 2>/dev/null || true
		systemctl restart ossec-agent 2>/dev/null || true
	elif systemctl list-unit-files 2>/dev/null | grep -q "ossec-hids.service"; then
		systemctl enable ossec-hids 2>/dev/null || true
		systemctl restart ossec-hids 2>/dev/null || true
	else
		log_event "upgrade: warning - could not determine ossec service name for restart"
	fi
}

function rpm_packages_installed_subset() {
	local word
	local ok=""
	for word in $1; do
		if rpm -q "$word" >/dev/null 2>&1; then
			ok+="$word "
		fi
	done
	echo "${ok%% }"
}

function upgrade_package_sync() {
	local PACKAGE=$1
	local RETVAL=1
	local OUTPUT
	local rpm_cli
	local rpm_targets

	check_repository

	if [[ $PKG == "zypper" ]]; then
		log_event "upgrade: ${PACKAGE} remote package upgrade not supported on zypper-backed hosts yet (no-op)"
		return 0
	fi

	if [[ $PKG == "rpm" ]]; then
		rpm_cli=$(rpm_upgrade_cli)
		if [[ -z "$rpm_cli" ]]; then
			log_event "upgrade: ${PACKAGE} no dnf/yum/microdnf found"
			return 1
		fi
		rpm_targets=$(rpm_packages_installed_subset "$PACKAGE")
		if [[ -z "$rpm_targets" ]]; then
			log_event "upgrade: (${PACKAGE}) none of the requested packages are installed; skipping rpm upgrade"
			return 1
		fi
		if [[ "$rpm_targets" != "$PACKAGE" ]]; then
			log_event "upgrade: limiting rpm upgrade to installed packages: (${rpm_targets})"
		fi
		systemctl mask ossec-agent 2>/dev/null || true
		log_event "upgrade: ${rpm_targets} upgrade_package:${rpm_cli} upgrade ${rpm_targets}"
		if [[ "$rpm_cli" == "microdnf" ]]; then
			OUTPUT=$($rpm_cli -y update $rpm_targets 2>&1)
		else
			OUTPUT=$($rpm_cli -y upgrade $rpm_targets 2>&1)
		fi
		RETVAL=$?
		if [[ $RETVAL -ne 0 ]]; then
			log_event "upgrade: ${PACKAGE} upgrade_package:${rpm_cli} upgrade returned ($OUTPUT)"
		fi
		systemctl unmask ossec-agent 2>/dev/null || true
	elif [[ $PKG == "deb" ]]; then
		systemctl mask ossec-hids 2>/dev/null || true
		log_event "upgrade: ${PACKAGE} upgrade_package:apt upgrade ${PACKAGE}"
		OUTPUT=$(/usr/bin/apt-get update -o APT::Update::Error-Mode=any 2>&1)
		if [[ $? -ge 1 ]]; then
			log_event "upgrade: ${PACKAGE} upgrade_package:apt update returned ($OUTPUT)"
		fi
		OUTPUT=$(DEBIAN_FRONTEND=noninteractive /usr/bin/apt-get install --only-upgrade -o Dpkg::Options::="--force-confmiss" -y ${PACKAGE} 2>&1)
		RETVAL=$?
		if [[ $RETVAL -ne 0 ]]; then
			log_event "upgrade: ${PACKAGE} upgrade_package:apt upgrade returned ($OUTPUT)"
		fi
		systemctl unmask ossec-hids 2>/dev/null || true
	else
		log_event "upgrade: ERROR $DIST not supported (${PACKAGE})"
		return 1
	fi

	if [[ $RETVAL -eq 0 ]]; then
		log_event "upgrade: ${PACKAGE} upgrade_package:success"
		enable_and_restart_ossec_service
	else
		log_event "upgrade: ${PACKAGE} upgrade_package:failure"
	fi
	log_event "upgrade: ${PACKAGE} upgrade_package:end"
	return "$RETVAL"
}

function spawn_agent_upgrade_worker() {
	local pkg="$1"
	local script_path
	local lock_fd=""

	script_path=$(readlink -f "$0" 2>/dev/null || echo "$0")

	if command -v flock >/dev/null 2>&1 && [[ -n "${BASH_VERSION:-}" ]]; then
		exec {lock_fd}>"$UPGRADE_LOCK" 2>/dev/null || lock_fd=""
		if [[ -n "$lock_fd" ]] && ! flock -n "$lock_fd" 2>/dev/null; then
			log_event "remote upgrade: lock busy (${UPGRADE_LOCK}), skipping duplicate schedule"
			exec {lock_fd}>&- 2>/dev/null || true
			return 0
		fi
	fi

	if command -v systemd-run >/dev/null 2>&1 && [[ -d /run/systemd/system ]]; then
		local sr_unit sr_cmd
		sr_unit="awp-remote-upg-${RANDOM}.service"
		# Avoid -p StandardOutput=append on transient units (can be ignored/mis-parsed on some EL9 setups);
		# run bash and redirect inside the unit.
		sr_cmd=$(printf 'exec >>%q 2>&1; exec %q __upgrade_worker__ %q' "$LOG_FILE" "$script_path" "$pkg")
		if systemd-run --no-block \
			--unit="$sr_unit" \
			--slice=system.slice \
			--uid=0 \
			--gid=0 \
			/usr/bin/bash -c "$sr_cmd"
		then
			write_upgrade_state_scheduled "systemd-run" "$pkg"
			log_event "remote upgrade: scheduled via systemd-run (${pkg}) unit=${sr_unit}"
			[[ -n "$lock_fd" ]] && exec {lock_fd}>&- 2>/dev/null || true
			return 0
		fi
		log_event "remote upgrade: systemd-run failed, falling back"
	fi

	if command -v setsid >/dev/null 2>&1; then
		setsid nohup "$script_path" __upgrade_worker__ "$pkg" >>"$LOG_FILE" 2>&1 </dev/null &
		write_upgrade_state_scheduled "setsid+nohup" "$pkg"
		log_event "remote upgrade: scheduled via setsid+nohup (${pkg})"
		[[ -n "$lock_fd" ]] && exec {lock_fd}>&- 2>/dev/null || true
		return 0
	fi

	if command -v at >/dev/null 2>&1; then
		local atjob
		atjob=$(printf '%q ' "$script_path" __upgrade_worker__ "$pkg")
		atjob+=" >>$(printf '%q' "$LOG_FILE") 2>&1 </dev/null"
		if printf '%s\n' "$atjob" | at now >/dev/null 2>&1; then
			write_upgrade_state_scheduled "at" "$pkg"
			log_event "remote upgrade: scheduled via at (${pkg})"
			[[ -n "$lock_fd" ]] && exec {lock_fd}>&- 2>/dev/null || true
			return 0
		fi
		log_event "remote upgrade: at queue failed, falling back"
	fi

	nohup "$script_path" __upgrade_worker__ "$pkg" >>"$LOG_FILE" 2>&1 </dev/null &
	write_upgrade_state_scheduled "nohup" "$pkg"
	log_event "remote upgrade: scheduled via nohup (${pkg})"
	[[ -n "$lock_fd" ]] && exec {lock_fd}>&- 2>/dev/null || true
	return 0
}

function request_ossec_agent_upgrade() {
	local pkgs="ossec-hids ossec-hids-agent awp-agent"
	if [[ "$PKG" == "zypper" ]]; then
		log_event "upgrade: ${pkgs} skipped on zypper (remote ossec package upgrade not supported)"
		return 0
	fi
	if [[ "${ATOMICORP_API_JSON_MODE:-0}" == 1 ]]; then
		log_event "upgrade: ${pkgs} scheduling detached remote upgrade"
		spawn_agent_upgrade_worker "$pkgs"
	else
		upgrade_package_sync "$pkgs"
	fi
}

function upgrade_package() {
	upgrade_package_sync "$1"
}


# TODO: This should be replaced by freshclam_update from atomicorp-functions
function update_clamav_signatures() {

	# if DEBUG is 1 or higher, then log the output of the signature update
	if [[ $DEBUG -ge 1 ]]; then
		log_event "upgrade (DEBUG1): in update_clamav_signatures function"
	fi


	if [ ! -d /var/lib/clamav ]; then
		mkdir -p /var/lib/clamav
	fi

	/var/ossec/modules/clamav/freshclam 
	RETVAL=$?
	if [ $RETVAL -eq 0 ]; then
		log_event "upgrade: clamav signature update: success"
	else
		log_event "upgrade: clamav signature update: failure"
	fi

}

function configure_nightly_updates() {
	local key_val="$1"
	
	# if DEBUG is 1 or higher, then log the output
	if [[ $DEBUG -ge 1 ]]; then
		log_event "config (DEBUG1): in configure_nightly_updates function with key_val: ${key_val}"
	fi

	# Parse the key=value pair
	KEY=$(echo "$key_val" | awk -F'=' '{print $1}')
	VALUE=$(echo "$key_val" | awk -F'=' '{print $2}')

	# Create the config file for this agent
	CONFIG_FILE="/var/ossec/etc/nightly-update-config.json"
	
	# Initialize config file if it doesn't exist
	if [[ ! -f "$CONFIG_FILE" ]]; then
		echo "{}" > "$CONFIG_FILE"
	fi
	
	# Handle different configuration keys
	case "$KEY" in
		"ossec")
			if [[ "$VALUE" == "true" ]]; then
				# Use jq to update the JSON config with temp file
				jq '.ossec = true' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
				log_event "config: nightly ossec updates enabled"
			elif [[ "$VALUE" == "false" ]]; then
				jq '.ossec = false' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
				log_event "config: nightly ossec updates disabled"
			else
				log_event "config: invalid value for ossec: ${VALUE}"
				return 1
			fi
			;;
		"antivirus")
			if [[ "$VALUE" == "true" ]]; then
				jq '.antivirus = true' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
				log_event "config: nightly antivirus updates enabled"
			elif [[ "$VALUE" == "false" ]]; then
				jq '.antivirus = false' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
				log_event "config: nightly antivirus updates disabled"
			else
				log_event "config: invalid value for antivirus: ${VALUE}"
				return 1
			fi
			;;

		*)
			log_event "config: unknown key: ${KEY}"
			return 1
			;;
	esac

	if [ $? -eq 0 ]; then
		log_event "config: nightly updates configuration updated"
	else
		log_event "config: failed to update nightly updates configuration"
		return 1
	fi

	# Check if both ossec and clamav are set to false, and if so, delete the config file
	if [[ -f "$CONFIG_FILE" ]]; then
		OSSEC_VAL=$(jq -r '.ossec // empty' "$CONFIG_FILE")
		ANTIVIRUS_VAL=$(jq -r '.antivirus // empty' "$CONFIG_FILE")
		if [[ "$OSSEC_VAL" == "false" && "$ANTIVIRUS_VAL" == "false" ]]; then
			rm -f "$CONFIG_FILE"
			log_event "config: nightly updates disabled for both ossec and antivirus, config file removed"
		fi
	fi
}





function init_clamav() {
	if [[ $PKG == "rpm" ]]; then
		# EPEL version
		if [ -f /etc/clamd.d/scan.conf ]; then
			cp /var/ossec/modules/clamav/template/clamd.conf.template /etc/clamd.d/scan.conf
			EPEL=1
			sed -i "s/^LogFile/#LogFile/g" /etc/clamd.d/scan.conf
			sed -i "s/^PidFile/#PidFile/g" /etc/clamd.d/scan.conf
			# This only needs the TCP settings for the default to work
		elif [ -f /etc/clamd.conf ]; then
			cp /var/ossec/modules/clamav/template/clamd.conf.template /etc/clamd.conf
			cp -f /var/ossec/modules/clamav/template/freshclam.conf.template /etc/freshclam.conf
		fi

		update_clamav_signatures
		# Settings for real-time
		/var/ossec/modules/clamav/clam-module.sh

		if [[ $EPEL -eq 1 ]]; then
			systemctl enable clamd@scan
			systemctl start clamd@scan
		else
			systemctl enable clamav-daemon
			systemctl start clamav-daemon
		fi
		
	elif [[ $PKG == "deb" ]]; then
		cp -f /var/ossec/modules/clamav/template/clamd.conf.template /etc/clamav/clamd.conf
		cp -f /var/ossec/modules/clamav/template/freshclam.conf.template /etc/freshclam.conf /etc/clamav/freshclam.conf
		update_clamav_signatures
		# Settings for real-time
		/var/ossec/modules/clamav/clam-module.sh
		systemctl enable clamav-daemon
		systemctl start clamav-daemon

	else
		log_event "init_clamav: platform not supported "
		exit 1
	fi
	log_event "init_clamav: platform initialized "
}


function install_clamav() {
	check_repository
	# This isnt super important since  awp-agent has a Requires on clam now
	if [[ $PKG == "rpm" ]]; then
		if [[ "$DIST" != "amazon" ]]; then
			install_package epel-release  
			if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
				exit 1
			fi
			
		fi
		install_package "clamav clamd clamav-update"
		if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
			exit 1
		fi
		
	elif [[ $PKG == "deb" ]]; then
		install_package clamav-daemon
	else
		log_event "install: ${PACKAGE} platform not supported ($PKG)"
		exit 1
	fi

}


function init_module () {
	MODULE=$1
	log_event "init: ${MODULE} start"

	if [[ "${MODULE}" == "auditd" ]]; then
		if [ -d /etc/auditd/rules.d ]; then
			RULES_DIR=/etc/auditd/rules.d
		elif [ -d /etc/audit/rules.d ]; then
			RULES_DIR=/etc/audit/rules.d
		fi

		# if RULES_DIR is not set, then we dont have a supported version of auditd
		if [ -z $RULES_DIR ]; then
			log_event "init: ${MODULE} auditd not detected"
			exit
		fi

		# copy the rules from /var/ossec/modules/auditd/* to $RULES_DIR
		cp -f /var/ossec/modules/auditd/* $RULES_DIR

		# restart auditd
		service auditd restart
		if [ $? -eq 0 ]; then
			log_event "init: ${MODULE} auditd restart:success"
		else
			log_event "init: ${MODULE} auditd restart:failure"
		fi


	fi

	if [[ "${MODULE}" == "clamav" ]]; then
		init_clamav
	fi

	if [[ "${MODULE}" == "fapolicyd" ]]; then
		init_fapolicyd
	fi
	log_event "init: ${MODULE} end"
}

function detect_webserver() {
	WEBSERVER=none
	if [[ $PKG == "rpm" ]]; then
		# Stub	
		if rpm -q httpd; then
			WEBSERVER=apache
		fi
		# TODO: is it running

	
	elif [[ $PKG == "deb" ]]; then
	
		# Stub
		if dpkg -s apache2 >/dev/null ; then
			WEBSERVER=apache
		fi
		# TODO: is it running
	fi


}

#function download_waf_rules() {
#	
#}
#
#function init_waf() {
#
#	# Set top level config
#
#}

function install_waf() {
	check_repository

	# Detect webserver type
	detect_webserver

	if  [[ $WEBSERVER == "none" ]]; then
		log_event "install: ${PACKAGE} webserver not detected"
		exit
	else
		log_event "install: ${PACKAGE} ${WEBSERVER} detected"
	fi


	
	# TODO: Test webserver, apachectl test, if its OK continue

        if [[ $PKG == "rpm" ]]; then
		if [[ $WEBSERVER == "apache" ]]; then
			install_package mod_security
		fi
		WEB_CONF_DIR=/etc/httpd/conf.d/
		
        elif [[ $PKG == "deb" ]]; then
		if [[ $WEBSERVER == "apache" ]]; then
			install_package libapache2-mod-security2
		fi
		WEB_CONF_DIR=/etc/apache2/conf-enabled/
        fi

	
	# el7
		# apache 2.4.6
			# mod_security.x86_64 2.9.2-1.el7
		# nginx 1.20.1
			# libmodsecurity.x86_64 3.0.2-6.el7, no configs
	# el8
		# apache 2.4.37
			# mod_security.x86_64 2.9.2-9.el8
		# module nginx, no libmodsecurity
		
	# el9
		# apache 2.4.51
			# mod_security 2.9.3-12

		# module nginx, no libmodsecurity

	# amzn2

		# apache 2.4.54
			# mod_security 2.9.3

		# nginx 1.20.0 - no modsecurity
			# Weird setup to get the repo too, it adds repos
			#amazon-linux-extras enable nginx1
			
	# u16
		# apache 2.4.18 
			# libapache2-mod-security2  2.9.0-1
		# nginx 1.10.3, no libmodsec

	# u18
		# apache 2.4.29
			# libapache2-mod-security2 2.9.2-1
		# nginx 1.14.0, no libmodsec

	# u20 
		# apache2-bin 2.4.41
			# libapache2-mod-security2 2.9.3
		# nginx 1.18.0
			# libmodsecurity 3.0.4-1, no configs

	# u22
		# apache 2.4.52
			# libapache2-mod-security2 2.9.5-1
		# nginx 1.18.0
			# libmodsecurity3 3.0.6-1 , no configs
}

# Create a restart_ossec function
function restart_ossec() {
	log_event "restart: ${PACKAGE} start"
	/var/ossec/bin/ossec-control reload
	log_event "restart: ${PACKAGE} end Exit Code: ($?)"
}


# Create restart_module function for  the values of clamav or ossec-hids
function restart_module() {
	MODULE=$1
	if [[ "${MODULE}" == "clamav" ]]; then
		log_event "restart: ${MODULE} start"
		restart_clamav
		log_event "restart: ${MODULE} end"
	elif [[ "${MODULE}" == "ossec-hids" ]]; then
		log_event "restart: ${MODULE} start"
		restart_ossec
		log_event "restart: ${MODULE} end"
	fi

}

# createa function that backs up ossec.conf to a random filename, changes <start-on-scan>yes</start-on-scan> to <start-on-scan>no</start-on-scan> in ossec.conf, and then restores it
function disable_ossec_on_scan() {
	log_event "disable: ${PACKAGE} start"
	# backup ossec.conf
	cp /var/ossec/etc/ossec.conf /var/ossec/etc/ossec.conf.$(date +%s)
	# change <start-on-scan>yes</start-on-scan> to <start-on-scan>no</start-on-scan>
	sed -i 's/<start-on-scan>yes<\/start-on-scan>/<start-on-scan>no<\/start-on-scan>/g' /var/ossec/etc/ossec.conf
	# restore ossec.conf
	cp /var/ossec/etc/ossec.conf.$(date +%s) /var/ossec/etc/ossec.conf
	log_event "disable: ${PACKAGE} end"
}


# Centos 7 hardening

# Create function to install fapolicyd
function install_fapolicyd() {
	check_repository
	log_event "install: ${PACKAGE} start"
	install_package fapolicyd
	log_event "install: ${PACKAGE} end"
}

# Create function to initalize fapolicyd
function init_fapolicyd() {
	log_event "init: ${PACKAGE} start"
	# Change /etc/fapolicyd/rules.d/90-deny-execute.rules deny_audit to deny_log
	sed -i 's/deny_audit/deny_log/g' /etc/fapolicyd/rules.d/90-deny-execute.rules
	# start fapolicyd
	systemctl start fapolicyd
	# enable fapolicyd
	systemctl enable fapolicyd
}

is_stdin_available() {
    if [ -t 0 ]; then
        return 1 # stdin is empty (terminal)
    else
        return 0 # stdin has data
    fi
}

function show_help() {
	echo
	echo "Atomicorp-API Version: ${VERSION}"
	echo "Usage: $0 [add|delete] [username] [package] [scanid]"
	echo
	echo "  Actions"
	echo "    install  - install module"
	echo "    upgrade  - upgrade module"
	echo "    init     - initialize module"
	echo "    restart  - restart module"
	echo
}


#############
# Main
#############
name=$(basename "$0")
set +o pipefail 2>/dev/null || true

if [[ "${1:-}" == "__upgrade_worker__" ]]; then
	shift
	PKGS="${1:-}"
	if [[ -z "$PKGS" ]]; then
		log_event "remote upgrade: worker invoked with empty package list"
		finish_upgrade_state 2
		exit 2
	fi
	detect_dist
	log_event "remote upgrade: worker running for (${PKGS})"
	upgrade_package_sync "$PKGS"
	rv=$?
	finish_upgrade_state "$rv"
	log_event "remote upgrade: worker finished exit=${rv}"
	exit "$rv"
fi

ATOMICORP_API_JSON_MODE=0
if [ "$1" = "add" ] || [ "$1" = "delete" ]; then
	ACTION=$1
	shift
	shift
	ARGS="$@"
else
	ATOMICORP_API_JSON_MODE=1
	# ossec-execd: one JSON line on stdin (see awp-firewall-drop.sh)
	IFS= read -r line || exit 0
	[ -n "$line" ] || exit 0

	if ! command -v jq >/dev/null 2>&1; then
		echo "$name: jq required; skipping." >&2
		printf '%s\n' '{"version":1,"origin":{"name":"atomicorp-api","module":"script"},"command":"continue","parameters":{}}'
		read -r phase2 || true
		exit 0
	fi

	mapfile -t fields < <(jq -r '
	def s: if . == null then "" else tostring end;
	(.command | s),
	((.parameters.alert.data.srcip // .parameters.alert.srcip) | s),
	(.parameters.alert.rule.id | s),
	(.parameters.alert.id | s)
' <<<"$line") || true

	COMMAND=${fields[0]:-}
	SRCIP=${fields[1]:-}
	[[ "$COMMAND" = "null" ]] && COMMAND=""
	[[ "$SRCIP" = "null" ]] && SRCIP=""

	# Command-driven API vs srcip fallback (legacy behavior)
	if [[ "$COMMAND" =~ upgrade:* ]] || [[ "$COMMAND" =~ config:* ]] || [[ "$COMMAND" =~ restart:* ]]; then
		ARGS="$COMMAND"
	else
		ARGS="$SRCIP"
	fi

	# Dedup key for execd timeout list (per distinct operation string)
	DEDUP_KEY="$ARGS"
	if [ -z "$DEDUP_KEY" ]; then
		DEDUP_KEY="${COMMAND:-atomicorp-api}"
	fi

	# Repeat-offender / dedup handshake (execd sends continue|abort on phase2)
	jq -n --arg name "$name" --arg k "$DEDUP_KEY" \
		'{version:1, origin:{name:$name, module:"active-response"}, command:"check_keys", parameters:{keys:[$k]}}'
	read -r phase2 || true
	if [ "$(jq -r '.command // empty' <<<"$phase2" 2>/dev/null)" = "abort" ]; then
		exit 0
	fi
fi

if [[ $DEBUG -ge 1 ]]; then
	log_event "upgrade (DEBUG1): calling atomicorp-api"
fi


detect_dist

if [[ "$ARGS" == "install"* ]]; then

	PACKAGE=$(echo $ARGS | awk -F: '{print $2}' |awk '{print $1}')
	SCANID=$(echo $ARGS |awk -Fscanid: '{print $2}' |awk '{print $1}')

	#Allowed package list
	if [[ ${PACKAGE} =~ auditd* ]]; then
		log_event "install: ${PACKAGE} start"
		install_package auditd
	fi

	if [[ ${PACKAGE} =~ clamav ]]; then
		log_event "install: ${PACKAGE} start"
		install_clamav
	fi

	if [[ ${PACKAGE} =~ waf ]]; then
		log_event "install: ${PACKAGE} start"
		install_waf apache
	fi

	# if PACKAGE is fapolicyd, install fapolicyd
	if [[ ${PACKAGE} =~ fapolicyd ]]; then
		log_event "install: ${PACKAGE} start"
		install_fapolicyd
	fi

elif [[  "$ARGS" == "init"* ]]; then

	PACKAGE=$(echo $ARGS | awk -F: '{print $2}' |awk '{print $1}')
	MODULE=$(echo $ARGS | awk -F: '{print $2}' |awk '{print $1}')
	init_module ${MODULE} 

elif [[  "$ARGS" == "upgrade"* ]]; then
	PACKAGE=$(echo $ARGS | awk -F: '{print $2}' |awk '{print $1}')

	if [[ ${PACKAGE} =~ ossec-hids* ]]; then
		log_event "upgrade: ${PACKAGE} start"
		request_ossec_agent_upgrade
    fi
	if [[ ${PACKAGE} =~ clamav* ]]; then
       	log_event "upgrade: ${PACKAGE} start"
		update_clamav_signatures
	fi

	if [[ ${PACKAGE} =~ user-check-ossec* ]]; then
		log_event "upgrade: ${PACKAGE} start"
		request_ossec_agent_upgrade
	fi

	if [[ ${PACKAGE} =~ user-check-clam* ]]; then
		log_event "upgrade: ${PACKAGE} start"
		update_clamav_signatures
	fi

	if [[ ${PACKAGE} =~ user-check-all* ]]; then
		log_event "upgrade: ${PACKAGE} start"
		update_clamav_signatures
		request_ossec_agent_upgrade
		
	fi

# Configuration commands
elif [[  "$ARGS" == "config"* ]]; then
	KEY_VAL=$(echo $ARGS | awk -F: '{print $2}')
	
	log_event "config: ${KEY_VAL} start"
	configure_nightly_updates "${KEY_VAL}"

# Restart module
elif [[  "$ARGS" == "restart"* ]]; then
	PACKAGE=$(echo $ARGS | awk -F: '{print $2}' |awk '{print $1}')
	if [[ ${PACKAGE} =~ ossec-hids* ]]; then
		log_event "restart: ${PACKAGE} start"
		restart_module ossec-hids-agent
	fi

# Show help
else
	show_help

fi
