#!/bin/sh
#
#
# Copyright (C) 2019 Intel Corporation
#
# This software and the related documents are Intel copyrighted materials, and your use of them
# is governed by the express license under which they were provided to you ("License"). Unless
# the License provides otherwise, you may not use, modify, copy, publish, distribute, disclose
# or transmit this software or the related documents without Intel's prior written permission.
#
# This software and the related documents are provided as is, with no express or implied
# warranties, other than those that are expressly stated in the License.
#

#
# File: kldload-sep
#
# Description: script to load SEP/PAX driver
#
# Version: 1.0
#

# ------------------------------ CONSTANTS -----------------------------------
SEP_DRIVER_NAME=sep
PAX_DRIVER_NAME=pax

# ------------------------------- OUTPUT -------------------------------------

print_msg()
{
  MSG="$*"
  echo "$MSG"
}

print_nnl()
{
  MSG="$*"
  echo -n "$MSG"
}

print_err()
{
  MSG="$*"
  if [ -w /dev/stderr ]; then
      if [ ! -S /dev/stderr ] ; then
          echo "$MSG" >> /dev/stderr
      else
          echo "$MSG" >&2
      fi
  else
    echo "$MSG"
  fi
}

# set the path to include "standard" locations so commands below can be found
PATH="/sbin:/usr/sbin:/usr/local/sbin:/bin:/usr/bin/:/usr/local/sbin:/usr/local/bin:/usr/local/gnu/bin:"${PATH}""
export PATH

# ------------------------------ COMMANDS ------------------------------------

DIRNAME="dirname"
CUT="cut"
GREP="grep"
PGREP="pgrep"
KENV="kenv"
KLDLOAD="kldload"
KLDSTAT="kldstat"
KLDUNLOAD="kldunload"
YPCAT="ypcat"
GETENT="getent"
AWK="awk"

print_usage_and_exit()
{
  err=${1:-0}
  print_msg ""
  print_msg "Usage: $0 [ options ]"
  print_msg ""
  print_msg " where \"options\" are the following:"
  print_msg ""
  print_msg "   -g | --group group"
  print_msg "     restricts access to the ${DRIVER_NAME} driver to users in the specified"
  print_msg "     group; if the option is not provided, the group \"${DEFAULT_GROUP}\""
  print_msg "     will be used"
  print_msg ""
  print_msg "   -p | --perms fileperms"
  print_msg "     restricts access to the ${DRIVER_NAME} driver based on the specified"
  print_msg "     file permissions; if the option is not provided, then file"
  print_msg "     permissions \"${DEFAULT_PERMS}\" (or equivalency, \"ug+rw\") will be used."
  print_msg ""
  exit $err
}

COMMANDS_TO_CHECK="${DIRNAME} ${GREP} ${KLDLOAD} ${KLDSTAT}"

# set the directory of the load-sep script
SCRIPT_DIR=`dirname $0`
SEP_SHELL=
SEP_FORCE=-f

DEFAULT_DRIVER_GROUP="vtune"
DEFAULT_DRIVER_PERM="660"
DRIVER_GROUP=${DEFAULT_DRIVER_GROUP}
DRIVER_PERM=${DEFAULT_DRIVER_PERM}
BACKUP_GROUP=root


if [ -n "${BUSYBOX_SHELL}" ] ; then
   SEP_SHELL=sh
   SEP_FORCE=
fi

while [ $# -gt 0 ] ; do
  case "$1" in
    -h | --help)
      print_usage_and_exit 0
      ;;
    -g | --group)
      DRIVER_GROUP=$2
      if [ -z "$DRIVER_GROUP" ] ; then
        print_err ""
        print_err "ERROR: must provide a group"
        print_usage_and_exit 254
      fi
      shift
      ;;
    -p | --perm)
      DRIVER_PERM=$2
      if [ -z "$DRIVER_PERM" ] ; then
        print_Err ""
        print_err "ERROR: must provide the file permissions"
        print_usage_and_exit 254
      fi
      user_def_perm=1
      shift
      ;;
    *)
      print_err ""
      print_err "ERROR: unrecognized option \"$1\""
      print_usage_and_exit 254
      ;;
  esac
  shift
done

