diff --git a/lib/ssh_subsystem_fwup.ex b/lib/ssh_subsystem_fwup.ex index 0c66191..f8471cb 100644 --- a/lib/ssh_subsystem_fwup.ex +++ b/lib/ssh_subsystem_fwup.ex @@ -96,7 +96,8 @@ defmodule SSHSubsystemFwup do @impl :ssh_client_channel def handle_msg({:ssh_channel_up, channel_id, cm}, state) do with {:ok, options} <- precheck(state.options[:precheck_callback], state.options), - :ok <- check_devpath(options[:devpath]) do + :ok <- check_devpath(options[:devpath]), + :ok <- check_fwup_not_running() do Logger.debug("ssh_subsystem_fwup: starting fwup") fwup = FwupPort.open_port(options) {:ok, %{state | id: channel_id, cm: cm, fwup: fwup}} @@ -210,6 +211,14 @@ defmodule SSHSubsystemFwup do end end + defp check_fwup_not_running() do + if Enum.any?(Port.list(), &("#{Port.info(&1)[:name]}" =~ ~r/fwup/)) do + {:error, "update already in progress"} + else + :ok + end + end + defp precheck(nil, options), do: {:ok, options} defp precheck({m, f, a}, options) do diff --git a/test/ssh_subsystem_fwup_test.exs b/test/ssh_subsystem_fwup_test.exs index a525d49..2b38dd0 100644 --- a/test/ssh_subsystem_fwup_test.exs +++ b/test/ssh_subsystem_fwup_test.exs @@ -23,7 +23,7 @@ defmodule SSHSubsystemFwupTest do def start_sshd(options) do {:ok, ref} = :ssh.daemon(@port, [ - {:max_sessions, 1}, + {:max_sessions, options[:max_sessions] || 1}, {:user_passwords, [{'user', 'password'}]}, {:system_dir, 'test/fixtures'}, {:subsystems, [SSHSubsystemFwup.subsystem_spec(options)]} @@ -285,4 +285,20 @@ defmodule SSHSubsystemFwupTest do # Check that the update was applied assert match?(<<"Hello, world!", _::binary()>>, File.read!(options[:devpath])) end + + test "does not allow more than one update at a time", context do + options = default_options(context.test) |> Keyword.put(:max_sessions, 2) + File.touch!(options[:devpath]) + start_sshd(options) + + large_fw = Fwup.create_firmware(message: :binary.copy("a", 24 * 1024 * 1024)) + fw_contents = Fwup.create_firmware() + + {:ok, t} = Task.start(fn -> do_ssh(large_fw) end) + {output, exit_status} = do_ssh(fw_contents) + + assert exit_status != 0 + assert output =~ "Error: update already in progress" + Process.exit(t, :kill) + end end