Remove use of iw and hostapd.
parent
ad30f27945
commit
acd3f2324d
|
@ -14,6 +14,7 @@ ttb_last_config
|
||||||
/nerves/*
|
/nerves/*
|
||||||
!/nerves/host
|
!/nerves/host
|
||||||
!/nerves/target
|
!/nerves/target
|
||||||
|
_nerves_tmp
|
||||||
|
|
||||||
# Secret config
|
# Secret config
|
||||||
auth_secret_test.exs
|
auth_secret_test.exs
|
||||||
|
|
|
@ -25,7 +25,6 @@ defmodule Farmbot do
|
||||||
end
|
end
|
||||||
|
|
||||||
def init([]) do
|
def init([]) do
|
||||||
Logger.remove_backend :console
|
|
||||||
RingLogger.attach()
|
RingLogger.attach()
|
||||||
children = [
|
children = [
|
||||||
{Farmbot.Logger.Supervisor, []},
|
{Farmbot.Logger.Supervisor, []},
|
||||||
|
|
|
@ -75,6 +75,7 @@ defmodule Farmbot.System.Init.Ecto do
|
||||||
migrated = migrator.(repo, migrations_path, :up, opts)
|
migrated = migrator.(repo, migrations_path, :up, opts)
|
||||||
pid && repo.stop(pid)
|
pid && repo.stop(pid)
|
||||||
Mix.Ecto.restart_apps_if_migrated(apps, migrated)
|
Mix.Ecto.restart_apps_if_migrated(apps, migrated)
|
||||||
|
Logger.remove_backend Logger.Backends.Console
|
||||||
Process.sleep(500)
|
Process.sleep(500)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
3
mix.exs
3
mix.exs
|
@ -142,7 +142,8 @@ defmodule Farmbot.Mixfile do
|
||||||
{:nerves_runtime, "0.5.3"},
|
{:nerves_runtime, "0.5.3"},
|
||||||
{:nerves_firmware, "~> 0.4.0"},
|
{:nerves_firmware, "~> 0.4.0"},
|
||||||
{:nerves_init_gadget, "~> 0.3.0", only: :dev},
|
{: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"},
|
{:dhcp_server, "~> 0.3.0"},
|
||||||
{:elixir_ale, "~> 1.0"},
|
{:elixir_ale, "~> 1.0"},
|
||||||
{:mdns, "~> 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_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_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_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_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_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"},
|
"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_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_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_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"},
|
"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"},
|
"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"},
|
"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"},
|
"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"},
|
"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"},
|
"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"},
|
"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"},
|
"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"},
|
"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")
|
@interface Application.get_env(:farmbot, :captive_portal_interface, "wlan0")
|
||||||
@address Application.get_env(:farmbot, :captive_portal_address, "192.168.25.1")
|
@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
|
def start_link() do
|
||||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
def init([]) do
|
def init([]) do
|
||||||
Logger.busy(3, "Starting captive portal.")
|
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 = [
|
dhcp_opts = [
|
||||||
gateway: @address,
|
gateway: @address,
|
||||||
netmask: "255.255.255.0",
|
netmask: "255.255.255.0",
|
||||||
|
@ -18,15 +42,32 @@ defmodule Farmbot.Target.Bootstrap.Configurator.CaptivePortal do
|
||||||
domain_servers: [@address],
|
domain_servers: [@address],
|
||||||
]
|
]
|
||||||
{:ok, dhcp_server} = DHCPServer.start_link(@interface, dhcp_opts)
|
{: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
|
end
|
||||||
|
|
||||||
def terminate(_, state) do
|
def terminate(_, state) do
|
||||||
Logger.busy 3, "Stopping captive portal GenServer."
|
Logger.busy 3, "Stopping captive portal GenServer."
|
||||||
|
|
||||||
Logger.busy 3, "Stopping DHCP GenServer."
|
Logger.busy 3, "Stopping DHCP GenServer."
|
||||||
GenServer.stop(state.dhcp_server, :normal)
|
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
|
end
|
||||||
|
|
||||||
defp dhcp_range_begin(address) do
|
defp dhcp_range_begin(address) do
|
||||||
|
@ -38,4 +79,77 @@ defmodule Farmbot.Target.Bootstrap.Configurator.CaptivePortal do
|
||||||
[a, b, c, _] = String.split(address, ".")
|
[a, b, c, _] = String.split(address, ".")
|
||||||
Enum.join([a, b, c, "10"], ".")
|
Enum.join([a, b, c, "10"], ".")
|
||||||
end
|
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
|
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
|
@behaviour Farmbot.System.Init
|
||||||
use Farmbot.Logger
|
use Farmbot.Logger
|
||||||
alias Farmbot.System.ConfigStorage
|
alias Farmbot.System.ConfigStorage
|
||||||
|
alias Farmbot.Target.Bootstrap.Configurator
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
This particular init module should block until all settings have been validated.
|
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
|
import Supervisor.Spec
|
||||||
:ets.new(:session, [:named_table, :public, read_concurrency: true])
|
:ets.new(:session, [:named_table, :public, read_concurrency: true])
|
||||||
Farmbot.System.GPIO.Leds.led_status_err()
|
Farmbot.System.GPIO.Leds.led_status_err()
|
||||||
alias Farmbot.Target.Bootstrap.Configurator
|
|
||||||
ConfigStorage.destroy_all_network_configs()
|
ConfigStorage.destroy_all_network_configs()
|
||||||
children = [
|
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]
|
opts = [strategy: :one_for_one]
|
||||||
|
|
|
@ -70,7 +70,7 @@ defmodule Farmbot.Target.Bootstrap.Configurator.Router do
|
||||||
get "/config_wireless" do
|
get "/config_wireless" do
|
||||||
try do
|
try do
|
||||||
ifname = conn.params["ifname"] || raise(MissingField, field: "ifname", message: "ifname not provided", redir: "/network")
|
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)
|
render_page(conn, "/config_wireless_step_1", opts)
|
||||||
rescue
|
rescue
|
||||||
e in MissingField -> redir(conn, e.redir)
|
e in MissingField -> redir(conn, e.redir)
|
||||||
|
|
|
@ -15,7 +15,6 @@ defmodule Farmbot.Target.Network.Manager do
|
||||||
end
|
end
|
||||||
|
|
||||||
def init({interface, opts} = args) do
|
def init({interface, opts} = args) do
|
||||||
Elixir.Logger.remove_backend Elixir.Logger.Backends.Console
|
|
||||||
Logger.busy(3, "Waiting for interface #{interface} up.")
|
Logger.busy(3, "Waiting for interface #{interface} up.")
|
||||||
|
|
||||||
unless interface in Nerves.NetworkInterface.interfaces() do
|
unless interface in Nerves.NetworkInterface.interfaces() do
|
||||||
|
@ -24,6 +23,8 @@ defmodule Farmbot.Target.Network.Manager do
|
||||||
end
|
end
|
||||||
Logger.success(3, "Interface #{interface} is up.")
|
Logger.success(3, "Interface #{interface} is up.")
|
||||||
|
|
||||||
|
Nerves.Network.teardown("wlan0")
|
||||||
|
|
||||||
SystemRegistry.register()
|
SystemRegistry.register()
|
||||||
{:ok, _} = Elixir.Registry.register(Nerves.NetworkInterface, interface, [])
|
{:ok, _} = Elixir.Registry.register(Nerves.NetworkInterface, interface, [])
|
||||||
{:ok, _} = Elixir.Registry.register(Nerves.Udhcpc, interface, [])
|
{:ok, _} = Elixir.Registry.register(Nerves.Udhcpc, interface, [])
|
||||||
|
|
|
@ -32,8 +32,8 @@ defmodule Farmbot.Target.Network do
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Scan on an interface. "
|
@doc "Scan on an interface. "
|
||||||
def do_scan(iface) do
|
def scan(iface) do
|
||||||
Nerves.Network.scan(iface)
|
do_scan(iface)
|
||||||
|> ScanResult.decode()
|
|> ScanResult.decode()
|
||||||
|> ScanResult.sort_results()
|
|> ScanResult.sort_results()
|
||||||
|> ScanResult.decode_security()
|
|> 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.map(&Map.update(&1, :ssid, nil, fn(ssid) -> to_string(ssid) end))
|
||||||
|> Enum.reject(&String.contains?(&1.ssid, "\\x00"))
|
|> Enum.reject(&String.contains?(&1.ssid, "\\x00"))
|
||||||
|> Enum.uniq_by(fn(%{ssid: ssid}) -> ssid end)
|
|> 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
|
end
|
||||||
|
|
||||||
@doc "Tests if we can make dns queries."
|
@doc "Tests if we can make dns queries."
|
||||||
|
|
Loading…
Reference in New Issue