command_stop.sh 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  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" ] || [ "${shortname}" == "scpslsm" ]; 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}" == "fctr" ]; then
  76. warncommand="%s"
  77. elif [ "${engine}" == "source" ] || [ "${engine}" == "source2" ] || [ "${engine}" == "goldsrc" ]; then
  78. warncommand="say %s"
  79. else
  80. local saygames=(bt cod cod2 cod4 coduo codwaw etl hyt jk2 mc mcb mohaa pmc q2 q3 q4 ql qw rtcw sof2 terraria unt wet wf xnt)
  81. local game
  82. for game in "${saygames[@]}"; do
  83. if [ "${shortname}" == "${game}" ]; then
  84. warncommand="say %s"
  85. fi
  86. done
  87. fi
  88. }
  89. # Sends a warning message to players using the game's own chat command.
  90. # A minutes value of 0 sends a cancelled message.
  91. fn_stop_warn_players_send() {
  92. local message="Server ${1} in ${2} minute"
  93. if [ "${2}" != "1" ]; then
  94. message+="s"
  95. fi
  96. if [ "${2}" == "0" ]; then
  97. message="Server ${1} cancelled"
  98. fi
  99. local warnmessage="${warncommand//%s/${message}}"
  100. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" -l "${warnmessage}" > /dev/null 2>&1
  101. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" ENTER > /dev/null 2>&1
  102. }
  103. # Waits for another LinuxGSM command that is already warning players on this server.
  104. fn_stop_warn_players_wait() {
  105. local warnlock="${lockdir}/${selfname}-warning.lock"
  106. local warnpid
  107. while [ -f "${warnlock}" ]; do
  108. warnpid="$(cat "${warnlock}" 2> /dev/null)"
  109. if [ -z "${warnpid}" ] || ! kill -0 "${warnpid}" 2> /dev/null; then
  110. rm -f "${warnlock:?}"
  111. break
  112. fi
  113. fn_print_info_nl "Warning players: another command is already warning players: waiting"
  114. fn_script_log_info "Warning players: another command is already warning players: waiting"
  115. sleep 30
  116. done
  117. }
  118. # Counts down to the stop with in-game warnings, e.g. warnplayerstimer="15" warns at 15, 10, 5, 3 and 1 minutes.
  119. # Skipped when the server is empty, and ends early if the server empties during the countdown.
  120. fn_stop_warn_players() {
  121. if [ "${firstcommandname}" == "STOP" ]; then
  122. warnplayersaction="stopping"
  123. elif [ "${firstcommandname}" == "RESTART" ] || [ "${firstcommandname}" == "BACKUP" ]; then
  124. warnplayersaction="restarting"
  125. elif [ "${firstcommandname}" == "UPDATE" ] || [ "${firstcommandname}" == "WORKSHOP-UPDATE" ]; then
  126. warnplayersaction="updating"
  127. else
  128. return
  129. fi
  130. if [ -z "${warnplayerstimer}" ] || [ "${forceupdate}" == "1" ]; then
  131. return
  132. fi
  133. if ! [[ "${warnplayerstimer}" =~ ^[0-9]+$ ]]; then
  134. fn_print_warn_nl "Warning players: warnplayerstimer must be a number of minutes: skipped"
  135. fn_script_log_warn "Warning players: warnplayerstimer \"${warnplayerstimer}\" is not a number of minutes: skipped"
  136. return
  137. fi
  138. local timer="$((10#${warnplayerstimer}))"
  139. if [ "${timer}" -eq 0 ]; then
  140. return
  141. fi
  142. fn_stop_warn_players_command
  143. if [ "${consoleinteract}" != "yes" ] || [ -z "${warncommand}" ]; then
  144. fn_print_info_nl "Warning players: not supported by ${gamename}: skipped"
  145. fn_script_log_info "Warning players: not supported by ${gamename}: skipped"
  146. return
  147. fi
  148. fn_stop_warn_players_wait
  149. check_status.sh
  150. if [ "${status}" == "0" ]; then
  151. return
  152. fi
  153. local steps=("${timer}")
  154. local step
  155. for step in 60 30 15 10 5 3 1; do
  156. if [ "${step}" -lt "${timer}" ]; then
  157. steps+=("${step}")
  158. fi
  159. done
  160. echo "$$" > "${lockdir:?}/${selfname}-warning.lock"
  161. local i next
  162. for i in "${!steps[@]}"; do
  163. check_status.sh
  164. if [ "${status}" == "0" ]; then
  165. break
  166. fi
  167. # shellcheck disable=SC2034 # Used by check_players_online.sh.
  168. playercheckforce=1
  169. check_players_online.sh
  170. unset playercheckforce
  171. if [ "${playerscounted}" == "1" ] && [ -z "${playersonline}" ]; then
  172. fn_print_info_nl "Warning players: no players on the server"
  173. fn_script_log_info "Warning players: no players on the server"
  174. break
  175. fi
  176. if [ "${i}" == "0" ] && [ -n "${playercountunavailable}" ]; then
  177. fn_print_info_nl "Warning players: unable to check for players (${playercountunavailable}): warning anyway"
  178. fn_script_log_info "Warning players: unable to check for players (${playercountunavailable}): warning anyway"
  179. fi
  180. fn_stop_warn_players_send "${warnplayersaction}" "${steps[i]}"
  181. fn_print_info_nl "Warning players: ${warnplayersaction} in ${steps[i]} minute(s)"
  182. fn_script_log_info "Warning players: ${warnplayersaction} in ${steps[i]} minute(s)"
  183. next="${steps[i + 1]:-0}"
  184. local waitseconds=$(((steps[i] - next) * 60))
  185. while [ "${waitseconds}" -gt 0 ]; do
  186. sleep 10
  187. waitseconds=$((waitseconds - 10))
  188. check_status.sh
  189. if [ "${status}" == "0" ]; then
  190. break 2
  191. fi
  192. done
  193. done
  194. unset warnplayersaction
  195. rm -f "${lockdir:?}/${selfname}-warning.lock"
  196. }
  197. # Attempts graceful shutdown by sending 'CTRL+c'.
  198. fn_stop_graceful_ctrlc() {
  199. fn_print_dots "Graceful: CTRL+c"
  200. fn_script_log_info "Graceful: CTRL+c"
  201. # Sends CTRL+c.
  202. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" C-c > /dev/null 2>&1
  203. # Waits up to 30 seconds giving the server time to shutdown gracefully.
  204. for seconds in {1..30}; do
  205. check_status.sh
  206. if [ "${status}" == "0" ]; then
  207. fn_print_ok "Graceful: CTRL+c: ${seconds}"
  208. fn_print_ok_eol_nl
  209. fn_script_log_pass "Graceful: CTRL+c: OK: ${seconds} seconds"
  210. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  211. alert="stopped"
  212. alert.sh
  213. fi
  214. break
  215. fi
  216. fn_sleep_time_1
  217. fn_print_dots "Graceful: CTRL+c: ${seconds}"
  218. done
  219. check_status.sh
  220. if [ "${status}" != "0" ]; then
  221. fn_print_error "Graceful: CTRL+c: "
  222. fn_print_fail_eol_nl
  223. fn_script_log_error "Graceful: CTRL+c: FAIL"
  224. fi
  225. }
  226. # Attempts graceful shutdown by sending a specified command.
  227. # Usage: fn_stop_graceful_cmd "console_command" "timeout_in_seconds"
  228. # e.g.: fn_stop_graceful_cmd "quit" "30"
  229. fn_stop_graceful_cmd() {
  230. fn_print_dots "Graceful: sending \"${1}\""
  231. fn_script_log_info "Graceful: sending \"${1}\""
  232. # Sends specific stop command.
  233. TERM=screen tmux -L "${socketname}" send -t "${sessionname}" ENTER "${1}" ENTER > /dev/null 2>&1
  234. # Waits up to ${seconds} seconds giving the server time to shutdown gracefully.
  235. for ((seconds = 1; seconds <= ${2}; seconds++)); do
  236. check_status.sh
  237. if [ "${status}" == "0" ]; then
  238. fn_print_ok "Graceful: sending \"${1}\": ${seconds}"
  239. fn_print_ok_eol_nl
  240. fn_script_log_pass "Graceful: sending \"${1}\": OK: ${seconds} seconds"
  241. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  242. alert="stopped"
  243. alert.sh
  244. fi
  245. break
  246. fi
  247. fn_sleep_time_1
  248. fn_print_dots "Graceful: sending \"${1}\": ${seconds}"
  249. done
  250. check_status.sh
  251. if [ "${status}" != "0" ]; then
  252. fn_print_error "Graceful: sending \"${1}\": "
  253. fn_print_fail_eol_nl
  254. fn_script_log_error "Graceful: sending \"${1}\": FAIL"
  255. fi
  256. }
  257. # Attempts graceful shutdown of goldsrc using rcon 'quit' command.
  258. # There is only a 3 second delay before a forced a tmux shutdown
  259. # as GoldSrc servers 'quit' command does a restart rather than shutdown.
  260. fn_stop_graceful_goldsrc() {
  261. fn_print_dots "Graceful: sending \"quit\""
  262. fn_script_log_info "Graceful: sending \"quit\""
  263. # sends quit
  264. TERM=screen tmux -L "${socketname}" send -t "${sessionname}" quit ENTER > /dev/null 2>&1
  265. # Waits 3 seconds as goldsrc servers restart with the quit command.
  266. for seconds in {1..3}; do
  267. fn_sleep_time_1
  268. fn_print_dots "Graceful: sending \"quit\": ${seconds}"
  269. done
  270. fn_print_ok "Graceful: sending \"quit\": ${seconds}"
  271. fn_print_ok_eol_nl
  272. fn_script_log_pass "Graceful: sending \"quit\": OK: ${seconds} seconds"
  273. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  274. alert="stopped"
  275. alert.sh
  276. fi
  277. }
  278. # telnet command for sdtd graceful shutdown.
  279. fn_stop_graceful_sdtd_telnet() {
  280. if [ -z "${telnetpassword}" ] || [ "${telnetpassword}" == "NOT SET" ]; then
  281. sdtdtelnetshutdown=$(expect -c '
  282. proc abort {} {
  283. puts "Timeout or EOF\n"
  284. exit 1
  285. }
  286. spawn telnet '"${telnetip}"' '"${telnetport}"'
  287. expect {
  288. "session." { send "shutdown\r" }
  289. default abort
  290. }
  291. expect { eof }
  292. puts "Completed.\n"
  293. ')
  294. else
  295. sdtdtelnetshutdown=$(expect -c '
  296. proc abort {} {
  297. puts "Timeout or EOF\n"
  298. exit 1
  299. }
  300. spawn telnet '"${telnetip}"' '"${telnetport}"'
  301. expect {
  302. "password:" { send "'"${telnetpassword}"'\r" }
  303. default abort
  304. }
  305. expect {
  306. "session." { send "shutdown\r" }
  307. default abort
  308. }
  309. expect { eof }
  310. puts "Completed.\n"
  311. ')
  312. fi
  313. }
  314. # Attempts graceful shutdown of 7 Days To Die using telnet.
  315. fn_stop_graceful_sdtd() {
  316. fn_print_dots "Graceful: telnet"
  317. fn_script_log_info "Graceful: telnet"
  318. if [ "${telnetenabled}" == "false" ]; then
  319. fn_print_info_nl "Graceful: telnet: DISABLED: Enable in ${servercfg}"
  320. elif [ "$(command -v expect 2> /dev/null)" ]; then
  321. # Tries to shutdown with both localhost and server IP.
  322. for telnetip in 127.0.0.1 ${ip}; do
  323. fn_print_dots "Graceful: telnet: ${telnetip}:${telnetport}"
  324. fn_script_log_info "Graceful: telnet: ${telnetip}:${telnetport}"
  325. fn_stop_graceful_sdtd_telnet
  326. completed=$(echo -en "\n ${sdtdtelnetshutdown}" | grep "Completed.")
  327. refused=$(echo -en "\n ${sdtdtelnetshutdown}" | grep "Timeout or EOF")
  328. if [ "${refused}" ]; then
  329. fn_print_error "Graceful: telnet: ${telnetip}:${telnetport} : "
  330. fn_print_fail_eol_nl
  331. fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
  332. elif [ "${completed}" ]; then
  333. break
  334. fi
  335. done
  336. # If telnet shutdown was successful will use telnet again to check
  337. # the connection has closed, confirming that the tmux session can now be killed.
  338. if [ "${completed}" ]; then
  339. for seconds in {1..30}; do
  340. fn_stop_graceful_sdtd_telnet
  341. refused=$(echo -en "\n ${sdtdtelnetshutdown}" | grep "Timeout or EOF")
  342. if [ "${refused}" ]; then
  343. fn_print_ok "Graceful: telnet: ${telnetip}:${telnetport} : "
  344. fn_print_ok_eol_nl
  345. fn_script_log_pass "Graceful: telnet: ${telnetip}:${telnetport} : ${seconds} seconds"
  346. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  347. alert="stopped"
  348. alert.sh
  349. fi
  350. break
  351. fi
  352. fn_sleep_time_1
  353. fn_print_dots "Graceful: telnet: ${seconds}"
  354. done
  355. # If telnet shutdown fails tmux shutdown will be used, this risks loss of world save.
  356. else
  357. if [ "${refused}" ]; then
  358. fn_print_error "Graceful: telnet: "
  359. fn_print_fail_eol_nl
  360. fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
  361. else
  362. fn_print_error_nl "Graceful: telnet: Unknown error"
  363. fn_script_log_error "Graceful: telnet: Unknown error"
  364. fi
  365. echo -en "\n" | tee -a "${lgsmlog}"
  366. echo -en "Telnet output:" | tee -a "${lgsmlog}"
  367. echo -en "\n ${sdtdtelnetshutdown}" | tee -a "${lgsmlog}"
  368. echo -en "\n\n" | tee -a "${lgsmlog}"
  369. fi
  370. else
  371. fn_print_warn "Graceful: telnet: expect not installed: "
  372. fn_print_fail_eol_nl
  373. fn_script_log_warn "Graceful: telnet: expect not installed: FAIL"
  374. fi
  375. }
  376. # Attempts graceful shutdown of Soulmask using telnet.
  377. fn_stop_graceful_sm() {
  378. fn_print_dots "Graceful: telnet"
  379. fn_script_log_info "Graceful: telnet"
  380. if [ "${telnetenabled}" == "false" ]; then
  381. fn_print_info_nl "Graceful: telnet: DISABLED: Enable in ${servercfg}"
  382. elif [ "$(command -v expect 2> /dev/null)" ]; then
  383. # Tries to shutdown with both localhost and server IP.
  384. for telnetip in 127.0.0.1 ${ip}; do
  385. fn_print_dots "Graceful: telnet: ${telnetip}:${telnetport}"
  386. fn_script_log_info "Graceful: telnet: ${telnetip}:${telnetport}"
  387. fn_stop_graceful_sm_telnet
  388. completed=$(echo -en "\n ${smtelnetshutdown}" | grep "Completed.")
  389. refused=$(echo -en "\n ${smtelnetshutdown}" | grep "Timeout or EOF")
  390. if [ "${refused}" ]; then
  391. fn_print_error "Graceful: telnet: ${telnetip}:${telnetport} : "
  392. fn_print_fail_eol_nl
  393. fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
  394. elif [ "${completed}" ]; then
  395. break
  396. fi
  397. done
  398. # If telnet shutdown was successful will use telnet again to check
  399. # the connection has closed, confirming that the tmux session can now be killed.
  400. if [ "${completed}" ]; then
  401. for seconds in {1..30}; do
  402. fn_stop_graceful_sm_telnet
  403. refused=$(echo -en "\n ${smtelnetshutdown}" | grep "Timeout or EOF")
  404. if [ "${refused}" ]; then
  405. fn_print_ok "Graceful: telnet: ${telnetip}:${telnetport} : "
  406. fn_print_ok_eol_nl
  407. fn_script_log_pass "Graceful: telnet: ${telnetip}:${telnetport} : ${seconds} seconds"
  408. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  409. alert="stopped"
  410. alert.sh
  411. fi
  412. break
  413. fi
  414. fn_sleep_time_1
  415. fn_print_dots "Graceful: telnet: ${seconds}"
  416. done
  417. # If telnet shutdown fails, show it and stop
  418. else
  419. if [ "${refused}" ]; then
  420. fn_print_error "Graceful: telnet: "
  421. fn_print_fail_eol_nl
  422. fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
  423. else
  424. fn_print_error_nl "Graceful: telnet: Unknown error"
  425. fn_script_log_error "Graceful: telnet: Unknown error"
  426. fi
  427. echo -en "\n" | tee -a "${lgsmlog}"
  428. echo -en "Telnet output:" | tee -a "${lgsmlog}"
  429. echo -en "\n ${smtelnetshutdown}" | tee -a "${lgsmlog}"
  430. echo -en "\n\n" | tee -a "${lgsmlog}"
  431. fi
  432. else
  433. fn_print_warn "Graceful: telnet: expect not installed: "
  434. fn_print_fail_eol_nl
  435. fn_script_log_warn "Graceful: telnet: expect not installed: FAIL"
  436. fi
  437. }
  438. # telnet command for soulmask graceful shutdown.
  439. fn_stop_graceful_sm_telnet() {
  440. smtelnetshutdown=$(expect -c '
  441. proc abort {} {
  442. puts "Timeout or EOF\n"
  443. exit 1
  444. }
  445. spawn telnet '"${telnetip}"' '"${telnetport}"'
  446. expect {
  447. "Hello:" { send "saveworld 1\r" }
  448. default abort
  449. }
  450. expect {
  451. "the world is saved." { send "quit 1\r" }
  452. default abort
  453. }
  454. expect {
  455. "World is closing..." {}
  456. default abort
  457. }
  458. expect { eof }
  459. puts "Completed.\n"
  460. ')
  461. }
  462. # Attempts graceful shutdown by sending /save /stop.
  463. fn_stop_graceful_avorion() {
  464. fn_print_dots "Graceful: /save /stop"
  465. fn_script_log_info "Graceful: /save /stop"
  466. # Sends /save.
  467. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" /save ENTER > /dev/null 2>&1
  468. fn_sleep_time_5
  469. # Sends /quit.
  470. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" /stop ENTER > /dev/null 2>&1
  471. # Waits up to 30 seconds giving the server time to shutdown gracefully.
  472. for seconds in {1..30}; do
  473. check_status.sh
  474. if [ "${status}" == "0" ]; then
  475. fn_print_ok "Graceful: /save /stop: ${seconds}"
  476. fn_print_ok_eol_nl
  477. fn_script_log_pass "Graceful: /save /stop: OK: ${seconds} seconds"
  478. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  479. alert="stopped"
  480. alert.sh
  481. fi
  482. break
  483. fi
  484. fn_sleep_time_1
  485. fn_print_dots "Graceful: /save /stop: ${seconds}"
  486. done
  487. check_status.sh
  488. if [ "${status}" != "0" ]; then
  489. fn_print_error "Graceful: /save /stop: "
  490. fn_print_fail_eol_nl
  491. fn_script_log_error "Graceful: /save /stop: FAIL"
  492. fi
  493. }
  494. fn_stop_graceful_rwr() {
  495. fn_print_dots "Graceful: save_profiles stop_server"
  496. fn_script_log_info "Graceful: save_profiles stop_server"
  497. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" save_profiles ENTER > /dev/null 2>&1
  498. fn_sleep_time_5
  499. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" stop_server ENTER > /dev/null 2>&1
  500. fn_sleep_time_5
  501. fn_stop_graceful_ctrlc
  502. }
  503. fn_stop_graceful_select() {
  504. if [ "${stopmode}" == "1" ]; then
  505. fn_stop_tmux
  506. elif [ "${stopmode}" == "2" ]; then
  507. fn_stop_graceful_ctrlc
  508. elif [ "${stopmode}" == "3" ]; then
  509. fn_stop_graceful_cmd "quit" 30
  510. elif [ "${stopmode}" == "4" ]; then
  511. fn_stop_graceful_cmd "quit" 120
  512. elif [ "${stopmode}" == "5" ]; then
  513. fn_stop_graceful_cmd "stop" 30
  514. elif [ "${stopmode}" == "6" ]; then
  515. fn_stop_graceful_cmd "q" 30
  516. elif [ "${stopmode}" == "7" ]; then
  517. fn_stop_graceful_cmd "exit" 30
  518. elif [ "${stopmode}" == "8" ]; then
  519. fn_stop_graceful_sdtd
  520. elif [ "${stopmode}" == "9" ]; then
  521. fn_stop_graceful_goldsrc
  522. elif [ "${stopmode}" == "10" ]; then
  523. fn_stop_graceful_avorion
  524. elif [ "${stopmode}" == "11" ]; then
  525. fn_stop_graceful_cmd "end" 30
  526. elif [ "${stopmode}" == "12" ]; then
  527. fn_stop_graceful_cmd "shutdown" 30
  528. elif [ "${stopmode}" == "13" ]; then
  529. fn_stop_graceful_sm
  530. elif [ "${stopmode}" == "14" ]; then
  531. fn_stop_graceful_rwr
  532. fi
  533. }
  534. fn_stop_tmux() {
  535. fn_print_dots "${servername}"
  536. fn_script_log_info "tmux kill-session: ${sessionname}: ${servername}"
  537. # Kill tmux session.
  538. TERM=screen tmux -L "${socketname}" kill-session -t "${sessionname}" > /dev/null 2>&1
  539. fn_sleep_time_1
  540. check_status.sh
  541. if [ "${status}" == "0" ]; then
  542. fn_print_ok_nl "${servername}"
  543. fn_script_log_pass "Stopped ${servername}"
  544. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  545. alert="stopped"
  546. alert.sh
  547. fi
  548. else
  549. fn_print_fail_nl "Unable to stop ${servername}"
  550. fn_script_log_fail "Unable to stop ${servername}"
  551. fi
  552. }
  553. # Checks if the server is already stopped.
  554. fn_stop_pre_check() {
  555. if [ "${status}" == "0" ]; then
  556. fn_print_skip_nl "${servername} is already stopped"
  557. fn_script_log_info "${servername} is already stopped"
  558. else
  559. # Select graceful shutdown.
  560. fn_stop_graceful_select
  561. # Check status again, a kill tmux session if graceful shutdown failed.
  562. check_status.sh
  563. if [ "${status}" != "0" ]; then
  564. fn_stop_tmux
  565. fi
  566. fi
  567. }
  568. fn_print_dots ""
  569. check.sh
  570. info_game.sh
  571. # Players are checked and warned before the stopping lockfile is created,
  572. # as a warning countdown can outlast monitor's stale lockfile check.
  573. if [ "${status}" != "0" ]; then
  574. fn_stop_players_online
  575. fn_stop_warn_players
  576. check_status.sh
  577. fi
  578. # Create a stopping lockfile that only exists while the stop command is running.
  579. date '+%s' > "${lockdir:?}/${selfname}-stopping.lock"
  580. fn_print_dots "${servername}"
  581. fn_stop_pre_check
  582. # Remove started lockfile.
  583. rm -f "${lockdir:?}/${selfname}-started.lock"
  584. # If user ran the stop command monitor will become disabled.
  585. if [ "${firstcommandname}" == "STOP" ]; then
  586. rm -f "${lockdir:?}/${selfname}-monitoring.lock"
  587. fi
  588. # Remove stopping lockfile.
  589. rm -f "${lockdir:?}/${selfname}-stopping.lock"
  590. if [ -z "${exitbypass}" ]; then
  591. core_exit.sh
  592. fi