#!/usr/bin/env bash
#
# Copyright (C) 2021 The Falco Authors.
#
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Simple script that desperately tries to load the kernel instrumentation by
# looking for it in a bunch of ways. Convenient when running Falco inside
# a container or in other weird environments.
#

#
# Returns 1 if $cos_ver > $base_ver, 0 otherwise
#
cos_version_greater() {
	if [[ $cos_ver == "${base_ver}" ]]; then
		return 0
	fi

	#
	# COS build numbers are in the format x.y.z
	#
	a=$(echo "${cos_ver}" | cut -d. -f1)
	b=$(echo "${cos_ver}" | cut -d. -f2)
	c=$(echo "${cos_ver}" | cut -d. -f3)

	d=$(echo "${base_ver}" | cut -d. -f1)
	e=$(echo "${base_ver}" | cut -d. -f2)
	f=$(echo "${base_ver}" | cut -d. -f3)

	# Test the first component
	if [[ $a -gt $d ]]; then
		return 1
	elif [[ $d -gt $a ]]; then
		return 0
	fi

	# Test the second component
	if [[ $b -gt $e ]]; then
		return 1
	elif [[ $e -gt $b ]]; then
		return 0
	fi

	# Test the third component
	if [[ $c -gt $f ]]; then
		return 1
	elif [[ $f -gt $c ]]; then
		return 0
	fi

	# If we get here, probably malformatted version string?

	return 0
}

get_kernel_config() {
	if [ -f /proc/config.gz ]; then
		echo "* Found kernel config at /proc/config.gz"
		KERNEL_CONFIG_PATH=/proc/config.gz
	elif [ -f "/boot/config-${KERNEL_RELEASE}" ]; then
		echo "* Found kernel config at /boot/config-${KERNEL_RELEASE}"
		KERNEL_CONFIG_PATH=/boot/config-${KERNEL_RELEASE}
	elif [ -n "${HOST_ROOT}" ] && [ -f "${HOST_ROOT}/boot/config-${KERNEL_RELEASE}" ]; then
		echo "* Found kernel config at ${HOST_ROOT}/boot/config-${KERNEL_RELEASE}"
		KERNEL_CONFIG_PATH="${HOST_ROOT}/boot/config-${KERNEL_RELEASE}"
	elif [ -f "/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" ]; then
		echo "* Found kernel config at /usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
		KERNEL_CONFIG_PATH="/usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
	elif [ -n "${HOST_ROOT}" ] && [ -f "${HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" ]; then
		echo "* Found kernel config at ${HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
		KERNEL_CONFIG_PATH="${HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
	elif [ -f "/lib/modules/${KERNEL_RELEASE}/config" ]; then
		# This code works both for native host and containers assuming that
		# Dockerfile sets up the desired symlink /lib/modules -> $HOST_ROOT/lib/modules
		echo "* Found kernel config at /lib/modules/${KERNEL_RELEASE}/config"
		KERNEL_CONFIG_PATH="/lib/modules/${KERNEL_RELEASE}/config"
	fi

	if [ -z "${KERNEL_CONFIG_PATH}" ]; then
		>&2 echo "Cannot find kernel config"
		exit 1
	fi

	if [[ "${KERNEL_CONFIG_PATH}" == *.gz ]]; then
		HASH=$(zcat "${KERNEL_CONFIG_PATH}" | md5sum - | cut -d' ' -f1)
	else
		HASH=$(md5sum "${KERNEL_CONFIG_PATH}" | cut -d' ' -f1)
	fi
}

