-
Notifications
You must be signed in to change notification settings - Fork 0
/
upload.sh
executable file
·139 lines (124 loc) · 5.2 KB
/
upload.sh
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
#!/bin/sh
#
# Upload new firmware to a target running nerves_firmware_ssh
#
# Usage:
# upload.sh [destination IP] [Path to .fw file]
#
# If unspecifed, the destination is nerves.local and the .fw file is naively
# guessed
#
# You may want to add the following to your `~/.ssh/config` to avoid recording
# the IP addresses of the target:
#
# Host nerves.local
# UserKnownHostsFile /dev/null
# StrictHostKeyChecking no
#
# The firmware update protocol is:
#
# 1. Connect to the nerves_firmware_ssh service running on port 8989
# 2. Send "fwup:$FILESIZE,reboot\n" where `$FILESIZE` is the size of the file
# being uploaded
# 3. Send the firmware file
# 4. The response from the device is a progress bar from fwup that can either
# be ignored or shown to the user.
# 5. The ssh connection is closed with an exit code to indicate success or
# failure
#
# Feel free to copy this script whereever is convenient. The template is at
# https://github.com/nerves-project/nerves_firmware_ssh/blob/master/priv/templates/script.upload.eex
#
set -e
DESTINATION=$1
FILENAME="$2"
help() {
echo
echo "upload.sh [destination IP] [Path to .fw file]"
echo
echo "Default destination IP is 'nerves.local'"
echo "Default firmware bundle is the first .fw file in '_build/\${MIX_TARGET}_\${MIX_ENV}/nerves/images'"
echo
echo "MIX_TARGET=$MIX_TARGET"
echo "MIX_ENV=$MIX_ENV"
exit 1
}
[ -n "$DESTINATION" ] || DESTINATION=nerves.local
[ -n "$MIX_TARGET" ] || MIX_TARGET=rpi0
[ -n "$MIX_ENV" ] || MIX_ENV=dev
if [ -z "$FILENAME" ]; then
FIRMWARE_PATH="./_build/${MIX_TARGET}_${MIX_ENV}/nerves/images"
if [ ! -d "$FIRMWARE_PATH" ]; then
# Try the Nerves 1.4 path if the user hasn't upgraded their mix.exs
FIRMWARE_PATH="./_build/${MIX_TARGET}/${MIX_TARGET}_${MIX_ENV}/nerves/images"
if [ ! -d "$FIRMWARE_PATH" ]; then
# Try the pre-Nerves 1.4 path
FIRMWARE_PATH="./_build/${MIX_TARGET}/${MIX_ENV}/nerves/images"
if [ ! -d "$FIRMWARE_PATH" ]; then
echo "Can't find the build products. Specify path to .fw file or try running 'mix firmware'"
exit 1
fi
fi
fi
FILENAME=$(ls "$FIRMWARE_PATH/"*.fw 2> /dev/null | head -n 1)
fi
[ -n "$FILENAME" ] || (echo "Error: error determining firmware bundle."; help)
[ -f "$FILENAME" ] || (echo "Error: can't find '$FILENAME'"; help)
# Check the flavor of stat for sending the filesize
if stat --version 2>/dev/null | grep GNU >/dev/null; then
# The QNU way
FILESIZE=$(stat -c%s "$FILENAME")
else
# Else default to the BSD way
FILESIZE=$(stat -f %z "$FILENAME")
fi
FIRMWARE_METADATA=$(fwup -m -i "$FILENAME" || echo "meta-product=Error reading metadata!")
FIRMWARE_PRODUCT=$(echo "$FIRMWARE_METADATA" | grep -E "^meta-product=" -m 1 2>/dev/null | cut -d '=' -f 2- | tr -d '"')
FIRMWARE_VERSION=$(echo "$FIRMWARE_METADATA" | grep -E "^meta-version=" -m 1 2>/dev/null | cut -d '=' -f 2- | tr -d '"')
FIRMWARE_PLATFORM=$(echo "$FIRMWARE_METADATA" | grep -E "^meta-platform=" -m 1 2>/dev/null | cut -d '=' -f 2- | tr -d '"')
FIRMWARE_UUID=$(echo "$FIRMWARE_METADATA" | grep -E "^meta-uuid=" -m 1 2>/dev/null | cut -d '=' -f 2- | tr -d '"')
echo "Path: $FILENAME"
echo "Product: $FIRMWARE_PRODUCT $FIRMWARE_VERSION"
echo "UUID: $FIRMWARE_UUID"
echo "Platform: $FIRMWARE_PLATFORM"
echo
echo "Uploading to $DESTINATION..."
# Don't fall back to asking for passwords, since that won't work
# and it's easy to misread the message thinking that it's asking
# for the private key password
SSH_OPTIONS="-o PreferredAuthentications=publickey"
if [ "$(uname -s)" = "Darwin" ]; then
DESTINATION_IP=$(arp -n $DESTINATION | sed 's/.* (\([0-9.]*\).*/\1/' || exit 0)
if [ -z "$DESTINATION_IP" ]; then
echo "Can't resolve $DESTINATION"
exit 1
fi
TEST_DESTINATION_IP=$(printf "$DESTINATION_IP" | head -n 1)
if [ "$DESTINATION_IP" != "$TEST_DESTINATION_IP" ]; then
echo "Multiple destination IP addresses for $DESTINATION found:"
echo "$DESTINATION_IP"
echo "Guessing the first one..."
DESTINATION_IP=$TEST_DESTINATION_IP
fi
IS_DEST_LL=$(echo $DESTINATION_IP | grep '^169\.254\.' || exit 0)
if [ -n "$IS_DEST_LL" ]; then
LINK_LOCAL_IP=$(ifconfig | grep 169.254 | sed 's/.*inet \([0-9.]*\) .*/\1/')
if [ -z "$LINK_LOCAL_IP" ]; then
echo "Can't find an interface with a link local address?"
exit 1
fi
TEST_LINK_LOCAL_IP=$(printf "$LINK_LOCAL_IP" | tail -n 1)
if [ "$LINK_LOCAL_IP" != "$TEST_LINK_LOCAL_IP" ]; then
echo "Multiple interfaces with link local addresses:"
echo "$LINK_LOCAL_IP"
echo "Guessing the last one, but YMMV..."
LINK_LOCAL_IP=$TEST_LINK_LOCAL_IP
fi
# If a link local address, then force ssh to bind to the link local IP
# when connecting. This fixes an issue where the ssh connection is bound
# to another Ethernet interface. The TCP SYN packet that goes out has no
# chance of working when this happens.
SSH_OPTIONS="$SSH_OPTIONS -b $LINK_LOCAL_IP"
fi
fi
printf "fwup:$FILESIZE,reboot\n" | cat - $FILENAME | ssh -s -p 8989 $SSH_OPTIONS $DESTINATION nerves_firmware_ssh