View Source Nerves.Runtime.KV (nerves_runtime v0.13.7)
Key-Value storage for firmware variables
KV provides functionality to read and modify firmware metadata. The firmware metadata contains information such as the active firmware slot, where the application data partition is located, etc. It may also contain board provisioning information like factory calibration so long as it is not too large.
The metadata store is a simple key-value store where both keys and values are ASCII strings. Writes are expected to be infrequent and primarily done on firmware updates. If you expect frequent writes, it is highly recommended to persist the data elsewhere.
The default KV backend loads and stores metadata to a U-Boot-formatted environment block. This doesn't mean that the device needs to run U-Boot. It just happens to be a convenient data format that's well supported.
There are some expectations on keys. See the Nerves.Runtime README.md for naming conventions and expected key names. These are not enforced
To change the KV backend, implement the Nerves.Runtime.KVBackend
behaviour and
configure the application environment in your
program's config.exs
like the following:
config :nerves_runtime, kv_backend: {MyKeyValueBackend, options}
Examples
Getting all firmware metadata:
iex> Nerves.Runtime.KV.get_all()
%{
"a.nerves_fw_application_part0_devpath" => "/dev/mmcblk0p3",
"a.nerves_fw_application_part0_fstype" => "ext4",
"a.nerves_fw_application_part0_target" => "/root",
"a.nerves_fw_architecture" => "arm",
"a.nerves_fw_author" => "The Nerves Team",
"a.nerves_fw_description" => "",
"a.nerves_fw_misc" => "",
"a.nerves_fw_platform" => "rpi0",
"a.nerves_fw_product" => "test_app",
"a.nerves_fw_uuid" => "d9492bdb-94de-5288-425e-2de6928ef99c",
"a.nerves_fw_vcs_identifier" => "",
"a.nerves_fw_version" => "0.1.0",
"b.nerves_fw_application_part0_devpath" => "/dev/mmcblk0p3",
"b.nerves_fw_application_part0_fstype" => "ext4",
"b.nerves_fw_application_part0_target" => "/root",
"b.nerves_fw_architecture" => "arm",
"b.nerves_fw_author" => "The Nerves Team",
"b.nerves_fw_description" => "",
"b.nerves_fw_misc" => "",
"b.nerves_fw_platform" => "rpi0",
"b.nerves_fw_product" => "test_app",
"b.nerves_fw_uuid" => "4e08ad59-fa3c-5498-4a58-179b43cc1a25",
"b.nerves_fw_vcs_identifier" => "",
"b.nerves_fw_version" => "0.1.1",
"nerves_fw_active" => "b",
"nerves_fw_devpath" => "/dev/mmcblk0",
"nerves_serial_number" => ""
}
Parts of the firmware metadata are global, while others pertain to a
specific firmware slot. This is indicated by the key - data which describes
firmware of a specific slot have keys prefixed with the name of the
firmware slot. In the above example, "nerves_fw_active"
and
"nerves_serial_number"
are global, while "a.nerves_fw_version"
and
"b.nerves_fw_version"
apply to the "a" and "b" firmware slots,
respectively.
It is also possible to get firmware metadata that only pertains to the currently active firmware slot:
iex> Nerves.Runtime.KV.get_all_active()
%{
"nerves_fw_application_part0_devpath" => "/dev/mmcblk0p3",
"nerves_fw_application_part0_fstype" => "ext4",
"nerves_fw_application_part0_target" => "/root",
"nerves_fw_architecture" => "arm",
"nerves_fw_author" => "The Nerves Team",
"nerves_fw_description" => "",
"nerves_fw_misc" => "",
"nerves_fw_platform" => "rpi0",
"nerves_fw_product" => "test_app",
"nerves_fw_uuid" => "4e08ad59-fa3c-5498-4a58-179b43cc1a25",
"nerves_fw_vcs_identifier" => "",
"nerves_fw_version" => "0.1.1"
}
Note that get_all_active/0
strips out the a.
and b.
prefixes.
Further, the two functions get/1
and get_active/1
allow you to get a
specific key from the firmware metadata. get/1
requires specifying the
entire key name, while get_active/1
will prepend the slot prefix for you:
iex> Nerves.Runtime.KV.get("nerves_fw_active")
"b"
iex> Nerves.Runtime.KV.get("b.nerves_fw_uuid")
"4e08ad59-fa3c-5498-4a58-179b43cc1a25"
iex> Nerves.Runtime.KV.get_active("nerves_fw_uuid")
"4e08ad59-fa3c-5498-4a58-179b43cc1a25"
Aside from reading values from the KV store, it is also possible to write new values to the firmware metadata. New values may either have unique keys, in which case they will be added to the firmware metadata, or re-use a key, in which case they will overwrite the current value with that key:
iex> :ok = Nerves.Runtime.KV.put("my_firmware_key", "my_value")
iex> :ok = Nerves.Runtime.KV.put("nerves_serial_number", "my_new_serial_number")
iex> Nerves.Runtime.KV.get("my_firmware_key")
"my_value"
iex> Nerves.Runtime.KV.get("nerves_serial_number")
"my_new_serial_number"
It is possible to write a collection of values at once, in order to minimize number of writes:
iex> :ok = Nerves.Runtime.KV.put(%{"one_key" => "one_val", "two_key" => "two_val"})
iex> Nerves.Runtime.KV.get("one_key")
"one_val"
Lastly, put_active/1
and put_active/2
allow you to write firmware metadata to the
currently active firmware slot without specifying the slot prefix yourself:
iex> :ok = Nerves.Runtime.KV.put_active("nerves_fw_misc", "Nerves is awesome")
iex> Nerves.Runtime.KV.get_active("nerves_fw_misc")
"Nerves is awesome"
Summary
Types
The KV store is a string -> string map
Functions
Returns a specification to start this module under a supervisor.
Get the key regardless of firmware slot
Get the key for only the active firmware slot
Get all keys regardless of firmware slot
Get all key value pairs for only the active firmware slot
Write a collection of key-value pairs to the firmware metadata
Write a key-value pair to the firmware metadata
Write a collection of key-value pairs to the active firmware slot
Write a key-value pair to the active firmware slot
Start the KV store server
Types
The KV store is a string -> string map
Since the KV store is backed by things like the U-Boot environment blocks,
the keys and values can't be just any string. For example, characters with
the value 0
(i.e., NULL
) are disallowed. The =
sign is also disallowed
in keys. Values may have embedded new lines. In general, it's recommended to
stick with ASCII values to avoid causing trouble when working with C programs
which also access the variables.
Functions
Returns a specification to start this module under a supervisor.
See Supervisor
.
Get the key regardless of firmware slot
Get the key for only the active firmware slot
@spec get_all() :: string_map()
Get all keys regardless of firmware slot
@spec get_all_active() :: string_map()
Get all key value pairs for only the active firmware slot
@spec put(string_map()) :: :ok
Write a collection of key-value pairs to the firmware metadata
Write a key-value pair to the firmware metadata
@spec put_active(string_map()) :: :ok
Write a collection of key-value pairs to the active firmware slot
Write a key-value pair to the active firmware slot
@spec start_link(keyword()) :: GenServer.on_start()
Start the KV store server
Options:
:kv_backend
- a KV backend of the form{module, options}
or justmodule