get_target_id() {
	if [ -f "${HOST_ROOT}/etc/os-release" ]; then
		# freedesktop.org and systemd
		# shellcheck source=/dev/null
		source "${HOST_ROOT}/etc/os-release"
		OS_ID=$ID
	elif [ -f "${HOST_ROOT}/etc/debian_version" ]; then
		# Older debian distros
		# fixme > Can this happen on older Ubuntu?
		OS_ID=debian
	elif [ -f "${HOST_ROOT}/etc/centos-release" ]; then
		# Older CentOS distros
		OS_ID=centos
	elif [ -f "${HOST_ROOT}/etc/VERSION" ]; then
		OS_ID=minikube
	else
		>&2 echo "Detected an unsupported target system, please get in touch with the Falco community"
		exit 1
	fi

	case "${OS_ID}" in
	("amzn")
		if [[ $VERSION_ID == "2" ]]; then
			TARGET_ID="amazonlinux2"
		else
			TARGET_ID="amazonlinux"
		fi
		;;
	("ubuntu")
		if [[ $KERNEL_RELEASE == *"aws"* ]]; then
			TARGET_ID="ubuntu-aws"
		else
			TARGET_ID="ubuntu-generic"
		fi
		;;
	(*)
		TARGET_ID=$(echo "${OS_ID}" | tr '[:upper:]' '[:lower:]')
		;;
	esac
}

load_kernel_module_compile() {
	# Skip dkms on UEK hosts because it will always fail
	if [[ $(uname -r) == *uek* ]]; then
		>&2 echo "Skipping because the dkms install always fail (on UEK hosts)"
		return
	fi

	if ! hash dkms >/dev/null 2>&1; then
		>&2 echo "This program requires dkms"
		return
	fi

	# Try to compile using all the available gcc versions
	for CURRENT_GCC in $(which gcc) $(ls "$(dirname "$(which gcc)")"/gcc-* | grep 'gcc-[0-9]\+' | sort -n -r -k 2 -t -); do
		echo "* Trying to dkms install ${DRIVER_NAME} module with GCC ${CURRENT_GCC}"
		echo "#!/usr/bin/env bash" > /tmp/scap-dkms-make
		echo "make CC=${CURRENT_GCC} \$@" >> /tmp/scap-dkms-make
		chmod +x /tmp/scap-dkms-make
		if dkms install --directive="MAKE='/tmp/scap-dkms-make'" -m "${DRIVER_NAME}" -v "${DRIVER_VERSION}" -k "${KERNEL_RELEASE}" 2>/dev/null; then
			echo "* ${DRIVER_NAME} module installed in dkms, trying to insmod"
			chcon -t modules_object_t "/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}.ko" > /dev/null 2>&1 || true
			chcon -t modules_object_t "/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}.ko.xz" > /dev/null 2>&1 || true
			if insmod "/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}.ko" > /dev/null 2>&1; then
				echo "* Success: ${DRIVER_NAME} module found and loaded in dkms"
				exit 0
			elif insmod "/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${DRIVER_NAME}.ko.xz" > /dev/null 2>&1; then
				echo "* Success: ${DRIVER_NAME} module found and loaded in dkms (xz)"
				exit 0
			else
				echo "* Unable to insmod ${DRIVER_NAME} module"
			fi
		else
			DKMS_LOG="/var/lib/dkms/${DRIVER_NAME}/${DRIVER_VERSION}/build/make.log"
			if [ -f "${DKMS_LOG}" ]; then
				echo "* Running dkms build failed, dumping ${DKMS_LOG} (with GCC ${CURRENT_GCC})"
				cat "${DKMS_LOG}"
			else
				echo "* Running dkms build failed, couldn't find ${DKMS_LOG} (with GCC ${CURRENT_GCC})"
			fi
		fi
	done
}

load_kernel_module_download() {
	get_target_id

	local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"

	local URL
	URL=$(echo "${DRIVERS_REPO}/${DRIVER_VERSION}/${FALCO_KERNEL_MODULE_FILENAME}" | sed s/+/%2B/g)

	echo "* Trying to download a prebuilt ${DRIVER_NAME} module from ${URL}"
	if curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.scap/${FALCO_KERNEL_MODULE_FILENAME}" "${URL}"; then
		echo "* Download succeeded"
		chcon -t modules_object_t "${HOME}/.scap/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
		insmod "${HOME}/.scap/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted"
		exit $?
	else
		>&2 echo "Unable to find a prebuilt ${DRIVER_NAME} module"
		return
	fi
}

