#!/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_longname() { id="longname$(relx_gen_id)-${NAME}" "$BINDIR/erl" -boot start_clean -eval 'io:format("~s~n", [node()]), halt()' -noshell -name $id | sed -e 's/.*@//g' } # 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" -kernel net_ticktime $TICKTIME } # Generate a random id relx_gen_id() { od -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 if [ $RELX_REPLACE_OS_VARS ]; then awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < $VMARGS_PATH > $VMARGS_PATH.2.config VMARGS_PATH=$VMARGS_PATH.2.config 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 if [ $RELX_REPLACE_OS_VARS ]; then awk '{while(match($0,"[$]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH -3);gsub("[$]{"var"}",ENVIRON[var])}}1' < $RELX_CONFIG_PATH > $RELX_CONFIG_PATH.2.config RELX_CONFIG_PATH=$RELX_CONFIG_PATH.2.config 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)" if [ -z "$COOKIE_ARG" ]; then echo "vm.args needs to have a -setcookie parameter." exit 1 fi # Extract cookie name from COOKIE_ARG COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')" 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 ;; *) # Add @hostname case $NAME_TYPE in -sname) NAME=$NAME@`hostname -s` ;; -name) NAME=$NAME@$(relx_get_longname) ;; esac ;; 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" "$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" "$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" \ -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 $@ ;; *) echo "Usage: $REL_NAME {start|start_boot |foreground|stop|restart|reboot|pid|ping|console|console_clean|console_boot |attach|remote_console|upgrade|escript|rpc|rpcterms}" exit 1 ;; esac exit 0