-
Notifications
You must be signed in to change notification settings - Fork 17
/
grub-mender-grubenv-print
executable file
·292 lines (252 loc) · 8.69 KB
/
grub-mender-grubenv-print
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
#!/bin/sh
# This script mimicks the behavior of fw_printenv/fw_setenv from U-Boot.
# Here is how the environment works. We've got two problems to overcome:
# 1. GRUB doesn't support checksumming the environment.
# 2. We want to store the environment on the boot partition, which is FAT, and
# can therefore be corrupted.
# For problem 1, we use an environment file for a "locking" variable, which can
# only take two values: 0 or 1. Since there are no other alternatives we don't
# need to checksum this environment file. In addition to the "locking"
# environment file, we have an environment file with the payload data in it. As
# long as "locking" is zero, we assume the real environment file is up to
# date. If it is non-zero, it is in an unknown state, and we need to restore it.
# Regarding problem 2, storing the environment on the data partition was
# considered, since it is typically ext4, but it seems wrong to store this very
# boot specific information here, especially if we consider things like being
# able to factory reset the data partition without losing which partition should
# boot. So what we do is to put two redundant environment files in two different
# directories on the boot partition. This should ensure that even if the Linux
# vfat driver updates the modification time of the file, and the system crashes
# in the middle of writing that sector, only that directory gets corrupted. The
# other one should be fine. Using the lock mechanics described in the previous
# paragraph, we can then use the healthy environment to restore the unhealthy
# one. It can't be guaranteed this will help against absolutely all forms of
# corruption, but it should cover the vast majority of cases, and one would have
# to be really unlucky to still get an unusable environment.
# In the past, we also used a sha256sum checksum file. The boot script doesn't
# use this anymore because the `hashsum` command is not included in all distro
# editions of GRUB. But because the sha256sum checksum also makes sure the
# format of the whole environment file is valid, not just the variable, we have
# kept using it in this command line utility for better detection of corruption.
if [ -r /etc/mender_grubenv.config ]; then
ENV_DIR="$(egrep '^ENV_DIR *= *' /etc/mender_grubenv.config | sed -e 's/^ENV_DIR *= *//' | tail -n 1)"
fi
if [ -z "$ENV_DIR" ]; then
if [ -d /boot/efi/grub-mender-grubenv ]; then
ENV_DIR=/boot/efi/grub-mender-grubenv
elif [ -d /boot/grub/grub-mender-grubenv ]; then
ENV_DIR=/boot/grub/grub-mender-grubenv
elif [ -d /boot/efi/EFI/BOOT/mender_grubenv1 ]; then
ENV_DIR=/boot/efi/EFI/BOOT
elif [ -d /boot/grub/mender_grubenv1 ]; then
ENV_DIR=/boot/grub
else
echo "Cannot determine environment directory." 1>&2
exit 2
fi
fi
ENV1="$ENV_DIR/mender_grubenv1/env"
LOCK1="$ENV_DIR/mender_grubenv1/lock"
LOCKSUM1="$ENV_DIR/mender_grubenv1/lock.sha256sum"
ENV2="$ENV_DIR/mender_grubenv2/env"
LOCK2="$ENV_DIR/mender_grubenv2/lock"
LOCKSUM2="$ENV_DIR/mender_grubenv2/lock.sha256sum"
LOCKFILE="/var/lock/grub-mender-grubenv-set.lock"
LOCKFILE_FD=9
GRUB_EDITENV=`which grub-editenv 2>/dev/null`
if [ -z "${GRUB_EDITENV}" ]; then
GRUB_EDITENV=`which grub2-editenv`
fi
# If empty, something is wrong and no point in proceeding
if [ -z "${GRUB_EDITENV}" ]; then
echo "Could not find a valid grub-editenv binary. Aborting..." 1>&2
exit 1
fi
if [ "`basename $0`" = "grub-mender-grubenv-set" ] || [ "`basename $0`" = "fw_setenv" ]; then
SETENV=1
else
SETENV=0
fi
usage() {
if [ $SETENV = 1 ]; then
cat 1>&2 <<EOF
`basename $0` [VARIABLES ...]
Sets variables in the mender-grubenv environment.
-h, --help Display help
-s, --script Batch mode to write multiple variables
Script Syntax:
key [space] value
lines starting with '#' are treated as comment
A variable without value will be deleted. Any number of spaces are
allowed between key and value. Space inside of the value is treated
as part of the value itself.
EOF
else
cat 1>&2 <<EOF
`basename $0` [VARIABLE ...]
Print variables from the mender-grubenv environment.
-h, --help Display help
EOF
fi
}
make_evalable() {
# Make string appropriate for eval'ing.
sed -e 's/'\''/'\'\\\\\'\''/g; s/\\\$/\\\$/g; s/=/='\''/; s/$/'\''/'
}
is_locksum_valid() {
local LOCKSUM="$1"
if ( cd "$(dirname $LOCKSUM)" && sha256sum -c "$LOCKSUM" > /dev/null ); then
echo "valid"
else
echo "invalid"
fi
}
check_and_restore_env() {
# Check that both environments are valid, and if not, restore one using the
# other.
state_of_env1=$(is_locksum_valid "$LOCKSUM1")
state_of_env2=$(is_locksum_valid "$LOCKSUM2")
# Check if env1 is good and env2 bad
if [ "$state_of_env1" = "valid" ] && [ "$state_of_env2" = "invalid" ] ; then
dd if=$ENV1 of=$ENV2 conv=notrunc > /dev/null 2>&1
sync $ENV2
${GRUB_EDITENV} $LOCK2 create
${GRUB_EDITENV} $LOCK2 set editing=0
sha256sum "$LOCK2" > "$LOCKSUM2"
sync "$LOCK2" "$LOCKSUM2"
if ! ( cd `dirname $LOCKSUM2` && sha256sum -c $LOCKSUM2 > /dev/null ); then
echo "Unable to restore environment 2." 1>&2
exit 2
fi
elif [ "$state_of_env1" = "invalid" ] && [ "$state_of_env2" = "valid" ] ; then
dd if=$ENV2 of=$ENV1 conv=notrunc > /dev/null 2>&1
sync $ENV1
${GRUB_EDITENV} $LOCK1 create
${GRUB_EDITENV} $LOCK1 set editing=0
sha256sum "$LOCK1" > "$LOCKSUM1"
sync "$LOCK1" "$LOCKSUM1"
if ! ( cd `dirname $LOCKSUM1` && sha256sum -c $LOCKSUM1 > /dev/null ); then
echo "Unable to restore environment 1." 1>&2
exit 2
fi
elif [ "$state_of_env1" = "invalid" ] && [ "$state_of_env2" = "invalid" ] ; then
echo "Unable to restore environment both are corrupt." 1>&2
exit 2
fi
}
lock_env() {
# Get a file descriptor of the lockfile
touch "$LOCKFILE"
eval "exec ${LOCKFILE_FD}< ${LOCKFILE}"
# Get the lock with a timeout of 1 second
flock -w 1 "$LOCKFILE_FD" || { echo "ERROR: can't acquire flock()." >&2; exit 1; }
}
unlock_env() {
flock -u "$LOCKFILE_FD"
}
setenv() {
while [ -n "$1" ]; do
if [ -n "$SCRIPT" ]; then
echo "--script used together with variables." 1>&2
exit 1
fi
case "$1" in
-s|--script)
shift
SCRIPT="$1"
if [ -z "$SCRIPT" ]; then
echo "No script given to --script option." 1>&2
exit 1
fi
;;
-h|--help)
usage
exit 1
;;
*)
break
;;
esac
shift
done
if [ -z "$SCRIPT" ]; then
VARS="$1"
shift
for arg in "$@"; do
VARS="$VARS $arg"
done
elif [ "$SCRIPT" = "-" ]; then
# Read from stdin.
VARS="`cat`"
else
# Read from a file.
VARS="`cat $SCRIPT`"
fi
if [ -z "$VARS" ]; then
echo "Must specify at least one variable assignment." 1>&2
exit 1
fi
# Split in newlines, not spaces.
IFS='
'
# Make string appropriate for eval'ing.
VARS="`echo "$VARS" | sed -ne '/^#/n; s/ */=/; /=/!s/$/=/; p' | make_evalable`"
set -e
lock_env
check_and_restore_env
${GRUB_EDITENV} $LOCK2 set editing=1
sync $LOCK2
eval ${GRUB_EDITENV} $ENV2 set $VARS
sync $ENV2
${GRUB_EDITENV} $LOCK2 set editing=0
sync $LOCK2
if ! ( cd `dirname $LOCKSUM2` && sha256sum -c $LOCKSUM2 > /dev/null ); then
echo "Environment 2 unexpectedly corrupt after update." 1>&2
unlock_env
exit 2
fi
${GRUB_EDITENV} $LOCK1 set editing=1
sync $LOCK1
eval ${GRUB_EDITENV} $ENV1 set $VARS
sync $ENV1
${GRUB_EDITENV} $LOCK1 set editing=0
sync $LOCK1
if ! ( cd `dirname $LOCKSUM1` && sha256sum -c $LOCKSUM1 > /dev/null ); then
echo "Environment 1 unexpectedly corrupt after update." 1>&2
unlock_env
exit 2
fi
unlock_env
}
printenv() {
NO_ARGS=1
while [ -n "$1" ]; do
case "$1" in
-h|--help)
usage
exit 1
;;
*)
NO_ARGS=0
break
;;
esac
done
lock_env
check_and_restore_env
VARS="`${GRUB_EDITENV} $ENV1 list`"
unlock_env
eval "`echo "$VARS" | make_evalable`"
if [ $NO_ARGS = 1 ]; then
echo "$VARS"
else
for var in "$@"; do
eval echo \"$var=\${$var}\"
done
fi
}
if [ $SETENV = 1 ]; then
setenv "$@"
else
printenv "$@"
fi