load_kernel_module() {
	if ! hash lsmod > /dev/null 2>&1; then
		>&2 echo "This program requires lsmod"
		exit 1
	fi

	if ! hash modprobe > /dev/null 2>&1; then
		>&2 echo "This program requires modprobe"
		exit 1
	fi

	if ! hash rmmod > /dev/null 2>&1; then
		>&2 echo "This program requires rmmod"
		exit 1
	fi

	echo "* Unloading ${DRIVER_NAME} module, if present"
	rmmod "${DRIVER_NAME}" 2>/dev/null
	WAIT_TIME=0
	KMOD_NAME=$(echo "${DRIVER_NAME}" | tr "-" "_")
	while lsmod | cut -d' ' -f1 | grep -qx "${KMOD_NAME}" && [ $WAIT_TIME -lt "${MAX_RMMOD_WAIT}" ]; do
		if rmmod "${DRIVER_NAME}" 2>/dev/null; then
			echo "* Unloading ${DRIVER_NAME} module succeeded after ${WAIT_TIME}s"
			break
		fi
		((++WAIT_TIME))
		if (( WAIT_TIME % 5 == 0 )); then
			echo "* ${DRIVER_NAME} module still loaded, waited ${WAIT_TIME}s (max wait ${MAX_RMMOD_WAIT}s)"
		fi
		sleep 1
	done

	if lsmod | cut -d' ' -f1 | grep -qx "${KMOD_NAME}" > /dev/null 2>&1; then
		echo "* ${DRIVER_NAME} module seems to still be loaded, hoping the best"
		exit 0
	fi

	echo "* Trying to load a system ${DRIVER_NAME} module, if present"
	if modprobe "${DRIVER_NAME}" > /dev/null 2>&1; then
		echo "* Success: ${DRIVER_NAME} module found and loaded with modprobe"
		exit 0
	fi

	echo "* Looking for a ${DRIVER_NAME} module locally (kernel ${KERNEL_RELEASE})"

	get_target_id

	local FALCO_KERNEL_MODULE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.ko"

	if [ -f "${HOME}/.scap/${FALCO_KERNEL_MODULE_FILENAME}" ]; then
		echo "* Found a prebuilt ${DRIVER_NAME} module at ${HOME}/.scap/${FALCO_KERNEL_MODULE_FILENAME}, loading it"
		chcon -t modules_object_t "${HOME}/.scap/${FALCO_KERNEL_MODULE_FILENAME}" > /dev/null 2>&1 || true
		insmod "${HOME}/.scap/${FALCO_KERNEL_MODULE_FILENAME}" && echo "* Success: ${DRIVER_NAME} module found and inserted"
		exit $?
	fi

	if [ -n "$ENABLE_DOWNLOAD" ]; then
		load_kernel_module_download
	fi

	if [ -n "$ENABLE_COMPILE" ]; then
		load_kernel_module_compile
	fi

	# Not able to download a prebuilt module nor to compile one on-the-fly
	>&2 echo "Consider compiling your own ${DRIVER_NAME} driver and loading it or getting in touch with the Falco community"
	exit 1
}

clean_kernel_module() {
	if ! hash lsmod > /dev/null 2>&1; then
		>&2 echo "This program requires lsmod"
		exit 1
	fi

	if ! hash rmmod > /dev/null 2>&1; then
		>&2 echo "This program requires rmmod"
		exit 1
	fi

	KMOD_NAME=$(echo "${DRIVER_NAME}" | tr "-" "_")
	if lsmod | cut -d' ' -f1 | grep -qx "${KMOD_NAME}"; then
		if rmmod "${DRIVER_NAME}" 2>/dev/null; then
			echo "* Unloading ${DRIVER_NAME} module succeeded"
		else
			echo "* Unloading ${DRIVER_NAME} module failed"
		fi
	else
		echo "* There is no ${DRIVER_NAME} module loaded"
	fi

	if ! hash dkms >/dev/null 2>&1; then
		echo "* Skipping dkms remove (dkms not found)"
		return
	fi

	DRIVER_VERSIONS=$(dkms status -m "${DRIVER_NAME}" | cut -d',' -f2 | sed -e 's/^[[:space:]]*//')
	if [ -z "${DRIVER_VERSIONS}" ]; then
		echo "* There is no ${DRIVER_NAME} module in dkms"
		return
	fi
	for CURRENT_VER in ${DRIVER_VERSIONS}; do
		if dkms remove -m "${DRIVER_NAME}" -v "${CURRENT_VER}" --all 2>/dev/null; then
			echo "* Removing ${DRIVER_NAME}/${CURRENT_VER} succeeded"
		else
			echo "* Removing ${DRIVER_NAME}/${CURRENT_VER} failed"
			exit 1
		fi
	done
}

