#!/bin/sh

# Description: Script to load the power driver
# Version: 3.0

# ***********************************************************************************************

 # 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) 2011 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.

  # Contact Information:
  # Gautam Upadhyaya <gautam.upadhyaya@intel.com>
  # 1906 Fox Drive, Champaign, IL - 61820, USA

  # BSD LICENSE 

  # Copyright(c) 2011 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.
  # ***********************************************************************************************

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

# base driver name and version
DRIVER_BASE=apwr
DRIVER_MAJOR=3
DRIVER_MINOR=1
# basic name of driver
DRIVER_NAME=${DRIVER_BASE}${DRIVER_MAJOR}_${DRIVER_MINOR}
DRIVER_NAME_PREFIX=${DRIVER_BASE}
# Character device file name -- APWR uses a driver-agnostic file name
DEV_FILE_NAME=apwr_driver_char_dev
# name to use with insmod/rmmod scripts
DRIVER_NAME_SCRIPT=${DRIVER_BASE}
# name of rmmod script
RMMOD_SCRIPT=rmmod-${DRIVER_NAME_SCRIPT}
# default driver device group
DEFAULT_GROUP=vtune
# default driver device permissions
DEFAULT_PERMS=660
# permissions for driver device directory
DIR_PERMS=775

# ------------------------------- 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
}

# 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 ------------------------------------

AWK="awk"
CHGRP="chgrp"
CHMOD="chmod"
CUT="cut"
DIRNAME="dirname"
GREP="grep"
INSMOD="insmod"
MKNOD="mknod"
MKDIR="mkdir"
RM="rm"
SED="sed"
SLEEP="sleep"
STAT="stat"
SU="su"
TR="tr"
UNAME="uname"
LSMOD="lsmod"
LS="ls"
HEAD="head"

DRIVER_SET_GROUP_TRIES=3

#COMMANDS_TO_CHECK="${CHGRP} ${CUT} ${DIRNAME} ${GREP} ${INSMOD} ${MKNOD} ${MKDIR} ${RM} ${SED} ${STAT} ${SU} ${TR} ${UNAME}"
COMMANDS_TO_CHECK="${CUT} ${DIRNAME} ${GREP} ${INSMOD} ${MKNOD} ${MKDIR} ${RM} ${SED} ${TR} ${UNAME}"

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

num_cpus=`${GREP} processor /proc/cpuinfo | ${SED} -e '1,$s/\t/ /g' | ${TR} -s ' ' | ${CUT} -d ' ' -f 3`

#DRIVER_NAME=`${LS} *.ko | ${GREP} ${DRIVER_NAME_PREFIX} | ${HEAD} -1`

# function to show usage and exit
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 this 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 this option is not provided, then file"
  print_msg "      permissions \"${DEFAULT_PERMS}\" (or equivalently, \"ug+rw\") will be used"
  print_msg ""
  print_msg "    -q | --query"
  print_msg "      returns 0 if driver is already loaded, non-zero otherwise;"
  print_msg "      if driver is loaded, information on group ownership"
  print_msg "      and file permissions on driver device will be displayed"
  print_msg ""
  print_msg "    -r | --reload"
  print_msg "      attempt to reload all relevant drivers; note that drivers will"
  print_msg "      not unload if they are still in use"
  print_msg ""
  print_msg "    -re | --restricted-environment"
  print_msg "      restricted environment mode: minimal requirements to the system runtime"
  print_msg "      like in busybox case"
  print_msg ""
  exit $err
}

install_driver()
{
    print_nnl "Setting group ownership of device to group \"${DRIVER_GROUP}\" ... "
    ${CHGRP} ${DRIVER_GROUP} /dev/${DEV_FILE_NAME}
    CHGRP_RESULT=$?
    if [ ${CHGRP_RESULT} -ne 0 ] ; then
      print_err ""
      print_err "Error:  unable to change group ownership of device!"
      print_err ""
      exit 230
    fi
}

# set the directory of the insmod-apwr2 script
SCRIPT_DIR=`dirname $0`

