#!/usr/bin/env bash

# Script should be run as root user
if [[ $EUID -ne 0 ]]; then
	echo "Error: diagnostics script must be run as the root user."
	exit 1
fi

read -r -d '' USAGE <<- EOF
Usage: rstudio-server run-diagnostics [--output-file=<path>]

Options:
	-o, --output-file  An optional location where the RStudio diagnostics
	                   report should be written. Defaults to a file in
	                   the /tmp/rstudio-diagnostics directory.
	
EOF

# Like 'cat', but also echos file names as a header,
# and prints a warning if the file contains characters
# not representable in the C locale.
vcat () {

	for FILE in "$@"; do

		ESCAPED=$(LC_ALL=C printf "%q" "${FILE}")
		if [ "${ESCAPED}" != "${FILE}" ]; then
			echo "WARNING: File '${FILE}' [${ESCAPED}] contains unexpected characters." 1>&6
		fi

		LC_ALL=C printf "==> %q <==\n" "${FILE}"
		cat "${FILE}" 2> /dev/null
		echo ""

	done

}

# parse command line arguments
while [ "$#" -ne "0" ]; do
	
	case "$1" in

	-h|--help)
		echo "${USAGE}"
		exit 0
	;;

	--output-file=*)
		OUTPUT_FILE="${1#*=}"
	;;

	-o|--output-file)
		OUTPUT_FILE="$2"
		shift
	;;

	*)
		echo "Error: unexpected argument '$1'."
		exit 1
	;;

	esac

	shift

done

if [ -z "${OUTPUT_FILE}" ]; then
	DATE="$(date +"%y%m%d%H%M%S")"
	OUTPUT_FILE="/tmp/rstudio-diagnostics/rsp-diagnostics-report-$(hostname)-${DATE}.txt"
fi

mkdir -p "$(dirname "${OUTPUT_FILE}")"

h1 () {
	echo ""
	echo "-------------------------------"
	echo "$1"
	echo "-------------------------------"
	echo ""
}

h2 () {
	echo ""
	echo "## $1"
}

cat_config () {

	find "$1/" -maxdepth 1 ! -type d | while IFS= read -r f; do
		vcat "${f}"
		echo ""
      done

}

echo "Generating diagnostics report ..."
echo ""

# save stdout, stderr descriptors
exec 5<&1
exec 6<&2

# start redirecting output to file
exec 1> "${OUTPUT_FILE}"
exec 2>&1

echo ""
echo "Started diagnostics at $(date)."
echo ""

echo "Hostname: $(hostname)"

