#!/bin/sh
set -e
SCRIPT=$(readlink $0 || true)
if [ -z $SCRIPT ]; then
SCRIPT=$0
fi;
SCRIPT_DIR="$(cd `dirname "$SCRIPT"` && pwd -P)"
RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)"
REL_NAME="{{ rel_name }}"
REL_VSN="{{ rel_vsn }}"
ERTS_VSN="{{ erts_vsn }}"
CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN"
ERL_OPTS="{{ erl_opts }}"
RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_ROOT_DIR/log}"
find_erts_dir() {
__erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
if [ -d "$__erts_dir" ]; then
ERTS_DIR="$__erts_dir";
ROOTDIR="$RELEASE_ROOT_DIR"
else
__erl="$(which erl)"
code="io:format(\"~s\", [code:root_dir()]), halt()."
__erl_root="$("$__erl" -noshell -eval "$code")"
ERTS_DIR="$__erl_root/erts-$ERTS_VSN"
ROOTDIR="$__erl_root"
fi
}
# Get node pid
relx_get_pid() {
if output="$(relx_nodetool rpcterms os getpid)"
then
echo "$output" | sed -e 's/"//g'
return 0
else
echo "$output"
return 1
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
}
# Connect to a remote node
relx_rem_sh() {
# Generate a unique id used to allow multiple remsh to the same node
# transparently
id="remsh$(relx_gen_id)-${NAME}"
# Get the node's ticktime so that we use the same thing.
TICKTIME="$(relx_nodetool rpcterms net_kernel get_net_ticktime)"
# Setup remote shell command to control node
exec "$BINDIR/erl" "$NAME_TYPE" "$id" -remsh "$NAME" -boot start_clean \
-boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
-setcookie "$COOKIE" -hidden -kernel net_ticktime $TICKTIME
}
# Generate a random id
relx_gen_id() {
od -t x -N 4 /dev/urandom | head -n1 | awk '{print $2}'
}
# Control a node
relx_nodetool() {
command="$1"; shift
"$ERTS_DIR/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \
-setcookie "$COOKIE" "$command" $@
}
# Run an escript in the node's environment
relx_escript() {
shift; scriptpath="$1"; shift
export RELEASE_ROOT_DIR
"$ERTS_DIR/bin/escript" "$ROOTDIR/$scriptpath" $@
}
# Output a start command for the last argument of run_erl
relx_start_command() {
printf "exec \"%s\" \"%s\"" "$RELEASE_ROOT_DIR/bin/$REL_NAME" \
"$START_OPTION"
}
# Use $CWD/vm.args if exists, otherwise releases/VSN/vm.args
if [ -z "$VMARGS_PATH" ]; then
if [ -f "$RELEASE_ROOT_DIR/vm.args" ]; then
VMARGS_PATH="$RELEASE_ROOT_DIR/vm.args"
else
VMARGS_PATH="$REL_DIR/vm.args"
fi
fi
orig_vmargs_path="$VMARGS_PATH.orig"
if [ $RELX_REPLACE_OS_VARS ]; then
#Make sure we don't break dev mode by keeping the symbolic link to
#the user's vm.args
if [ ! -L "$orig_vmargs_path" ]; then
#we're in copy mode, rename the vm.args file to vm.args.orig
mv "$VMARGS_PATH" "$orig_vmargs_path"
fi
awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < "$orig_vmargs_path" > "$VMARGS_PATH"
else
#We don't need to replace env. vars, just rename the
#symlink vm.args.orig to vm.args, and keep it as a
#symlink.
if [ -L "$orig_vmargs_path" ]; then
mv "$orig_vmargs_path" "$VMARGS_PATH"
fi
fi
# Make sure log directory exists
mkdir -p "$RUNNER_LOG_DIR"
# Use $CWD/sys.config if exists, otherwise releases/VSN/sys.config
if [ -z "$RELX_CONFIG_PATH" ]; then
if [ -f "$RELEASE_ROOT_DIR/sys.config" ]; then
RELX_CONFIG_PATH="$RELEASE_ROOT_DIR/sys.config"
else
RELX_CONFIG_PATH="$REL_DIR/sys.config"
fi
fi
orig_relx_config_path="$RELX_CONFIG_PATH.orig"
if [ $RELX_REPLACE_OS_VARS ]; then
#Make sure we don't break dev mode by keeping the symbolic link to
#the user's sys.config
if [ ! -L "$orig_relx_config_path" ]; then
#We're in copy mode, rename sys.config to sys.config.orig
mv "$RELX_CONFIG_PATH" "$orig_relx_config_path"
fi
awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < "$orig_relx_config_path" > "$RELX_CONFIG_PATH"
else
#We don't need to replace env. vars, just rename the
#symlink sys.config.orig to sys.config. Keep it as
#a symlink.
if [ -L "$orig_relx_config_path" ]; then
mv "$orig_relx_config_path" "$RELX_CONFIG_PATH"
fi
fi
# Extract the target node name from node.args
NAME_ARG=$(egrep '^-s?name' "$VMARGS_PATH" || true)
if [ -z "$NAME_ARG" ]; then
echo "vm.args needs to have either -name or -sname parameter."
exit 1
fi
# Extract the name type and name from the NAME_ARG for REMSH
NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')"
NAME="$(echo "$NAME_ARG" | awk '{print $2}')"
PIPE_DIR="${PIPE_DIR:-/tmp/erl_pipes/$NAME/}"
# Extract the target cookie
COOKIE_ARG="$(grep '^-setcookie' "$VMARGS_PATH" || true)"
DEFAULT_COOKIE_FILE="$HOME/.erlang.cookie"
if [ -z "$COOKIE_ARG" ]; then
if [ -f "$DEFAULT_COOKIE_FILE" ]; then
COOKIE="$(cat $DEFAULT_COOKIE_FILE)"
else
echo "vm.args needs to have a -setcookie, or $DEFAULT_COOKIE_FILE (its permission must be 400) is required."
exit 1
fi
else
# Extract cookie name from COOKIE_ARG
COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')"
fi
find_erts_dir
export ROOTDIR="$RELEASE_ROOT_DIR"
export BINDIR="$ERTS_DIR/bin"
export EMU="beam"
export PROGNAME="erl"
export LD_LIBRARY_PATH="$ERTS_DIR/lib:$LD_LIBRARY_PATH"
ERTS_LIB_DIR="$ERTS_DIR/../lib"
cd "$ROOTDIR"
# User can specify an sname without @hostname
# This will fail when creating remote shell
# So here we check for @ and add @hostname if missing
case $NAME in
*@*)
# Nothing to do
;;
*)
NAME=$NAME@$(relx_get_nodename)
;;
esac
# Check the first argument for instructions
case "$1" in
start|start_boot)
# Make sure there is not already a node running
#RES=`$NODETOOL ping`
#if [ "$RES" = "pong" ]; then
# echo "Node is already running!"
# exit 1
#fi
# Save this for later.
CMD=$1
case "$1" in
start)
shift
START_OPTION="console"
HEART_OPTION="start"
;;
start_boot)
shift
START_OPTION="console_boot"
HEART_OPTION="start_boot"
;;
esac
RUN_PARAM="$@"
# Set arguments for the heart command
set -- "$SCRIPT_DIR/$REL_NAME" "$HEART_OPTION"
[ "$RUN_PARAM" ] && set -- "$@" "$RUN_PARAM"
# Export the HEART_COMMAND
HEART_COMMAND="$RELEASE_ROOT_DIR/bin/$REL_NAME $CMD"
export HEART_COMMAND
mkdir -p "$PIPE_DIR"
"$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \
"$(relx_start_command)"
;;
stop)
# Wait for the node to completely stop...
PID="$(relx_get_pid)"
if ! relx_nodetool "stop"; then
exit 1
fi
while $(kill -s 0 "$PID" 2>/dev/null);
do
sleep 1
done
;;
restart)
## Restart the VM without exiting the process
if ! relx_nodetool "restart"; then
exit 1
fi
;;
reboot)
## Restart the VM completely (uses heart to restart it)
if ! relx_nodetool "reboot"; then
exit 1
fi
;;
pid)
## Get the VM's pid
if ! relx_get_pid; then
exit 1
fi
;;
ping)
## See if the VM is alive
if ! relx_nodetool "ping"; then
exit 1
fi
;;
escript)
## Run an escript under the node's environment
if ! relx_escript $@; then
exit 1
fi
;;
attach)
# Make sure a node IS running
if ! relx_nodetool "ping" > /dev/null; then
echo "Node is not running!"
exit 1
fi
shift
exec "$BINDIR/to_erl" "$PIPE_DIR"
;;
remote_console)
# Make sure a node IS running
if ! relx_nodetool "ping" > /dev/null; then
echo "Node is not running!"
exit 1
fi
shift
relx_rem_sh
;;
upgrade|downgrade|install)
if [ -z "$2" ]; then
echo "Missing package argument"
echo "Usage: $REL_NAME $1 {package base name}"
echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
exit 1
fi
# Make sure a node IS running
if ! relx_nodetool "ping" > /dev/null; then
echo "Node is not running!"
exit 1
fi
exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \
"install" "$REL_NAME" "$NAME_TYPE" "$NAME" "$COOKIE" "$2"
;;
unpack)
if [ -z "$2" ]; then
echo "Missing package argument"
echo "Usage: $REL_NAME $1 {package base name}"
echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
exit 1
fi
# Make sure a node IS running
if ! relx_nodetool "ping" > /dev/null; then
echo "Node is not running!"
exit 1
fi
exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \
"unpack" "$REL_NAME" "$NAME_TYPE" "$NAME" "$COOKIE" "$2"
;;
console|console_clean|console_boot)
# .boot file typically just $REL_NAME (ie, the app name)
# however, for debugging, sometimes start_clean.boot is useful.
# For e.g. 'setup', one may even want to name another boot script.
case "$1" in
console)
if [ -f "$REL_DIR/$REL_NAME.boot" ]; then
BOOTFILE="$REL_DIR/$REL_NAME"
else
BOOTFILE="$REL_DIR/start"
fi
;;
console_clean)
BOOTFILE="$ROOTDIR/bin/start_clean"
;;
console_boot)
shift
BOOTFILE="$1"
shift
;;
esac
# Setup beam-required vars
EMU="beam"
PROGNAME="${0#*/}"
export EMU
export PROGNAME
# Store passed arguments since they will be erased by `set`
ARGS="$@"
# 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" \
-boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
-config "$RELX_CONFIG_PATH" \
-args_file "$VMARGS_PATH"
# Dump environment info for logging purposes
echo "Exec: $@" -- ${1+$ARGS}
echo "Root: $ROOTDIR"
# Log the startup
echo "$RELEASE_ROOT_DIR"
logger -t "$REL_NAME[$$]" "Starting up"
# Start the VM
exec "$@" -- ${1+$ARGS}
;;
foreground)
# start up the release in the foreground for use by runit
# or other supervision services
[ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start
FOREGROUNDOPTIONS="-noshell -noinput +Bd"
# Setup beam-required vars
EMU=beam
PROGNAME="${0#*/}"
export EMU
export PROGNAME
# Store passed arguments since they will be erased by `set`
ARGS="$@"
# 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 \
-boot "$REL_DIR/$BOOTFILE" -mode "$CODE_LOADING_MODE" -config "$RELX_CONFIG_PATH" \
-boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
-args_file "$VMARGS_PATH"
# Dump environment info for logging purposes
echo "Exec: $@" -- ${1+$ARGS}
echo "Root: $ROOTDIR"
# Start the VM
exec "$@" -- ${1+$ARGS}
;;
rpc)
# Make sure a node IS running
if ! relx_nodetool "ping" > /dev/null; then
echo "Node is not running!"
exit 1
fi
shift
relx_nodetool rpc $@
;;
rpcterms)
# Make sure a node IS running
if ! relx_nodetool "ping" > /dev/null; then
echo "Node is not running!"
exit 1
fi
shift
relx_nodetool rpcterms $@
;;
eval)
# Make sure a node IS running
if ! relx_nodetool "ping" > /dev/null; then
echo "Node is not running!"
exit 1
fi
shift
relx_nodetool "eval" $@
;;
*)
echo "Usage: $REL_NAME {start|start_boot <file>|foreground|stop|restart|reboot|pid|ping|console|console_clean|console_boot <file>|attach|remote_console|upgrade|downgrade|install|escript|rpc|rpcterms|eval}"
exit 1
;;
esac
exit 0