start working out configurator
parent
05c2c4cb61
commit
4c40d288d7
|
@ -14,9 +14,9 @@ config :farmbot, Farmbot.System.ConfigStorage,
|
|||
|
||||
config :farmbot, data_path: "/root"
|
||||
|
||||
# Configure your our system.
|
||||
# Default implementation needs no special stuff.
|
||||
# Configure your our init system.
|
||||
config :farmbot, :init, [
|
||||
# Allows for first boot configuration.
|
||||
Farmbot.Target.Bootstrap.Configurator,
|
||||
|
||||
# Start up Network
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
# Additional configuration for erlinit
|
||||
|
||||
# Turn on the debug prints
|
||||
#-v
|
||||
|
||||
# Specify where erlinit should send the IEx prompt. Only one may be enabled at
|
||||
# a time.
|
||||
-c ttyS0 # UART pins on the GPIO connector
|
||||
# -c tty1 # HDMI output
|
||||
|
||||
# If more than one tty are available, always warn if the user is looking at the
|
||||
# wrong one.
|
||||
--warn-unused-tty
|
||||
|
||||
# Use dtach to capture the iex session so that it can be redirected to the
|
||||
# app's GUI
|
||||
#-s "/usr/bin/dtach -N /tmp/iex_prompt"
|
||||
|
||||
# Specify the user and group IDs for the Erlang VM
|
||||
#--uid 100
|
||||
#--gid 200
|
||||
|
||||
# Uncomment to hang the board rather than rebooting when Erlang exits
|
||||
# NOTE: Do not enable on production boards
|
||||
--hang-on-exit
|
||||
|
||||
# Change the graceful shutdown time. If 10 seconds isn't long enough between
|
||||
# calling "poweroff", "reboot", or "halt" and :init.stop/0 stopping all OTP
|
||||
# applications, enable this option with a new timeout in milliseconds.
|
||||
#--graceful-shutdown-timeout 15000
|
||||
|
||||
# Optionally run a program if the Erlang VM exits
|
||||
#--run-on-exit /bin/sh
|
||||
|
||||
# Enable UTF-8 filename handling in Erlang and custom inet configuration
|
||||
-e LANG=en_US.UTF-8;LANGUAGE=en;ERL_INETRC=/etc/erl_inetrc;ERL_CRASH_DUMP=/root/crash.dump
|
||||
|
||||
# Mount the application partition
|
||||
# NOTE: This must match the location in the fwup.conf. If it doesn't the system
|
||||
# will probably still work fine, but you won't get shell history since
|
||||
# bootloader/nerves_runtime can't mount the application filesystem before
|
||||
# the history is loaded. If this mount fails due to corruption, etc.,
|
||||
# nerves_runtime will auto-format it. Your applications will need to handle
|
||||
# initializing any expected files and folders.
|
||||
-m /dev/mmcblk0p3:/root:ext4::
|
||||
|
||||
# Erlang release search path
|
||||
-r /srv/erlang
|
||||
|
||||
# Assign a unique hostname based on the board id
|
||||
-d "/usr/bin/boardid -b rpi -n 4"
|
||||
-n nerves-%.4s
|
||||
|
||||
# If using bootloader (https://github.com/nerves-project/bootloader), start the
|
||||
# bootloader OTP release up first. If bootloader isn't around, erlinit fails back
|
||||
# to the main OTP release.
|
||||
--boot bootloader
|
1
mix.exs
1
mix.exs
|
@ -104,6 +104,7 @@ defmodule Farmbot.Mixfile do
|
|||
{:nerves_runtime, "~> 0.4"},
|
||||
{:nerves_network, "~> 0.3"},
|
||||
{:nerves_firmware_ssh, "~> 0.2"},
|
||||
{:dhcp_server, "~> 0.1.3"}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
"cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [], [], "hexpm"},
|
||||
"db_connection": {:hex, :db_connection, "1.1.2", "2865c2a4bae0714e2213a0ce60a1b12d76a6efba0c51fbda59c9ab8d1accc7a8", [], [{:connection, "~> 1.0.2", [hex: :connection, repo: "hexpm", optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"decimal": {:hex, :decimal, "1.4.0", "fac965ce71a46aab53d3a6ce45662806bdd708a4a95a65cde8a12eb0124a1333", [], [], "hexpm"},
|
||||
"dhcp_server": {:hex, :dhcp_server, "0.1.3", "9423311a7aa25bf517f5504f085a7d69e7dc98e98cc123ee77f5e8ae4fadc701", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nerves_network, ">= 0.0.0", [hex: :nerves_network, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"distillery": {:hex, :distillery, "1.5.1", "7ad7354214959c0f65f57ddd49478c81c3b2733ca2e5ccfb9eb55351108466aa", [], [], "hexpm"},
|
||||
"dns": {:hex, :dns, "1.0.1", "1d88187fdf564d937cee202949141090707fd0c9d7fcae903a6878ef24ef5d1e", [], [{:socket, "~> 0.3.12", [hex: :socket, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ecto": {:hex, :ecto, "2.2.4", "defde3c8eca385bd86466d2e1491d19e77f9b79ad996dc8e89e4e107f3942f40", [], [{:db_connection, "~> 1.1", [hex: :db_connection, repo: "hexpm", optional: true]}, {:decimal, "~> 1.2", [hex: :decimal, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.13.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
|
@ -28,6 +29,7 @@
|
|||
"nerves_network_interface": {:hex, :nerves_network_interface, "0.4.2", "7a3663a07803f2f9f1e37146714d24ccec1e9349268586e4ed8c41f38641d837", [], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_runtime": {:hex, :nerves_runtime, "0.4.4", "26034bc7d13dbd46aab2f429f988656621a4d91872ccf5fa748c16630bd65016", [], [{: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, "0.13.8", "bca89f31ef27ddad48feb30de648913f0091e205652d005b95255c49e743d087", [], [], "hexpm"},
|
||||
"nerves_system_farmbot_rpi3": {:hex, :nerves_system_farmbot_rpi3, "0.16.2-farmbot", "1e913b6360bdcac8e9e11b22f63da8c48cb6cd1f9a6ba5c8aa43a44121693166", [], [{:nerves, "~> 0.7", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "~> 0.13.7", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_toolchain_arm_unknown_linux_gnueabihf, "~> 0.11.0", [hex: :nerves_toolchain_arm_unknown_linux_gnueabihf, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_system_rpi3": {:hex, :nerves_system_rpi3, "0.16.1", "46b2fc942d7434d9eb8164c06c25a94ee6dd6db7e88c857101e5a06e6b7d01a8", [], [{:nerves, "~> 0.7", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_system_br, "~> 0.13.7", [hex: :nerves_system_br, repo: "hexpm", optional: false]}, {:nerves_toolchain_arm_unknown_linux_gnueabihf, "~> 0.11.0", [hex: :nerves_toolchain_arm_unknown_linux_gnueabihf, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_toolchain_arm_unknown_linux_gnueabihf": {:hex, :nerves_toolchain_arm_unknown_linux_gnueabihf, "0.11.0", "8d7606275a2d19de26ae238cd59475f4c06679aa9222b8987518d7c8a7beae51", [], [{:nerves, "~> 0.7", [hex: :nerves, repo: "hexpm", optional: false]}, {:nerves_toolchain_ctng, "~> 1.1", [hex: :nerves_toolchain_ctng, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"nerves_toolchain_ctng": {:hex, :nerves_toolchain_ctng, "1.1.0", "0f03e4a3f3beef5fe271de0148b9f106c417e57f303f635c21c74b4bd6eb68ee", [], [], "hexpm"},
|
||||
|
@ -48,4 +50,4 @@
|
|||
"tzdata": {:hex, :tzdata, "0.1.201605", "0c4184819b9d6adedcc02107b68321c45d8e853def7a32629b7961b9f2e95f33", [], [], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [], [], "hexpm"},
|
||||
"uuid": {:hex, :uuid, "1.1.7", "007afd58273bc0bc7f849c3bdc763e2f8124e83b957e515368c498b641f7ab69", [], [], "hexpm"},
|
||||
"vmq_commons": {:hex, :vmq_commons, "1.0.0", "5f5005c12db33f92f40e818a3617fb148972d59adcf99298c9d3808ef3582e34", [], [], "hexpm"}}
|
||||
"vmq_commons": {:git, "https://github.com/farmbot-labs/vmq_commons.git", "e780a297d5807a3537723d590036d37abce07cc5", []}}
|
||||
|
|
|
@ -22,14 +22,14 @@ defmodule Farmbot.Target.Bootstrap.Configurator do
|
|||
def start_link(_, opts) do
|
||||
Logger.info "Configuring Farmbot."
|
||||
supervisor = Supervisor.start_link(__MODULE__, [self()], opts)
|
||||
# case supervisor do
|
||||
# {:ok, pid} ->
|
||||
# receive do
|
||||
# :ok -> stop(pid, :ignore)
|
||||
# {:error, _reason} = err -> stop(pid, err)
|
||||
# end
|
||||
# :ignore -> :ignore
|
||||
# end
|
||||
case supervisor do
|
||||
{:ok, pid} ->
|
||||
receive do
|
||||
:ok -> stop(pid, :ignore)
|
||||
{:error, _reason} = err -> stop(pid, err)
|
||||
end
|
||||
:ignore -> :ignore
|
||||
end
|
||||
end
|
||||
|
||||
def init(cb) do
|
||||
|
@ -39,7 +39,8 @@ defmodule Farmbot.Target.Bootstrap.Configurator do
|
|||
import Supervisor.Spec
|
||||
:ets.new(:session, [:named_table, :public, read_concurrency: true])
|
||||
children = [
|
||||
Plug.Adapters.Cowboy.child_spec(:http, Farmbot.Target.Bootstrap.Configurator.Router, [], [port: 4001])
|
||||
Plug.Adapters.Cowboy.child_spec(:http, Farmbot.Target.Bootstrap.Configurator.Router, [], [port: 80]),
|
||||
worker(Farmbot.Target.Bootstrap.Configurator.CaptivePortal, [cb])
|
||||
]
|
||||
opts = [strategy: :one_for_one]
|
||||
supervise(children, opts)
|
||||
|
|
|
@ -0,0 +1,156 @@
|
|||
defmodule Farmbot.Target.Bootstrap.Configurator.CaptivePortal do
|
||||
defmodule Hostapd do
|
||||
@moduledoc """
|
||||
Manages an OS process of hostapd.
|
||||
"""
|
||||
|
||||
defmodule State do
|
||||
@moduledoc false
|
||||
defstruct [:hostapd, :interface]
|
||||
end
|
||||
|
||||
use GenServer
|
||||
require Logger
|
||||
|
||||
@hostapd_conf_file "hostapd.conf"
|
||||
@hostapd_pid_file "hostapd.pid"
|
||||
|
||||
@doc ~s"""
|
||||
Example:
|
||||
Iex> Hostapd.start_link ip_address: "192.168.24.1",
|
||||
...> manager: Farmbot.Network.Manager, interface: "wlan0"
|
||||
"""
|
||||
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)
|
||||
Logger.info ">> is starting hostapd on #{interface}"
|
||||
|
||||
{hostapd_port, hostapd_os_pid} = setup_hostapd(interface, "192.168.24.1")
|
||||
|
||||
state = %State{
|
||||
hostapd: {hostapd_port, hostapd_os_pid},
|
||||
interface: interface,
|
||||
}
|
||||
{:ok, state}
|
||||
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 =
|
||||
"ip" |> System.cmd(["link", "set", "#{interface}", "up"])
|
||||
|> print_cmd
|
||||
:ok =
|
||||
"ip" |> System.cmd(["addr", "add", "#{ip_addr}/24", "dev", "#{interface}"])
|
||||
|> print_cmd
|
||||
:ok
|
||||
end
|
||||
|
||||
defp hostapd_ip_settings_down(interface, ip_addr) do
|
||||
:ok =
|
||||
"ip" |> System.cmd(["link", "set", "#{interface}", "down"])
|
||||
|> print_cmd
|
||||
:ok =
|
||||
"ip" |> System.cmd(["addr", "del", "#{ip_addr}/24", "dev", "#{interface}"])
|
||||
|> print_cmd
|
||||
:ok =
|
||||
"ip" |> System.cmd(["link", "set", "#{interface}", "up"])
|
||||
|> print_cmd
|
||||
: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
|
||||
[name, "farmbot-" <> id] =
|
||||
node_str |> String.split("@")
|
||||
name <> "-" <> id
|
||||
end
|
||||
|
||||
defp kill(os_pid),
|
||||
do: :ok = "kill" |> System.cmd(["15", "#{os_pid}"]) |> print_cmd
|
||||
|
||||
defp print_cmd({_, 0}), do: :ok
|
||||
defp print_cmd({res, num}) do
|
||||
Logger.error ">> encountered an error (#{num}): #{res}"
|
||||
:error
|
||||
end
|
||||
|
||||
def handle_info({port, {:data, data}}, state) do
|
||||
{hostapd_port, _} = state.hostapd
|
||||
cond do
|
||||
port == hostapd_port ->
|
||||
handle_hostapd(data, state)
|
||||
true -> {:noreply, state}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_info(_, state), do: {:noreply, state}
|
||||
|
||||
defp handle_hostapd(data, state) when is_bitstring(data) do
|
||||
String.trim(data) |> Logger.debug()
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp handle_hostapd(_, state), do: {:noreply, state}
|
||||
|
||||
def terminate(_, state) do
|
||||
Logger.info ">> is stopping hostapd"
|
||||
{_hostapd_port, hostapd_pid} = state.hostapd
|
||||
:ok = kill(hostapd_pid)
|
||||
hostapd_ip_settings_down(state.interface, state.ip_addr)
|
||||
File.rm_rf! "/tmp/hostapd"
|
||||
end
|
||||
end
|
||||
use GenServer
|
||||
require Logger
|
||||
@interface "wlan0"
|
||||
|
||||
def start_link(cb) do
|
||||
GenServer.start_link(__MODULE__, [cb], name: __MODULE__)
|
||||
end
|
||||
|
||||
def init([cb]) do
|
||||
Logger.debug "Starting captive portal."
|
||||
{:ok, hostapd} = Hostapd.start_link(interface: @interface)
|
||||
{:ok, dhcp_server} = DHCPServer.start_link(interface: @interface)
|
||||
{:ok, %{callback: cb, hostapd: hostapd, hcp_server: dhcp_server}}
|
||||
end
|
||||
|
||||
def terminate(_, state) do
|
||||
Logger.debug "Stopping captive portal."
|
||||
GenServer.stop(state.hostapd, :normal)
|
||||
GenServer.stop(state.dhcp_server, :normal)
|
||||
send state.callback, :ok
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue