fix ssh, remove Downloader

pull/321/head
connor rigby 2017-06-13 15:31:33 -07:00
parent d569618679
commit f29798eba1
31 changed files with 206 additions and 345 deletions

View File

@ -0,0 +1 @@
../tmp/authorized_keys

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAvkZEivnVE57KRR4viuL/Zai1ZdMI4xZW2SdAW+5oHEZxKsXV
1+tJ1UWGw4Pq/IRIAPkI45nlEjfZeTN/enryMO6UOv75mkZBo0GQBYs2FjGZ7BWh
bf/8WFkeuE3hOp1Y+DTsX4CMm75kyVCmCYba0ZskbhawwCWg7d2hsnVoH3Fm5Wsu
s8Lg1fHXQmGxS9vEZ2sTehPhKNM1O4lyYkZE0v/Whf7AbwE/BKkg5+lcDke7GScX
HpeqEv5Hk5HVL4d8O+7mkFPmIOBuHFJEvGwAwu0nP0jsYuD+velQPd8UFqp+x4+/
TLe+sxEUsGyULj5Rvj1Zc75LWTAqdpei8rL+ZwIDAQABAoIBAQCL6p/8kjpdcybK
LKhJOSMVXjoF5mlik8rJZFOvRmDIagRNr6zQWfSxH685lof1qFBKZtZih4grHWaN
+ZCEQGRYFxhpSZXCHGen5U5CxVvAdjj5oe5TNSoazW4JmTtGHu6Nll063QyIejki
0GtIFiJSgAMJ8SWIJiNwxslgsXUfqL7no1Ax1cmLCCjWDyFiTcUtj1niEsqF9/fH
+BMauA17D3SDvZz2cI8ZBPM23sEBTNhbcYXqEPY40Y6+1XLupb85rgAcnMK6bvA6
RIu7in1nnrzKeLbobCvV8N6ceooVJFqOlhnClOJnm6tXxRQoLFr4NNadSOZTbgae
Lk2QdMwBAoGBAOxXN+zm/z+4/9UvEiiQk6+DU4dEC5VLtSlL3t19eTxNxRxmzGu1
q8bjsLqeg1tFTR+QyRT6qSSYcQCKYmQYn5XyjqqDWgykqx+3TLSeftpyXHteJ4ly
ZT0l2eBls2kn3zKUwWsWgRgTHJAWqj5wv84tHeKEaeP4Vw8M1jn0FQgPAoGBAM4a
FmcVqB21f9drEQZCVyuxqrd3EesuBI+BmcdzMMWdNrwPSqKkLwXYyhkii8iE6SHy
ufbGUUg7RoXYCXC6TU7U3WC2IR63cJMwubeNqNo8BGivjM+OC3UWt8jYt4gsFSiS
FH03qmKmFbwVQJJa5jBrTUhDIr+e9IGuJiZdzAwpAoGBAOGZ9BGZA2hxkQyP7TZ5
o/BzXOWPJ7tw1FLCUNB++J5+4jxVSKc8eOfW6xmJPHyAb7lE/R2glJsRRTUt4/QG
qTd6nYLY9bmB6rPQxlDXxuGBLxS4cOc8IrW3Dv6sNhGX9PAUnXk6tyoi/C3y5LnL
6NvHp3JV0brCkWAVcd1KSFUzAoGAajdZmlC37m+ubed4w6AG6rbKg3iy3GA/63Fg
PkDoPr+yCKNc1IsGzN2X8fIy1elheKRuIHa+Rxp9UtcurlDlLJ5ZZNEYKothUgoH
fEheX/IUV5s5V3IeB37ownKly2Dkmp8GPi4k+mgbxW2ydHqyLPvvvTEURGr4GakV
cipe0VkCgYEAz8MvEWp8F7ZSb+JL1ov7cpoqyTJVd9n2x0A58SAjTPo6od0FYzwZ
AVhCtkwiU7VV8xOudwrhqeGax0GsbIczYivoHMEm2ZCFHZo5aYrdShFoLgqJ+9C6
AuFBssHRz+nN1ErHNzeekW/WrW1cCrfvx6NLPXCoDuiLE5N4RIgUJDI=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+RkSK+dUTnspFHi+K4v9lqLVl0wjjFlbZJ0Bb7mgcRnEqxdXX60nVRYbDg+r8hEgA+QjjmeUSN9l5M396evIw7pQ6/vmaRkGjQZAFizYWMZnsFaFt//xYWR64TeE6nVj4NOxfgIybvmTJUKYJhtrRmyRuFrDAJaDt3aGydWgfcWblay6zwuDV8ddCYbFL28RnaxN6E+Eo0zU7iXJiRkTS/9aF/sBvAT8EqSDn6VwOR7sZJxcel6oS/keTkdUvh3w77uaQU+Yg4G4cUkS8bADC7Sc/SOxi4P696VA93xQWqn7Hj79Mt76zERSwbJQuPlG+PVlzvktZMCp2l6Lysv5n connor@connor-mini-pc

