emqx 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969
  1. #!/usr/bin/env bash
  2. # -*- tab-width:4;indent-tabs-mode:nil -*-
  3. # ex: ts=4 sw=4 et
  4. set -euo pipefail
  5. DEBUG="${DEBUG:-0}"
  6. if [ "$DEBUG" -eq 1 ]; then
  7. set -x
  8. fi
  9. ROOT_DIR="$(cd "$(dirname "$(readlink "$0" || echo "$0")")"/..; pwd -P)"
  10. # shellcheck disable=SC1090,SC1091
  11. . "$ROOT_DIR"/releases/emqx_vars
  12. # defined in emqx_vars
  13. export RUNNER_ROOT_DIR
  14. export RUNNER_ETC_DIR
  15. export REL_VSN
  16. export SCHEMA_MOD
  17. RUNNER_SCRIPT="$RUNNER_BIN_DIR/$REL_NAME"
  18. CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
  19. REL_DIR="$RUNNER_ROOT_DIR/releases/$REL_VSN"
  20. WHOAMI=$(whoami)
  21. # Make sure log directory exists
  22. mkdir -p "$RUNNER_LOG_DIR"
  23. # hocon try to read environment variables starting with "EMQX_"
  24. export HOCON_ENV_OVERRIDE_PREFIX='EMQX_'
  25. export ROOTDIR="$RUNNER_ROOT_DIR"
  26. export ERTS_DIR="$ROOTDIR/erts-$ERTS_VSN"
  27. export BINDIR="$ERTS_DIR/bin"
  28. export EMU="beam"
  29. export PROGNAME="erl"
  30. export ERTS_LIB_DIR="$ERTS_DIR/../lib"
  31. DYNLIBS_DIR="$RUNNER_ROOT_DIR/dynlibs"
  32. # Echo to stderr on errors
  33. echoerr() { echo "ERROR: $*" 1>&2; }
  34. die() {
  35. echoerr "ERROR: $1"
  36. errno=${2:-1}
  37. exit "$errno"
  38. }
  39. assert_node_alive() {
  40. if ! relx_nodetool "ping" > /dev/null; then
  41. die "node_is_not_running!" 1
  42. fi
  43. }
  44. # Echo to stderr on errors
  45. echoerr() { echo "$*" 1>&2; }
  46. check_erlang_start() {
  47. # RELEASE_LIB is used by Elixir
  48. "$BINDIR/$PROGNAME" \
  49. -noshell \
  50. -boot_var RELEASE_LIB "$ERTS_LIB_DIR/lib" \
  51. -boot "$REL_DIR/start_clean" \
  52. -s crypto start \
  53. -s erlang halt
  54. }
  55. usage() {
  56. local command="$1"
  57. case "$command" in
  58. start)
  59. echo "Start EMQX service in daemon mode"
  60. ;;
  61. stop)
  62. echo "Stop the running EMQX program"
  63. ;;
  64. console)
  65. echo "Boot up EMQX service in an interactive Erlang or Elixir shell"
  66. echo "This command needs a tty"
  67. ;;
  68. console_clean)
  69. echo "This command does NOT boot up the EMQX service"
  70. echo "It only starts an interactive Erlang or Elixir console with all the"
  71. echo "EMQX code available"
  72. ;;
  73. foreground)
  74. echo "Start EMQX in foreground mode without an interactive shell"
  75. ;;
  76. pid)
  77. echo "Print out EMQX process identifier"
  78. ;;
  79. ping)
  80. echo "Check if the EMQX node is up and running"
  81. echo "This command exit with 0 silently if node is running"
  82. ;;
  83. escript)
  84. echo "Execute a escript using the Erlang runtime from EMQX package installation"
  85. echo "For example $REL_NAME escript /path/to/my/escript my_arg1 my_arg2"
  86. ;;
  87. attach)
  88. echo "This command is applicable when EMQX is started in daemon"
  89. echo "mode. it attaches the current shell to EMQX's control console"
  90. echo "through a named pipe"
  91. echo "WARNING: try to use the safer alternative, remote_console command."
  92. ;;
  93. remote_console)
  94. echo "Start a dummy Erlang or Elixir node and hidden-connect EMQX to"
  95. echo "with an interactive Erlang or Elixir shell"
  96. ;;
  97. ertspath)
  98. echo "Print path to Erlang runtime dir"
  99. ;;
  100. rpc)
  101. echo "Usge $REL_NAME rpc MODULE FUNCTION [ARGS, ...]"
  102. echo "Connect to the EMQX node and make an Erlang RPC"
  103. echo "This command blocks for at most 60 seconds."
  104. echo "It exits with non-zero code in case of any RPC failure"
  105. echo "including connection error and runtime exception"
  106. ;;
  107. rpcterms)
  108. echo "Usge $REL_NAME rpcterms MODULE FUNCTION [ARGS, ...]"
  109. echo "Connect to the EMQX node and make an Erlang RPC"
  110. echo "The result of the RPC call is pretty-printed as an "
  111. echo "Erlang term"
  112. ;;
  113. root_dir)
  114. echo "Print EMQX installation root dir"
  115. ;;
  116. eval)
  117. echo "Evaluate an Erlang or Elxir expression in the EMQX node"
  118. ;;
  119. eval-erl)
  120. echo "Evaluate an Erlang expression in the EMQX node, even on Elixir node"
  121. ;;
  122. versions)
  123. echo "List installed EMQX versions and their status"
  124. ;;
  125. unpack)
  126. echo "Usage: $REL_NAME unpack [VERSION]"
  127. echo "Unpacks a release package VERSION, it assumes that this"
  128. echo "release package tarball has already been deployed at one"
  129. echo "of the following locations:"
  130. echo " releases/<relname>-<version>.tar.gz"
  131. ;;
  132. install)
  133. echo "Usage: $REL_NAME install [VERSION]"
  134. echo "Installs a release package VERSION, it assumes that this"
  135. echo "release package tarball has already been deployed at one"
  136. echo "of the following locations:"
  137. echo " releases/<relname>-<version>.tar.gz"
  138. echo ""
  139. echo " --no-permanent Install release package VERSION but"
  140. echo " don't make it permanent"
  141. ;;
  142. uninstall)
  143. echo "Usage: $REL_NAME uninstall [VERSION]"
  144. echo "Uninstalls a release VERSION, it will only accept"
  145. echo "versions that are not currently in use"
  146. ;;
  147. upgrade)
  148. echo "Usage: $REL_NAME upgrade [VERSION]"
  149. echo "Upgrades the currently running release to VERSION, it assumes"
  150. echo "that a release package tarball has already been deployed at one"
  151. echo "of the following locations:"
  152. echo " releases/<relname>-<version>.tar.gz"
  153. echo ""
  154. echo " --no-permanent Install release package VERSION but"
  155. echo " don't make it permanent"
  156. ;;
  157. downgrade)
  158. echo "Usage: $REL_NAME downgrade [VERSION]"
  159. echo "Downgrades the currently running release to VERSION, it assumes"
  160. echo "that a release package tarball has already been deployed at one"
  161. echo "of the following locations:"
  162. echo " releases/<relname>-<version>.tar.gz"
  163. echo ""
  164. echo " --no-permanent Install release package VERSION but"
  165. echo " don't make it permanent"
  166. ;;
  167. *)
  168. echo "Usage: $REL_NAME COMMAND [help]"
  169. echo ''
  170. echo "Commonly used COMMANDs:"
  171. echo " start: Start EMQX in daemon mode"
  172. echo " console: Start EMQX in an interactive Erlang or Elixir shell"
  173. echo " foreground: Start EMQX in foreground mode without an interactive shell"
  174. echo " stop: Stop the running EMQX node"
  175. echo " ctl: Administration commands, execute '$REL_NAME ctl help' for more details"
  176. echo ''
  177. echo "More:"
  178. echo " Shell attach: remote_console | attach"
  179. echo " Up/Down-grade: upgrade | downgrade | install | uninstall"
  180. echo " Install info: ertspath | root_dir | versions | root_dir"
  181. echo " Runtime info: pid | ping | versions"
  182. echo " Advanced: console_clean | escript | rpc | rpcterms | eval | eval-erl"
  183. echo ''
  184. echo "Execute '$REL_NAME COMMAND help' for more information"
  185. ;;
  186. esac
  187. }
  188. COMMAND="${1:-}"
  189. if [ -z "$COMMAND" ]; then
  190. usage 'nil'
  191. exit 1
  192. fi
  193. if [ "${2:-}" = 'help' ]; then
  194. ## 'ctl' command has its own usage info
  195. if [ "$COMMAND" != 'ctl' ]; then
  196. usage "$COMMAND"
  197. exit 0
  198. fi
  199. fi
  200. if ! check_erlang_start >/dev/null 2>&1; then
  201. BUILD_INFO="$(cat "${REL_DIR}/BUILD_INFO")"
  202. ## failed to start, might be due to missing libs, try to be portable
  203. export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-$DYNLIBS_DIR}"
  204. if [ "$LD_LIBRARY_PATH" != "$DYNLIBS_DIR" ]; then
  205. export LD_LIBRARY_PATH="$DYNLIBS_DIR:$LD_LIBRARY_PATH"
  206. fi
  207. deps_hint="Please make sure openssl-1.1.1 (libcrypto), libncurses and libatomic1 are installed."
  208. if ! check_erlang_start; then
  209. ## it's hopeless
  210. echoerr "FATAL: Unable to start Erlang."
  211. echoerr "$deps_hint"
  212. echoerr "Also ensure it's running on the correct platform:"
  213. echoerr "$BUILD_INFO"
  214. exit 1
  215. fi
  216. echoerr "Using libs from '${DYNLIBS_DIR}' due to missing from the OS."
  217. echoerr "$deps_hint"
  218. fi
  219. ## backward compatible
  220. if [ -d "$ERTS_DIR/lib" ]; then
  221. export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH"
  222. fi
  223. # Simple way to check the correct user and fail early
  224. check_user() {
  225. # Validate that the user running the script is the owner of the
  226. # RUN_DIR.
  227. if [ "$RUNNER_USER" ] && [ "x$WHOAMI" != "x$RUNNER_USER" ]; then
  228. if [ "x$WHOAMI" != "xroot" ]; then
  229. echo "You need to be root or use sudo to run this command"
  230. exit 1
  231. fi
  232. CMD="DEBUG=$DEBUG \"$RUNNER_SCRIPT\" "
  233. for ARG in "$@"; do
  234. CMD="${CMD} \"$ARG\""
  235. done
  236. # This will drop privileges into the runner user
  237. # It exec's in a new shell and the current shell will exit
  238. exec su - "$RUNNER_USER" -c "$CMD"
  239. fi
  240. }
  241. # Make sure the user running this script is the owner and/or su to that user
  242. check_user "$@"
  243. ES=$?
  244. if [ "$ES" -ne 0 ]; then
  245. exit $ES
  246. fi
  247. # EPMD_ARG="-start_epmd true $PROTO_DIST_ARG"
  248. NO_EPMD="-start_epmd false -epmd_module ekka_epmd -proto_dist ekka"
  249. EPMD_ARG="${EPMD_ARG:-${NO_EPMD}}"
  250. # Warn the user if ulimit -n is less than 1024
  251. ULIMIT_F=$(ulimit -n)
  252. if [ "$ULIMIT_F" -lt 1024 ]; then
  253. echo "!!!!"
  254. echo "!!!! WARNING: ulimit -n is ${ULIMIT_F}; 1024 is the recommended minimum."
  255. echo "!!!!"
  256. fi
  257. SED_REPLACE="sed -i "
  258. case $(sed --help 2>&1) in
  259. *GNU*) SED_REPLACE="sed -i ";;
  260. *BusyBox*) SED_REPLACE="sed -i ";;
  261. *) SED_REPLACE="sed -i '' ";;
  262. esac
  263. # Get node pid
  264. relx_get_pid() {
  265. if output="$(relx_nodetool rpcterms os getpid)"
  266. then
  267. # shellcheck disable=SC2001 # Escaped quote taken as closing quote in editor
  268. echo "$output" | sed -e 's/"//g'
  269. return 0
  270. else
  271. echo "$output"
  272. return 1
  273. fi
  274. }
  275. # Connect to a remote node
  276. relx_rem_sh() {
  277. # Generate a unique id used to allow multiple remsh to the same node
  278. # transparently
  279. id="remsh$(relx_gen_id)-${NAME}"
  280. # Get the node's ticktime so that we use the same thing.
  281. TICKTIME="$(relx_nodetool rpcterms net_kernel get_net_ticktime)"
  282. # shellcheck disable=SC2086 # $EPMD_ARG is supposed to be split by whitespace
  283. # Setup remote shell command to control node
  284. if [ "$IS_ELIXIR" = "yes" ]
  285. then
  286. exec "$REL_DIR/iex" \
  287. --remsh "$NAME" \
  288. --boot-var RELEASE_LIB "$ERTS_LIB_DIR" \
  289. --cookie "$COOKIE" \
  290. --hidden \
  291. --erl "-kernel net_ticktime $TICKTIME" \
  292. --erl "$EPMD_ARG" \
  293. --erl "$NAME_TYPE $id" \
  294. --boot "$REL_DIR/start_clean"
  295. else
  296. exec "$BINDIR/erl" "$NAME_TYPE" "$id" \
  297. -remsh "$NAME" -boot "$REL_DIR/start_clean" \
  298. -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
  299. -setcookie "$COOKIE" -hidden -kernel net_ticktime "$TICKTIME" \
  300. $EPMD_ARG
  301. fi
  302. }
  303. # Generate a random id
  304. relx_gen_id() {
  305. od -t x -N 4 /dev/urandom | head -n1 | awk '{print $2}'
  306. }
  307. call_nodetool() {
  308. "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$@"
  309. }
  310. # Control a node
  311. relx_nodetool() {
  312. command="$1"; shift
  313. ERL_FLAGS="${ERL_FLAGS:-} $EPMD_ARG" \
  314. call_nodetool "$NAME_TYPE" "$NAME" \
  315. -setcookie "$COOKIE" "$command" "$@"
  316. }
  317. call_hocon() {
  318. call_nodetool hocon "$@" \
  319. || die "call_hocon_failed: $*" $?
  320. }
  321. get_config_value() {
  322. path_to_value="$1"
  323. call_hocon -s "$SCHEMA_MOD" -c "$RUNNER_ETC_DIR"/emqx.conf get "$path_to_value" | tr -d \"
  324. }
  325. check_license() {
  326. if [ "$IS_ENTERPRISE" == "no" ]; then
  327. return 0
  328. fi
  329. file_license="${EMQX_LICENSE__FILE:-$(get_config_value license.file)}"
  330. if [[ -n "$file_license" && ("$file_license" != "undefined") ]]; then
  331. call_nodetool check_license_file "$file_license"
  332. else
  333. key_license="${EMQX_LICENSE__KEY:-$(get_config_value license.key)}"
  334. if [[ -n "$key_license" && ("$key_license" != "undefined") ]]; then
  335. call_nodetool check_license_key "$key_license"
  336. else
  337. echoerr "License not found."
  338. echoerr "Please specify one via EMQX_LICENSE__KEY or EMQX_LICENSE__FILE variables"
  339. echoerr "or via license.key|file in emqx_enterprise.conf."
  340. return 1
  341. fi
  342. fi
  343. }
  344. # Run an escript in the node's environment
  345. relx_escript() {
  346. shift; scriptpath="$1"; shift
  347. "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" "$@"
  348. }
  349. # Output a start command for the last argument of run_erl
  350. relx_start_command() {
  351. printf "exec \"%s\" \"%s\"" "$RUNNER_SCRIPT" \
  352. "$START_OPTION"
  353. }
  354. DATA_DIR="$(get_config_value 'node.data_dir')"
  355. DATA_DIR="${DATA_DIR%/}"
  356. if [[ $DATA_DIR != /* ]]; then
  357. # relative
  358. DATA_DIR="${RUNNER_ROOT_DIR}/${DATA_DIR}"
  359. fi
  360. CONFIGS_DIR="$DATA_DIR/configs"
  361. mkdir -p "$CONFIGS_DIR"
  362. # Function to generate app.config and vm.args
  363. generate_config() {
  364. local name_type="$1"
  365. local node_name="$2"
  366. ## Delete the *.siz files first or it can't start after
  367. ## changing the config 'log.rotation.size'
  368. rm -rf "${RUNNER_LOG_DIR}"/*.siz
  369. ## timestamp for each generation
  370. local NOW_TIME
  371. NOW_TIME="$(call_hocon now_time)"
  372. ## this command populates two files: app.<time>.config and vm.<time>.args
  373. ## NOTE: the generate command merges environment variables to the base config (emqx.conf),
  374. ## but does not include the cluster-override.conf and local-override.conf
  375. ## meaning, certain overrides will not be mapped to app.<time>.config file
  376. call_hocon -v -t "$NOW_TIME" -s "$SCHEMA_MOD" -c "$RUNNER_ETC_DIR"/emqx.conf -d "$DATA_DIR"/configs generate
  377. ## filenames are per-hocon convention
  378. local CONF_FILE="$CONFIGS_DIR/app.$NOW_TIME.config"
  379. local HOCON_GEN_ARG_FILE="$CONFIGS_DIR/vm.$NOW_TIME.args"
  380. # This is needed by the Elixir scripts.
  381. # Do NOT append `.config`.
  382. RELEASE_SYS_CONFIG="$CONFIGS_DIR/app.$NOW_TIME"
  383. export RELEASE_SYS_CONFIG
  384. CONFIG_ARGS="-config $CONF_FILE -args_file $HOCON_GEN_ARG_FILE"
  385. ## Merge hocon generated *.args into the vm.args
  386. TMP_ARG_FILE="$CONFIGS_DIR/vm.args.tmp"
  387. cp "$RUNNER_ETC_DIR/vm.args" "$TMP_ARG_FILE"
  388. echo "" >> "$TMP_ARG_FILE"
  389. echo "-pa ${REL_DIR}/consolidated" >> "$TMP_ARG_FILE"
  390. ## read lines from generated vm.<time>.args file
  391. ## drop comment lines, and empty lines using sed
  392. ## pipe the lines to a while loop
  393. sed '/^#/d' "$HOCON_GEN_ARG_FILE" | sed '/^$/d' | while IFS='' read -r ARG_LINE || [ -n "$ARG_LINE" ]; do
  394. ## in the loop, split the 'key[:space:]value' pair
  395. ARG_KEY=$(echo "$ARG_LINE" | awk '{$NF="";print}')
  396. ARG_VALUE=$(echo "$ARG_LINE" | awk '{print $NF}')
  397. ## use the key to look up in vm.args file for the value
  398. TMP_ARG_VALUE=$(grep "^$ARG_KEY" "$TMP_ARG_FILE" || true | awk '{print $NF}')
  399. ## compare generated (to override) value to original (to be overridden) value
  400. if [ "$ARG_VALUE" != "$TMP_ARG_VALUE" ] ; then
  401. ## if they are different
  402. if [ -n "$TMP_ARG_VALUE" ]; then
  403. ## if the old value is present, replace it with generated value
  404. sh -c "$SED_REPLACE 's|^$ARG_KEY.*$|$ARG_LINE|' $TMP_ARG_FILE"
  405. else
  406. ## otherwise append generated value to the end
  407. echo "$ARG_LINE" >> "$TMP_ARG_FILE"
  408. fi
  409. fi
  410. done
  411. echo "$name_type $node_name" >> "$TMP_ARG_FILE"
  412. ## rename the generated vm.<time>.args file
  413. mv -f "$TMP_ARG_FILE" "$HOCON_GEN_ARG_FILE"
  414. # shellcheck disable=SC2086
  415. if ! relx_nodetool chkconfig $CONFIG_ARGS; then
  416. die "failed_to_check_config $CONFIG_ARGS"
  417. fi
  418. }
  419. # check if a PID is down
  420. is_down() {
  421. PID="$1"
  422. if ps -p "$PID" >/dev/null; then
  423. # still around
  424. # shellcheck disable=SC2009 # this grep pattern is not a part of the progra names
  425. if ps -p "$PID" | grep -q 'defunct'; then
  426. # zombie state, print parent pid
  427. parent="$(ps -o ppid= -p "$PID" | tr -d ' ')"
  428. echo "WARN: $PID is marked <defunct>, parent:"
  429. ps -p "$parent"
  430. return 0
  431. fi
  432. return 1
  433. fi
  434. # it's gone
  435. return 0
  436. }
  437. wait_for() {
  438. local WAIT_TIME
  439. local CMD
  440. WAIT_TIME="$1"
  441. shift
  442. CMD="$*"
  443. while true; do
  444. if $CMD >/dev/null 2>&1; then
  445. return 0
  446. fi
  447. if [ "$WAIT_TIME" -le 0 ]; then
  448. return 1
  449. fi
  450. WAIT_TIME=$((WAIT_TIME - 1))
  451. sleep 1
  452. done
  453. }
  454. wait_until_return_val() {
  455. local RESULT
  456. local WAIT_TIME
  457. local CMD
  458. RESULT="$1"
  459. WAIT_TIME="$2"
  460. shift 2
  461. CMD="$*"
  462. while true; do
  463. if [ "$($CMD 2>/dev/null)" = "$RESULT" ]; then
  464. return 0
  465. fi
  466. if [ "$WAIT_TIME" -le 0 ]; then
  467. return 1
  468. fi
  469. WAIT_TIME=$((WAIT_TIME - 1))
  470. sleep 1
  471. done
  472. }
  473. latest_vm_args() {
  474. local hint_var_name="$1"
  475. local vm_args_file
  476. vm_args_file="$(find "$CONFIGS_DIR" -type f -name "vm.*.args" | sort | tail -1)"
  477. if [ -f "$vm_args_file" ]; then
  478. echo "$vm_args_file"
  479. else
  480. echoerr "ERROR: node not initialized?"
  481. echoerr "Generated config file vm.*.args is not found for command '$COMMAND'"
  482. echoerr "in config dir: $CONFIGS_DIR"
  483. echoerr "In case the file has been deleted while the node is running,"
  484. echoerr "set environment variable '$hint_var_name' to continue"
  485. exit 1
  486. fi
  487. }
  488. # backward compabible with 4.x
  489. tr_log_to_env() {
  490. local log_to=${EMQX_LOG__TO:-undefined}
  491. # unset because it's unknown to 5.0
  492. unset EMQX_LOG__TO
  493. case "${log_to}" in
  494. console)
  495. export EMQX_LOG__CONSOLE_HANDLER__ENABLE='true'
  496. export EMQX_LOG__FILE_HANDLERS__DEFAULT__ENABLE='false'
  497. ;;
  498. file)
  499. export EMQX_LOG__CONSOLE_HANDLER__ENABLE='false'
  500. export EMQX_LOG__FILE_HANDLERS__DEFAULT__ENABLE='true'
  501. ;;
  502. both)
  503. export EMQX_LOG__CONSOLE_HANDLER__ENABLE='true'
  504. export EMQX_LOG__FILE_HANDLERS__DEFAULT__ENABLE='true'
  505. ;;
  506. default)
  507. # want to use config file defaults, do nothing
  508. ;;
  509. undefined)
  510. # value not set, do nothing
  511. ;;
  512. *)
  513. echoerr "Unknown environment value for EMQX_LOG__TO=${log_to} discarded"
  514. ;;
  515. esac
  516. }
  517. maybe_log_to_console() {
  518. if [ "${EMQX_LOG__TO:-}" = 'default' ]; then
  519. # want to use config file defaults, do nothing
  520. unset EMQX_LOG__TO
  521. else
  522. tr_log_to_env
  523. # ensure defaults
  524. export EMQX_LOG__CONSOLE_HANDLER__ENABLE="${EMQX_LOG__CONSOLE_HANDLER__ENABLE:-true}"
  525. export EMQX_LOG__FILE_HANDLERS__DEFAULT__ENABLE="${EMQX_LOG__FILE_HANDLERS__DEFAULT__ENABLE:-false}"
  526. fi
  527. }
  528. ## IS_BOOT_COMMAND is set for later to inspect node name and cookie from hocon config (or env variable)
  529. case "${COMMAND}" in
  530. start|console|console_clean|foreground)
  531. IS_BOOT_COMMAND='yes'
  532. ;;
  533. ertspath)
  534. echo "$ERTS_PATH"
  535. exit 0
  536. ;;
  537. *)
  538. IS_BOOT_COMMAND='no'
  539. ;;
  540. esac
  541. ## make EMQX_NODE_COOKIE right
  542. if [ -n "${EMQX_NODE_NAME:-}" ]; then
  543. export EMQX_NODE__NAME="${EMQX_NODE_NAME}"
  544. unset EMQX_NODE_NAME
  545. fi
  546. ## Possible ways to configure emqx node name:
  547. ## 1. configure node.name in emqx.conf
  548. ## 2. override with environment variable EMQX_NODE__NAME
  549. ## Node name is either short-name (without '@'), e.g. 'emqx'
  550. ## or long name (with '@') e.g. 'emqx@example.net' or 'emqx@127.0.0.1'
  551. NAME="${EMQX_NODE__NAME:-}"
  552. if [ -z "$NAME" ]; then
  553. if [ "$IS_BOOT_COMMAND" = 'yes' ]; then
  554. # for boot commands, inspect emqx.conf for node name
  555. NAME="$(get_config_value node.name)"
  556. else
  557. vm_args_file="$(latest_vm_args 'EMQX_NODE__NAME')"
  558. NAME="$(grep -E '^-s?name' "${vm_args_file}" | awk '{print $2}')"
  559. fi
  560. fi
  561. # force to use 'emqx' short name
  562. [ -z "$NAME" ] && NAME='emqx'
  563. MNESIA_DATA_DIR="$DATA_DIR/mnesia/$NAME"
  564. case "$NAME" in
  565. *@*)
  566. NAME_TYPE='-name'
  567. ;;
  568. *)
  569. NAME_TYPE='-sname'
  570. esac
  571. SHORT_NAME="$(echo "$NAME" | awk -F'@' '{print $1}')"
  572. export ESCRIPT_NAME="$SHORT_NAME"
  573. PIPE_DIR="${PIPE_DIR:-/$DATA_DIR/${WHOAMI}_erl_pipes/$NAME/}"
  574. ## make EMQX_NODE_COOKIE right
  575. if [ -n "${EMQX_NODE_COOKIE:-}" ]; then
  576. export EMQX_NODE__COOKIE="${EMQX_NODE_COOKIE}"
  577. unset EMQX_NODE_COOKIE
  578. fi
  579. COOKIE="${EMQX_NODE__COOKIE:-}"
  580. if [ -z "$COOKIE" ]; then
  581. if [ "$IS_BOOT_COMMAND" = 'yes' ]; then
  582. COOKIE="$(get_config_value node.cookie)"
  583. else
  584. vm_args_file="$(latest_vm_args 'EMQX_NODE__COOKIE')"
  585. COOKIE="$(grep -E '^-setcookie' "${vm_args_file}" | awk '{print $2}')"
  586. fi
  587. fi
  588. if [ -z "$COOKIE" ]; then
  589. die "Please set node.cookie in $RUNNER_ETC_DIR/emqx.conf or override from environment variable EMQX_NODE__COOKIE"
  590. fi
  591. cd "$ROOTDIR"
  592. case "${COMMAND}" in
  593. start)
  594. # Make sure a node IS not running
  595. if relx_nodetool "ping" >/dev/null 2>&1; then
  596. die "node_is_already_running!"
  597. fi
  598. # this flag passes down to console mode
  599. # so we know it's intended to be run in daemon mode
  600. export _EMQX_START_DAEMON_MODE=1
  601. case "$COMMAND" in
  602. start)
  603. shift
  604. START_OPTION="console"
  605. HEART_OPTION="start"
  606. ;;
  607. esac
  608. RUN_PARAM="$*"
  609. # Set arguments for the heart command
  610. set -- "$RUNNER_SCRIPT" "$HEART_OPTION"
  611. [ "$RUN_PARAM" ] && set -- "$@" "$RUN_PARAM"
  612. # Export the HEART_COMMAND
  613. HEART_COMMAND="$RUNNER_SCRIPT $COMMAND"
  614. export HEART_COMMAND
  615. ## See: http://erlang.org/doc/man/run_erl.html
  616. # Export the RUN_ERL_LOG_GENERATIONS
  617. export RUN_ERL_LOG_GENERATIONS=${RUN_ERL_LOG_GENERATIONS:-"5"}
  618. # Export the RUN_ERL_LOG_MAXSIZE
  619. export RUN_ERL_LOG_MAXSIZE=${RUN_ERL_LOG_MAXSIZE:-"10485760"}
  620. mkdir -p "$PIPE_DIR"
  621. "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \
  622. "$(relx_start_command)"
  623. WAIT_TIME=${EMQX_WAIT_FOR_START:-120}
  624. if wait_until_return_val "true" "$WAIT_TIME" 'relx_nodetool' \
  625. 'eval' 'emqx:is_running()'; then
  626. echo "$EMQX_DESCRIPTION $REL_VSN is started successfully!"
  627. exit 0
  628. else
  629. echo "$EMQX_DESCRIPTION $REL_VSN failed to start in ${WAIT_TIME} seconds."
  630. echo "Please find more information in erlang.log.N"
  631. echo "Or run 'DEBUG=1 $0 console' to have logs printed to console."
  632. exit 1
  633. fi
  634. ;;
  635. stop)
  636. # Wait for the node to completely stop...
  637. PID="$(relx_get_pid)"
  638. if ! relx_nodetool "stop"; then
  639. echoerr "Graceful shutdown failed PID=[$PID]"
  640. exit 1
  641. fi
  642. WAIT_TIME="${EMQX_WAIT_FOR_STOP:-120}"
  643. if ! wait_for "$WAIT_TIME" 'is_down' "$PID"; then
  644. msg="dangling after ${WAIT_TIME} seconds"
  645. # also log to syslog
  646. logger -t "${REL_NAME}[${PID}]" "STOP: $msg"
  647. # log to user console
  648. echoerr "Stop failed, $msg"
  649. echo "ERROR: $PID is still around"
  650. ps -p "$PID"
  651. exit 1
  652. fi
  653. echo "ok"
  654. logger -t "${REL_NAME}[${PID}]" "STOP: OK"
  655. ;;
  656. pid)
  657. ## Get the VM's pid
  658. if ! relx_get_pid; then
  659. exit 1
  660. fi
  661. ;;
  662. ping)
  663. assert_node_alive
  664. echo pong
  665. ;;
  666. escript)
  667. ## Run an escript under the node's environment
  668. if ! relx_escript "$@"; then
  669. exit 1
  670. fi
  671. ;;
  672. attach)
  673. assert_node_alive
  674. shift
  675. exec "$BINDIR/to_erl" "$PIPE_DIR"
  676. ;;
  677. remote_console)
  678. assert_node_alive
  679. shift
  680. relx_rem_sh
  681. ;;
  682. upgrade|downgrade|install|unpack|uninstall)
  683. if [ -z "${2:-}" ]; then
  684. echo "Missing version argument"
  685. echo "Usage: $REL_NAME $COMMAND {version}"
  686. exit 1
  687. fi
  688. shift
  689. assert_node_alive
  690. ERL_FLAGS="${ERL_FLAGS:-} $EPMD_ARG" \
  691. exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \
  692. "$COMMAND" "{'$REL_NAME', \"$NAME_TYPE\", '$NAME', '$COOKIE'}" "$@"
  693. ;;
  694. versions)
  695. assert_node_alive
  696. shift
  697. ERL_FLAGS="${ERL_FLAGS:-} $EPMD_ARG" \
  698. exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \
  699. "versions" "{'$REL_NAME', \"$NAME_TYPE\", '$NAME', '$COOKIE'}" "$@"
  700. ;;
  701. console|console_clean)
  702. # .boot file typically just $REL_NAME (ie, the app name)
  703. # however, for debugging, sometimes start_clean.boot is useful.
  704. # For e.g. 'setup', one may even want to name another boot script.
  705. case "$COMMAND" in
  706. console)
  707. if [ -f "$REL_DIR/$REL_NAME.boot" ]; then
  708. BOOTFILE="$REL_DIR/$REL_NAME"
  709. else
  710. BOOTFILE="$REL_DIR/start"
  711. fi
  712. ;;
  713. console_clean)
  714. BOOTFILE="$REL_DIR/start_clean"
  715. ;;
  716. esac
  717. # set before generate_config
  718. if [ "${_EMQX_START_DAEMON_MODE:-}" = 1 ]; then
  719. tr_log_to_env
  720. else
  721. maybe_log_to_console
  722. fi
  723. #generate app.config and vm.args
  724. generate_config "$NAME_TYPE" "$NAME"
  725. check_license
  726. # Setup beam-required vars
  727. EMU="beam"
  728. PROGNAME="${0#*/}"
  729. export EMU
  730. export PROGNAME
  731. # Store passed arguments since they will be erased by `set`
  732. ARGS="$*"
  733. # shellcheck disable=SC2086 # $CONFIG_ARGS $EPMD_ARG are supposed to be split by whitespace
  734. # Build an array of arguments to pass to exec later on
  735. # Build it here because this command will be used for logging.
  736. if [ "$IS_ELIXIR" = yes ]
  737. then
  738. set -- "$REL_DIR/iex" \
  739. --boot "$BOOTFILE" \
  740. --erl "-mode $CODE_LOADING_MODE" \
  741. --boot-var RELEASE_LIB "$ERTS_LIB_DIR" \
  742. --erl "-mnesia dir \"${MNESIA_DATA_DIR}\"" \
  743. --erl "$CONFIG_ARGS" \
  744. --erl "$EPMD_ARG" \
  745. --werl
  746. else
  747. set -- "$BINDIR/erlexec" \
  748. -boot "$BOOTFILE" -mode "$CODE_LOADING_MODE" \
  749. -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
  750. -mnesia dir "\"${MNESIA_DATA_DIR}\"" \
  751. $CONFIG_ARGS $EPMD_ARG
  752. fi
  753. # Log the startup
  754. logger -t "${REL_NAME}[$$]" "EXEC: $* -- ${1+$ARGS}"
  755. # Start the VM
  756. exec "$@" -- ${1+$ARGS}
  757. ;;
  758. foreground)
  759. # start up the release in the foreground for use by runit
  760. # or other supervision services
  761. maybe_log_to_console
  762. #generate app.config and vm.args
  763. generate_config "$NAME_TYPE" "$NAME"
  764. check_license
  765. [ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start
  766. FOREGROUNDOPTIONS="-noshell -noinput +Bd"
  767. # Setup beam-required vars
  768. EMU=beam
  769. PROGNAME="${0#*/}"
  770. export EMU
  771. export PROGNAME
  772. # Store passed arguments since they will be erased by `set`
  773. ARGS="$*"
  774. # shellcheck disable=SC2086 # $CONFIG_ARGS $EPMD_ARG are supposed to be split by whitespace
  775. # Build an array of arguments to pass to exec later on
  776. # Build it here because this command will be used for logging.
  777. if [ "$IS_ELIXIR" = yes ]
  778. then
  779. set -- "$REL_DIR/elixir" \
  780. --boot "$REL_DIR/start" \
  781. --erl "$FOREGROUNDOPTIONS" \
  782. --erl "-mode $CODE_LOADING_MODE" \
  783. --boot-var RELEASE_LIB "$ERTS_LIB_DIR" \
  784. --boot-var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
  785. --erl "-mnesia dir \"${MNESIA_DATA_DIR}\"" \
  786. --erl "$CONFIG_ARGS" \
  787. --erl "$EPMD_ARG" \
  788. --no-halt
  789. else
  790. set -- "$BINDIR/erlexec" $FOREGROUNDOPTIONS \
  791. -boot "$REL_DIR/$BOOTFILE" -mode "$CODE_LOADING_MODE" \
  792. -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
  793. -mnesia dir "\"${MNESIA_DATA_DIR}\"" \
  794. $CONFIG_ARGS $EPMD_ARG
  795. fi
  796. # Log the startup
  797. logger -t "${REL_NAME}[$$]" "EXEC: $* -- ${1+$ARGS}"
  798. # Start the VM
  799. exec "$@" -- ${1+$ARGS}
  800. ;;
  801. ctl)
  802. assert_node_alive
  803. shift
  804. relx_nodetool rpc_infinity emqx_ctl run_command "$@"
  805. ;;
  806. rpc)
  807. assert_node_alive
  808. shift
  809. relx_nodetool rpc "$@"
  810. ;;
  811. rpcterms)
  812. assert_node_alive
  813. shift
  814. relx_nodetool rpcterms "$@"
  815. ;;
  816. root_dir)
  817. assert_node_alive
  818. shift
  819. relx_nodetool "eval" 'code:root_dir()'
  820. ;;
  821. eval)
  822. assert_node_alive
  823. shift
  824. if [ "$IS_ELIXIR" = "yes" ]
  825. then
  826. "$REL_DIR/elixir" \
  827. --hidden \
  828. --name "rand-$(relx_gen_id)-$NAME" \
  829. --cookie "$COOKIE" \
  830. --boot "$REL_DIR/start_clean" \
  831. --boot-var RELEASE_LIB "$ERTS_LIB_DIR" \
  832. --vm-args "$REL_DIR/remote.vm.args" \
  833. --erl "-start_epmd false -epmd_module ekka_epmd" \
  834. --rpc-eval "$NAME" "$@"
  835. else
  836. relx_nodetool "eval" "$@"
  837. fi
  838. ;;
  839. eval-erl)
  840. assert_node_alive
  841. shift
  842. relx_nodetool "eval" "$@"
  843. ;;
  844. *)
  845. usage "$COMMAND"
  846. exit 1
  847. ;;
  848. esac
  849. exit 0