Remove use of iw and hostapd.
parent
ad30f27945
commit
acd3f2324d
|
@ -14,6 +14,7 @@ ttb_last_config
|
|||
/nerves/*
|
||||
!/nerves/host
|
||||
!/nerves/target
|
||||
_nerves_tmp
|
||||
|
||||
# Secret config
|
||||
auth_secret_test.exs
|
||||
|
|
|
@ -25,7 +25,6 @@ defmodule Farmbot do
|
|||
end
|
||||
|
||||
def init([]) do
|
||||
Logger.remove_backend :console
|
||||
RingLogger.attach()
|
||||
children = [
|
||||
{Farmbot.Logger.Supervisor, []},
|
||||
|
|
|
@ -75,6 +75,7 @@ defmodule Farmbot.System.Init.Ecto do
|
|||
migrated = migrator.(repo, migrations_path, :up, opts)
|
||||
pid && repo.stop(pid)
|
||||
Mix.Ecto.restart_apps_if_migrated(apps, migrated)
|
||||
Logger.remove_backend Logger.Backends.Console
|
||||
Process.sleep(500)
|
||||
end
|
||||
end
|
||||
|
|
3
mix.exs
3
mix.exs
|
@ -142,7 +142,8 @@ defmodule Farmbot.Mixfile do
|
|||
{:nerves_runtime, "0.5.3"},
|
||||
{:nerves_firmware, "~> 0.4.0"},
|
||||
{:nerves_init_gadget, "~> 0.3.0", only: :dev},
|
||||
{:nerves_network, "~> 0.3.7-rc0", override: true},
|
||||
{:nerves_network, "~> 0.3"},
|
||||
{:nerves_wpa_supplicant, github: "nerves-project/nerves_wpa_supplicant", override: true},
|
||||
{:dhcp_server, "~> 0.3.0"},
|
||||
{:elixir_ale, "~> 1.0"},
|
||||
{:mdns, "~> 1.0"},
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
"nerves_firmware_ssh": {:hex, :nerves_firmware_ssh, "0.3.1", "e8b1967fa0aff255230be539c68ec868d33884193a385caff957ebad7d6aa8af", [:mix], [{:nerves_runtime, "~> 0.4", [hex: :nerves_runtime, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_init_gadget": {:hex, :nerves_init_gadget, "0.3.0", "4c8fdd6af9f9ad82763d7e93ab1a3f380f64d4b2804cf70acad907261b7b16be", [:mix], [{:mdns, "~> 1.0", [hex: :mdns, repo: "hexpm", optional: false]}, {:nerves_firmware_ssh, "~> 0.2", [hex: :nerves_firmware_ssh, repo: "hexpm", optional: false]}, {:nerves_network, "~> 0.3", [hex: :nerves_network, repo: "hexpm", optional: false]}, {:nerves_runtime, "~> 0.3", [hex: :nerves_runtime, repo: "hexpm", optional: false]}, {:ring_logger, "~> 0.4", [hex: :ring_logger, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_leds": {:hex, :nerves_leds, "0.8.0", "193692767dca1a201b09113d242648493b9be0087bab83ebee99c3b0a254f5e1", [:mix], [], "hexpm"},
|
||||
"nerves_network": {:hex, :nerves_network, "0.3.7-rc0", "83d2967c6e90c9b2b740312885cb890bfd7a9e9025feee1464b768577b1fb28b", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nerves_network_interface, "~> 0.4.4", [hex: :nerves_network_interface, repo: "hexpm", optional: false]}, {:nerves_wpa_supplicant, "~> 0.3.2", [hex: :nerves_wpa_supplicant, repo: "hexpm", optional: false]}, {:system_registry, "~> 0.7", [hex: :system_registry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_network": {:hex, :nerves_network, "0.3.6", "c95779283ace071e9d12882d6a80e31edc8c476012adc61aba2ff6c306ef97b3", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nerves_network_interface, "~> 0.4.0", [hex: :nerves_network_interface, repo: "hexpm", optional: false]}, {:nerves_wpa_supplicant, "~> 0.3.0", [hex: :nerves_wpa_supplicant, repo: "hexpm", optional: false]}, {:system_registry, "~> 0.4", [hex: :system_registry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_network_interface": {:hex, :nerves_network_interface, "0.4.4", "200b1a84bc1a7fdeaf3a1e0e2d4e9b33e240b034e73f39372768d43f8690bae0", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_runtime": {:hex, :nerves_runtime, "0.5.3", "7447a3e718762f3901046f72cc824e528f8d0565a581b8ae58f4f5b6436bca7f", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:system_registry, "~> 0.5", [hex: :system_registry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_system_br": {:hex, :nerves_system_br, "1.0.1", "ca3b6f3c9bad0eadbeef7567a83dbbd87208e290edb2377ff9932609e3806c30", [:mix], [], "hexpm"},
|
||||
|
@ -47,7 +47,7 @@
|
|||
"nerves_toolchain_arm_unknown_linux_gnueabihf": {:hex, :nerves_toolchain_arm_unknown_linux_gnueabihf, "1.0.0", "39da5b503b977a594c9e386ca16a50c433b333797bc30ac941fd402ce1832274", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.4", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "1.4.0", "ec844dd286a5281223e023edb1359c8763fef79a3af9daac45397713cff1cb88", [:mix], [{:nerves, "~> 1.0", [hex: :nerves, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_uart": {:hex, :nerves_uart, "1.1.1", "2ba6282b45513268249e78880cd84bc37c5758ee7db9c6d92f442be21fcacc35", [:mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_wpa_supplicant": {:hex, :nerves_wpa_supplicant, "0.3.2", "19dc7e1248336e7f542b11b2b857ceb5b088d3eb41a6ca75b7b76628dcf67aad", [:make, :mix], [{:elixir_make, "~> 0.3", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_wpa_supplicant": {:git, "https://github.com/nerves-project/nerves_wpa_supplicant.git", "2b3bb056a1594bff16b44d37876e8d599349dcb5", []},
|
||||
"phoenix_html": {:hex, :phoenix_html, "2.10.5", "4f9df6b0fb7422a9440a73182a566cb9cbe0e3ffe8884ef9337ccf284fc1ef0a", [:mix], [{:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"plug": {:hex, :plug, "1.4.5", "7b13869283fff6b8b21b84b8735326cc012c5eef8607095dc6ee24bd0a273d8e", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"poison": {:hex, :poison, "3.1.0", "d9eb636610e096f86f25d9a46f35a9facac35609a7591b3be3326e99a0484665", [:mix], [], "hexpm"},
|
||||
|
@ -64,7 +64,7 @@
|
|||
"sqlite_ecto2": {:hex, :sqlite_ecto2, "2.2.2", "7a3e5c0521e1cb6e30a4907ba4d952b97db9b2ab5d1a4806ceeb66a10b23ba65", [:mix], [{:connection, "~> 1.0.3", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 1.1.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 2.2.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.3", [hex: :esqlite, repo: "hexpm", optional: false]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: false]}, {:sqlitex, "~> 1.3.2 or ~> 1.4", [hex: :sqlitex, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"sqlitex": {:hex, :sqlitex, "1.3.3", "3aac5fd702be346f71d9de6e01702c9954484cd0971aa443490bb3bde045d919", [:mix], [{:decimal, "~> 1.1", [hex: :decimal, repo: "hexpm", optional: false]}, {:esqlite, "~> 0.2.3", [hex: :esqlite, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.1", "28a4d65b7f59893bc2c7de786dec1e1555bd742d336043fe644ae956c3497fbe", [:make, :rebar], [], "hexpm"},
|
||||
"system_registry": {:hex, :system_registry, "0.7.0", "cd3aaf2c15392fa60f93869dde49f536fcf60e54f3b15db737e7d4ebcac108f4", [:mix], [], "hexpm"},
|
||||
"system_registry": {:hex, :system_registry, "0.8.0", "09240347628b001433d18279a2759ef7237ba7361239890d8c599cca9a2fbbc2", [:mix], [], "hexpm"},
|
||||
"timex": {:hex, :timex, "3.2.1", "639975eac45c4c08c2dbf7fc53033c313ff1f94fad9282af03619a3826493612", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.10", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 0.1.8 or ~> 0.5", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"tzdata": {:hex, :tzdata, "0.5.16", "13424d3afc76c68ff607f2df966c0ab4f3258859bbe3c979c9ed1606135e7352", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
|
||||
|
|
|
@ -4,13 +4,37 @@ defmodule Farmbot.Target.Bootstrap.Configurator.CaptivePortal do
|
|||
|
||||
@interface Application.get_env(:farmbot, :captive_portal_interface, "wlan0")
|
||||
@address Application.get_env(:farmbot, :captive_portal_address, "192.168.25.1")
|
||||
|
||||
@dnsmasq_conf_file "dnsmasq.conf"
|
||||
@dnsmasq_pid_file "dnsmasq.pid"
|
||||
|
||||
def start_link() do
|
||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
end
|
||||
|
||||
def init([]) do
|
||||
Logger.busy(3, "Starting captive portal.")
|
||||
{:ok, hostapd} = Hostapd.start_link(interface: @interface, address: @address)
|
||||
ensure_interface(@interface)
|
||||
|
||||
Nerves.Network.teardown(@interface)
|
||||
|
||||
host_ap_opts = [
|
||||
ssid: build_ssid(),
|
||||
key_mgmt: :NONE,
|
||||
mode: 2,
|
||||
# ap_scan: 0,
|
||||
# scan_ssid: 1,
|
||||
]
|
||||
Nerves.Network.setup(@interface, host_ap_opts)
|
||||
|
||||
ip_opts = [
|
||||
ipv4_address_method: :static,
|
||||
ipv4_address: @address, ipv4_subnet_mask: "255.255.0.0",
|
||||
nameservers: [@address]
|
||||
]
|
||||
|
||||
Nerves.NetworkInterface.setup(@interface, ip_opts)
|
||||
|
||||
dhcp_opts = [
|
||||
gateway: @address,
|
||||
netmask: "255.255.255.0",
|
||||
|
@ -18,15 +42,32 @@ defmodule Farmbot.Target.Bootstrap.Configurator.CaptivePortal do
|
|||
domain_servers: [@address],
|
||||
]
|
||||
{:ok, dhcp_server} = DHCPServer.start_link(@interface, dhcp_opts)
|
||||
{:ok, %{hostapd: hostapd, dhcp_server: dhcp_server}}
|
||||
|
||||
dnsmasq = setup_dnsmasq(@address, @interface)
|
||||
|
||||
wpa_pid = wait_for_wpa()
|
||||
Nerves.WpaSupplicant.request(wpa_pid, {:AP_SCAN, 2})
|
||||
{:ok, %{dhcp_server: dhcp_server, dnsmasq: dnsmasq}}
|
||||
end
|
||||
|
||||
defp wait_for_wpa do
|
||||
name = :"Nerves.WpaSupplicant.#{@interface}"
|
||||
GenServer.whereis(name) || wait_for_wpa()
|
||||
end
|
||||
|
||||
def terminate(_, state) do
|
||||
Logger.busy 3, "Stopping captive portal GenServer."
|
||||
|
||||
Logger.busy 3, "Stopping DHCP GenServer."
|
||||
GenServer.stop(state.dhcp_server, :normal)
|
||||
Logger.busy 3, "Stopping Hostapd GenServer."
|
||||
GenServer.stop(state.hostapd, :normal)
|
||||
|
||||
stop_dnsmasq(state)
|
||||
|
||||
Nerves.Network.teardown(@interface)
|
||||
end
|
||||
|
||||
def handle_info({_port, {:data, _data}}, state) do
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp dhcp_range_begin(address) do
|
||||
|
@ -38,4 +79,77 @@ defmodule Farmbot.Target.Bootstrap.Configurator.CaptivePortal do
|
|||
[a, b, c, _] = String.split(address, ".")
|
||||
Enum.join([a, b, c, "10"], ".")
|
||||
end
|
||||
|
||||
defp ensure_interface(interface) do
|
||||
unless interface in Nerves.NetworkInterface.interfaces() do
|
||||
Logger.debug 2, "Waiting for #{interface}: #{inspect Nerves.NetworkInterface.interfaces()}"
|
||||
Process.sleep(100)
|
||||
ensure_interface(interface)
|
||||
end
|
||||
end
|
||||
|
||||
defp build_ssid do
|
||||
node_str = node() |> Atom.to_string()
|
||||
case node_str |> String.split("@") do
|
||||
[name, "farmbot-" <> id] -> name <> "-" <> id
|
||||
_ -> "Farmbot"
|
||||
end
|
||||
end
|
||||
|
||||
defp setup_dnsmasq(ip_addr, interface) do
|
||||
dnsmasq_conf = build_dnsmasq_conf(ip_addr, interface)
|
||||
File.mkdir!("/tmp/dnsmasq")
|
||||
:ok = File.write("/tmp/dnsmasq/#{@dnsmasq_conf_file}", dnsmasq_conf)
|
||||
dnsmasq_cmd = "dnsmasq -k --dhcp-lease " <>
|
||||
"/tmp/dnsmasq/#{@dnsmasq_pid_file} " <>
|
||||
"--conf-dir=/tmp/dnsmasq"
|
||||
dnsmasq_port = Port.open({:spawn, dnsmasq_cmd}, [:binary])
|
||||
dnsmasq_os_pid = dnsmasq_port|> Port.info() |> Keyword.get(:os_pid)
|
||||
{dnsmasq_port, dnsmasq_os_pid}
|
||||
end
|
||||
|
||||
defp build_dnsmasq_conf(ip_addr, interface) do
|
||||
"""
|
||||
interface=#{interface}
|
||||
address=/#/#{ip_addr}
|
||||
server=/farmbot/#{ip_addr}
|
||||
local=/farmbot/
|
||||
domain=farmbot
|
||||
"""
|
||||
end
|
||||
|
||||
defp stop_dnsmasq(state) do
|
||||
case state.dnsmasq do
|
||||
{dnsmasq_port, dnsmasq_os_pid} ->
|
||||
Logger.busy 3, "Stopping dnsmasq"
|
||||
Logger.busy 3, "Killing dnsmasq PID."
|
||||
:ok = kill(dnsmasq_os_pid)
|
||||
Port.close(dnsmasq_port)
|
||||
Logger.success 3, "Stopped dnsmasq."
|
||||
:ok
|
||||
_ ->
|
||||
Logger.debug 3, "Dnsmasq not running."
|
||||
:ok
|
||||
end
|
||||
rescue
|
||||
e ->
|
||||
Logger.error 3, "Error stopping dnsmasq: #{Exception.message(e)}"
|
||||
:ok
|
||||
end
|
||||
|
||||
defp kill(os_pid), do: :ok = cmd("kill -9 #{os_pid}")
|
||||
|
||||
defp cmd(cmd_str) do
|
||||
[command | args] = String.split(cmd_str, " ")
|
||||
System.cmd(command, args, into: IO.stream(:stdio, :line))
|
||||
|> print_cmd()
|
||||
end
|
||||
|
||||
defp print_cmd({_, 0}), do: :ok
|
||||
|
||||
defp print_cmd({_, num}) do
|
||||
Logger.error(2, "Encountered an error (#{num})")
|
||||
:error
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,218 +0,0 @@
|
|||
defmodule Hostapd do
|
||||
@moduledoc """
|
||||
Manages an OS process of hostapd.
|
||||
"""
|
||||
|
||||
defmodule State do
|
||||
@moduledoc false
|
||||
defstruct [:hostapd, :dnsmasq, :interface, :ip_addr]
|
||||
end
|
||||
|
||||
use GenServer
|
||||
use Farmbot.Logger
|
||||
|
||||
@hostapd_conf_file "hostapd.conf"
|
||||
@hostapd_pid_file "hostapd.pid"
|
||||
|
||||
@dnsmasq_conf_file "dnsmasq.conf"
|
||||
@dnsmasq_pid_file "dnsmasq.pid"
|
||||
|
||||
defp ensure_interface(interface) do
|
||||
unless interface in Nerves.NetworkInterface.interfaces() do
|
||||
Logger.debug 2, "Waiting for #{interface}: #{inspect Nerves.NetworkInterface.interfaces()}"
|
||||
Process.sleep(100)
|
||||
ensure_interface(interface)
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def start_link(opts, gen_server_opts \\ []) do
|
||||
GenServer.start_link(__MODULE__, opts, gen_server_opts)
|
||||
end
|
||||
|
||||
def init(opts) do
|
||||
# We want to know if something does.
|
||||
Process.flag(:trap_exit, true)
|
||||
interface = Keyword.fetch!(opts, :interface)
|
||||
address = Keyword.fetch!(opts, :address)
|
||||
Logger.busy(3, "Starting hostapd on #{interface}")
|
||||
ensure_interface(interface)
|
||||
|
||||
dnsmasq_path = System.find_executable("dnsmasq")
|
||||
dnsmasq_settings = if dnsmasq_path do
|
||||
setup_dnsmasq(address, interface)
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
{hostapd_port, hostapd_os_pid} = setup_hostapd(interface, address)
|
||||
|
||||
state = %State{
|
||||
hostapd: {hostapd_port, hostapd_os_pid},
|
||||
dnsmasq: dnsmasq_settings,
|
||||
interface: interface,
|
||||
ip_addr: address
|
||||
}
|
||||
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
|
||||
defp setup_dnsmasq(ip_addr, interface) do
|
||||
dnsmasq_conf = build_dnsmasq_conf(ip_addr, interface)
|
||||
File.mkdir!("/tmp/dnsmasq")
|
||||
:ok = File.write("/tmp/dnsmasq/#{@dnsmasq_conf_file}", dnsmasq_conf)
|
||||
dnsmasq_cmd = "dnsmasq -k --dhcp-lease " <>
|
||||
"/tmp/dnsmasq/#{@dnsmasq_pid_file} " <>
|
||||
"--conf-dir=/tmp/dnsmasq"
|
||||
dnsmasq_port = Port.open({:spawn, dnsmasq_cmd}, [:binary])
|
||||
dnsmasq_os_pid = dnsmasq_port|> Port.info() |> Keyword.get(:os_pid)
|
||||
{dnsmasq_port, dnsmasq_os_pid}
|
||||
end
|
||||
|
||||
defp build_dnsmasq_conf(ip_addr, interface) do
|
||||
"""
|
||||
interface=#{interface}
|
||||
address=/#/#{ip_addr}
|
||||
server=/farmbot/#{ip_addr}
|
||||
local=/farmbot/
|
||||
domain=farmbot
|
||||
"""
|
||||
end
|
||||
|
||||
defp setup_hostapd(interface, ip_addr) do
|
||||
# Make sure the interface is in proper condition.
|
||||
:ok = hostapd_ip_settings_up(interface, ip_addr)
|
||||
# build the hostapd configuration
|
||||
hostapd_conf = build_hostapd_conf(interface, build_ssid())
|
||||
# build a config file
|
||||
File.mkdir!("/tmp/hostapd")
|
||||
File.write!("/tmp/hostapd/#{@hostapd_conf_file}", hostapd_conf)
|
||||
|
||||
hostapd_cmd =
|
||||
"hostapd -P /tmp/hostapd/#{@hostapd_pid_file} "
|
||||
<> "/tmp/hostapd/#{@hostapd_conf_file}"
|
||||
|
||||
hostapd_port = Port.open({:spawn, hostapd_cmd}, [:binary])
|
||||
hostapd_os_pid = hostapd_port |> Port.info() |> Keyword.get(:os_pid)
|
||||
{hostapd_port, hostapd_os_pid}
|
||||
end
|
||||
|
||||
defp hostapd_ip_settings_up(interface, ip_addr) do
|
||||
:ok = cmd("ip link set #{interface} up")
|
||||
:ok = cmd("ip addr add #{ip_addr}/24 dev #{interface}")
|
||||
:ok
|
||||
end
|
||||
|
||||
defp hostapd_ip_settings_down(interface, ip_addr) do
|
||||
:ok = cmd("ip link set #{interface} down")
|
||||
:ok = cmd("ip addr del #{ip_addr}/24 dev #{interface}")
|
||||
:ok = cmd("ip link set #{interface} up")
|
||||
:ok
|
||||
end
|
||||
|
||||
defp build_hostapd_conf(interface, ssid) do
|
||||
"""
|
||||
interface=#{interface}
|
||||
ssid=#{ssid}
|
||||
hw_mode=g
|
||||
channel=6
|
||||
auth_algs=1
|
||||
wmm_enabled=0
|
||||
"""
|
||||
end
|
||||
|
||||
defp build_ssid do
|
||||
node_str = node() |> Atom.to_string()
|
||||
case node_str |> String.split("@") do
|
||||
[name, "farmbot-" <> id] -> name <> "-" <> id
|
||||
_ -> "Farmbot"
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info({port, {:data, data}}, state) do
|
||||
{hostapd_port, _} = state.hostapd
|
||||
|
||||
cond do
|
||||
port == hostapd_port -> handle_hostapd(data, state)
|
||||
match?({^port, _}, state.dnsmasq) -> handle_dnsmasq(data, state)
|
||||
true -> {:noreply, state}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info(_, state), do: {:noreply, state}
|
||||
|
||||
defp handle_hostapd(data, state) when is_bitstring(data) do
|
||||
Logger.debug(3, String.trim(data))
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp handle_dnsmasq(data, state) when is_bitstring(data) do
|
||||
Logger.debug(3, String.trim(data))
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp stop_hostapd(state) do
|
||||
case state.hostapd do
|
||||
{hostapd_port, hostapd_pid} ->
|
||||
Logger.busy 3, "Stopping hostapd"
|
||||
Logger.busy 3, "Killing hostapd PID."
|
||||
:ok = kill(hostapd_pid)
|
||||
Port.close(hostapd_port)
|
||||
Logger.busy 3, "Resetting ip settings."
|
||||
hostapd_ip_settings_down(state.interface, state.ip_addr)
|
||||
Logger.busy 3, "removing PID."
|
||||
File.rm_rf!("/tmp/hostapd")
|
||||
Logger.success 3, "Stopped hostapd."
|
||||
:ok
|
||||
_ ->
|
||||
Logger.debug 3, "Hostapd not running."
|
||||
:ok
|
||||
end
|
||||
rescue
|
||||
e ->
|
||||
Logger.error 3, "Error stopping hostapd: #{Exception.message(e)}"
|
||||
:ok
|
||||
end
|
||||
|
||||
defp stop_dnsmasq(state) do
|
||||
case state.dnsmasq do
|
||||
{dnsmasq_port, dnsmasq_os_pid} ->
|
||||
Logger.busy 3, "Stopping dnsmasq"
|
||||
Logger.busy 3, "Killing dnsmasq PID."
|
||||
:ok = kill(dnsmasq_os_pid)
|
||||
Port.close(dnsmasq_port)
|
||||
Logger.success 3, "Stopped dnsmasq."
|
||||
:ok
|
||||
_ ->
|
||||
Logger.debug 3, "Dnsmasq not running."
|
||||
:ok
|
||||
end
|
||||
rescue
|
||||
e ->
|
||||
Logger.error 3, "Error stopping dnsmasq: #{Exception.message(e)}"
|
||||
:ok
|
||||
end
|
||||
|
||||
def terminate(_, state) do
|
||||
stop_hostapd(state)
|
||||
stop_dnsmasq(state)
|
||||
Nerves.NetworkInterface.ifdown(state.interface)
|
||||
Nerves.NetworkInterface.ifup(state.interface)
|
||||
end
|
||||
|
||||
defp kill(os_pid), do: :ok = cmd("kill -9 #{os_pid}")
|
||||
|
||||
defp cmd(cmd_str) do
|
||||
[command | args] = String.split(cmd_str, " ")
|
||||
System.cmd(command, args, into: IO.stream(:stdio, :line))
|
||||
|> print_cmd()
|
||||
end
|
||||
|
||||
defp print_cmd({_, 0}), do: :ok
|
||||
|
||||
defp print_cmd({_, num}) do
|
||||
Logger.error(2, "Encountered an error (#{num})")
|
||||
:error
|
||||
end
|
||||
end
|
|
@ -7,6 +7,7 @@ defmodule Farmbot.Target.Bootstrap.Configurator do
|
|||
@behaviour Farmbot.System.Init
|
||||
use Farmbot.Logger
|
||||
alias Farmbot.System.ConfigStorage
|
||||
alias Farmbot.Target.Bootstrap.Configurator
|
||||
|
||||
@doc """
|
||||
This particular init module should block until all settings have been validated.
|
||||
|
@ -48,11 +49,10 @@ defmodule Farmbot.Target.Bootstrap.Configurator do
|
|||
import Supervisor.Spec
|
||||
:ets.new(:session, [:named_table, :public, read_concurrency: true])
|
||||
Farmbot.System.GPIO.Leds.led_status_err()
|
||||
alias Farmbot.Target.Bootstrap.Configurator
|
||||
ConfigStorage.destroy_all_network_configs()
|
||||
children = [
|
||||
{Plug.Adapters.Cowboy, scheme: :http, plug: Configurator.Router, options: [port: 80, acceptors: 1]},
|
||||
worker(Configurator.CaptivePortal, [])
|
||||
worker(Configurator.CaptivePortal, []),
|
||||
{Plug.Adapters.Cowboy, scheme: :http, plug: Configurator.Router, options: [port: 80, acceptors: 1]}
|
||||
]
|
||||
|
||||
opts = [strategy: :one_for_one]
|
||||
|
|
|
@ -70,7 +70,7 @@ defmodule Farmbot.Target.Bootstrap.Configurator.Router do
|
|||
get "/config_wireless" do
|
||||
try do
|
||||
ifname = conn.params["ifname"] || raise(MissingField, field: "ifname", message: "ifname not provided", redir: "/network")
|
||||
opts = [ifname: ifname, ssids: Farmbot.Target.Network.do_scan(ifname), post_action: "config_wireless_step_1"]
|
||||
opts = [ifname: ifname, ssids: Farmbot.Target.Network.scan(ifname), post_action: "config_wireless_step_1"]
|
||||
render_page(conn, "/config_wireless_step_1", opts)
|
||||
rescue
|
||||
e in MissingField -> redir(conn, e.redir)
|
||||
|
|
|
@ -15,7 +15,6 @@ defmodule Farmbot.Target.Network.Manager do
|
|||
end
|
||||
|
||||
def init({interface, opts} = args) do
|
||||
Elixir.Logger.remove_backend Elixir.Logger.Backends.Console
|
||||
Logger.busy(3, "Waiting for interface #{interface} up.")
|
||||
|
||||
unless interface in Nerves.NetworkInterface.interfaces() do
|
||||
|
@ -24,6 +23,8 @@ defmodule Farmbot.Target.Network.Manager do
|
|||
end
|
||||
Logger.success(3, "Interface #{interface} is up.")
|
||||
|
||||
Nerves.Network.teardown("wlan0")
|
||||
|
||||
SystemRegistry.register()
|
||||
{:ok, _} = Elixir.Registry.register(Nerves.NetworkInterface, interface, [])
|
||||
{:ok, _} = Elixir.Registry.register(Nerves.Udhcpc, interface, [])
|
||||
|
|
|
@ -32,8 +32,8 @@ defmodule Farmbot.Target.Network do
|
|||
end
|
||||
|
||||
@doc "Scan on an interface. "
|
||||
def do_scan(iface) do
|
||||
Nerves.Network.scan(iface)
|
||||
def scan(iface) do
|
||||
do_scan(iface)
|
||||
|> ScanResult.decode()
|
||||
|> ScanResult.sort_results()
|
||||
|> ScanResult.decode_security()
|
||||
|
@ -41,6 +41,38 @@ defmodule Farmbot.Target.Network do
|
|||
|> Enum.map(&Map.update(&1, :ssid, nil, fn(ssid) -> to_string(ssid) end))
|
||||
|> Enum.reject(&String.contains?(&1.ssid, "\\x00"))
|
||||
|> Enum.uniq_by(fn(%{ssid: ssid}) -> ssid end)
|
||||
|
||||
end
|
||||
|
||||
# While scanning in AP mode, The CTRL-EVENT-SCAN-COMPLETE event never happens.
|
||||
defp wait_for_scan_results(pid, timer, count, loops_without_change, acc)
|
||||
defp wait_for_scan_results(_pid, _timer, _count, 10, acc), do: acc
|
||||
defp wait_for_scan_results(pid, timer, count, loops_without_change, acc) do
|
||||
res = Nerves.WpaSupplicant.request(pid, {:BSS, count})
|
||||
new_acc = if res, do: [res | acc], else: acc
|
||||
new_count = if res, do: count + 1, else: count
|
||||
new_loops_without_change = if res, do: 0, else: loops_without_change + 1
|
||||
|
||||
receive do
|
||||
:complete -> acc
|
||||
{Nerves.WpaSupplicant, {:"CTRL-EVENT-BSS-REMOVED", _, _}, _} ->
|
||||
Process.cancel_timer(timer)
|
||||
wait_for_scan_results(pid, Process.send_after(self(), :complete, 5000), 0, 0, [])
|
||||
_ -> wait_for_scan_results(pid, timer, new_count, new_loops_without_change, new_acc)
|
||||
after 10 -> wait_for_scan_results(pid, timer, new_count, new_loops_without_change, new_acc)
|
||||
end
|
||||
end
|
||||
|
||||
def do_scan(iface) do
|
||||
pid = :"Nerves.WpaSupplicant.#{iface}"
|
||||
Elixir.Registry.register(Nerves.WpaSupplicant, iface, [])
|
||||
case Nerves.WpaSupplicant.request(pid, :SCAN) do
|
||||
r when r in ["FAIL-BUSY", :ok] ->
|
||||
results = wait_for_scan_results(pid, Process.send_after(self(), :complete, 5000), 0, 0, [])
|
||||
Elixir.Registry.unregister(Nerves.WpaSupplicant, iface)
|
||||
results
|
||||
resp -> raise("Unexpected scan result: #{inspect resp}")
|
||||
end
|
||||
end
|
||||
|
||||
@doc "Tests if we can make dns queries."
|
||||
|
|
Loading…
Reference in New Issue