View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -0,0 +1 @@
../tmp/authorized_keys

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAvkZEivnVE57KRR4viuL/Zai1ZdMI4xZW2SdAW+5oHEZxKsXV
1+tJ1UWGw4Pq/IRIAPkI45nlEjfZeTN/enryMO6UOv75mkZBo0GQBYs2FjGZ7BWh
bf/8WFkeuE3hOp1Y+DTsX4CMm75kyVCmCYba0ZskbhawwCWg7d2hsnVoH3Fm5Wsu
s8Lg1fHXQmGxS9vEZ2sTehPhKNM1O4lyYkZE0v/Whf7AbwE/BKkg5+lcDke7GScX
HpeqEv5Hk5HVL4d8O+7mkFPmIOBuHFJEvGwAwu0nP0jsYuD+velQPd8UFqp+x4+/
TLe+sxEUsGyULj5Rvj1Zc75LWTAqdpei8rL+ZwIDAQABAoIBAQCL6p/8kjpdcybK
LKhJOSMVXjoF5mlik8rJZFOvRmDIagRNr6zQWfSxH685lof1qFBKZtZih4grHWaN
+ZCEQGRYFxhpSZXCHGen5U5CxVvAdjj5oe5TNSoazW4JmTtGHu6Nll063QyIejki
0GtIFiJSgAMJ8SWIJiNwxslgsXUfqL7no1Ax1cmLCCjWDyFiTcUtj1niEsqF9/fH
+BMauA17D3SDvZz2cI8ZBPM23sEBTNhbcYXqEPY40Y6+1XLupb85rgAcnMK6bvA6
RIu7in1nnrzKeLbobCvV8N6ceooVJFqOlhnClOJnm6tXxRQoLFr4NNadSOZTbgae
Lk2QdMwBAoGBAOxXN+zm/z+4/9UvEiiQk6+DU4dEC5VLtSlL3t19eTxNxRxmzGu1
q8bjsLqeg1tFTR+QyRT6qSSYcQCKYmQYn5XyjqqDWgykqx+3TLSeftpyXHteJ4ly
ZT0l2eBls2kn3zKUwWsWgRgTHJAWqj5wv84tHeKEaeP4Vw8M1jn0FQgPAoGBAM4a
FmcVqB21f9drEQZCVyuxqrd3EesuBI+BmcdzMMWdNrwPSqKkLwXYyhkii8iE6SHy
ufbGUUg7RoXYCXC6TU7U3WC2IR63cJMwubeNqNo8BGivjM+OC3UWt8jYt4gsFSiS
FH03qmKmFbwVQJJa5jBrTUhDIr+e9IGuJiZdzAwpAoGBAOGZ9BGZA2hxkQyP7TZ5
o/BzXOWPJ7tw1FLCUNB++J5+4jxVSKc8eOfW6xmJPHyAb7lE/R2glJsRRTUt4/QG
qTd6nYLY9bmB6rPQxlDXxuGBLxS4cOc8IrW3Dv6sNhGX9PAUnXk6tyoi/C3y5LnL
6NvHp3JV0brCkWAVcd1KSFUzAoGAajdZmlC37m+ubed4w6AG6rbKg3iy3GA/63Fg
PkDoPr+yCKNc1IsGzN2X8fIy1elheKRuIHa+Rxp9UtcurlDlLJ5ZZNEYKothUgoH
fEheX/IUV5s5V3IeB37ownKly2Dkmp8GPi4k+mgbxW2ydHqyLPvvvTEURGr4GakV
cipe0VkCgYEAz8MvEWp8F7ZSb+JL1ov7cpoqyTJVd9n2x0A58SAjTPo6od0FYzwZ
AVhCtkwiU7VV8xOudwrhqeGax0GsbIczYivoHMEm2ZCFHZo5aYrdShFoLgqJ+9C6
AuFBssHRz+nN1ErHNzeekW/WrW1cCrfvx6NLPXCoDuiLE5N4RIgUJDI=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+RkSK+dUTnspFHi+K4v9lqLVl0wjjFlbZJ0Bb7mgcRnEqxdXX60nVRYbDg+r8hEgA+QjjmeUSN9l5M396evIw7pQ6/vmaRkGjQZAFizYWMZnsFaFt//xYWR64TeE6nVj4NOxfgIybvmTJUKYJhtrRmyRuFrDAJaDt3aGydWgfcWblay6zwuDV8ddCYbFL28RnaxN6E+Eo0zU7iXJiRkTS/9aF/sBvAT8EqSDn6VwOR7sZJxcel6oS/keTkdUvh3w77uaQU+Yg4G4cUkS8bADC7Sc/SOxi4P696VA93xQWqn7Hj79Mt76zERSwbJQuPlG+PVlzvktZMCp2l6Lysv5n connor@connor-mini-pc

