command_stop.sh 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  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. # telnet command for sdtd graceful shutdown.
  281. fn_stop_graceful_sdtd_telnet() {
  282. if [ -z "${telnetpassword}" ] || [ "${telnetpassword}" == "NOT SET" ]; then
  283. sdtdtelnetshutdown=$(expect -c '
  284. proc abort {} {
  285. puts "Timeout or EOF\n"
  286. exit 1
  287. }
  288. spawn telnet '"${telnetip}"' '"${telnetport}"'
  289. expect {
  290. "session." { send "shutdown\r" }
  291. default abort
  292. }
  293. expect { eof }
  294. puts "Completed.\n"
  295. ')
  296. else
  297. sdtdtelnetshutdown=$(expect -c '
  298. proc abort {} {
  299. puts "Timeout or EOF\n"
  300. exit 1
  301. }
  302. spawn telnet '"${telnetip}"' '"${telnetport}"'
  303. expect {
  304. "password:" { send "'"${telnetpassword}"'\r" }
  305. default abort
  306. }
  307. expect {
  308. "session." { send "shutdown\r" }
  309. default abort
  310. }
  311. expect { eof }
  312. puts "Completed.\n"
  313. ')
  314. fi
  315. }
  316. # Attempts graceful shutdown of 7 Days To Die using telnet.
  317. fn_stop_graceful_sdtd() {
  318. fn_print_dots "Graceful: telnet"
  319. fn_script_log_info "Graceful: telnet"
  320. if [ "${telnetenabled}" == "false" ]; then
  321. fn_print_info_nl "Graceful: telnet: DISABLED: Enable in ${servercfg}"
  322. elif [ "$(command -v expect 2> /dev/null)" ]; then
  323. # Tries to shutdown with both localhost and server IP.
  324. for telnetip in 127.0.0.1 ${ip}; do
  325. fn_print_dots "Graceful: telnet: ${telnetip}:${telnetport}"
  326. fn_script_log_info "Graceful: telnet: ${telnetip}:${telnetport}"
  327. fn_stop_graceful_sdtd_telnet
  328. completed=$(echo -en "\n ${sdtdtelnetshutdown}" | grep "Completed.")
  329. refused=$(echo -en "\n ${sdtdtelnetshutdown}" | grep "Timeout or EOF")
  330. if [ "${refused}" ]; then
  331. fn_print_error "Graceful: telnet: ${telnetip}:${telnetport} : "
  332. fn_print_fail_eol_nl
  333. fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
  334. elif [ "${completed}" ]; then
  335. break
  336. fi
  337. done
  338. # If telnet shutdown was successful will use telnet again to check
  339. # the connection has closed, confirming that the tmux session can now be killed.
  340. if [ "${completed}" ]; then
  341. for seconds in {1..30}; do
  342. fn_stop_graceful_sdtd_telnet
  343. refused=$(echo -en "\n ${sdtdtelnetshutdown}" | grep "Timeout or EOF")
  344. if [ "${refused}" ]; then
  345. fn_print_ok "Graceful: telnet: ${telnetip}:${telnetport} : "
  346. fn_print_ok_eol_nl
  347. fn_script_log_pass "Graceful: telnet: ${telnetip}:${telnetport} : ${seconds} seconds"
  348. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  349. alert="stopped"
  350. alert.sh
  351. fi
  352. break
  353. fi
  354. fn_sleep_time_1
  355. fn_print_dots "Graceful: telnet: ${seconds}"
  356. done
  357. # If telnet shutdown fails tmux shutdown will be used, this risks loss of world save.
  358. else
  359. if [ "${refused}" ]; then
  360. fn_print_error "Graceful: telnet: "
  361. fn_print_fail_eol_nl
  362. fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
  363. else
  364. fn_print_error_nl "Graceful: telnet: Unknown error"
  365. fn_script_log_error "Graceful: telnet: Unknown error"
  366. fi
  367. echo -en "\n" | tee -a "${lgsmlog}"
  368. echo -en "Telnet output:" | tee -a "${lgsmlog}"
  369. echo -en "\n ${sdtdtelnetshutdown}" | tee -a "${lgsmlog}"
  370. echo -en "\n\n" | tee -a "${lgsmlog}"
  371. fi
  372. else
  373. fn_print_warn "Graceful: telnet: expect not installed: "
  374. fn_print_fail_eol_nl
  375. fn_script_log_warn "Graceful: telnet: expect not installed: FAIL"
  376. fi
  377. }
  378. # Attempts graceful shutdown of Soulmask using telnet.
  379. fn_stop_graceful_sm() {
  380. fn_print_dots "Graceful: telnet"
  381. fn_script_log_info "Graceful: telnet"
  382. if [ "${telnetenabled}" == "false" ]; then
  383. fn_print_info_nl "Graceful: telnet: DISABLED: Enable in ${servercfg}"
  384. elif [ "$(command -v expect 2> /dev/null)" ]; then
  385. # Tries to shutdown with both localhost and server IP.
  386. for telnetip in 127.0.0.1 ${ip}; do
  387. fn_print_dots "Graceful: telnet: ${telnetip}:${telnetport}"
  388. fn_script_log_info "Graceful: telnet: ${telnetip}:${telnetport}"
  389. fn_stop_graceful_sm_telnet
  390. completed=$(echo -en "\n ${smtelnetshutdown}" | grep "Completed.")
  391. refused=$(echo -en "\n ${smtelnetshutdown}" | grep "Timeout or EOF")
  392. if [ "${refused}" ]; then
  393. fn_print_error "Graceful: telnet: ${telnetip}:${telnetport} : "
  394. fn_print_fail_eol_nl
  395. fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
  396. elif [ "${completed}" ]; then
  397. break
  398. fi
  399. done
  400. # If telnet shutdown was successful will use telnet again to check
  401. # the connection has closed, confirming that the tmux session can now be killed.
  402. if [ "${completed}" ]; then
  403. for seconds in {1..30}; do
  404. fn_stop_graceful_sm_telnet
  405. refused=$(echo -en "\n ${smtelnetshutdown}" | grep "Timeout or EOF")
  406. if [ "${refused}" ]; then
  407. fn_print_ok "Graceful: telnet: ${telnetip}:${telnetport} : "
  408. fn_print_ok_eol_nl
  409. fn_script_log_pass "Graceful: telnet: ${telnetip}:${telnetport} : ${seconds} seconds"
  410. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  411. alert="stopped"
  412. alert.sh
  413. fi
  414. break
  415. fi
  416. fn_sleep_time_1
  417. fn_print_dots "Graceful: telnet: ${seconds}"
  418. done
  419. # If telnet shutdown fails, show it and stop
  420. else
  421. if [ "${refused}" ]; then
  422. fn_print_error "Graceful: telnet: "
  423. fn_print_fail_eol_nl
  424. fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
  425. else
  426. fn_print_error_nl "Graceful: telnet: Unknown error"
  427. fn_script_log_error "Graceful: telnet: Unknown error"
  428. fi
  429. echo -en "\n" | tee -a "${lgsmlog}"
  430. echo -en "Telnet output:" | tee -a "${lgsmlog}"
  431. echo -en "\n ${smtelnetshutdown}" | tee -a "${lgsmlog}"
  432. echo -en "\n\n" | tee -a "${lgsmlog}"
  433. fi
  434. else
  435. fn_print_warn "Graceful: telnet: expect not installed: "
  436. fn_print_fail_eol_nl
  437. fn_script_log_warn "Graceful: telnet: expect not installed: FAIL"
  438. fi
  439. }
  440. # telnet command for soulmask graceful shutdown.
  441. fn_stop_graceful_sm_telnet() {
  442. smtelnetshutdown=$(expect -c '
  443. proc abort {} {
  444. puts "Timeout or EOF\n"
  445. exit 1
  446. }
  447. spawn telnet '"${telnetip}"' '"${telnetport}"'
  448. expect {
  449. "Hello:" { send "saveworld 1\r" }
  450. default abort
  451. }
  452. expect {
  453. "the world is saved." { send "quit 1\r" }
  454. default abort
  455. }
  456. expect {
  457. "World is closing..." {}
  458. default abort
  459. }
  460. expect { eof }
  461. puts "Completed.\n"
  462. ')
  463. }
  464. # Attempts graceful shutdown by sending /save /stop.
  465. fn_stop_graceful_avorion() {
  466. fn_print_dots "Graceful: /save /stop"
  467. fn_script_log_info "Graceful: /save /stop"
  468. # Sends /save.
  469. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" /save ENTER > /dev/null 2>&1
  470. fn_sleep_time_5
  471. # Sends /quit.
  472. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" /stop ENTER > /dev/null 2>&1
  473. # Waits up to 30 seconds giving the server time to shutdown gracefully.
  474. for seconds in {1..30}; do
  475. check_status.sh
  476. if [ "${status}" == "0" ]; then
  477. fn_print_ok "Graceful: /save /stop: ${seconds}"
  478. fn_print_ok_eol_nl
  479. fn_script_log_pass "Graceful: /save /stop: OK: ${seconds} seconds"
  480. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  481. alert="stopped"
  482. alert.sh
  483. fi
  484. break
  485. fi
  486. fn_sleep_time_1
  487. fn_print_dots "Graceful: /save /stop: ${seconds}"
  488. done
  489. check_status.sh
  490. if [ "${status}" != "0" ]; then
  491. fn_print_error "Graceful: /save /stop: "
  492. fn_print_fail_eol_nl
  493. fn_script_log_error "Graceful: /save /stop: FAIL"
  494. fi
  495. }
  496. fn_stop_graceful_rwr() {
  497. fn_print_dots "Graceful: save_profiles stop_server"
  498. fn_script_log_info "Graceful: save_profiles stop_server"
  499. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" save_profiles ENTER > /dev/null 2>&1
  500. fn_sleep_time_5
  501. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" stop_server ENTER > /dev/null 2>&1
  502. fn_sleep_time_5
  503. fn_stop_graceful_ctrlc
  504. }
  505. # Attempts graceful shutdown of Rising World using 'shutdown'.
  506. # Its console can miss input, so the command is sent again every 10 seconds until the server stops.
  507. fn_stop_graceful_rw() {
  508. fn_print_dots "Graceful: sending \"shutdown\""
  509. fn_script_log_info "Graceful: sending \"shutdown\""
  510. for ((seconds = 1; seconds <= 120; seconds++)); do
  511. if [ $((seconds % 10)) -eq 1 ]; then
  512. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" ENTER > /dev/null 2>&1
  513. sleep 1
  514. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" -l "shutdown" > /dev/null 2>&1
  515. TERM=screen tmux -L "${socketname}" send-keys -t "${sessionname}" ENTER > /dev/null 2>&1
  516. fi
  517. check_status.sh
  518. if [ "${status}" == "0" ]; then
  519. fn_print_ok "Graceful: sending \"shutdown\": ${seconds}"
  520. fn_print_ok_eol_nl
  521. fn_script_log_pass "Graceful: sending \"shutdown\": OK: ${seconds} seconds"
  522. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  523. alert="stopped"
  524. alert.sh
  525. fi
  526. break
  527. fi
  528. fn_sleep_time_1
  529. fn_print_dots "Graceful: sending \"shutdown\": ${seconds}"
  530. done
  531. check_status.sh
  532. if [ "${status}" != "0" ]; then
  533. fn_print_error "Graceful: sending \"shutdown\": "
  534. fn_print_fail_eol_nl
  535. fn_script_log_error "Graceful: sending \"shutdown\": FAIL"
  536. fi
  537. }
  538. fn_stop_graceful_select() {
  539. if [ "${stopmode}" == "1" ]; then
  540. fn_stop_tmux
  541. elif [ "${stopmode}" == "2" ]; then
  542. fn_stop_graceful_ctrlc
  543. elif [ "${stopmode}" == "3" ]; then
  544. fn_stop_graceful_cmd "quit" 30
  545. elif [ "${stopmode}" == "4" ]; then
  546. fn_stop_graceful_cmd "quit" 120
  547. elif [ "${stopmode}" == "5" ]; then
  548. fn_stop_graceful_cmd "stop" 30
  549. elif [ "${stopmode}" == "6" ]; then
  550. fn_stop_graceful_cmd "q" 30
  551. elif [ "${stopmode}" == "7" ]; then
  552. fn_stop_graceful_cmd "exit" 30
  553. elif [ "${stopmode}" == "8" ]; then
  554. fn_stop_graceful_sdtd
  555. elif [ "${stopmode}" == "9" ]; then
  556. fn_stop_graceful_goldsrc
  557. elif [ "${stopmode}" == "10" ]; then
  558. fn_stop_graceful_avorion
  559. elif [ "${stopmode}" == "11" ]; then
  560. fn_stop_graceful_cmd "end" 30
  561. elif [ "${stopmode}" == "12" ]; then
  562. fn_stop_graceful_cmd "shutdown" 30
  563. elif [ "${stopmode}" == "13" ]; then
  564. fn_stop_graceful_sm
  565. elif [ "${stopmode}" == "14" ]; then
  566. fn_stop_graceful_rwr
  567. elif [ "${stopmode}" == "15" ]; then
  568. fn_stop_graceful_rw
  569. fi
  570. }
  571. fn_stop_tmux() {
  572. fn_print_dots "${servername}"
  573. fn_script_log_info "tmux kill-session: ${sessionname}: ${servername}"
  574. # Kill tmux session.
  575. TERM=screen tmux -L "${socketname}" kill-session -t "${sessionname}" > /dev/null 2>&1
  576. fn_sleep_time_1
  577. check_status.sh
  578. if [ "${status}" == "0" ]; then
  579. fn_print_ok_nl "${servername}"
  580. fn_script_log_pass "Stopped ${servername}"
  581. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  582. alert="stopped"
  583. alert.sh
  584. fi
  585. else
  586. fn_print_fail_nl "Unable to stop ${servername}"
  587. fn_script_log_fail "Unable to stop ${servername}"
  588. fi
  589. }
  590. # Checks if the server is already stopped.
  591. fn_stop_pre_check() {
  592. if [ "${status}" == "0" ]; then
  593. fn_print_skip_nl "${servername} is already stopped"
  594. fn_script_log_info "${servername} is already stopped"
  595. else
  596. # Select graceful shutdown.
  597. fn_stop_graceful_select
  598. # Check status again, a kill tmux session if graceful shutdown failed.
  599. check_status.sh
  600. if [ "${status}" != "0" ]; then
  601. fn_stop_tmux
  602. fi
  603. fi
  604. }
  605. fn_print_dots ""
  606. check.sh
  607. info_game.sh
  608. # Players are checked and warned before the stopping lockfile is created,
  609. # as a warning countdown can outlast monitor's stale lockfile check.
  610. if [ "${status}" != "0" ]; then
  611. fn_stop_players_online
  612. fn_stop_warn_players
  613. check_status.sh
  614. fi
  615. # Create a stopping lockfile that only exists while the stop command is running.
  616. date '+%s' > "${lockdir:?}/${selfname}-stopping.lock"
  617. fn_print_dots "${servername}"
  618. fn_stop_pre_check
  619. # Remove started lockfile.
  620. rm -f "${lockdir:?}/${selfname}-started.lock"
  621. # If user ran the stop command monitor will become disabled.
  622. if [ "${firstcommandname}" == "STOP" ]; then
  623. rm -f "${lockdir:?}/${selfname}-monitoring.lock"
  624. fi
  625. # Remove stopping lockfile.
  626. rm -f "${lockdir:?}/${selfname}-stopping.lock"
  627. if [ -z "${exitbypass}" ]; then
  628. core_exit.sh
  629. fi