#!/bin/bash
#==============================================================================
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://workshop.perforce.com/view/p4-sdp/main/LICENSE
#------------------------------------------------------------------------------
set -u

#------------------------------------------------------------------------------

# Usage (bash syntax):
# ./os_tweaks.sh 2>&1 | tee /tmp/os_tweaks.log

# This script checks, and if need be sets, certain OS parameters per
# Perforce recommendations: http://answers.perforce.com/articles/KB/3005#LINUX

# Following is a sample manual session:
# ROOT@perforce1:/root cat /sys/kernel/mm/transparent_hugepage/khugepaged/defrag
# [yes] no
# ROOT@perforce1:/root cat /sys/kernel/mm/transparent_hugepage/defrag
# [always] madvise never
# ROOT@perforce1:/root cat /sys/kernel/mm/transparent_hugepage/enabled
# [always] madvise never
# ROOT@perforce1:/root echo no > /sys/kernel/mm/transparent_hugepage/khugepaged/defrag
# ROOT@perforce1:/root echo never > /sys/kernel/mm/transparent_hugepage/defrag
# ROOT@perforce1:/root echo never > /sys/kernel/mm/transparent_hugepage/enabled
# ROOT@perforce1:/root cat /sys/kernel/mm/transparent_hugepage/khugepaged/defrag
# yes [no]
# ROOT@perforce1:/root cat /sys/kernel/mm/transparent_hugepage/defrag
# always madvise [never]
# ROOT@perforce1:/root cat /sys/kernel/mm/transparent_hugepage/enabled
# always madvise [never]

function msg () { echo -e "$*"; }
function bail () { msg "\\n${1:-Unknown Error}\\n"; exit "${2:-1}"; }

# Version ID Block. Relies on +k filetype modifier.
#------------------------------------------------------------------------------
# shellcheck disable=SC2016
declare VersionID='$Id: //p4-sdp/r26.1.0.BETA/Server/Unix/setup/os_tweaks.sh#1 $ $Change: 33444 $'
declare VersionStream=${VersionID#*//}; VersionStream=${VersionStream#*/}; VersionStream=${VersionStream%%/*};
declare VersionCL=${VersionID##*: }; VersionCL=${VersionCL%% *}
declare Version=${VersionStream}.${VersionCL}

declare -i i=0
declare -a Files
declare -i ChangesNeeded=0
declare -a CurrentValues
declare -a CorrectValues
declare CurrentValue=

# Static Data
Files[0]=/sys/kernel/mm/transparent_hugepage/khugepaged/defrag
Files[1]=/sys/kernel/mm/transparent_hugepage/defrag
Files[2]=/sys/kernel/mm/transparent_hugepage/enabled

# The format of one of the files varies by kernel version, either
# numeric or string format.  Figure out what format it is, and
# set the correct value based on the format of the current value.
if [[ -r "${Files[0]}" ]]; then
   CurrentValue=$(cat ${Files[0]})
   if [[ $CurrentValue =~ ^[0-9]{1}$ ]]; then
      CorrectValues[0]="0"
   else
      CorrectValues[0]="no"
   fi
fi

[[ $(whoami) == root ]] || bail "This must be run as root."

CorrectValues[1]="never"
CorrectValues[2]="never"

msg "\\nStarted ${0##*/} version $Version at $(date) on host ${HOSTNAME%%.*}.\\n"

while [[ $i -lt ${#Files[*]} ]]; do
   if [[ -r "${Files[$i]}" ]]; then
      CurrentValue=$(cat "${Files[$i]}")
      CurrentValue=${CurrentValue#*\[}
      CurrentValue=${CurrentValue%\]*}
      CurrentValues[$i]="$CurrentValue"
   else
      bail "Expected file does not exist: ${Files[$i]}"
   fi

   echo -e -n "File:        : ${Files[$i]}\nCurrent Value: ${CurrentValues[$i]}\nCorrect Value: ${CorrectValues[$i]}"

   if [[ "$CurrentValue" == "${CorrectValues[$i]}" ]]; then
      msg " (OK)\\n"
   else
      msg " (Needs to be adjusted)\\n"
      ChangesNeeded=$((ChangesNeeded+1))
   fi

   i=$((i+1))
done

if [[ $ChangesNeeded -eq 0 ]]; then
   msg "Verified: No changes to the OS parameters checked by this script are needed.\\n"
   exit 0
else
   msg "Detected $ChangesNeeded of ${#Files[*]} OS parameters needing to be updated.\\n"
fi

msg "Making updates.\\n"
i=0
while [[ $i -lt ${#Files[*]} ]]; do
   CurrentValue=$(cat ${Files[$i]})
   CurrentValue=${CurrentValue#*\[}
   CurrentValue=${CurrentValue%\]*}

   [[ "$CurrentValue" == "${CorrectValues[$i]}" ]] && continue

   msg "Setting value to ${CorrectValues[$i]} in ${Files[$i]}"
   echo "${CorrectValues[$i]}" > "${Files[$i]}" || bail  "Failed to set value in ${Files[$i]}."

   i=$((i+1))
done

msg "\\nRechecking files after making updates.\\n"
i=0
ChangesNeeded=0

while [[ $i -lt ${#Files[*]} ]]; do
   CurrentValue=$(cat ${Files[$i]})
   CurrentValue=${CurrentValue#*\[}
   CurrentValue=${CurrentValue%\]*}

   echo -e -n "File:        : ${Files[$i]}\nCurrent Value: $CurrentValue\nCorrect Value: ${CorrectValues[$i]}"

   if [[ "$CurrentValue" == "${CorrectValues[$i]}" ]]; then
      msg " (OK)\\n"
   else
      msg " (Still needs to be adjusted)\\n"
      ChangesNeeded=$((ChangesNeeded+1))
   fi

   i=$((i+1))
done

if [[ $ChangesNeeded -eq 0 ]]; then
   msg "Done. All OS parameters needing updates have been made.\\n"
else
   bail "Some OS parameter updates failed.  See output above.\\n"
fi