# Before anything else, check to see if the driver is already loaded
DRIVER_LOADED=`${LSMOD} | ${CUT} -d ' ' -f 1 | ${GREP} ${DRIVER_NAME_PREFIX}`

# check for certain options
reload_driver=0
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 | --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
      shift
      ;;
    -q | --query)
      err_pwr=0
      if [ -z "$DRIVER_LOADED" ] ; then
        err_pwr=213
        print_msg "${DRIVER_NAME_PREFIX} driver is not loaded."
      else
          # SEP has subdev perms/groups checks here -- we have just the one device so no need to check
          # We DO, however, need to output some messages (for installer compatibility)
          # To do so, retrieve the driver group/perms settings here.
          drv_group=`${STAT} -c "%G" /dev/${DEV_FILE_NAME}`
          drv_perms=`${STAT} -c "%a" /dev/${DEV_FILE_NAME}`
          print_msg "${DRIVER_LOADED} driver is loaded and owned by group \"${drv_group}\" with file permissions \"${drv_perms}\"."
      fi 
      exit $err_pwr
      ;;
    -r | --reload)
      reload_driver=1
      ;;
    -re | --restricted-environment)
      BUSYBOX_SHELL=yes
      ;;
    *)
      print_err ""
      print_err "ERROR: unrecognized option \"$1\""
      print_usage_and_exit 254
      ;;
  esac
  shift
done

# 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=`type -P $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 "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


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

SCRIPT=$0
PLATFORM=`${UNAME} -m`
KERNEL_VERSION=`${UNAME} -r`
PLATFORM=`${UNAME} -m`
DRIVER_DIRECTORY=`${DIRNAME} ${SCRIPT}`
# use existing driver group and device permissions as default if available
if [ -e /dev/${DEV_FILE_NAME} ] && [ -z "${BUSYBOX_SHELL}" ]; then
  drv_group=`${STAT} --format "%G" /dev/${DEV_FILE_NAME}`
  drv_perms=`${STAT} --format "%a" /dev/${DEV_FILE_NAME}`
  echo "FOUND file: group = $drv_group, PERMS = $drv_perms"
else
  drv_group=${DEFAULT_GROUP}
  drv_perms=${DEFAULT_PERMS}
fi
DRIVER_GROUP=${DRIVER_GROUP:-${drv_group}}
DRIVER_PERMS=${DRIVER_PERMS:-${drv_perms}}

# check if platform is supported
if [ "${PLATFORM}" = "ia64" ] ; then
  ARCH="x64"
elif [ "${PLATFORM}" = "x86_64" ] ; then
  ARCH="x32_64"
elif [ "${PLATFORM}" = "i386" -o "${PLATFORM}" = "i486" -o "${PLATFORM}" = "i586" -o "${PLATFORM}" = "i686" ] ; then
  ARCH="x32"
else
  print_err ""
  print_err "ERROR: Unsupported platform \"${PLATFORM}\" ... exiting."
  print_err ""
  exit 253
fi

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

# check if driver devices exist or if the driver is loaded, and if so, then exit
DEVNUM=`${GREP} -w ${DEV_FILE_NAME} /proc/devices | ${TR} -s ' ' | ${CUT} -d ' ' -f 1`
if [ -n "${DRIVER_LOADED}" ] || [ -n "${DEVNUM}" ] ; then
  if [ $reload_driver -ne 1 ] ; then
    print_err ""
    print_err "The ${DRIVER_LOADED} driver is already loaded!  Use ${DRIVER_DIRECTORY}/${RMMOD_SCRIPT} to remove it."
    print_err ""
    exit 236
  fi
fi

# check if USER is root
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}"
    if [ $reload_driver -eq 1 ] ; then
      OPTIONS="$OPTIONS -r"
    fi
    exec ${SU} -c "/bin/bash ${SCRIPT} ${OPTIONS}"
    print_msg ""
    exit 0
  fi
fi

