mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-06-01 19:43:03 +08:00
515 lines
14 KiB
Bash
Executable File
515 lines
14 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
exit_error() {
|
|
printf "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
if [ $(echo ${BASH_VERSION} | cut -d '.' -f 1) -lt 4 ]; then
|
|
exit_error "This script requires Bash 4.x or higher"
|
|
fi
|
|
|
|
log() {
|
|
printf "%s" "$1" >&2
|
|
}
|
|
|
|
logln() {
|
|
printf "%s\n" "$1" >&2
|
|
}
|
|
|
|
is_sourced() {
|
|
if [[ "${BASH_SOURCE[0]}" != "${0}" ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
start_bitcoind() {
|
|
logln "[$$] Setting up bitcoind..."
|
|
test -f "$BITCOIN_CONF".in || exit_error "No such file or directory: $BITCOIN_CONF"
|
|
mkdir -p "$BITCOIN_DATA_DIR" || exit_error "Failed to create bitcoin data directory $BITCOIN_DATA_DIR"
|
|
|
|
log "[$$] Generating bitcoind config file..."
|
|
sed -r \
|
|
-e "s!@@BITCOIN_DATA_DIR@@!$BITCOIN_DATA_DIR!g" \
|
|
"$BITCOIN_CONF".in \
|
|
> "$BITCOIN_CONF"
|
|
logln "ok"
|
|
|
|
log "[$$] Starting bitcoind..."
|
|
bitcoind -fallbackfee=0.0002 -conf="$BITCOIN_CONF" >"$BITCOIN_LOGFILE" 2>&1 &
|
|
local BITCOIN_PID=$!
|
|
logln "PID $BITCOIN_PID"
|
|
|
|
while true; do
|
|
bitcoin-cli -regtest -conf="$BITCOIN_CONF" ping >/dev/null 2>&1
|
|
if [ $? -eq 0 ]; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
echo "$BITCOIN_PID"
|
|
return 0
|
|
}
|
|
|
|
start_bitcoind_controller() {
|
|
logln "[$$] Setting up bitcoind controller..."
|
|
test -f "$BITCOIN_CONTROLLER_CONF".in || exit_error "No such file or directory: $BITCOIN_CONTROLLER_CONF.in"
|
|
|
|
log "[$$] Generating bitcoind controller config file..."
|
|
local NOW="$(date +%s)"
|
|
sed -r \
|
|
-e "s/@@BITCOIN_NEON_CONTROLLER_GENESIS_TIMESTAMP@@/$NOW/g" \
|
|
"$BITCOIN_CONTROLLER_CONF".in \
|
|
> "$BITCOIN_CONTROLLER_CONF"
|
|
logln "ok"
|
|
|
|
log "[$$] Starting bitcoind controller..."
|
|
puppet-chain "$BITCOIN_CONTROLLER_CONF" >"$BITCOIN_CONTROLLER_LOGFILE" 2>&1 &
|
|
local RC=$?
|
|
local BITCOIN_NEON_CONTROLLER_PID=$!
|
|
|
|
if [ $RC -ne 0 ]; then
|
|
logln "FAILED"
|
|
return 1
|
|
fi
|
|
|
|
logln "PID $BITCOIN_NEON_CONTROLLER_PID"
|
|
|
|
echo "$BITCOIN_NEON_CONTROLLER_PID"
|
|
return 0
|
|
}
|
|
|
|
start_faucet() {
|
|
logln "[$$] Setting up bitcoin faucet..."
|
|
test -f "$BITCOIN_CONTROLLER_CONF" || exit_error "No such file or directory: $BITCOIN_CONTROLLER_CONF"
|
|
test -f "$BITCOIN_CONF" || exit_error "No such file or directory: $BITCOIN_CONF"
|
|
|
|
local PRIVKEY="$(cat "$BITCOIN_CONTROLLER_CONF" | grep "# WIF" | cut -d ' ' -f 3)"
|
|
|
|
log "[$$] Importing faucet private key..."
|
|
bitcoin-cli -conf="$BITCOIN_CONF" importprivkey "$PRIVKEY" >"$FAUCET_LOGFILE" 2>&1
|
|
local RC=$?
|
|
if [ $RC -ne 0 ]; then
|
|
logln "FAILED!"
|
|
return 1
|
|
fi
|
|
logln "ok"
|
|
|
|
local MINER_ADDR="$(cat "$BITCOIN_CONTROLLER_CONF" | grep "miner_address =" | cut -d ' ' -f 3 | sed -r 's/"//g')"
|
|
|
|
log "[$$] Importing miner address $MINER_ADDR..."
|
|
|
|
echo "Try importing miner address $MINER_ADDR" >>"$FAUCET_LOGFILE"
|
|
bitcoin-cli -conf="$BITCOIN_CONF" importaddress "$MINER_ADDR" >>"$FAUCET_LOGFILE" 2>&1
|
|
RC=$?
|
|
if [ $RC -ne 0 ]; then
|
|
logln "FAILED!"
|
|
return 1
|
|
fi
|
|
|
|
logln "ok"
|
|
|
|
log "[$$] Starting bitcoin faucet..."
|
|
faucet.sh "$FAUCET_PORT" "$BITCOIN_CONF" "$STACKS_MASTER_CHAINSTATE_DIR" >"$FAUCET_LOGFILE" 2>&1 &
|
|
local FAUCET_PID=$!
|
|
logln "PID $FAUCET_PID"
|
|
|
|
echo "$FAUCET_PID"
|
|
return 0
|
|
}
|
|
|
|
start_stacks_master_node() {
|
|
test -d "$STACKS_MASTER_CHAINSTATE_DIR" && exit_error "Cannot start Stacks master node: Directory exists: $STACKS_MASTER_CHAINSTATE_DIR"
|
|
|
|
logln "[$$] Setting up Stacks master node..."
|
|
test -f "$STACKS_MASTER_CONF_IN" || exit_error "No such file or directory: $STACKS_MASTER_CONF_IN"
|
|
|
|
log "[$$] Generating Stacks master config file..."
|
|
sed -r \
|
|
-e "s!@@STACKS_CHAINSTATE_DIR@@!$STACKS_MASTER_CHAINSTATE_DIR!g" \
|
|
-e "s!@@STACKS_PUBLIC_IP@@!$STACKS_MASTER_PUBLIC_IP!g" \
|
|
-e "s!@@STACKS_MASTER_IS_MINER@@!$STACKS_MASTER_IS_MINER!g" \
|
|
-e "s/@@PROCESS_EXIT_AT_BLOCK_HEIGHT@@/$PROCESS_EXIT_AT_BLOCK_HEIGHT/g" \
|
|
-e "s/@@STACKS_DENY_NODES@@/$STACKS_MASTER_DENY_NODES/g" \
|
|
-e "s/@@DISABLE_INBOUND_HANDSHAKES@@/$STACKS_MASTER_DISABLE_INBOUND_HANDSHAKES/g" \
|
|
-e "s/@@DISABLE_INBOUND_WALKS@@/$STACKS_MASTER_DISABLE_INBOUND_WALKS/g" \
|
|
-e "s/@@STACKS_MASTER_MINE_MICROBLOCKS@@/$STACKS_MASTER_MINE_MICROBLOCKS/g" \
|
|
-e "s/@@STACKS_MASTER_MICROBLOCK_FREQUENCY@@/$STACKS_MASTER_MICROBLOCK_FREQUENCY/g" \
|
|
-e "s/@@STACKS_MASTER_MAX_MICROBLOCKS@@/$STACKS_MASTER_MAX_MICROBLOCKS/g" \
|
|
-e "s/@@STACKS_MASTER_WAIT_FOR_MICROBLOCKS@@/$STACKS_MASTER_WAIT_FOR_MICROBLOCKS/g" \
|
|
"$STACKS_MASTER_CONF_IN" \
|
|
> "$STACKS_MASTER_CONF"
|
|
logln "ok"
|
|
|
|
log "[$$] Starting Stacks master node..."
|
|
BLOCKSTACK_DEBUG=1 RUST_BACKTRACE=full stacks-node start --config "$STACKS_MASTER_CONF" >"$STACKS_MASTER_LOGFILE" 2>&1 &
|
|
local STACKS_PID=$!
|
|
logln "PID $STACKS_PID"
|
|
|
|
echo "$STACKS_PID"
|
|
return 0
|
|
}
|
|
|
|
start_stacks_miner_node() {
|
|
test -d "$STACKS_MINER_CHAINSTATE_DIR" && exit_error "Cannot start Stacks miner node: Directory exists: $STACKS_MASTER_CHAINSTATE_DIR"
|
|
|
|
logln "[$$] Setting up Stacks miner node ($STACKS_MINER_CONF)..."
|
|
test -f "$STACKS_MINER_CONF_IN" || exit_error "No such file or directory: $STACKS_MINER_CONF_IN"
|
|
|
|
log "[$$] Generating Stacks miner config file..."
|
|
local STACKS_MINER_SEED="$(blockstack-cli generate-sk | jq -r '.secretKey')"
|
|
|
|
sed -r \
|
|
-e "s/@@STACKS_BOOTSTRAP_IP@@/$STACKS_MINER_BOOTSTRAP_IP/g" \
|
|
-e "s/@@STACKS_BOOTSTRAP_PORT@@/$STACKS_MINER_BOOTSTRAP_PORT/g" \
|
|
-e "s/@@STACKS_DENY_NODES@@/$STACKS_MINER_DENY_NODES/g" \
|
|
-e "s/@@BITCOIN_IP@@/$BITCOIN_PUBLIC_IP/g" \
|
|
-e "s!@@STACKS_PUBLIC_IP@@!$STACKS_MINER_PUBLIC_IP!g" \
|
|
-e "s/@@STACKS_MINER_SEED@@/$STACKS_MINER_SEED/g" \
|
|
-e "s!@@STACKS_CHAINSTATE_DIR@@!$STACKS_MINER_CHAINSTATE_DIR!g" \
|
|
-e "s!@@STACKS_MINER_P2P_PORT@@!$STACKS_MINER_P2P_PORT!g" \
|
|
-e "s!@@STACKS_MINER_RPC_PORT@@!$STACKS_MINER_RPC_PORT!g" \
|
|
-e "s/@@PROCESS_EXIT_AT_BLOCK_HEIGHT@@/$PROCESS_EXIT_AT_BLOCK_HEIGHT/g" \
|
|
-e "s/@@DISABLE_INBOUND_HANDSHAKES@@/$STACKS_MINER_DISABLE_INBOUND_HANDSHAKES/g" \
|
|
-e "s/@@DISABLE_INBOUND_WALKS@@/$STACKS_MINER_DISABLE_INBOUND_WALKS/g" \
|
|
-e "s/@@STACKS_MINER_MINE_MICROBLOCKS@@/$STACKS_MINER_MINE_MICROBLOCKS/g" \
|
|
-e "s/@@STACKS_MINER_MICROBLOCK_FREQUENCY@@/$STACKS_MINER_MICROBLOCK_FREQUENCY/g" \
|
|
-e "s/@@STACKS_MINER_MAX_MICROBLOCKS@@/$STACKS_MINER_MAX_MICROBLOCKS/g" \
|
|
-e "s/@@STACKS_MINER_WAIT_FOR_MICROBLOCKS@@/$STACKS_MINER_WAIT_FOR_MICROBLOCKS/g" \
|
|
"$STACKS_MINER_CONF_IN" \
|
|
> "$STACKS_MINER_CONF"
|
|
logln "ok"
|
|
|
|
local BTCADDR="$(blockstack-cli --testnet addresses "$STACKS_MINER_SEED" | jq -r '.BTC')"
|
|
local TIMEOUT=2
|
|
local TXID=""
|
|
local CONFIRMATIONS=0
|
|
|
|
log "[$$] Fetching BTC from the faucet for this miner ($BTCADDR)..."
|
|
for i in $(seq 1 10); do
|
|
TXID="$(curl -sf -X POST -d $BTCADDR$'\n' -H "content-type: text/plain" "$FAUCET_URL"/bitcoin/fund)"
|
|
local RC=$?
|
|
if [ $RC -ne 0 ]; then
|
|
# curl failed, or we didn't get 200. Try again
|
|
logln "FAILED! Try again in $TIMEOUT seconds..."
|
|
sleep $TIMEOUT
|
|
|
|
TIMEOUT=$((TIMEOUT + $RANDOM % TIMEOUT))
|
|
continue
|
|
fi
|
|
break
|
|
done
|
|
logln "txid $TXID"
|
|
|
|
log "[$$] Waiting for BTC to get confirmed..."
|
|
TIMEOUT=2
|
|
for i in $(seq 1 20); do
|
|
CONFIRMATIONS="$(curl -sf "$FAUCET_URL"/bitcoin/confirmations/"$TXID")"
|
|
local RC=$?
|
|
if [ $RC -ne 0 ]; then
|
|
logln "FAILED! Try again (attempt $i)..."
|
|
sleep $TIMEOUT
|
|
|
|
TIMEOUT=$((TIMEOUT + $RANDOM % TIMEOUT))
|
|
continue
|
|
fi
|
|
if (( $CONFIRMATIONS > 0 )); then
|
|
break
|
|
else
|
|
log ".wait $TIMEOUT."
|
|
sleep $TIMEOUT
|
|
|
|
TIMEOUT=$((TIMEOUT + $RANDOM % TIMEOUT))
|
|
fi
|
|
done
|
|
|
|
if (( $CONFIRMATIONS == 0 )); then
|
|
logln "FAILED"
|
|
return 1
|
|
fi
|
|
logln "ok"
|
|
|
|
log "[$$] Starting Stacks miner node..."
|
|
BLOCKSTACK_DEBUG=1 RUST_BACKTRACE=full stacks-node start --config "$STACKS_MINER_CONF" >"$STACKS_MINER_LOGFILE" 2>&1 &
|
|
local STACKS_PID=$!
|
|
logln "PID $STACKS_PID"
|
|
|
|
echo "$STACKS_PID"
|
|
return 0
|
|
}
|
|
|
|
start_stacks_follower_node() {
|
|
test -d "$STACKS_FOLLOWER_CHAINSTATE_DIR" && exit_error "Cannot start Stacks miner node: Directory exists: $STACKS_MASTER_CHAINSTATE_DIR"
|
|
|
|
logln "[$$] Setting up Stacks follower node ($STACKS_FOLLOWER_CONF)..."
|
|
test -f "$STACKS_FOLLOWER_CONF_IN" || exit_error "No such file or directory: $STACKS_FOLLOWER_CONF_IN"
|
|
|
|
log "[$$] Generating Stacks follower config file..."
|
|
sed -r \
|
|
-e "s/@@STACKS_BOOTSTRAP_IP@@/$STACKS_FOLLOWER_BOOTSTRAP_IP/g" \
|
|
-e "s/@@STACKS_BOOTSTRAP_PORT@@/$STACKS_FOLLOWER_BOOTSTRAP_PORT/g" \
|
|
-e "s/@@BITCOIN_IP@@/$BITCOIN_PUBLIC_IP/g" \
|
|
-e "s!@@STACKS_PUBLIC_IP@@!$STACKS_FOLLOWER_PUBLIC_IP!g" \
|
|
-e "s/@@STACKS_DENY_NODES@@/$STACKS_FOLLOWER_DENY_NODES/g" \
|
|
-e "s!@@STACKS_CHAINSTATE_DIR@@!$STACKS_FOLLOWER_CHAINSTATE_DIR!g" \
|
|
-e "s!@@STACKS_FOLLOWER_P2P_PORT@@!$STACKS_FOLLOWER_P2P_PORT!g" \
|
|
-e "s!@@STACKS_FOLLOWER_RPC_PORT@@!$STACKS_FOLLOWER_RPC_PORT!g" \
|
|
-e "s/@@PROCESS_EXIT_AT_BLOCK_HEIGHT@@/$PROCESS_EXIT_AT_BLOCK_HEIGHT/g" \
|
|
-e "s/@@DISABLE_INBOUND_HANDSHAKES@@/$STACKS_FOLLOWER_DISABLE_INBOUND_HANDSHAKES/g" \
|
|
-e "s/@@DISABLE_INBOUND_WALKS@@/$STACKS_FOLLOWER_DISABLE_INBOUND_WALKS/g" \
|
|
"$STACKS_FOLLOWER_CONF_IN" \
|
|
> "$STACKS_FOLLOWER_CONF"
|
|
logln "ok"
|
|
|
|
log "[$$] Starting Stacks follower node..."
|
|
BLOCKSTACK_DEBUG=1 RUST_BACKTRACE=full stacks-node start --config "$STACKS_FOLLOWER_CONF" >"$STACKS_FOLLOWER_LOGFILE" 2>&1 &
|
|
local STACKS_PID=$!
|
|
logln "PID $STACKS_PID"
|
|
|
|
echo "$STACKS_PID"
|
|
return 0
|
|
}
|
|
|
|
check_if_running() {
|
|
local PID=$1
|
|
if [ $PID -gt 0 ]; then
|
|
kill -s 0 "$PID" 2>/dev/null
|
|
return $?
|
|
else
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
kill_if_running() {
|
|
local SIG="$1"
|
|
local PID=$2
|
|
|
|
if [ $PID -gt 0 ]; then
|
|
check_if_running $PID
|
|
if [ $? -eq 0 ]; then
|
|
logln "[$$] Send $SIG to PID $PID"
|
|
kill -s "$SIG" "$PID" 2>/dev/null || true
|
|
fi
|
|
fi
|
|
}
|
|
|
|
BITCOIN_PID=0
|
|
BITCOIN_NEON_CONTROLLER_PID=0
|
|
STACKS_NODE_PID=0
|
|
FAUCET_PID=0
|
|
|
|
wait_pids() {
|
|
while true; do
|
|
wait
|
|
sleep 5
|
|
|
|
local ALL_GOOD=0
|
|
local PID=0
|
|
for PID in $@; do
|
|
check_if_running $PID
|
|
if [ $? -ne 0 ]; then
|
|
ALL_GOOD=1
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [ $ALL_GOOD -ne 0 ]; then
|
|
break
|
|
fi
|
|
done
|
|
return 0
|
|
}
|
|
|
|
shutdown() {
|
|
echo "[$$] Sending SIGTERM to all processes"
|
|
for PID in $BITCOIN_PID $BITCOIN_NEON_CONTROLLER_PID $STACKS_NODE_PID $FAUCET_PID; do
|
|
kill_if_running SIGTERM $PID
|
|
done
|
|
|
|
sleep 5
|
|
echo "[$$] Sending SIGKILL to all processes"
|
|
for PID in $BITCOIN_PID $BITCOIN_NEON_CONTROLLER_PID $STACKS_NODE_PID $FAUCET_PID; do
|
|
kill_if_running SIGKILL $PID
|
|
done
|
|
|
|
exit 0
|
|
}
|
|
|
|
usage() {
|
|
exit_error "Usage: $0 [master|miner|follower]"
|
|
}
|
|
|
|
start_node() {
|
|
local MODE="$CONFIG_MODE"
|
|
if [ $# -gt 0 ]; then
|
|
MODE="$1"
|
|
fi
|
|
if [ -z "$MODE" ]; then
|
|
usage
|
|
fi
|
|
|
|
set -uo pipefail
|
|
trap 'shutdown' INT
|
|
|
|
case "$MODE" in
|
|
master)
|
|
BITCOIN_PID="$(start_bitcoind)"
|
|
|
|
sleep 5
|
|
BITCOIN_NEON_CONTROLLER_PID="$(start_bitcoind_controller)"
|
|
|
|
sleep 5
|
|
FAUCET_PID="$(start_faucet)"
|
|
|
|
sleep 5
|
|
STACKS_NODE_PID="$(start_stacks_master_node)"
|
|
;;
|
|
|
|
miner)
|
|
STACKS_NODE_PID="$(start_stacks_miner_node)"
|
|
;;
|
|
|
|
follower)
|
|
STACKS_NODE_PID="$(start_stacks_follower_node)"
|
|
;;
|
|
*)
|
|
usage
|
|
;;
|
|
esac
|
|
|
|
echo >&2 "[$$] Running!"
|
|
|
|
# reap children
|
|
wait_pids $BITCOIN_PID $BITCOIN_NEON_CONTROLLER_PID $STACKS_NODE_PID $FAUCET_PID
|
|
|
|
echo >&2 "[$$] Shutting down"
|
|
shutdown
|
|
}
|
|
|
|
wait_node() {
|
|
local MODE="$CONFIG_MODE"
|
|
if [ $# -gt 0 ]; then
|
|
MODE="$1"
|
|
fi
|
|
local RPC_HOST=""
|
|
local RPC_PORT=""
|
|
case "$MODE" in
|
|
master)
|
|
RPC_HOST="$STACKS_MASTER_PUBLIC_IP"
|
|
RPC_PORT="20443"
|
|
;;
|
|
miner)
|
|
RPC_HOST="$STACKS_MINER_PUBLIC_IP"
|
|
RPC_PORT="$STACKS_MINER_RPC_PORT"
|
|
;;
|
|
follower)
|
|
RPC_HOST="$STACKS_FOLLOWER_PUBLIC_IP"
|
|
RPC_PORT="$STACKS_FOLLOWER_RPC_PORT"
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
local CNT=0
|
|
for CNT in $(seq 1 360); do
|
|
curl -sLf "http://$RPC_HOST:$RPC_PORT/v2/info" >/dev/null 2>&1
|
|
if [ $? -eq 0 ]; then
|
|
return 0
|
|
fi
|
|
sleep 5
|
|
done
|
|
return 1
|
|
}
|
|
|
|
wait_until_burn_block() {
|
|
local RPC_HOST=""
|
|
local RPC_PORT=""
|
|
local BURN_BLOCK_HEIGHT="$1"
|
|
local MODE="$CONFIG_MODE"
|
|
if [ $# -gt 1 ]; then
|
|
MODE="$2"
|
|
fi
|
|
|
|
case "$MODE" in
|
|
master)
|
|
RPC_HOST="$STACKS_MASTER_PUBLIC_IP"
|
|
RPC_PORT="20443"
|
|
;;
|
|
miner)
|
|
RPC_HOST="$STACKS_MINER_PUBLIC_IP"
|
|
RPC_PORT="$STACKS_MINER_RPC_PORT"
|
|
;;
|
|
follower)
|
|
RPC_HOST="$STACKS_FOLLOWER_PUBLIC_IP"
|
|
RPC_PORT="$STACKS_FOLLOWER_RPC_PORT"
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
local INFO_URL="http://$RPC_HOST:$RPC_PORT/v2/info"
|
|
while true; do
|
|
curl -sLf "$INFO_URL" >/dev/null 2>&1
|
|
if [ $? -ne 0 ]; then
|
|
sleep 5
|
|
continue
|
|
fi
|
|
|
|
local BURN_HEIGHT="$(curl -sLf "$INFO_URL" | jq -r '.burn_block_height')"
|
|
if (( $BURN_HEIGHT >= $BURN_BLOCK_HEIGHT )); then
|
|
break
|
|
fi
|
|
sleep 5
|
|
done
|
|
}
|
|
|
|
stop_node() {
|
|
local PID="$1"
|
|
kill -s SIGINT "$PID"
|
|
return $?
|
|
}
|
|
|
|
report() {
|
|
local REPORT_NAME="$1"
|
|
local CHAINSTATE_DIR=""
|
|
case "$CONFIG_MODE" in
|
|
master)
|
|
CHAINSTATE_DIR="$STACKS_MASTER_CHAINSTATE_DIR"
|
|
;;
|
|
miner)
|
|
CHAINSTATE_DIR="$STACKS_MINER_CHAINSTATE_DIR"
|
|
;;
|
|
follower)
|
|
CHAINSTATE_DIR="$STACKS_FOLLOWER_CHAINSTATE_DIR"
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
faucet.sh report "$BITCOIN_CONF" "$CHAINSTATE_DIR" "$REPORT_NAME"
|
|
}
|
|
|
|
is_sourced
|
|
if [ $? -ne 0 ]; then
|
|
for cmd in bitcoind bitcoin-cli puppet-chain stacks-node blockstack-cli date jq grep sed kill cat curl faucet.sh seq; do
|
|
which $cmd 2>&1 >/dev/null || exit_error "Missing \"$cmd\""
|
|
done
|
|
|
|
CONF_FILE=""
|
|
if [ -n "$CONFIG" ]; then
|
|
CONF_FILE="$CONFIG"
|
|
fi
|
|
if [ -z "$CONF_FILE" ]; then
|
|
exit_error "$CONFIG environ not set"
|
|
fi
|
|
if ! [ -f "$CONF_FILE" ]; then
|
|
exit_error "No such file or directory: $CONF_FILE"
|
|
fi
|
|
source "$CONF_FILE"
|
|
start_node $@
|
|
fi
|
|
|