/
rootfs-image
executable file
·258 lines (223 loc) · 8.5 KB
/
rootfs-image
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
#!/bin/sh
set -ue
STATE="$1"
FILES="$2"
if which jq >/dev/null 2>&1; then
JQ_AVAILABLE=1
else
JQ_AVAILABLE=0
fi
if which mender-flash >/dev/null 2>&1; then
MENDER_FLASH_AVAILABLE=1
else
MENDER_FLASH_AVAILABLE=0
fi
if command -v grub-mender-grubenv-print > /dev/null; then
PRINTENV=grub-mender-grubenv-print
SETENV=grub-mender-grubenv-set
else
PRINTENV=fw_printenv
SETENV=fw_setenv
fi
parse_conf_file() {
MENDER_ROOTFS_PART_A=""
MENDER_ROOTFS_PART_B=""
# Try first the fallback config file, which has least precedence
for CONF_FILE in \
${MENDER_DATASTORE_DIR:-/var/lib/mender}/mender.conf \
${MENDER_CONF_DIR:-/etc/mender}/mender.conf \
; do
if [ "$JQ_AVAILABLE" = 1 ]; then
# Use the alternative operator "//" to set tmp to "" instead of "null"
tmp="$(jq -r '.RootfsPartA // empty' < "$CONF_FILE" || true)"
MENDER_ROOTFS_PART_A="${tmp:-${MENDER_ROOTFS_PART_A}}"
tmp="$(jq -r '.RootfsPartB // empty' < "$CONF_FILE" || true)"
MENDER_ROOTFS_PART_B="${tmp:-${MENDER_ROOTFS_PART_B}}"
else
# Fall back to line based parsing. Vulnerable to weird JSON nesting, as well as unexpected
# newlines, although it is unlikely with a regular configuration file.
# Poor man's case insensitive match.
MATCH="[Rr][Oo][Oo][Tt][Ff][Ss][Pp][Aa][Rr][Tt][Aa]"
tmp="$(sed -ne '/"'"$MATCH"'" *: *"[^"]*"/ { s/.*"'"$MATCH"'" *: *"\([^"]*\)".*/\1/; p }' "$CONF_FILE" || true)"
MENDER_ROOTFS_PART_A="${tmp:-${MENDER_ROOTFS_PART_A}}"
MATCH="[Rr][Oo][Oo][Tt][Ff][Ss][Pp][Aa][Rr][Tt][Bb]"
tmp="$(sed -ne '/"'"$MATCH"'" *: *"[^"]*"/ { s/.*"'"$MATCH"'" *: *"\([^"]*\)".*/\1/; p }' "$CONF_FILE" || true)"
MENDER_ROOTFS_PART_B="${tmp:-${MENDER_ROOTFS_PART_B}}"
fi
done
if [ -z "$MENDER_ROOTFS_PART_A" -o -z "$MENDER_ROOTFS_PART_B" ]; then
echo "Cannot parse RootfsPartA/B in any configuration file!" 1>&2
return 1
fi
# For UBI, standardize on the `/dev/` variant. The kernel only accepts an argument without
# `/dev/`, but all userspace tools use the `/dev/` variant.
MENDER_ROOTFS_PART_A="$(echo $MENDER_ROOTFS_PART_A | sed -e 's,^ubi,/dev/ubi,')"
MENDER_ROOTFS_PART_B="$(echo $MENDER_ROOTFS_PART_B | sed -e 's,^ubi,/dev/ubi,')"
MENDER_ROOTFS_PART_A_NUMBER="$(echo "$MENDER_ROOTFS_PART_A" | grep -Eo '[0-9]+$' || true)"
MENDER_ROOTFS_PART_B_NUMBER="$(echo "$MENDER_ROOTFS_PART_B" | grep -Eo '[0-9]+$' || true)"
return 0
}
set_upgrade_vars() {
active_num="$(${PRINTENV} mender_boot_part)"
active_num="${active_num#mender_boot_part=}"
if test $active_num -eq $MENDER_ROOTFS_PART_A_NUMBER; then
active=$MENDER_ROOTFS_PART_A
passive=$MENDER_ROOTFS_PART_B
passive_num=$MENDER_ROOTFS_PART_B_NUMBER
else
active=$MENDER_ROOTFS_PART_B
passive=$MENDER_ROOTFS_PART_A
passive_num=$MENDER_ROOTFS_PART_A_NUMBER
fi
passive_num_hex=$(printf '%x' $passive_num)
upgrade_available="$(${PRINTENV} upgrade_available)"
upgrade_available="${upgrade_available#upgrade_available=}"
}
check_environment_canary() {
mender_check_saveenv_canary="$(${PRINTENV} mender_check_saveenv_canary)"
if [ "$mender_check_saveenv_canary" = "mender_check_saveenv_canary=1" ]; then
# If the check canary exists (added during build), we need to check the real canary to make
# sure that the boot loader was successful in adding it during boot.
mender_saveenv_canary="$(${PRINTENV} mender_saveenv_canary)"
if [ "$mender_saveenv_canary" != "mender_saveenv_canary=1" ]; then
cat 1>&2 <<'EOF'
`mender_check_saveenv_canary` was set in the boot environment, but
`mender_saveenv_canary` was not. This is an indication that the bootloader
integration is not working correctly, and the bootloader was not able to save
an environment which we can read from userspace. Please re-check your
bootloader integration, and refer to the section on Bootloader support in the
Mender documentation if you need more information.
EOF
return 1
fi
fi
return 0
}
check_device_matches_root() {
case "$1" in
/dev/ubi*)
# UBI is a bit peculiar. Trying to take the device number of the root device does not
# match the major/minor number of the UBI device file, even though that filesystem is
# mounted. So fall back on name comparison for UBI.
# Standardize on the `/dev/` variant. The kernel only accepts an argument without
# `/dev/`, but all userspace tools use the `/dev/` variant.
ROOT_DEVICE="$(mount | grep -F ' on / ' | sed -e 's/ .*//; s,^ubi,/dev/ubi,')"
if [ "$1" = "$ROOT_DEVICE" ]; then
return 0
else
echo "Mounted root ($ROOT_DEVICE) does not match boot loader environment ($1)!" 1>&2
return 1
fi
;;
*)
# Match major/minor device number against mounted root device.
if [ "$(stat -L -c %02t%02T "$1")" = "$(stat -L -c %04D /)" ]; then
return 0
else
echo "Mounted root does not match boot loader environment ($1)!" 1>&2
return 1
fi
;;
esac
}
check_requirements() {
parse_conf_file
check_environment_canary
set_upgrade_vars
}
case "$STATE" in
ProvidePayloadFileSizes)
echo "Yes"
;;
Download)
echo "This module supports DownloadWithFileSizes only" 1>&2
exit 1
;;
DownloadWithFileSizes)
check_requirements
if [ "$upgrade_available" != 0 ]; then
echo "Unexpected \`upgrade_available=$upgrade_available\` in $STATE." 1>&2
exit 1
fi
check_device_matches_root "$active"
line="$(cat stream-next)"
file="$(echo $line | cut -d' ' -f1)"
size="$(echo $line | cut -d' ' -f2)"
if [ -z "$file" -o -z "$size" ]; then
echo "Cannot parse line from stream-next, got: $line" 1>&2
exit 1
fi
if [ "$MENDER_FLASH_AVAILABLE" = 1 ]; then
mender-flash --input-size $size --input $file --output $passive
elif echo "$passive" | grep "^/dev/ubi" > /dev/null; then
ubiupdatevol $passive --size=$size $file
else
cat "$file" > $passive
fi
if [ "$(cat stream-next)" != "" ]; then
echo "More than one file in payload" 1>&2
exit 1
fi
;;
ArtifactInstall)
check_requirements
if [ "$upgrade_available" != 0 ]; then
echo "Unexpected \`upgrade_available=$upgrade_available\` in $STATE." 1>&2
exit 1
fi
check_device_matches_root "$active"
${SETENV} -s - <<EOF
mender_boot_part=$passive_num
mender_boot_part_hex=$passive_num_hex
upgrade_available=1
bootcount=0
EOF
;;
NeedsArtifactReboot)
echo "Automatic"
;;
SupportsRollback)
echo "Yes"
;;
ArtifactVerifyReboot)
check_requirements
if test "$upgrade_available" != 1; then
exit 1
fi
check_device_matches_root "$active"
;;
ArtifactVerifyRollbackReboot)
check_requirements
if test "$upgrade_available" = 1; then
exit 1
fi
check_device_matches_root "$active"
;;
ArtifactCommit)
check_requirements
if [ "$upgrade_available" != 1 ]; then
echo "Unexpected \`upgrade_available=$upgrade_available\` in $STATE." 1>&2
# If we get here, an upgrade in standalone mode failed to boot and the user is trying to commit from the old OS.
# This communicates to the user that the upgrade failed.
echo "Upgrade failed and was reverted: refusing to commit!" 1>&2
exit 1
fi
check_device_matches_root "$active"
${SETENV} upgrade_available 0
;;
ArtifactRollback)
# If we cannot parse the config file, exit anyway and let the bootloader handle the rollback
parse_conf_file || exit 0
check_requirements
# We do not use `check_device_matches_root` here, since we can be on either partition at
# this point.
if test "$upgrade_available" = 1; then
${SETENV} -s - <<EOF
mender_boot_part=$passive_num
mender_boot_part_hex=$passive_num_hex
upgrade_available=0
EOF
fi
;;
esac
exit 0