View File

@ -0,0 +1 @@
../tmp/authorized_keys

View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAvkZEivnVE57KRR4viuL/Zai1ZdMI4xZW2SdAW+5oHEZxKsXV
1+tJ1UWGw4Pq/IRIAPkI45nlEjfZeTN/enryMO6UOv75mkZBo0GQBYs2FjGZ7BWh
bf/8WFkeuE3hOp1Y+DTsX4CMm75kyVCmCYba0ZskbhawwCWg7d2hsnVoH3Fm5Wsu
s8Lg1fHXQmGxS9vEZ2sTehPhKNM1O4lyYkZE0v/Whf7AbwE/BKkg5+lcDke7GScX
HpeqEv5Hk5HVL4d8O+7mkFPmIOBuHFJEvGwAwu0nP0jsYuD+velQPd8UFqp+x4+/
TLe+sxEUsGyULj5Rvj1Zc75LWTAqdpei8rL+ZwIDAQABAoIBAQCL6p/8kjpdcybK
LKhJOSMVXjoF5mlik8rJZFOvRmDIagRNr6zQWfSxH685lof1qFBKZtZih4grHWaN
+ZCEQGRYFxhpSZXCHGen5U5CxVvAdjj5oe5TNSoazW4JmTtGHu6Nll063QyIejki
0GtIFiJSgAMJ8SWIJiNwxslgsXUfqL7no1Ax1cmLCCjWDyFiTcUtj1niEsqF9/fH
+BMauA17D3SDvZz2cI8ZBPM23sEBTNhbcYXqEPY40Y6+1XLupb85rgAcnMK6bvA6
RIu7in1nnrzKeLbobCvV8N6ceooVJFqOlhnClOJnm6tXxRQoLFr4NNadSOZTbgae
Lk2QdMwBAoGBAOxXN+zm/z+4/9UvEiiQk6+DU4dEC5VLtSlL3t19eTxNxRxmzGu1
q8bjsLqeg1tFTR+QyRT6qSSYcQCKYmQYn5XyjqqDWgykqx+3TLSeftpyXHteJ4ly
ZT0l2eBls2kn3zKUwWsWgRgTHJAWqj5wv84tHeKEaeP4Vw8M1jn0FQgPAoGBAM4a
FmcVqB21f9drEQZCVyuxqrd3EesuBI+BmcdzMMWdNrwPSqKkLwXYyhkii8iE6SHy
ufbGUUg7RoXYCXC6TU7U3WC2IR63cJMwubeNqNo8BGivjM+OC3UWt8jYt4gsFSiS
FH03qmKmFbwVQJJa5jBrTUhDIr+e9IGuJiZdzAwpAoGBAOGZ9BGZA2hxkQyP7TZ5
o/BzXOWPJ7tw1FLCUNB++J5+4jxVSKc8eOfW6xmJPHyAb7lE/R2glJsRRTUt4/QG
qTd6nYLY9bmB6rPQxlDXxuGBLxS4cOc8IrW3Dv6sNhGX9PAUnXk6tyoi/C3y5LnL
6NvHp3JV0brCkWAVcd1KSFUzAoGAajdZmlC37m+ubed4w6AG6rbKg3iy3GA/63Fg
PkDoPr+yCKNc1IsGzN2X8fIy1elheKRuIHa+Rxp9UtcurlDlLJ5ZZNEYKothUgoH
fEheX/IUV5s5V3IeB37ownKly2Dkmp8GPi4k+mgbxW2ydHqyLPvvvTEURGr4GakV
cipe0VkCgYEAz8MvEWp8F7ZSb+JL1ov7cpoqyTJVd9n2x0A58SAjTPo6od0FYzwZ
AVhCtkwiU7VV8xOudwrhqeGax0GsbIczYivoHMEm2ZCFHZo5aYrdShFoLgqJ+9C6
AuFBssHRz+nN1ErHNzeekW/WrW1cCrfvx6NLPXCoDuiLE5N4RIgUJDI=
-----END RSA PRIVATE KEY-----

View File

