#!/bin/bash
#==============================================================================
# Copyright and license info is available in the LICENSE file included with
# the Server Deployment Package (SDP), and also available online:
# https://swarm.workshop.perforce.com/projects/perforce-software-sdp/view/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}"; }

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

# 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##*/} v$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

