-
Notifications
You must be signed in to change notification settings - Fork 196
/
rootfs-image
executable file
·296 lines (257 loc) · 9.77 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
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
#!/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
resolve_rootfs() {
case "$1" in
/dev/root|/dev/disk/by-partlabel/*|/dev/disk/by-partuuid/*)
# This is a symlink that points to the regular device
# (e.g. /dev/disk/by-partuuid/b3c4f349-1180-45e1-9a3d-0a6697f4960e --> /dev/sda2)
echo "$(readlink -f $1)"
;;
*)
# Keep any other path as-is
# (cf. https://github.com/mendersoftware/mender/pull/1613#discussion_r1584353642 for the reasoning)
echo "$1"
;;
esac
}
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,')"
# Resolve paths if required.
MENDER_ROOTFS_PART_A="$(resolve_rootfs $MENDER_ROOTFS_PART_A)"
MENDER_ROOTFS_PART_B="$(resolve_rootfs $MENDER_ROOTFS_PART_B)"
# Extract the partition number from the regular device path (e.g. /dev/sda2 --> 2).
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
active_num_hex=$(printf '%x' $active_num)
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
sync
fi
if [ "$(cat stream-next)" != "" ]; then
echo "More than one file in payload" 1>&2
exit 1
fi
;;
ArtifactInstall)
check_requirements
cat > "$FILES/tmp/orig-part.tmp" <<EOF
orig_part_num=$active_num
orig_part_num_hex=$active_num_hex
EOF
sync "$FILES/tmp/orig-part.tmp"
mv "$FILES/tmp/orig-part.tmp" "$FILES/tmp/orig-part"
sync "$FILES/tmp"
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
elif [ -f "$FILES/tmp/orig-part" ]; then
. "$FILES/tmp/orig-part"
${SETENV} -s - <<EOF
mender_boot_part=$orig_part_num
mender_boot_part_hex=$orig_part_num_hex
upgrade_available=0
EOF
fi
;;
esac
exit 0