#!/bin/sh

#
# File: boot-script
#
# Description: script to install/uninstall the socperf driver boot script
#
# Version: 1.5
#
#  This file is provided under a dual BSD/GPLv2 license.  When using or 
#  redistributing this file, you may do so under either license.
#
#  GPL LICENSE SUMMARY
#
#  Copyright (C) 2008-2019 Intel Corporation. All rights reserved.
#
#  This program is free software; you can redistribute it and/or modify 
#  it under the terms of version 2 of the GNU General Public License as
#  published by the Free Software Foundation.
#
#  This program is distributed in the hope that it will be useful, but 
#  WITHOUT ANY WARRANTY; without even the implied warranty of 
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
#  General Public License for more details.
#
#  You should have received a copy of the GNU General Public License 
#  along with this program; if not, write to the Free Software 
#  Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
#  The full GNU General Public License is included in this distribution 
#  in the file called LICENSE.GPL.
#
#  BSD LICENSE 
#
#  Copyright (C) 2008-2019 Intel Corporation. All rights reserved.
#  All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without 
#  modification, are permitted provided that the following conditions 
#  are met:
#
#    * Redistributions of source code must retain the above copyright 
#      notice, this list of conditions and the following disclaimer.
#    * Redistributions in binary form must reproduce the above copyright 
#      notice, this list of conditions and the following disclaimer in 
#      the documentation and/or other materials provided with the 
#      distribution.
#    * Neither the name of Intel Corporation nor the names of its 
#      contributors may be used to endorse or promote products derived 
#      from this software without specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
#  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
#  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
#  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
#  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
#  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
#  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
#  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
#  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
#  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#

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

# ------------------------------ CONSTANTS -----------------------------------

# base driver name and version
DRIVER_BASE=soc_perf
DRIVER_MAJOR=3
DRIVER_MINOR=0
# basic name of driver
DRIVER_NAME=${DRIVER_BASE}${DRIVER_MAJOR}
# name to use with insmod/rmmod scripts
DRIVER_NAME_SCRIPT=${DRIVER_BASE}
# default driver device group
DEFAULT_GROUP=${GROUP:-root}
# default driver device permissions
DEFAULT_PERMS=660

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

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

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

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

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

GREP="grep"
LN="ln"
RM="rm"
SED="sed"
SU="su"
WHICH="which"

COMMANDS_TO_CHECK="${LN} ${RM} ${SED}"

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

# busybox binary check
BUSYBOX_SHELL=` ${GREP} --help 2>&1 | ${GREP} BusyBox`

if [ -z "${BUSYBOX_SHELL}" ] ; then
  COMMANDS_TO_CHECK="${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` ;
  if [ -z "${CMD}" ] ; then
    OK="false"
    print_err "ERROR: unable to find command \"$c\" !"
  fi
done
if [ ${OK} != "true" ] ; then
  print_err "Please add the location to the above commands to your PATH and re-run the script ... exiting."
  exit 255
fi

# ------------------------------ VARIABLES -----------------------------------

SCRIPT=$0
SCRIPT_ARGS="$@"
PREBUILT_DRIVER_DIRECTORY=`dirname $PWD/$SCRIPT`
DEFAULT_REDHAT_BOOT_INSTALL="/etc/rc.d/init.d"
DEFAULT_SUSE_BOOT_INSTALL="/etc/init.d"
DEFAULT_DEBIAN_BOOT_INSTALL="/etc/init.d"
PER_USER_MODE=""

# ------------------------------ FUNCTIONS -----------------------------------

# function to show usage and exit

print_usage_and_exit()
{
  err=${1:-0}
  print_msg ""
  print_msg "Usage: $0 [ option ]"
  print_msg ""
  print_msg " where \"option\" is one of the following:"
  print_msg ""
  print_msg "    -h | --help"
  print_msg "      prints out usage"
  print_msg ""
  print_msg "    -i | --install"
  print_msg "      configures the ${DRIVER_NAME} boot script as per options below"
  print_msg "      and then installs it in the appropriate system directory"
  print_msg ""
  print_msg "    -d | --driver-directory [ prebuilt-drivers-directory ]"
  print_msg "      configures the ${DRIVER_NAME} boot script to use the specified"
  print_msg "      prebuilt driver directory; if this option is not provided,"
  print_msg "      then the directory,"
  print_msg "          $PREBUILT_DRIVER_DIRECTORY"
  print_msg "       will be used; implies installation"
  print_msg ""
  print_msg "    -g | --group group"
  print_msg "      configures the ${DRIVER_NAME} boot script to restrict driver access to users in"
  print_msg "      the specified group; if this option is not provided, the group \"${DEFAULT_GROUP}\""
  print_msg "      will be used; implies installation"
  print_msg ""
  print_msg "    -p | --perms fileperms"
  print_msg "      configures the ${DRIVER_NAME} boot script to restrict driver access based on the"
  print_msg "      specified file permissions; if this option is not provided, then file"
  print_msg "      permissions \"${DEFAULT_PERMS}\" (or equivalently, \"ug+rw\") will be used"
  print_msg "      implies installation"
  print_msg ""
  print_msg "    -u | --uninstall"
  print_msg "      uninstalls a previously installed ${DRIVER_NAME} driver boot script"
  print_msg ""
  print_msg "    -pu | --per-user"
  print_msg "      install the sampling driver in secure mode to enable per user collection"
  print_msg ""
  exit $err
}