# check whether to reload the driver
if [ $reload_driver -eq 1 ] ; then
  (cd ${SCRIPT_DIR} && ./${RMMOD_SCRIPT})
  err=$?
  if [ $err -ne 0 ] ; then
    print_err ""
    print_err "Error: failed to reload ${DRIVER_NAME_PREFIX} driver"
    print_err ""
    exit $err
  fi
fi

# check whether kernel is for UP or SMP
SMP=`${UNAME} -v | ${GREP} SMP`
if [ -z "${SMP}" ] ; then
  ARITY="up"
else
  ARITY="smp"
fi

# file name of the driver to load
#DRIVER_FILENAME=${DRIVER_NAME}-${ARCH}-${KERNEL_VERSION}${ARITY}.ko
DRIVER_FILENAME_SUFFIX=${ARCH}-${KERNEL_VERSION}${ARITY}.ko
DRIVER_FILENAME=`ls *.ko | ${GREP} ${DRIVER_FILENAME_SUFFIX}`

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

# try to load the PAX driver if it was not already loaded
# GU: NOT supported for APWR

# check if PAX driver has same access rights as this driver
# GU: NOT supported for APWR

# insmod command to execute
INSMOD_CMD="${INSMOD} ${DRIVER_DIRECTORY}/${DRIVER_FILENAME}"

# execute the command
print_msg "Executing: ${INSMOD_CMD}"
# The probe_on_syscall switch (if set to 1) enables syscall tracepoint hooking
# which can incur substantial performance overhead because every system calls
# are hooked. However, there are two known issues that can be addressed 
# only by this technique for now. They are:
# 1) fork-exec problem on Linux. The device driver tracks "fork" syscalls 
# to determine when a new process is launched. However, the process name can be 
# changed with the same PID when the process image of the program is swapped 
# because of "exec" syscall. By hooking exec syscall, the driver will be able to
# generate PID and process name mappings in exec syscall. Therefore, the process 
# name can be updated, when changed.  
# 2) Determining kernel timers on Linux. For the kernel timers initialized 
# by device drivers that inherit the TID and PID of the process that did the 'insmod',
# the driver cannot identify them as kernel timers. This is because the driver 
# relies on the thread ID of the calling context to determine if a timer is 
# initialized in Ring-0 space. If turned on, therefore, the driver can track all 
# timer related syscalls, all the kernel timers can be discriminated.
#
# We currently turn it off by default to minimize overhead. If you want to use this 
# solution, however, you can set probe_on_syscalls to 1.
${INSMOD_CMD} probe_on_syscalls=0
INSMOD_RESULT=$?

# abort if unable to load the driver
if [ ${INSMOD_RESULT} -ne 0 ] ; then
  print_err ""
  print_err "Error:  ${DRIVER_NAME} driver failed to load!"
  print_err ""
  print_err "You may need to build ${DRIVER_NAME} driver for your kernel."
  print_err "Please see the ${DRIVER_NAME} driver README for instructions."
  print_err ""
  exit 234
fi

# check if the driver has been loaded into the kernel
DEVNUM=`${GREP} -w ${DEV_FILE_NAME} /proc/devices | ${TR} -s ' ' | ${CUT} -d ' ' -f 1`
if [ -z "${DEVNUM}" ] ; then
  print_err ""
  print_err "Error:  unable to find device \"${DRIVER_NAME}\" in /proc/devices !"
  print_err ""
  exit 233
fi

# create the apwr file if not already done so -- required ONLY to support older drivers!!!
umask 116 # rw for ug, no permission for the other
if [ ! -e /dev/${DEV_FILE_NAME} ] ; then
    print_nnl "Creating /dev/${DEV_FILE_NAME} character device file with major number ${DEVNUM}..."
    ${MKNOD} /dev/${DEV_FILE_NAME} c ${DEVNUM} 0
    MKNOD_RESULT=$?
    if [ ${MKNOD_RESULT} -ne 0 ] ; then
        print_err ""
        print_err "Error: unable to create required /dev/${DEV_FILE_NAME} character device file!"
        print_err ""
        exit 232
    fi
fi
print_msg "done."

