-
-
Notifications
You must be signed in to change notification settings - Fork 187
/
burn.ex
160 lines (123 loc) · 4.54 KB
/
burn.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
defmodule Mix.Tasks.Burn do
use Mix.Task
import Mix.Nerves.Utils
alias Mix.Nerves.Preflight
alias Nerves.Utils.WSL
@switches [device: :string, task: :string, firmware: :string]
@aliases [d: :device, t: :task, i: :firmware]
@shortdoc "Write a firmware image to an SDCard"
@moduledoc """
Writes the generated firmware image to an attached SDCard or file.
By default, this task detects attached SDCards and then invokes `fwup`
to overwrite the contents of the selected SDCard with the new image.
Data on the SDCard will be lost, so be careful.
## Command line options
* `--device <filename>` - skip SDCard detection and write the image to
the specified filename. SDCard paths depend on the operating system, but
have a form like `/dev/sdc` or `/dev/mmcblk0`. You may also specify a
filename to create an image that can be used with a bulk memory programmer
or copied to an SDCard manually with a utility like `dd`.
* `--task <name>` - apply the specified `fwup` task. See the `fwup.conf`
file that was used to create the firmware image for options. By
convention, the `complete` task writes everything to the SDCard including
bootloader and application data partitions. The `upgrade` task only
modifies the parts of the SDCard required to run the new software.
* `--firmware <name>` - (Optional) The path to the fw file to use.
Defaults to `<image_path>/<otp_app>.fw`
## Examples
```
# Upgrade the contents of the SDCard located at /dev/mmcblk0
mix burn --device /dev/mmcblk0 --task upgrade
```
"""
@impl true
def run(argv) do
Preflight.check!()
debug_info("Nerves Burn")
{opts, argv, _} = OptionParser.parse(argv, switches: @switches, aliases: @aliases)
firmware_config = Application.get_env(:nerves, :firmware)
target = mix_target()
_ = check_nerves_system_is_set!()
_ = check_nerves_toolchain_is_set!()
fw = firmware_file(opts)
unless File.exists?(fw) do
Mix.raise("Firmware for target #{target} not found at #{fw} run `mix firmware` to build")
end
{fw, firmware_location} =
WSL.make_file_accessible(fw, WSL.running_on_wsl?(), WSL.has_wslpath?())
dev =
case opts[:device] do
nil -> prompt_dev()
dev -> dev
end
set_provisioning(firmware_config[:provisioning])
burn(fw, dev, opts, argv)
# Remove the temporary .fw file
WSL.cleanup_file(fw, firmware_location)
end
defp burn(fw, dev, opts, argv) do
task = opts[:task] || "complete"
args = ["-a", "-i", fw, "-t", task, "-d", dev] ++ argv
{cmd, args} =
case :os.type() do
{_, :darwin} ->
{"fwup", args}
{_, :linux} ->
if WSL.running_on_wsl?() do
WSL.admin_powershell_command("fwup", Enum.join(args, " "))
else
fwup = System.find_executable("fwup")
case File.stat(dev) do
{:ok, %File.Stat{access: :read_write}} ->
{"fwup", args}
{:error, :enoent} ->
case File.touch(dev, System.os_time(:second)) do
:ok ->
{"fwup", args}
{:error, :eacces} ->
elevate_user()
{"sudo", provision_env() ++ [fwup] ++ args}
end
_ ->
elevate_user()
{"sudo", provision_env() ++ [fwup] ++ args}
end
end
{_, :nt} ->
{"fwup", args}
{_, type} ->
raise "Unable to burn firmware on your host #{inspect(type)}"
end
shell(cmd, args)
end
# Requests an elevation of user through askpass
@doc false
def elevate_user() do
ask_pass = System.get_env("SUDO_ASKPASS") || "/usr/bin/ssh-askpass"
System.put_env("SUDO_ASKPASS", ask_pass)
end
# This is a fix for linux when running through sudo.
# Sudo will strip the environment and therefore any variables
# that are set during device provisioning.
@doc false
def provision_env() do
System.get_env()
|> Enum.filter(fn {k, _} ->
String.starts_with?(k, "NERVES_") or String.equivalent?(k, "SERIAL_NUMBER")
end)
|> Enum.map(fn {k, v} -> k <> "=" <> v end)
end
def firmware_file(opts) do
with {:ok, fw} <- Keyword.fetch(opts, :firmware),
fw <- Path.expand(fw),
true <- File.exists?(fw) do
fw
else
false ->
fw = Keyword.get(opts, :firmware)
Mix.raise("The firmware file #{fw} does not exist")
_ ->
Nerves.Env.firmware_path()
end
end
end