command_stop.sh 10 KB

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