# change group ownership to whichever group is permitted to open the driver
if [ -z "${BUSYBOX_SHELL}" ] ; then
    if [ -z "${DRIVER_GROUP}" ] ; then
      print_err ""
      print_err "Error:  no group ownership specified for /dev/${DEV_FILE_NAME} device ... exiting."
      print_err ""
      exit 231
    fi

    # do 3 tries to set the driver's group as requested.
    dr_grp_ok=0
    dr_grp_tries=0
    while [ $dr_grp_tries -lt $DRIVER_SET_GROUP_TRIES ] && [ $dr_grp_ok -eq 0 ]
    do
        install_driver
        asg_group=`${LS} -l  /dev/${DEV_FILE_NAME} | ${AWK} '{split ($0,a) ; print a[4]}'`
		# keep code for testing this branch in the future
        if [  1 -gt 2 ] ; then
            if [ $dr_grp_tries -lt 2 ] ; then
                ${CHGRP} root /dev/${DEV_FILE_NAME}
                asg_group=`${LS} -l  /dev/${DEV_FILE_NAME} | ${AWK} '{split ($0,a) ; print a[4]}'`
            fi 
        fi 
        if [ "$asg_group" == "${DRIVER_GROUP}" ] ; then
            print_msg "done. Installed Power driver with group "$asg_group
            dr_grp_ok=1
        else
            dr_grp_ok=0
            dr_grp_tries=`expr $dr_grp_tries + 1`
            print_msg "Not managed to install the driver with the proper group. Wrong group is " $asg_group
            ${SLEEP} 1
        fi
    done

    asg_group=`${LS} -l  /dev/${DEV_FILE_NAME} | ${AWK} '{split ($0,a) ; print a[4]}'`
    if [ "$asg_group" == "root" ] && [ !  "root" == "${DRIVER_GROUP}" ] ; then
        print_err ""
        print_err "Error:  the installed Power Tools driver was installed with root group ownership instead of ${DRIVER_GROUP} group ownership."
        print_err ""
        exit 228
    fi
fi

# GU: setting driver perms to 666 EXPLICITLY
# (This is a temporary workaround for perm issues we're experiencing on Ubuntu systems).
# UPDATE: changed to "660" to resolve a CQ
APWR_DRIVER_PERMS=660

if [ -z "${BUSYBOX_SHELL}" ] ; then

    print_nnl "Setting file permissions on device to \"${APWR_DRIVER_PERMS}\" ... "
    ${CHMOD} ${APWR_DRIVER_PERMS} /dev/${DEV_FILE_NAME}
    CHMOD_RESULT=$?
    if [ ${CHMOD_RESULT} -ne 0 ] ; then
      print_err ""
      print_err "Error:  unable to change permissions to ${APWR_DRIVER_PERMS} on device!"
      print_err ""
      exit 229
    fi
    print_msg "done."

    asg_group=`${LS} -l  /dev/${DEV_FILE_NAME} | ${AWK} '{split ($0,a) ; print a[4]}'`
    if [ "$asg_group" == "root" ] && [ !  "root" == "${DRIVER_GROUP}" ] ; then
       # if we came here chgrp command works, so protect against surprised of chmod comand CQ DPD200312013,
       ${CHGRP} ${DRIVER_GROUP} /dev/${DEV_FILE_NAME}
       asg_group=`${LS} -l  /dev/${DEV_FILE_NAME} | ${AWK} '{split ($0,a) ; print a[4]}'`
       if [ "$asg_group" == "root" ] && [ !  "root" == "${DRIVER_GROUP}" ] ; then
            print_err ""
            print_err "Error:  the installed Power Tools driver was installed with root group ownership instead of ${DRIVER_GROUP} group ownership,"
            print_err "after the change of permissions step."
            print_err ""
            exit 227
       fi
    fi

    oth_perm=`${LS} -l  /dev/${DEV_FILE_NAME} | ${AWK} '{split ($0,a,"-") ; print a[3]}'`
    if [ "$oth_perm" == "rw" ] ; then
      print_err ""
      print_err "Error:  the installed Power Tools driver is world writable!"
      print_err ""
      exit 226
    fi
fi

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

exit 0