@ -0,0 +1 @@
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+RkSK+dUTnspFHi+K4v9lqLVl0wjjFlbZJ0Bb7mgcRnEqxdXX60nVRYbDg+r8hEgA+QjjmeUSN9l5M396evIw7pQ6/vmaRkGjQZAFizYWMZnsFaFt//xYWR64TeE6nVj4NOxfgIybvmTJUKYJhtrRmyRuFrDAJaDt3aGydWgfcWblay6zwuDV8ddCYbFL28RnaxN6E+Eo0zU7iXJiRkTS/9aF/sBvAT8EqSDn6VwOR7sZJxcel6oS/keTkdUvh3w77uaQU+Yg4G4cUkS8bADC7Sc/SOxi4P696VA93xQWqn7Hj79Mt76zERSwbJQuPlG+PVlzvktZMCp2l6Lysv5n connor@connor-mini-pc

View File

@ -1,166 +0,0 @@
defmodule Downloader do
@moduledoc """
Blatently ripped off
from: https://github.com/nerves-project/nerves/blob/master/lib/nerves/utils/http_client.ex
"""
use GenServer
require Logger
@progress_steps 50
@redirect_status_codes [301, 302, 303, 307, 308]
def start_link do
start_httpc()
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def stop(client \\ __MODULE__) do
GenServer.stop(client)
end
def get(client \\ __MODULE__, url) do
GenServer.call(client, {:get, url}, :infinity)
end
def run(url, path) do
{:ok, file} = get(__MODULE__, url)
:ok = File.write!(path, file)
if File.exists?(path) do
path
else
throw :NO_FILE
end
end
def init([]) do
{:ok, %{
url: nil,
content_length: 0,
buffer: "",
buffer_size: 0,
filename: "",
caller: nil,
number_of_redirects: 0,
}}
end
def handle_call({:get, _url}, _, %{number_of_redirects: n}=s) when n > 5 do
GenServer.reply(s.caller, {:error, :too_many_redirects})
{:noreply, %{s | url: nil, number_of_redirects: 0, caller: nil}}
end
def handle_call({:get, url}, from, s) do
headers = [
{'Content-Type', 'application/octet-stream'}
]
http_opts = [timeout: :infinity, autoredirect: false]
opts = [stream: :self, receiver: self(), sync: false]
:httpc.request(:get,
{String.to_char_list(url), headers}, http_opts, opts, :nerves)
{:noreply, %{s | url: url, caller: from}}
end
def handle_info({:http, {_, :stream_start, headers}}, s) do
#TODO This is sometimes nil
# Something about chunked transfer-encoding
content_length = maybe_content_length(headers)
filename = try do
{_, filename} =
headers
|> Enum.find(fn({key, _}) -> key == 'content-disposition' end)
filename
|> to_string
|> String.split(";")
|> List.last
|> String.strip
|> String.trim("filename=")
rescue
_ ->
"unknown-filename"
end
{:noreply, %{s | content_length: content_length, filename: filename}}
end
def handle_info({:http, {_, :stream, data}}, s) do
size = byte_size(data) + s.buffer_size
buffer = s.buffer <> data
put_progress(size, s.content_length)
{:noreply, %{s | buffer_size: size, buffer: buffer}}
end
def handle_info({:http, {_, :stream_end, _headers}}, s) do
IO.write(:stderr, "\n")
GenServer.reply(s.caller, {:ok, s.buffer})
{:noreply, %{s | filename: "", content_length: 0, buffer: "", buffer_size: 0, url: nil}}
end
def handle_info({:http, {_ref, {{_, status_code, _}, headers, _body}}}, s) when status_code in @redirect_status_codes do
case Enum.find(headers, fn({key,_}) -> key == 'location' end) do
{'location', next_url} ->
handle_call({:get, List.to_string(next_url)}, s.caller, %{s | buffer: "", buffer_size: 0, number_of_redirects: s.number_of_redirects + 1})
_ ->
GenServer.reply(s.caller, {:error, status_code})
end
end
def handle_info({:http, {error, _headers}}, s) do
GenServer.reply(s.caller, {:error, error})
{:noreply, s}
end
def terminate(reason, state) do
GenServer.reply(state.caller, {:error, reason})
end
def put_progress(size, nil) do
case rem(size, 2) do
0 ->
IO.write(:stderr, "\r|-=-=-=-=-=-=-=-=-=-=-=-=-| ---%")
_ ->
IO.write(:stderr, "\r|=-=-=-=-=-=-=-=-=-=-=-=-=| ---%")
end
end
def put_progress(size, max) do
fraction = (size / max)
completed = trunc(fraction * @progress_steps)
percent = trunc(fraction * 100)
unfilled = @progress_steps - completed
if rem(size, 10) == 0, do: Logger.info("Download: #{percent}%")
IO.write(:stderr, "\r|#{String.duplicate("=", completed)}#{String.duplicate(" ", unfilled)}| #{percent}% (#{bytes_to_mb(size)} / #{bytes_to_mb(max)}) MB")
end
defp maybe_content_length(headers) do
try do
{_, content_length} =
headers
|> Enum.find(fn({key, _}) -> key == 'content-length' end)
{content_length, _} =
content_length
|> to_string()
|> Integer.parse()
content_length
rescue
_ -> nil
end
end
defp start_httpc do
:inets.start(:httpc, profile: :nerves)
opts = [
max_sessions: 8,
max_keep_alive_length: 4,
max_pipeline_length: 4,
keep_alive_timeout: 120_000,
pipeline_timeout: 60_000
]
:httpc.set_options(opts, :nerves)
end
defp bytes_to_mb(bytes) do
trunc(bytes / 1024 / 1024)
end
end

