text: #!/bin/bash
  #
  # Copyright (C) 2013-2018 Draios Inc dba Sysdig.
  #
  # This file is part of sysdig .
  #
  # 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 sysdig-probe looking
  # for it in a bunch of ways. Convenient when running sysdig 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 malformed 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 [ ! -z "${SYSDIG_HOST_ROOT}" ] && [ -f "${SYSDIG_HOST_ROOT}/boot/config-${KERNEL_RELEASE}" ]; then
  		echo "Found kernel config at ${SYSDIG_HOST_ROOT}/boot/config-${KERNEL_RELEASE}"
  		KERNEL_CONFIG_PATH="${SYSDIG_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 [ ! -z "${SYSDIG_HOST_ROOT}" ] && [ -f "${SYSDIG_HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}" ]; then
  		echo "Found kernel config at ${SYSDIG_HOST_ROOT}/usr/lib/ostree-boot/config-${KERNEL_RELEASE}"
  		KERNEL_CONFIG_PATH="${SYSDIG_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 agent container assuming that
  		# Dockerfile sets up the desired symlink /lib/modules -> $SYSDIG_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
  		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
  }
  
  load_kernel_probe() {
  	if ! hash lsmod > /dev/null 2>&1; then
  		echo "This program requires lsmod"
  		exit 1
  	fi
  
  	if ! hash modprobe > /dev/null 2>&1; then
  		echo "This program requires modprobe"
  		exit 1
  	fi
  
  	if ! hash rmmod > /dev/null 2>&1; then
  		echo "This program requires rmmod"
  		exit 1
  	fi
  
  	echo "* Unloading ${PROBE_NAME}, if present"
  	rmmod "${PROBE_NAME}" 2>/dev/null
  	WAIT_TIME=0
  	KMOD_NAME=$(echo "${PROBE_NAME}" | tr "-" "_")
  	while lsmod | grep "${KMOD_NAME}" > /dev/null 2>&1 && [ $WAIT_TIME -lt $MAX_RMMOD_WAIT ]; do
  		if rmmod "${PROBE_NAME}" 2>/dev/null; then
  			echo "* Unloading ${PROBE_NAME} succeeded after ${WAIT_TIME}s"
  			break
  		fi
  		((++WAIT_TIME))
  		if (( $WAIT_TIME % 5 == 0 )); then
  			echo "* ${PROBE_NAME} still loaded, waited ${WAIT_TIME}s (max wait ${MAX_RMMOD_WAIT}s)"
  		fi
  		sleep 1
  	done
  
  	if lsmod | grep "${KMOD_NAME}" > /dev/null 2>&1; then
  		echo "* ${PROBE_NAME} seems to still be loaded, hoping the best"
  		exit 0
  	fi
  
  	# skip dkms on UEK hosts because it will always fail
  	if [[ $(uname -r) == *uek* ]]; then
  		echo "* Skipping dkms install for UEK host"
  	else
  		echo "* Running dkms install for ${PACKAGE_NAME}"
  		if dkms install -m "${PACKAGE_NAME}" -v "${SYSDIG_VERSION}" -k "${KERNEL_RELEASE}"; then
  			echo "* Trying to load a dkms ${PROBE_NAME}, if present"
  
  			if insmod "/var/lib/dkms/${PACKAGE_NAME}/${SYSDIG_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${PROBE_NAME}.ko" > /dev/null 2>&1; then
  				echo "${PROBE_NAME} found and loaded in dkms"
  				exit 0
  			elif insmod "/var/lib/dkms/${PACKAGE_NAME}/${SYSDIG_VERSION}/${KERNEL_RELEASE}/${ARCH}/module/${PROBE_NAME}.ko.xz" > /dev/null 2>&1; then
  				echo "${PROBE_NAME} found and loaded in dkms (xz)"
  				exit 0
  			else
  				echo "* Unable to insmod"
  			fi
  		else
  			DKMS_LOG="/var/lib/dkms/${PACKAGE_NAME}/${SYSDIG_VERSION}/build/make.log"
  			if [ -f "${DKMS_LOG}" ]; then
  				echo "* Running dkms build failed, dumping ${DKMS_LOG}"
  				cat "${DKMS_LOG}"
  			else
  				echo "* Running dkms build failed, couldn't find ${DKMS_LOG}"
  			fi
  		fi
  	fi
  
  	echo "* Trying to load a system ${PROBE_NAME}, if present"
  
  	if modprobe "${PROBE_NAME}" > /dev/null 2>&1; then
  		echo "${PROBE_NAME} found and loaded with modprobe"
  		exit 0
  	fi
  
  	echo "* Trying to find precompiled ${PROBE_NAME} for ${KERNEL_RELEASE}"
  
  	get_kernel_config
  
  	local SYSDIG_PROBE_FILENAME="${PROBE_NAME}-${SYSDIG_VERSION}-${ARCH}-${KERNEL_RELEASE}-${HASH}.ko"
  
  	if [ -f "${HOME}/.sysdig/${SYSDIG_PROBE_FILENAME}" ]; then
  		echo "Found precompiled module at ~/.sysdig/${SYSDIG_PROBE_FILENAME}, loading module"
  		insmod "${HOME}/.sysdig/${SYSDIG_PROBE_FILENAME}"
  		exit $?
  	fi
  
  	local URL
  	URL=$(echo "${SYSDIG_PROBE_URL}/${SYSDIG_REPOSITORY}/sysdig-probe-binaries/${SYSDIG_PROBE_FILENAME}" | sed s/+/%2B/g)
  
  	echo "* Trying to download precompiled module from ${URL}"
  
  	curl_out=$(curl --create-dirs "${SYSDIG_PROBE_CURL_OPTIONS}" -o "${HOME}/.sysdig/${SYSDIG_PROBE_FILENAME}" "${URL}" 2>&1)
  	if [ "$?" = "0" ]; then
  		echo "Download succeeded, loading module"
  		insmod "${HOME}/.sysdig/${SYSDIG_PROBE_FILENAME}"
  		exit $?
  	else
  		if [[ "$curl_out" =~ "404 Not Found" ]]; then
  			# The curl output throws a "curl: (22) The requested URL returned error: 404 Not Found" - but that is because the probe doesn't exist in the repo.
  			echo "Download of ${PROBE_NAME} for version ${SYSDIG_VERSION} failed. This is because the probe for this particular version does not exist in the repo."
  			if [ ! -z "${KERNEL_ERR_MESSAGE}" ]; then
  				echo "${KERNEL_ERR_MESSAGE}"
  			else
  				echo "Consider compiling your own ${PROBE_NAME} and loading it or getting in touch with the sysdig community"
  			fi
  		else
  			echo "$curl_out"
  		fi
  		exit 1
  	fi
  }
  
  load_bpf_probe() {
  	echo "* Mounting debugfs"
  
  	if [ ! -d /sys/kernel/debug/tracing ]; then
  		mount -t debugfs nodev /sys/kernel/debug
  	fi
  
  	get_kernel_config
  
  	if [ ! -z "${SYSDIG_HOST_ROOT}" ] && [ -f "${SYSDIG_HOST_ROOT}/etc/os-release" ]; then
  		. "${SYSDIG_HOST_ROOT}/etc/os-release"
  
  		if [ "${ID}" == "cos" ]; then
  			COS=1
  		fi
  	fi
  
  	if [ ! -z "${SYSDIG_HOST_ROOT}" ] && [ -f "${SYSDIG_HOST_ROOT}/etc/VERSION" ]; then
  		MINIKUBE=1
  		MINIKUBE_VERSION="$(cat ${SYSDIG_HOST_ROOT}/etc/VERSION)"
  	fi
  
  	local BPF_PROBE_FILENAME="${BPF_PROBE_NAME}-${SYSDIG_VERSION}-${ARCH}-${KERNEL_RELEASE}-${HASH}.o"
  
  	if [ ! -f "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" ]; then
  
  	        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 [ -n "${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
  
  			    # Note: this overrides the KERNELDIR set while untarring the tarball
  			    export KERNELDIR=`pwd`
  
  			    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
  
  			    # 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 [ -n "${MINIKUBE}" ]; then
  			echo "* Minikube detected (${MINIKUBE_VERSION}), using linux kernel sources for minikube kernel"
  			local 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 "${SYSDIG_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
  			echo "* Downloading ${BPF_KERNEL_SOURCES_URL}"
  
  			mkdir -p /tmp/kernel
  			cd /tmp/kernel
  			cd `mktemp -d -p /tmp/kernel`
  			if ! curl -o kernel-sources.tgz --create-dirs "${SYSDIG_PROBE_CURL_OPTIONS}" "${BPF_KERNEL_SOURCES_URL}"; then
  				exit 1;
  			fi
  
  			echo "* Extracting kernel sources"
  
  			mkdir kernel-sources && tar xf kernel-sources.tgz -C kernel-sources --strip-components "${STRIP_COMPONENTS}"
  
  			cd kernel-sources
  			export KERNELDIR=`pwd`
  
  			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 BPF probe ${BPF_PROBE_NAME} (${BPF_PROBE_FILENAME})"
  
  		make -C "/usr/src/${PACKAGE_NAME}-${SYSDIG_VERSION}/bpf" > /dev/null
  
  		mkdir -p ~/.sysdig
  		mv "/usr/src/${PACKAGE_NAME}-${SYSDIG_VERSION}/bpf/probe.o" "${HOME}/.sysdig/${BPF_PROBE_FILENAME}"
  
  		if [ -n "${BPF_KERNEL_SOURCES_URL}" ]; then
  			rm -r /tmp/kernel
  		fi
  	fi
  
  	if [ ! -f "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" ]; then
  		local URL
  		URL=$(echo "${SYSDIG_PROBE_URL}/${SYSDIG_REPOSITORY}/sysdig-probe-binaries/${BPF_PROBE_FILENAME}" | sed s/+/%2B/g)
  
  		echo "* Trying to download precompiled BPF probe from ${URL}"
  
  		curl --create-dirs "${SYSDIG_PROBE_CURL_OPTIONS}" -o "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" "${URL}"
  	fi
  
  	if [ -f "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" ]; then
  		if [ ! -f /proc/sys/net/core/bpf_jit_enable ]; 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
  
  		echo "* BPF probe located, it's now possible to start sysdig"
  
  		ln -sf "${HOME}/.sysdig/${BPF_PROBE_FILENAME}" "${HOME}/.sysdig/${BPF_PROBE_NAME}.o"
  		exit $?
  	else
  		echo "* Failure to find a BPF probe"
  		exit 1
  	fi
  }
  
  ARCH=$(uname -m)
  KERNEL_RELEASE=$(uname -r)
  SCRIPT_NAME=$(basename "${0}")
  SYSDIG_PROBE_URL=${SYSDIG_PROBE_URL:-https://download.sysdig.com}
  if [ -n "$SYSDIG_PROBE_INSECURE_DOWNLOAD" ]
  then
  	SYSDIG_PROBE_CURL_OPTIONS=-fsSk
  else
  	SYSDIG_PROBE_CURL_OPTIONS=-fsS
  fi
  
  MAX_RMMOD_WAIT=60
  KERNEL_ERR_MESSAGE=""
  if [[ $# -ge 1 ]]; then
  	KERNEL_ERR_MESSAGE="$1"
  fi
  
  if [ -z "${SYSDIG_REPOSITORY}" ]; then
  	SYSDIG_REPOSITORY="stable"
  fi
  
  if [ "${SCRIPT_NAME}" = "sysdig-probe-loader" ]; then
          if [ -z "$SYSDIG_VERSION" ]; then
  	    SYSDIG_VERSION=$(sysdig --version | cut -d' ' -f3)
  	fi
  	PROBE_NAME="sysdig-probe"
  	BPF_PROBE_NAME="sysdig-probe-bpf"
  	PACKAGE_NAME="sysdig"
  elif [ "${SCRIPT_NAME}" = "sysdigcloud-probe-loader" ]; then
  	EXEPATH=$(dirname "$(readlink -f "${0}")")
          if [ -z "$SYSDIG_VERSION" ]; then
  	    SYSDIG_VERSION=$("${EXEPATH}"/dragent --version)
  	fi
  	PROBE_NAME="sysdigcloud-probe"
  	BPF_PROBE_NAME="sysdigcloud-probe-bpf"
  	PACKAGE_NAME="draios-agent"
  else
  	echo "This script must be called as sysdig-probe-loader or sysdigcloud-probe-loader"
  	exit 1
  fi
  
  if [ "$(id -u)" != 0 ]; then
  	echo "Installer must be run as root (or with sudo)."
  	exit 1
  fi
  
  if ! hash curl > /dev/null 2>&1; then
  	echo "This program requires curl"
  	exit 1
  fi
  
  if [ -v SYSDIG_BPF_PROBE ] || [ "${1}" = "bpf" ]; then
  	load_bpf_probe
  else
  	load_kernel_probe
  fi
  