info_distro.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. #!/bin/bash
  2. # LinuxGSM info_distro.sh module
  3. # Author: Daniel Gibbs
  4. # Contributors: https://linuxgsm.com/contrib
  5. # Website: https://linuxgsm.com
  6. # Description: Variables providing useful info on the Operating System such as disk and performance info.
  7. # Used for command_details.sh, command_debug.sh and alert.sh.
  8. # !Note: When adding variables to this script, ensure that they are also added to the command_dev_parse_distro_details.sh script.
  9. moduleselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
  10. ### Game Server pid
  11. if [ "${status}" == "1" ]; then
  12. gameserverpid="$(TERM=screen tmux -L "${socketname}" list-sessions -F "#{session_name} #{pane_pid}" | grep "^${sessionname} " | awk '{print $NF}')"
  13. if [ "${engine}" == "source" ]; then
  14. srcdslinuxpid="$(pgrep -P "${gameserverpid}" -x srcds_linux 2> /dev/null | head -n 1)"
  15. elif [ "${querytype}" == "protocol-valve-won" ]; then
  16. hldslinuxpid="$(pgrep -P "${gameserverpid}" -x hlds_i686 2> /dev/null | head -n 1)"
  17. elif [ "${engine}" == "goldsrc" ]; then
  18. hldslinuxpid="$(pgrep -P "${gameserverpid}" -x hlds_linux 2> /dev/null | head -n 1)"
  19. fi
  20. fi
  21. ### Distro information
  22. ## Distro
  23. # Returns architecture, kernel and distro/os.
  24. arch="$(uname -m)" # Architecture e.g. x86_64
  25. kernel="$(uname -r)" # Kernel e.g. 2.6.32-042stab120.16
  26. # Gathers distro info from various sources filling in missing gaps.
  27. distro_info_array=(os-release lsb_release hostnamectl debian_version redhat-release)
  28. for distro_info in "${distro_info_array[@]}"; do
  29. if [ -f "/etc/os-release" ] && [ "${distro_info}" == "os-release" ]; then
  30. distroname="$(grep "PRETTY_NAME" /etc/os-release | awk -F= '{gsub(/"/,"",$2);print $2}')" # e.g. Ubuntu 22.04.3 LTS
  31. distroversion="$(grep "VERSION_ID" /etc/os-release | awk -F= '{gsub(/"/,"",$2);print $2}')" # e.g. 22.04
  32. distroid="$(grep "ID=" /etc/os-release | grep -v _ID | awk -F= '{gsub(/"/,"",$2);print $2}')" # e.g. ubuntu
  33. distroidlike="$(grep "ID_LIKE=" /etc/os-release | grep -v _ID | awk -F= '{gsub(/"/,"",$2);print $2}')" # e.g. debian
  34. distrocodename="$(grep "VERSION_CODENAME" /etc/os-release | awk -F= '{gsub(/"/,"",$2);print $2}')" # e.g. jammy
  35. # Special var for rhel like distros to remove point in number e.g 8.4 to just 8.
  36. if [[ "${distroidlike}" == *"rhel"* ]] || [ "${distroid}" == "rhel" ]; then
  37. distroversionrh="$(sed -nr 's/^VERSION_ID="([0-9]*).+?"/\1/p' /etc/os-release)" # e.g. 8
  38. fi
  39. elif [ "$(command -v lsb_release 2> /dev/null)" ] && [ "${distro_info}" == "lsb_release" ]; then
  40. if [ -z "${distroname}" ]; then
  41. distroname="$(lsb_release -sd)" # e.g. Ubuntu 22.04.3 LTS
  42. elif [ -z "${distroversion}" ]; then
  43. distroversion="$(lsb_release -sr)" # e.g. 22.04
  44. elif [ -z "${distroid}" ]; then
  45. distroid="$(lsb_release -si)" # e.g. Ubuntu
  46. elif [ -z "${distrocodename}" ]; then
  47. distrocodename="$(lsb_release -sc)" # e.g. jammy
  48. fi
  49. elif [ "$(command -v hostnamectl 2> /dev/null)" ] && [ "${distro_info}" == "hostnamectl" ]; then
  50. if [ -z "${distroname}" ]; then
  51. distroname="$(hostnamectl | grep "Operating System" | sed 's/Operating System: //g')" # e.g. Ubuntu 22.04.3 LTS
  52. fi
  53. elif [ -f "/etc/debian_version" ] && [ "${distro_info}" == "debian_version" ]; then
  54. if [ -z "${distroname}" ]; then
  55. distroname="Debian $(cat /etc/debian_version)" # e.g. Debian bookworm/sid
  56. elif [ -z "${distroversion}" ]; then
  57. distroversion="$(cat /etc/debian_version)" # e.g. bookworm/sid
  58. elif [ -z "${distroid}" ]; then
  59. distroid="debian"
  60. fi
  61. elif [ -f "/etc/redhat-release" ] && [ "${distro_info}" == "redhat-release" ]; then
  62. if [ -z "${distroname}" ]; then
  63. distroname="$(cat /etc/redhat-release)" # e.g. Rocky Linux release 9.2 (Blue Onyx)
  64. elif [ -z "${distroversion}" ]; then
  65. distroversion="$(rpm -qa \*-release | grep -Ei "oracle|redhat|centos|fedora|rocky|alma" | cut -d"-" -f3)" # e.g. 9.2
  66. elif [ -z "${distroid}" ]; then
  67. distroid="$(awk '{print $1}' /etc/redhat-release)" # e.g. Rocky
  68. fi
  69. fi
  70. done
  71. # Get virtual environment type.
  72. virtualenvironment="unavailable"
  73. if [ "$(command -v systemd-detect-virt 2> /dev/null)" ]; then
  74. virtualenvironment="$(systemd-detect-virt 2> /dev/null)"
  75. virtualenvironmentstatus=$?
  76. # systemd-detect-virt returns "none" with a nonzero status on bare metal.
  77. if [ "${virtualenvironment}" != "none" ] && { [ "${virtualenvironmentstatus}" != "0" ] || [[ ! "${virtualenvironment}" =~ ^[a-zA-Z0-9_-]+$ ]]; }; then
  78. virtualenvironment="unavailable"
  79. fi
  80. fi
  81. # distroversioncsv is used for selecting the correct distro csv in data directory
  82. if [ -n "${distroversionrh}" ]; then
  83. distroversioncsv="${distroversionrh}" # e.g. 8
  84. else
  85. distroversioncsv="${distroversion}" # e.g. 22.04
  86. fi
  87. # Check if distro supported by distro vendor.
  88. # distro-info available in debian based distros.
  89. if [ "$(command -v distro-info 2> /dev/null)" ]; then
  90. distrosunsupported="$(distro-info --unsupported)"
  91. distrosunsupported_array=("${distrosunsupported}")
  92. for distrounsupported in "${distrosunsupported_array[@]}"; do
  93. if [ "${distrounsupported}" == "${distrocodename}" ]; then
  94. distrosupport="unsupported"
  95. break
  96. else
  97. distrosupport="supported"
  98. fi
  99. done
  100. elif [[ "${distroidlike}" == *"rhel"* ]] || [ "${distroid}" == "rhel" ]; then
  101. # RHEL/CentOS 7 EOL 2024-06-30. Will be unsupported after this date.
  102. if [ "${distroversionrh}" -lt "8" ] && [ "$(date +%s)" -lt "1719705600" ]; then
  103. distrosupport="unsupported"
  104. else
  105. distrosupport="supported"
  106. fi
  107. fi
  108. ## Glibc version
  109. glibcversion="$(ldd --version | sed -n '1s/.* //p')" # e.g: 2.17
  110. ## tmux version
  111. if [ "$(command -V tmux 2> /dev/null)" ]; then
  112. tmuxversion="$(tmux -V | awk '{print $2}')" # e.g: tmux 3.3
  113. fi
  114. if [ "$(command -V java 2> /dev/null)" ]; then
  115. javaversion="$(java -version 2>&1 | grep "version")" # e.g: openjdk version "17.0.8.1" 2023-08-24
  116. fi
  117. if [ "$(command -v mono 2> /dev/null)" ]; then
  118. monoversion="$(mono --version 2>&1 | grep -Po '(?<=version )\d')" # e.g: 6
  119. fi
  120. if [ "$(command -v dotnet 2> /dev/null)" ]; then
  121. dotnetversion="$(dotnet --list-runtimes | grep -E 'Microsoft\.NETCore\.App' | awk '{print $2}')" # e.g: 6.0.0
  122. fi
  123. ## Uptime
  124. uptime="$(< /proc/uptime)"
  125. uptime=${uptime/[. ]*/}
  126. minutes="$((uptime / 60 % 60))"
  127. hours="$((uptime / 60 / 60 % 24))"
  128. days="$((uptime / 60 / 60 / 24))"
  129. ### Performance information
  130. ## Average server load
  131. load="$(uptime | awk -F 'load average: ' '{ print $2 }')" # e.g 0.01, 0.05, 0.11
  132. ## CPU information
  133. cpumodel="$(awk -F: '/model name/ {name=$2} END {print name}' /proc/cpuinfo | sed 's/^[ \t]*//;s/[ \t]*$//')" # e.g Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz
  134. cpulogicalcores="$(find /sys/devices/system/cpu -maxdepth 1 -type d -name 'cpu[0-9]*' 2> /dev/null | while IFS= read -r cpu; do
  135. if [ ! -f "${cpu}/online" ] || [ "$(< "${cpu}/online")" = "1" ]; then
  136. echo "${cpu##*cpu}"
  137. fi
  138. done | wc -l | tr -d ' ')"
  139. if [ -z "${cpumodel}" ] && [ "$(command -v lscpu 2> /dev/null)" ]; then
  140. cpumodel="$(lscpu 2> /dev/null | sed -n 's/^Model name:[[:space:]]*//p')"
  141. fi
  142. if [ "${cpulogicalcores:-0}" -eq 0 ] && [ "$(command -v lscpu 2> /dev/null)" ]; then
  143. cpulogicalcores="$(lscpu --parse=CPU 2> /dev/null | awk -F, '!/^#/ && $1 ~ /^[0-9]+$/ { count++ } END { print count+0 }')"
  144. fi
  145. if [ "${cpulogicalcores:-0}" -eq 0 ]; then
  146. cpulogicalcores="$(awk -F: '/^[[:space:]]*processor[[:space:]]*:/ { count++ } END { print count+0 }' /proc/cpuinfo)"
  147. fi
  148. cpuphysicalcores="$(for cpu in /sys/devices/system/cpu/cpu[0-9]*; do
  149. [ -d "${cpu}" ] || continue
  150. [ ! -f "${cpu}/online" ] || [ "$(< "${cpu}/online")" = "1" ] || continue
  151. if [ -r "${cpu}/topology/physical_package_id" ] && [ -r "${cpu}/topology/core_id" ]; then
  152. printf '%s:%s\n' "$(< "${cpu}/topology/physical_package_id")" "$(< "${cpu}/topology/core_id")"
  153. fi
  154. done | sort -u | wc -l | tr -d ' ')"
  155. if [ "${cpuphysicalcores:-0}" -eq 0 ] && [ "$(command -v lscpu 2> /dev/null)" ]; then
  156. cpuphysicalcores="$(lscpu --parse=CORE,SOCKET 2> /dev/null | awk -F, '!/^#/ && NF >= 2 && $1 != "" && $2 != "" { cores[$1 ":" $2]=1 } END { for (core in cores) count++; print count+0 }')"
  157. fi
  158. if [ "${cpuphysicalcores:-0}" -eq 0 ]; then
  159. cpuphysicalcores="$(awk -F: '
  160. BEGIN { package="0" }
  161. /^[[:space:]]*physical id[[:space:]]*:/ { package=$2; gsub(/[[:space:]]/, "", package) }
  162. /^[[:space:]]*core id[[:space:]]*:/ { core=$2; gsub(/[[:space:]]/, "", core); cores[package ":" core]=1; has_core=1 }
  163. /^[[:space:]]*cpu cores[[:space:]]*:/ { package_cores=$2; gsub(/[[:space:]]/, "", package_cores); cores_per_package[package]=package_cores }
  164. /^[[:space:]]*$/ { package="0" }
  165. END {
  166. if (has_core) { for (core in cores) total++; print total+0 }
  167. else { for (package in cores_per_package) total+=cores_per_package[package]; print total+0 }
  168. }
  169. ' /proc/cpuinfo)"
  170. fi
  171. cpucores="${cpuphysicalcores}"
  172. cpuphysicalcoresdisplay="${cpucores}"
  173. if [ "${cpucores:-0}" -eq 0 ]; then
  174. cpuphysicalcoresdisplay="unavailable"
  175. fi
  176. cpufrequencydata="$(awk -F: '/^[[:space:]]*cpu MHz[[:space:]]*:/ { value=$2; gsub(/[[:space:]]/, "", value); if (value ~ /^[0-9]+([.][0-9]+)?$/) print value }' /proc/cpuinfo)"
  177. if [ -z "${cpufrequencydata}" ] && [ "$(command -v lscpu 2> /dev/null)" ]; then
  178. cpufrequencydata="$(lscpu --parse=MHZ 2> /dev/null | awk -F, '!/^#/ && $1 ~ /^[0-9]+([.][0-9]+)?$/ { print $1 }')"
  179. fi
  180. read -r cpufreqency cpufrequencydisplay < <(printf '%s\n' "${cpufrequencydata}" | awk '
  181. NF && $1 ~ /^[0-9]+([.][0-9]+)?$/ { value=$1; if (!count || value < min) min=value; if (!count || value > max) max=value; sum+=value; count++ }
  182. END { if (!count) printf "0.00 unavailable"; else if (min == max) printf "%.2f %.2fMHz", sum/count, min; else printf "%.2f %.2f-%.2fMHz", sum/count, min, max }
  183. ')
  184. cpufreqency="${cpufreqency:-0}"
  185. export cpuphysicalcoresdisplay cpufrequencydisplay
  186. export cpufrequencyaveragedisplay="${cpufreqency}"
  187. if [ "${cpufrequencydisplay}" = "unavailable" ]; then
  188. cpufrequencyaveragedisplay="unavailable"
  189. fi
  190. # CPU usage of the game server pid
  191. if [ -n "${gameserverpid}" ]; then
  192. cpuused="$(ps --forest -o pcpu -g "${gameserverpid}" | awk '{s+=$1} END {print s}')" # integer
  193. cpuusedmhz="$(echo "${cpufreqency} * ${cpuused} / 100" | bc)" # integer
  194. fi
  195. ## Memory information
  196. # Available RAM and swap.
  197. # Newer distros can use numfmt to give more accurate results.
  198. if [ "$(command -v numfmt 2> /dev/null)" ]; then
  199. # Issue #2005 - Kernel 3.14+ contains MemAvailable which should be used. All others will be calculated.
  200. # get the raw KB values of these fields.
  201. physmemtotalkb="$(grep MemTotal /proc/meminfo | awk '{print $2}')" # integer
  202. physmemfreekb="$(grep ^MemFree /proc/meminfo | awk '{print $2}')" # integer
  203. physmembufferskb="$(grep ^Buffers /proc/meminfo | awk '{print $2}')" # integer
  204. physmemcachedkb="$(grep ^Cached /proc/meminfo | awk '{print $2}')" # integer
  205. physmemreclaimablekb="$(grep ^SReclaimable /proc/meminfo | awk '{print $2}')" # integer
  206. # check if MemAvailable Exists.
  207. if grep -q ^MemAvailable /proc/meminfo; then
  208. physmemactualfreekb="$(grep ^MemAvailable /proc/meminfo | awk '{print $2}')" # integer
  209. else
  210. physmemactualfreekb="$((physmemfreekb + physmembufferskb + physmemcachedkb))" # integer
  211. fi
  212. # Available RAM and swap.
  213. physmemtotalmb="$(((physmemtotalkb + 512) / 1024))" # integer
  214. physmemtotalgb="$(((physmemtotalmb + 512) / 1024))" # integer
  215. physmemtotal="$(numfmt --to=iec --from=iec --suffix=B "${physmemtotalkb}K")" # string
  216. physmemfree="$(numfmt --to=iec --from=iec --suffix=B "${physmemactualfreekb}K")" # string
  217. physmemused="$(numfmt --to=iec --from=iec --suffix=B "$((physmemtotalkb - physmemfreekb - physmembufferskb - physmemcachedkb - physmemreclaimablekb))K")" # string
  218. physmemavailable="$(numfmt --to=iec --from=iec --suffix=B "${physmemactualfreekb}K")" # string
  219. physmemcached="$(numfmt --to=iec --from=iec --suffix=B "$((physmemcachedkb + physmemreclaimablekb))K")" # string
  220. swaptotal="$(numfmt --to=iec --from=iec --suffix=B "$(grep ^SwapTotal /proc/meminfo | awk '{print $2}')K")" # string
  221. swapfree="$(numfmt --to=iec --from=iec --suffix=B "$(grep ^SwapFree /proc/meminfo | awk '{print $2}')K")" # string
  222. swapused="$(numfmt --to=iec --from=iec --suffix=B "$(($(grep ^SwapTotal /proc/meminfo | awk '{print $2}') - $(grep ^SwapFree /proc/meminfo | awk '{print $2}')))K")" # string
  223. # RAM usage of the game server pid
  224. if [ "${gameserverpid}" ]; then
  225. memusedmb="$(ps --forest -o rss -g "${gameserverpid}" | awk '{s+=$1} END {print s}' | awk '{$1/=1024;printf "%.0f",$1}{print $2}')" # integer
  226. memusedpct="$(ps --forest -o %mem -g "${gameserverpid}" | awk '{s+=$1} END {print s}')" # integer
  227. fi
  228. else
  229. # Older distros will need to use free.
  230. # Older versions of free do not support -h option.
  231. if free -h > /dev/null 2>&1; then
  232. humanreadable="-h"
  233. else
  234. humanreadable="-m"
  235. fi
  236. physmemtotalmb="$(free -m | awk '/Mem:/ {print $2}')" # integer
  237. physmemtotalgb="$(free -m | awk '/Mem:/ {print $2}')" # integer
  238. physmemtotal="$(free ${humanreadable} | awk '/Mem:/ {print $2}')" # string
  239. physmemfree="$(free ${humanreadable} | awk '/Mem:/ {print $4}')" # string
  240. physmemused="$(free ${humanreadable} | awk '/Mem:/ {print $3}')" # string
  241. oldfree="$(free ${humanreadable} | awk '/cache:/')"
  242. if [ "${oldfree}" ]; then
  243. physmemavailable="n/a" # string
  244. physmemcached="n/a" # string
  245. else
  246. physmemavailable="$(free ${humanreadable} | awk '/Mem:/ {print $7}')" # string
  247. physmemcached="$(free ${humanreadable} | awk '/Mem:/ {print $6}')" # string
  248. fi
  249. swaptotal="$(free ${humanreadable} | awk '/Swap:/ {print $2}')" # string
  250. swapused="$(free ${humanreadable} | awk '/Swap:/ {print $3}')" # string
  251. swapfree="$(free ${humanreadable} | awk '/Swap:/ {print $4}')" # string
  252. fi
  253. ### Disk information
  254. ## Available disk space on the partition.
  255. filesystem="$(LC_ALL=C df -hP "${rootdir}" | tail -n 1 | awk '{print $1}')" # string e.g /dev/sda
  256. totalspace="$(LC_ALL=C df -hP "${rootdir}" | tail -n 1 | awk '{print $2}')" # string e.g 20G
  257. usedspace="$(LC_ALL=C df -hP "${rootdir}" | tail -n 1 | awk '{print $3}')" # string e.g 15G
  258. availspace="$(LC_ALL=C df -hP "${rootdir}" | tail -n 1 | awk '{print $4}')" # string e.g 5G
  259. ## LinuxGSM used space total.
  260. rootdirdu="$(du -sh "${rootdir}" 2> /dev/null | awk '{print $1}')"
  261. if [ -z "${rootdirdu}" ]; then
  262. rootdirdu="0M"
  263. fi
  264. ## LinuxGSM used space in serverfiles dir.
  265. serverfilesdu="$(du -sh "${serverfiles}" 2> /dev/null | awk '{print $1}')"
  266. if [ -z "${serverfilesdu}" ]; then
  267. serverfilesdu="0M"
  268. fi
  269. ## LinuxGSM used space total minus backup dir.
  270. rootdirduexbackup="$(du -sh --exclude="${backupdir}" "${serverfiles}" 2> /dev/null | awk '{print $1}')"
  271. if [ -z "${rootdirduexbackup}" ]; then
  272. rootdirduexbackup="0M"
  273. fi
  274. ## Backup info
  275. if [ -d "${backupdir}" ]; then
  276. # Used space in backups dir.
  277. backupdirdu="$(du -sh "${backupdir}" | awk '{print $1}')"
  278. # If no backup dir, size is 0M.
  279. if [ -z "${backupdirdu}" ]; then
  280. backupdirdu="0M"
  281. fi
  282. # number of backups set to 0 by default.
  283. backupcount=0
  284. # If there are backups in backup dir.
  285. if [ "$(find "${backupdir}" -maxdepth 1 -type f -name "*.tar.*" | wc -l)" -ne "0" ]; then
  286. # number of backups.
  287. backupcount="$(find "${backupdir}" -maxdepth 1 -type f -name "*.tar.*" | wc -l)" # integer
  288. # most recent backup.
  289. lastbackup="$(find "${backupdir}" -maxdepth 1 -type f -name "*.tar.*" -printf '%T@ %p\n' 2> /dev/null | sort -nr | head -n 1 | cut -d' ' -f2-)" # string
  290. # date of most recent backup.
  291. lastbackupdate="$(date -r "${lastbackup}")" # string
  292. # no of days since last backup.
  293. lastbackupdaysago="$((($(date +'%s') - $(date -r "${lastbackup}" +'%s')) / 60 / 60 / 24))" # integer
  294. # size of most recent backup.
  295. lastbackupsize="$(du -h "${lastbackup}" | awk '{print $1}')" # string
  296. fi
  297. fi
  298. # Network Interface name
  299. netint=$(${ipcommand} -o addr | grep "${ip}" | awk '{print $2}') # e.g eth0
  300. netlink=$(${ethtoolcommand} "${netint}" 2> /dev/null | grep Speed | awk '{print $2}') # e.g 1000Mb/s
  301. # Sets the SteamCMD glibc requirement if the game server requirement is less or not required.
  302. if [ "${appid}" ]; then
  303. if [ "${glibc}" = "null" ] || [ -z "${glibc}" ] || [ "$(printf '%s\n' "${glibc}" "2.14" | sort -V | head -n 1)" != "2.14" ]; then
  304. glibc="2.14"
  305. fi
  306. fi
  307. # Gather Port Info using ss.
  308. ssinfo="$(ss -tuplwn)"