Awesome
SSHSubsystemFwup
This library provides an ssh
subsystem that applies Nerves "over-the-air" firmware updates. It is an
alternative to
nerves_firmware_ssh
that extracts the update service to a :ssh.daemon/1
spec. This trims down the
responsibilities of the library and makes it possible to:
- Customize ssh authentication (for example, password-based auth is possible)
- Handle host keys differently and more securely
- Run firmware updates on port 22 with other ssh services
In addition, the protocol for sending updates over ssh has been simplified. If
you're coming from nerves_firmware_ssh
, you'll have used the upload.sh
script or mix upload
. This library provides the same interface. If using
upload.sh
, you will need to rerun mix firmware.gen.script
since the script
has changed.
Installation
The easiest installation is to use
nerves_ssh
and have it bring
in this library as a dependency. See that project for details.
However, if you do not want to use nerves_ssh
, here's what do do. First, add
the dependency:
def deps do
[{:ssh_subsystem_fwup, "~> 0.6.0"}]
end
Then add ssh subsystem spec to the call that starts the ssh
daemon. This code
will look something like:
{:ok, ref} =
:ssh.daemon([
{:subsystems, [SSHSubsystemFwup.subsystem_spec()]}
])
You will likely have many more options passed to the ssh.daemon
.
Uploading firmware
There are two ways of uploading firmware. The first is to run:
mix upload
That doesn't work for everyone due to ssh
authentication preferences. The
alternative is to use commandline ssh
. For convenience, ssh_subsystem_fwup
can generate a script that makes this easier. Go to your Nerves project
directory and run:
mix firmware.gen.script
This should create an upload.sh
script. Frequently when starting out, you can
run ./upload.sh
without arguments since it will guess that it's supposed to
upload to nerves.local
. To specify a device to upload to, pass the device's
hostname as the first argument. For example:
$ ./upload.sh nerves-1234.local
fwup: Upgrading partition B
|====================================| 100% (32.34 / 32.34) MB
Success!
Elapsed time: 4.720 s
Disconnected from 172.31.207.89 port 22
Note that the .local
address assumes that mDNS has been configured on the
device and that mDNS works on your network and OS. That's not always the case
and a frequent source of frustration when it fails. When in doubt, check that
you can upload to the device's IP address. You can get the IP address from your
router or by connecting to the device's IEx prompt and running ifconfig
.
Upload protocol
It's not necessary to use the upload.sh
script. The following line is
equivalent:
cat $firmware | ssh -s $nerves_device fwup
Configuration
The default options should satisfy most use cases, but it's possible to alter
how updates are applied by passing options when creating the SSH subsystem spec
(see SSHSubsystemFwup.subsystem_spec/1
) or by setting the application
environment.
Here's an example of what the code looks like when setting options via a subsystem spec:
:ssh.daemon(@port, [
...
{:subsystems, [SSHSubsystemFwup.subsystem_spec(task: "my_upgrade_task")]}
])
If another library starts the SSH deamon for you, like
nerves_ssh
, it might be more convenient
to set options via the application environment. ssh_subsystem_fwup
uses its
defaults first, then those from the application environment and finally those in
the subsystem spec, so as long as the options you specify in the application
environment aren't overridden, you'll be fine. Here's an example:
config :ssh_subsystem_fwup, precheck_callback: {MyProject, :precheck, []}
The following options are available:
:devpath
- path for fwup to upgrade (Required):fwup_path
- path to the fwup firmware update utility:fwup_env
- a list of name,value tuples to be passed to the OS environment for fwup:fwup_extra_options
- additional options to pass to fwup like for setting public keys:precheck_callback
- an MFArgs to call when there's a connection. If specified, the callback will be passed the username and the current set of options. If allowed, it should return{:ok, new_options}
. Any other return value closes the connection.:success_callback
- an MFArgs to call when a firmware update completes successfully. Defaults to{Nerves.Runtime, :reboot, []}
.:task
- the task to run in the firmware update. Defaults to"upgrade"
License
Copyright (C) 2017-21 The Nerves Project Authors developers@nerves-project.org
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.