shutdown_processes()
{
    SEP_PROCESSES="sep emon"
    SHUTDOWN_SUCCEEDED=1
    for i in ${SEP_PROCESSES} ; do
        if [ -z "${BUSYBOX_SHELL}" ] ; then
            PLIST=`${PGREP} -l -x $i`
            if [ -n "${PLIST}" ] ; then
                print_msg "Shutting down the following $i process(es):"
                print_nnl "${PLIST} "
                ${KILL} $i
                sleep 2
                PLIST=`${PGREP} -l -x $i`
                if [ -n "${PLIST}" ] ; then
                    print_msg " -- shutdown FAILED"
                    SHUTDOWN_SUCCEEDED=0
                else
                    print_msg ""
                fi
            fi
        else
            PLIST=`ps | ${GREP} -w "\s$i\s" | ${GREP} -v grep | ${CUT} -d ' ' -f 2`
            if [ -n "${PLIST}" ] ; then
                print_msg "Shutting down the following $i process(es):"
                print_nnl "${PLIST} "
                kill ${PLIST}
                sleep 2
                PLIST=`ps | ${GREP} -w "\s$i\s" | ${GREP} -v grep`
                if [ -n "${PLIST}" ] ; then
                    print_msg " -- shutdown FAILED"
                    SHUTDOWN_SUCCEEDED=0
                else
                    print_msg ""
                fi
            fi
        fi
    done

    # if any driver processes are still running, exit with error
    if [ ${SHUTDOWN_SUCCEEDED} -eq 0 ] ; then
        print_err ""
        print_err "ERROR:  The above process(es) must be shutdown before unloading the driver."
        print_err ""
        exit 245
    fi
}

#
# Note: Busybox has a restricted shell environment, and
#       conventional system utilities may not be present;
#       so need to account for this ...
#

# busybox binary check
if [ -z "${BUSYBOX_SHELL}" ]; then
  # if not forced by command line option -re then check it
  BUSYBOX_SHELL=` ${GREP} --help 2>&1 | ${GREP} BusyBox`
fi


if [ -n "${BUSYBOX_SHELL}" ] ; then
  DEFAULT_GROUP=${GROUP:-0}
  INSMOD_ADDITIONAL_OPTIONS="$INSMOD_ADDITIONAL_OPTIONS -re"
else
  COMMANDS_TO_CHECK="${CHGRP} ${CHMOD} ${STAT} ${SU} ${COMMANDS_TO_CHECK}"
fi

# if any of the COMMANDS_TO_CHECK are not executable, then exit script
OK="true"
for c in ${COMMANDS_TO_CHECK} ; do
  CMD=`${WHICH} $c 2>&1` ;
  ret_val=$?
  if [ ${ret_val} -ne 0 ] ; then
    OK="false"
    print_err "ERROR: unable to find command \"$c\" !"
  fi
done
if [ ${OK} != "true" ] ; then
  print_err "If you are using BusyBox, please re-run this script with the '-re' flag added"
  print_err "Otherwise, please add the above commands to your PATH and re-run the script ... exiting."
  exit 255
fi

# check if USER is root
if [ -z "${BUSYBOX_SHELL}" ] ; then
  if [ "${USER}x" != "rootx" ] ; then
    if [ ! -w /dev ] ; then
      print_msg "NOTE:  super-user or \"root\" privileges are required in order to continue."
      print_nnl "Please enter \"root\" "
      OPTIONS="-g ${DRIVER_GROUP} -p ${DRIVER_PERMS} ${SEP_PER_USER}"
      if [ $reload_driver -eq 1 ] ; then
        OPTIONS="$OPTIONS -r"
      fi
      exec ${SU} -c "/bin/sh ${SCRIPT} ${OPTIONS}"
      print_msg ""
      exit 0
    fi
  fi
fi

# shutdown any currently running SEP processes
shutdown_processes

# file name of the driver to load
SEP_DRIVER_FILENAME=${SEP_DRIVER_NAME}.ko
PAX_DRIVER_FILENAME=${PAX_DRIVER_NAME}.ko
SEP_DRIVER_DIRECTORY=${SEP_DRIVER_NAME}
PAX_DRIVER_DIRECTORY=${PAX_DRIVER_NAME}

# check if driver files exist
if [ ! -r ${SEP_DRIVER_DIRECTORY}/${SEP_DRIVER_FILENAME} ] ; then
    print_err ""
    print_err "Warning: ${SEP_DRIVER_NAME} driver \"${SEP_DRIVER_FILENAME}\""
    print_err "was not found in directory \"${SEP_DRIVER_DIRECTORY}\" !"
    print_err ""
    print_err "This means you may need to build ${SEP_DRIVER_NAME} driver from the provided"
    print_err "driver sources.  Please see the driver README for instructions."
    print_err ""
    exit 235
fi

if [ ! -r ${PAX_DRIVER_DIRECTORY}/${PAX_DRIVER_FILENAME} ] ; then
    print_err ""
    print_err "Warning: ${PAX_DRIVER_NAME} driver \"${PAX_DRIVER_FILENAME}\""
    print_err "was not found in directory \"${PAX_DRIVER_DIRECTORY}\" !"
    print_err ""
    print_err "This means you may need to build ${PAX_SEP_DRIVER_NAME} driver from the provided"
    print_err "driver sources.  Please see the driver README for instructions."
    print_err ""
    exit 235
