-
Notifications
You must be signed in to change notification settings - Fork 1
/
functions.sh
187 lines (172 loc) · 6.98 KB
/
functions.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
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
#!/bin/bash
# shellcheck disable=SC2034
readonly default_cache_dir="${XDG_CACHE_HOME:-${HOME}/.cache}/firecracker"
readonly LOG_ERROR=0
readonly LOG_WARN=1
readonly LOG_INFO=2
readonly LOG_DEBUG=3
declare -A quiet_at
# Default log level.
log_level="${LOG_INFO}"
# Default colour setting.
[[ -t 1 && -t 2 ]] && colour=true || colour=false
function error() {
# usage: error $message [...]
# Prints message to stderr if the global log level allows it.
if [[ "${log_level}" -ge "${LOG_ERROR}" ]]; then
printf "%s%s%s\n" "${C_ERROR}" "$*" "${C_RESET}" >&2
fi
}
function warn() {
# usage: warn $message [...]
# Prints message to stderr if the global log level allows it.
if [[ "${log_level}" -ge "${LOG_WARN}" ]]; then
printf "%s%s%s\n" "${C_WARN}" "$*" "${C_RESET}" >&2
fi
}
function info() {
# usage: info $message [...]
# Prints message to stderr if the global log level allows it.
if [[ "${log_level}" -ge "${LOG_INFO}" ]]; then
printf "%s%s%s\n" "${C_INFO}" "$*" "${C_RESET}" >&2
fi
}
function debug() {
# usage: debug $message [...]
# Prints message to stderr if the global log level allows it.
if [[ "${log_level}" -ge "${LOG_DEBUG}" ]]; then
printf "%s%s%s\n" "${C_DEBUG}" "$*" "${C_RESET}" >&2
fi
}
function die() {
# usage: die $code $message [...]
# Prints $message to stderr and exits with the given exit $code.
local code="$1"
shift
error "$@"
exit "${code}"
}
function log_level_setup() {
# usage: log_level_setup
# Sets up $quiet_at as an associative array with a key for each log level
# and values that are either 'true' (if messages of this log level should
# be suppressed) or empty (otherwise).
for level in DEBUG INFO WARN ERROR; do
local level_name="LOG_${level}"
quiet_at["${level}"]="$([[ "${log_level}" -le "${!level_name}" ]] && echo 'true')"
done
}
function colour_setup() {
# usage: colour_setup
# Sets up named colour variables, if coloured output is enabled and tput is
# available.
if "${colour}" && command -v tput >/dev/null; then
readonly C_RED="$(if (( $(tput colors) > 8 )); then tput setaf 9; else tput setaf 1; fi)"
readonly C_GREEN="$(tput setaf 2)"
readonly C_YELLOW="$(tput setaf 3)"
readonly C_BLUE="$(tput setaf 4)"
readonly C_ERROR="${C_RED}"
readonly C_WARN="${C_YELLOW}"
readonly C_INFO="${C_GREEN}"
readonly C_DEBUG="${C_BLUE}"
readonly C_RESET="$(tput sgr0)"
fi
}
function busybox_setup() {
# usage: busybox_setup $dir
# If BusyBox is installed, creates $dir, installs symlinks to all applets in
# $dir and appends $dir to $PATH.
# This means that regular system tools are preferred over BusyBox
# implementations, but BusyBox provides a fallback for missing tools.
local busybox_bindir="$1"
if ! command -v busybox >&/dev/null; then
# BusyBox not found.
return 1
fi
debug "Setting up busybox to provide fallback tool implementations."
mkdir -p -- "${busybox_bindir}"
busybox --install -s "${busybox_bindir}"
export PATH="${PATH}:${busybox_bindir}"
}
function download() {
# usage: download $url
# Downloads the given $url into $cache_dir and sets the global variable
# $downloaded_file to the full (absolute or relative) path to the file in
# $cache_dir.
# If the file is already present in $cache_dir, it is only downloaded if
# $url is more recent than the local file.
# If the download fails, returns 1 and sets $downloaded_file to the empty
# string.
local url="$1"
# shellcheck disable=SC2154
downloaded_file="${cache_dir}/${url##*/}"
# Download $url into $cache_dir.
if [[ -n "${quiet_at[WARN]}" ]]; then
# Only display wget's (non-verbose) output if there was an error.
# shellcheck disable=SC2015
(cd "${cache_dir}" && { wget_log="$(wget --no-verbose --timestamping "${url}" 2>&1)" || { r="$?"; error "${wget_log}"; exit "${r}"; }; })
r="$?"
else
# Just let wget print its (verbose) output to stderr.
(cd "${cache_dir}" && wget --timestamping "${url}")
r="$?"
fi
if [[ "${r}" -gt 0 ]]; then
# shellcheck disable=SC2034
downloaded_file=
error "Error: Could not download ${url}"
return 1
fi
}
function verify_signature() {
# usage verify_signature [--key $keyid|$email] $signed_file [$signature_file]
# Verifies the PGP signature on $signed_file. If $signature_file is given,
# it is interpreted as a detached signature. If it is omitted, $signed_file
# needs to contains it's signature.
# If --key is given, the key with the ID $keyid is obtained from a key
# server or the key for the identity $email is obtained via the wkd
# protocol. --key can be used multiple times to specify multiple keys.
# The signature is verified against the given keys, or, if none are given,
# against the default keyring of the user that happens to run this code.
signature_keys=()
while [[ "$1" == '--key' ]]; do
local signature_keys+=( "$2" )
shift
shift
done
local signed_file="$1"
local signature_file="$2"
if [[ -z "${signature_file}" ]]; then
debug "Checking PGP signature of ${signed_file}."
else
debug "Checking PGP signature of ${signed_file} using detached signature ${signature_file}."
fi
(
gpg=gpg
command -v gpg2 &>/dev/null && gpg=gpg2
if [[ "${#signature_keys[@]}" -gt 0 ]]; then
# Import the signature keys into a temporary keyring.
keyring="$(mktemp -d -p "${TMPDIR:-/tmp}" gpg.XXXXXX)"
trap 'rm -rf -- "${keyring}"' EXIT INT TERM QUIT
for key in "${signature_keys[@]}"; do
if [[ "${key}" = *@* ]]; then
# $key seems to be an email address, use wkd to get it.
([[ -n "${quiet_at[WARN]}" ]] && exec 1>/dev/null; "${gpg}" ${quiet_at[INFO]:+--quiet} --homedir="${keyring}" --auto-key-locate wkd --locate-keys "${key}") || die 1 "Error: Could not import public PGP key for ${key}"
else
# $key seems to be a key ID, use the Ubuntu key server to get it.
echo 'keyserver hkps://keyserver.ubuntu.com' > "${keyring}/dirmngr.conf"
"${gpg}" ${quiet_at[INFO]:+--quiet} --homedir="${keyring}" --recv-key "${key}" || die 1 "Error: Could not import public PGP key ${key}"
fi
done
fi
# Verify signature.
gpg_status="$([[ -n "${quiet_at[WARN]}" ]] && exec 2>/dev/null; exec 4>&1; "${gpg}" --quiet ${keyring:+--homedir="${keyring}"} ${keyring:+--trust-model=always} --status-fd=3 --verify ${signature_file:+"${signature_file}"} "${signed_file}" 3>&1 1>&4)" 4>&1
# Check that gpg found a valid signature that is not untrusted.
if grep --quiet -Fw VALIDSIG <<< "${gpg_status}" && \
! grep --quiet -Ew 'TRUST_(UNDEFINED|NEVER|MARGINAL)' <<< "${gpg_status}" ; then
exit 0
else
exit 1
fi
) || return 1
}