# --------------------------------- MAIN -------------------------------------

# must specifiy at least one option
if [ $# -lt 1 ] ; then
  print_usage_and_exit 0
fi

# if only help option specified, then show options
if [ $# -eq 1 ] ; then
  case "$1" in
    -h | --help)
      print_usage_and_exit 0
      ;;
  esac
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\" "
      exec ${SU} -c "/bin/sh ${SCRIPT} ${SCRIPT_ARGS}"
      print_msg ""
      exit 0
    fi
  fi
fi

# parse the options
install_boot_script=0
uninstall_boot_script=0
override_group=0
override_perms=0
while [ $# -gt 0 ] ; do
  case "$1" in
    -d | --driver-directory)
      DRIVER_DIR=$2
      if [ -z "$DRIVER_DIR" ] ; then
        print_err ""
        print_err "ERROR: must provide location of pre-built driver directory"
        print_usage_and_exit 254
      fi
      install_boot_script=1
      shift
      ;;
    -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
      override_group=1
      install_boot_script=1
      shift
      ;;
    -p | --perms)
      DRIVER_PERMS=$2
      if [ -z "$DRIVER_PERMS" ] ; then
        print_err ""
        print_err "ERROR: must provide the file permissions"
        print_usage_and_exit 254
      fi
      override_perms=1
      install_boot_script=1
      shift
      ;;
    -i | --install)
      install_boot_script=1
      ;;
    -u | --uninstall)
      uninstall_boot_script=1
      ;;
    -pu | --per-user)
      PER_USER_MODE=" -pu "
      ;;
    *)
      print_err ""
      print_err "ERROR: unrecognized option \"$1\""
      print_usage_and_exit 254
      ;;
  esac
  shift
done

# set install options based on user input ...
DRIVER_DIR=${DRIVER_DIR:-$PREBUILT_DRIVER_DIRECTORY}
DRIVER_GROUP=${DRIVER_GROUP:-$DEFAULT_GROUP}
DRIVER_PERMS=${DRIVER_PERMS:-$DEFAULT_PERMS}

# check options
if [ $install_boot_script -eq 1 ] ; then
  if [ ! -d ${DRIVER_DIR} ] ; then
    print_err ""
    print_err "ERROR: the prebuilt driver directory,"
    print_err "          $DRIVER_DIR"
    print_err "       is not accessible by root user!"
    print_err ""
    print_err "Please specifiy a valid directory and then re-run this script."
    print_err ""
    exit 101
  fi
  print_msg "Configuring ${DRIVER_NAME} boot script with the following options:"
  print_msg "    driver files = ${DRIVER_DIR}"
  INSMOD_OPTIONS=""
  if [ $override_group -eq 1 ] ; then
    print_msg "    driver group = ${DRIVER_GROUP}"
    INSMOD_OPTIONS="-g ${DRIVER_GROUP}"
  fi
  if [ $override_perms -eq 1 ] ; then
    print_msg "    driver perms = ${DRIVER_PERMS}"
    INSMOD_OPTIONS="${INSMOD_OPTIONS} -p ${DRIVER_PERMS}"
  fi
  INSMOD_OPTIONS="${INSMOD_OPTIONS} ${PER_USER_MODE}"
fi

# ---------------------------- BOOT SCRIPT BEGIN -----------------------------

