#!/bin/bash
#==============================================================================
# Declarations and Environment
set -u
declare ThisScript="${0##*/}"
declare ThisHost=${HOSTNAME%%.*}
declare ThisUser=
declare CmdLine="$ThisScript $*"
declare RunUser=perforce
declare RunGroup=perforce
declare SudoersFile="/etc/sudoers.d/$RunUser"
declare RunUserHomeDir="/home/$RunUser"
declare SDPMountPoints="/hxdepots /hxcheckpoints /hxmetadata /hxmetadata1 /hxmetadata2 /hxmetadata3 /hxlogs"
declare SDPStructureDirs="/p4 /opt/perforce/helix-sdp"
declare Version=1.4.1
declare Log=
declare LogLink=
declare -i ErrorCount=0
declare -i Debug=${DEBUG:-0}
declare -i NoOp=1
declare -i ItemsCleanedCount=0
declare -i RetryCount=0
declare -i RetryMax=10
declare -i UserRemoved=0
declare SDPInstance=1
declare P4ROOT="/p4/$SDPInstance/root"
declare P4DPidFile="$P4ROOT/server.pid"
declare ServiceFile=
declare ServiceName=
declare FirewallDir=
declare TmpFile=
declare -i ServicesRemoved=0
declare -i i=0
declare H1="=============================================================================="
#==============================================================================
# Local Functions
#------------------------------------------------------------------------------
# Functions msg(), dbg(), and bail().
# Sample Usage:
# bail "Missing something important. Aborting."
# bail "Aborting with exit code 3." 3
function msg () { echo -e "$*"; }
function errmsg () { msg "\\nError: ${1:-Unknown Error}\\n"; ErrorCount+=1; }
function dbg () { [[ "$Debug" -eq 0 ]] || msg "DEBUG: $*"; }
function bail () { errmsg "${1:-Unknown Error}"; exit "${2:-1}"; }
#------------------------------------------------------------------------------
# Functions run($cmd, $desc, $ignoreNoOp, $incrementItemCount)
#
# This function is similar to functions defined in SDP core libraries, but we
# need to duplicate them here since this script runs before the SDP is
# available on the machine (and thus this script must limited dependencies).
#
# This runs commands or merely displays them if NoOp is 1.
#
# Arguments:
# $1 - command to execute.
# $2 - description of command to run
# $3 - ignoreNoOp. If 1, always execute command, even if NoOP is 1.
# $4 - incrementItemCount. If 1, increment global ItemsCleanedCount
# to count actions involved in cleanup.
function run {
local cmd="${1:-echo Testing run}"
local desc="${2:-}"
local -i ignoreNoOp=${3:-0}
local -i incrementItemCount=${4:-1}
[[ -n "$desc" ]] && msg "$desc"
if [[ "$NoOp" -eq 0 || "$ignoreNoOp" -eq 1 ]]; then
msg "Running: $cmd"
$cmd
CMDEXITCODE=$?
else
msg "NO_OP: Would have run: $cmd"
CMDEXITCODE=0
fi
[[ "$incrementItemCount" -eq 1 ]] && ItemsCleanedCount+=1
return $CMDEXITCODE
}
#------------------------------------------------------------------------------
# Function: usage (required function)
#
# Input:
# $1 - style, either -h (for short form) or -man (for man-page like format).
#------------------------------------------------------------------------------
function usage
{
declare style="${1:--h}"
declare errorMessage="${2:-}"
if [[ -n "$errorMessage" ]]; then
msg "\\nUSAGE ERROR: $errorMessage\\n"
fi
msg "USAGE for $ThisScript v$Version:
$ThisScript [-d|-D]
or
$ThisScript [-h|-man]
"
if [[ $style == -man ]]; then
msg "
DESCRIPTION:
This script undoes what install_sdp.sh does to enable a series of clean
start-from-scratch tests.
DEBUGGING OPTIONS:
-d Enable debug message.
-D Enable extreme debugging with bash 'set -x'. Implies '-d'.
HELP OPTIONS:
-h Display short help message.
-man Display this full manual page.
-V Display script and version.
"
fi
exit 2
}
#==============================================================================
# Command Line Processing
declare -i shiftArgs=0
set +u
while [[ $# -gt 0 ]]; do
case $1 in
(-y) NoOp=0;;
(-h) usage -h;;
(-man|--help) usage -man;;
(-V) msg "$ThisScript v$Version"; exit 2;;
(-d) Debug=1;;
(-D) Debug=1; set -x;; # Debug; use 'set -x' mode.
(-*) usage -h "Unknown flag/option ($1).";;
(*) usage -h "Unknown parameter ($1).";;
esac
# Shift (modify $#) the appropriate number of times.
shift; while [[ $shiftArgs -gt 0 ]]; do
[[ $# -eq 0 ]] && bail "Usage Error: Wrong number of parameters to flags/optionss."
shiftArgs=$shiftArgs-1
shift
done
done
set -u
#==============================================================================
# Main Program
Log="${ThisScript%.sh}.$(date +'%Y-%m-%d-%H%M%S').log"
LogLink="${ThisScript%.sh}.log"
touch "$Log" || bail "Could not initialize log with: touch \"$Log\""
exec > >(tee "$Log")
exec 2>&1
if [[ -e "$LogLink" ]]; then
if [[ -L "$LogLink" ]]; then
rm -f "$LogLink"
else
# If the name that should be a symlink is not a symlink, move it aside before
# creating the symlink.
mv -f "$LogLink" "${LogLink%.log}.old.log"
fi
fi
# Point LogLink symlink to current log. Use a subshell so the 'cd' doesn't persist.
ln -s "$Log" "$LogLink"
msg "${H1}\\nLog is: $Log\\n"
ThisUser=$(whoami)
msg "Started $ThisScript v$Version on host $ThisHost as user $ThisUser at $(date), called as:\\n\\t$CmdLine\\n"
if [[ "$NoOp" -eq 0 ]]; then
msg "Live Operation Mode."
else
msg "Dry Run / Preview Mode."
fi
command -v getent > /dev/null || bail "Can't find utility getent in PATH."
#------------------------------------------------------------------------------
# Stop and remove systemd services.
ServicesRemoved=0
for bin in p4d p4broker p4p; do
ServiceName="${bin}_$SDPInstance"
ServiceFile="/etc/systemd/system/${ServiceName}.service"
if [[ -r "$ServiceFile" ]]; then
run "systemctl stop $ServiceName" "Issuing stop to service $ServiceName." ||\
errmsg "Error calling: systemctl stop $ServiceName"
run "rm -f $ServiceFile" "Removing $ServiceFile" ||\
errmsg "Could not do: rm -f $ServiceFile"
ServicesRemoved=1
fi
done
if [[ "$ServicesRemoved" -eq 1 ]]; then
run "systemctl daemon-reload" "Reloading systemd after removing services." 0 0 ||\
errmsg "Error encountered doing: systemctl daemon-reload"
fi
#------------------------------------------------------------------------------
# Remove local OSUSER (default 'perforce') account.
if id -u "$RunUser" > /dev/null 2>&1; then
if [[ -r "$P4DPidFile" ]]; then
run "kill $(cat "$P4DPidFile")" "Gently killing p4d to avoid errant systemd issues." ||\
errmsg "Error killing pid in $P4DPidFile. If the process is still, a SIGKILL will be sent."
sleep 1
fi
if crontab -l -u "$RunUser" > /dev/null 2>&1; then
run "crontab -r -u $RunUser" "Removing crontab for user $RunUser" ||\
errmsg "Error removing crontab for user $RunUser."
fi
# If anything remains running, kill away.
if run "pgrep -u $RunUser" "Checking for processes by user $RunUser." 1 0 > /dev/null; then
run "pkill -u $RunUser -KILL" \
"Killing all processes by OSUSER $RunUser" ||\
errmsg "Failed to kill all processes by user $RunUser."
else
msg "No OS processes detected for user $RunUser."
fi
UserRemoved=0
for ((RetryCount=0; i<RetryMax; i++)); do
if run "userdel $RunUser" "Removing user $RunUser."; then
msg "Verified: User $RunUser does not exist."
UserRemoved=1
break
else
RetryCount+=1
msg "Could not remove user $RunUser on try $RetryCount. Waiting a bit and trying (up to $RetryMax times)."
sleep 2
fi
done
[[ "$UserRemoved" -eq 1 ]] || errmsg "Failed to remove user $RunUser after $RetryMax tries."
else
msg "Verified: User $RunUser does not exist."
fi
if [[ -r "$SudoersFile" ]]; then
run "rm -f $SudoersFile" ||\
errmsg "Failed to do: rm -f $SudoersFile"
else
msg "Verified: No sudoers file for user $RunUser exists."
fi
if getent group "$RunGroup" > /dev/null 2>&1; then
run "groupdel $RunGroup" "Removing group $RunGroup" ||\
errmsg "Failed to remove group $RunGroup."
else
msg "Verified: Group $RunGroup does not exist."
fi
if [[ -d "$RunUserHomeDir" ]]; then
run "rm -rf $RunUserHomeDir" "Removing user home dir: $RunUserHomeDir" ||\
errmsg "Failed to remove user home dir [$RunUserHomeDir]."
else
msg "Verified: Home dir for user $RunUser [$RunUserHomeDir] does not exist."
fi
TmpFile=$(mktemp)
# Get a list of mount points.
mount | awk '{print $3}' > "$TmpFile"
# For directories that might be mount points, check to see if they are indeed
# mount points. If they not, simply 'rm -rf' on the directory. If they are
# mount ponts, do 'rm -rf $d/*'
for d in $SDPMountPoints; do
if [[ -d "$d" ]]; then
if grep -q "^$d$" "$TmpFile"; then
run "rm -rf $d/*" "Removing directories under SDP mount point: $d" ||\
errmsg "Failed to remove directories under SDP mount point: $d/*"
else
run "rm -rf $d" "Removing SDP simulated mount point dir: $d" ||\
errmsg "Failed to remove SDP simulated mount point dir: $d"
fi
else
msg "Verified: SDP dir [$d] does not exist."
fi
done
rm -f "$TmpFile"
for d in $SDPStructureDirs; do
if [[ -d "$d" ]]; then
run "rm -rf $d" "Removing SDP dir: $d" ||\
errmsg "Failed to remove SDP dir [$d]."
else
msg "Verified: SDP dir [$d] does not exist."
fi
done
FirewallDir=/etc/firewalld/services
ServicesRemoved=0
if [[ -d "$FirewallDir" ]]; then
cd "$FirewallDir" || bail "Could not do: cd $FirewallDir"
msg "Checking for firewalld service files in: $PWD"
if [[ -n "$(command -v firewall-cmd)" ]]; then
for ServiceFile in p4*.xml; do
[[ -r "$ServiceFile" ]] || continue
ServiceName="${ServiceFile%.xml}"
run "firewall-cmd --permanent --delete-service=$ServiceName" \
"Deleting firewall entry for $ServiceName" ||\
errmsg "Deleting firewall entry for $ServiceName failed."
ServicesRemoved=1
# Firewalld renames the *.xml files to *.xml.old upon deletion of the
# rule.
if [[ -r "$PWD/${ServiceFile}.old" ]]; then
run "rm -f $PWD/${ServiceFile}.old" "Removing $PWD/${ServiceFile}.old" ||\
errmsg "Deleting file $PWD/${ServiceFile}.old failed."
fi
done
if [[ "$ServicesRemoved" -eq 1 ]]; then
run "firewall-cmd --reload" "Firewall reload after firewalld service cleanup." 0 0 ||\
errmsg "Firewall reload failed after firewalld service cleanup."
else
dbg "No firewalld services for Helix Core found to remove."
fi
else
msg "No firewall-cmd firewall utilty found. Skipping firewall cleanup."
fi
fi
FirewallDir=/etc/ufw/applications.d
ServicesRemoved=0
if [[ -d "$FirewallDir" ]]; then
cd "$FirewallDir" || bail "Could not do: cd $FirewallDir"
msg "Checking for ufw firewall application files in: $PWD"
if [[ -n "$(command -v ufw)" ]]; then
for ServiceFile in p4*; do
run "rm -f $PWD/${ServiceFile}" "Removing $PWD/${ServiceFile}" ||\
errmsg "Failed to remove file: $PWD/${ServiceFile}"
ServicesRemoved=1
done
if [[ "$ServicesRemoved" -eq 1 ]]; then
run "ufw reload" "Firewall reload after ufw application cleanup." 0 0 ||\
errmsg "Firewall reload failed after ufw application cleanup."
else
dbg "uo ufw applications for Helix Core found to remove."
fi
else
msg "No ufw firewall utilty found. Skipping firewall cleanup."
fi
fi
if [[ "$ErrorCount" -eq 0 ]]; then
msg "\\nAll processing completed OK. $ItemsCleanedCount items needed cleaning."
else
msg "\\nProcessing completed, but with $ErrorCount errors. $ItemsCleanedCount items needed cleaning."
fi
msg "\\nLog is: $Log\\n${H1}"
exit "$ErrorCount"