work on network.
parent
800c80c50f
commit
8d72ed51ab
|
@ -40,7 +40,7 @@ defmodule Farmbot.Mixfile do
|
|||
version: @version} ]
|
||||
},
|
||||
applications: applications(),
|
||||
included_applications: [:gen_mqtt, :"farmbot_system_#{target(Mix.env)}"]]
|
||||
included_applications: [:gen_mqtt]]
|
||||
end
|
||||
|
||||
# common for test, prod, and dev
|
||||
|
@ -60,6 +60,7 @@ defmodule Farmbot.Mixfile do
|
|||
:quantum,
|
||||
:gen_stage,
|
||||
:nerves,
|
||||
:"farmbot_system_#{target(Mix.env)}",
|
||||
:farmbot_system,
|
||||
:farmbot_auth,
|
||||
:farmbot_configurator,
|
||||
|
|
|
@ -61,7 +61,7 @@ defmodule Farmbot.Configurator.Router do
|
|||
spawn fn() ->
|
||||
# sleep to allow the request to finish.
|
||||
Process.sleep(100)
|
||||
Logger.debug "THAT ISNT WORKINGF YET!!!"
|
||||
Farmbot.System.Network.restart
|
||||
end
|
||||
conn |> send_resp(200, "OK")
|
||||
end
|
||||
|
|
|
@ -30,8 +30,8 @@ defmodule Farmbot.System.Network.Ntp do
|
|||
:stderr_to_stdout])
|
||||
case handle_port(port) do
|
||||
:ok -> :ok
|
||||
{:error, _} ->
|
||||
Logger.debug ">> failed to get time. trying again."
|
||||
{:error, reason} ->
|
||||
Logger.debug ">> failed to get time: #{inspect reason} trying again."
|
||||
# kill old ntp if it exists
|
||||
System.cmd("killall", ["ntpd"])
|
||||
# sleep for a second
|
||||
|
|
|
@ -15,6 +15,10 @@ defmodule Farmbot.System.Network.SSH do
|
|||
|
||||
def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
|
||||
def stop(reason) do
|
||||
GenServer.stop(__MODULE__, reason)
|
||||
end
|
||||
|
||||
def handle_info({:EXIT, port, reason}, state)
|
||||
when state == port do
|
||||
Logger.error ">>`s ssh client died: #{inspect reason}"
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
defmodule Farmbot.System.Network do
|
||||
@moduledoc """
|
||||
Network functionality.
|
||||
"""
|
||||
require Logger
|
||||
use GenServer
|
||||
alias Farmbot.System.FS.ConfigStorage, as: CS
|
||||
alias Farmbot.System.Network.SSH
|
||||
alias Farmbot.System.Network.Ntp
|
||||
alias Farmbot.Auth
|
||||
|
||||
@spec mod(atom) :: atom
|
||||
defp mod(target), do: Module.concat([Farmbot, System, target, Network])
|
||||
|
||||
def init(target) do
|
||||
Logger.debug ">> is starting networking"
|
||||
m = mod(target)
|
||||
{:ok, _cb} = m.start_link
|
||||
{:ok, interface_config} = get_config("interfaces")
|
||||
parse_and_start_config(interface_config, m)
|
||||
{:ok, target}
|
||||
end
|
||||
|
||||
# if networking is disabled.
|
||||
defp parse_and_start_config(nil, _), do: :ok
|
||||
|
||||
defp parse_and_start_config(config, m) do
|
||||
for {interface, settings} <- config do
|
||||
m.start_interface(interface, settings)
|
||||
end
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
GenServer.start_link(__MODULE__, args, name: __MODULE__)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Scans for wireless ssids.
|
||||
"""
|
||||
@spec scan(String.t) :: [String.t]
|
||||
def scan(interface_name) do
|
||||
GenServer.call(__MODULE__, {:scan, interface_name})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Restarts networking services. This will block.
|
||||
"""
|
||||
def restart() do
|
||||
stop_all()
|
||||
start_all()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Starts an interface
|
||||
"""
|
||||
def start_interface(interface, settings) do
|
||||
GenServer.call(__MODULE__, {:start, interface, settings}, :infinity)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Stops an interface
|
||||
"""
|
||||
def stop_interface(interface) do
|
||||
GenServer.call(__MODULE__, {:stop, interface}, :infinity)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Stops all interfaces
|
||||
"""
|
||||
def stop_all do
|
||||
{:ok, interfaces} = get_config("interfaces")
|
||||
if interfaces do
|
||||
for {iface, _} <- interfaces do
|
||||
stop_interface(iface)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Starts all interfaces
|
||||
"""
|
||||
def start_all do
|
||||
{:ok, interfaces} = get_config("interfaces")
|
||||
if interfaces do
|
||||
for {iface, settings} <- interfaces do
|
||||
start_interface(iface, settings)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
Connected to the World Wide Web. Should be called from the
|
||||
callback module.
|
||||
"""
|
||||
def on_connect(fun \\ nil) do
|
||||
# this happens because on wifi we try to do stuff before linux is
|
||||
# finished setting stuff up.
|
||||
Process.sleep(2000)
|
||||
Logger.debug ">> is connected to the World Wide Web"
|
||||
{:ok, ssh} = get_config("ssh")
|
||||
{:ok, ntp} = get_config("ntp")
|
||||
if ssh, do: SSH.start_link
|
||||
if ntp, do: Ntp.set_time
|
||||
if fun do
|
||||
spawn fun
|
||||
end
|
||||
Auth.try_log_in
|
||||
end
|
||||
|
||||
@spec get_config(String.t) :: {:ok, any}
|
||||
@spec get_config() :: {:ok, false | map}
|
||||
defp get_config(key), do: GenServer.call(CS, {:get, Network, key})
|
||||
defp get_config, do: GenServer.call(CS, {:get, Network, :all})
|
||||
|
||||
# GENSERVER STUFF
|
||||
def handle_call({:scan, interface_name}, _, target) do
|
||||
f = mod(target).scan(interface_name)
|
||||
{:reply, f, target}
|
||||
end
|
||||
|
||||
def handle_call({:start, interface, settings}, _, target) do
|
||||
f = mod(target).start_interface(interface, settings)
|
||||
{:reply, f, target}
|
||||
end
|
||||
|
||||
def handle_call({:stop, interface}, _, target) do
|
||||
f = mod(target).stop_interface(interface)
|
||||
{:reply, f, target}
|
||||
end
|
||||
|
||||
def terminate(reason, target) do
|
||||
ssh_pid = Process.whereis(SSH)
|
||||
if ssh_pid do
|
||||
SSH.stop(reason)
|
||||
end
|
||||
target_pid = Process.whereis(mod(target))
|
||||
if target_pid do
|
||||
GenServer.stop(target_pid, reason)
|
||||
end
|
||||
end
|
||||
|
||||
# Behavior
|
||||
@type return_type :: :ok | {:error, term}
|
||||
@callback scan(String.t) :: [String.t]
|
||||
@callback start_interface(String.t, map) :: return_type
|
||||
@callback stop_interface(String.t) :: return_type
|
||||
@callback start_link :: {:ok, pid}
|
||||
end
|
|
@ -1,57 +0,0 @@
|
|||
defmodule Farmbot.System.Network do
|
||||
@moduledoc """
|
||||
Network functionality.
|
||||
"""
|
||||
require Logger
|
||||
use GenServer
|
||||
|
||||
@spec mod(atom) :: atom
|
||||
defp mod(target), do: Module.concat([Farmbot, System, target, Network])
|
||||
|
||||
def init(target) do
|
||||
Logger.debug ">> is starting networking"
|
||||
m = mod(target)
|
||||
{:ok, _cb} = m.start_link
|
||||
{:ok, target}
|
||||
end
|
||||
|
||||
def start_link(args) do
|
||||
GenServer.start_link(__MODULE__, args, name: __MODULE__)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Scans for wireless ssids.
|
||||
"""
|
||||
@spec scan(String.t) :: [String.t]
|
||||
def scan(interface_name) do
|
||||
GenServer.call(__MODULE__, {:scan, interface_name})
|
||||
end
|
||||
|
||||
@doc """
|
||||
Restarts networking services. This will block.
|
||||
"""
|
||||
def restart() do
|
||||
GenServer.call(__MODULE__, :restart_network, :infinity)
|
||||
end
|
||||
|
||||
# GENSERVER STUFF
|
||||
def handle_call({:scan, interface_name}, _, target) do
|
||||
f = mod(target).scan(interface_name)
|
||||
{:reply, f, target}
|
||||
end
|
||||
|
||||
def handle_call(:restart_network, _, target) do
|
||||
mod(target).restart
|
||||
{:reply, :ok, target}
|
||||
end
|
||||
|
||||
# Behavior
|
||||
@type return_type :: :ok | {:error, term}
|
||||
@callback scan(String.t) :: [String.t]
|
||||
@callback start_interface(String.t) :: return_type
|
||||
@callback stop_interface(String.t) :: return_type
|
||||
@callback restart_interface(String.t) :: return_type
|
||||
@callback stop_all :: return_type
|
||||
@callback start_all :: return_type
|
||||
@callback start_link :: {:ok, pid}
|
||||
end
|
|
@ -2,8 +2,16 @@ defmodule Module.concat([Farmbot, System, "development", Network]) do
|
|||
@moduledoc false
|
||||
@behaviour Farmbot.System.Network
|
||||
use GenServer
|
||||
|
||||
def init(_) do
|
||||
spawn fn() ->
|
||||
# sleep because dev mode thinks network is up before it is technically possible.
|
||||
Process.sleep(2500)
|
||||
Farmbot.System.Network.on_connect()
|
||||
end
|
||||
{:ok, []}
|
||||
end
|
||||
def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
def init(_), do: {:ok, []}
|
||||
def scan(_), do: []
|
||||
def start_all, do: :ok
|
||||
def stop_all, do: :ok
|
||||
|
|
|
@ -2,12 +2,98 @@ defmodule Module.concat([Farmbot, System, "rpi2", Network]) do
|
|||
@moduledoc false
|
||||
@behaviour Farmbot.System.Network
|
||||
use GenServer
|
||||
require Logger
|
||||
alias Nerves.InterimWiFi, as: NervesWifi
|
||||
alias Farmbot.System.Network.Hostapd
|
||||
|
||||
def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
def init(_), do: {:ok, []}
|
||||
def scan(_), do: []
|
||||
def start_all, do: :ok
|
||||
def stop_all, do: :ok
|
||||
def start_interface(_), do: :ok
|
||||
def stop_interface(_), do: :ok
|
||||
def restart_interface(_), do: :ok
|
||||
|
||||
def init(_) do
|
||||
GenEvent.add_handler(event_manager(),
|
||||
Module.concat([Farmbot, System, "rpi2", Network, EventManager]), [])
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
||||
# don't start this interface
|
||||
def start_interface(_interface, %{"default" => false}) do
|
||||
:ok
|
||||
end
|
||||
|
||||
def start_interface(interface, %{"default" => "dhcp", "type" => "wired"} = s) do
|
||||
{:ok, pid} = NervesWifi.setup(interface, [])
|
||||
GenServer.cast(__MODULE__, {:start_interface, interface, s, pid})
|
||||
:ok
|
||||
end
|
||||
|
||||
def start_interface(interface,
|
||||
%{"default" => "dhcp",
|
||||
"type" => "wireless",
|
||||
"settings" => settings} = s)
|
||||
do
|
||||
ssid = settings["ssid"]
|
||||
case settings["key_mgmt"] do
|
||||
"NONE" ->
|
||||
{:ok, pid} = NervesWifi.setup(interface, [ssid: ssid, key_mgmt: :NONE])
|
||||
GenServer.cast(__MODULE__, {:start_interface, interface, s, pid})
|
||||
"WPA-PSK" ->
|
||||
psk = settings["psk"]
|
||||
{:ok, pid} = NervesWifi.setup(interface,
|
||||
[ssid: ssid, key_mgmt: :"WPA-PSK", psk: psk])
|
||||
GenServer.cast(__MODULE__, {:start_interface, interface, s, pid})
|
||||
end
|
||||
:ok
|
||||
end
|
||||
|
||||
def start_interface(interface,
|
||||
%{"default" => "hostapd",
|
||||
"settings" => %{"ipv4_address" => ip_addr}, "type" => "wireless"} = s)
|
||||
do
|
||||
{:ok, pid} = Hostapd.start_link([interface: interface, ip_address: ip_addr, manager: event_manager()])
|
||||
GenServer.cast(__MODULE__, {:start_interface, interface, s, pid})
|
||||
:ok
|
||||
end
|
||||
|
||||
def stop_interface(interface) do
|
||||
GenServer.call(__MODULE__, {:stop_interface, interface})
|
||||
end
|
||||
|
||||
def scan(iface) do
|
||||
{hc, 0} = System.cmd("iw", [iface, "scan", "ap-force"])
|
||||
hc |> clean_ssid
|
||||
end
|
||||
|
||||
defp event_manager, do: Nerves.NetworkInterface.event_manager()
|
||||
defp clean_ssid(hc) do
|
||||
hc
|
||||
|> String.replace("\t", "")
|
||||
|> String.replace("\\x00", "")
|
||||
|> String.split("\n")
|
||||
|> Enum.filter(fn(s) -> String.contains?(s, "SSID") end)
|
||||
|> Enum.map(fn(z) -> String.replace(z, "SSID: ", "") end)
|
||||
|> Enum.filter(fn(z) -> String.length(z) != 0 end)
|
||||
end
|
||||
|
||||
# GENSERVER STUFF
|
||||
def handle_call({:stop_interface, interface}, _, state) do
|
||||
case state[interface] do
|
||||
{settings, pid} ->
|
||||
if settings["default"] == "hostapd" do
|
||||
GenServer.stop(pid, :uhhh)
|
||||
{:reply, :ok, Map.delete(state, interface)}
|
||||
else
|
||||
Logger.debug ">> cant stop: #{interface}"
|
||||
{:reply, {:error, :not_implemented}, state}
|
||||
end
|
||||
_ -> {:reply, {:error, :not_started}, state}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_cast({:start_interface, interface, settings, pid}, state) do
|
||||
{:noreply, Map.put(state, interface, {settings, pid})}
|
||||
end
|
||||
|
||||
def terminate(_,_state) do
|
||||
# TODO STOP INTERFACES
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
defmodule Module.concat([Farmbot, System, "rpi2", Network, EventManager]) do
|
||||
use GenEvent
|
||||
require Logger
|
||||
|
||||
def handle_event({:udhcpc, _, :bound,
|
||||
%{ipv4_address: _address, ifname: _interface}}, state)
|
||||
do
|
||||
Farmbot.System.Network.on_connect(fn() ->
|
||||
nil
|
||||
end)
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
# just print hostapd data
|
||||
def handle_event({:hostapd, data}, state) do
|
||||
Logger.debug ">> got some hostapd data: #{data}"
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
# def handle_event(event, state) do
|
||||
# Logger.warn "got event: #{inspect event}"
|
||||
# {:ok, state}
|
||||
# end
|
||||
|
||||
# handle stray events that we don't care about
|
||||
def handle_event(_, state), do: {:ok, state}
|
||||
end
|
|
@ -2,12 +2,98 @@ defmodule Module.concat([Farmbot, System, "rpi3", Network]) do
|
|||
@moduledoc false
|
||||
@behaviour Farmbot.System.Network
|
||||
use GenServer
|
||||
require Logger
|
||||
alias Nerves.InterimWiFi, as: NervesWifi
|
||||
alias Farmbot.System.Network.Hostapd
|
||||
|
||||
def start_link(), do: GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
def init(_), do: {:ok, []}
|
||||
def scan(_), do: []
|
||||
def start_all, do: :ok
|
||||
def stop_all, do: :ok
|
||||
def start_interface(_), do: :ok
|
||||
def stop_interface(_), do: :ok
|
||||
def restart_interface(_), do: :ok
|
||||
|
||||
def init(_) do
|
||||
GenEvent.add_handler(event_manager(),
|
||||
Module.concat([Farmbot, System, "rpi3", Network, EventManager]), [])
|
||||
{:ok, %{}}
|
||||
end
|
||||
|
||||
# don't start this interface
|
||||
def start_interface(_interface, %{"default" => false}) do
|
||||
:ok
|
||||
end
|
||||
|
||||
def start_interface(interface, %{"default" => "dhcp", "type" => "wired"} = s) do
|
||||
{:ok, pid} = NervesWifi.setup(interface, [])
|
||||
GenServer.cast(__MODULE__, {:start_interface, interface, s, pid})
|
||||
:ok
|
||||
end
|
||||
|
||||
def start_interface(interface,
|
||||
%{"default" => "dhcp",
|
||||
"type" => "wireless",
|
||||
"settings" => settings} = s)
|
||||
do
|
||||
ssid = settings["ssid"]
|
||||
case settings["key_mgmt"] do
|
||||
"NONE" ->
|
||||
{:ok, pid} = NervesWifi.setup(interface, [ssid: ssid, key_mgmt: :NONE])
|
||||
GenServer.cast(__MODULE__, {:start_interface, interface, s, pid})
|
||||
"WPA-PSK" ->
|
||||
psk = settings["psk"]
|
||||
{:ok, pid} = NervesWifi.setup(interface,
|
||||
[ssid: ssid, key_mgmt: :"WPA-PSK", psk: psk])
|
||||
GenServer.cast(__MODULE__, {:start_interface, interface, s, pid})
|
||||
end
|
||||
:ok
|
||||
end
|
||||
|
||||
def start_interface(interface,
|
||||
%{"default" => "hostapd",
|
||||
"settings" => %{"ipv4_address" => ip_addr}, "type" => "wireless"} = s)
|
||||
do
|
||||
{:ok, pid} = Hostapd.start_link([interface: interface, ip_address: ip_addr, manager: event_manager()])
|
||||
GenServer.cast(__MODULE__, {:start_interface, interface, s, pid})
|
||||
:ok
|
||||
end
|
||||
|
||||
def stop_interface(interface) do
|
||||
GenServer.call(__MODULE__, {:stop_interface, interface})
|
||||
end
|
||||
|
||||
def scan(iface) do
|
||||
{hc, 0} = System.cmd("iw", [iface, "scan", "ap-force"])
|
||||
hc |> clean_ssid
|
||||
end
|
||||
|
||||
defp event_manager, do: Nerves.NetworkInterface.event_manager()
|
||||
defp clean_ssid(hc) do
|
||||
hc
|
||||
|> String.replace("\t", "")
|
||||
|> String.replace("\\x00", "")
|
||||
|> String.split("\n")
|
||||
|> Enum.filter(fn(s) -> String.contains?(s, "SSID") end)
|
||||
|> Enum.map(fn(z) -> String.replace(z, "SSID: ", "") end)
|
||||
|> Enum.filter(fn(z) -> String.length(z) != 0 end)
|
||||
end
|
||||
|
||||
# GENSERVER STUFF
|
||||
def handle_call({:stop_interface, interface}, _, state) do
|
||||
case state[interface] do
|
||||
{settings, pid} ->
|
||||
if settings["default"] == "hostapd" do
|
||||
GenServer.stop(pid, :uhhh)
|
||||
{:reply, :ok, Map.delete(state, interface)}
|
||||
else
|
||||
Logger.debug ">> cant stop: #{interface}"
|
||||
{:reply, {:error, :not_implemented}, state}
|
||||
end
|
||||
_ -> {:reply, {:error, :not_started}, state}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_cast({:start_interface, interface, settings, pid}, state) do
|
||||
{:noreply, Map.put(state, interface, {settings, pid})}
|
||||
end
|
||||
|
||||
def terminate(_,_state) do
|
||||
# TODO STOP INTERFACES
|
||||
:ok
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
defmodule Module.concat([Farmbot, System, "rpi3", Network, EventManager]) do
|
||||
use GenEvent
|
||||
require Logger
|
||||
|
||||
def handle_event({:udhcpc, _, :bound,
|
||||
%{ipv4_address: _address, ifname: _interface}}, state)
|
||||
do
|
||||
Farmbot.System.Network.on_connect(fn() ->
|
||||
Farmbot.System.Network.stop_interface("wlan0")
|
||||
end)
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
# just print hostapd data
|
||||
def handle_event({:hostapd, data}, state) do
|
||||
Logger.debug ">> got some hostapd data: #{data}"
|
||||
{:ok, state}
|
||||
end
|
||||
|
||||
# def handle_event(event, state) do
|
||||
# Logger.warn "got event: #{inspect event}"
|
||||
# {:ok, state}
|
||||
# end
|
||||
|
||||
# handle stray events that we don't care about
|
||||
def handle_event(_, state), do: {:ok, state}
|
||||
end
|
Loading…
Reference in New Issue