create_script()
{ 
  SCRIPT=$1
  ${RM} -f ${SCRIPT}
cat > $SCRIPT <<EOF
#!/bin/sh

#
# File: ${DRIVER_NAME}
#
# Description: script to load ${DRIVER_NAME} driver at boot time
#
# Version: 1.4
#
# Copyright(C) 2008-2018 Intel Corporation.  All Rights Reserved.
# 
#     This file is part of SocPerf Development Kit
# 
#     SocPerf Development Kit is free software; you can redistribute it
#     and/or modify it under the terms of the GNU General Public License
#     version 2 as published by the Free Software Foundation.
# 
#     SocPerf Development Kit is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.
# 
#     You should have received a copy of the GNU General Public License
#     along with SocPerf Development Kit; if not, write to the Free Software
#     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# 
#     As a special exception, you may use this file as part of a free software
#     library without restriction.  Specifically, if other files instantiate
#     templates or use macros or inline functions from this file, or you compile
#     this file and link it with other files to produce an executable, this
#     file does not by itself cause the resulting executable to be covered by
#     the GNU General Public License.  This exception does not however
#     invalidate any other reasons why the executable file might be covered by
#     the GNU General Public License.
#

#
# chkconfig: 2345 30 40
#
### BEGIN INIT INFO ###
# Provides: ${DRIVER_NAME}
# Required-Start: \$syslog \$remote_fs
# Required-Stop: \$syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0
# Short-Description: loads/unloads the ${DRIVER_NAME} driver at boot/shutdown time
# Description: loads/unloads the ${DRIVER_NAME} driver at boot/shutdown time
### END INIT INFO ###

# name of the driver

DRIVER_NAME=${DRIVER_NAME}

# location where the pre-built drivers are installed

DRIVER_DIR=${DRIVER_DIR}

# name of driver load/unload scripts

INSMOD_SCRIPT=insmod-${DRIVER_NAME_SCRIPT}

RMMOD_SCRIPT=rmmod-${DRIVER_NAME_SCRIPT}

# source the function library, if it exists

[ -r /etc/rc.d/init.d/functions ] && . /etc/rc.d/init.d/functions

# check whether directories and scripts are available

if [ ! -d \${DRIVER_DIR} ] ; then
  echo "Unable to access the ${DRIVER_NAME} driver directory \"\${DRIVER_DIR}\" !"
  exit 101
fi

if [ ! -f \${DRIVER_DIR}/\${INSMOD_SCRIPT} ] ; then
  echo "The ${DRIVER_NAME} load script \"\${DRIVER_DIR}/\${INSMOD_SCRIPT}\" does not exist!"
  exit 102
fi

if [ ! -f \${DRIVER_DIR}/\${RMMOD_SCRIPT} ] ; then
  echo "The ${DRIVER_NAME} unload script \"\${DRIVER_DIR}/\${RMMOD_SCRIPT}\" does not exist!"
  exit 103
fi

# define function to load the driver

start() {
    echo "Loading the ${DRIVER_NAME} driver: "
    (cd \${DRIVER_DIR} && ./\${INSMOD_SCRIPT} ${INSMOD_OPTIONS})
    RETVAL=\$?
    return \$RETVAL
}

# define function to unload the driver

stop() {
    echo "Unloading ${DRIVER_NAME} driver: "
    (cd \${DRIVER_DIR} && ./\${RMMOD_SCRIPT})
    RETVAL=\$?
    return \$RETVAL
}

# define function to query whether driver is loaded

status() {
    (cd \${DRIVER_DIR} && ./\${INSMOD_SCRIPT} -q)
    ERR=\$?
    if [ \$ERR -eq 0 ] ; then
      RETVAL=0
    else
      RETVAL=3
    fi
    return \$RETVAL
}

# parse command-line options and execute

RETVAL=0

case "\$1" in
	start)
	    start
	    ;;
	stop)
	    stop
	    ;;
	restart)
	    stop
	    start
	    ;;
	status)
	    status
	    ;;
	*)
	    echo "Usage: \$0 {start|stop|restart|status}"
	    exit 1
esac

exit \$RETVAL
EOF
  chmod a+rx $SCRIPT
}

# ---------------------------- BOOT SCRIPT END -------------------------------

# check which distro (hack!)
if [ -d ${DEFAULT_REDHAT_BOOT_INSTALL} ] ; then
  LINUX_DISTRO="RedHat"
  DEFAULT_BOOT_INSTALL=${DEFAULT_REDHAT_BOOT_INSTALL}
  RUNLEVEL_DIR=/etc/rc.d
  RELATIVE_BOOT_INSTALL=../init.d
else
  LSB_BIN=/usr/lib/lsb
  if [ -x ${LSB_BIN}/install_initd -a -x ${LSB_BIN}/remove_initd ] ; then
    LINUX_DISTRO="SuSE"
    DEFAULT_BOOT_INSTALL=${DEFAULT_SUSE_BOOT_INSTALL}
    RUNLEVEL_DIR=/etc/init.d
    RELATIVE_BOOT_INSTALL=.
  else
    LINUX_DISTRO="Debian"
    DEFAULT_BOOT_INSTALL=${DEFAULT_DEBIAN_BOOT_INSTALL}
    RUNLEVEL_DIR=/etc
    RELATIVE_BOOT_INSTALL=../init.d
  fi