View File

@ -4,7 +4,7 @@ defmodule Farmbot.CeleryScript.Command.CheckUpdates do
"""
require Logger
alias Farmbot.CeleryScript.Command
alias Farmbot.CeleryScript.{Command, Error}
@behaviour Command
@doc ~s"""
@ -17,12 +17,13 @@ defmodule Farmbot.CeleryScript.Command.CheckUpdates do
def run(%{package: package}, [], context) do
case package do
"arduino_firmware" ->
raise "arduino firmware is now bundled into the OS."
raise Error, context: context,
message: "arduino firmware is now bundled into the OS."
"farmbot_os" ->
Farmbot.System.Updates.check_and_download_updates()
Farmbot.System.Updates.check_and_download_updates(context)
u -> raise("unknown package: #{u}")
u -> raise(Error, message: "unknown package: #{u}", context: context)
end
context
end

View File

@ -17,13 +17,18 @@ defmodule Farmbot.CeleryScript.Command.ExecuteScript do
@spec run(%{label: binary},
[Command.Pair.t], Context.t) :: Context.t | no_return
def run(%{label: uuid}, env_vars, context) when is_uuid(uuid) do
Command.set_user_env(%{}, env_vars, context)
new_context = Command.set_user_env(%{}, env_vars, context)
case Manager.lookup(context, uuid) do
{:ok, %Farmware{} = fw} -> Runtime.execute(context, fw)
{:ok, %Farmware{} = fw} -> Runtime.execute(new_context, fw)
{:error, e} ->
raise Error,
message: "Could not locate farmware: #{e}",
context: context
context: new_context
end
end
def run(%{label: not_uuid}, _, context) do
raise Error, context: context,
message: "Expected a uuid but got: #{inspect not_uuid}"
end
end

View File