h1 "Operating sytem information"
tail -n+1 -v /etc/*-release

if [ -f /etc/lsb-release ]; then
	DISTRO=$(. /etc/lsb-release; echo "${DISTRIB_ID}")
elif [ -f /etc/os-release ]; then
	ID=$(. /etc/os-release; echo "${ID}")
	case "${ID}" in
	*suse*)   DISTRO="SUSE"   ;;
	*debian*) DISTRO="Debian" ;;
	*centos*) DISTRO="CentOS" ;;
	*rhel*)   DISTRO="RedHat" ;;
	esac
elif [ -f /etc/centos-release ]; then
	DISTRO="CentOS"
elif [ -f /etc/redhat-release ]; then
	DISTRO="RedHat"
fi

if [ -z "${DISTRO}" ]; then
	DISTRO="RedHat"
	echo "Distribution unable to be determined; RedHat set as default."
fi

h2 "uname -a"
uname -a

h1 "Mounts"

h2 "Mount point space usage"
df -h | sort

h2 "Mount point inode usage"
df -i | sort

if command -v nfsstat &> /dev/null; then
	h2 "nfsstat -m"
	nfsstat -m || echo "<nfsstat -m had exit code $?>"
fi

h1 "R Installation"
echo "which R: $(which R)"
echo "which gcc: $(which gcc)"
if [[ "$(which R 2> /dev/null)" ]] && [[ "$(R RHOME 2> /dev/null)" ]]; then

	R_HOME=$(R RHOME)

	echo "R_HOME: $R_HOME"
	R --version

	h1 "R config files"

	# Parse contents of r-versions to detect all R_HOME paths and store in array
	R_CONFIG_DIR=($(cat /var/lib/rstudio-server/r-versions | grep R_HOME | cut -d '"' -f4))

	# append /etc to each directory in array
	R_CONFIG_DIR=( "${R_CONFIG_DIR[@]/%//etc}" )

	for m in "${R_CONFIG_DIR[@]}"
	do
		find "${m}/" -maxdepth 1 ! -type d | while IFS= read -r f
		do
			vcat "${f}" 2> /dev/null
			echo ""
		done
	done
else
	echo "R not found."
fi

h1 "RStudio Server"

h2 "Status of rstudio-server package"
case "${DISTRO}" in
"RedHat" | "CentOS")
	yum info rstudio-server
;;
"Ubuntu" | "Debian")
	dpkg -s rstudio-server
;;
"SUSE")
	zypper info rstudio-server
;;
esac

h2 "rstudio-server version, status, and verify-installation"
rstudio-server version
rstudio-server status
rstudio-server verify-installation

h2 "rstudio-launcher version and status"
rstudio-launcher version
rstudio-launcher status

h1 "RStudio Server config files"
RSP_CONFIG_DIR="/etc/rstudio"
cat_config "$RSP_CONFIG_DIR"
vcat /etc/profile /etc/environment

h2 "PAM Configuration"
vcat /etc/pam.conf /etc/security/pam_env.conf
PAM_CONFIG_DIR="/etc/pam.d"
cat_config "$PAM_CONFIG_DIR"

LB_SCRIPT="/usr/lib/rstudio-server/bin/rserver-balancer"
h1 "Custom load-balancing script"
if [ -f "$LB_SCRIPT" ]; then
	vcat "$LB_SCRIPT"
else
	echo "<${LB_SCRIPT} does not exist>"
fi

h1 "License manager status"
if ping -c 1 www.wyday.com &> /dev/null; then
	echo "OK: ping to www.wyday.com successful"
else
	echo "NOTE: ping to www.wyday.com unsuccessful"
fi

h2 "Online license status"
rstudio-server license-manager status

h2 "Offline license status"
rstudio-server license-manager status-offline

h1 "Machine environment"
env

h2 "ldd rserver"
ldd /usr/lib/rstudio-server/bin/rserver

h2 "ldd rsession"
ldd /usr/lib/rstudio-server/bin/rsession

h1 "R environment outside RStudio (default R)"

h2 "Sys.info()"
Rscript -e 'Sys.info()'

h2 "sessionInfo()"
Rscript -e 'sessionInfo()'

h2 "Sys.getenv()"
Rscript -e 'Sys.getenv()'

h2 ".libPaths()"
Rscript -e '.libPaths()'

h2 "R RHOME:"
echo "${R_HOME}"

if [ -f "/var/log/messages" ]; then
	SYS_LOG_PATH="/var/log/messages"
elif [ -f "/var/log/syslog" ]; then
	SYS_LOG_PATH="/var/log/syslog"
fi

h1 "RStudio messages in the system log"
if [ -f "$SYS_LOG_PATH" ]; then
	grep -E "rstudio|rserver|rsession|rworkspaces" "${SYS_LOG_PATH}"
else
	echo "<failed to determine path to system logs>"
fi

h1 "RStudio Server log"

RSP_LOG_PATH="/var/lib/rstudio-server/monitor/log/rstudio-server.log"
if [ -f "$RSP_LOG_PATH" ]; then
	cat "$RSP_LOG_PATH"
else
	echo "<${RSP_LOG_PATH} does not exist>"
fi

if [ -f "$SYS_LOG_PATH" ]; then
	grep "rstudio\(-.*\)\?-launcher" "${SYS_LOG_PATH}"
else
	echo "<failed to determine path to system logs>"
fi

# also include any files seen in a configured log directory
if [ -f /etc/rstudio/logging.conf ]; then
	RSP_LOG_DIRS=$(grep -E '^log-dir=' /etc/rstudio/logging.conf | cut -d'=' -f2)
	for RSP_LOG_DIR in "${RSP_LOG_DIRS[@]}"; do
		if [ -d "${RSP_LOG_DIR}" ]; then
			cat_config "${RSP_LOG_DIR}"
		fi
	done
fi

h1 "Job Launcher debug logs"
LAUNCHER_CONF_FILE="/etc/rstudio/launcher.conf"
LAUNCHER_LOG_PATH="/var/log/rstudio/launcher"
declare -a LAUNCHER_CONF_FILES
cat $LAUNCHER_CONF_FILE | while read LINE; do
      if [[ $LINE =~ "logging-dir" ]]; then
            launcherLoggingDir=$(echo $LINE | sed -n /^logging-dir=/s/^logging-dir=//p)
            if [ -z "$launcherLoggingDir" ]; then
              continue
            fi
            LAUNCHER_LOG_PATH="${launcherLoggingDir}"
	elif [[ $LINE =~ "config-file" ]]; then
		configFile=$(echo $LINE | sed -n /^config-file=/s/^config-file=//p)
		if [ -z "$configFile" ]; then
			continue
		fi
		LAUNCHER_CONF_FILES+=("${configFile}")
	fi
done

LAUNCHER_LOG_FILES=($(find $LAUNCHER_LOG_PATH -name "*.log"))
for LOG_FILE in "${LAUNCHER_LOG_FILES[@]}"; do 
	vcat "$LOG_FILE"
done

for CONF_FILE in "${LAUNCHER_CONF_FILES[@]}"; do
	cat $CONF_FILE | while read LINE; do
            if [[ $LINE =~ "logging-dir" ]]; then
                  launcherLoggingDir=$(echo $LINE | sed -n /^logging-dir=/s/^logging-dir=//p)
                  if [ -z "$launcherLoggingDir" ]; then
                        continue
                  fi
			PLUGIN_LOG_FILES=($(find $scratchPath -name "*.log"))
			for LOG_FILE in "${PLUGIN_LOG_FILES[@]}"; do
				vcat $LOG_FILE
			done
		fi
	done
done

h1 "Java environment"

h2 "whereis java"
whereis java

h2 "java -version"
java -version

h2 "JAVA_HOME"
echo "${JAVA_HOME-"<unset>"}"

h2 "PATH"
echo "${PATH-"<unset>"}"

if command -v jps &> /dev/null; then
	echo "JVM Status:"
	jps -lvm
fi

echo ""
echo "** Compare this data to the contents of the ${R_CONFIG_DIR}/javaconf file above."
echo ""

h1 "Network connections"

cat <<- EOF
http_proxy:  ${http_proxy-"<unset>"}
https_proxy: ${https_proxy-"<unset>"}
all_proxy:   ${all_proxy-"<unset>"}
EOF

if ping -c 1 www.google.com &> /dev/null; then
	echo "OK: ping to www.google.com successful"
else
	echo "NOTE: ping to www.google.com unsuccessful"
fi

h2 "GET headers"
curl -vs www.google.com 1> /dev/null

h2 "Check websocket handshake headers"
PORT=$(sed -nE 's/^www-port[^{0-9}]+//p' < /etc/rstudio/rserver.conf)
[ -z "${PORT}" ] && PORT=8787
curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" -H "Host: localhost:$PORT" -H "Origin: localhost:$PORT" http://localhost:$PORT

echo ""
echo "Finished diagnostics report at $(date)."

# restore stdout, stderr
exec 1>&5
exec 2>&6

# notify that we're all done
cat <<- EOF

Diagnostics report has been generated: ${OUTPUT_FILE}

Please note that the diagnostics report may contain sensitive data.
You may wish to sanitize data that you feel is sensitive, but please us know if you have done so.

EOF

if [ "$(stat -c '%s' "${OUTPUT_FILE}")" -gt 52428800 ]; then

	cat <<- EOF
	The generated report is quite large ($(du -h "${OUTPUT_FILE}" | cut -f1)).
	Consider compressing and using our Support Upload tool to submit this file:

	http://apps.rstudio.com/SupportUpload/

	Please let us know you have done this, along with the name of the uploaded file.
	EOF

fi

