command_stop.sh 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619
  1. #!/bin/bash
  2. # LinuxGSM command_stop.sh module
  3. # Author: Daniel Gibbs
  4. # Contributors: https://linuxgsm.com/contrib
  5. # Website: https://linuxgsm.com
  6. # Description: Stops the server.
  7. commandname="STOP"
  8. commandaction="Stopping"
  9. moduleselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
  10. fn_firstcommand_set
  11. # Only stop the server if there are no players online.
  12. # An explicit stop or restart always respects players when the feature is on.
  13. # Other commands that stop the server as a side effect (e.g. update) only respect
  14. # players when stoponlyifnoplayersallcommands is on.
  15. fn_stop_players_online() {
  16. if [ "${firstcommandname}" == "STOP" ] || [ "${firstcommandname}" == "RESTART" ] || [ "${stoponlyifnoplayersallcommands}" == "on" ]; then
  17. check_players_online.sh
  18. if [ -n "${playersonline}" ]; then
  19. fn_print_info_nl "${playersonline} players are on the server: stop prevented"
  20. fn_script_log_info "${playersonline} players are on the server: stop prevented"
  21. echo "${playersonline}" > "${lockdir:?}/${selfname}-player-numbers.lock"
  22. # Monitor runs the update once the server is empty.
  23. if [ "${firstcommandname}" == "UPDATE" ]; then
  24. date '+%s' > "${lockdir:?}/${selfname}-update-request.lock"
  25. if [ "${forceupdate}" == "1" ]; then
  26. echo "force" >> "${lockdir:?}/${selfname}-update-request.lock"
  27. fi
  28. fn_print_info_nl "Update postponed until the server is empty"
  29. fn_script_log_info "Update postponed until the server is empty"
  30. elif [ "${firstcommandname}" == "BACKUP" ]; then
  31. if [ ! -f "${lockdir}/${selfname}-backup-request.lock" ]; then
  32. date '+%s' > "${lockdir:?}/${selfname}-backup-request.lock"
  33. fi
  34. fn_print_info_nl "Backup postponed until the server is empty"
  35. fn_script_log_info "Backup postponed until the server is empty"
  36. fi
  37. fn_stop_cancel_locks
  38. # Exit the whole command, including when called by another command (e.g. update) with exitbypass.
  39. unset exitbypass
  40. core_exit.sh
  41. fi
  42. fi
  43. }
  44. # Removes the lockfiles of the command that called stop when it ends without stopping the server.
  45. fn_stop_cancel_locks() {
  46. if [ "${firstcommandname}" == "UPDATE" ] || [ "${firstcommandname}" == "WORKSHOP-UPDATE" ]; then
  47. rm -f "${lockdir:?}/update.lock" "${lockdir:?}/last-updated.lock"
  48. elif [ "${firstcommandname}" == "VALIDATE" ]; then
  49. rm -f "${lockdir:?}/update.lock"
  50. elif [ "${firstcommandname}" == "BACKUP" ]; then
  51. rm -f "${lockdir:?}/backup.lock"
  52. fi
  53. }
  54. # Tells players a warning countdown was cancelled, used when CTRL-C is pressed.
  55. fn_stop_warn_players_cancel() {
  56. if [ "$(cat "${lockdir}/${selfname}-warning.lock" 2> /dev/null)" == "$$" ]; then
  57. fn_stop_warn_players_send "${warnplayersaction}" "0"
  58. rm -f "${lockdir:?}/${selfname}-warning.lock"
  59. fn_stop_cancel_locks
  60. fi
  61. }
  62. # Sets the game's chat command used to warn players, %s is replaced by the message.
  63. fn_stop_warn_players_command() {
  64. warncommand=""
  65. if [ "${shortname}" == "pz" ]; then
  66. warncommand='servermsg "%s"'
  67. elif [ "${shortname}" == "dst" ]; then
  68. warncommand='c_announce("%s")'
  69. elif [ "${shortname}" == "scpsl" ]; then
  70. warncommand="/bc 10 %s"
  71. elif [ "${shortname}" == "vints" ]; then
  72. warncommand="/announce %s"
  73. elif [ "${shortname}" == "wmc" ]; then
  74. warncommand="alert %s"
  75. elif [ "${shortname}" == "av" ]; then
  76. warncommand="/say %s"
  77. elif [ "${shortname}" == "fctr" ]; then
  78. warncommand="%s"
  79. elif [ "${engine}" == "source" ] || [ "${engine}" == "source2" ] || [ "${engine}" == "goldsrc" ]; then
  80. warncommand="say %s"
  81. else
  82. local saygames=(bt cod cod2 cod4 coduo codwaw ct etl hyt jk2 mc mcb mohaa mta nec pmc q2 q3 q4 ql qw rtcw rw sof2 terraria unt wet wf xnt)
  83. local game
  84. for game in "${saygames[@]}"; do
  85. if [ "${shortname}" == "${game}" ]; then
  86. warncommand="say %s"
  87. fi
  88. done
  89. fi
  90. }
  91. # Sends a warning message to players using the game's own chat command.
  92. # A minutes value of 0 sends a cancelled message.
  93. fn_stop_warn_players_send() {
  94. local message="Server ${1} in ${2} minute"
  95. if [ "${2}" != "1" ]; then
  96. message+="s"
  97. fi
  98. if [ "${2}" == "0" ]; then
  99. message="Server ${1} cancelled"
  100. fi
  101. local warnmessage="${warncommand//%s/${message}}"
  102. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" -l "${warnmessage}" > /dev/null 2>&1
  103. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" ENTER > /dev/null 2>&1
  104. }
  105. # Waits for another LinuxGSM command that is already warning players on this server.
  106. fn_stop_warn_players_wait() {
  107. local warnlock="${lockdir}/${selfname}-warning.lock"
  108. local warnpid
  109. while [ -f "${warnlock}" ]; do
  110. warnpid="$(cat "${warnlock}" 2> /dev/null)"
  111. if [ -z "${warnpid}" ] || ! kill -0 "${warnpid}" 2> /dev/null; then
  112. rm -f "${warnlock:?}"
  113. break
  114. fi
  115. fn_print_info_nl "Warning players: another command is already warning players: waiting"
  116. fn_script_log_info "Warning players: another command is already warning players: waiting"
  117. sleep 30
  118. done
  119. }
  120. # Counts down to the stop with in-game warnings, e.g. warnplayerstimer="15" warns at 15, 10, 5, 3 and 1 minutes.
  121. # Skipped when the server is empty, and ends early if the server empties during the countdown.
  122. fn_stop_warn_players() {
  123. if [ "${firstcommandname}" == "STOP" ]; then
  124. warnplayersaction="stopping"
  125. elif [ "${firstcommandname}" == "RESTART" ] || [ "${firstcommandname}" == "BACKUP" ]; then
  126. warnplayersaction="restarting"
  127. elif [ "${firstcommandname}" == "UPDATE" ] || [ "${firstcommandname}" == "WORKSHOP-UPDATE" ]; then
  128. warnplayersaction="updating"
  129. else
  130. return
  131. fi
  132. if [ -z "${warnplayerstimer}" ] || [ "${forceupdate}" == "1" ]; then
  133. return
  134. fi
  135. if ! [[ "${warnplayerstimer}" =~ ^[0-9]+$ ]]; then
  136. fn_print_warn_nl "Warning players: warnplayerstimer must be a number of minutes: skipped"
  137. fn_script_log_warn "Warning players: warnplayerstimer \"${warnplayerstimer}\" is not a number of minutes: skipped"
  138. return
  139. fi
  140. local timer="$((10#${warnplayerstimer}))"
  141. if [ "${timer}" -eq 0 ]; then
  142. return
  143. fi
  144. fn_stop_warn_players_command
  145. if [ "${consoleinteract}" != "yes" ] || [ -z "${warncommand}" ]; then
  146. fn_print_info_nl "Warning players: not supported by ${gamename}: skipped"
  147. fn_script_log_info "Warning players: not supported by ${gamename}: skipped"
  148. return
  149. fi
  150. fn_stop_warn_players_wait
  151. check_status.sh
  152. if [ "${status}" == "0" ]; then
  153. return
  154. fi
  155. local steps=("${timer}")
  156. local step
  157. for step in 60 30 15 10 5 3 1; do
  158. if [ "${step}" -lt "${timer}" ]; then
  159. steps+=("${step}")
  160. fi
  161. done
  162. echo "$$" > "${lockdir:?}/${selfname}-warning.lock"
  163. local i next
  164. for i in "${!steps[@]}"; do
  165. check_status.sh
  166. if [ "${status}" == "0" ]; then
  167. break
  168. fi
  169. # shellcheck disable=SC2034 # Used by check_players_online.sh.
  170. playercheckforce=1
  171. check_players_online.sh
  172. unset playercheckforce
  173. if [ "${playerscounted}" == "1" ] && [ -z "${playersonline}" ]; then
  174. fn_print_info_nl "Warning players: no players on the server"
  175. fn_script_log_info "Warning players: no players on the server"
  176. break
  177. fi
  178. if [ "${i}" == "0" ] && [ -n "${playercountunavailable}" ]; then
  179. fn_print_info_nl "Warning players: unable to check for players (${playercountunavailable}): warning anyway"
  180. fn_script_log_info "Warning players: unable to check for players (${playercountunavailable}): warning anyway"
  181. fi
  182. fn_stop_warn_players_send "${warnplayersaction}" "${steps[i]}"
  183. fn_print_info_nl "Warning players: ${warnplayersaction} in ${steps[i]} minute(s)"
  184. fn_script_log_info "Warning players: ${warnplayersaction} in ${steps[i]} minute(s)"
  185. next="${steps[i + 1]:-0}"
  186. local waitseconds=$(((steps[i] - next) * 60))
  187. while [ "${waitseconds}" -gt 0 ]; do
  188. sleep 10
  189. waitseconds=$((waitseconds - 10))
  190. check_status.sh
  191. if [ "${status}" == "0" ]; then
  192. break 2
  193. fi
  194. done
  195. done
  196. unset warnplayersaction
  197. rm -f "${lockdir:?}/${selfname}-warning.lock"
  198. }
  199. # Attempts graceful shutdown by sending 'CTRL+c'.
  200. fn_stop_graceful_ctrlc() {
  201. fn_print_dots "Graceful: CTRL+c"
  202. fn_script_log_info "Graceful: CTRL+c"
  203. # Sends CTRL+c.
  204. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" C-c > /dev/null 2>&1
  205. # Waits up to 30 seconds giving the server time to shutdown gracefully.
  206. for seconds in {1..30}; do
  207. check_status.sh
  208. if [ "${status}" == "0" ]; then
  209. fn_print_ok "Graceful: CTRL+c: ${seconds}"
  210. fn_print_ok_eol_nl
  211. fn_script_log_pass "Graceful: CTRL+c: OK: ${seconds} seconds"
  212. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  213. alert="stopped"
  214. alert.sh
  215. fi
  216. break
  217. fi
  218. fn_sleep_time_1
  219. fn_print_dots "Graceful: CTRL+c: ${seconds}"
  220. done
  221. check_status.sh
  222. if [ "${status}" != "0" ]; then
  223. fn_print_error "Graceful: CTRL+c: "
  224. fn_print_fail_eol_nl
  225. fn_script_log_error "Graceful: CTRL+c: FAIL"
  226. fi
  227. }
  228. # Attempts graceful shutdown by sending a specified command.
  229. # Usage: fn_stop_graceful_cmd "console_command" "timeout_in_seconds"
  230. # e.g.: fn_stop_graceful_cmd "quit" "30"
  231. fn_stop_graceful_cmd() {
  232. fn_print_dots "Graceful: sending \"${1}\""
  233. fn_script_log_info "Graceful: sending \"${1}\""
  234. # Sends specific stop command.
  235. TERM=screen tmux -L "${socketname}" send -t "${sessionname}" ENTER "${1}" ENTER > /dev/null 2>&1
  236. # Waits up to ${seconds} seconds giving the server time to shutdown gracefully.
  237. for ((seconds = 1; seconds <= ${2}; seconds++)); do
  238. check_status.sh
  239. if [ "${status}" == "0" ]; then
  240. fn_print_ok "Graceful: sending \"${1}\": ${seconds}"
  241. fn_print_ok_eol_nl
  242. fn_script_log_pass "Graceful: sending \"${1}\": OK: ${seconds} seconds"
  243. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  244. alert="stopped"
  245. alert.sh
  246. fi
  247. break
  248. fi
  249. fn_sleep_time_1
  250. fn_print_dots "Graceful: sending \"${1}\": ${seconds}"
  251. done
  252. check_status.sh
  253. if [ "${status}" != "0" ]; then
  254. fn_print_error "Graceful: sending \"${1}\": "
  255. fn_print_fail_eol_nl
  256. fn_script_log_error "Graceful: sending \"${1}\": FAIL"
  257. fi
  258. }
  259. # Attempts graceful shutdown of goldsrc using rcon 'quit' command.
  260. # There is only a 3 second delay before a forced a tmux shutdown
  261. # as GoldSrc servers 'quit' command does a restart rather than shutdown.
  262. fn_stop_graceful_goldsrc() {
  263. fn_print_dots "Graceful: sending \"quit\""
  264. fn_script_log_info "Graceful: sending \"quit\""
  265. # sends quit
  266. TERM=screen tmux -L "${socketname}" send -t "${sessionname}" quit ENTER > /dev/null 2>&1
  267. # Waits 3 seconds as goldsrc servers restart with the quit command.
  268. for seconds in {1..3}; do
  269. fn_sleep_time_1
  270. fn_print_dots "Graceful: sending \"quit\": ${seconds}"
  271. done
  272. fn_print_ok "Graceful: sending \"quit\": ${seconds}"
  273. fn_print_ok_eol_nl
  274. fn_script_log_pass "Graceful: sending \"quit\": OK: ${seconds} seconds"
  275. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  276. alert="stopped"
  277. alert.sh
  278. fi
  279. }
  280. # Opens a telnet connection using bash /dev/tcp.
  281. # Usage: fn_stop_telnet_open "ip"
  282. fn_stop_telnet_open() {
  283. telnetoutput=""
  284. exec {telnetfd}<> "/dev/tcp/${1}/${telnetport}"
  285. } 2> /dev/null
  286. # Closes the telnet connection.
  287. fn_stop_telnet_close() {
  288. if [ -n "${telnetfd}" ]; then
  289. exec {telnetfd}>&-
  290. unset telnetfd
  291. fi
  292. }
  293. # Reads from the telnet connection until the text is received, ignoring case.
  294. # Fails if the connection closes or nothing is received for the timeout.
  295. # Usage: fn_stop_telnet_expect "text" "timeout_in_seconds"
  296. fn_stop_telnet_expect() {
  297. local char
  298. while IFS= read -r -N 1 -t "${2:-10}" -u "${telnetfd}" char; do
  299. telnetoutput+="${char}"
  300. if [[ "${telnetoutput,,}" == *"${1,,}"* ]]; then
  301. return 0
  302. fi
  303. done
  304. return 1
  305. }
  306. # Reads from the telnet connection until the server closes it.
  307. fn_stop_telnet_read_to_close() {
  308. local char
  309. while IFS= read -r -N 1 -t 10 -u "${telnetfd}" char; do
  310. telnetoutput+="${char}"
  311. done
  312. }
  313. # Sends a line to the telnet connection.
  314. # Usage: fn_stop_telnet_send "text"
  315. fn_stop_telnet_send() {
  316. (printf '%s\r\n' "${1}" >&"${telnetfd}") 2> /dev/null
  317. }
  318. # Telnet session for 7 Days to Die graceful shutdown.
  319. # Usage: fn_stop_graceful_sdtd_telnet "ip"
  320. fn_stop_graceful_sdtd_telnet() {
  321. fn_stop_telnet_open "${1}" || return 1
  322. if [ -n "${telnetpassword}" ] && [ "${telnetpassword}" != "NOT SET" ]; then
  323. if ! fn_stop_telnet_expect "password:" || ! fn_stop_telnet_send "${telnetpassword}"; then
  324. fn_stop_telnet_close
  325. return 1
  326. fi
  327. fi
  328. if fn_stop_telnet_expect "session." && fn_stop_telnet_send "shutdown"; then
  329. fn_stop_telnet_read_to_close
  330. fn_stop_telnet_close
  331. return 0
  332. fi
  333. fn_stop_telnet_close
  334. return 1
  335. }
  336. # Telnet session for Soulmask graceful shutdown.
  337. # Usage: fn_stop_graceful_sm_telnet "ip"
  338. fn_stop_graceful_sm_telnet() {
  339. fn_stop_telnet_open "${1}" || return 1
  340. if fn_stop_telnet_send "saveworld 1" && fn_stop_telnet_expect "world is saved" 120 && fn_stop_telnet_send "quit 1"; then
  341. fn_stop_telnet_read_to_close
  342. fn_stop_telnet_close
  343. return 0
  344. fi
  345. fn_stop_telnet_close
  346. return 1
  347. }
  348. # Attempts graceful shutdown using a telnet session.
  349. # Tries localhost first, then the telnet and server IPs.
  350. # Usage: fn_stop_graceful_telnet "session_function" "timeout_in_seconds"
  351. # e.g.: fn_stop_graceful_telnet "fn_stop_graceful_sdtd_telnet" "60"
  352. fn_stop_graceful_telnet() {
  353. fn_print_dots "Graceful: telnet"
  354. fn_script_log_info "Graceful: telnet"
  355. if [ "${telnetenabled}" == "false" ]; then
  356. fn_print_info_nl "Graceful: telnet: DISABLED: Enable in ${servercfg}"
  357. fn_script_log_info "Graceful: telnet: DISABLED"
  358. return
  359. fi
  360. local telnetaddresses=("127.0.0.1")
  361. local telnetaddress telnetsent
  362. for telnetaddress in "${telnetip[@]}" ${ip}; do
  363. if [ -n "${telnetaddress}" ] && [[ " ${telnetaddresses[*]} " != *" ${telnetaddress} "* ]]; then
  364. telnetaddresses+=("${telnetaddress}")
  365. fi
  366. done
  367. for telnetaddress in "${telnetaddresses[@]}"; do
  368. fn_print_dots "Graceful: telnet: ${telnetaddress}:${telnetport}"
  369. fn_script_log_info "Graceful: telnet: ${telnetaddress}:${telnetport}"
  370. if "${1}" "${telnetaddress}"; then
  371. telnetsent=1
  372. break
  373. fi
  374. fn_print_error "Graceful: telnet: ${telnetaddress}:${telnetport}: "
  375. fn_print_fail_eol_nl
  376. fn_script_log_error "Graceful: telnet: ${telnetaddress}:${telnetport}: FAIL"
  377. if [ -n "${telnetoutput}" ]; then
  378. fn_script_log_error "Graceful: telnet: output: ${telnetoutput//$'\r'/}"
  379. fi
  380. done
  381. if [ -z "${telnetsent}" ]; then
  382. return
  383. fi
  384. # Waits up to ${seconds} seconds giving the server time to shutdown gracefully.
  385. for ((seconds = 1; seconds <= ${2}; seconds++)); do
  386. check_status.sh
  387. if [ "${status}" == "0" ]; then
  388. fn_print_ok "Graceful: telnet: ${telnetaddress}:${telnetport}: ${seconds}"
  389. fn_print_ok_eol_nl
  390. fn_script_log_pass "Graceful: telnet: ${telnetaddress}:${telnetport}: OK: ${seconds} seconds"
  391. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  392. alert="stopped"
  393. alert.sh
  394. fi
  395. break
  396. fi
  397. fn_sleep_time_1
  398. fn_print_dots "Graceful: telnet: ${telnetaddress}:${telnetport}: ${seconds}"
  399. done
  400. check_status.sh
  401. if [ "${status}" != "0" ]; then
  402. fn_print_error "Graceful: telnet: ${telnetaddress}:${telnetport}: "
  403. fn_print_fail_eol_nl
  404. fn_script_log_error "Graceful: telnet: ${telnetaddress}:${telnetport}: FAIL"
  405. fi
  406. }
  407. # Attempts graceful shutdown by sending /save /stop.
  408. fn_stop_graceful_avorion() {
  409. fn_print_dots "Graceful: /save /stop"
  410. fn_script_log_info "Graceful: /save /stop"
  411. # Sends /save.
  412. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" /save ENTER > /dev/null 2>&1
  413. fn_sleep_time_5
  414. # Sends /quit.
  415. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" /stop ENTER > /dev/null 2>&1
  416. # Waits up to 30 seconds giving the server time to shutdown gracefully.
  417. for seconds in {1..30}; do
  418. check_status.sh
  419. if [ "${status}" == "0" ]; then
  420. fn_print_ok "Graceful: /save /stop: ${seconds}"
  421. fn_print_ok_eol_nl
  422. fn_script_log_pass "Graceful: /save /stop: OK: ${seconds} seconds"
  423. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  424. alert="stopped"
  425. alert.sh
  426. fi
  427. break
  428. fi
  429. fn_sleep_time_1
  430. fn_print_dots "Graceful: /save /stop: ${seconds}"
  431. done
  432. check_status.sh
  433. if [ "${status}" != "0" ]; then
  434. fn_print_error "Graceful: /save /stop: "
  435. fn_print_fail_eol_nl
  436. fn_script_log_error "Graceful: /save /stop: FAIL"
  437. fi
  438. }
  439. fn_stop_graceful_rwr() {
  440. fn_print_dots "Graceful: save_profiles stop_server"
  441. fn_script_log_info "Graceful: save_profiles stop_server"
  442. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" save_profiles ENTER > /dev/null 2>&1
  443. fn_sleep_time_5
  444. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" stop_server ENTER > /dev/null 2>&1
  445. fn_sleep_time_5
  446. fn_stop_graceful_ctrlc
  447. }
  448. fn_stop_graceful_ce() {
  449. fn_print_dots "Graceful: CTRL+c"
  450. fn_script_log_info "Graceful: CTRL+c"
  451. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" C-c > /dev/null 2>&1
  452. fn_sleep_time_5
  453. fn_stop_graceful_ctrlc
  454. }
  455. # Attempts graceful shutdown of Rising World using 'shutdown'.
  456. # Its console can miss input, so the command is sent again every 10 seconds until the server stops.
  457. fn_stop_graceful_rw() {
  458. fn_print_dots "Graceful: sending \"shutdown\""
  459. fn_script_log_info "Graceful: sending \"shutdown\""
  460. for ((seconds = 1; seconds <= 120; seconds++)); do
  461. if [ $((seconds % 10)) -eq 1 ]; then
  462. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" ENTER > /dev/null 2>&1
  463. sleep 1
  464. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" -l "shutdown" > /dev/null 2>&1
  465. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" ENTER > /dev/null 2>&1
  466. fi
  467. check_status.sh
  468. if [ "${status}" == "0" ]; then
  469. fn_print_ok "Graceful: sending \"shutdown\": ${seconds}"
  470. fn_print_ok_eol_nl
  471. fn_script_log_pass "Graceful: sending \"shutdown\": OK: ${seconds} seconds"
  472. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  473. alert="stopped"
  474. alert.sh
  475. fi
  476. break
  477. fi
  478. fn_sleep_time_1
  479. fn_print_dots "Graceful: sending \"shutdown\": ${seconds}"
  480. done
  481. check_status.sh
  482. if [ "${status}" != "0" ]; then
  483. fn_print_error "Graceful: sending \"shutdown\": "
  484. fn_print_fail_eol_nl
  485. fn_script_log_error "Graceful: sending \"shutdown\": FAIL"
  486. fi
  487. }
  488. fn_stop_graceful_select() {
  489. if [ "${stopmode}" == "1" ]; then
  490. fn_stop_tmux
  491. elif [ "${stopmode}" == "2" ]; then
  492. fn_stop_graceful_ctrlc
  493. elif [ "${stopmode}" == "3" ]; then
  494. fn_stop_graceful_cmd "quit" 30
  495. elif [ "${stopmode}" == "4" ]; then
  496. fn_stop_graceful_cmd "quit" 120
  497. elif [ "${stopmode}" == "5" ]; then
  498. fn_stop_graceful_cmd "stop" 30
  499. elif [ "${stopmode}" == "6" ]; then
  500. fn_stop_graceful_cmd "q" 30
  501. elif [ "${stopmode}" == "7" ]; then
  502. fn_stop_graceful_cmd "exit" 30
  503. elif [ "${stopmode}" == "8" ]; then
  504. fn_stop_graceful_telnet "fn_stop_graceful_sdtd_telnet" "60"
  505. elif [ "${stopmode}" == "9" ]; then
  506. fn_stop_graceful_goldsrc
  507. elif [ "${stopmode}" == "10" ]; then
  508. fn_stop_graceful_avorion
  509. elif [ "${stopmode}" == "11" ]; then
  510. fn_stop_graceful_cmd "end" 30
  511. elif [ "${stopmode}" == "12" ]; then
  512. fn_stop_graceful_cmd "shutdown" 30
  513. elif [ "${stopmode}" == "13" ]; then
  514. fn_stop_graceful_telnet "fn_stop_graceful_sm_telnet" "30"
  515. elif [ "${stopmode}" == "14" ]; then
  516. fn_stop_graceful_rwr
  517. elif [ "${stopmode}" == "15" ]; then
  518. fn_stop_graceful_rw
  519. elif [ "${stopmode}" == "16" ]; then
  520. fn_stop_graceful_ce
  521. fi
  522. }
  523. fn_stop_tmux() {
  524. fn_print_dots "${servername}"
  525. fn_script_log_info "tmux kill-session: ${sessionname}: ${servername}"
  526. # Kill tmux session.
  527. TERM=screen tmux -L "${socketname}" kill-session -t "${sessionname}" > /dev/null 2>&1
  528. fn_sleep_time_1
  529. check_status.sh
  530. if [ "${status}" == "0" ]; then
  531. fn_print_ok_nl "${servername}"
  532. fn_script_log_pass "Stopped ${servername}"
  533. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  534. alert="stopped"
  535. alert.sh
  536. fi
  537. else
  538. fn_print_fail_nl "Unable to stop ${servername}"
  539. fn_script_log_fail "Unable to stop ${servername}"
  540. fi
  541. }
  542. # Checks if the server is already stopped.
  543. fn_stop_pre_check() {
  544. if [ "${status}" == "0" ]; then
  545. fn_print_skip_nl "${servername} is already stopped"
  546. fn_script_log_info "${servername} is already stopped"
  547. else
  548. # Select graceful shutdown.
  549. fn_stop_graceful_select
  550. # Check status again, a kill tmux session if graceful shutdown failed.
  551. check_status.sh
  552. if [ "${status}" != "0" ]; then
  553. fn_stop_tmux
  554. fi
  555. fi
  556. }
  557. fn_print_dots ""
  558. check.sh
  559. info_game.sh
  560. # Players are checked and warned before the stopping lockfile is created,
  561. # as a warning countdown can outlast monitor's stale lockfile check.
  562. if [ "${status}" != "0" ]; then
  563. fn_stop_players_online
  564. fn_stop_warn_players
  565. check_status.sh
  566. fi
  567. # Create a stopping lockfile that only exists while the stop command is running.
  568. date '+%s' > "${lockdir:?}/${selfname}-stopping.lock"
  569. fn_print_dots "${servername}"
  570. fn_stop_pre_check
  571. # Remove started lockfile.
  572. rm -f "${lockdir:?}/${selfname}-started.lock"
  573. # If user ran the stop command monitor will become disabled.
  574. if [ "${firstcommandname}" == "STOP" ]; then
  575. rm -f "${lockdir:?}/${selfname}-monitoring.lock"
  576. fi
  577. # Remove stopping lockfile.
  578. rm -f "${lockdir:?}/${selfname}-stopping.lock"
  579. if [ -z "${exitbypass}" ]; then
  580. core_exit.sh
  581. fi