@ -3,15 +3,9 @@ defmodule Farmbot.Database.Syncable.Regimen do
A Regimen from the Farmbot API.
"""
alias Farmbot.{Context, Database}
alias Farmbot.Database
alias Database.Syncable
use Syncable, model: [
:regimen_items
], endpoint: {"/regimens", "/regimens"}
defimpl Farmbot.FarmEvent.Executer, for: __MODULE__ do
def execute_event(regimen, %Context{} = ctx, now) do
{:ok, _pid} = Farmbot.Regimen.Supervisor.add_child(ctx, regimen, now)
end
end
end

View File

@ -3,7 +3,7 @@ defmodule Farmbot.Database.Syncable.Sequence do
A Sequence from the Farmbot API.
"""
alias Farmbot.{Context, Database}
alias Farmbot.{Database}
alias Database.Syncable
use Syncable, model: [
:version,

View File

@ -0,0 +1,5 @@
defimpl Farmbot.FarmEvent.Executer, for: Farmbot.Database.Syncable.Regimen do
def execute_event(regimen, %Farmbot.Context{} = ctx, now) do
{:ok, _pid} = Farmbot.Regimen.Supervisor.add_child(ctx, regimen, now)
end
end

View File

@ -5,7 +5,6 @@ defmodule Farmbot.FarmEvent.Runner do
require Logger
alias Farmbot.{Context, DebugLog, Database, CeleryScript}
import Farmbot.FarmEvent.Executer
alias CeleryScript.Ast
use DebugLog
use GenServer
alias Database.Syncable.{

View File

@ -9,8 +9,9 @@ defmodule Farmbot.FarmEvent.Supervisor do
children = [
worker(Farmbot.Regimen.Supervisor,
[context, [name: Farmbot.Regimen.Supervisor ]], [restart: :permanent]),
worker(Farmbot.FarmEvent.Runner,
[context, [name: Farmbot.FarmEvent.Runner ]], [restart: :permanent])
[context, [name: Farmbot.FarmEvent.Runner ]], [restart: :permanent])
]
opts = [strategy: :one_for_one]
supervise(children, opts)

View File

@ -1,95 +0,0 @@
defmodule Farmbot.System.Network.SSH do
@moduledoc """
Module to manage SSH via an Erlang port.
"""
use GenServer
require Logger
alias Farmbot.System.FS
@banner "/tmp/banner"
@cmd "dropbear -R -F -a -B -E -b #{@banner}"
@var_run_dir "/var/run/dropbear"
def init(_) do
Logger.info ">> Is starting SSH service."
Process.flag(:trap_exit, true)
make_banner()
# this is where dropbear puts keys and stuff.
if !File.exists? @var_run_dir do
Logger.info ">> needs to create a place for ssh keys."
File.mkdir_p @var_run_dir
end
if File.exists? "#{FS.path()}/dropbear_ecdsa_host_key" do
Logger.info ">> loading old ssh keys"
File.cp "#{FS.path()}/dropbear_ecdsa_host_key", @var_run_dir
end
port = open_port()
{:ok, port}
end
def open_port do
Port.open({:spawn, @cmd},
[:stream,
:binary,
:exit_status,
:hide,
:use_stdio,
:stderr_to_stdout])
end
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}"
new_state = open_port()
{:noreply, new_state}
end
def handle_info({_, {:data, data}}, port) when is_bitstring(data) do
Logger.info ">> got ssh data: #{String.trim(data)}"
save_contents()
{:noreply, port}
end
def handle_info(_i, port) do
{:noreply, port}
end
def terminate(_reason, _state) do
save_contents()
end
def save_contents do
Logger.info ">> Saving ssh keys"
case File.read "#{@var_run_dir}/dropbear_ecdsa_host_key" do
{:ok, c} ->
FS.transaction fn() ->
File.write "#{FS.path()}/dropbear_ecdsa_host_key", c
end
_ -> :ok
end
:ok
end
def make_banner do
contents =
"""
__________________________________________________________________
| WELCOME TO FARMBOT SHELL. I AM TRULY SORRY YOU HAVE TO BE HERE |
|_______________________________|________________________________|
| THERE IS NO $PATH | THERE IS NO BASH |
| THERE IS NO SU | THERE IS NO APT-GET |
| THERE IS NO MAKE | THERE IS NO WGET |
|_______________________________|________________________________|
"""
File.write(@banner, contents)
end
end

View File

@ -4,7 +4,7 @@ defmodule Farmbot.System.Network do
"""
require Logger
alias Farmbot.System.FS.ConfigStorage, as: CS
alias Farmbot.System.Network.{Ntp, SSH}
alias Farmbot.System.Network.Ntp
alias Farmbot.{Context, Auth, HTTP, DebugLog}
use DebugLog
use GenServer
@ -115,17 +115,26 @@ defmodule Farmbot.System.Network do
end
defp maybe_start_ssh do
{:ok, ssh} = get_config("ssh")
try do
if ssh do
Logger.info ">> starting SSH server."
spawn SSH, :start_link, []
end
{:ok, public_key} = get_config("ssh")
ssh_dir = '/ssh'
ssh_dir_exists? = File.exists?(ssh_dir)
shell = {Elixir.IEx, :start, []}
# shell = {Elixir.Nerves.Runtime.Shell, :start, []}
if is_binary(public_key) and ssh_dir_exists? do
File.write "#{ssh_dir}/authorized_keys", public_key
debug_log "Starting SSH."
:ok = :ssh.start()
opts =
[
{:shell, shell},
{:system_dir, ssh_dir},
{:user_dir, ssh_dir},
]
{:ok, _sshd} = :ssh.daemon(22, opts)
:ok
else
debug_log "Not starting SSH"
:ok
rescue
error ->
Logger.warn(">> Failed to start ssh: #{inspect error}")
:ok
end
end
@ -184,29 +193,34 @@ defmodule Farmbot.System.Network do
"""
def on_connect(context, pre_fun \\ nil, post_fun \\ nil)
def on_connect(%Context{} = context, pre_fun, post_fun) do
# Start the Downloader http client.
Supervisor.start_child(Farmbot.System.Supervisor,
Supervisor.Spec.worker(Downloader, [], [restart: :permanent]))
try do
# If we were supplied a pre connect callback, do that.
if pre_fun, do: pre_fun.()
# If we were supplied a pre connect callback, do that.
if pre_fun, do: pre_fun.()
:ok = connection_test(context)
:ok = connection_test(context)
:ok = maybe_set_time()
:ok = maybe_start_ssh()
:ok = maybe_get_fpf(context)
:ok = maybe_set_time()
:ok = maybe_start_ssh()
:ok = maybe_get_fpf(context)
Logger.info ">> is trying to log in."
{:ok, token} = Auth.try_log_in!(context.auth)
Logger.info ">> is trying to log in."
{:ok, token} = Auth.try_log_in!(context.auth)
:ok = maybe_setup_rollbar(token)
:ok = maybe_setup_rollbar(token)
if post_fun do
post_fun.(token)
end
if post_fun do
post_fun.(token)
{:ok, token}
rescue
exception ->
Logger.error "Farmbot failed to log in."
debug_log("#{inspect exception}")
Farmbot.System.factory_reset("#{inspect exception}")
end
{:ok, token}
end
if Mix.env == :prod do
@ -290,10 +304,6 @@ defmodule Farmbot.System.Network do
end
def terminate(reason, state) do
ssh_pid = Process.whereis(SSH)
if ssh_pid do
SSH.stop(reason)
end
target_pid = Process.whereis(mod(state.target))
if target_pid do
GenServer.stop(target_pid, reason)
@ -301,7 +311,7 @@ defmodule Farmbot.System.Network do
end
# Behavior
@type return_type :: :ok | {:error, term}
@type return_type :: :ok | {:error, term}
@callback scan(Context.t, binary) :: [binary] | {:error, term}
@callback enumerate(Context.t) :: [binary] | {:error, term}
@callback start_interface(Context.t, binary, map) :: return_type

