diff --git a/.ci/build_packages/tests.sh b/.ci/build_packages/tests.sh index 8895ec3b3..a6bb1d765 100755 --- a/.ci/build_packages/tests.sh +++ b/.ci/build_packages/tests.sh @@ -7,7 +7,7 @@ export RELUP_PACKAGE_PATH="/emqx/relup_packages/${EMQX_NAME}" # export EMQX_NODE_COOKIE=$(date +%s%N) emqx_prepare(){ - mkdir -p ${PACKAGE_PATH} + mkdir -p "${PACKAGE_PATH}" if [ ! -d "/paho-mqtt-testing" ]; then git clone -b develop-4.0 https://github.com/emqx/paho.mqtt.testing.git /paho-mqtt-testing @@ -16,28 +16,28 @@ emqx_prepare(){ } emqx_test(){ - cd ${PACKAGE_PATH} + cd "${PACKAGE_PATH}" - for var in $(ls $PACKAGE_PATH/${EMQX_NAME}-*);do + for var in "$PACKAGE_PATH"/"${EMQX_NAME}"-*;do case ${var##*.} in "zip") - packagename=`basename ${PACKAGE_PATH}/${EMQX_NAME}-*.zip` - unzip -q ${PACKAGE_PATH}/$packagename - sed -i "/zone.external.server_keepalive/c zone.external.server_keepalive = 60" ${PACKAGE_PATH}/emqx/etc/emqx.conf - sed -i "/mqtt.max_topic_alias/c mqtt.max_topic_alias = 10" ${PACKAGE_PATH}/emqx/etc/emqx.conf - sed -i '/emqx_telemetry/d' ${PACKAGE_PATH}/emqx/data/loaded_plugins + packagename=$(basename "${PACKAGE_PATH}/${EMQX_NAME}"-*.zip) + unzip -q "${PACKAGE_PATH}/${packagename}" + sed -i "/zone.external.server_keepalive/c zone.external.server_keepalive = 60" "${PACKAGE_PATH}"/emqx/etc/emqx.conf + sed -i "/mqtt.max_topic_alias/c mqtt.max_topic_alias = 10" "${PACKAGE_PATH}"/emqx/etc/emqx.conf + sed -i '/emqx_telemetry/d' "${PACKAGE_PATH}"/emqx/data/loaded_plugins - if [ ! -z $(echo ${EMQX_DEPS_DEFAULT_VSN#v} | grep -oE "[0-9]+\.[0-9]+(\.[0-9]+)?-(alpha|beta|rc)\.[0-9]") ]; then - if [ ! -d ${PACKAGE_PATH}/emqx/lib/emqx-${EMQX_DEPS_DEFAULT_VSN#v} ] || [ ! -d ${PACKAGE_PATH}/emqx/releases/${EMQX_DEPS_DEFAULT_VSN#v} ] ;then + if echo "${EMQX_DEPS_DEFAULT_VSN#v}" | grep -qE "[0-9]+\.[0-9]+(\.[0-9]+)?-(alpha|beta|rc)\.[0-9]"; then + if [ ! -d "${PACKAGE_PATH}/emqx/lib/emqx-${EMQX_DEPS_DEFAULT_VSN#v}" ] || [ ! -d "${PACKAGE_PATH}/emqx/releases/${EMQX_DEPS_DEFAULT_VSN#v}" ] ;then echo "emqx zip version error" exit 1 fi fi echo "running ${packagename} start" - ${PACKAGE_PATH}/emqx/bin/emqx start || tail ${PACKAGE_PATH}/emqx/log/erlang.log.1 + "${PACKAGE_PATH}"/emqx/bin/emqx start || tail "${PACKAGE_PATH}"/emqx/log/erlang.log.1 IDLE_TIME=0 - while [ -z "$(${PACKAGE_PATH}/emqx/bin/emqx_ctl status |grep 'is running'|awk '{print $1}')" ] + while [ -z "$("${PACKAGE_PATH}"/emqx/bin/emqx_ctl status |grep 'is running'|awk '{print $1}')" ] do if [ $IDLE_TIME -gt 10 ] then @@ -48,54 +48,54 @@ emqx_test(){ IDLE_TIME=$((IDLE_TIME+1)) done pytest -v /paho-mqtt-testing/interoperability/test_client/V5/test_connect.py::test_basic - ${PACKAGE_PATH}/emqx/bin/emqx stop + "${PACKAGE_PATH}"/emqx/bin/emqx stop echo "running ${packagename} stop" - rm -rf ${PACKAGE_PATH}/emqx + rm -rf "${PACKAGE_PATH}"/emqx ;; "deb") - packagename=`basename ${PACKAGE_PATH}/${EMQX_NAME}-*.deb` - dpkg -i ${PACKAGE_PATH}/$packagename - if [ $(dpkg -l |grep emqx |awk '{print $1}') != "ii" ] + packagename=$(basename "${PACKAGE_PATH}/${EMQX_NAME}"-*.deb) + dpkg -i "${PACKAGE_PATH}/${packagename}" + if [ "$(dpkg -l |grep emqx |awk '{print $1}')" != "ii" ] then echo "package install error" exit 1 fi - + echo "running ${packagename} start" - running_test + running_test echo "running ${packagename} stop" - dpkg -r ${EMQX_NAME} - if [ $(dpkg -l |grep emqx |awk '{print $1}') != "rc" ] + dpkg -r "${EMQX_NAME}" + if [ "$(dpkg -l |grep emqx |awk '{print $1}')" != "rc" ] then echo "package remove error" exit 1 fi - dpkg -P ${EMQX_NAME} - if [ ! -z "$(dpkg -l |grep emqx)" ] + dpkg -P "${EMQX_NAME}" + if dpkg -l |grep -q emqx then echo "package uninstall error" exit 1 fi ;; "rpm") - packagename=`basename ${PACKAGE_PATH}/${EMQX_NAME}-*.rpm` - rpm -ivh ${PACKAGE_PATH}/$packagename - if [ -z $(rpm -q emqx | grep -o emqx) ];then + packagename=$(basename "${PACKAGE_PATH}/${EMQX_NAME}"-*.rpm) + rpm -ivh "${PACKAGE_PATH}/${packagename}" + if ! rpm -q emqx | grep -q emqx; then echo "package install error" exit 1 fi - + echo "running ${packagename} start" - running_test + running_test echo "running ${packagename} stop" - - rpm -e ${EMQX_NAME} + + rpm -e "${EMQX_NAME}" if [ "$(rpm -q emqx)" != "package emqx is not installed" ];then echo "package uninstall error" exit 1 - fi + fi ;; esac @@ -103,8 +103,8 @@ emqx_test(){ } running_test(){ - if [ ! -z $(echo ${EMQX_DEPS_DEFAULT_VSN#v} | grep -oE "[0-9]+\.[0-9]+(\.[0-9]+)?-(alpha|beta|rc)\.[0-9]") ]; then - if [ ! -d /usr/lib/emqx/lib/emqx-${EMQX_DEPS_DEFAULT_VSN#v} ] || [ ! -d /usr/lib/emqx/releases/${EMQX_DEPS_DEFAULT_VSN#v} ];then + if echo "${EMQX_DEPS_DEFAULT_VSN#v}" | grep -qE "[0-9]+\.[0-9]+(\.[0-9]+)?-(alpha|beta|rc)\.[0-9]"; then + if [ ! -d /usr/lib/emqx/lib/emqx-"${EMQX_DEPS_DEFAULT_VSN#v}" ] || [ ! -d /usr/lib/emqx/releases/"${EMQX_DEPS_DEFAULT_VSN#v}" ];then echo "emqx package version error" exit 1 fi @@ -127,11 +127,12 @@ running_test(){ IDLE_TIME=$((IDLE_TIME+1)) done pytest -v /paho-mqtt-testing/interoperability/test_client/V5/test_connect.py::test_basic - emqx stop || kill $(ps -ef | grep -E '\-progname\s.+emqx\s' |awk '{print $2}') + # shellcheck disable=SC2009 # pgrep does not support Extended Regular Expressions + emqx stop || kill "$(ps -ef | grep -E '\-progname\s.+emqx\s' |awk '{print $2}')" - if [ $(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g') = ubuntu ] \ - || [ $(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g') = debian ] \ - || [ $(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g') = raspbian ];then + if [ "$(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g')" = ubuntu ] \ + || [ "$(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g')" = debian ] \ + || [ "$(sed -n '/^ID=/p' /etc/os-release | sed -r 's/ID=(.*)/\1/g' | sed 's/"//g')" = raspbian ];then service emqx start || tail /var/log/emqx/erlang.log.1 IDLE_TIME=0 while [ -z "$(emqx_ctl status |grep 'is running'|awk '{print $1}')" ] @@ -149,18 +150,18 @@ running_test(){ } relup_test(){ - if [ -d ${RELUP_PACKAGE_PATH} ];then - cd ${RELUP_PACKAGE_PATH } + if [ -d "${RELUP_PACKAGE_PATH}" ];then + cd "${RELUP_PACKAGE_PATH }" - for var in $(ls ${EMQX_NAME}-*-$(uname -m).zip);do - packagename=`basename ${var}` - unzip $packagename + for var in "${EMQX_NAME}"-*-"$(uname -m)".zip;do + packagename=$(basename "${var}") + unzip "$packagename" ./emqx/bin/emqx start ./emqx/bin/emqx_ctl status ./emqx/bin/emqx versions - cp ${PACKAGE_PATH}/${EMQX_NAME}-*-${EMQX_DEPS_DEFAULT_VSN#v}-$(uname -m).zip ./emqx/releases - ./emqx/bin/emqx install ${EMQX_DEPS_DEFAULT_VSN#v} - [ $(./emqx/bin/emqx versions |grep permanent | grep -oE "[0-9].[0-9].[0-9]") = ${EMQX_DEPS_DEFAULT_VSN#v} ] || exit 1 + cp "${PACKAGE_PATH}/${EMQX_NAME}"-*-"${EMQX_DEPS_DEFAULT_VSN#v}-$(uname -m)".zip ./emqx/releases + ./emqx/bin/emqx install "${EMQX_DEPS_DEFAULT_VSN#v}" + [ "$(./emqx/bin/emqx versions |grep permanent | grep -oE "[0-9].[0-9].[0-9]")" = "${EMQX_DEPS_DEFAULT_VSN#v}" ] || exit 1 ./emqx/bin/emqx_ctl status ./emqx/bin/emqx stop rm -rf emqx diff --git a/.ci/compatibility_tests/redis/redis.sh b/.ci/compatibility_tests/redis/redis.sh index 7da422c21..a233659ee 100755 --- a/.ci/compatibility_tests/redis/redis.sh +++ b/.ci/compatibility_tests/redis/redis.sh @@ -31,7 +31,7 @@ rm -f \ /data/conf/nodes.7001.conf \ /data/conf/nodes.7002.conf ; -if [ ${node} = "cluster" ] ; then +if [ "${node}" = "cluster" ] ; then if $tls ; then redis-server /data/conf/redis-tls.conf --port 7000 --cluster-config-file /data/conf/nodes.7000.conf \ --tls-port 8000 --cluster-enabled yes ; @@ -44,7 +44,7 @@ if [ ${node} = "cluster" ] ; then redis-server /data/conf/redis.conf --port 7001 --cluster-config-file /data/conf/nodes.7001.conf --cluster-enabled yes; redis-server /data/conf/redis.conf --port 7002 --cluster-config-file /data/conf/nodes.7002.conf --cluster-enabled yes; fi -elif [ ${node} = "sentinel" ] ; then +elif [ "${node}" = "sentinel" ] ; then redis-server /data/conf/redis.conf --port 7000 --cluster-config-file /data/conf/nodes.7000.conf \ --cluster-enabled no; redis-server /data/conf/redis.conf --port 7001 --cluster-config-file /data/conf/nodes.7001.conf \ @@ -75,9 +75,9 @@ do else continue; fi - if [ ${node} = "cluster" ] ; then + if [ "${node}" = "cluster" ] ; then yes "yes" | redis-cli --cluster create 172.16.239.10:7000 172.16.239.10:7001 172.16.239.10:7002; - elif [ ${node} = "sentinel" ] ; then + elif [ "${node}" = "sentinel" ] ; then cp /data/conf/sentinel.conf /_sentinel.conf redis-server /_sentinel.conf --sentinel; fi diff --git a/.github/workflows/shellcheck.yaml b/.github/workflows/shellcheck.yaml new file mode 100644 index 000000000..2084f3863 --- /dev/null +++ b/.github/workflows/shellcheck.yaml @@ -0,0 +1,18 @@ +name: Shellcheck + +on: [pull_request] + +jobs: + shellcheck: + runs-on: ubuntu-20.04 + steps: + - name: Checkout source code + uses: actions/checkout@master + - name: Install shellcheck + run: | + sudo apt-get update + sudo apt install shellcheck + - name: Run shellcheck + run: | + ./scripts/shellcheck.sh + echo "success" diff --git a/bin/emqx b/bin/emqx index 4ee6a508d..025f08ce5 100755 --- a/bin/emqx +++ b/bin/emqx @@ -1,11 +1,12 @@ -#!/bin/sh +#!/bin/bash # -*- tab-width:4;indent-tabs-mode:nil -*- # ex: ts=4 sw=4 et set -e -ROOT_DIR="$(cd $(dirname $(readlink $0 || echo $0))/..; pwd -P)" -. $ROOT_DIR/releases/emqx_vars +ROOT_DIR="$(cd "$(dirname "$(readlink "$0" || echo "$0")")"/..; pwd -P)" +# shellcheck disable=SC1090 +. "$ROOT_DIR"/releases/emqx_vars RUNNER_SCRIPT="$RUNNER_BIN_DIR/$REL_NAME" CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}" @@ -79,7 +80,7 @@ relx_usage() { check_user() { # Validate that the user running the script is the owner of the # RUN_DIR. - if ([ "$RUNNER_USER" ] && [ "x$WHOAMI" != "x$RUNNER_USER" ]); then + if [ "$RUNNER_USER" ] && [ "x$WHOAMI" != "x$RUNNER_USER" ]; then if [ "x$WHOAMI" != "xroot" ]; then echo "You need to be root or use sudo to run this command" exit 1 @@ -90,13 +91,13 @@ check_user() { done # This will drop priviledges into the runner user # It exec's in a new shell and the current shell will exit - exec su - $RUNNER_USER -c "$CMD" + exec su - "$RUNNER_USER" -c "$CMD" fi } # Make sure the user running this script is the owner and/or su to that user -check_user $@ +check_user "$@" ES=$? if [ "$ES" -ne 0 ]; then exit $ES @@ -109,7 +110,7 @@ else fi # Warn the user if ulimit -n is less than 1024 -ULIMIT_F=`ulimit -n` +ULIMIT_F=$(ulimit -n) if [ "$ULIMIT_F" -lt 1024 ]; then echo "!!!!" echo "!!!! WARNING: ulimit -n is ${ULIMIT_F}; 1024 is the recommended minimum." @@ -133,6 +134,7 @@ esac relx_get_pid() { if output="$(relx_nodetool rpcterms os getpid)" then + # shellcheck disable=SC2001 # Escaped quote taken as closing quote in editor echo "$output" | sed -e 's/"//g' return 0 else @@ -143,7 +145,7 @@ relx_get_pid() { relx_get_nodename() { id="longname$(relx_gen_id)-${NAME}" - "$BINDIR/erl" -boot "$REL_DIR/start_clean" -eval '[Host] = tl(string:tokens(atom_to_list(node()),"@")), io:format("~s~n", [Host]), halt()' -noshell ${NAME_TYPE} $id + "$BINDIR/erl" -boot "$REL_DIR/start_clean" -eval '[Host] = tl(string:tokens(atom_to_list(node()),"@")), io:format("~s~n", [Host]), halt()' -noshell "${NAME_TYPE}" "$id" } # Connect to a remote node @@ -154,10 +156,11 @@ relx_rem_sh() { # Get the node's ticktime so that we use the same thing. TICKTIME="$(relx_nodetool rpcterms net_kernel get_net_ticktime)" + # shellcheck disable=SC2086 # $EPMD_ARG is supposed to be split by whitespace # Setup remote shell command to control node exec "$BINDIR/erl" "$NAME_TYPE" "$id" -remsh "$NAME" -boot "$REL_DIR/start_clean" \ -boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \ - -setcookie "$COOKIE" -hidden -kernel net_ticktime $TICKTIME $EPMD_ARG + -setcookie "$COOKIE" -hidden -kernel net_ticktime "$TICKTIME" $EPMD_ARG } # Generate a random id @@ -171,7 +174,7 @@ relx_nodetool() { ERL_FLAGS="$ERL_FLAGS $EPMD_ARG" \ "$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \ - -setcookie "$COOKIE" "$command" $@ + -setcookie "$COOKIE" "$command" "$@" } # Run an escript in the node's environment @@ -179,7 +182,7 @@ relx_escript() { shift; scriptpath="$1"; shift export RUNNER_ROOT_DIR - "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" $@ + "$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" "$@" } # Output a start command for the last argument of run_erl @@ -202,19 +205,19 @@ generate_config() { # the vm, we need to pass it in twice. CONFIG_ARGS=" -config $RUNNER_ETC_DIR/app.config -args_file $RUNNER_ETC_DIR/vm.args -vm_args $RUNNER_ETC_DIR/vm.args " else - CONFIG_ARGS=`$ERTS_PATH/escript $RUNNER_ROOT_DIR/bin/cuttlefish -i $REL_DIR/emqx.schema -c $RUNNER_ETC_DIR/emqx.conf -d $RUNNER_DATA_DIR/configs generate` + CONFIG_ARGS=$("$ERTS_PATH"/escript "$RUNNER_ROOT_DIR"/bin/cuttlefish -i "$REL_DIR"/emqx.schema -c "$RUNNER_ETC_DIR"/emqx.conf -d "$RUNNER_DATA_DIR"/configs generate) ## Merge cuttlefish generated *.args into the vm.args - CUTTLE_GEN_ARG_FILE=`echo "$CONFIG_ARGS" | sed -n 's/^.*\(vm_args[[:space:]]\)//p' | awk '{print $1}'` + CUTTLE_GEN_ARG_FILE=$(echo "$CONFIG_ARGS" | sed -n 's/^.*\(vm_args[[:space:]]\)//p' | awk '{print $1}') TMP_ARG_FILE="$RUNNER_DATA_DIR/configs/vm.args.tmp" cp "$RUNNER_ETC_DIR/vm.args" "$TMP_ARG_FILE" echo "" >> "$TMP_ARG_FILE" - sed '/^#/d' $CUTTLE_GEN_ARG_FILE | sed '/^$/d' | while IFS='' read -r ARG_LINE || [ -n "$ARG_LINE" ]; do - ARG_KEY=`echo "$ARG_LINE" | awk '{$NF="";print}'` - ARG_VALUE=`echo "$ARG_LINE" | awk '{print $NF}'` - TMP_ARG_VALUE=`grep "^$ARG_KEY" "$TMP_ARG_FILE" | awk '{print $NF}'` + sed '/^#/d' "$CUTTLE_GEN_ARG_FILE" | sed '/^$/d' | while IFS='' read -r ARG_LINE || [ -n "$ARG_LINE" ]; do + ARG_KEY=$(echo "$ARG_LINE" | awk '{$NF="";print}') + ARG_VALUE=$(echo "$ARG_LINE" | awk '{print $NF}') + TMP_ARG_VALUE=$(grep "^$ARG_KEY" "$TMP_ARG_FILE" | awk '{print $NF}') if [ "$ARG_VALUE" != "$TMP_ARG_VALUE" ] ; then - if [ ! -z $TMP_ARG_VALUE ]; then + if [ -n "$TMP_ARG_VALUE" ]; then sh -c "$SED_REPLACE 's/^$ARG_KEY.*$/$ARG_LINE/' $TMP_ARG_FILE" else echo "$ARG_LINE" >> "$TMP_ARG_FILE" @@ -224,6 +227,7 @@ generate_config() { mv -f "$TMP_ARG_FILE" "$CUTTLE_GEN_ARG_FILE" fi + # shellcheck disable=SC2086 if ! relx_nodetool chkconfig $CONFIG_ARGS; then echoerr "Error reading $CONFIG_ARGS" exit 1 @@ -233,7 +237,7 @@ generate_config() { # Call bootstrapd for daemon commands like start/stop/console bootstrapd() { if [ -e "$RUNNER_DATA_DIR/.erlang.cookie" ]; then - chown $RUNNER_USER $RUNNER_DATA_DIR/.erlang.cookie + chown "$RUNNER_USER" "$RUNNER_DATA_DIR"/.erlang.cookie fi } @@ -249,8 +253,9 @@ fi if [ -z "$NAME_ARG" ]; then NODENAME="${EMQX_NODE_NAME:-}" # check if there is a node running, inspect its name - [ -z "$NODENAME" ] && NODENAME=`ps -ef | grep -E '\-progname\s.*emqx\s' | grep -o -E '\-name (\S*)' | awk '{print $2}'` - [ -z "$NODENAME" ] && NODENAME=`egrep '^[ \t]*node.name[ \t]*=[ \t]*' "$RUNNER_ETC_DIR/emqx.conf" 2> /dev/null | tail -1 | cut -d = -f 2-` + # shellcheck disable=SC2009 # pgrep does not support Extended Regular Expressions + [ -z "$NODENAME" ] && NODENAME=$(ps -ef | grep -E '\-progname\s.*emqx\s' | grep -o -E '\-name (\S*)' | awk '{print $2}') + [ -z "$NODENAME" ] && NODENAME=$(grep -E '^[ \t]*node.name[ \t]*=[ \t]*' "$RUNNER_ETC_DIR/emqx.conf" 2> /dev/null | tail -1 | cut -d = -f 2-) if [ -z "$NODENAME" ]; then echoerr "vm.args needs to have a -name parameter." echoerr " -sname is not supported." @@ -273,8 +278,9 @@ PIPE_DIR="${PIPE_DIR:-/$RUNNER_DATA_DIR/${WHOAMI}_erl_pipes/$NAME/}" if [ -z "$COOKIE_ARG" ]; then COOKIE="${EMQX_NODE_COOKIE:-}" # check if there is a node running, steal its cookie - [ -z "$COOKIE" ] && COOKIE=`ps -ef | grep -E '\-progname\s.*emqx\s' | grep -o -E '\-setcookie (\S*)' | awk '{print $2}'` - [ -z "$COOKIE" ] && COOKIE=`egrep '^[ \t]*node.cookie[ \t]*=[ \t]*' "$RUNNER_ETC_DIR/emqx.conf" 2> /dev/null | tail -1 | cut -d = -f 2-` + # shellcheck disable=SC2009 # pgrep does not support Extended Regular Expressions + [ -z "$COOKIE" ] && COOKIE=$(ps -ef | grep -E '\-progname\s.*emqx\s' | grep -o -E '\-setcookie (\S*)' | awk '{print $2}') + [ -z "$COOKIE" ] && COOKIE=$(grep -E '^[ \t]*node.cookie[ \t]*=[ \t]*' "$RUNNER_ETC_DIR/emqx.conf" 2> /dev/null | tail -1 | cut -d = -f 2-) if [ -z "$COOKIE" ]; then echoerr "vm.args needs to have a -setcookie parameter." echoerr "please check $RUNNER_ETC_DIR/emqx.conf" @@ -288,7 +294,7 @@ fi COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')" # Support for IPv6 Dist. See: https://github.com/emqtt/emqttd/issues/1460 -PROTO_DIST=`egrep '^[ \t]*cluster.proto_dist[ \t]*=[ \t]*' "$RUNNER_ETC_DIR/emqx.conf" 2> /dev/null | tail -1 | cut -d = -f 2-` +PROTO_DIST=$(grep -E '^[ \t]*cluster.proto_dist[ \t]*=[ \t]*' "$RUNNER_ETC_DIR/emqx.conf" 2> /dev/null | tail -1 | cut -d = -f 2-) if [ -z "$PROTO_DIST" ]; then PROTO_DIST_ARG="" else @@ -327,7 +333,7 @@ case "$1" in exit 1 fi # Bootstrap daemon command (check perms & drop to $RUNNER_USER) - bootstrapd $@ + bootstrapd # Save this for later. CMD=$1 @@ -343,7 +349,7 @@ case "$1" in HEART_OPTION="start_boot" ;; esac - RUN_PARAM="$@" + RUN_PARAM="$*" # Set arguments for the heart command set -- "$RUNNER_SCRIPT" "$HEART_OPTION" @@ -366,9 +372,9 @@ case "$1" in "$(relx_start_command)" WAIT_TIME=${WAIT_FOR_ERLANG:-15} - while [ $WAIT_TIME -gt 0 ]; do + while [ "$WAIT_TIME" -gt 0 ]; do if ! relx_nodetool "ping" >/dev/null 2>&1; then - WAIT_TIME=`expr $WAIT_TIME - 1` + WAIT_TIME=$((WAIT_TIME - 1)) sleep 1 continue fi @@ -390,14 +396,14 @@ case "$1" in if ! relx_nodetool "stop"; then exit 1 fi - while $(kill -s 0 "$PID" 2>/dev/null); do + while kill -s 0 "$PID" 2>/dev/null; do sleep 1 done ;; restart|reboot) - echo "$EMQX_DISCR $REL_VSN is stopped: $($RUNNER_BIN_DIR/emqx stop)" - $RUNNER_BIN_DIR/emqx start + echo "$EMQX_DISCR $REL_VSN is stopped: $("$RUNNER_BIN_DIR"/emqx stop)" + "$RUNNER_BIN_DIR"/emqx start ;; pid) @@ -416,7 +422,7 @@ case "$1" in escript) ## Run an escript under the node's environment - if ! relx_escript $@; then + if ! relx_escript "$@"; then exit 1 fi ;; @@ -429,7 +435,7 @@ case "$1" in fi # Bootstrap daemon command (check perms & drop to $RUNNER_USER) - bootstrapd $@ + bootstrapd shift exec "$BINDIR/to_erl" "$PIPE_DIR" @@ -443,7 +449,7 @@ case "$1" in fi # Bootstrap daemon command (check perms & drop to $RUNNER_USER) - bootstrapd $@ + bootstrapd shift relx_rem_sh @@ -485,7 +491,7 @@ case "$1" in console|console_clean|console_boot) # Bootstrap daemon command (check perms & drop to $RUNNER_USER) - bootstrapd $@ + bootstrapd # .boot file typically just $REL_NAME (ie, the app name) # however, for debugging, sometimes start_clean.boot is useful. @@ -519,8 +525,9 @@ case "$1" in export PROGNAME # Store passed arguments since they will be erased by `set` - ARGS="$@" + ARGS="$*" + # shellcheck disable=SC2086 # $RELX_CONFIG_PATH $CONFIG_ARGS $EPMD_ARG are supposed to be split by whitespace # Build an array of arguments to pass to exec later on # Build it here because this command will be used for logging. set -- "$BINDIR/erlexec" -boot "$BOOTFILE" -mode "$CODE_LOADING_MODE" \ @@ -529,12 +536,12 @@ case "$1" in $RELX_CONFIG_PATH $CONFIG_ARGS $EPMD_ARG # Dump environment info for logging purposes - echo "Exec: $@" -- ${1+$ARGS} + echo "Exec: $*" -- ${1+$ARGS} echo "Root: $ROOTDIR" # Log the startup echo "$RUNNER_ROOT_DIR" - logger -t "$REL_NAME[$$]" "Starting up" + logger -t "${REL_NAME[$$]}" "Starting up" # Start the VM exec "$@" -- ${1+$ARGS} @@ -542,7 +549,7 @@ case "$1" in foreground) # Bootstrap daemon command (check perms & drop to $RUNNER_USER) - bootstrapd $@ + bootstrapd # start up the release in the foreground for use by runit # or other supervision services @@ -560,8 +567,9 @@ case "$1" in export PROGNAME # Store passed arguments since they will be erased by `set` - ARGS="$@" + ARGS="$*" + # shellcheck disable=SC2086 # $RELX_CONFIG_PATH $CONFIG_ARGS $EPMD_ARG are supposed to be split by whitespace # Build an array of arguments to pass to exec later on # Build it here because this command will be used for logging. set -- "$BINDIR/erlexec" $FOREGROUNDOPTIONS \ @@ -571,14 +579,14 @@ case "$1" in $RELX_CONFIG_PATH $CONFIG_ARGS $EPMD_ARG # Dump environment info for logging purposes - echo "Exec: $@" -- ${1+$ARGS} + echo "Exec: $*" -- ${1+$ARGS} echo "Root: $ROOTDIR" # Start the VM exec "$@" -- ${1+$ARGS} ;; ertspath) - echo $ERTS_PATH + echo "$ERTS_PATH" ;; rpc) # Make sure a node IS running @@ -589,7 +597,7 @@ case "$1" in shift - relx_nodetool rpc $@ + relx_nodetool rpc "$@" ;; rpcterms) # Make sure a node IS running @@ -600,7 +608,7 @@ case "$1" in shift - relx_nodetool rpcterms $@ + relx_nodetool rpcterms "$@" ;; root_dir) # Make sure a node IS running @@ -620,10 +628,10 @@ case "$1" in fi shift - relx_nodetool "eval" $@ + relx_nodetool "eval" "$@" ;; *) - relx_usage $1 + relx_usage "$1" exit 1 ;; esac diff --git a/bin/emqx_ctl b/bin/emqx_ctl index 88e28e716..961832c86 100755 --- a/bin/emqx_ctl +++ b/bin/emqx_ctl @@ -4,8 +4,9 @@ set -e -ROOT_DIR="$(cd $(dirname $(readlink $0 || echo $0))/..; pwd -P)" -. $ROOT_DIR/releases/emqx_vars +ROOT_DIR="$(cd "$(dirname "$(readlink "$0" || echo "$0")")"/..; pwd -P)" +# shellcheck disable=SC1090 +. "$ROOT_DIR"/releases/emqx_vars # Echo to stderr on errors echoerr() { echo "$@" 1>&2; } @@ -18,7 +19,7 @@ fi relx_get_nodename() { id="longname$(relx_gen_id)-${NAME}" - "$BINDIR/erl" -boot start_clean -eval '[Host] = tl(string:tokens(atom_to_list(node()),"@")), io:format("~s~n", [Host]), halt()' -noshell ${NAME_TYPE} $id + "$BINDIR/erl" -boot start_clean -eval '[Host] = tl(string:tokens(atom_to_list(node()),"@")), io:format("~s~n", [Host]), halt()' -noshell "${NAME_TYPE}" "$id" } # Control a node @@ -34,8 +35,9 @@ relx_nodetool() { if [ -z "$NAME_ARG" ]; then NODENAME="${EMQX_NODE_NAME:-}" # check if there is a node running, inspect its name - [ -z "$NODENAME" ] && NODENAME=`ps -ef | grep -E '\progname\s.*emqx\s' | grep -o -E '\-name (\S*)' | awk '{print $2}'` - [ -z "$NODENAME" ] && NODENAME=`egrep '^[ \t]*node.name[ \t]*=[ \t]*' "$RUNNER_ETC_DIR/emqx.conf" 2> /dev/null | tail -1 | cut -d = -f 2-` + # shellcheck disable=SC2009 # pgrep does not support Extended Regular Expressions + [ -z "$NODENAME" ] && NODENAME=$(ps -ef | grep -E '\progname\s.*emqx\s' | grep -o -E '\-name (\S*)' | awk '{print $2}') + [ -z "$NODENAME" ] && NODENAME=$(grep -E '^[ \t]*node.name[ \t]*=[ \t]*' "$RUNNER_ETC_DIR/emqx.conf" 2> /dev/null | tail -1 | cut -d = -f 2-) if [ -z "$NODENAME" ]; then echoerr "vm.args needs to have a -name parameter." echoerr " -sname is not supported." @@ -54,8 +56,9 @@ NAME="$(echo "$NAME_ARG" | awk '{print $2}')" if [ -z "$COOKIE_ARG" ]; then COOKIE="${EMQX_NODE_COOKIE:-}" # check if there is a node running, steal its cookie - [ -z "$COOKIE" ] && COOKIE=`ps -ef | grep -E '\-progname\s.*emqx\s' | grep -o -E '\-setcookie (\S*)' | awk '{print $2}'` - [ -z "$COOKIE" ] && COOKIE=`egrep '^[ \t]*node.cookie[ \t]*=[ \t]*' "$RUNNER_ETC_DIR/emqx.conf" 2> /dev/null | tail -1 | cut -d = -f 2-` + # shellcheck disable=SC2009 # pgrep does not support Extended Regular Expressions + [ -z "$COOKIE" ] && COOKIE=$(ps -ef | grep -E '\-progname\s.*emqx\s' | grep -o -E '\-setcookie (\S*)' | awk '{print $2}') + [ -z "$COOKIE" ] && COOKIE=$(grep -E '^[ \t]*node.cookie[ \t]*=[ \t]*' "$RUNNER_ETC_DIR/emqx.conf" 2> /dev/null | tail -1 | cut -d = -f 2-) if [ -z "$COOKIE" ]; then echoerr "vm.args needs to have a -setcookie parameter." echoerr "please check $RUNNER_ETC_DIR/emqx.conf" @@ -69,7 +72,7 @@ fi COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')" # Support for IPv6 Dist. See: https://github.com/emqtt/emqttd/issues/1460 -PROTO_DIST=`egrep '^[ \t]*cluster.proto_dist[ \t]*=[ \t]*' $RUNNER_ETC_DIR/emqx.conf 2> /dev/null | tail -1 | cut -d = -f 2-` +PROTO_DIST=$(grep -E '^[ \t]*cluster.proto_dist[ \t]*=[ \t]*' "$RUNNER_ETC_DIR"/emqx.conf 2> /dev/null | tail -1 | cut -d = -f 2-) if [ -z "$PROTO_DIST" ]; then PROTO_DIST_ARG="" else diff --git a/build b/build index 43ce7b898..03b734fc8 100755 --- a/build +++ b/build @@ -71,7 +71,8 @@ make_relup() { local releases=() if [ -d "$releases_dir" ]; then while read -r dir; do - local version="$(basename "$dir")" + local version + version="$(basename "$dir")" # skip current version if [ "$version" != "$PKG_VSN" ]; then releases+=( "$version" ) diff --git a/deploy/docker/docker-entrypoint.sh b/deploy/docker/docker-entrypoint.sh index dbd117962..2934f3a78 100755 --- a/deploy/docker/docker-entrypoint.sh +++ b/deploy/docker/docker-entrypoint.sh @@ -128,9 +128,9 @@ try_fill_config() { if grep -qE "^[#[:space:]]*$escaped_key\s*=" "$file"; then echo_value "$key" "$value" if [[ -z "$value" ]]; then - echo "$(sed -r "s/^[#[:space:]]*($escaped_key)\s*=\s*(.*)/# \1 = \2/" "$file")" > "$file" + sed -r "s/^[#[:space:]]*($escaped_key)\s*=\s*(.*)/# \1 = \2/" "$file" > tmpfile && cat tmpfile > "$file" else - echo "$(sed -r "s/^[#[:space:]]*($escaped_key)\s*=\s*(.*)/\1 = $escaped_value/" "$file")" > "$file" + sed -r "s/^[#[:space:]]*($escaped_key)\s*=\s*(.*)/\1 = $escaped_value/" "$file" > tmpfile && cat tmpfile > "$file" fi # Check if config has a numbering system, but no existing configuration line in file elif echo "$key" | grep -qE '\.\d+|\d+\.'; then @@ -139,7 +139,7 @@ try_fill_config() { template="$(echo "$escaped_key" | sed -r -e 's/\\\.[0-9]+/\\.[0-9]+/g' -e 's/[0-9]+\\\./[0-9]+\\./g')" if grep -qE "^[#[:space:]]*$template\s*=" "$file"; then echo_value "$key" "$value" - echo "$(sed '$a'\\ "$file")" > "$file" + sed '$a'\\ "$file" > tmpfile && cat tmpfile > "$file" echo "$key = $value" >> "$file" fi fi @@ -171,12 +171,12 @@ fill_tuples() { local elements=${*:2} for var in $elements; do if grep -qE "\{\s*$var\s*,\s*(true|false)\s*\}\s*\." "$file"; then - echo "$(sed -r "s/\{\s*($var)\s*,\s*(true|false)\s*\}\s*\./{\1, true}./1" "$file")" > "$file" + sed -r "s/\{\s*($var)\s*,\s*(true|false)\s*\}\s*\./{\1, true}./1" "$file" > tmpfile && mv tmpfile "$file" elif grep -q "$var\s*\." "$file"; then # backward compatible. - echo "$(sed -r "s/($var)\s*\./{\1, true}./1" "$file")" > "$file" + sed -r "s/($var)\s*\./{\1, true}./1" "$file" > tmpfile && cat tmpfile > "$file" else - echo "$(sed '$a'\\ "$file")" > "$file" + sed '$a'\\ "$file" > tmpfile && cat tmpfile > "$file" echo "{$var, true}." >> "$file" fi done diff --git a/deploy/packages/rpm/init.script b/deploy/packages/rpm/init.script index bdca033a2..bb30ee685 100755 --- a/deploy/packages/rpm/init.script +++ b/deploy/packages/rpm/init.script @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # # emqx # @@ -8,11 +8,11 @@ # # Source function library. +# shellcheck disable=SC1091 . /etc/rc.d/init.d/functions RETVAL=0 PATH=/sbin:/usr/sbin:/bin:/usr/bin -DESC="EMQX, a distributed, massively scalable, highly extensible MQTT message broker written in Erlang/OTP" NAME=emqx DAEMON=/usr/bin/$NAME lockfile=/var/lock/subsys/$NAME @@ -25,6 +25,7 @@ pidfile=/var/run/$NAME/$NAME.pid [ -d /var/lib/$NAME ] || exit 0 # Read configuration variable file if it is present and readable +# shellcheck disable=SC1090 [ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME # `service` strips all environmental VARS so @@ -34,15 +35,17 @@ if [ -z "$HOME" ]; then export HOME= fi -status -p $pidfile -l $(basename $lockfile) $NAME >/dev/null 2>&1 +status -p $pidfile -l "$(basename $lockfile)" $NAME >/dev/null 2>&1 running=$? find_pid() { + # shellcheck disable=SC2009 # pgrep does not support Extended Regular Expressions ps ax | grep -E "\-progname\s+$NAME\s" | awk '{print $1}' } check_pid_status() { - local pid="$(find_pid)" + local pid + pid="$(find_pid)" if [ "$pid" = "" ]; then # prog not running? return 1 @@ -72,7 +75,7 @@ stop() { # Stop daemon. echo -n $"Shutting down emqx: " $DAEMON stop 2>/dev/null - for n in $(seq 1 10); do + for _ in $(seq 1 10); do sleep 1 check_pid_status RETVAL=$? @@ -93,7 +96,7 @@ stop() { hardstop() { echo -n $"Shutting down $NAME: " su - emqx -c "ps -ef | grep -E '\-progname\s+$NAME\s' | awk '{print \$2}' | xargs kill -9" - for n in $(seq 1 10); do + for _ in $(seq 1 10); do sleep 1 check_pid_status RETVAL=$? @@ -133,7 +136,7 @@ case "$1" in restart ;; status) - status -p $pidfile -l $(basename $lockfile) $NAME + status -p $pidfile -l "$(basename $lockfile)" $NAME ;; ping) $DAEMON ping || exit $? diff --git a/scripts/shellcheck.sh b/scripts/shellcheck.sh new file mode 100755 index 000000000..eebf9049a --- /dev/null +++ b/scripts/shellcheck.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -euo pipefail + +target_files=() +while IFS='' read -r line; do target_files+=("$line"); done < <(grep -r -l --exclude-dir=.git --exclude-dir=_build "#!/bin/" .) +return_code=0 +for i in "${target_files[@]}"; do + echo checking "$i" ... + if ! shellcheck "$i"; then + return_code=1 + fi +done + +exit $return_code diff --git a/scripts/start-two-nodes-in-docker.sh b/scripts/start-two-nodes-in-docker.sh index 7f870e974..45307ba7f 100755 --- a/scripts/start-two-nodes-in-docker.sh +++ b/scripts/start-two-nodes-in-docker.sh @@ -39,8 +39,8 @@ docker run -d -t --restart=always --name "$NODE1" \ -e EMQX_NODE_COOKIE="$COOKIE" \ -e WAIT_FOR_ERLANG=60 \ -p 18083:18083 \ - -v $PROJ_DIR/_build/emqx/rel/emqx:/built \ - $IMAGE sh -c 'cp -r /built /emqx && /emqx/bin/emqx console' + -v "$PROJ_DIR"/_build/emqx/rel/emqx:/built \ + "$IMAGE" sh -c 'cp -r /built /emqx && /emqx/bin/emqx console' docker run -d -t --restart=always --name "$NODE2" \ --net "$NET" \ @@ -48,8 +48,8 @@ docker run -d -t --restart=always --name "$NODE2" \ -e EMQX_NODE_COOKIE="$COOKIE" \ -e WAIT_FOR_ERLANG=60 \ -p 18084:18083 \ - -v $PROJ_DIR/_build/emqx/rel/emqx:/built \ - $IMAGE sh -c 'cp -r /built /emqx && /emqx/bin/emqx console' + -v "$PROJ_DIR"/_build/emqx/rel/emqx:/built \ + "$IMAGE" sh -c 'cp -r /built /emqx && /emqx/bin/emqx console' wait (){ container="$1" diff --git a/sync-apps.sh b/sync-apps.sh index ab4a75004..3f66de06e 100755 --- a/sync-apps.sh +++ b/sync-apps.sh @@ -43,12 +43,14 @@ mkdir -p tmp/ download_zip() { local app="$1" local ref="$2" - local vsn="$(echo "$ref" | tr '/' '-')" + local vsn + vsn="$(echo "$ref" | tr '/' '-')" local file="tmp/${app}-${vsn}.zip" if [ -f "$file" ] && [ "$force" != "force" ]; then return 0 fi - local repo="$(echo "$app" | sed 's#_#-#g')" + local repo + repo=${app//_/-} local url="https://github.com/emqx/$repo/archive/$ref.zip" echo "downloading ${url}" curl -fLsS -o "$file" "$url" @@ -56,7 +58,7 @@ download_zip() { default_vsn="dev/v4.3.0" download_zip "emqx_auth_mnesia" "e4.2.3" -for app in ${apps[@]}; do +for app in "${apps[@]}"; do download_zip "$app" "$default_vsn" done @@ -64,7 +66,8 @@ extract_zip(){ local app="$1" local ref="$2" local vsn_arg="${3:-}" - local vsn_dft="$(echo "$ref" | tr '/' '-')" + local vsn_dft + vsn_dft="$(echo "$ref" | tr '/' '-')" local vsn if [ -n "$vsn_arg" ]; then vsn="$vsn_arg" @@ -72,14 +75,15 @@ extract_zip(){ vsn="$vsn_dft" fi local file="tmp/${app}-${vsn_dft}.zip" - local repo="$(echo "$app" | sed 's#_#-#g')" + local repo + repo=${app//_/-} rm -rf "apps/${app}/" unzip "$file" -d apps/ mv "apps/${repo}-${vsn}/" "apps/$app/" } extract_zip "emqx_auth_mnesia" "e4.2.3" "e4.2.3" -for app in ${apps[@]}; do +for app in "${apps[@]}"; do extract_zip "$app" "$default_vsn" done @@ -95,6 +99,6 @@ cleanup_app(){ } apps+=( "emqx_auth_mnesia" ) -for app in ${apps[@]}; do - cleanup_app $app +for app in "${apps[@]}"; do + cleanup_app "$app" done