emqx 27 KB

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