View File

@ -30,7 +30,9 @@ defmodule Farmbot.System.NervesCommon.FileSystem do
:ok = format_state_part()
else
# If not, we are fine. continue
_ -> :ok
_ ->
File.touch "/tmp/authorized_keys"
:ok
end
:ok = tzdata_hack()

View File

@ -26,7 +26,7 @@ defmodule Farmbot.System.Updates do
def do_update_check do
context = Farmbot.Context.new()
if Farmbot.BotState.get_config(context, :os_auto_update) do
check_and_download_updates()
check_and_download_updates(context)
else
Logger.info ">> Will not do update check!"
end
@ -35,13 +35,14 @@ defmodule Farmbot.System.Updates do
@doc """
Checks for updates, and if there is an update, downloads, and applies it.
"""
@spec check_and_download_updates :: :ok | {:error, term} | :no_updates
def check_and_download_updates do
case check_updates() do
@spec check_and_download_updates(Context.t)
:: :ok | {:error, term} | :no_updates
def check_and_download_updates(%Context{} = ctx) do
case check_updates(ctx) do
{:update, url} ->
Logger.info ">> has found a new Operating System update! #{url}",
type: :busy
install_updates(url)
install_updates(ctx, url)
:no_updates ->
Logger.info ">> is already on the latest Operating System version!",
type: :success
@ -55,9 +56,9 @@ defmodule Farmbot.System.Updates do
@doc """
Checks for updates
"""
@spec check_updates :: {:update, binary} | :no_updates | {:error, term}
def check_updates do
context = Context.new()
@spec check_updates(Context.t)
:: {:update, binary} | :no_updates | {:error, term}
def check_updates(%Context{} = context) do
current = Farmbot.BotState.get_os_version(context)
if String.contains?(current, "rc") do
msg = "Release Candidate Releases don't currently support updates!"
@ -68,7 +69,8 @@ defmodule Farmbot.System.Updates do
end
end
@spec check_updates :: {:update, binary} | :no_updates | {:error, term}
@spec check_updates(Context.t)
:: {:update, binary} | :no_updates | {:error, term}
defp do_http_req(%Context{} = ctx) do
case HTTP.get(ctx, releases_url(ctx)) do
{:ok, %HTTP.Response{body: body, status_code: 200}} ->
@ -88,15 +90,11 @@ defmodule Farmbot.System.Updates do
@doc """
Installs an update from a url
"""
@spec install_updates(String.t) :: no_return
def install_updates(url) do
@spec install_updates(Context.t, String.t) :: no_return
def install_updates(%Context{} = context, url) do
# Ignore the compiler warning here.
# "I'll fix it later i promise" -- Connor Rigby
case Process.whereis(Downloader) do
pid when is_pid(pid) -> :ok
_ -> Downloader.start_link()
end
path = Downloader.run(url, @path)
path = HTTP.download_file!(context, url, @path)
case File.stat(path) do
{:ok, file} ->
Logger.info "Found file: #{inspect file}", type: :success

View File

