forked from dnschneid/crouton
-
Notifications
You must be signed in to change notification settings - Fork 0
/
functions
296 lines (277 loc) · 9.34 KB
/
functions
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 -e
# Copyright (c) 2016 The crouton Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#
# This file contains functions common to many of the scripts.
# The file is sourced when the scripts are run directly out of the repo, and
# directly inserted into the files when built into a bundle.
# Installs a script to a destination, statically inserting the below functions
# in this file if this file is sourced. If the file is a symlink, copies the
# symlink without modification.
# $1: the source script to process
# $2: the destination file or directory
# $3: optional additional commands to pass to sed (passed to -e)
installscript() {
local src="$1" dst="$2"
if [ -d "$dst" -o "${dst%/}" != "$dst" ]; then
mkdir -p "$dst"
dst="${dst%/}/${src##*/}"
fi
if [ -h "$src" ]; then
cp -fPT "$src" "$dst"
return
fi
thisfile="${INSTALLERDIR:-"`dirname "$0"`/../installer"}/functions"
if [ ! -f "$thisfile" ]; then
# This should never happen.
error 1 "Unable to find common functions file."
fi
{
# Find the line sourcing the functions file
line="$(grep -n '^\. .*/installer/functions' "$src" | cut -d : -f 1)"
if [ -n "$line" ]; then
head -n $((line - 1)) "$src"
awk 'ok; !ok && /^###/ {ok=1}' "$thisfile"
tail -n +$((line + 1)) "$src"
else
cat "$src"
fi
} | sed -e "${3:-;}" > "$dst"
}
### Everything after this line will be statically inserted into scripts.
# Exits the script with exit code $1, spitting out message $@ to stderr
error() {
local ecode="$1"
shift
if [ "$ecode" -eq 1 ]; then
echo_color "r" "$*" 1>&2
else
echo "$*" 1>&2
fi
exit "$ecode"
}
# Setup trap ($1) in case of interrupt or error.
# Traps explicitly do not exit on command error.
# Traps are first disabled to avoid executing clean-up commands twice.
# In the case of interrupts, exit is called to avoid the script continuing.
# $1 must either be empty or end in a semicolon.
settrap() {
trap "set +e; trap - INT HUP TERM 0; $1 exit 2" INT HUP TERM
trap "set +e; trap - INT HUP TERM 0; $1" 0
}
# Prepend a command to the existing $TRAP
addtrap() {
OLDTRAP="$TRAP"
TRAP="$1;$TRAP"
settrap "$TRAP"
}
# Revert the last trap change
undotrap() {
TRAP="$OLDTRAP"
settrap "$TRAP"
}
# Works mostly like built-in getopts but silently coalesces positional arguments.
# Does not take parameters. Set getopts_string prior to calling.
# Sets getopts_var and getopts_arg.
# $@ will be left with the positional arguments, so you should NOT shift at all.
# In bash, enables alias expansion, but that shouldn't impact anything.
shopt -q -s expand_aliases 2>/dev/null || true
# Running count of the number of positional arguments
# We're done processing if all of the remaining arguments are positional.
getopts_npos=0
getopts_dashdash=''
alias getopts_nextarg='getopts_ret=1
while [ "$#" -gt "$getopts_npos" ]; do
if [ -z "$getopts_dashdash" ] && getopts "$getopts_string" getopts_var; then
if [ "$(($#+1-OPTIND))" -lt "$getopts_npos" ]; then
# Bad parameter usage ate a positional argument.
# Generate the proper error message by abusing getopts.
set -- "-$getopts_var"
getopts "$getopts_var:" getopts_var
shift
fi
getopts_arg="$OPTARG"
getopts_ret=0
# Avoid -- confusion by shifting if OPTARG is set
if [ -n "$OPTARG" ]; then
shift "$((OPTIND-1))"
OPTIND=1
fi
break
fi
# Do not let getopts consume a --
if [ "$OPTIND" -gt 1 ]; then
shift "$((OPTIND-2))"
if [ "$1" != "--" ]; then
shift
fi
fi
OPTIND=1
if [ -z "$getopts_dashdash" -a "$1" = "--" ]; then
# Still need to loop through to fix the ordering
getopts_dashdash=y
else
set -- "$@" "$1"
getopts_npos="$((getopts_npos+1))"
fi
shift
done
[ "$getopts_ret" = 0 ]'
# Compares $RELEASE to the specified releases, assuming $DISTRODIR/releases is
# sorted oldest to newest. Every two parameters are considered criteria that are
# ORed together. The first parameter is the comparator, as provided to "test".
# The second parameter is the release to compare to. A comparison against a
# release from a different distro always fails. Since either $DISTRODIR/releases
# has to be readable or the release list has to be embedded, and RELEASE has to
# be set properly, this function should only be used in the context of targets.
# Returns non-zero if the release doesn't match
# Example: release -ge quantal -ge wheezy
release() {
if [ "$(($# % 2))" -ne 0 ]; then
error 3 "$(echo_color "y" "invalid parameters to release(): $*")"
fi
# Load up the list of releases; this will be replaced with a literal list
local releases="`cat "$DISTRODIR/releases" 2>/dev/null`"
if [ -z "$releases" ]; then
error 3 "$(echo_color "y" "list of releases for $DISTRO not found")"
fi
# End-of-word regex for awk
local eow='([^a-z]|$)'
local relnum="`echo "$releases" | awk "/^$RELEASE$eow/ {print NR; exit}"`"
if [ -z "$relnum" ]; then
error 3 "$(echo_color "y" "$RELEASE not found in $DISTRO")"
fi
while [ "$#" -ge 2 ]; do
local cmp="`echo "$releases" | awk "/^$2$eow/ {print NR; exit}"`"
if [ -n "$cmp" ] && test "$relnum" "$1" "$cmp"; then
return 0
fi
shift 2
done
return 1
}
# Large writes to slow devices (e.g. SD card or USB stick) can cause a task to
# be stuck for longer than 120 seconds, which triggers a kernel panic (and an
# immediate reboot). Instead of disabling the timeout altogether, we just make
# sure the kernel does not panic (this is the default configuration of a vanilla
# kernel). See crbug.com/260955 for details.
disablehungtask() {
echo 0 > /proc/sys/kernel/hung_task_panic
}
# Run an awk program, without buffering its output.
# unbuffered_awk 'program' [argument ...]
# In the awk code, all "print" calls must be replaced by "output"
#
# - Detects whether to run mawk or gawk (mawk is preferred as it is faster),
# - Prepends the definition of the "output" function to the awk program
# - Run the modified awk program.
# mawk needs '-W interactive' to flush the output properly (fflush does not
# work as documented, but apparently this behaviour is intentional, see
# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=593504).
# Furthermore, fflush is not POSIX so we cannot use any other awk flavor.
unbuffered_awk() {
local cmd
if hash mawk 2>/dev/null; then
cmd="mawk -W interactive"
elif hash gawk 2>/dev/null; then
cmd="gawk"
else
echo "Cannot find mawk or gawk." 1>&2
return 1
fi
local program="$1"
shift
$cmd '
function output(s) {
print s
fflush()
}
'"$program" "$@"
}
# Validate a chroot name: It cannot contain any /, and cannot be ".", ".." nor
# an empty string.
validate_name() {
if [ "${1%/*}" != "$1" -o "$1" = "." -o "$1" = ".." -o -z "$1" ]; then
return 1
fi
return 0
}
# Returns the mountpoint a path is on. The path doesn't need to exist.
# $1: the path to check
# outputs on stdout
getmountpoint() {
mp="`readlink -m -- "$1"`"
while ! stat -c '%m' "${mp:-/}" 2>/dev/null; do
mp="${mp%/*}"
done
}
# Echos to stderr, or /dev/tty if stdin is a tty but stderr is not
echo_tty() {
if [ -t 0 -a ! -t 2 ]; then
echo "$@" 1>/dev/tty
else
echo "$@" 1>&2
fi
}
# Outputs colored text to stdout
# usage: echo_color [l][color] [colored string] [uncolored string]
# Specifying "t" (thick) uses bold text.
# Color can be red, green, yellow, blue, magenta, cyan.
# Color can be specified with just the first letter.
# example: echo_color "tr" "bold red" "normal text"
echo_color() {
# If not outputting to a tty print no colors.
if [ ! -t 2 ]; then
shift
echo "$@"
return
fi
printf "\033["
local c="$1"
if [ "${c#t}" != "$c" ]; then
printf "1;"
c="${c#t}"
else
printf "0;"
fi
case "$c" in
r*) printf "31m";;
g*) printf "32m";;
y*) printf "33m";;
b*) printf "34m";;
m*) printf "35m";;
c*) printf "36m";;
*) printf "37m";;
esac
shift
local s='\n'
if [ "$1" = '-n' ]; then
s=''
shift
fi
echo '-n' "$1"
printf "\033[0m"
shift
echo '-n' "$*"
printf "$s"
}
# Websocket interface
PIPEDIR='/tmp/crouton-ext'
CRIATDISPLAY="$PIPEDIR/kiwi-display"
CROUTONLOCKDIR='/tmp/crouton-lock'
# Write a command to croutonwebsocket, and read back response
websocketcommand() {
# Check that $PIPEDIR and the FIFO pipes exist
if ! [ -d "$PIPEDIR" -a -p "$PIPEDIR/in" -a -p "$PIPEDIR/out" ]; then
echo "EError $PIPEDIR/in or $PIPEDIR/out are not pipes."
exit 0
fi
if ! timeout 3 \
sh -c "flock 5; cat > '$PIPEDIR/in';
cat '$PIPEDIR/out'" 5>"$PIPEDIR/lock"; then
echo "EError timeout"
fi
chmod -Rf g+rwX "$PIPEDIR" || true
chgrp -Rf crouton "$PIPEDIR" || true
}