diff --git a/lib/nerves_runtime/fwup_ops.ex b/lib/nerves_runtime/fwup_ops.ex index 3296846..076ee1f 100644 --- a/lib/nerves_runtime/fwup_ops.ex +++ b/lib/nerves_runtime/fwup_ops.ex @@ -104,6 +104,29 @@ defmodule Nerves.Runtime.FwupOps do end end + @doc """ + Return boot status + + This invokes the "status" task in the `ops.fw` to report the current + firmware slot and what slot will be tried on the next reboot. The `ops.fw` + is expected to print the slot name or two slot names separated by "->". + """ + @spec status(options()) :: + {:ok, %{current: String.t(), next: String.t()}} | {:error, reason :: any} + def status(opts \\ []) do + with {:ok, raw_result} <- run_fwup("status", opts), + {:ok, result} <- deframe(raw_result, []) do + Enum.find_value(result, {:error, "Invalid status"}, &find_status/1) + end + end + + defp find_status({:warning, <>}), do: {:ok, %{current: slot, next: slot}} + + defp find_status({:warning, <", next::1-bytes>>}), + do: {:ok, %{current: current, next: next}} + + defp find_status(_status), do: nil + defp run_fwup(task, opts) do devpath = Keyword.get(opts, :devpath, "/dev/rootdisk0") cmd_opts = [env: Keyword.get(opts, :env, %{})] diff --git a/test/nerves_runtime/fwup_ops_test.exs b/test/nerves_runtime/fwup_ops_test.exs index 18bc026..bab105c 100644 --- a/test/nerves_runtime/fwup_ops_test.exs +++ b/test/nerves_runtime/fwup_ops_test.exs @@ -50,6 +50,20 @@ defmodule NervesRuntime.FwupOpsTest do assert {:error, "prevent-revert error"} = FwupOps.prevent_revert(@fwup_fail_options) end + defp status_ops(status) do + Keyword.put(@fwup_options, :env, %{"STATUS" => status}) + end + + test "status" do + # ops.conf lets you set the status via $STATUS + assert {:ok, %{current: "a", next: "b"}} = FwupOps.status(status_ops("a->b")) + assert {:ok, %{current: "b", next: "a"}} = FwupOps.status(status_ops("b->a")) + assert {:ok, %{current: "c", next: "c"}} = FwupOps.status(status_ops("c")) + assert {:error, "Invalid status"} = FwupOps.status(status_ops("xyz")) + + assert {:error, "status error"} = FwupOps.status(@fwup_fail_options) + end + test "missing ops.fw" do Application.put_env(:nerves_runtime, :revert_fw_path, "/does/not/exist/missing_ops.fw")