load_bpf_probe_compile() {
	local BPF_KERNEL_SOURCES_URL=""
	local STRIP_COMPONENTS=1

	customize_kernel_build() {
		if [ -n "${KERNEL_EXTRA_VERSION}" ]; then
			sed -i "s/LOCALVERSION=\"\"/LOCALVERSION=\"${KERNEL_EXTRA_VERSION}\"/" .config
		fi
		make olddefconfig > /dev/null
		make modules_prepare > /dev/null
	}

	if [ "${TARGET_ID}" == "cos" ]; then
		echo "* COS detected (build ${BUILD_ID}), using COS kernel headers"

		BPF_KERNEL_SOURCES_URL="https://storage.googleapis.com/cos-tools/${BUILD_ID}/kernel-headers.tgz"
		KERNEL_EXTRA_VERSION="+"
		STRIP_COMPONENTS=0

		customize_kernel_build() {
			pushd usr/src/* > /dev/null || exit

			# Note: this overrides the KERNELDIR set while untarring the tarball
			KERNELDIR=$(pwd)
			export KERNELDIR

			sed -i '/^#define randomized_struct_fields_start	struct {$/d' include/linux/compiler-clang.h
			sed -i '/^#define randomized_struct_fields_end	};$/d' include/linux/compiler-clang.h

			popd > /dev/null || exit

			# Might need to configure our own sources depending on COS version
			cos_ver=${BUILD_ID}
			base_ver=11553.0.0

			cos_version_greater
			greater_ret=$?

			if [[ greater_ret -eq 1 ]]; then
			export KBUILD_EXTRA_CPPFLAGS=-DCOS_73_WORKAROUND
			fi
		}
	fi

	if [ "${TARGET_ID}" == "minikube" ]; then
		MINIKUBE_VERSION="$(cat "${HOST_ROOT}/etc/VERSION")"
		echo "* Minikube detected (${MINIKUBE_VERSION}), using linux kernel sources for minikube kernel"
		local kernel_version
		kernel_version=$(uname -r)
		local -r kernel_version_major=$(echo "${kernel_version}" | cut -d. -f1)
		local -r kernel_version_minor=$(echo "${kernel_version}" | cut -d. -f2)
		local -r kernel_version_patch=$(echo "${kernel_version}" | cut -d. -f3)

		if [ "${kernel_version_patch}" == "0" ]; then
			kernel_version="${kernel_version_major}.${kernel_version_minor}"
		fi

		BPF_KERNEL_SOURCES_URL="http://mirrors.edge.kernel.org/pub/linux/kernel/v${kernel_version_major}.x/linux-${kernel_version}.tar.gz"
	fi

	if [ -n "${BPF_USE_LOCAL_KERNEL_SOURCES}" ]; then
		local -r kernel_version_major=$(uname -r | cut -d. -f1)
		local -r kernel_version=$(uname -r | cut -d- -f1)
		KERNEL_EXTRA_VERSION="-$(uname -r | cut -d- -f2)"

		echo "* Using downloaded kernel sources for kernel version ${kernel_version}..."

		BPF_KERNEL_SOURCES_URL="http://mirrors.edge.kernel.org/pub/linux/kernel/v${kernel_version_major}.x/linux-${kernel_version}.tar.gz"
	fi

	if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then
		get_kernel_config

		echo "* Downloading ${BPF_KERNEL_SOURCES_URL}"

		mkdir -p /tmp/kernel
		cd /tmp/kernel || exit
		cd "$(mktemp -d -p /tmp/kernel)" || exit
		if ! curl -L -o kernel-sources.tgz --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" "${BPF_KERNEL_SOURCES_URL}"; then
			>&2 echo "Unable to download the kernel sources"
			return
		fi

		echo "* Extracting kernel sources"

		mkdir kernel-sources && tar xf kernel-sources.tgz -C kernel-sources --strip-components "${STRIP_COMPONENTS}"

		cd kernel-sources || exit
		KERNELDIR=$(pwd)
		export KERNELDIR

		if [[ "${KERNEL_CONFIG_PATH}" == *.gz ]]; then
			zcat "${KERNEL_CONFIG_PATH}" > .config
		else
			cat "${KERNEL_CONFIG_PATH}" > .config
		fi

		echo "* Configuring kernel"
		customize_kernel_build
	fi

	echo "* Trying to compile the eBPF probe (${BPF_PROBE_FILENAME})"

	make -C "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf" > /dev/null

	mkdir -p "${HOME}/.scap"
	mv "/usr/src/${DRIVER_NAME}-${DRIVER_VERSION}/bpf/probe.o" "${HOME}/.scap/${BPF_PROBE_FILENAME}"

	if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then
		rm -r /tmp/kernel
	fi

}

load_bpf_probe_download() {
	local URL
	URL=$(echo "${DRIVERS_REPO}/${DRIVER_VERSION}/${BPF_PROBE_FILENAME}" | sed s/+/%2B/g)

	echo "* Trying to download a prebuilt eBPF probe from ${URL}"

	if ! curl -L --create-dirs "${FALCO_DRIVER_CURL_OPTIONS}" -o "${HOME}/.scap/${BPF_PROBE_FILENAME}" "${URL}"; then
		>&2 echo "Unable to find a prebuilt ${DRIVER_NAME} eBPF probe"
		return
	fi
}

load_bpf_probe() {
	echo "* Mounting debugfs"

	if [ ! -d /sys/kernel/debug/tracing ]; then
		mount -t debugfs nodev /sys/kernel/debug
	fi

	get_target_id

	BPF_PROBE_FILENAME="${DRIVER_NAME}_${TARGET_ID}_${KERNEL_RELEASE}_${KERNEL_VERSION}.o"

	if [ -n "$ENABLE_DOWNLOAD" ]; then
		if [ -f "${HOME}/.scap/${BPF_PROBE_FILENAME}" ]; then
			echo "* Skipping download, eBPF probe is already present in ${HOME}/.scap/${BPF_PROBE_FILENAME}"
		else
			load_bpf_probe_download
		fi
	fi

	if [ -n "$ENABLE_COMPILE" ]; then
		if [ -f "${HOME}/.scap/${BPF_PROBE_FILENAME}" ]; then
			echo "* Skipping compilation, eBPF probe is already present in ${HOME}/.scap/${BPF_PROBE_FILENAME}"
		else
			load_bpf_probe_compile
		fi
	fi

	if [ -f "${HOME}/.scap/${BPF_PROBE_FILENAME}" ]; then
		echo "* eBPF probe located in ${HOME}/.scap/${BPF_PROBE_FILENAME}"

		if [ ! -f /proc/sys/net/core/bpf_jit_enable ] || [ $(cat /proc/sys/net/core/bpf_jit_enable) -eq 0 ]; then
			echo "**********************************************************"
			echo "** BPF doesn't have JIT enabled, performance might be   **"
			echo "** degraded. Please ensure to run on a kernel with      **"
			echo "** CONFIG_BPF_JIT enabled and/or use --net=host if      **"
			echo "** running inside a container.                          **"
			echo "**********************************************************"
		fi

		ln -sf "${HOME}/.scap/${BPF_PROBE_FILENAME}" "${HOME}/.scap/${DRIVER_NAME}-bpf.o" \
			&& echo "* Success: eBPF probe symlinked to ${HOME}/.scap/${DRIVER_NAME}-bpf.o"
		exit $?
	else
		>&2 echo "Unable to load the ${DRIVER_NAME} eBPF probe"
		exit 1
	fi
}

print_usage() {
	echo ""
	echo "Usage:"
	echo "  scap-driver-loader [driver] [options]"
	echo ""
	echo "Available drivers:"
	echo "  module        kernel module (default)"
	echo "  bpf           eBPF probe"
	echo ""
	echo "Options:"
	echo "  --help         show brief help"
	echo "  --clean        try to remove an already present driver installation"
	echo "  --compile      try to compile the driver locally (default true)"
	echo "  --download     try to download a prebuilt driver (default true)"
	echo "  --source-only  skip execution and allow sourcing in another script"
	echo ""
	echo "Environment variables:"
	echo "  DRIVER_REPO              specify a different URL where to look for prebuilt Falco drivers"
	echo "  DRIVER_NAME              specify a different name for the driver"
	echo "  DRIVER_INSECURE_DOWNLOAD whether you want to allow insecure downloads or not"
	echo ""
	echo "Versions:"
	echo "  Driver version ${DRIVER_VERSION}"
	echo ""
}

ARCH=$(uname -m)

KERNEL_RELEASE=$(uname -r)

if ! hash sed > /dev/null 2>&1; then
	>&2 echo "This program requires sed"
	exit 1
fi
KERNEL_VERSION=$(uname -v | sed 's/#\([[:digit:]]\+\).*/\1/')

DRIVERS_REPO=${DRIVERS_REPO:-"@DRIVERS_REPO@"}

if [ -n "$DRIVER_INSECURE_DOWNLOAD" ]
then
	FALCO_DRIVER_CURL_OPTIONS=-fsSk
else
	FALCO_DRIVER_CURL_OPTIONS=-fsS
fi

if [[ -z "$MAX_RMMOD_WAIT" ]]; then
	MAX_RMMOD_WAIT=60
fi

DRIVER_VERSION="@DRIVER_VERSION@"
DRIVER_NAME=${DRIVER_NAME:-"@DRIVER_NAME@"}

DRIVER="module"
if [ -v FALCO_BPF_PROBE ]; then
	DRIVER="bpf"
fi

ENABLE_COMPILE=
ENABLE_DOWNLOAD=

clean=
has_args=
has_opts=
source_only=
while test $# -gt 0; do
	case "$1" in
		module|bpf)
			if [ -n "$has_args" ]; then
				>&2 echo "Only one driver per invocation"
				print_usage
				exit 1
			else
				DRIVER="$1"
				has_args="true"
				shift
			fi
			;;
		-h|--help)
			print_usage
			exit 0
			;;
		--clean)
			clean="true"
			shift
			;;
		--compile)
			ENABLE_COMPILE="yes"
			has_opts="true"
			shift
			;;
		--download)
			ENABLE_DOWNLOAD="yes"
			has_opts="true"
			shift
			;;
		--source-only)
			source_only="true"
			shift
			;;
		--*)
			>&2 echo "Unknown option: $1"
			print_usage
			exit 1
			;;
		*)
			>&2 echo "Unknown driver: $1"
			print_usage
			exit 1
			;;
	esac