@ -103,17 +103,20 @@ defmodule Farmbot.Transport do
Emit a message over all transports
"""
@spec emit(Context.t, term) :: :ok
def emit(%Context{} = ctx, message), do: GenStage.cast(ctx.transport, {:emit, message})
def emit(%Context{} = ctx, message),
do: GenStage.cast(ctx.transport, {:emit, message})
@doc """
Log a log message over all transports
"""
@spec log(Context.t, term) :: :ok
def log(%Context{} = ctx, message), do: GenStage.cast(ctx.transport, {:log, message})
def log(%Context{} = ctx, message),
do: GenStage.cast(ctx.transport, {:log, message})
@doc """
Force a state push
"""
@spec force_state_push(Context.t) :: State.t
def force_state_push(%Context{} = ctx), do: GenServer.call(ctx.transport, :force_state_push)
def force_state_push(%Context{} = ctx),
do: GenServer.call(ctx.transport, :force_state_push)
end

View File

@ -75,7 +75,8 @@ defmodule Farmbot.Mixfile do
:gen_mqtt,
:ex_json_schema,
:fs,
# :rollbax
:ex_rollbar,
:ssh
] ++ included_apps(Mix.env)]
end

View File

@ -0,0 +1,15 @@
defmodule DisableSSHAgain do
def run(json) do
network = json["network"]
net_config =
if network do
# if we have a network config, make ssh key false
Map.put(network, "ssh", false)
else
# if no network, just make it false.
false
end
%{json | "network" => net_config}
end
end

View File

@ -1,12 +1,12 @@
defmodule Farmbot.CeleryScript.Command.CheckUpdatesTest do
use ExUnit.Case, async: true
alias Farmbot.CeleryScript.{Ast, Command}
alias Farmbot.CeleryScript.{Ast, Command, Error}
import Mock
test "doesnt check for arduino updates anymore" do
ctx = Farmbot.Context.new()
ast = %Ast{kind: "check_updates", args: %{package: "arduino_firmware"}, body: []}
assert_raise RuntimeError, "arduino firmware is now bundled into the OS.", fn() ->
assert_raise Error, "arduino firmware is now bundled into the OS.", fn() ->
Command.do_command(ast, ctx)
end
end
@ -14,18 +14,18 @@ defmodule Farmbot.CeleryScript.Command.CheckUpdatesTest do
test "doesnt know what to do with other strings" do
ctx = Farmbot.Context.new()
ast = %Ast{kind: "check_updates", args: %{package: "explorer.exe"}, body: []}
assert_raise RuntimeError, "unknown package: #{ast.args.package}", fn() ->
assert_raise Error, "unknown package: #{ast.args.package}", fn() ->
Command.do_command(ast, ctx)
end
end
test "does update check for fbos" do
# the real update thing will be checked elsewhere, this is just for the ast node.
with_mock Farmbot.System.Updates, [check_and_download_updates: fn() -> :ok end] do
with_mock Farmbot.System.Updates, [check_and_download_updates: fn(_) -> :ok end] do
ctx = Farmbot.Context.new()
ast = %Ast{kind: "check_updates", args: %{package: "farmbot_os"}, body: []}
Command.do_command(ast, ctx)
assert called Farmbot.System.Updates.check_and_download_updates
assert called Farmbot.System.Updates.check_and_download_updates(ctx)
end
end
end

View File

@ -7,7 +7,7 @@ interface Props {
mobx: MainState;
}
export function AdvancedSettings({ mobx }: Props) {
let ssh = false;
let ssh: string | false = false;
let ntp = false;
let hasNetwork = mobx.configuration.network ? true : false;
let customFW = false;
@ -53,11 +53,10 @@ export function AdvancedSettings({ mobx }: Props) {
{/* SSH */}
<fieldset>
<label> Enable SSH </label>
<input type="checkbox" defaultChecked={ssh}
onChange={(event) => {
let blah = event.currentTarget.checked;
mobx.toggleSSH(blah);
}} />
<input onChange={(event) => {
let blah = event.currentTarget.value;
mobx.enableSSH(blah);
}} />
</fieldset>
{/* NTP */}
@ -75,4 +74,4 @@ export function AdvancedSettings({ mobx }: Props) {
</div>
}
}

View File

@ -83,7 +83,7 @@ export interface BotConfigFile {
/** Should this bot set time after boot. */
ntp: boolean;
/** ssh */
ssh: boolean;
ssh: string | false;
} | false;
/** Just holds the server. All other authorization should use a jwt */
authorization: {

View File

@ -185,9 +185,9 @@ export class MainState {
}
@action
toggleSSH(b: boolean) {
enableSSH(public_key: string) {
if (this.configuration.network) {
this.configuration.network.ssh = b;
this.configuration.network.ssh = public_key;
}
}