fi

# remove boot script, if it was previously installed
if [ $uninstall_boot_script -eq 1 ] ; then
  if [ -f ${DEFAULT_BOOT_INSTALL}/${DRIVER_NAME} ] ; then
    print_nnl "Removing ${DRIVER_NAME} boot script and symlinks for runlevels 2 through 5 ... "
    sleep 1
    if [ "${LINUX_DISTRO}"x = "SuSEx" ] ; then
      ${LSB_BIN}/remove_initd ${DEFAULT_BOOT_INSTALL}/${DRIVER_NAME}
      err=$?
      if [ $err -ne 0 ] ; then
        print_err "${LSB_BIN}/remove_initd returned error $err ... exiting."
        exit 105
      fi
    elif [ "${LINUX_DISTRO}"x = "RedHatx" -o "${LINUX_DISTRO}"x = "Debianx" ] ; then
      [ -w ${RUNLEVEL_DIR}/rc2.d ] && ${RM} -f ${RUNLEVEL_DIR}/rc2.d/S99${DRIVER_NAME}
      [ -w ${RUNLEVEL_DIR}/rc3.d ] && ${RM} -f ${RUNLEVEL_DIR}/rc3.d/S99${DRIVER_NAME}
      [ -w ${RUNLEVEL_DIR}/rc4.d ] && ${RM} -f ${RUNLEVEL_DIR}/rc4.d/S99${DRIVER_NAME}
      [ -w ${RUNLEVEL_DIR}/rc5.d ] && ${RM} -f ${RUNLEVEL_DIR}/rc5.d/S99${DRIVER_NAME}
    else
      print_nnl "WARNING: unable to remove symlinks ... "
    fi
    [ -w ${DEFAULT_BOOT_INSTALL} ] && ${RM} -f ${DEFAULT_BOOT_INSTALL}/${DRIVER_NAME}
    print_msg "done."
  else
    print_msg "No previously installed ${DRIVER_NAME} driver boot script was found."
  fi
fi

# install boot script
if [ $install_boot_script -eq 1 ] ; then
  # create the boot script in target directory or exit if error
  if [ -w ${DEFAULT_BOOT_INSTALL} ] ; then
    print_nnl "Creating boot script ${DEFAULT_BOOT_INSTALL}/${DRIVER_NAME} ... "
    create_script ${DEFAULT_BOOT_INSTALL}/${DRIVER_NAME}
    if [ -r ${DEFAULT_BOOT_INSTALL}/${DRIVER_NAME} ] ; then
      print_msg "done."
    else
      print_err "Unable to create boot script ... exiting."
      exit 104
    fi
  else
    print_err "Unable to write to ${DEFAULT_BOOT_INSTALL} ... exiting."
    exit 104
  fi
  # configure autoload ...
  print_nnl "Configuring autoload of ${DRIVER_NAME} driver for runlevels 2 through 5 ... "
  sleep 1
  if [ "${LINUX_DISTRO}"x = "SuSEx" ] ; then
    ${LSB_BIN}/install_initd ${DEFAULT_BOOT_INSTALL}/${DRIVER_NAME}
  elif [ "${LINUX_DISTRO}"x = "RedHatx" -o "${LINUX_DISTRO}"x = "Debianx" ] ; then
    [ -w ${RUNLEVEL_DIR}/rc2.d ] && ${LN} -sf ${RELATIVE_BOOT_INSTALL}/${DRIVER_NAME} ${RUNLEVEL_DIR}/rc2.d/S99${DRIVER_NAME}
    [ -w ${RUNLEVEL_DIR}/rc3.d ] && ${LN} -sf ${RELATIVE_BOOT_INSTALL}/${DRIVER_NAME} ${RUNLEVEL_DIR}/rc3.d/S99${DRIVER_NAME}
    [ -w ${RUNLEVEL_DIR}/rc4.d ] && ${LN} -sf ${RELATIVE_BOOT_INSTALL}/${DRIVER_NAME} ${RUNLEVEL_DIR}/rc4.d/S99${DRIVER_NAME}
    [ -w ${RUNLEVEL_DIR}/rc5.d ] && ${LN} -sf ${RELATIVE_BOOT_INSTALL}/${DRIVER_NAME} ${RUNLEVEL_DIR}/rc5.d/S99${DRIVER_NAME}
  else
    print_nnl "WARNING: unable to create symlinks ... "
  fi
  print_msg "done."
fi

# epilogue

exit 0
