aboutsummaryrefslogtreecommitdiffstats
path: root/priv
diff options
context:
space:
mode:
Diffstat (limited to 'priv')
-rwxr-xr-x[-rw-r--r--]priv/templates/bin.dtl68
-rw-r--r--priv/templates/bin_windows.dtl75
-rw-r--r--priv/templates/erl_ini.dtl4
-rw-r--r--priv/templates/extended_bin.dtl248
-rw-r--r--priv/templates/extended_bin_windows.dtl176
-rw-r--r--priv/templates/nodetool.dtl2
6 files changed, 446 insertions, 127 deletions
diff --git a/priv/templates/bin.dtl b/priv/templates/bin.dtl
index 067e9f7..4434552 100644..100755
--- a/priv/templates/bin.dtl
+++ b/priv/templates/bin.dtl
@@ -2,43 +2,61 @@
set -e
-SCRIPT_DIR=`dirname $0`
-RELEASE_ROOT_DIR=`cd $SCRIPT_DIR/.. && pwd`
-REL_NAME={{ rel_name }}
-REL_VSN={{ rel_vsn }}
-ERTS_VSN={{ erts_vsn }}
-REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_VSN
-ERL_OPTS={{ erl_opts }}
+SCRIPT_DIR="$(dirname "$0")"
+RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
+REL_NAME="{{ rel_name }}"
+REL_VSN="{{ rel_vsn }}"
+ERTS_VSN="{{ erts_vsn }}"
+REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN"
+ERL_OPTS="{{ erl_opts }}"
find_erts_dir() {
- local erts_dir=$RELEASE_ROOT_DIR/erts-$ERTS_VSN
+ local erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
if [ -d "$erts_dir" ]; then
- ERTS_DIR=$erts_dir;
- ROOTDIR=$RELEASE_ROOT_DIR
+ ERTS_DIR="$erts_dir";
+ ROOTDIR="$RELEASE_ROOT_DIR"
else
- local erl=`which erl`
- local erl_root=`$erl -noshell -eval "io:format(\\"~s\\", [code:root_dir()])." -s init stop`
- ERTS_DIR=$erl_root/erts-$ERTS_VSN
- ROOTDIR=$erl_root
+ local erl="$(which erl)"
+ local erl_root="$("$erl" -noshell -eval "io:format(\\"~s\\", [code:root_dir()])." -s init stop)"
+ ERTS_DIR="$erl_root/erts-$ERTS_VSN"
+ ROOTDIR="$erl_root"
fi
}
find_sys_config() {
- local possible_sys=$REL_DIR/sys.config
+ local possible_sys="$REL_DIR/sys.config"
if [ -f "$possible_sys" ]; then
- SYS_CONFIG="-config $possible_sys"
+ SYS_CONFIG="$possible_sys"
+ fi
+}
+
+find_vm_args() {
+ local possible_vm_args="$REL_DIR/vm.args"
+ if [ -f "$possible_vm_args" ]; then
+ VM_ARGS="$possible_vm_args"
fi
}
find_erts_dir
find_sys_config
-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
-
-cd $ROOTDIR
-
-$BINDIR/erlexec $ERL_OPTS $SYS_CONFIG -boot $REL_DIR/$REL_NAME $@
+find_vm_args
+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"
+
+cd "$ROOTDIR"
+
+# Save extra arguments
+ARGS="$@"
+
+# Build arguments for erlexec
+set -- "$ERL_OPTS"
+[ "$SYS_CONFIG" ] && set -- "$@" -config "$SYS_CONFIG"
+[ "$VM_ARGS" ] && set -- "$@" -args_file "$VM_ARGS"
+set -- "$@" -boot "$REL_DIR/$REL_NAME" "$ARGS"
+
+# Boot the release
+"$BINDIR/erlexec" "$@"
diff --git a/priv/templates/bin_windows.dtl b/priv/templates/bin_windows.dtl
new file mode 100644
index 0000000..170d957
--- /dev/null
+++ b/priv/templates/bin_windows.dtl
@@ -0,0 +1,75 @@
+:: This is a simple start batch file that runs the release in an Erlang shell
+
+:: Set variables that describe the release
+@set rel_name={{ rel_name }}
+@set rel_vsn={{ rel_vsn }}
+@set erts_vsn={{ erts_vsn }}
+@set erl_opts={{ erl_opts }}
+
+:: Set the root release directory based on the location of this batch file
+@set script_dir=%~dp0
+@for %%A in ("%script_dir%\..") do (
+ @set "release_root_dir=%%~fA"
+)
+@set rel_dir=%release_root_dir%\releases\%rel_vsn%
+
+@call :find_erts_dir
+@call :find_sys_config
+
+@set rootdir=%release_root_dir%
+@set bindir=%erts_dir%\bin
+@set progname=erl
+@set erl=%bindir%\erl
+
+cd %rootdir%
+
+:: Write the erl.ini file
+@set erl_ini=%erts_dir%\bin\erl.ini
+@set converted_bindir=%bindir:\=\\%
+@set converted_rootdir=%rootdir:\=\\%
+@echo [erlang] > "%erl_ini%"
+@echo Bindir=%converted_bindir% >> "%erl_ini%"
+@echo Progname=%progname% >> "%erl_ini%"
+@echo Rootdir=%converted_rootdir% >> "%erl_ini%"
+
+:: Start the release in an `erl` shell
+@%erl% %erl_opts% %sys_config% -boot "%rel_dir%\%rel_name%" %*
+
+@goto :eof
+
+:: Find the ERTS dir
+:find_erts_dir
+@set erts_dir=%release_root_dir%\erts-%erts_vsn%
+@if exist %erts_dir% (
+ @goto :set_erts_dir_from_default
+) else (
+ @goto :set_erts_dir_from_erl
+)
+@goto :eof
+
+:: Set the ERTS dir from the passed in erts_vsn
+:set_erts_dir_from_default
+@set erts_dir=%erts_dir%
+@set root_dir=%release_root_dir%
+@goto :eof
+
+:: Set the ERTS dir from erl
+:set_erts_dir_from_erl
+@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
+@for /f %%i in ('%%dir_cmd%%') do (
+ @set erl_root=%%i
+)
+@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
+@if exist "%possible_sys%" (
+ @set sys_config=-config "%possible_sys%"
+)
+@goto :eof
diff --git a/priv/templates/erl_ini.dtl b/priv/templates/erl_ini.dtl
new file mode 100644
index 0000000..b2ce5bc
--- /dev/null
+++ b/priv/templates/erl_ini.dtl
@@ -0,0 +1,4 @@
+[erlang]
+Bindir={{ bin_dir }}
+Progname=erl
+Rootdir={{ output_dir }}
diff --git a/priv/templates/extended_bin.dtl b/priv/templates/extended_bin.dtl
index a0e46a9..59cf692 100644
--- a/priv/templates/extended_bin.dtl
+++ b/priv/templates/extended_bin.dtl
@@ -2,101 +2,113 @@
set -e
-SCRIPT_DIR=`dirname $0`
-RELEASE_ROOT_DIR=`cd $SCRIPT_DIR/.. && pwd`
-REL_NAME={{ rel_name }}
-REL_VSN={{ rel_vsn }}
-ERTS_VSN={{ erts_vsn }}
-REL_DIR=$RELEASE_ROOT_DIR/releases/$REL_VSN
-ERL_OPTS={{ erl_opts }}
-PIPE_DIR=/tmp/erl_pipes/{{ rel_name }}/
+SCRIPT_DIR="$(dirname "$0")"
+RELEASE_ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
+REL_NAME="{{ rel_name }}"
+REL_VSN="{{ rel_vsn }}"
+ERTS_VSN="{{ erts_vsn }}"
+REL_DIR="$RELEASE_ROOT_DIR/releases/$REL_VSN"
+ERL_OPTS="{{ erl_opts }}"
+PIPE_DIR="/tmp/erl_pipes/{{ rel_name }}/"
+RUNNER_LOG_DIR="${RUNNER_LOG_DIR:-$RELEASE_ROOT_DIR/log}"
find_erts_dir() {
- local erts_dir=$RELEASE_ROOT_DIR/erts-$ERTS_VSN
+ local erts_dir="$RELEASE_ROOT_DIR/erts-$ERTS_VSN"
if [ -d "$erts_dir" ]; then
- ERTS_DIR=$erts_dir;
- ROOTDIR=$RELEASE_ROOT_DIR
+ ERTS_DIR="$erts_dir";
+ ROOTDIR="$RELEASE_ROOT_DIR"
else
- local erl=`which erl`
- local erl_root=`$erl -noshell -eval "io:format(\\"~s\\", [code:root_dir()])." -s init stop`
- ERTS_DIR=$erl_root/erts-$ERTS_VSN
- ROOTDIR=$erl_root
+ local erl="$(which erl)"
+ local erl_root="$("$erl" -noshell -eval "io:format(\\"~s\\", [code:root_dir()])." -s init stop)"
+ ERTS_DIR="$erl_root/erts-$ERTS_VSN"
+ ROOTDIR="$erl_root"
fi
+}
+
+# Connect to a remote node
+relx_rem_sh() {
+ # Generate a unique id used to allow multiple remsh to the same node
+ # transparently
+ id="remsh$(date +%s`@`echo "$NAME" | awk -F@ '{print $2}')"
+ # Setup remote shell command to control node
+ exec "$BINDIR/erl" "$NAME_TYPE" "$id" -remsh "$NAME" -boot start_clean \
+ -setcookie "$COOKIE"
}
-find_sys_config() {
- local possible_sys=$REL_DIR/sys.config
- if [ -f "$possible_sys" ]; then
- SYS_CONFIG="-config $possible_sys"
- fi
+# Control a node
+relx_nodetool() {
+ command="$1"; shift
+ "erts-$ERTS_VSN/bin/escript" "$ROOTDIR/bin/nodetool" "$NAME_TYPE" "$NAME" \
+ -setcookie "$COOKIE" "$command"
+}
+
+# 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/APP_VSN/vm.args, or else etc/vm.args
-if [ -e "$RELEASE_ROOT_DIR/vm.args" ]; then
- VMARGS_PATH=$RELEASE_ROOT_DIR/vm.args
- USE_DIR=$RELEASE_ROOT_DIR
-else
- USE_DIR=$REL_DIR
- if [ -e "$REL_DIR/vm.args" ]; then
- VMARGS_PATH="$REL_DIR/vm.args"
+if [ -z "$VMARGS_PATH" ]; then
+ if [ -e "$RELEASE_ROOT_DIR/vm.args" ]; then
+ VMARGS_PATH="$RELEASE_ROOT_DIR/vm.args"
+ USE_DIR="$RELEASE_ROOT_DIR"
else
- VMARGS_PATH="$REL_DIR/vm.args"
+ USE_DIR="$REL_DIR"
+ if [ -e "$REL_DIR/vm.args" ]; then
+ VMARGS_PATH="$REL_DIR/vm.args"
+ else
+ VMARGS_PATH="$REL_DIR/vm.args"
+ fi
fi
fi
-RUNNER_LOG_DIR=$RELEASE_ROOT_DIR/log
# Make sure log directory exists
-mkdir -p $RUNNER_LOG_DIR
+mkdir -p "$RUNNER_LOG_DIR"
# Use releases/VSN/sys.config if it exists otherwise use etc/app.config
-if [ -e "$USE_DIR/sys.config" ]; then
- CONFIG_PATH="$USE_DIR/sys.config"
-else
- if [ -e "$REL_DIR/sys.config" ]; then
- CONFIG_PATH="$REL_DIR/sys.config"
+if [ -z "$CONFIG_PATH" ]; then
+ if [ -e "$USE_DIR/sys.config" ]; then
+ CONFIG_PATH="$USE_DIR/sys.config"
else
- CONFIG_PATH="$REL_DIR/app.config"
+ if [ -e "$REL_DIR/sys.config" ]; then
+ CONFIG_PATH="$REL_DIR/sys.config"
+ else
+ CONFIG_PATH="$REL_DIR/app.config"
+ fi
fi
fi
# Extract the target node name from node.args
-NAME_ARG=`egrep '^-s?name' $VMARGS_PATH`
+NAME_ARG=$(egrep '^-s?name' "$VMARGS_PATH")
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
-REMSH_TYPE=`echo $NAME_ARG | awk '{print $1}'`
-REMSH_NAME=`echo $NAME_ARG | awk '{print $2}'`
-
-# Note the `date +%s`, used to allow multiple remsh to the same node transparently
-REMSH_NAME_ARG="$REMSH_TYPE remsh`date +%s`@`echo $REMSH_NAME | awk -F@ '{print $2}'`"
-REMSH_REMSH_ARG="-remsh $REMSH_NAME -boot start_clean"
+NAME_TYPE="$(echo "$NAME_ARG" | awk '{print $1}')"
+NAME="$(echo "$NAME_ARG" | awk '{print $2}')"
# Extract the target cookie
-COOKIE_ARG=`grep '^-setcookie' $VMARGS_PATH`
+COOKIE_ARG="$(grep '^-setcookie' "$VMARGS_PATH")"
if [ -z "$COOKIE_ARG" ]; then
echo "vm.args needs to have a -setcookie parameter."
exit 1
fi
-find_erts_dir
-find_sys_config
-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
-
-cd $ROOTDIR
+# Extract cookie name from COOKIE_ARG
+COOKIE="$(echo "$COOKIE_ARG" | awk '{print $2}')"
-# Setup remote shell command to control node
-REMSH="$BINDIR/erl $REMSH_NAME_ARG $REMSH_REMSH_ARG $COOKIE_ARG"
+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"
-# Setup command to control the node
-NODETOOL="$BINDIR/escript $ROOTDIR/bin/nodetool $NAME_ARG $COOKIE_ARG"
+cd "$ROOTDIR"
# Check the first argument for instructions
case "$1" in
@@ -120,37 +132,46 @@ case "$1" in
HEART_OPTION="start_boot"
;;
esac
- RUN_PARAM=$(printf "'%s' " "$@")
- HEART_COMMAND="$SCRIPT_DIR/bin/$REL_NAME $HEART_OPTION $RUN_PARAM"
+ 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="$@"
export HEART_COMMAND
- mkdir -p $PIPE_DIR
- $BINDIR/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RELEASE_ROOT_DIR/bin/$REL_NAME $START_OPTION $RUN_PARAM" 2>&1
+
+ mkdir -p "$PIPE_DIR"
+
+ "$BINDIR/run_erl" -daemon "$PIPE_DIR" "$RUNNER_LOG_DIR" \
+ "$(relx_start_command)"
;;
stop)
# Wait for the node to completely stop...
- case `uname -s` in
+ case $(uname -s) in
Linux|Darwin|FreeBSD|DragonFly|NetBSD|OpenBSD)
# PID COMMAND
- PID=`ps ax -o pid= -o command=|
- grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $1}'`
+ PID=$(ps ax -o pid= -o command=|
+ grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $1}')
;;
SunOS)
# PID COMMAND
- PID=`ps -ef -o pid= -o args=|
- grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $1}'`
+ PID=$(ps -ef -o pid= -o args=|
+ grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $1}')
;;
CYGWIN*)
# UID PID PPID TTY STIME COMMAND
- PID=`ps -efW|grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $2}'`
+ PID=$(ps -efw|grep "$SCRIPT_DIR/.*/[b]eam"|awk '{print $2}')
;;
esac
- $NODETOOL stop
- ES=$?
+ relx_nodetool "stop"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
- exit $ES
+ exit "$ES"
fi
- while `kill -0 $PID 2>/dev/null`;
+ while $(kill -0 "$PID" 2>/dev/null);
do
sleep 1
done
@@ -158,8 +179,8 @@ case "$1" in
restart)
## Restart the VM without exiting the process
- $NODETOOL restart
- ES=$?
+ relx_nodetool "restart"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
exit $ES
fi
@@ -167,8 +188,8 @@ case "$1" in
reboot)
## Restart the VM completely (uses heart to restart it)
- $NODETOOL reboot
- ES=$?
+ relx_nodetool "reboot"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
exit $ES
fi
@@ -176,8 +197,8 @@ case "$1" in
ping)
## See if the VM is alive
- $NODETOOL ping
- ES=$?
+ relx_nodetool "ping"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
exit $ES
fi
@@ -185,28 +206,28 @@ case "$1" in
attach)
# Make sure a node IS running
- RES=`$NODETOOL ping`
- ES=$?
+ RES="$(relx_nodetool "ping")"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
echo "Node is not running!"
exit $ES
fi
shift
- exec $BINDIR/to_erl $PIPE_DIR
+ exec "$BINDIR/to_erl" "$PIPE_DIR"
;;
remote_console)
# Make sure a node IS running
- RES=`$NODETOOL ping`
- ES=$?
+ RES="$(relx_nodetool "ping")"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
echo "Node is not running!"
exit $ES
fi
shift
- exec $REMSH
+ relx_rem_sh
;;
upgrade|downgrade|install)
@@ -218,17 +239,15 @@ case "$1" in
fi
# Make sure a node IS running
- RES=`$NODETOOL ping`
- ES=$?
+ RES="$(relx_nodetool "ping")"
+ ES="$?"
if [ "$ES" -ne 0 ]; then
echo "Node is not running!"
exit $ES
fi
- node_name=`echo $NAME_ARG | awk '{print $2}'`
- erlang_cookie=`echo $COOKIE_ARG | awk '{print $2}'`
-
- exec $BINDIR/escript $ROOTDIR/bin/install_upgrade.escript $REL_NAME $node_name $erlang_cookie $2
+ exec "$BINDIR/escript" "$ROOTDIR/bin/install_upgrade.escript" \
+ "$REL_NAME" "$NAME" "$COOKIE" "$2"
;;
console|console_clean|console_boot)
@@ -236,8 +255,16 @@ case "$1" in
# 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) [ -f $REL_DIR/$REL_NAME.boot ] && BOOTFILE=$REL_DIR/$REL_NAME || BOOTFILE=$REL_DIR/start ;;
- console_clean) BOOTFILE=$ROOTDIR/bin/start_clean ;;
+ console)
+ if [ "$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"
@@ -245,43 +272,62 @@ case "$1" in
;;
esac
# Setup beam-required vars
- EMU=beam
- PROGNAME=`echo $0 | sed 's/.*\\///'`
- CMD="$BINDIR/erlexec -boot $BOOTFILE -env ERL_LIBS $REL_DIR/lib -config $CONFIG_PATH -args_file $VMARGS_PATH"
+ 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" \
+ -env ERL_LIBS "$REL_DIR/lib" -config "$CONFIG_PATH" \
+ -args_file "$VMARGS_PATH"
+
# Dump environment info for logging purposes
- echo "Exec: $CMD" -- ${1+"$@"}
+ echo "Exec: $@ -- ${1+$ARGS}"
echo "Root: $ROOTDIR"
# Log the startup
+ echo "$RELEASE_ROOT_DIR"
logger -t "$REL_NAME[$$]" "Starting up"
# Start the VM
- exec $CMD -- ${1+"$@"}
+ 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
+ [ -f "$REL_DIR/$REL_NAME.boot" ] && BOOTFILE="$REL_NAME" || BOOTFILE=start
FOREGROUNDOPTIONS="-noinput +Bd"
# Setup beam-required vars
EMU=beam
- PROGNAME=`echo $0 | sed 's/.*\\///'`
- CMD="$BINDIR/erlexec $FOREGROUNDOPTIONS -boot $REL_DIR/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH"
+ 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 embedded -config "$CONFIG_PATH" \
+ -args_file "$VMARGS_PATH"
+
# Dump environment info for logging purposes
- echo "Exec: $CMD" -- ${1+"$@"}
+ echo "Exec: $@" -- "${1+$ARGS}"
echo "Root: $ROOTDIR"
# Start the VM
- exec $CMD -- ${1+"$@"}
+ exec "$@" -- "${1+$ARGS}"
;;
*)
echo "Usage: $REL_NAME {start|start_boot <file>|foreground|stop|restart|reboot|ping|console|console_clean|console_boot <file>|attach|remote_console|upgrade}"
diff --git a/priv/templates/extended_bin_windows.dtl b/priv/templates/extended_bin_windows.dtl
new file mode 100644
index 0000000..1f94675
--- /dev/null
+++ b/priv/templates/extended_bin_windows.dtl
@@ -0,0 +1,176 @@
+:: This batch file handles managing an Erlang node as a Windows service.
+::
+:: Commands provided:
+::
+:: * install - install the release as a Windows service
+:: * start - start the service and Erlang node
+:: * stop - stop the service and Erlang node
+:: * restart - run the stop command and start command
+:: * uninstall - uninstall the service and kill a running node
+:: * ping - check if the node is running
+:: * console - start the Erlang release in a `werl` Windows shell
+:: * attach - connect to a running node and open an interactive console
+:: * list - display a listing of installed Erlang services
+:: * usage - display available commands
+
+:: Set variables that describe the release
+@set rel_name={{ rel_name }}
+@set rel_vsn={{ rel_vsn }}
+@set erts_vsn={{ erts_vsn }}
+@set erl_opts={{ erl_opts }}
+
+:: Discover the release root directory from the directory
+:: of this script
+@set script_dir=%~dp0
+@for %%A in ("%script_dir%\..") do @(
+ @set release_root_dir=%%~fA
+)
+@set rel_dir=%release_root_dir%\releases\%rel_vsn%
+
+@call :find_erts_dir
+@call :find_sys_config
+
+@set bindir=%erts_dir%\bin
+@set vm_args=%rel_dir%\vm.args
+@set progname=erl.exe
+@set boot_script=%rel_dir%\%rel_name%
+@set clean_boot_script=%release_root_dir%\bin\start_clean
+@set erlsrv="%bindir%\erlsrv.exe"
+@set epmd="%bindir%\epmd.exe"
+@set escript="%bindir%\escript.exe"
+@set werl="%bindir%\werl.exe"
+@set nodetool="%release_root_dir%\bin\nodetool"
+
+:: Extract node type and name from vm.args
+@for /f "usebackq tokens=1-2" %%I in (`findstr /b "\-name \-sname" "%vm_args%"`) do @(
+ @set node_type=%%I
+ @set node_name=%%J
+)
+
+:: Extract cookie from vm.args
+@for /f "usebackq tokens=1-2" %%I in (`findstr /b \-setcookie "%vm_args%"`) do @(
+ @set cookie=%%J
+)
+
+:: Write the erl.ini file to set up paths relative to this script
+@call :write_ini
+
+:: 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
+)
+
+@if "%1"=="install" @goto install
+@if "%1"=="uninstall" @goto uninstall
+@if "%1"=="start" @goto start
+@if "%1"=="stop" @goto stop
+@if "%1"=="restart" @call :stop && @goto start
+@if "%1"=="console" @goto console
+@if "%1"=="ping" @goto ping
+@if "%1"=="list" @goto list
+@if "%1"=="attach" @goto attach
+@if "%1"=="" @goto usage
+@echo Unknown command: "%1"
+
+@goto :eof
+
+:: Find the ERTS dir
+:find_erts_dir
+@set possible_erts_dir=%release_root_dir%\erts-%erts_vsn%
+@if exist "%possible_erts_dir%" (
+ @call :set_erts_dir_from_default
+) else (
+ @call :set_erts_dir_from_erl
+)
+@goto :eof
+
+:: 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%
+@goto :eof
+
+:: Set the ERTS dir from erl
+:set_erts_dir_from_erl
+@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
+@for /f %%i in ('%%dir_cmd%%') do @(
+ @set erl_root=%%i
+)
+@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
+@if exist %possible_sys% (
+ @set sys_config=-config %possible_sys%
+)
+@goto :eof
+
+:: Write the erl.ini file
+:write_ini
+@set erl_ini=%erts_dir%\bin\erl.ini
+@set converted_bindir=%bindir:\=\\%
+@set converted_rootdir=%rootdir:\=\\%
+@echo [erlang] > "%erl_ini%"
+@echo Bindir=%converted_bindir% >> "%erl_ini%"
+@echo Progname=%progname% >> "%erl_ini%"
+@echo Rootdir=%converted_rootdir% >> "%erl_ini%"
+@goto :eof
+
+:: Display usage information
+:usage
+@echo usage: %~n0 ^(install^|uninstall^|start^|stop^|restart^|console^|ping^|list^|attach^)
+@goto :eof
+
+:: Install the release as a Windows service
+:install
+@set args=%erl_opts% -setcookie %cookie% ++ -rootdir \"%rootdir%\"
+@set start_erl=%erts_dir%\bin\start_erl.exe
+@set description=Erlang node %node_name% in %rootdir%
+@%erlsrv% add %rel_name% %node_type% "%node_name%" -c "%description%" ^
+ -w "%rootdir%" -m "%start_erl%" -args "%args%" ^
+ -stopaction "init:stop()."
+@goto :eof
+
+:: Uninstall the Windows service
+:uninstall
+@%erlsrv% remove %rel_name%
+@%epmd% -kill
+@goto :eof
+
+:: Start the Windows service
+:start
+@%erlsrv% start %rel_name%
+@goto :eof
+
+:: Stop the Windows service
+:stop
+@%erlsrv% stop %rel_name%
+@goto :eof
+
+:: Start a console
+:console
+@start "%rel_name% console" %werl% -boot "%boot_script%" "%sys_config%" ^
+ -args_file "%vm_args%" %node_type% %node_name%
+@goto :eof
+
+:: Ping the running node
+:ping
+@%escript% %nodetool% ping %node_type% "%node_name%" -setcookie "%cookie%"
+@goto :eof
+
+:: List installed Erlang services
+:list
+@%erlsrv% list %rel_name%
+@goto :eof
+
+:: Attach to a running node
+:attach
+@start "%node_name% attach" %werl% -boot "%clean_boot_script%" ^
+ -remsh %node_name% %node_type% console -setcookie %cookie%
+@goto :eof
diff --git a/priv/templates/nodetool.dtl b/priv/templates/nodetool.dtl
index a6faad5..80f1965 100644
--- a/priv/templates/nodetool.dtl
+++ b/priv/templates/nodetool.dtl
@@ -75,7 +75,7 @@ process_args([Arg | Rest], Acc, Opts) ->
start_epmd() ->
- [] = os:cmd(epmd_path() ++ " -daemon"),
+ [] = os:cmd("\"" ++ epmd_path() ++ "\" -daemon"),
ok.
epmd_path() ->