command_stop.sh 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. #!/bin/bash
  2. # LinuxGSM command_stop.sh module
  3. # Author: Daniel Gibbs
  4. # Contributors: http://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 on the server
  12. fn_stop_players_online() {
  13. if [ "${stopifnoplayers}" == "on" ]; then
  14. if [ "${querymode}" == "2" ] || [ "${querymode}" == "3" ]; then
  15. for queryip in "${queryips[@]}"; do
  16. query_gamedig.sh
  17. if [ "${querystatus}" == "0" ]; then
  18. if [ -n "${gdplayers}" ] && [ "${gdplayers}" -ne 0 ]; then
  19. fn_print_info "Server will not stop while ${gdplayers} players are on the server"
  20. fn_script_log_info "Server will not stop while ${gdplayers} players are on the server"
  21. core_exit.sh
  22. else
  23. echo "No players on server, stopping server"
  24. break
  25. fi
  26. fi
  27. done
  28. fi
  29. fi
  30. }
  31. # Attempts graceful shutdown by sending 'CTRL+c'.
  32. fn_stop_graceful_ctrlc() {
  33. fn_print_dots "Graceful: CTRL+c"
  34. fn_script_log_info "Graceful: CTRL+c"
  35. # Sends CTRL+c.
  36. tmux -L "${socketname}" send-keys -t "${sessionname}" C-c > /dev/null 2>&1
  37. # Waits up to 30 seconds giving the server time to shutdown gracefuly.
  38. for seconds in {1..30}; do
  39. check_status.sh
  40. if [ "${status}" == "0" ]; then
  41. fn_print_ok "Graceful: CTRL+c: ${seconds}: "
  42. fn_print_ok_eol_nl
  43. fn_script_log_pass "Graceful: CTRL+c: OK: ${seconds} seconds"
  44. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  45. alert="stopped"
  46. alert.sh
  47. fi
  48. break
  49. fi
  50. fn_sleep_time_1
  51. fn_print_dots "Graceful: CTRL+c: ${seconds}"
  52. done
  53. check_status.sh
  54. if [ "${status}" != "0" ]; then
  55. fn_print_error "Graceful: CTRL+c: "
  56. fn_print_fail_eol_nl
  57. fn_script_log_error "Graceful: CTRL+c: FAIL"
  58. fi
  59. }
  60. # Attempts graceful shutdown by sending a specified command.
  61. # Usage: fn_stop_graceful_cmd "console_command" "timeout_in_seconds"
  62. # e.g.: fn_stop_graceful_cmd "quit" "30"
  63. fn_stop_graceful_cmd() {
  64. fn_print_dots "Graceful: sending \"${1}\""
  65. fn_script_log_info "Graceful: sending \"${1}\""
  66. # Sends specific stop command.
  67. tmux -L "${socketname}" send -t "${sessionname}" ENTER "${1}" ENTER > /dev/null 2>&1
  68. # Waits up to ${seconds} seconds giving the server time to shutdown gracefully.
  69. for ((seconds = 1; seconds <= ${2}; seconds++)); do
  70. check_status.sh
  71. if [ "${status}" == "0" ]; then
  72. fn_print_ok "Graceful: sending \"${1}\": ${seconds}: "
  73. fn_print_ok_eol_nl
  74. fn_script_log_pass "Graceful: sending \"${1}\": OK: ${seconds} seconds"
  75. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  76. alert="stopped"
  77. alert.sh
  78. fi
  79. break
  80. fi
  81. fn_sleep_time_1
  82. fn_print_dots "Graceful: sending \"${1}\": ${seconds}"
  83. done
  84. check_status.sh
  85. if [ "${status}" != "0" ]; then
  86. fn_print_error "Graceful: sending \"${1}\": "
  87. fn_print_fail_eol_nl
  88. fn_script_log_error "Graceful: sending \"${1}\": FAIL"
  89. fi
  90. }
  91. # Attempts graceful shutdown of goldsrc using rcon 'quit' command.
  92. # There is only a 3 second delay before a forced a tmux shutdown
  93. # as GoldSrc servers 'quit' command does a restart rather than shutdown.
  94. fn_stop_graceful_goldsrc() {
  95. fn_print_dots "Graceful: sending \"quit\""
  96. fn_script_log_info "Graceful: sending \"quit\""
  97. # sends quit
  98. tmux -L "${socketname}" send -t "${sessionname}" quit ENTER > /dev/null 2>&1
  99. # Waits 3 seconds as goldsrc servers restart with the quit command.
  100. for seconds in {1..3}; do
  101. fn_sleep_time_1
  102. fn_print_dots "Graceful: sending \"quit\": ${seconds}"
  103. done
  104. fn_print_ok "Graceful: sending \"quit\": ${seconds}: "
  105. fn_print_ok_eol_nl
  106. fn_script_log_pass "Graceful: sending \"quit\": OK: ${seconds} seconds"
  107. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  108. alert="stopped"
  109. alert.sh
  110. fi
  111. }
  112. # telnet command for sdtd graceful shutdown.
  113. fn_stop_graceful_sdtd_telnet() {
  114. if [ -z "${telnetpass}" ] || [ "${telnetpass}" == "NOT SET" ]; then
  115. sdtd_telnet_shutdown=$(expect -c '
  116. proc abort {} {
  117. puts "Timeout or EOF\n"
  118. exit 1
  119. }
  120. spawn telnet '"${telnetip}"' '"${telnetport}"'
  121. expect {
  122. "session." { send "shutdown\r" }
  123. default abort
  124. }
  125. expect { eof }
  126. puts "Completed.\n"
  127. ')
  128. else
  129. sdtd_telnet_shutdown=$(expect -c '
  130. proc abort {} {
  131. puts "Timeout or EOF\n"
  132. exit 1
  133. }
  134. spawn telnet '"${telnetip}"' '"${telnetport}"'
  135. expect {
  136. "password:" { send "'"${telnetpass}"'\r" }
  137. default abort
  138. }
  139. expect {
  140. "session." { send "shutdown\r" }
  141. default abort
  142. }
  143. expect { eof }
  144. puts "Completed.\n"
  145. ')
  146. fi
  147. }
  148. # Attempts graceful shutdown of 7 Days To Die using telnet.
  149. fn_stop_graceful_sdtd() {
  150. fn_print_dots "Graceful: telnet"
  151. fn_script_log_info "Graceful: telnet"
  152. if [ "${telnetenabled}" == "false" ]; then
  153. fn_print_info_nl "Graceful: telnet: DISABLED: Enable in ${servercfg}"
  154. elif [ "$(command -v expect 2> /dev/null)" ]; then
  155. # Tries to shutdown with both localhost and server IP.
  156. for telnetip in 127.0.0.1 ${ip}; do
  157. fn_print_dots "Graceful: telnet: ${telnetip}:${telnetport}"
  158. fn_script_log_info "Graceful: telnet: ${telnetip}:${telnetport}"
  159. fn_stop_graceful_sdtd_telnet
  160. completed=$(echo -en "\n ${sdtd_telnet_shutdown}" | grep "Completed.")
  161. refused=$(echo -en "\n ${sdtd_telnet_shutdown}" | grep "Timeout or EOF")
  162. if [ "${refused}" ]; then
  163. fn_print_error "Graceful: telnet: ${telnetip}:${telnetport} : "
  164. fn_print_fail_eol_nl
  165. fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
  166. elif [ "${completed}" ]; then
  167. break
  168. fi
  169. done
  170. # If telnet shutdown was successful will use telnet again to check
  171. # the connection has closed, confirming that the tmux session can now be killed.
  172. if [ "${completed}" ]; then
  173. for seconds in {1..30}; do
  174. fn_stop_graceful_sdtd_telnet
  175. refused=$(echo -en "\n ${sdtd_telnet_shutdown}" | grep "Timeout or EOF")
  176. if [ "${refused}" ]; then
  177. fn_print_ok "Graceful: telnet: ${telnetip}:${telnetport} : "
  178. fn_print_ok_eol_nl
  179. fn_script_log_pass "Graceful: telnet: ${telnetip}:${telnetport} : ${seconds} seconds"
  180. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  181. alert="stopped"
  182. alert.sh
  183. fi
  184. break
  185. fi
  186. fn_sleep_time_1
  187. fn_print_dots "Graceful: telnet: ${seconds}"
  188. done
  189. # If telnet shutdown fails tmux shutdown will be used, this risks loss of world save.
  190. else
  191. if [ "${refused}" ]; then
  192. fn_print_error "Graceful: telnet: "
  193. fn_print_fail_eol_nl
  194. fn_script_log_error "Graceful: telnet: ${telnetip}:${telnetport} : FAIL"
  195. else
  196. fn_print_error_nl "Graceful: telnet: Unknown error"
  197. fn_script_log_error "Graceful: telnet: Unknown error"
  198. fi
  199. echo -en "\n" | tee -a "${lgsmlog}"
  200. echo -en "Telnet output:" | tee -a "${lgsmlog}"
  201. echo -en "\n ${sdtd_telnet_shutdown}" | tee -a "${lgsmlog}"
  202. echo -en "\n\n" | tee -a "${lgsmlog}"
  203. fi
  204. else
  205. fn_print_warn "Graceful: telnet: expect not installed: "
  206. fn_print_fail_eol_nl
  207. fn_script_log_warn "Graceful: telnet: expect not installed: FAIL"
  208. fi
  209. }
  210. # Attempts graceful shutdown by sending /save /stop.
  211. fn_stop_graceful_avorion() {
  212. fn_print_dots "Graceful: /save /stop"
  213. fn_script_log_info "Graceful: /save /stop"
  214. # Sends /save.
  215. tmux -L "${socketname}" send-keys -t "${sessionname}" /save ENTER > /dev/null 2>&1
  216. fn_sleep_time_5
  217. # Sends /quit.
  218. tmux -L "${socketname}" send-keys -t "${sessionname}" /stop ENTER > /dev/null 2>&1
  219. # Waits up to 30 seconds giving the server time to shutdown gracefuly.
  220. for seconds in {1..30}; do
  221. check_status.sh
  222. if [ "${status}" == "0" ]; then
  223. fn_print_ok "Graceful: /save /stop: ${seconds}: "
  224. fn_print_ok_eol_nl
  225. fn_script_log_pass "Graceful: /save /stop: OK: ${seconds} seconds"
  226. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  227. alert="stopped"
  228. alert.sh
  229. fi
  230. break
  231. fi
  232. fn_sleep_time_1
  233. fn_print_dots "Graceful: /save /stop: ${seconds}"
  234. done
  235. check_status.sh
  236. if [ "${status}" != "0" ]; then
  237. fn_print_error "Graceful: /save /stop: "
  238. fn_print_fail_eol_nl
  239. fn_script_log_error "Graceful: /save /stop: FAIL"
  240. fi
  241. }
  242. fn_stop_graceful_select() {
  243. if [ "${stopmode}" == "1" ]; then
  244. fn_stop_tmux
  245. elif [ "${stopmode}" == "2" ]; then
  246. fn_stop_graceful_ctrlc
  247. elif [ "${stopmode}" == "3" ]; then
  248. fn_stop_graceful_cmd "quit" 30
  249. elif [ "${stopmode}" == "4" ]; then
  250. fn_stop_graceful_cmd "quit" 120
  251. elif [ "${stopmode}" == "5" ]; then
  252. fn_stop_graceful_cmd "stop" 30
  253. elif [ "${stopmode}" == "6" ]; then
  254. fn_stop_graceful_cmd "q" 30
  255. elif [ "${stopmode}" == "7" ]; then
  256. fn_stop_graceful_cmd "exit" 30
  257. elif [ "${stopmode}" == "8" ]; then
  258. fn_stop_graceful_sdtd
  259. elif [ "${stopmode}" == "9" ]; then
  260. fn_stop_graceful_goldsrc
  261. elif [ "${stopmode}" == "10" ]; then
  262. fn_stop_graceful_avorion
  263. elif [ "${stopmode}" == "11" ]; then
  264. fn_stop_graceful_cmd "end" 30
  265. elif [ "${stopmode}" == "12" ]; then
  266. fn_stop_graceful_cmd "shutdown" 30
  267. fi
  268. }
  269. fn_stop_tmux() {
  270. fn_print_dots "${servername}"
  271. fn_script_log_info "tmux kill-session: ${sessionname}: ${servername}"
  272. # Kill tmux session.
  273. tmux -L "${socketname}" kill-session -t "${sessionname}" > /dev/null 2>&1
  274. fn_sleep_time_1
  275. check_status.sh
  276. if [ "${status}" == "0" ]; then
  277. fn_print_ok_nl "${servername}"
  278. fn_script_log_pass "Stopped ${servername}"
  279. if [ "${statusalert}" == "on" ] && [ "${firstcommandname}" == "STOP" ]; then
  280. alert="stopped"
  281. alert.sh
  282. fi
  283. else
  284. fn_print_fail_nl "Unable to stop ${servername}"
  285. fn_script_log_fail "Unable to stop ${servername}"
  286. fi
  287. }
  288. # Checks if the server is already stopped.
  289. fn_stop_pre_check() {
  290. if [ "${status}" == "0" ]; then
  291. fn_print_info_nl "${servername} is already stopped"
  292. fn_script_log_info "${servername} is already stopped"
  293. else
  294. fn_stop_players_online
  295. # Select graceful shutdown.
  296. fn_stop_graceful_select
  297. # Check status again, a kill tmux session if graceful shutdown failed.
  298. check_status.sh
  299. if [ "${status}" != "0" ]; then
  300. fn_stop_tmux
  301. fi
  302. fi
  303. }
  304. fn_print_dots ""
  305. check.sh
  306. # Create a stopping lockfile that only exists while the stop command is running.
  307. date '+%s' > "${lockdir:?}/${selfname}-stopping.lock"
  308. fn_print_dots "${servername}"
  309. info_game.sh
  310. fn_stop_pre_check
  311. # Remove started lockfile.
  312. rm -f "${lockdir:?}/${selfname}-started.lock"
  313. # If user ran the stop command, monitor will become disabled.
  314. if [ "${firstcommandname}" == "STOP" ]; then
  315. rm -f "${lockdir:?}/${selfname}-monitoring.lock"
  316. fi
  317. # Remove stopping lockfile.
  318. rm -f "${lockdir:?}/${selfname}-stopping.lock"
  319. if [ -z "${exitbypass}" ]; then
  320. core_exit.sh
  321. fi