Packages
nerves_runtime
0.9.3
0.13.13
0.13.12
0.13.11
retired
0.13.10
0.13.9
0.13.8
0.13.7
0.13.6
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.0
0.11.10
0.11.9
0.11.8
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.3
0.10.2
0.10.1
0.10.0
retired
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.0
0.7.0
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.3
0.5.2
0.5.1
0.5.0
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.1
0.3.0
0.2.0
0.1.2
0.1.1
0.1.0
Small, general runtime utilities for Nerves devices
Current section
Files
Jump to
Current section
Files
lib/nerves_runtime.ex
defmodule Nerves.Runtime do
require Logger
alias Nerves.Runtime.OutputLogger
# This is provided by all of the official Nerves system images
@revert_fw_path "/usr/share/fwup/revert.fw"
@typedoc """
Options for `Nerves.Runtime.revert/1`.
* `:reboot` - Call `Nerves.Runtime.reboot/0` after reverting (defaults to `true`)
"""
@type revert_options :: {:reboot, boolean()}
@moduledoc """
Nerves.Runtime contains functions useful for almost all Nerves-based devices.
"""
@doc """
Reboot the device and gracefully shutdown the Erlang VM.
This calls `:init.stop/0` internally. If `:init.stop/0` takes longer than the
`erlinit.config`'s `--graceful-powerdown` setting (likely 10 seconds) then
the system will be hard rebooted.
"""
@spec reboot() :: no_return()
def reboot(), do: logged_shutdown("reboot")
@doc """
Power off the device.
This calls `:init.stop/0` internally. If `:init.stop/0` takes longer than the
`erlinit.config`'s `--graceful-powerdown` setting (likely 10 seconds) then
the system will be hard rebooted.
"""
@spec poweroff() :: no_return()
def poweroff(), do: logged_shutdown("poweroff")
@doc """
Halt the device (meaning hang, not power off, nor reboot).
Note: this is different than :erlang.halt(), which exits BEAM, and may end up
rebooting the device if `erlinit.config` settings allow reboot on exit.
"""
@spec halt() :: no_return()
def halt(), do: logged_shutdown("halt")
@doc """
Revert the device to running the previous firmware.
This requires a specially constructed fw file.
"""
@spec revert([revert_options]) :: :ok | {:error, reason :: any} | no_return()
def revert(opts \\ []) do
reboot? = if opts[:reboot] != nil, do: opts[:reboot], else: true
if File.exists?(@revert_fw_path) do
{_, 0} = cmd("fwup", [@revert_fw_path, "-t", "revert", "-d", "/dev/rootdisk0"], :info)
if reboot? do
reboot()
else
:ok
end
else
{:error, "Unable to locate revert firmware at path: #{@revert_fw_path}"}
end
end
@doc """
Run system command and log output into logger.
"""
@spec cmd(binary(), [binary()], :debug | :info | :warn | :error | :return) ::
{Collectable.t(), exit_status :: non_neg_integer()}
def cmd(cmd, params, :return), do: System.cmd(cmd, params, stderr_to_stdout: true)
def cmd(cmd, params, out),
do: System.cmd(cmd, params, into: OutputLogger.new(out), stderr_to_stdout: true)
@doc """
Return whether the application was built for either the host or the target
"""
@spec target() :: String.t()
def target() do
target = Application.get_env(:nerves_runtime, :target)
if target == "host", do: "host", else: "target"
end
# private helpers
@spec logged_shutdown(String.t()) :: no_return()
defp logged_shutdown(cmd) do
try do
_ = Logger.info("#{__MODULE__} : device told to #{cmd}")
# Invoke the appropriate command to tell erlinit that a shutdown of the
# Erlang VM is imminent. Once this returns, the Erlang has about 10
# seconds to exit unless `--graceful-powerdown` is used in the
# `erlinit.config` to modify the timeout.
{_, 0} = cmd(cmd, [], :info)
# Start a graceful shutdown
:ok = :init.stop()
# `:init.stop()` is asynchronous, so sleep longer than it takes to avoid
# returning.
Process.sleep(60_000)
after
# If anything unexpected happens, call :erlang.halt() to avoid getting
# stuck in a state where the application thinks it's done.
:erlang.halt()
end
end
end