Current section

Files

Jump to
nerves_runtime lib nerves_runtime kv.ex
Raw

lib/nerves_runtime/kv.ex

defmodule Nerves.Runtime.KV do
@moduledoc """
Key Value Storage for firmware vairables provided by fwup
KV provides access to metadata variables set by fwup.
It can be used to obtain information such as the active
firmware slot, where the application data partition
is located, etc.
Values are stored in two ways.
* Values that do not pertain to a specific firmware slot
For example:
`"nerves_fw_active" => "a"`
* Values that pertain to a specific firmware slot
For Example:
`"a.nerves_fw_author" => "The Nerves Team"`
You can find values for just the active firmware slot by
using get_active and get_all_active. The result of these
functions will trim the firmware slot (`"a."` or `"b."`)
from the leading characters of the keys returned.
"""
use GenServer
require Logger
@doc """
Start the KV store server
"""
def start_link(kv \\ "") do
GenServer.start_link(__MODULE__, kv, name: __MODULE__)
end
@doc """
Get the key for only the active firmware slot
"""
def get_active(key) do
GenServer.call(__MODULE__, {:get_active, key})
end
@doc """
Get the key regardless of firmware slot
"""
def get(key) do
GenServer.call(__MODULE__, {:get, key})
end
@doc """
Get all key value pairs for only the active firmware slot
"""
def get_all_active() do
GenServer.call(__MODULE__, :get_all_active)
end
@doc """
Get all keys regardless of firmware slot
"""
def get_all() do
GenServer.call(__MODULE__, :get_all)
end
# GenServer API
def init(_kv) do
exec = System.find_executable("fw_printenv")
s = load_kv(exec)
{:ok, s}
end
def handle_call({:get_active, key}, _from, s) do
{:reply, active(key, s), s}
end
def handle_call({:get, key}, _from, s) do
{:reply, Map.get(s, key), s}
end
def handle_call(:get_all_active, _from, s) do
active = active(s) <> "."
reply = filter_trim_active(s, active)
{:reply, reply, s}
end
def handle_call(:get_all, _from, s) do
{:reply, s, s}
end
defp load_kv(nil), do: %{}
defp load_kv(exec) do
case System.cmd(exec, []) do
{result, 0} ->
parse_kv(result)
_ ->
Logger.warn("#{inspect(__MODULE__)} could not find executable fw_printenv")
%{}
end
end
def parse_kv(str) do
String.split(str, "\n")
|> Enum.map(&String.split(&1, "="))
|> Enum.reduce(%{}, fn
[k, v], acc ->
Map.put(acc, k, v)
_, acc ->
acc
end)
end
defp active(s), do: Map.get(s, "nerves_fw_active", "")
defp active(key, s) do
Map.get(s, "#{active(s)}.#{key}")
end
defp filter_trim_active(s, active) do
Enum.filter(s, fn {k, _} ->
String.starts_with?(k, active)
end)
|> Enum.map(fn {k, v} -> {String.replace_leading(k, active, ""), v} end)
|> Enum.into(%{})
end
end