emqx 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213
  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. if [ "$DEBUG" -eq 2 ]; then
  10. set -x
  11. export PS4='+(${BASH_SOURCE}:${LINENO}): ${FUNCNAME[0]:+${FUNCNAME[0]}(): }'
  12. fi
  13. # We need to find real directory with emqx files on all platforms
  14. # even when bin/emqx is symlinked on several levels
  15. # - readlink -f works perfectly, but `-f` flag has completely different meaning in BSD version,
  16. # so we can't use it universally.
  17. # - `stat -f%R` on MacOS does exactly what `readlink -f` does on Linux, but we can't use it
  18. # as a universal solution either because GNU stat has different syntax and this argument is invalid.
  19. # Also, version of stat which supports this syntax is only available since MacOS 12
  20. if [ "$(uname -s)" == 'Darwin' ]; then
  21. product_version="$(sw_vers -productVersion | cut -d '.' -f 1)"
  22. if [ "$product_version" -ge 12 ]; then
  23. # if homebrew coreutils package is installed, GNU version of stat can take precedence,
  24. # so we use absolute path to ensure we are calling MacOS default
  25. RUNNER_ROOT_DIR="$(cd "$(dirname "$(/usr/bin/stat -f%R "$0" || echo "$0")")"/..; pwd -P)"
  26. else
  27. # try our best to resolve link on MacOS <= 11
  28. RUNNER_ROOT_DIR="$(cd "$(dirname "$(readlink "$0" || echo "$0")")"/..; pwd -P)"
  29. fi
  30. else
  31. RUNNER_ROOT_DIR="$(cd "$(dirname "$(realpath "$0" || echo "$0")")"/..; pwd -P)"
  32. fi
  33. # shellcheck disable=SC1090,SC1091
  34. . "$RUNNER_ROOT_DIR"/releases/emqx_vars
  35. # defined in emqx_vars
  36. export RUNNER_ROOT_DIR
  37. export EMQX_ETC_DIR
  38. export REL_VSN
  39. export SCHEMA_MOD
  40. export IS_ENTERPRISE
  41. RUNNER_SCRIPT="$RUNNER_BIN_DIR/$REL_NAME"
  42. CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
  43. REL_DIR="$RUNNER_ROOT_DIR/releases/$REL_VSN"
  44. WHOAMI=$(whoami)
  45. # hocon try to read environment variables starting with "EMQX_"
  46. export HOCON_ENV_OVERRIDE_PREFIX='EMQX_'
  47. export ERTS_DIR="$RUNNER_ROOT_DIR/erts-$ERTS_VSN"
  48. export BINDIR="$ERTS_DIR/bin"
  49. export EMU="beam"
  50. export PROGNAME="erl"
  51. export ERTS_LIB_DIR="$RUNNER_ROOT_DIR/lib"
  52. DYNLIBS_DIR="$RUNNER_ROOT_DIR/dynlibs"
  53. logerr() {
  54. if [ "${TERM:-dumb}" = dumb ]; then
  55. echo -e "ERROR: $*" 1>&2
  56. else
  57. echo -e "$(tput setaf 1)ERROR: $*$(tput sgr0)" 1>&2
  58. fi
  59. }
  60. logwarn() {
  61. if [ "${TERM:-dumb}" = dumb ]; then
  62. echo "WARNING: $*"
  63. else
  64. echo "$(tput setaf 3)WARNING: $*$(tput sgr0)"
  65. fi
  66. }
  67. die() {
  68. set +x
  69. logerr "$1"
  70. errno=${2:-1}
  71. exit "$errno"
  72. }
  73. assert_node_alive() {
  74. if ! relx_nodetool "ping" > /dev/null; then
  75. die "node_is_not_running!" 1
  76. fi
  77. }
  78. usage() {
  79. local command="$1"
  80. case "$command" in
  81. start)
  82. echo "Start EMQX service in daemon mode"
  83. ;;
  84. stop)
  85. echo "Stop the running EMQX program"
  86. ;;
  87. console)
  88. echo "Boot up EMQX service in an interactive Erlang or Elixir shell"
  89. echo "This command needs a tty"
  90. ;;
  91. console_clean)
  92. echo "This command does NOT boot up the EMQX service"
  93. echo "It only starts an interactive Erlang or Elixir console with all the"
  94. echo "EMQX code available"
  95. ;;
  96. foreground)
  97. echo "Start EMQX in foreground mode without an interactive shell"
  98. ;;
  99. pid)
  100. echo "Print out EMQX process identifier"
  101. ;;
  102. ping)
  103. echo "Check if the EMQX node is up and running"
  104. echo "This command exit with 0 silently if node is running"
  105. ;;
  106. escript)
  107. echo "Execute a escript using the Erlang runtime from EMQX package installation"
  108. echo "For example $REL_NAME escript /path/to/my/escript my_arg1 my_arg2"
  109. ;;
  110. attach)
  111. echo "This command is applicable when EMQX is started in daemon mode."
  112. echo "It attaches the current shell to EMQX's control console"
  113. echo "through a named pipe."
  114. logwarn "try to use the safer alternative, remote_console command."
  115. ;;
  116. remote_console)
  117. echo "Start an interactive shell running an Erlang or Elixir node which "
  118. echo "hidden-connects to the running EMQX node".
  119. echo "This command is mostly used for troubleshooting."
  120. ;;
  121. ertspath)
  122. echo "Print path to Erlang runtime bin dir"
  123. ;;
  124. rpc)
  125. echo "Usage: $REL_NAME rpc MODULE FUNCTION [ARGS, ...]"
  126. echo "Connect to the EMQX node and make an Erlang RPC"
  127. echo "This command blocks for at most 60 seconds."
  128. echo "It exits with non-zero code in case of any RPC failure"
  129. echo "including connection error and runtime exception"
  130. ;;
  131. rpcterms)
  132. echo "Usage: $REL_NAME rpcterms MODULE FUNCTION [ARGS, ...]"
  133. echo "Connect to the EMQX node and make an Erlang RPC"
  134. echo "The result of the RPC call is pretty-printed as an "
  135. echo "Erlang term"
  136. ;;
  137. root_dir)
  138. echo "Print EMQX installation root dir"
  139. ;;
  140. eval)
  141. echo "Evaluate an Erlang or Elixir expression in the EMQX node"
  142. ;;
  143. eval-erl)
  144. echo "Evaluate an Erlang expression in the EMQX node, even on Elixir node"
  145. ;;
  146. versions)
  147. echo "List installed EMQX versions and their status"
  148. ;;
  149. unpack)
  150. echo "Usage: $REL_NAME unpack [VERSION]"
  151. echo "Unpacks a release package VERSION, it assumes that this"
  152. echo "release package tarball has already been deployed at one"
  153. echo "of the following locations:"
  154. echo " releases/<relname>-<version>.tar.gz"
  155. ;;
  156. install)
  157. echo "Usage: $REL_NAME install [VERSION]"
  158. echo "Installs a release package VERSION, it assumes that this"
  159. echo "release package tarball has already been deployed at one"
  160. echo "of the following locations:"
  161. echo " releases/<relname>-<version>.tar.gz"
  162. echo ""
  163. echo " --no-permanent Install release package VERSION but"
  164. echo " don't make it permanent"
  165. ;;
  166. uninstall)
  167. echo "Usage: $REL_NAME uninstall [VERSION]"
  168. echo "Uninstalls a release VERSION, it will only accept"
  169. echo "versions that are not currently in use"
  170. ;;
  171. upgrade)
  172. echo "Usage: $REL_NAME upgrade [VERSION]"
  173. echo "Upgrades the currently running release to VERSION, it assumes"
  174. echo "that a release package tarball has already been deployed at one"
  175. echo "of the following locations:"
  176. echo " releases/<relname>-<version>.tar.gz"
  177. echo ""
  178. echo " --no-permanent Install release package VERSION but"
  179. echo " don't make it permanent"
  180. ;;
  181. downgrade)
  182. echo "Usage: $REL_NAME downgrade [VERSION]"
  183. echo "Downgrades the currently running release to VERSION, it assumes"
  184. echo "that a release package tarball has already been deployed at one"
  185. echo "of the following locations:"
  186. echo " releases/<relname>-<version>.tar.gz"
  187. echo ""
  188. echo " --no-permanent Install release package VERSION but"
  189. echo " don't make it permanent"
  190. ;;
  191. check_config)
  192. echo "Checks the EMQX config without generating any files"
  193. ;;
  194. *)
  195. echo "Usage: $REL_NAME COMMAND [help]"
  196. echo ''
  197. echo "Commonly used COMMANDs:"
  198. echo " start: Start EMQX in daemon mode"
  199. echo " console: Start EMQX in an interactive Erlang or Elixir shell"
  200. echo " foreground: Start EMQX in foreground mode without an interactive shell"
  201. echo " stop: Stop the running EMQX node"
  202. echo " ctl: Administration commands, execute '$REL_NAME ctl help' for more details"
  203. echo ''
  204. echo "More:"
  205. echo " Shell attach: remote_console | attach"
  206. echo " Up/Down-grade: upgrade | downgrade | install | uninstall"
  207. echo " Install info: ertspath | root_dir"
  208. echo " Runtime info: pid | ping | versions"
  209. echo " Validate Config: check_config"
  210. echo " Advanced: console_clean | escript | rpc | rpcterms | eval | eval-erl"
  211. echo ''
  212. echo "Execute '$REL_NAME COMMAND help' for more information"
  213. ;;
  214. esac
  215. }
  216. COMMAND="${1:-}"
  217. if [ -z "$COMMAND" ]; then
  218. usage 'help'
  219. exit 1
  220. elif [ "$COMMAND" = 'help' ]; then
  221. usage 'help'
  222. exit 0
  223. fi
  224. if [ "${2:-}" = 'help' ]; then
  225. ## 'ctl' command has its own usage info
  226. if [ "$COMMAND" != 'ctl' ]; then
  227. usage "$COMMAND"
  228. exit 0
  229. fi
  230. fi
  231. ## IS_BOOT_COMMAND is set for later to inspect node name and cookie from hocon config (or env variable)
  232. case "${COMMAND}" in
  233. start|console|console_clean|foreground|check_config)
  234. IS_BOOT_COMMAND='yes'
  235. ;;
  236. ertspath)
  237. echo "$ERTS_DIR"
  238. exit 0
  239. ;;
  240. root_dir)
  241. echo "$RUNNER_ROOT_DIR"
  242. exit 0
  243. ;;
  244. *)
  245. IS_BOOT_COMMAND='no'
  246. ;;
  247. esac
  248. ## backward compatible
  249. if [ -d "$ERTS_DIR/lib" ]; then
  250. export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH"
  251. fi
  252. # Simple way to check the correct user and fail early
  253. check_user() {
  254. # Validate that the user running the script is the owner of the
  255. # RUN_DIR.
  256. if [ "$RUNNER_USER" ] && [ "x$WHOAMI" != "x$RUNNER_USER" ]; then
  257. if [ "x$WHOAMI" != "xroot" ]; then
  258. echo "You need to be root or use sudo to run this command"
  259. exit 1
  260. fi
  261. CMD="DEBUG=$DEBUG \"$RUNNER_SCRIPT\" "
  262. for ARG in "$@"; do
  263. CMD="${CMD} \"$ARG\""
  264. done
  265. # This will drop privileges into the runner user
  266. # It exec's in a new shell and the current shell will exit
  267. exec su - "$RUNNER_USER" -c "$CMD"
  268. fi
  269. }
  270. # Make sure the user running this script is the owner and/or su to that user
  271. check_user "$@"
  272. ES=$?
  273. if [ "$ES" -ne 0 ]; then
  274. exit $ES
  275. fi
  276. # Make sure log directory exists
  277. mkdir -p "$RUNNER_LOG_DIR"
  278. COMPATIBILITY_CHECK='
  279. io:format("BEAM_OK~n", []),
  280. try
  281. [_|_] = L = crypto:info_lib(),
  282. io:format("CRYPTO_OK ~0p~n", [L])
  283. catch
  284. _ : _ ->
  285. %% so logger has the chance to log something
  286. timer:sleep(100),
  287. halt(1)
  288. end,
  289. try
  290. mnesia_hook:module_info(),
  291. io:format("MNESIA_OK~n", [])
  292. catch
  293. _ : _ ->
  294. io:format("WARNING: Mnesia app has no post-coommit hook support~n", []),
  295. halt(2)
  296. end,
  297. halt(0).
  298. '
  299. compatiblity_info() {
  300. # RELEASE_LIB is used by Elixir
  301. # set crash-dump bytes to zero to ensure no crash dump is generated when erl crashes
  302. env ERL_CRASH_DUMP_BYTES=0 "$BINDIR/$PROGNAME" \
  303. -noshell \
  304. -boot_var RELEASE_LIB "$ERTS_LIB_DIR/lib" \
  305. -boot "$REL_DIR/start_clean" \
  306. -eval "$COMPATIBILITY_CHECK"
  307. }
  308. # Collect Erlang/OTP runtime sanity and compatibility in one go
  309. if [ "$IS_BOOT_COMMAND" = 'yes' ]; then
  310. # Read BUILD_INFO early as the next commands may mess up the shell
  311. BUILD_INFO="$(cat "${REL_DIR}/BUILD_INFO")"
  312. COMPATIBILITY_INFO="$(compatiblity_info 2>/dev/null || true)"
  313. if ! (echo -e "$COMPATIBILITY_INFO" | grep -q 'CRYPTO_OK'); then
  314. ## failed to start, might be due to missing libs, try to be portable
  315. export LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-$DYNLIBS_DIR}"
  316. if [ "$LD_LIBRARY_PATH" != "$DYNLIBS_DIR" ]; then
  317. export LD_LIBRARY_PATH="$DYNLIBS_DIR:$LD_LIBRARY_PATH"
  318. fi
  319. ## Turn off debug, because COMPATIBILITY_INFO needs to capture stderr
  320. set +x
  321. COMPATIBILITY_INFO="$(compatiblity_info 2>&1 || true)"
  322. if ! (echo -e "$COMPATIBILITY_INFO" | grep -q 'BEAM_OK'); then
  323. ## not able to start beam.smp
  324. set +x
  325. logerr "$COMPATIBILITY_INFO"
  326. logerr "Please ensure it is running on the correct platform:"
  327. logerr "$BUILD_INFO"
  328. logerr "Version=$REL_VSN"
  329. logerr "Required dependencies: openssl-1.1.1 (libcrypto), libncurses and libatomic1"
  330. exit 1
  331. elif ! (echo -e "$COMPATIBILITY_INFO" | grep -q 'CRYPTO_OK'); then
  332. ## not able to start crypto app
  333. set +x
  334. logerr "$COMPATIBILITY_INFO"
  335. exit 2
  336. fi
  337. logerr "Using libs from '${DYNLIBS_DIR}' due to missing from the OS."
  338. fi
  339. [ "$DEBUG" -eq 1 ] && set -x
  340. fi
  341. # Warn the user if ulimit -n is less than 1024
  342. ULIMIT_F=$(ulimit -n)
  343. if [ "$ULIMIT_F" -lt 1024 ]; then
  344. logwarn "ulimit -n is ${ULIMIT_F}; 1024 is the recommended minimum."
  345. fi
  346. SED_REPLACE="sed -i "
  347. case $(sed --help 2>&1) in
  348. *GNU*) SED_REPLACE="sed -i ";;
  349. *BusyBox*) SED_REPLACE="sed -i ";;
  350. *) SED_REPLACE="sed -i '' ";;
  351. esac
  352. # Get node pid
  353. relx_get_pid() {
  354. if output="$(relx_nodetool rpcterms os getpid)"
  355. then
  356. # shellcheck disable=SC2001 # Escaped quote taken as closing quote in editor
  357. echo "$output" | sed -e 's/"//g'
  358. return 0
  359. else
  360. echo "$output"
  361. return 1
  362. fi
  363. }
  364. # Connect to a remote node
  365. remsh() {
  366. # Generate a unique id used to allow multiple remsh to the same node
  367. # transparently
  368. id="remsh$(relx_gen_id)-${NAME}"
  369. # Get the node's ticktime so that we use the same thing.
  370. TICKTIME="$(relx_nodetool rpcterms net_kernel get_net_ticktime)"
  371. # shellcheck disable=SC2086
  372. # Setup remote shell command to control node
  373. if [ "$IS_ELIXIR" = no ] || [ "${EMQX_CONSOLE_FLAVOR:-}" = 'erl' ] ; then
  374. set -- "$BINDIR/erl" "$NAME_TYPE" "$id" \
  375. -remsh "$NAME" -boot "$REL_DIR/start_clean" \
  376. -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
  377. -boot_var RELEASE_LIB "$ERTS_LIB_DIR" \
  378. -setcookie "$COOKIE" \
  379. -hidden \
  380. -kernel net_ticktime "$TICKTIME" \
  381. $EPMD_ARGS
  382. else
  383. set -- "$REL_DIR/iex" \
  384. --remsh "$NAME" \
  385. --boot-var RELEASE_LIB "$ERTS_LIB_DIR" \
  386. --cookie "$COOKIE" \
  387. --hidden \
  388. --erl "-kernel net_ticktime $TICKTIME" \
  389. --erl "$EPMD_ARGS" \
  390. --erl "$NAME_TYPE $id" \
  391. --boot "$REL_DIR/start_clean"
  392. fi
  393. exec "$@"
  394. }
  395. # Generate a random id
  396. relx_gen_id() {
  397. od -t u -N 4 /dev/urandom | head -n1 | awk '{print $2 % 1000}'
  398. }
  399. call_nodetool() {
  400. "$ERTS_DIR/bin/escript" "$RUNNER_ROOT_DIR/bin/nodetool" "$@"
  401. }
  402. # Control a node
  403. relx_nodetool() {
  404. command="$1"; shift
  405. ERL_FLAGS="${ERL_FLAGS:-} $EPMD_ARGS" \
  406. call_nodetool "$NAME_TYPE" "$NAME" \
  407. -setcookie "$COOKIE" "$command" "$@"
  408. }
  409. call_hocon() {
  410. call_nodetool hocon "$@" \
  411. || die "call_hocon_failed: $*" $?
  412. }
  413. ## Resolve boot configs in a batch
  414. ## This is because starting the Erlang beam with all modules loaded
  415. ## and parsing HOCON config + environment variables is a non-trivial task
  416. CONF_KEYS=( 'node.data_dir' 'node.name' 'node.cookie' 'node.db_backend' 'cluster.proto_dist' )
  417. if [ "$IS_ENTERPRISE" = 'yes' ]; then
  418. CONF_KEYS+=( 'license.key' )
  419. fi
  420. if [ "$IS_BOOT_COMMAND" = 'yes' ]; then
  421. if [ "${EMQX_BOOT_CONFIGS:-}" = '' ]; then
  422. EMQX_BOOT_CONFIGS="$(call_hocon -s "$SCHEMA_MOD" -c "$EMQX_ETC_DIR"/emqx.conf multi_get "${CONF_KEYS[@]}")"
  423. ## export here so the 'console' command recursively called from
  424. ## 'start' command does not have to parse the configs again
  425. export EMQX_BOOT_CONFIGS
  426. fi
  427. else
  428. # For non-boot commands, we try to get data_dir and ssl_dist_optfile from 'ps -ef' output
  429. # shellcheck disable=SC2009
  430. PS_LINE="$(ps -ef | grep "\-[r]oot $RUNNER_ROOT_DIR" || true)"
  431. if [ "$(echo -e "$PS_LINE" | wc -l)" -eq 1 ]; then
  432. ## only one emqx node is running
  433. ## strip 'emqx_data_dir ' and ' --' because the dir in between may contain spaces
  434. DATA_DIR="$(echo -e "$PS_LINE" | grep -oE "\-emqx_data_dir.*" | sed -E 's#.+emqx_data_dir[[:blank:]]##g' | sed -E 's#[[:blank:]]--$##g' || true)"
  435. if [ "$DATA_DIR" = '' ]; then
  436. ## this should not happen unless -emqx_data_dir is not set
  437. die "node_is_not_running!" 1
  438. fi
  439. # get ssl_dist_optfile option
  440. SSL_DIST_OPTFILE="$(echo -e "$PS_LINE" | grep -oE '\-ssl_dist_optfile\s.+\s' | awk '{print $2}' || true)"
  441. if [ -z "$SSL_DIST_OPTFILE" ]; then
  442. EMQX_BOOT_CONFIGS="node.data_dir=${DATA_DIR}\ncluster.proto_dist=inet_tcp"
  443. else
  444. EMQX_BOOT_CONFIGS="node.data_dir=${DATA_DIR}\ncluster.proto_dist=inet_tls"
  445. fi
  446. else
  447. ## None or more than one node is running, resolve from boot config
  448. EMQX_BOOT_CONFIGS="$(call_hocon -s "$SCHEMA_MOD" -c "$EMQX_ETC_DIR"/emqx.conf multi_get "${CONF_KEYS[@]}")"
  449. fi
  450. fi
  451. get_boot_config() {
  452. path_to_value="$1"
  453. echo -e "$EMQX_BOOT_CONFIGS" | grep "$path_to_value=" | sed -e "s/$path_to_value=//g" | tr -d \"
  454. }
  455. EPMD_ARGS="-start_epmd false -epmd_module ekka_epmd -proto_dist ekka"
  456. PROTO_DIST="$(get_boot_config 'cluster.proto_dist' || true)"
  457. # this environment variable is required by ekka_dist module
  458. # because proto_dist is overriden to ekka, and there is a lack of ekka_tls module
  459. export EKKA_PROTO_DIST_MOD="${PROTO_DIST:-inet_tcp}"
  460. if [ "$EKKA_PROTO_DIST_MOD" = 'inet_tls' ]; then
  461. if [ "$IS_BOOT_COMMAND" = 'yes' ]; then
  462. SSL_DIST_OPTFILE=${EMQX_SSL_DIST_OPTFILE:-"$EMQX_ETC_DIR/ssl_dist.conf"}
  463. case "$SSL_DIST_OPTFILE" in
  464. *\ *)
  465. # there is unfortunately no way to support space for this option because we'd need to grep
  466. # from 'ps -ef' result to get this option for non-boot commands (nodetool) to run
  467. set +x
  468. logerr "Got space in: $SSL_DIST_OPTFILE"
  469. logerr "No space is allowed for Erlang distribution over SSL option file path."
  470. logerr "Configure it from environment variable EMQX_SSL_DIST_OPTFILE."
  471. logerr "Or make sure emqx root path '$RUNNER_ROOT_DIR' has no space"
  472. exit 1
  473. ;;
  474. *)
  475. true
  476. ;;
  477. esac
  478. fi
  479. EPMD_ARGS="${EPMD_ARGS} -ssl_dist_optfile $SSL_DIST_OPTFILE"
  480. fi
  481. DATA_DIR="$(get_boot_config 'node.data_dir')"
  482. # ensure no trailing /
  483. DATA_DIR="${DATA_DIR%/}"
  484. if [[ $DATA_DIR != /* ]]; then
  485. # relative path
  486. DATA_DIR="${RUNNER_ROOT_DIR}/${DATA_DIR}"
  487. fi
  488. CONFIGS_DIR="$DATA_DIR/configs"
  489. mkdir -p "$CONFIGS_DIR"
  490. check_license() {
  491. if [ "$IS_ENTERPRISE" == "no" ]; then
  492. return 0
  493. fi
  494. key_license="${EMQX_LICENSE__KEY:-$(get_boot_config 'license.key')}"
  495. if [[ -n "$key_license" && ("$key_license" != "undefined") ]]; then
  496. call_nodetool check_license_key "$key_license"
  497. else
  498. set +x
  499. logerr "License not found."
  500. logerr "Please specify one via the EMQX_LICENSE__KEY variable"
  501. logerr "or via license.key in emqx-enterprise.conf."
  502. return 1
  503. fi
  504. }
  505. # When deciding which install upgrade script to run, we have to check
  506. # our own version so we may avoid infinite loops and call the correct
  507. # version.
  508. current_script_version() {
  509. curr_script=$(basename "${BASH_SOURCE[0]}")
  510. suffix=${curr_script#*-}
  511. if [[ "${suffix}" == "${curr_script}" ]]; then
  512. # there's no suffix, so we're running the default `emqx` script;
  513. # we'll have to trust the REL_VSN variable
  514. echo "$REL_VSN"
  515. else
  516. echo "${suffix}"
  517. fi
  518. }
  519. parse_semver() {
  520. echo "$1" | tr '.|-' ' '
  521. }
  522. max_version_of() {
  523. local vsn1="$1"
  524. local vsn2="$2"
  525. echo "${vsn1}" "${vsn2}" | tr " " "\n" | sort -rV | head -n1
  526. }
  527. versioned_script_path() {
  528. local script_name="$1"
  529. local vsn="$2"
  530. echo "$RUNNER_ROOT_DIR/bin/$script_name-$vsn"
  531. }
  532. does_script_version_exist() {
  533. local script_name="$1"
  534. local vsn="$2"
  535. if [[ -f "$(versioned_script_path "$script_name" "$vsn")" ]]; then
  536. return 0
  537. else
  538. return 1
  539. fi
  540. }
  541. # extract_from_package packege_path destination file1 file2
  542. extract_from_package() {
  543. local package="$1"
  544. local dest_dir="$2"
  545. shift 2
  546. tar -C "$dest_dir" -xf "$package" "$@"
  547. }
  548. am_i_the_newest_script() {
  549. local curr_vsn other_vsn
  550. curr_vsn="$(current_script_version)"
  551. other_vsn="$1"
  552. max_vsn="$(max_version_of "$other_vsn" "$curr_vsn")"
  553. if [[ "$max_vsn" == "$curr_vsn" ]]; then
  554. return 0
  555. else
  556. return 1
  557. fi
  558. }
  559. locate_package() {
  560. local package_path candidates vsn
  561. vsn="$1"
  562. if [[ "${IS_ENTERPRISE}" == "yes" ]]; then
  563. package_pattern="$RUNNER_ROOT_DIR/releases/emqx-enterprise-$vsn-*.tar.gz"
  564. else
  565. package_pattern="$RUNNER_ROOT_DIR/releases/emqx-$vsn-*.tar.gz"
  566. fi
  567. # shellcheck disable=SC2207,SC2086
  568. candidates=($(ls $package_pattern))
  569. if [[ "${#candidates[@]}" == 0 ]]; then
  570. logerr "No package matching $package_pattern found."
  571. exit 1
  572. elif [[ "${#candidates[@]}" -gt 1 ]]; then
  573. logerr "Multiple packages matching $package_pattern found. Ensure only one exists."
  574. exit 1
  575. else
  576. echo "${candidates[0]}"
  577. fi
  578. }
  579. ensure_newest_script_is_extracted() {
  580. local newest_vsn="$1"
  581. local package_path tmpdir
  582. if does_script_version_exist "emqx" "$newest_vsn" \
  583. && does_script_version_exist "install_upgrade.escript" "$newest_vsn"; then
  584. return
  585. else
  586. package_path="$(locate_package "$newest_vsn")"
  587. tmpdir="$(mktemp -dp /tmp emqx.XXXXXXXXXXX)"
  588. extract_from_package \
  589. "$package_path" \
  590. "$tmpdir" \
  591. "bin/emqx-$newest_vsn" \
  592. "bin/install_upgrade.escript-$newest_vsn"
  593. cp "$tmpdir/bin/emqx-$newest_vsn" \
  594. "$tmpdir/bin/install_upgrade.escript-$newest_vsn" \
  595. "$RUNNER_ROOT_DIR/bin/"
  596. rm -rf "$tmpdir"
  597. fi
  598. }
  599. # Run an escript in the node's environment
  600. relx_escript() {
  601. shift; scriptpath="$1"; shift
  602. "$ERTS_DIR/bin/escript" "$RUNNER_ROOT_DIR/$scriptpath" "$@"
  603. }
  604. # Output a start command for the last argument of run_erl
  605. relx_start_command() {
  606. printf "exec \"%s\" \"%s\"" "$RUNNER_SCRIPT" \
  607. "$START_OPTION"
  608. }
  609. # Function to check configs without generating them
  610. check_config() {
  611. ## this command checks the configs without generating any files
  612. call_hocon -v -s "$SCHEMA_MOD" -c "$EMQX_ETC_DIR"/emqx.conf check_schema
  613. }
  614. # Function to generate app.config and vm.args
  615. # sets two environment variables CONF_FILE and ARGS_FILE
  616. generate_config() {
  617. local name_type="$1"
  618. local node_name="$2"
  619. ## Delete the *.siz files first or it can't start after
  620. ## changing the config 'log.rotation.size'
  621. rm -rf "${RUNNER_LOG_DIR}"/*.siz
  622. ## timestamp for each generation
  623. local NOW_TIME
  624. NOW_TIME="$(date +'%Y.%m.%d.%H.%M.%S')"
  625. ## this command populates two files: app.<time>.config and vm.<time>.args
  626. ## NOTE: the generate command merges environment variables to the base config (emqx.conf),
  627. ## but does not include the cluster-override.conf and local-override.conf
  628. ## meaning, certain overrides will not be mapped to app.<time>.config file
  629. call_hocon -v -t "$NOW_TIME" -s "$SCHEMA_MOD" -c "$EMQX_ETC_DIR"/emqx.conf -d "$DATA_DIR"/configs generate
  630. ## filenames are per-hocon convention
  631. CONF_FILE="$CONFIGS_DIR/app.$NOW_TIME.config"
  632. ARGS_FILE="$CONFIGS_DIR/vm.$NOW_TIME.args"
  633. ## Merge hocon generated *.args into the vm.args
  634. TMP_ARG_FILE="$CONFIGS_DIR/vm.args.tmp"
  635. cp "$EMQX_ETC_DIR/vm.args" "$TMP_ARG_FILE"
  636. echo "" >> "$TMP_ARG_FILE"
  637. echo "-pa \"${REL_DIR}/consolidated\"" >> "$TMP_ARG_FILE"
  638. ## read lines from generated vm.<time>.args file
  639. ## drop comment lines, and empty lines using sed
  640. ## pipe the lines to a while loop
  641. sed '/^#/d' "$ARGS_FILE" | sed '/^$/d' | while IFS='' read -r ARG_LINE || [ -n "$ARG_LINE" ]; do
  642. ## in the loop, split the 'key[:space:]value' pair
  643. ARG_KEY=$(echo "$ARG_LINE" | awk '{$NF="";print}')
  644. ARG_VALUE=$(echo "$ARG_LINE" | awk '{print $NF}')
  645. ## use the key to look up in vm.args file for the value
  646. TMP_ARG_VALUE=$(grep "^$ARG_KEY" "$TMP_ARG_FILE" || true | awk '{print $NF}')
  647. ## compare generated (to override) value to original (to be overridden) value
  648. if [ "$ARG_VALUE" != "$TMP_ARG_VALUE" ] ; then
  649. ## if they are different
  650. if [ -n "$TMP_ARG_VALUE" ]; then
  651. ## if the old value is present, replace it with generated value
  652. sh -c "$SED_REPLACE 's|^$ARG_KEY.*$|$ARG_LINE|' \"$TMP_ARG_FILE\""
  653. else
  654. ## otherwise append generated value to the end
  655. echo "$ARG_LINE" >> "$TMP_ARG_FILE"
  656. fi
  657. fi
  658. done
  659. echo "$name_type $node_name" >> "$TMP_ARG_FILE"
  660. echo "-mnesia dir '\"$DATA_DIR/mnesia/$NAME\"'" >> "$TMP_ARG_FILE"
  661. ## rename the generated vm.<time>.args file
  662. mv -f "$TMP_ARG_FILE" "$ARGS_FILE"
  663. }
  664. # check if a PID is down
  665. is_down() {
  666. PID="$1"
  667. if ps -p "$PID" >/dev/null; then
  668. # still around
  669. # shellcheck disable=SC2009 # this grep pattern is not a part of the progra names
  670. if ps -p "$PID" | grep -q 'defunct'; then
  671. # zombie state, print parent pid
  672. parent="$(ps -o ppid= -p "$PID" | tr -d ' ')"
  673. logwarn "$PID is marked <defunct>, parent: $(ps -p "$parent")"
  674. return 0
  675. fi
  676. return 1
  677. fi
  678. # it's gone
  679. return 0
  680. }
  681. wait_for() {
  682. local WAIT_TIME
  683. local CMD
  684. WAIT_TIME="$1"
  685. shift
  686. CMD="$*"
  687. while true; do
  688. if $CMD >/dev/null 2>&1; then
  689. return 0
  690. fi
  691. if [ "$WAIT_TIME" -le 0 ]; then
  692. return 1
  693. fi
  694. WAIT_TIME=$((WAIT_TIME - 1))
  695. sleep 1
  696. done
  697. }
  698. wait_until_return_val() {
  699. local RESULT
  700. local WAIT_TIME
  701. local CMD
  702. RESULT="$1"
  703. WAIT_TIME="$2"
  704. shift 2
  705. CMD="$*"
  706. while true; do
  707. if [ "$($CMD 2>/dev/null)" = "$RESULT" ]; then
  708. return 0
  709. fi
  710. if [ "$WAIT_TIME" -le 0 ]; then
  711. return 1
  712. fi
  713. WAIT_TIME=$((WAIT_TIME - 1))
  714. sleep 1
  715. done
  716. }
  717. latest_vm_args() {
  718. local hint_var_name="$1"
  719. local vm_args_file
  720. vm_args_file="$(find "$CONFIGS_DIR" -type f -name "vm.*.args" | sort | tail -1)"
  721. if [ -f "$vm_args_file" ]; then
  722. echo "$vm_args_file"
  723. else
  724. set +x
  725. logerr "Node not initialized?"
  726. logerr "Generated config file vm.*.args is not found for command '$COMMAND'"
  727. logerr "in config dir: $CONFIGS_DIR"
  728. logerr "In case the file has been deleted while the node is running,"
  729. logerr "set environment variable '$hint_var_name' to continue"
  730. exit 1
  731. fi
  732. }
  733. # backward compatible with 4.x
  734. tr_log_to_env() {
  735. local log_to=${EMQX_LOG__TO:-undefined}
  736. # unset because it's unknown to 5.0
  737. unset EMQX_LOG__TO
  738. case "${log_to}" in
  739. console)
  740. export EMQX_LOG__CONSOLE_HANDLER__ENABLE='true'
  741. export EMQX_LOG__FILE_HANDLERS__DEFAULT__ENABLE='false'
  742. ;;
  743. file)
  744. export EMQX_LOG__CONSOLE_HANDLER__ENABLE='false'
  745. export EMQX_LOG__FILE_HANDLERS__DEFAULT__ENABLE='true'
  746. ;;
  747. both)
  748. export EMQX_LOG__CONSOLE_HANDLER__ENABLE='true'
  749. export EMQX_LOG__FILE_HANDLERS__DEFAULT__ENABLE='true'
  750. ;;
  751. default)
  752. # want to use config file defaults, do nothing
  753. ;;
  754. undefined)
  755. # value not set, do nothing
  756. ;;
  757. *)
  758. logerr "Unknown environment value for EMQX_LOG__TO=${log_to} discarded"
  759. ;;
  760. esac
  761. }
  762. maybe_log_to_console() {
  763. if [ "${EMQX_LOG__TO:-}" = 'default' ]; then
  764. # want to use config file defaults, do nothing
  765. unset EMQX_LOG__TO
  766. else
  767. tr_log_to_env
  768. # ensure defaults
  769. export EMQX_LOG__CONSOLE_HANDLER__ENABLE="${EMQX_LOG__CONSOLE_HANDLER__ENABLE:-true}"
  770. export EMQX_LOG__FILE_HANDLERS__DEFAULT__ENABLE="${EMQX_LOG__FILE_HANDLERS__DEFAULT__ENABLE:-false}"
  771. fi
  772. }
  773. if [ -n "${EMQX_NODE_NAME:-}" ]; then
  774. export EMQX_NODE__NAME="${EMQX_NODE_NAME}"
  775. unset EMQX_NODE_NAME
  776. fi
  777. ## Possible ways to configure emqx node name:
  778. ## 1. configure node.name in emqx.conf
  779. ## 2. override with environment variable EMQX_NODE__NAME
  780. ## Node name is either short-name (without '@'), e.g. 'emqx'
  781. ## or long name (with '@') e.g. 'emqx@example.net' or 'emqx@127.0.0.1'
  782. NAME="${EMQX_NODE__NAME:-}"
  783. if [ -z "$NAME" ]; then
  784. if [ "$IS_BOOT_COMMAND" = 'yes' ]; then
  785. # for boot commands, inspect emqx.conf for node name
  786. NAME="$(get_boot_config 'node.name')"
  787. else
  788. vm_args_file="$(latest_vm_args 'EMQX_NODE__NAME')"
  789. NAME="$(grep -E '^-s?name' "${vm_args_file}" | awk '{print $2}')"
  790. fi
  791. fi
  792. # force to use 'emqx' short name
  793. [ -z "$NAME" ] && NAME='emqx'
  794. case "$NAME" in
  795. *@*)
  796. NAME_TYPE='-name'
  797. ;;
  798. *)
  799. NAME_TYPE='-sname'
  800. esac
  801. SHORT_NAME="$(echo "$NAME" | awk -F'@' '{print $1}')"
  802. if ! (echo "$SHORT_NAME" | grep -q '^[0-9A-Za-z_\-]\+$'); then
  803. echo "Invalid node name, should be of format '^[0-9A-Za-z_-]+$'."
  804. exit 1
  805. fi
  806. # This also changes the program name from 'beam.smp' to node name
  807. # e.g. the 'ps' command output
  808. export ESCRIPT_NAME="$SHORT_NAME"
  809. PIPE_DIR="${PIPE_DIR:-/$DATA_DIR/${WHOAMI}_erl_pipes/$NAME/}"
  810. ## Resolve Erlang cookie.
  811. if [ -n "${EMQX_NODE_COOKIE:-}" ]; then
  812. ## To be backward compatible, read EMQX_NODE_COOKIE
  813. export EMQX_NODE__COOKIE="${EMQX_NODE_COOKIE}"
  814. unset EMQX_NODE_COOKIE
  815. fi
  816. COOKIE="${EMQX_NODE__COOKIE:-}"
  817. if [ -z "$COOKIE" ]; then
  818. if [ "$IS_BOOT_COMMAND" = 'yes' ]; then
  819. COOKIE="$(get_boot_config 'node.cookie')"
  820. else
  821. vm_args_file="$(latest_vm_args 'EMQX_NODE__COOKIE')"
  822. COOKIE="$(grep -E '^-setcookie' "${vm_args_file}" | awk '{print $2}')"
  823. fi
  824. fi
  825. [ -z "$COOKIE" ] && COOKIE="$EMQX_DEFAULT_ERLANG_COOKIE"
  826. if [ $IS_BOOT_COMMAND = 'yes' ] && [ "$COOKIE" = "$EMQX_DEFAULT_ERLANG_COOKIE" ]; then
  827. logwarn "Default (insecure) Erlang cookie is in use."
  828. logwarn "Configure node.cookie in $EMQX_ETC_DIR/emqx.conf or override from environment variable EMQX_NODE__COOKIE"
  829. logwarn "Use the same config value for all nodes in the cluster."
  830. fi
  831. ## check if OTP version has mnesia_hook feature; if not, fallback to
  832. ## using Mnesia DB backend.
  833. if [[ "$IS_BOOT_COMMAND" == 'yes' && "$(get_boot_config 'node.db_backend')" == "rlog" ]]; then
  834. if ! (echo -e "$COMPATIBILITY_INFO" | grep -q 'MNESIA_OK'); then
  835. logerr "DB Backend is RLOG, but an incompatible OTP version has been detected. Falling back to using Mnesia DB backend."
  836. export EMQX_NODE__DB_BACKEND=mnesia
  837. export EMQX_NODE__DB_ROLE=core
  838. fi
  839. fi
  840. cd "$RUNNER_ROOT_DIR"
  841. case "${COMMAND}" in
  842. start)
  843. # Make sure a node IS not running
  844. if relx_nodetool "ping" >/dev/null 2>&1; then
  845. die "node_is_already_running!"
  846. fi
  847. # this flag passes down to console mode
  848. # so we know it's intended to be run in daemon mode
  849. export _EMQX_START_DAEMON_MODE=1
  850. case "$COMMAND" in
  851. start)
  852. shift
  853. START_OPTION="console"
  854. HEART_OPTION="start"
  855. ;;
  856. esac
  857. RUN_PARAM="$*"
  858. # Set arguments for the heart command
  859. set -- "$RUNNER_SCRIPT" "$HEART_OPTION"
  860. [ "$RUN_PARAM" ] && set -- "$@" "$RUN_PARAM"
  861. # Export the HEART_COMMAND
  862. HEART_COMMAND="$RUNNER_SCRIPT $COMMAND"
  863. export HEART_COMMAND
  864. ## See: http://erlang.org/doc/man/run_erl.html
  865. # Export the RUN_ERL_LOG_GENERATIONS
  866. export RUN_ERL_LOG_GENERATIONS=${RUN_ERL_LOG_GENERATIONS:-"5"}
  867. # Export the RUN_ERL_LOG_MAXSIZE
  868. export RUN_ERL_LOG_MAXSIZE=${RUN_ERL_LOG_MAXSIZE:-"10485760"}
  869. mkdir -p "$PIPE_DIR"
  870. "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \
  871. "$(relx_start_command)"
  872. WAIT_TIME=${EMQX_WAIT_FOR_START:-120}
  873. if wait_until_return_val "true" "$WAIT_TIME" 'relx_nodetool' \
  874. 'eval' 'emqx:is_running()'; then
  875. echo "$EMQX_DESCRIPTION $REL_VSN is started successfully!"
  876. exit 0
  877. else
  878. echo "$EMQX_DESCRIPTION $REL_VSN failed to start in ${WAIT_TIME} seconds."
  879. echo "Please find more information in erlang.log.N"
  880. echo "Or run 'env DEBUG=1 $0 console' to have logs printed to console."
  881. exit 1
  882. fi
  883. ;;
  884. stop)
  885. # Wait for the node to completely stop...
  886. PID="$(relx_get_pid)"
  887. if ! relx_nodetool "stop"; then
  888. die "Graceful shutdown failed PID=[$PID]"
  889. fi
  890. WAIT_TIME="${EMQX_WAIT_FOR_STOP:-120}"
  891. if ! wait_for "$WAIT_TIME" 'is_down' "$PID"; then
  892. msg="dangling after ${WAIT_TIME} seconds"
  893. # also log to syslog
  894. logger -t "${REL_NAME}[${PID}]" "STOP: $msg"
  895. # log to user console
  896. set +x
  897. logerr "Stop failed, $msg"
  898. echo "ERROR: $PID is still around"
  899. ps -p "$PID"
  900. exit 1
  901. fi
  902. echo "ok"
  903. logger -t "${REL_NAME}[${PID}]" "STOP: OK"
  904. ;;
  905. pid)
  906. ## Get the VM's pid
  907. if ! relx_get_pid; then
  908. exit 1
  909. fi
  910. ;;
  911. ping)
  912. assert_node_alive
  913. echo pong
  914. ;;
  915. escript)
  916. ## Run an escript under the node's environment
  917. if ! relx_escript "$@"; then
  918. exit 1
  919. fi
  920. ;;
  921. attach)
  922. assert_node_alive
  923. shift
  924. exec "$BINDIR/to_erl" "$PIPE_DIR"
  925. ;;
  926. remote_console)
  927. assert_node_alive
  928. shift
  929. remsh
  930. ;;
  931. upgrade|downgrade|install|unpack|uninstall)
  932. if [ -z "${2:-}" ]; then
  933. echo "Missing version argument"
  934. echo "Usage: $REL_NAME $COMMAND {version}"
  935. exit 1
  936. fi
  937. shift
  938. assert_node_alive
  939. curr_vsn="$(current_script_version)"
  940. target_vsn="$1"
  941. newest_vsn="$(max_version_of "$target_vsn" "$curr_vsn")"
  942. ensure_newest_script_is_extracted "$newest_vsn"
  943. # if we are not the newest script, run the same command from it
  944. if ! am_i_the_newest_script "$newest_vsn"; then
  945. script_path="$(versioned_script_path emqx "$newest_vsn")"
  946. exec "$script_path" "$COMMAND" "$@"
  947. fi
  948. upgrade_script_path="$(versioned_script_path install_upgrade.escript "$newest_vsn")"
  949. echo "using ${upgrade_script_path} to run ${COMMAND} $*"
  950. ERL_FLAGS="${ERL_FLAGS:-} $EPMD_ARGS" \
  951. exec "$BINDIR/escript" "$upgrade_script_path" \
  952. "$COMMAND" "{'$REL_NAME', \"$NAME_TYPE\", '$NAME', '$COOKIE'}" "$@"
  953. ;;
  954. versions)
  955. assert_node_alive
  956. shift
  957. ERL_FLAGS="${ERL_FLAGS:-} $EPMD_ARGS" \
  958. exec "$BINDIR/escript" "$RUNNER_ROOT_DIR/bin/install_upgrade.escript" \
  959. "versions" "{'$REL_NAME', \"$NAME_TYPE\", '$NAME', '$COOKIE'}" "$@"
  960. ;;
  961. console|console_clean|foreground)
  962. # .boot file typically just $REL_NAME (ie, the app name)
  963. # however, for debugging, sometimes start_clean.boot is useful.
  964. # For e.g. 'setup', one may even want to name another boot script.
  965. case "$COMMAND" in
  966. console|foreground)
  967. if [ -f "$REL_DIR/$REL_NAME.boot" ]; then
  968. BOOTFILE="$REL_DIR/$REL_NAME"
  969. else
  970. BOOTFILE="$REL_DIR/start"
  971. fi
  972. ;;
  973. console_clean)
  974. BOOTFILE="$REL_DIR/start_clean"
  975. ;;
  976. esac
  977. case "$COMMAND" in
  978. foreground)
  979. FOREGROUNDOPTIONS="-noshell -noinput +Bd"
  980. ;;
  981. *)
  982. FOREGROUNDOPTIONS=''
  983. ;;
  984. esac
  985. # set before generate_config
  986. if [ "${_EMQX_START_DAEMON_MODE:-}" = 1 ]; then
  987. tr_log_to_env
  988. else
  989. maybe_log_to_console
  990. fi
  991. #generate app.config and vm.args
  992. generate_config "$NAME_TYPE" "$NAME"
  993. check_license
  994. # Setup beam-required vars
  995. EMU="beam"
  996. PROGNAME="${0}"
  997. export EMU
  998. export PROGNAME
  999. # Store passed arguments since they will be erased by `set`
  1000. # add emqx_data_dir to boot command so it is visible from 'ps -ef'
  1001. ARGS="$*"
  1002. # shellcheck disable=SC2086
  1003. # Build an array of arguments to pass to exec later on
  1004. # Build it here because this command will be used for logging.
  1005. if [ "$IS_ELIXIR" = no ] || [ "${EMQX_CONSOLE_FLAVOR:-}" = 'erl' ] ; then
  1006. # pass down RELEASE_LIB so we can switch to IS_ELIXIR=no
  1007. # to boot an Erlang node from the elixir release
  1008. set -- "$BINDIR/erlexec" \
  1009. $FOREGROUNDOPTIONS \
  1010. -boot "$BOOTFILE" \
  1011. -boot_var RELEASE_LIB "$ERTS_LIB_DIR" \
  1012. -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
  1013. -mode "$CODE_LOADING_MODE" \
  1014. -config "$CONF_FILE" \
  1015. -args_file "$ARGS_FILE" \
  1016. $EPMD_ARGS
  1017. else
  1018. set -- "$REL_DIR/iex" \
  1019. --boot "$BOOTFILE" \
  1020. --boot-var RELEASE_LIB "${ERTS_LIB_DIR}" \
  1021. --erl-config "${CONF_FILE}" \
  1022. --vm-args "${ARGS_FILE}" \
  1023. --erl "$FOREGROUNDOPTIONS" \
  1024. --erl "-mode $CODE_LOADING_MODE" \
  1025. --erl "$EPMD_ARGS" \
  1026. --werl
  1027. fi
  1028. # Log the startup
  1029. logger -t "${REL_NAME}[$$]" "EXEC: $* -- ${1+$ARGS} -emqx_data_dir ${DATA_DIR}"
  1030. # Start the VM
  1031. exec "$@" -- ${1+$ARGS} -emqx_data_dir "${DATA_DIR}"
  1032. ;;
  1033. ctl)
  1034. assert_node_alive
  1035. shift
  1036. relx_nodetool rpc_infinity emqx_ctl run_command "$@"
  1037. ;;
  1038. rpc)
  1039. assert_node_alive
  1040. shift
  1041. relx_nodetool rpc "$@"
  1042. ;;
  1043. rpcterms)
  1044. assert_node_alive
  1045. shift
  1046. relx_nodetool rpcterms "$@"
  1047. ;;
  1048. eval)
  1049. assert_node_alive
  1050. shift
  1051. if [ "$IS_ELIXIR" = "yes" ]
  1052. then
  1053. "$REL_DIR/elixir" \
  1054. --hidden \
  1055. --name "rand-$(relx_gen_id)-$NAME" \
  1056. --cookie "$COOKIE" \
  1057. --boot "$REL_DIR/start_clean" \
  1058. --boot-var RELEASE_LIB "$ERTS_LIB_DIR" \
  1059. --vm-args "$REL_DIR/remote.vm.args" \
  1060. --erl "-start_epmd false -epmd_module ekka_epmd" \
  1061. --rpc-eval "$NAME" "$@"
  1062. else
  1063. relx_nodetool "eval" "$@"
  1064. fi
  1065. ;;
  1066. eval-erl)
  1067. assert_node_alive
  1068. shift
  1069. relx_nodetool "eval" "$@"
  1070. ;;
  1071. check_config)
  1072. check_config
  1073. ;;
  1074. *)
  1075. usage "$COMMAND"
  1076. exit 1
  1077. ;;
  1078. esac
  1079. exit 0