Packages
relx
4.6.0
4.10.0
4.9.0
4.8.0
4.7.0
4.6.0
4.5.0
4.4.0
4.3.0
4.2.0
4.1.0
4.0.2
4.0.1
4.0.0
4.0.0-rc2
3.33.0
3.32.1
3.32.0
3.31.0
3.30.0
3.29.0
3.28.0
3.27.0
3.26.0
3.25.0
3.24.4
3.24.3
3.24.2
3.24.1
3.23.1
3.23.0
3.22.4
3.22.3
retired
3.22.2
3.22.1
retired
3.22.0
3.21.1
3.21.0
3.20.0
3.19.0
3.18.0
3.17.0
3.16.0
3.15.0
3.14.0
3.13.0
3.12.0
3.11.0
3.10.0
3.9.0
3.8.0
3.7.1
3.7.0
3.6.0
3.5.0
3.4.0
3.3.2
3.3.1
3.3.0
3.2.0
3.1.0
3.0.0
2.1.1
2.1.0
Release assembler for Erlang/OTP Releases
Current section
Files
Jump to
Current section
Files
priv/templates/extended_bin_windows_ps
#! /usr/bin/pwsh
# This script 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
# * remote_console - alias for attach
# * list - display a listing of installed Erlang services
# * usage - display available commands
param (
[string] $Command
)
# Terminate on error
$ErrorActionPreference = "Stop"
# Set variables that describe the release
$rel_name = '{{ rel_name }}'
$rel_vsn = '{{ rel_vsn }}'
$erts_vsn = '{{ erts_vsn }}'
$erl_opts = '{{ erl_opts }}'
# export these to match mix release environment variables
$RELEASE_NAME = '{{ rel_name }}'
$RELEASE_VERSION = '{{ rel_vsn }}'
$RELEASE_PROG = $MyInvocation.MyCommand.Name
# Ensure we have PSScriptRoot
if (!(Test-Path variable:global:PSScriptRoot)) {
# Support powershell 2.0
$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition
}
# Ensure we have PSCommandPath
if (!(Test-Path variable:global:PSCommandPath)) {
# Support powershell 2.0
$PSCommandPath = $MyInvocation.PSCommandPath
}
$PSCriptName = Split-Path -Leaf -Path $PSCommandPath
# Import psutil helper functions
. $PSScriptRoot\psutil.ps1
# Set the root release directory based on the location of this batch file
$rootdir = Split-Path -Parent -Path $PSScriptRoot
$rel_dir = "$rootdir\releases\$rel_vsn"
$erts_root, $erts_dir = Find-ERTS -RelRoot $rootdir -Vsn $erts_vsn
$sys_config = Find-FormatConfig -RelDir $rel_dir -File 'sys.config'
$vm_args = Find-FormatConfig -RelDir $rel_dir -File 'vm.args'
$boot_script = Find-BootScript -RelDir $rel_dir -RelName $rel_name
$service_name = "${rel_name}_${rel_vsn}"
$nodetool = "$rootdir\bin\nodetool"
$bindir = "$erts_dir\bin"
$werl = "$bindir\werl.exe"
$erl = "$bindir\erl.exe"
$erlsrv = "$bindir\erlsrv.exe"
$escript = "$bindir\escript.exe"
# Added in OTP-23
$erl_call = "$bindir\erl_call.exe"
# if RELX_RPC_TIMEOUT is set then use that
# otherwise check for NODETOOL_TIMEOUT and convert to seconds
if (Test-Path env:RELX_RPC_TIMEOUT) {
$relx_rpc_timeout = $env:RELX_RPC_TIMEOUT
}
elseif (Test-Path env:NODETOOL_TIMEOUT) {
# will exit the script if NODETOOL_TIMEOUT isn't a number
$relx_rpc_timeout = $env:NODETOOL_TIMEOUT / 1000
}
else {
$relx_rpc_timeout = 60
}
$extensions = @("{{ extensions }}" -split '[|]' | Where-Object { $_ -ne 'undefined' })
# Run extension script
function RunExtension() {
param(
[string]$Name
)
# Manifest xxx_extension as local variable
"{{ extension_declarations }}" -split ";" | ForEach-Object { Invoke-Expression "`$$_" }
# Manifest value of xxx_extension as $script
$script = Invoke-Expression "`$${Name}_extension"
# Execute
& "$PSScriptRoot\$script" @args
}
# Extract node type and name from vm.args
$node_type, $node_name, $hostname = Get-NodeAndHost $vm_args
# Extract the target cookie
# Do this before relx_get_nodename so we can use it and not create a ~/.erlang.cookie
$cookie = Get-Cookie $vm_args
# Collect additional (allowed) VM args into erl_opts for erlsrv/start_erl
$opts = Get-Content $vm_args `
| Select-String '^(#|-name|-sname|-setcookie|-noinput|-noshell)' -NotMatch `
| ForEach-Object { $_.line } `
| Where-Object { $_.length -gt 0 }
$erl_opts += "$opts"
# If a start.boot file is not present, copy one from the named .boot file
if (!(Test-Path "$rel_dir\start.boot")) {
Copy-Item "$rel_dir\$rel_name.boot" "$rel_dir\start.boot" | Out-Null
}
# List of supported commands, help, etc
$commands = @{
'help' = @{
'ShortHelp' = 'Show help';
'LongHelp' = 'Shows help for <command>';
'Usage' = '<command>';
'Invoke' = 'Usage';
'PassArgs' = $true
` };
'install' = @{
'ShortHelp' = 'Install the service';
'LongHelp' = 'Installs release <version> as a windows service';
'Usage' = '<version>';
'Invoke' = 'Install';
'PassArgs' = $true
` };
'uninstall' = @{
'ShortHelp' = 'Uninstall the service';
'LongHelp' = 'Uninstalls release <version> as a windows service';
'Usage' = '<version>';
'Invoke' = 'Uninstall';
'PassArgs' = $true
};
'start' = @{
'ShortHelp' = 'Start the service';
'LongHelp' = 'Starts the installed windows service';
'Usage' = '';
'Invoke' = 'Start-SVC';
'Aliases' = @('daemon')
};
'stop' = @{
'ShortHelp' = 'Stop the service';
'LongHelp' = 'Stops the installed windows service';
'Usage' = '';
'Invoke' = 'Stop-SVC'
};
'restart' = @{
'ShortHelp' = 'Restart the service';
'LongHelp' = 'Restarts the installed windows service';
'Usage' = '';
'Invoke' = @('Stop-SVC','Start-SVC')
};
'upgrade' = @{
'ShortHelp' = 'Upgrade the running service';
'LongHelp' = 'Upgrades the service to release <version>';
'Usage' = '<version> [--no-permanent]';
'Invoke' = 'Relup';
'PassArgs' = $true
};
'downgrade' = @{
'ShortHelp' = 'Downgrade the running service';
'LongHelp' = 'Downgrades the service to release <version>';
'Usage' = '<version>';
'Invoke' = 'Relup';
'PassArgs' = $true
};
'console' = @{
'ShortHelp' = 'Start the release with an interactive shell';
'LongHelp' = 'Starts release VERSION with an interactive shell';
'Usage' = '';
'Invoke' = 'Console'
};
'foreground' = @{
'ShortHelp' = 'Start the release with a non-interactive shell';
'LongHelp' = 'Starts the release with a non-interactive shell';
'Usage' = '';
'Invoke' = 'Foreground'
};
'ping' = @{
'ShortHelp' = 'Print pong if the node is alive';
'LongHelp' = 'Checks if the service (or console) is running';
'Usage' = '';
'Invoke' = 'Ping'
};
'list' = @{
'ShortHelp' = 'List installed Erlang services';
'LongHelp' = 'Lists all Erlang services installed on this machine';
'Usage' = '';
'Invoke' = 'List'
};
'attach' = @{
'ShortHelp' = 'Connect remote shell to running node';
'LongHelp' = 'Connects to running service or console shell';
'Usage' = '';
'Invoke' = 'Attach';
'Aliases' = @('remote_console', 'remote', 'remsh', 'daemon_attach')
};
'rpc' = @{
'ShortHelp' = 'Applies the specified function and returns the result';
'LongHelp' = "Applies the specified function and returns the result.`n" `
+ "Mod must be specified. However, start and [] are assumed`n" `
+ "for unspecified Fun and Args, respectively. Args is to`n" `
+ "be in the same format as for erlang:apply/3 in ERTS."
'Usage' = '<mod> [<func> [<args>]]';
'Invoke' = @('PingOrExit', 'Rpc');
'PassArgs' = $true
};
'eval' = @{
'ShortHelp' = 'Executes a sequence of Erlang expressions';
'LongHelp' = 'Executes a sequence of Erlang expressions, separated by`n' `
+ 'comma (,) and ended with a full stop (.)'
'Usage' = '[<exprs>]';
'Invoke' = @('PingOrExit', 'Eval');
'PassArgs' = $true
};
'versions' = @{
'ShortHelp' = 'Print versions of the release available';
'LongHelp' = 'Print versions of the release available'
'Usage' = '';
'Invoke' = 'Versions'
}
}
# Build easy aliases
$aliases = @{}
$commands.GetEnumerator() |
Where-Object { $_.Value.ContainsKey('Aliases') } |
ForEach-Object { $target = $_.Key; $_.Value.Aliases | Foreach-Object { $aliases += @{ $_ = $target } }; }
# Output usage
function Usage() {
param(
[string]$Command
)
switch ($Command) {
{$commands.ContainsKey($Command)} {
$cmd = $commands.item($_)
"Usage: $PSCriptName $Command $($cmd.Usage)"
""
"$($cmd.LongHelp)"
""
}
{$aliases.ContainsKey($Command)} {
$cmd = $commands.item($aliases.item($_))
"Usage: $PSCriptName $Command $($cmd.Usage)"
""
"$($cmd.LongHelp)"
""
}
{$extensions -contains $_} { RunExtension $_ help }
Default {
"Usage: $($MyInvocation.MyCommand.Name) <command> <args>"
"Commands:"
$commands.keys | Sort-Object | ForEach-Object {
@{"$_" = $commands.item($_).ShortHelp }
} | Format-Table -HideTableHeaders
}
}
}
# Install the service
function Install() {
param(
[string]$Version
)
if (![string]::IsNullOrWhiteSpace($Version)) {
# relup and reldown
Relup $Version
return
}
# Install the service
$args = "$erl_opts -setcookie $cookie ++ -rootdir `"`"$erts_root`"`" -reldir `"`"$rootdir\releases`"`""
$start_erl = "$erts_dir\bin\start_erl.exe"
$description = "Erlang node ${node_name}${hostname} in $rootdir"
$params = @('add', $service_name, $node_type, "${node_name}${hostname}", `
'-c', $description, `
'-w', $rootdir, `
'-m', $start_erl, `
#'-d', 'reuse', ` # Add this to debug startup
'-e', "ERL_LIBS=$rootdir\lib", `
'-stopaction', 'init:stop().', `
'-args', $args)
& $erlsrv $params
}
# Uninstall the Windows service
function Uninstall() {
param(
[string]$Version
)
if (![string]::IsNullOrWhiteSpace($Version)) {
# relup and reldown
Relup $Version
return
}
# Uninstall the Windows service
& $erlsrv remove $service_name
}
# Start the Windows service
function Start-SVC() {
& $erlsrv start $service_name
}
# Stop the Windows service
function Stop-SVC() {
& $erlsrv stop $service_name
}
# Relup and reldown
function Relup() {
param(
[string]$Version
)
PingOrExit
$params = @("$rootdir/bin/install_upgrade.escript", 'install', "{'$rel_name', `"$node_type`", '${node_name}${hostname}', '$cookie'}", $Version)
& $escript $params @args
}
# Start a console
function Console() {
# Set the ERL_LIBS environment variable
$env:ERL_LIBS = "$rootdir\lib"
# Start the release in an `erl` shell
$params = @($erl_opts, '-boot', $boot_script)
if (![string]::IsNullOrEmpty($sys_config)) { $params += '-config', $sys_config }
if (![string]::IsNullOrEmpty($vm_args)) { $params += '-args_file', $vm_args }
& $werl $params
}
# Start a non-interactive console
function Foreground() {
# Set the ERL_LIBS environment variable
$env:ERL_LIBS = "$rootdir\lib"
# Start the release in an `erl` shell
$params = @($erl_opts, '-boot', $boot_script, '-noinput', '+Bd')
if (![string]::IsNullOrEmpty($sys_config)) { $params += '-config', $sys_config }
if (![string]::IsNullOrEmpty($vm_args)) { $params += '-args_file', $vm_args }
& $erl $params
}
# Execute erl_call.exe
function ErlCall()
{
param(
[string]$Type
)
$params = @($node_type, $node_name, '-R', '-c', $cookie, '-timeout', $relx_rpc_timeout)
switch($Type) {
'-a' { & $erl_call $params '-a' "`"$args`"" }
'-e' { "$args" | & $erl_call $params '-e' }
Default {
Write-Error "Unsupported erlcall ($_)"
}
}
}
# Execute escript.exe nodetool
function Nodetool()
{
$params = @($node_type, $node_name, '-setcookie', $cookie)
& $escript $nodetool $params @args
}
# Execute an RPC call
function Rpc()
{
if (Test-Path $erl_call) {
ErlCall '-a' @args
}
else {
Nodetool rpc @args
}
}
function Eval()
{
if (Test-Path $erl_call) {
ErlCall '-e' @args
}
else {
Nodetool eval @args
}
}
# Ping node and error (exit) if not running
function PingOrExit() {
$result = (Rpc erlang is_alive)
if ($result -ne "true") {
Write-Error "Node is not running! ($result)"
}
}
# Ping the running node
function Ping() {
PingOrExit
"pong"
}
# List installed Erlang services
function List() {
& $erlsrv 'list' $service_name
}
# Attach to a running node
function Attach() {
PingOrExit
$params = @('-boot', "$rel_dir\start_clean", `
'-remsh', "${node_name}${hostname}", `
$node_type, "console_${node_name}_$(Get-Random -maximum 99999)", `
'-setcookie', $cookie)
& $werl $params
}
# Print versions of the release available
function Versions()
{
PingOrExit
$params = @("$rootdir\bin\install_upgrade.escript", 'versions', "{'$rel_name', `"$node_type`", '${node_name}${hostname}', '$cookie'}")
& $escript $params @args
}
# Run a command
function RunCommand()
{
param(
[string]$Name
)
$cmd = $commands.item($Name)
if ($cmd.PassArgs) { $pass_args = $args }
$cmd.Invoke | ForEach-Object {
& $_ @pass_args
}
}
# Finally, here is where we run the command
switch ($Command)
{
"" { Usage }
{$commands.ContainsKey($_)} {
RunCommand $_ @args
}
{$aliases.ContainsKey($Command)} {
RunCommand $aliases.item($_) @args
}
{$extensions -contains $_} {
RunExtension $_ @args
}
Default {
Write-Warning "Unknown command: `"$_`""
Usage
}
}