-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
functions.sh
225 lines (201 loc) · 7.49 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
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
# Turn on "strict mode." See http://redsymbol.net/articles/unofficial-bash-strict-mode/.
# -e: exit if any command unexpectedly fails.
# -u: exit if we have a variable typo.
# -o pipefail: don't ignore errors in the non-last command in a pipeline
set -euo pipefail
function hide_output {
# This function hides the output of a command unless the command fails
# and returns a non-zero exit code.
# Get a temporary file.
OUTPUT=$(tempfile)
# Execute command, redirecting stderr/stdout to the temporary file. Since we
# check the return code ourselves, disable 'set -e' temporarily.
set +e
$@ &> $OUTPUT
E=$?
set -e
# If the command failed, show the output that was captured in the temporary file.
if [ $E != 0 ]; then
# Something failed.
echo
echo FAILED: $@
echo -----------------------------------------
cat $OUTPUT
echo -----------------------------------------
exit $E
fi
# Remove temporary file.
rm -f $OUTPUT
}
function apt_get_quiet {
# Run apt-get in a totally non-interactive mode.
#
# Somehow all of these options are needed to get it to not ask the user
# questions about a) whether to proceed (-y), b) package options (noninteractive),
# and c) what to do about files changed locally (we don't cause that to happen but
# some VM providers muck with their images; -o).
#
# Although we could pass -qq to apt-get to make output quieter, many packages write to stdout
# and stderr things that aren't really important. Use our hide_output function to capture
# all of that and only show it if there is a problem (i.e. if apt_get returns a failure exit status).
DEBIAN_FRONTEND=noninteractive hide_output apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confnew" "$@"
}
function apt_install {
# Install a bunch of packages. We used to report which packages were already
# installed and which needed installing, before just running an 'apt-get
# install' for all of the packages. Calling `dpkg` on each package is slow,
# and doesn't affect what we actually do, except in the messages, so let's
# not do that anymore.
PACKAGES=$@
apt_get_quiet install $PACKAGES
}
function apt_add_repository_to_unattended_upgrades {
if [ -f /etc/apt/apt.conf.d/50unattended-upgrades ]; then
if ! grep -q "$1" /etc/apt/apt.conf.d/50unattended-upgrades; then
sed -i "/Allowed-Origins/a \
\"$1\";" /etc/apt/apt.conf.d/50unattended-upgrades
fi
fi
}
function get_default_hostname {
# Guess the machine's hostname. It should be a fully qualified
# domain name suitable for DNS. None of these calls may provide
# the right value, but it's the best guess we can make.
set -- $(hostname --fqdn 2>/dev/null ||
hostname --all-fqdns 2>/dev/null ||
hostname 2>/dev/null)
printf '%s\n' "$1" # return this value
}
function get_publicip_from_web_service {
# This seems to be the most reliable way to determine the
# machine's public IP address: asking a very nice web API
# for how they see us. Thanks go out to icanhazip.com.
# See: https://major.io/icanhazip-com-faq/
#
# Pass '4' or '6' as an argument to this function to specify
# what type of address to get (IPv4, IPv6).
curl -$1 --fail --silent --max-time 15 icanhazip.com 2>/dev/null || /bin/true
}
function get_default_privateip {
# Return the IP address of the network interface connected
# to the Internet.
#
# Pass '4' or '6' as an argument to this function to specify
# what type of address to get (IPv4, IPv6).
#
# We used to use `hostname -I` and then filter for either
# IPv4 or IPv6 addresses. However if there are multiple
# network interfaces on the machine, not all may be for
# reaching the Internet.
#
# Instead use `ip route get` which asks the kernel to use
# the system's routes to select which interface would be
# used to reach a public address. We'll use 8.8.8.8 as
# the destination. It happens to be Google Public DNS, but
# no connection is made. We're just seeing how the box
# would connect to it. There many be multiple IP addresses
# assigned to an interface. `ip route get` reports the
# preferred. That's good enough for us. See issue #121.
#
# With IPv6, the best route may be via an interface that
# only has a link-local address (fe80::*). These addresses
# are only unique to an interface and so need an explicit
# interface specification in order to use them with bind().
# In these cases, we append "%interface" to the address.
# See the Notes section in the man page for getaddrinfo and
# https://discourse.mailinabox.email/t/update-broke-mailinabox/34/9.
#
# Also see ae67409603c49b7fa73c227449264ddd10aae6a9 and
# issue #3 for why/how we originally added IPv6.
target=8.8.8.8
# For the IPv6 route, use the corresponding IPv6 address
# of Google Public DNS. Again, it doesn't matter so long
# as it's an address on the public Internet.
if [ "$1" == "6" ]; then target=2001:4860:4860::8888; fi
# Get the route information.
route=$(ip -$1 -o route get $target | grep -v unreachable)
# Parse the address out of the route information.
address=$(echo $route | sed "s/.* src \([^ ]*\).*/\1/")
if [[ "$1" == "6" && $address == fe80:* ]]; then
# For IPv6 link-local addresses, parse the interface out
# of the route information and append it with a '%'.
interface=$(echo $route | sed "s/.* dev \([^ ]*\).*/\1/")
address=$address%$interface
fi
echo $address
}
function ufw_allow {
if [ -z "${DISABLE_FIREWALL:-}" ]; then
# ufw has completely unhelpful output
ufw allow $1 > /dev/null;
fi
}
function restart_service {
hide_output service $1 restart
}
## Dialog Functions ##
function message_box {
dialog --title "$1" --msgbox "$2" 0 0
}
function input_box {
# input_box "title" "prompt" "defaultvalue" VARIABLE
# The user's input will be stored in the variable VARIABLE.
# The exit code from dialog will be stored in VARIABLE_EXITCODE.
# Temporarily turn off 'set -e' because we need the dialog return code.
declare -n result=$4
declare -n result_code=$4_EXITCODE
set +e
result=$(dialog --stdout --title "$1" --inputbox "$2" 0 0 "$3")
result_code=$?
set -e
}
function input_menu {
# input_menu "title" "prompt" "tag item tag item" VARIABLE
# The user's input will be stored in the variable VARIABLE.
# The exit code from dialog will be stored in VARIABLE_EXITCODE.
declare -n result=$4
declare -n result_code=$4_EXITCODE
local IFS=^$'\n'
set +e
result=$(dialog --stdout --title "$1" --menu "$2" 0 0 0 $3)
result_code=$?
set -e
}
function wget_verify {
# Downloads a file from the web and checks that it matches
# a provided hash. If the comparison fails, exit immediately.
URL=$1
HASH=$2
DEST=$3
CHECKSUM="$HASH $DEST"
rm -f $DEST
hide_output wget -O $DEST $URL
if ! echo "$CHECKSUM" | sha1sum --check --strict > /dev/null; then
echo "------------------------------------------------------------"
echo "Download of $URL did not match expected checksum."
echo "Found:"
sha1sum $DEST
echo
echo "Expected:"
echo "$CHECKSUM"
rm -f $DEST
exit 1
fi
}
function git_clone {
# Clones a git repository, checks out a particular commit or tag,
# and moves the repository (or a subdirectory in it) to some path.
# We use separate clone and checkout because -b only supports tags
# and branches, but we sometimes want to reference a commit hash
# directly when the repo doesn't provide a tag.
REPO=$1
TREEISH=$2
SUBDIR=$3
TARGETPATH=$4
TMPPATH=/tmp/git-clone-$$
rm -rf $TMPPATH $TARGETPATH
git clone -q $REPO $TMPPATH || exit 1
(cd $TMPPATH; git checkout -q $TREEISH;) || exit 1
mv $TMPPATH/$SUBDIR $TARGETPATH
rm -rf $TMPPATH
}