fi

pw group show ${DRIVER_GROUP}
verifygroup_err=$?
if [ ${verifygroup_err} -ne 0 ] ; then
    ${YPCAT} group 2> /dev/null | ${CUT} -d : -f1 | ${GREP} -E "^${DRIVER_GROUP}" > /dev/null 2>&1
    verifygroup_err=$?
    if [ ${verifygroup_err} -ne 0 ] ; then
      ${GETENT} group ${DRIVER_GROUP} 2> /dev/null | ${GREP} -E "^${DRIVER_GROUP}" > /dev/null 2>&1
      verifygroup_err=$?
      if [ ${verifygroup_err} -ne 0 ] ; then
        print_nnl "Creating group ${DRIVER_GROUP}"
        print_nnl ""
        pw group add ${DRIVER_GROUP}
        verifygroup_err=$?
        if [ ${verifygroup_err} -ne 0 ] ; then
          print_msg ""
          print_msg "Warning: ${DRIVER_GROUP} group creation failed ..."
          DRIVER_GROUP=${BACKUP_GROUP}
          print_msg "         proceeding with group ${DRIVER_GROUP} instead ..."
          print_msg ""
        fi
      fi
    fi
fi

${KENV} hw.sep.gid=`${GETENT} group ${DRIVER_GROUP} | ${AWK} -F: '{print $3;}'`
${KENV} hw.pax.gid=`${GETENT} group ${DRIVER_GROUP} | ${AWK} -F: '{print $3;}'`
${KENV} hw.sep.perm=0${DRIVER_PERM}
${KENV} hw.pax.perm=0${DRIVER_PERM}

# check if there are drivers already loaded
SEP_CHECK_RESULT=$( ${KLDSTAT} -v | ${GREP} ${SEP_DRIVER_NAME} )
if [ -n "${SEP_CHECK_RESULT}" ] ; then
  print_msg "There is SEP driver loaded. Unloading it..."
  sleep 2
  SEP_UNLOAD_RESULT=$( ${KLDUNLOAD} ${SEP_DRIVER_NAME} )
  sleep 1
  if [ -n "${SEP_UNLOAD_RESULT}" ] ; then
    print_err ""
    print_err "Error: Unable to unload ${SEP_DRIVER_NAME}. Exiting..."
    print_err ""
    exit 236
  else
    print_msg "${SEP_DRIVER_NAME} was successfully unloaded."
  fi
fi

PAX_CHECK_RESULT=$( ${KLDSTAT} -v | ${GREP} ${PAX_DRIVER_NAME} )
if [ -n "${PAX_CHECK_RESULT}" ] ; then
  print_msg "There is PAX driver loaded. Unloading it..."
  sleep 2
  PAX_UNLOAD_RESULT=$( ${KLDUNLOAD} ${PAX_DRIVER_NAME} )
  sleep 1
  if [ -n "${PAX_UNLOAD_RESULT}" ] ; then
    print_err ""
    print_err "Error: Unable to unload ${PAX_DRIVER_NAME}. Exiting..."
    print_err ""
    exit 237
  else
    print_msg "${PAX_DRIVER_NAME} was successfully unloaded."
  fi
fi

# Loading SEP and PAX drivers
print_msg "Loading ${SEP_DRIVER_NAME} driver..."
${KLDLOAD} ${SEP_DRIVER_DIRECTORY}/${SEP_DRIVER_FILENAME}
SEP_KLDLOAD_RESULT=$?
sleep 2
if [ 0$(echo $SEP_KLDLOAD_RESULT|tr -d ' ' ) -ne 0 ] ; then
    print_err ""
    print_err "Error: unable to load ${SEP_DRIVER_NAME} to the kernel ..."
    print_err ""
    exit 238
fi

print_msg "Loading ${PAX_DRIVER_NAME} driver..."
${KLDLOAD} ${PAX_DRIVER_DIRECTORY}/${PAX_DRIVER_FILENAME}
PAX_KLDLOAD_RESULT=$?
sleep 2
if [ 0$(echo $PAX_KLDLOAD_RESULT|tr -d ' ') -ne 0 ] ; then
    print_err ""
    print_err "Error: unable to load ${PAX_DRIVER_NAME} to the kernel ..."
    print_err ""
    exit 239
fi

# show which driver was loaded
print_msg ""
print_msg "The ${SEP_DRIVER_NAME} and ${PAX_DRIVER_NAME} driver has been successfully loaded."
print_msg ""

exit 0