done

if [ -z "$has_opts" ]; then
	ENABLE_COMPILE="yes"
	ENABLE_DOWNLOAD="yes"
fi

if [ -z "$source_only" ]; then
	echo "* Running scap-driver-loader for: driver version=${DRIVER_VERSION}"

	if [ "$(id -u)" != 0 ]; then
		>&2 echo "This program must be run as root (or with sudo)"
		exit 1
	fi

	if [ -n "$clean" ]; then
		if [ -n "$has_opts" ]; then
			>&2 echo "Cannot use --clean with other options"
			exit 1
		fi

		echo "* Running scap-driver-loader with: driver=$DRIVER, clean=yes"
		case $DRIVER in
		module)
			clean_kernel_module
			;;
		bpf)
			>&2 echo "--clean not supported for driver=bpf"
			exit 1
		esac
	else
		if ! hash curl > /dev/null 2>&1; then
			>&2 echo "This program requires curl"
			exit 1
		fi

		echo "* Running scap-driver-loader with: driver=$DRIVER, compile=${ENABLE_COMPILE:-"no"}, download=${ENABLE_DOWNLOAD:-"no"}"
		case $DRIVER in
			module)
				load_kernel_module
				;;
			bpf)
				load_bpf_probe
				;;
		esac
	fi
fi
