aboutsummaryrefslogtreecommitdiffstats
path: root/priv
diff options
context:
space:
mode:
Diffstat (limited to 'priv')
-rwxr-xr-xpriv/templates/bin2
-rw-r--r--priv/templates/bin_windows25
-rw-r--r--priv/templates/builtin_hook_pid12
-rw-r--r--priv/templates/builtin_hook_status3
-rw-r--r--priv/templates/builtin_hook_wait_for_process17
-rw-r--r--priv/templates/builtin_hook_wait_for_vm_start7
-rwxr-xr-xpriv/templates/extended_bin474
-rw-r--r--priv/templates/extended_bin_windows40
-rw-r--r--priv/templates/install_upgrade_escript298
-rw-r--r--priv/templates/vm_args11
10 files changed, 668 insertions, 221 deletions
diff --git a/priv/templates/bin b/priv/templates/bin
index 8bb6890..523b88d 100755
--- a/priv/templates/bin
+++ b/priv/templates/bin
@@ -22,7 +22,7 @@ find_erts_dir() {
else
__erl="$(which erl)"
code="io:format(\"~s\", [code:root_dir()]), halt()."
- __erl_root="$("$__erl" -noshell -eval "$code")"
+ __erl_root="$("$__erl" -boot no_dot_erlang -noshell -eval "$code")"
ERTS_DIR="$__erl_root/erts-$ERTS_VSN"
ROOTDIR="$__erl_root"
fi
diff --git a/priv/templates/bin_windows b/priv/templates/bin_windows
index 06303f5..b3ce796 100644
--- a/priv/templates/bin_windows
+++ b/priv/templates/bin_windows
@@ -11,14 +11,14 @@
@for %%A in ("%script_dir%\..") do (
set "release_root_dir=%%~fA"
)
-@set rel_dir=%release_root_dir%\releases\%rel_vsn%
+@set "rel_dir=%release_root_dir%\releases\%rel_vsn%"
@call :find_erts_dir
@call :find_sys_config
@call :set_boot_script_var
-@set rootdir=%release_root_dir%
-@set bindir=%erts_dir%\bin
+@set "rootdir=%release_root_dir%"
+@set "bindir=%erts_dir%\bin"
@set progname=erl
@set erl=%bindir%\erl
@@ -41,7 +41,7 @@ cd %rootdir%
:: Find the ERTS dir
:find_erts_dir
-@set erts_dir=%release_root_dir%\erts-%erts_vsn%
+@set "erts_dir=%release_root_dir%\erts-%erts_vsn%"
@if exist %erts_dir% (
goto :set_erts_dir_from_default
) else (
@@ -60,34 +60,27 @@ cd %rootdir%
@for /f "delims=" %%i in ('where erl') do (
set erl=%%i
)
-@set dir_cmd="%erl%" -noshell -eval "io:format(\"~s\", [filename:nativename(code:root_dir())])." -s init stop
+@set dir_cmd="%erl%" -boot no_dot_erlang -noshell -eval "io:format(\"~s\", [filename:nativename(code:root_dir())])." -s init stop
@for /f "delims=" %%i in ('%%dir_cmd%%') do (
set erl_root=%%i
)
-@set erts_dir=%erl_root%\erts-%erts_vsn%
+@set "erts_dir=%erl_root%\erts-%erts_vsn%"
@set rootdir=%erl_root%
@goto :eof
:: Find the sys.config file
:find_sys_config
-@set possible_sys=%rel_dir%\sys.config
+@set "possible_sys=%rel_dir%\sys.config"
@if exist "%possible_sys%" (
set sys_config=-config "%possible_sys%"
)
-@if exist "%possible_sys%".orig (
- ren "%possible_sys%".orig "%possible_sys%"
- set sys_config=-config "%possible_sys%"
-)
-@if exist "%rel_dir%\vm.args".orig (
- ren "%rel_dir%\vm.args" ".orig %rel_dir%\vm.args"
-)
@goto :eof
:: set boot_script variable
:set_boot_script_var
@if exist "%rel_dir%\%rel_name%.boot" (
- set boot_script=%rel_dir%\%rel_name%
+ set "boot_script=%rel_dir%\%rel_name%"
) else (
- set boot_script=%rel_dir%\start
+ set "boot_script=%rel_dir%\start"
)
@goto :eof
diff --git a/priv/templates/builtin_hook_pid b/priv/templates/builtin_hook_pid
new file mode 100644
index 0000000..0151631
--- /dev/null
+++ b/priv/templates/builtin_hook_pid
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# loop until the VM starts responding to pings
+while ! $(relx_nodetool "ping">/dev/null)
+do
+ sleep 1
+done
+
+# get the beam pid and write it to the file passed as
+# argument
+PID="$(relx_get_pid)"
+echo $PID > $1
diff --git a/priv/templates/builtin_hook_status b/priv/templates/builtin_hook_status
new file mode 100644
index 0000000..e5dd792
--- /dev/null
+++ b/priv/templates/builtin_hook_status
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+echo $(relx_nodetool eval "application:which_applications().")
diff --git a/priv/templates/builtin_hook_wait_for_process b/priv/templates/builtin_hook_wait_for_process
new file mode 100644
index 0000000..af5994d
--- /dev/null
+++ b/priv/templates/builtin_hook_wait_for_process
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+# loop until the VM starts responding to pings
+while ! $(relx_nodetool "ping">/dev/null)
+do
+ sleep 1
+done
+
+# loop until the name provided as argument gets
+# registered
+while true
+do
+ if [ "$(relx_nodetool eval "whereis($1).")" != "undefined" ]
+ then
+ break
+ fi
+done
diff --git a/priv/templates/builtin_hook_wait_for_vm_start b/priv/templates/builtin_hook_wait_for_vm_start
new file mode 100644
index 0000000..6b9ee12
--- /dev/null
+++ b/priv/templates/builtin_hook_wait_for_vm_start
@@ -0,0 +1,7 @@
+#!/bin/bash
+
+# loop until the VM starts responding to pings
+while ! $(relx_nodetool "ping">/dev/null)
+do
+ sleep 1
+done
diff --git a/priv/templates/extended_bin b/priv/templates/extended_bin
index 7a9f0c7..0abf38b 100755
--- a/priv/templates/extended_bin
+++ b/priv/templates/extended_bin
@@ -2,13 +2,36 @@
set -e
-SCRIPT=$(readlink $0 || true)
-if [ -z $SCRIPT ]; then
- SCRIPT=$0
-fi;
+# http://erlang.org/doc/man/run_erl.html
+# If defined, disables input and output flow control for the pty
+# opend by run_erl. Useful if you want to remove any risk of accidentally
+# blocking the flow control by using Ctrl-S (instead of Ctrl-D to detach),
+# which can result in blocking of the entire Beam process, and in the case
+# of running heart as supervisor even the heart process becomes blocked
+# when writing log message to terminal, leaving the heart process unable
+# to do its work.
+RUN_ERL_DISABLE_FLOWCNTRL=${RUN_ERL_DISABLE_FLOWCNTRL:-true}
+export $RUN_ERL_DISABLE_FLOWCNTRL
+
+if [ "$TERM" = "dumb" -o -z "$TERM" ]; then
+ export TERM=screen
+fi
+
+# OSX does not support readlink '-f' flag, work
+# around that
+case $OSTYPE in
+ darwin*)
+ SCRIPT=$(readlink $0 || true)
+ ;;
+ *)
+ SCRIPT=$(readlink -f $0 || true)
+ ;;
+esac
+[ -z $SCRIPT ] && SCRIPT=$0
SCRIPT_DIR="$(cd `dirname "$SCRIPT"` && pwd -P)"
RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd -P)"
-REL_NAME="{{ rel_name }}"
+# Make the value available to variable substitution calls below
+export REL_NAME="{{ rel_name }}"
REL_VSN="{{ rel_vsn }}"
ERTS_VSN="{{ erts_vsn }}"
CODE_LOADING_MODE="${CODE_LOADING_MODE:-embedded}"
@@ -16,6 +39,79 @@ REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN"
ERL_OPTS="{{ erl_opts }}"
RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_ROOT_DIR/log}"
+# start/stop/install/upgrade pre/post hooks
+PRE_START_HOOKS="{{{ pre_start_hooks }}}"
+POST_START_HOOKS="{{{ post_start_hooks }}}"
+PRE_STOP_HOOKS="{{{ pre_stop_hooks }}}"
+POST_STOP_HOOKS="{{{ post_stop_hooks }}}"
+PRE_INSTALL_UPGRADE_HOOKS="{{{ pre_install_upgrade_hooks }}}"
+POST_INSTALL_UPGRADE_HOOKS="{{{ post_install_upgrade_hooks }}}"
+STATUS_HOOK="{{{ status_hook }}}"
+
+relx_usage() {
+ command="$1"
+
+ case "$command" in
+ unpack)
+ echo "Usage: $REL_NAME unpack [VERSION]"
+ echo "Unpacks a release package VERSION, it assumes that this"
+ echo "release package tarball has already been deployed at one"
+ echo "of the following locations:"
+ echo " releases/<relname>-<version>.tar.gz"
+ echo " releases/<version>/<relname>-<version>.tar.gz"
+ echo " releases/<version>/<relname>.tar.gz"
+ ;;
+ install)
+ echo "Usage: $REL_NAME install [VERSION]"
+ echo "Installs a release package VERSION, it assumes that this"
+ echo "release package tarball has already been deployed at one"
+ echo "of the following locations:"
+ echo " releases/<relname>-<version>.tar.gz"
+ echo " releases/<version>/<relname>-<version>.tar.gz"
+ echo " releases/<version>/<relname>.tar.gz"
+ echo ""
+ echo " --no-permanent Install release package VERSION but"
+ echo " don't make it permanent"
+ ;;
+ uninstall)
+ echo "Usage: $REL_NAME uninstall [VERSION]"
+ echo "Uninstalls a release VERSION, it will only accept"
+ echo "versions that are not currently in use"
+ ;;
+ upgrade)
+ echo "Usage: $REL_NAME upgrade [VERSION]"
+ echo "Upgrades the currently running release to VERSION, it assumes"
+ echo "that a release package tarball has already been deployed at one"
+ echo "of the following locations:"
+ echo " releases/<relname>-<version>.tar.gz"
+ echo " releases/<version>/<relname>-<version>.tar.gz"
+ echo " releases/<version>/<relname>.tar.gz"
+ echo ""
+ echo " --no-permanent Install release package VERSION but"
+ echo " don't make it permanent"
+ ;;
+ downgrade)
+ echo "Usage: $REL_NAME downgrade [VERSION]"
+ echo "Downgrades the currently running release to VERSION, it assumes"
+ echo "that a release package tarball has already been deployed at one"
+ echo "of the following locations:"
+ echo " releases/<relname>-<version>.tar.gz"
+ echo " releases/<version>/<relname>-<version>.tar.gz"
+ echo " releases/<version>/<relname>.tar.gz"
+ echo ""
+ echo " --no-permanent Install release package VERSION but"
+ echo " don't make it permanent"
+ ;;
+ status)
+ echo "Usage: $REL_NAME status"
+ echo "Obtains node status information."
+ ;;
+ *)
+ 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|uninstall|versions|escript|rpc|rpcterms|eval|status}"
+ ;;
+ esac
+}
+
find_erts_dir() {
__erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
if [ -d "$__erts_dir" ]; then
@@ -24,7 +120,7 @@ find_erts_dir() {
else
__erl="$(which erl)"
code="io:format(\"~s\", [code:root_dir()]), halt()."
- __erl_root="$("$__erl" -noshell -eval "$code")"
+ __erl_root="$("$__erl" -boot no_dot_erlang -sasl errlog_type error -noshell -eval "$code")"
ERTS_DIR="$__erl_root/erts-$ERTS_VSN"
ROOTDIR="$__erl_root"
fi
@@ -44,7 +140,9 @@ relx_get_pid() {
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 '[H]=tl(string:tokens(atom_to_list(node()),"@")), io:format("~s~n",[H]), halt()' \
+ -noshell ${NAME_TYPE} $id
}
# Connect to a remote node
@@ -89,76 +187,206 @@ relx_start_command() {
"$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"
+relx_get_code_paths() {
+ code="{ok, [{release,_,_,Apps}]} = file:consult(\"$REL_DIR/$REL_NAME.rel\"),"\
+"lists:foreach(fun(A) ->"\
+" io:fwrite(\"$ROOTDIR/lib/~p-~s/ebin \", [element(1, A), element(2, A)]) "\
+"end, Apps),"\
+"halt()."
+
+ "$BINDIR/erl" -noshell -boot start_clean -eval "$code"
+}
+
+make_out_file_path() {
+ # Use output directory provided in the RELX_OUT_FILE_PATH environment variable
+ # (default to the current location of vm.args and sys.config)
+ DIR=$(dirname $1)
+ [ -d "${RELX_OUT_FILE_PATH}" ] && DIR="${RELX_OUT_FILE_PATH}"
+ FILE=$(basename $1)
+ IN="${DIR}/${FILE}"
+
+ PFX=$(echo $IN | awk '{sub(/\.[^.]+$/, "", $0)}1')
+ SFX=$(echo $FILE | awk -F . '{if (NF>1) print $NF}')
+ if [ $RELX_MULTI_NODE ]; then
+ echo "${PFX}.${NAME}.${SFX}"
else
- VMARGS_PATH="$REL_DIR/vm.args"
+ echo "${PFX}.${SFX}"
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
+# Replace environment variables
+replace_os_vars() {
+ awk '{
+ while(match($0,"[$]{[^}]*}")) {
+ var=substr($0,RSTART+2,RLENGTH -3)
+ gsub("[$]{"var"}",ENVIRON[var])
+ }
+ }1' < "$1" > "$2"
+}
- 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"
+add_path() {
+ # Use $CWD/$1 if exists, otherwise releases/VSN/$1
+ IN_FILE_PATH=$2
+ if [ -z "$IN_FILE_PATH" ]; then
+ if [ -f "$RELEASE_ROOT_DIR/$1" ]; then
+ IN_FILE_PATH="$RELEASE_ROOT_DIR/$1"
+ else
+ IN_FILE_PATH="$REL_DIR/$1"
+ fi
fi
-fi
+ echo $IN_FILE_PATH
+}
-# Make sure log directory exists
-mkdir -p "$RUNNER_LOG_DIR"
+check_replace_os_vars() {
+ IN_FILE_PATH=$(add_path $1 $2)
+ OUT_FILE_PATH="$IN_FILE_PATH"
+ ORIG_FILE_PATH="$IN_FILE_PATH.orig"
+ if [ $RELX_REPLACE_OS_VARS ]; then
+ # Create a new file in the same location as original
+ OUT_FILE_PATH=$(make_out_file_path $IN_FILE_PATH)
+ # If vm.args.orig or sys.config.orig is present then use that
+ if [ -f "$ORIG_FILE_PATH" ]; then
+ IN_FILE_PATH="$ORIG_FILE_PATH"
+ fi
-# 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"
+ # apply the environment variable substitution to $IN_FILE_PATH
+ # the result is saved to $OUT_FILE_PATH
+ # if they are both the same, then ensure that we don't clobber
+ # the file by saving a backup with the .orig extension
+ if [ "$IN_FILE_PATH" = "$OUT_FILE_PATH" ]; then
+ cp "$IN_FILE_PATH" "$ORIG_FILE_PATH"
+ replace_os_vars "$ORIG_FILE_PATH" "$OUT_FILE_PATH"
+ else
+ replace_os_vars "$IN_FILE_PATH" "$OUT_FILE_PATH"
+ fi
else
- RELX_CONFIG_PATH="$REL_DIR/sys.config"
+ # If vm.arg.orig or sys.config.orig is present then use that
+ if [ -f "$ORIG_FILE_PATH" ]; then
+ cp "$ORIG_FILE_PATH" "$OUT_FILE_PATH"
+ fi
fi
-fi
+ echo $OUT_FILE_PATH
+}
-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
+relx_run_hooks() {
+ HOOKS=$1
+ for hook in $HOOKS
+ do
+ # the scripts arguments at this point are separated
+ # from each other by | , we now replace these
+ # by empty spaces and give them to the `set`
+ # command in order to be able to extract them
+ # separately
+ set `echo "$hook" | sed -e 's/|/ /g'`
+ HOOK_SCRIPT=$1; shift
+ # all hook locations are expected to be
+ # relative to the start script location
+ [ "$SCRIPT_DIR/$HOOK_SCRIPT" ] && . "$SCRIPT_DIR/$HOOK_SCRIPT" $@
+ done
+}
- 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
+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="$(dirname "$ERTS_DIR")/lib"
+
+VMARGS_PATH=$(add_path vm.args $VMARGS_PATH)
+
+# Check vm.args and other files referenced via -args_file parameters for:
+# - nonexisting -args_files
+# - circular dependencies of -args_files
+# - relative paths in -args_file parameters
+# - multiple/mixed occurences of -name and -sname parameters
+# - missing -name or -sname parameters
+# If all checks pass, extract the target node name
+set +e
+TMP_NAME_ARG=$(awk 'function check_name(file)
+{
+ if (system("test -f "file)) {
+ print file" not found"
+ exit 3
+ }
+ if (system("test -r "file)) {
+ print file" not readable"
+ exit 3
+ }
+ while ((getline line<file)>0) {
+ if (line~/^-args_file +/) {
+ gsub(/^-args_file +| *$/, "", line)
+ if (!(line~/^\//)) {
+ print "relative path "line" encountered in "file
+ exit 4
+ }
+ if (line in files) {
+ print "circular reference to "line" encountered in "file
+ exit 5
+ }
+ files[line]=line
+ check_name(line)
+ }
+ else if (line~/^-s?name +/) {
+ if (name!="") {
+ print "\""line"\" parameter found in "file" but already specified as \""name"\""
+ exit 2
+ }
+ name=line
+ }
+ }
+}
-# 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
+BEGIN {
+ split("", files)
+ name=""
+}
+
+{
+ files[FILENAME]=FILENAME
+ check_name(FILENAME)
+ if (name=="") {
+ print "need to have exactly one of either -name or -sname parameters but none found"
+ exit 1
+ }
+ print name
+ exit 0
+}' "$VMARGS_PATH")
+TMP_NAME_ARG_RC=$?
+case $TMP_NAME_ARG_RC in
+ 0) NAME_ARG="$TMP_NAME_ARG";;
+ *) echo "$TMP_NAME_ARG"
+ exit $TMP_NAME_ARG_RC;;
+esac
+unset TMP_NAME_ARG
+unset TMP_NAME_ARG_RC
+set -e
+
+# Perform replacement of variables in ${NAME_ARG}
+NAME_ARG=$(eval echo "${NAME_ARG}")
# 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}')"
+# 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);; # Add @hostname
+esac
+
+# Export the variable so that it's available in the 'eval' calls
+export NAME
+
+VMARGS_PATH=$(check_replace_os_vars vm.args $VMARGS_PATH)
+RELX_CONFIG_PATH=$(check_replace_os_vars sys.config $RELX_CONFIG_PATH)
+
+# Make sure log directory exists
+mkdir -p "$RUNNER_LOG_DIR"
+
+test -z "$PIPE_DIR" && PIPE_BASE_DIR='/tmp/erl_pipes/'
PIPE_DIR="${PIPE_DIR:-/tmp/erl_pipes/$NAME/}"
# Extract the target cookie
@@ -176,28 +404,8 @@ else
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)
@@ -232,13 +440,17 @@ case "$1" in
HEART_COMMAND="$RELEASE_ROOT_DIR/bin/$REL_NAME $CMD"
export HEART_COMMAND
+ test -z "$PIPE_BASE_DIR" || mkdir -m 1777 -p "$PIPE_BASE_DIR"
mkdir -p "$PIPE_DIR"
+ relx_run_hooks "$PRE_START_HOOKS"
"$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \
"$(relx_start_command)"
+ relx_run_hooks "$POST_START_HOOKS"
;;
stop)
+ relx_run_hooks "$PRE_STOP_HOOKS"
# Wait for the node to completely stop...
PID="$(relx_get_pid)"
if ! relx_nodetool "stop"; then
@@ -248,6 +460,7 @@ case "$1" in
do
sleep 1
done
+ relx_run_hooks "$POST_STOP_HOOKS"
;;
restart)
@@ -307,43 +520,45 @@ case "$1" in
relx_rem_sh
;;
- upgrade|downgrade|install)
+ upgrade|downgrade|install|unpack|uninstall)
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"
+ echo "Missing version argument"
+ echo "Usage: $REL_NAME $1 {version}"
exit 1
fi
+ COMMAND="$1"; shift
+
# Make sure a node IS running
if ! relx_nodetool "ping" > /dev/null; then
echo "Node is not running!"
exit 1
fi
+ relx_run_hooks "$PRE_INSTALL_UPGRADE_HOOKS"
+
exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \
- "install" "$REL_NAME" "$NAME_TYPE" "$NAME" "$COOKIE" "$2"
- ;;
+ "$COMMAND" "{'$REL_NAME', \"$NAME_TYPE\", '$NAME', '$COOKIE'}" "$@"
- 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
+ relx_run_hooks "$POST_INSTALL_UPGRADE_HOOKS"
+ ;;
+ versions)
# Make sure a node IS running
if ! relx_nodetool "ping" > /dev/null; then
echo "Node is not running!"
exit 1
fi
+ COMMAND="$1"; shift
+
exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \
- "unpack" "$REL_NAME" "$NAME_TYPE" "$NAME" "$COOKIE" "$2"
+ "versions" "{'$REL_NAME', \"$NAME_TYPE\", '$NAME', '$COOKIE'}" "$@"
;;
- console|console_clean|console_boot)
+ console|console_clean|console_boot|foreground)
+ __code_paths=""
+ FOREGROUNDOPTIONS=""
# .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.
@@ -355,7 +570,18 @@ case "$1" in
BOOTFILE="$REL_DIR/start"
fi
;;
+ foreground)
+ # start up the release in the foreground for use by runit
+ # or other supervision services
+ if [ -f "$REL_DIR/$REL_NAME.boot" ]; then
+ BOOTFILE="$REL_DIR/$REL_NAME"
+ else
+ BOOTFILE="$REL_DIR/start"
+ fi
+ FOREGROUNDOPTIONS="-noshell -noinput +Bd"
+ ;;
console_clean)
+ __code_paths=$(relx_get_code_paths)
BOOTFILE="$ROOTDIR/bin/start_clean"
;;
console_boot)
@@ -376,10 +602,12 @@ case "$1" in
# 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" \
+ set -- "$BINDIR/erlexec" $FOREGROUNDOPTIONS \
+ -boot "$BOOTFILE" -mode "$CODE_LOADING_MODE" \
-boot_var ERTS_LIB_DIR "$ERTS_LIB_DIR" \
-config "$RELX_CONFIG_PATH" \
- -args_file "$VMARGS_PATH"
+ -args_file "$VMARGS_PATH" \
+ -pa ${__code_paths}
# Dump environment info for logging purposes
echo "Exec: $@" -- ${1+$ARGS}
@@ -392,38 +620,6 @@ case "$1" in
# 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
@@ -456,8 +652,26 @@ case "$1" in
shift
relx_nodetool "eval" $@
;;
+ status)
+ # Make sure a node IS running
+ if ! relx_nodetool "ping" > /dev/null; then
+ echo "Node is not running!"
+ exit 1
+ fi
+
+ [ ! -z "${STATUS_HOOK}" ] && [ "$SCRIPT_DIR/$STATUS_HOOK" ] && . "$SCRIPT_DIR/$STATUS_HOOK" $@
+ ;;
+ help)
+ if [ -z "$2" ]; then
+ relx_usage
+ exit 1
+ fi
+
+ TOPIC="$2"; shift
+ relx_usage $TOPIC
+ ;;
*)
- 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}"
+ relx_usage
exit 1
;;
esac
diff --git a/priv/templates/extended_bin_windows b/priv/templates/extended_bin_windows
index d892ea6..d0c41d8 100644
--- a/priv/templates/extended_bin_windows
+++ b/priv/templates/extended_bin_windows
@@ -25,7 +25,7 @@
@for %%A in ("%script_dir%\..") do @(
set release_root_dir=%%~fA
)
-@set rel_dir=%release_root_dir%\releases\%rel_vsn%
+@set "rel_dir=%release_root_dir%\releases\%rel_vsn%"
@call :find_erts_dir
@call :find_sys_config
@@ -56,6 +56,19 @@
:: Write the erl.ini file to set up paths relative to this script
@call :write_ini
+:: Collect any additional VM args into erl_opts
+@setlocal EnableDelayedExpansion
+@for /f "usebackq tokens=1-2" %%I in (`findstr /r "^[^#]" "%vm_args%"`) do @(
+ if not "%%I" == "-name" (
+ if not "%%I" == "-sname" (
+ if not "%%I" == "-setcookie" (
+ set erl_opts=!erl_opts! %%I %%J
+ )
+ )
+ )
+)
+@endlocal && set erl_opts=%erl_opts%
+
:: If a start.boot file is not present, copy one from the named .boot file
@if not exist "%rel_dir%\start.boot" (
copy "%rel_dir%\%rel_name%.boot" "%rel_dir%\start.boot" >nul
@@ -79,7 +92,7 @@
:: Find the ERTS dir
:find_erts_dir
-@set possible_erts_dir=%release_root_dir%\erts-%erts_vsn%
+@set "possible_erts_dir=%release_root_dir%\erts-%erts_vsn%"
@if exist "%possible_erts_dir%" (
call :set_erts_dir_from_default
) else (
@@ -89,8 +102,8 @@
:: Set the ERTS dir from the passed in erts_vsn
:set_erts_dir_from_default
-@set erts_dir=%possible_erts_dir%
-@set rootdir=%release_root_dir%
+@set "erts_dir=%possible_erts_dir%"
+@set "rootdir=%release_root_dir%"
@goto :eof
:: Set the ERTS dir from erl
@@ -98,17 +111,17 @@
@for /f "delims=" %%i in ('where erl') do @(
set erl=%%i
)
-@set dir_cmd="%erl%" -noshell -eval "io:format(\"~s\", [filename:nativename(code:root_dir())])." -s init stop
+@set dir_cmd="%erl%" -boot no_dot_erlang -noshell -eval "io:format(\"~s\", [filename:nativename(code:root_dir())])." -s init stop
@for /f "delims=" %%i in ('%%dir_cmd%%') do @(
set erl_root=%%i
)
-@set erts_dir=%erl_root%\erts-%erts_vsn%
-@set rootdir=%erl_root%
+@set "erts_dir=%erl_root%\erts-%erts_vsn%"
+@set "rootdir=%erl_root%"
@goto :eof
:: Find the sys.config file
:find_sys_config
-@set possible_sys=%rel_dir%\sys.config
+@set "possible_sys=%rel_dir%\sys.config"
@if exist %possible_sys% (
set sys_config=-config "%possible_sys%"
)
@@ -117,9 +130,9 @@
:: set boot_script variable
:set_boot_script_var
@if exist "%rel_dir%\%rel_name%.boot" (
- set boot_script=%rel_dir%\%rel_name%
+ set "boot_script=%rel_dir%\%rel_name%"
) else (
- set boot_script=%rel_dir%\start
+ set "boot_script=%rel_dir%\start"
)
@goto :eof
@@ -175,13 +188,12 @@ set description=Erlang node %node_name% in %rootdir%
:: Relup and reldown
:relup
@if "" == "%2" (
- echo Missing package argument
- echo Usage: %rel_name% %1 {package base name}
- echo NOTE {package base name} MUST NOT include the .tar.gz suffix
+ echo Missing version argument
+ echo Usage: %rel_name% %1 {version}
set ERRORLEVEL=1
exit /b %ERRORLEVEL%
)
-@%escript% "%rootdir%/bin/install_upgrade.escript" "%rel_name%" "%node_name%" "%cookie%" "%2"
+@%escript% "%rootdir%/bin/install_upgrade.escript" "install" "%rel_name%" "%node_name%" "%cookie%" "%2"
@goto :eof
:: Start a console
diff --git a/priv/templates/install_upgrade_escript b/priv/templates/install_upgrade_escript
index 0910c38..47521c6 100644
--- a/priv/templates/install_upgrade_escript
+++ b/priv/templates/install_upgrade_escript
@@ -6,85 +6,236 @@
-define(TIMEOUT, 300000).
-define(INFO(Fmt,Args), io:format(Fmt,Args)).
-%% Unpack or upgrade to a new tar.gz release
-main(["unpack", RelName, NameTypeArg, NodeName, Cookie, VersionArg]) ->
+main([Command0, DistInfoStr | CommandArgs]) ->
+ %% convert the distribution info arguments string to an erlang term
+ {ok, Tokens, _} = erl_scan:string(DistInfoStr ++ "."),
+ {ok, DistInfo} = erl_parse:parse_term(Tokens),
+ %% convert arguments into a proplist
+ Opts = parse_arguments(CommandArgs),
+ %% invoke the command passed as argument
+ F = case Command0 of
+ "install" -> fun(A, B) -> install(A, B) end;
+ "unpack" -> fun(A, B) -> unpack(A, B) end;
+ "upgrade" -> fun(A, B) -> upgrade(A, B) end;
+ "downgrade" -> fun(A, B) -> downgrade(A, B) end;
+ "uninstall" -> fun(A, B) -> uninstall(A, B) end;
+ "versions" -> fun(A, B) -> versions(A, B) end
+ end,
+ F(DistInfo, Opts);
+main(Args) ->
+ ?INFO("unknown args: ~p\n", [Args]),
+ erlang:halt(1).
+
+unpack({RelName, NameTypeArg, NodeName, Cookie}, Opts) ->
TargetNode = start_distribution(NodeName, NameTypeArg, Cookie),
- WhichReleases = which_releases(TargetNode),
- Version = parse_version(VersionArg),
- case proplists:get_value(Version, WhichReleases) of
- undefined ->
- %% not installed, so unpack tarball:
- ?INFO("Release ~s not found, attempting to unpack releases/~s/~s.tar.gz~n",[Version,Version,RelName]),
- ReleasePackage = Version ++ "/" ++ RelName,
- case rpc:call(TargetNode, release_handler, unpack_release,
- [ReleasePackage], ?TIMEOUT) of
- {ok, Vsn} ->
- ?INFO("Unpacked successfully: ~p~n", [Vsn]);
- {error, UnpackReason} ->
- print_existing_versions(TargetNode),
- ?INFO("Unpack failed: ~p~n",[UnpackReason]),
- erlang:halt(2)
- end;
+ Version = proplists:get_value(version, Opts),
+ case unpack_release(RelName, TargetNode, Version) of
+ {ok, Vsn} ->
+ ?INFO("Unpacked successfully: ~p~n", [Vsn]);
old ->
%% no need to unpack, has been installed previously
- ?INFO("Release ~s is marked old, switching to it.~n",[Version]);
+ ?INFO("Release ~s is marked old.~n",[Version]);
unpacked ->
- ?INFO("Release ~s is already unpacked, now installing.~n",[Version]);
+ ?INFO("Release ~s is already unpacked.~n",[Version]);
current ->
- ?INFO("Release ~s is already installed and current. Making permanent.~n",[Version]);
+ ?INFO("Release ~s is already installed and current.~n",[Version]);
permanent ->
- ?INFO("Release ~s is already installed, and set permanent.~n",[Version])
+ ?INFO("Release ~s is already installed and set permanent.~n",[Version]);
+ {error, Reason} ->
+ ?INFO("Unpack failed: ~p~n",[Reason]),
+ print_existing_versions(TargetNode),
+ erlang:halt(2)
end;
-main(["install", RelName, NameTypeArg, NodeName, Cookie, VersionArg]) ->
+unpack(_, Args) ->
+ ?INFO("unpack: unknown args ~p\n", [Args]).
+
+install({RelName, NameTypeArg, NodeName, Cookie}, Opts) ->
TargetNode = start_distribution(NodeName, NameTypeArg, Cookie),
- WhichReleases = which_releases(TargetNode),
- Version = parse_version(VersionArg),
- case proplists:get_value(Version, WhichReleases) of
- undefined ->
- %% not installed, so unpack tarball:
- ?INFO("Release ~s not found, attempting to unpack releases/~s/~s.tar.gz~n",[Version,Version,RelName]),
- ReleasePackage = Version ++ "/" ++ RelName,
- case rpc:call(TargetNode, release_handler, unpack_release,
- [ReleasePackage], ?TIMEOUT) of
- {ok, Vsn} ->
- ?INFO("Unpacked successfully: ~p~n", [Vsn]),
- install_and_permafy(TargetNode, RelName, Vsn);
- {error, UnpackReason} ->
- print_existing_versions(TargetNode),
- ?INFO("Unpack failed: ~p~n",[UnpackReason]),
- erlang:halt(2)
- end;
+ Version = proplists:get_value(version, Opts),
+ case unpack_release(RelName, TargetNode, Version) of
+ {ok, Vsn} ->
+ ?INFO("Unpacked successfully: ~p~n", [Vsn]),
+ check_and_install(TargetNode, Vsn),
+ maybe_permafy(TargetNode, RelName, Vsn, Opts);
old ->
%% no need to unpack, has been installed previously
?INFO("Release ~s is marked old, switching to it.~n",[Version]),
- install_and_permafy(TargetNode, RelName, Version);
+ check_and_install(TargetNode, Version),
+ maybe_permafy(TargetNode, RelName, Version, Opts);
unpacked ->
?INFO("Release ~s is already unpacked, now installing.~n",[Version]),
- install_and_permafy(TargetNode, RelName, Version);
- current -> %% installed and in-use, just needs to be permanent
- ?INFO("Release ~s is already installed and current. Making permanent.~n",[Version]),
- permafy(TargetNode, RelName, Version);
+ check_and_install(TargetNode, Version),
+ maybe_permafy(TargetNode, RelName, Version, Opts);
+ current ->
+ case proplists:get_value(permanent, Opts, true) of
+ true ->
+ ?INFO("Release ~s is already installed and current, making permanent.~n",
+ [Version]),
+ permafy(TargetNode, RelName, Version);
+ false ->
+ ?INFO("Release ~s is already installed and current.~n",
+ [Version])
+ end;
permanent ->
- ?INFO("Release ~s is already installed, and set permanent.~n",[Version])
+ %% this release is marked permanent, however it might not the
+ %% one currently running
+ case current_release_version(TargetNode) of
+ Version ->
+ ?INFO("Release ~s is already installed, running and set permanent.~n",
+ [Version]);
+ CurrentVersion ->
+ ?INFO("Release ~s is the currently running version.~n",
+ [CurrentVersion]),
+ check_and_install(TargetNode, Version),
+ maybe_permafy(TargetNode, RelName, Version, Opts)
+ end;
+ {error, Reason} ->
+ ?INFO("Unpack failed: ~p~n",[Reason]),
+ print_existing_versions(TargetNode),
+ erlang:halt(2)
end;
-main(_) ->
- erlang:halt(1).
+install(_, Args) ->
+ ?INFO("install: unknown args ~p\n", [Args]).
+
+upgrade(DistInfo, Args) ->
+ install(DistInfo, Args).
+
+downgrade(DistInfo, Args) ->
+ install(DistInfo, Args).
+
+uninstall({_RelName, NameTypeArg, NodeName, Cookie}, Opts) ->
+ TargetNode = start_distribution(NodeName, NameTypeArg, Cookie),
+ WhichReleases = which_releases(TargetNode),
+ Version = proplists:get_value(version, Opts),
+ case proplists:get_value(Version, WhichReleases) of
+ undefined ->
+ ?INFO("Release ~s is already uninstalled.~n", [Version]);
+ old ->
+ ?INFO("Release ~s is marked old, uninstalling it.~n", [Version]),
+ remove_release(TargetNode, Version);
+ unpacked ->
+ ?INFO("Release ~s is marked unpacked, uninstalling it~n",
+ [Version]),
+ remove_release(TargetNode, Version);
+ current ->
+ ?INFO("Uninstall failed: Release ~s is marked current.~n", [Version]),
+ erlang:halt(2);
+ permanent ->
+ ?INFO("Uninstall failed: Release ~s is running.~n", [Version]),
+ erlang:halt(2)
+ end;
+uninstall(_, Args) ->
+ ?INFO("uninstall: unknown args ~p\n", [Args]).
+
+versions({_RelName, NameTypeArg, NodeName, Cookie}, []) ->
+ TargetNode = start_distribution(NodeName, NameTypeArg, Cookie),
+ print_existing_versions(TargetNode).
+
+parse_arguments(Args) ->
+ parse_arguments(Args, []).
+
+parse_arguments([], Acc) -> Acc;
+parse_arguments(["--no-permanent"|Rest], Acc) ->
+ parse_arguments(Rest, [{permanent, false}] ++ Acc);
+parse_arguments([VersionStr|Rest], Acc) ->
+ Version = parse_version(VersionStr),
+ parse_arguments(Rest, [{version, Version}] ++ Acc).
+
+unpack_release(RelName, TargetNode, Version) ->
+ WhichReleases = which_releases(TargetNode),
+ case proplists:get_value(Version, WhichReleases) of
+ undefined ->
+ %% not installed, so unpack tarball:
+ %% look for a release package with the intended version in the following order:
+ %% releases/<relname>-<version>.tar.gz
+ %% releases/<version>/<relname>-<version>.tar.gz
+ %% releases/<version>/<relname>.tar.gz
+ case find_and_link_release_package(Version, RelName) of
+ {_, undefined} ->
+ {error, release_package_not_found};
+ {ReleasePackage, ReleasePackageLink} ->
+ ?INFO("Release ~s not found, attempting to unpack ~s~n",
+ [Version, ReleasePackage]),
+ case rpc:call(TargetNode, release_handler, unpack_release,
+ [ReleasePackageLink], ?TIMEOUT) of
+ {ok, Vsn} -> {ok, Vsn};
+ {error, _} = Error -> Error
+ end
+ end;
+ Other -> Other
+ end.
+
+%% 1. look for a release package tarball with the provided version in the following order:
+%% releases/<relname>-<version>.tar.gz
+%% releases/<version>/<relname>-<version>.tar.gz
+%% releases/<version>/<relname>.tar.gz
+%% 2. create a symlink from a fixed location (ie. releases/<version>/<relname>.tar.gz)
+%% to the release package tarball found in 1.
+%% 3. return a tuple with the paths to the release package and
+%% to the symlink that is to be provided to release handler
+find_and_link_release_package(Version, RelName) ->
+ RelNameStr = atom_to_list(RelName),
+ %% regardless of the location of the release package, we'll
+ %% always give release handler the same path which is the symlink
+ %% the path to the package link is relative to "releases/" because
+ %% that's what release handler is expecting
+ ReleaseHandlerPackageLink = filename:join(Version, RelNameStr),
+ %% this is the symlink name we'll create once
+ %% we've found where the actual release package is located
+ ReleaseLink = filename:join(["releases", Version,
+ RelNameStr ++ ".tar.gz"]),
+ case first_value(fun filelib:is_file/1,
+ [filename:join(["releases",
+ RelNameStr ++ "-" ++ Version ++ ".tar.gz"]),
+ filename:join(["releases", Version,
+ RelNameStr ++ "-" ++ Version ++ ".tar.gz"]),
+ filename:join(["releases", Version,
+ RelNameStr ++ ".tar.gz"])]) of
+ no_value ->
+ {undefined, undefined};
+ %% no need to create the link since the release package we
+ %% found is located in the same place as the link would be
+ {ok, Filename} when is_list(Filename) andalso
+ Filename =:= ReleaseLink ->
+ {Filename, ReleaseHandlerPackageLink};
+ {ok, Filename} when is_list(Filename) ->
+ %% we now have the location of the release package, however
+ %% release handler expects a fixed nomenclature (<relname>.tar.gz)
+ %% so give it just that by creating a symlink to the tarball
+ %% we found.
+ %% make sure that the dir where we're creating the link in exists
+ ok = filelib:ensure_dir(filename:join([filename:dirname(ReleaseLink), "dummy"])),
+ %% create the symlink pointing to the full path name of the
+ %% release package we found
+ ok = file:make_symlink(filename:absname(Filename), ReleaseLink),
+ {Filename, ReleaseHandlerPackageLink}
+ end.
+
+first_value(_Fun, []) -> no_value;
+first_value(Fun, [Value | Rest]) ->
+ case Fun(Value) of
+ false ->
+ first_value(Fun, Rest);
+ true ->
+ {ok, Value}
+ end.
parse_version(V) when is_list(V) ->
hd(string:tokens(V,"/")).
-install_and_permafy(TargetNode, RelName, Vsn) ->
- case rpc:call(TargetNode, release_handler, check_install_release, [Vsn], ?TIMEOUT) of
+check_and_install(TargetNode, Vsn) ->
+ case rpc:call(TargetNode, release_handler,
+ check_install_release, [Vsn], ?TIMEOUT) of
{ok, _OtherVsn, _Desc} ->
ok;
{error, Reason} ->
?INFO("ERROR: release_handler:check_install_release failed: ~p~n",[Reason]),
erlang:halt(3)
end,
- case rpc:call(TargetNode, release_handler, install_release, [Vsn], ?TIMEOUT) of
+ case rpc:call(TargetNode, release_handler, install_release,
+ [Vsn, [{update_paths, true}]], ?TIMEOUT) of
{ok, _, _} ->
?INFO("Installed Release: ~s~n", [Vsn]),
- permafy(TargetNode, RelName, Vsn),
ok;
{error, {no_such_release, Vsn}} ->
VerList =
@@ -106,28 +257,55 @@ install_and_permafy(TargetNode, RelName, Vsn) ->
erlang:halt(4)
end.
+maybe_permafy(TargetNode, RelName, Vsn, Opts) ->
+ case proplists:get_value(permanent, Opts, true) of
+ true ->
+ permafy(TargetNode, RelName, Vsn);
+ false -> ok
+ end.
+
permafy(TargetNode, RelName, Vsn) ->
- ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], ?TIMEOUT),
- file:copy(filename:join(["bin", RelName++"-"++Vsn]),
- filename:join(["bin", RelName])),
+ ok = rpc:call(TargetNode, release_handler,
+ make_permanent, [Vsn], ?TIMEOUT),
+ file:copy(filename:join(["bin", atom_to_list(RelName)++"-"++Vsn]),
+ filename:join(["bin", atom_to_list(RelName)])),
?INFO("Made release permanent: ~p~n", [Vsn]),
ok.
+remove_release(TargetNode, Vsn) ->
+ case rpc:call(TargetNode, release_handler, remove_release, [Vsn], ?TIMEOUT) of
+ ok ->
+ ?INFO("Uninstalled Release: ~s~n", [Vsn]),
+ ok;
+ {error, Reason} ->
+ ?INFO("ERROR: release_handler:remove_release failed: ~p~n", [Reason]),
+ erlang:halt(3)
+ end.
+
which_releases(TargetNode) ->
R = rpc:call(TargetNode, release_handler, which_releases, [], ?TIMEOUT),
[ {V, S} || {_,V,_, S} <- R ].
+%% the running release version is either the only one marked `current´
+%% or, if none exists, the one marked `permanent`
+current_release_version(TargetNode) ->
+ R = rpc:call(TargetNode, release_handler, which_releases,
+ [], ?TIMEOUT),
+ Versions = [ {S, V} || {_,V,_, S} <- R ],
+ %% current version takes priority over the permanent
+ proplists:get_value(current, Versions,
+ proplists:get_value(permanent, Versions)).
+
print_existing_versions(TargetNode) ->
VerList = iolist_to_binary([
io_lib:format("* ~s\t~s~n",[V,S])
|| {V,S} <- which_releases(TargetNode) ]),
?INFO("Installed versions:~n~s", [VerList]).
-start_distribution(NodeName, NameTypeArg, Cookie) ->
- MyNode = make_script_node(NodeName),
+start_distribution(TargetNode, NameTypeArg, Cookie) ->
+ MyNode = make_script_node(TargetNode),
{ok, _Pid} = net_kernel:start([MyNode, get_name_type(NameTypeArg)]),
- erlang:set_cookie(node(), list_to_atom(Cookie)),
- TargetNode = list_to_atom(NodeName),
+ erlang:set_cookie(node(), Cookie),
case {net_kernel:connect_node(TargetNode),
net_adm:ping(TargetNode)} of
{true, pong} ->
@@ -141,7 +319,7 @@ start_distribution(NodeName, NameTypeArg, Cookie) ->
TargetNode.
make_script_node(Node) ->
- [Name, Host] = string:tokens(Node, "@"),
+ [Name, Host] = string:tokens(atom_to_list(Node), "@"),
list_to_atom(lists:concat([Name, "_upgrader_", os:getpid(), "@", Host])).
%% get name type from arg
diff --git a/priv/templates/vm_args b/priv/templates/vm_args
index 02841bd..f65c7dc 100644
--- a/priv/templates/vm_args
+++ b/priv/templates/vm_args
@@ -17,3 +17,14 @@
## Tweak GC to run more often
##-env ERL_FULLSWEEP_AFTER 10
+
+# +B [c | d | i]
+# Option c makes Ctrl-C interrupt the current shell instead of invoking the emulator break
+# handler. Option d (same as specifying +B without an extra option) disables the break handler. # Option i makes the emulator ignore any break signal.
+# If option c is used with oldshell on Unix, Ctrl-C will restart the shell process rather than
+# interrupt it.
+# Disable the emulator break handler
+# it easy to accidentally type ctrl-c when trying
+# to reach for ctrl-d. ctrl-c on a live node can
+# have very undesirable results
+##+Bi