turn on bash strict mode to better catch setup errors

fixes #893
This commit is contained in:
Joshua Tauberer 2018-11-30 10:24:19 -05:00
parent aa52f52d02
commit e5e0c64395
7 changed files with 47 additions and 34 deletions

View file

@ -6,7 +6,7 @@ if [ -z "`tools/mail.py user`" ]; then
# If we didn't ask for an email address at the start, do so now.
if [ -z "$EMAIL_ADDR" ]; then
# In an interactive shell, ask the user for an email address.
if [ -z "$NONINTERACTIVE" ]; then
if [ -z "${NONINTERACTIVE:-}" ]; then
input_box "Mail Account" \
"Let's create your first mail account.
\n\nWhat email address do you want?" \

View file

@ -1,3 +1,9 @@
# 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.
@ -5,11 +11,14 @@ function hide_output {
# Get a temporary file.
OUTPUT=$(tempfile)
# Execute command, redirecting stderr/stdout to the temporary file.
# 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.
E=$?
if [ $E != 0 ]; then
# Something failed.
echo
@ -75,7 +84,7 @@ function get_publicip_from_web_service {
#
# 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
curl -$1 --fail --silent --max-time 15 icanhazip.com 2>/dev/null || /bin/true
}
function get_default_privateip {
@ -131,11 +140,10 @@ function get_default_privateip {
fi
echo $address
}
function ufw_allow {
if [ -z "$DISABLE_FIREWALL" ]; then
if [ -z "${DISABLE_FIREWALL:-}" ]; then
# ufw has completely unhelpful output
ufw allow $1 > /dev/null;
fi
@ -154,10 +162,13 @@ 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 {
@ -167,8 +178,10 @@ function input_menu {
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 {

View file

@ -29,7 +29,7 @@ address 127.0.0.1
# send alerts to the following address
contacts admin
contact.admin.command mail -s "Munin notification ${var:host}" administrator@$PRIMARY_HOSTNAME
contact.admin.command mail -s "Munin notification \${var:host}" administrator@$PRIMARY_HOSTNAME
contact.admin.always_send warning critical
EOF

View file

@ -41,7 +41,7 @@ if [ $TOTAL_PHYSICAL_MEM -lt 750000 ]; then
fi
# Check that tempfs is mounted with exec
MOUNTED_TMP_AS_NO_EXEC=$(grep "/tmp.*noexec" /proc/mounts)
MOUNTED_TMP_AS_NO_EXEC=$(grep "/tmp.*noexec" /proc/mounts || /bin/true)
if [ -n "$MOUNTED_TMP_AS_NO_EXEC" ]; then
echo "Mail-in-a-Box has to have exec rights on /tmp, please mount /tmp with exec"
exit

View file

@ -1,4 +1,4 @@
if [ -z "$NONINTERACTIVE" ]; then
if [ -z "${NONINTERACTIVE:-}" ]; then
# Install 'dialog' so we can ask the user questions. The original motivation for
# this was being able to ask the user for input even if stdin has been redirected,
# e.g. if we piped a bootstrapping install script to bash to get started. In that
@ -25,8 +25,8 @@ if [ -z "$NONINTERACTIVE" ]; then
fi
# The box needs a name.
if [ -z "$PRIMARY_HOSTNAME" ]; then
if [ -z "$DEFAULT_PRIMARY_HOSTNAME" ]; then
if [ -z "${PRIMARY_HOSTNAME:-}" ]; then
if [ -z "${DEFAULT_PRIMARY_HOSTNAME:-}" ]; then
# We recommend to use box.example.com as this hosts name. The
# domain the user possibly wants to use is example.com then.
# We strip the string "box." from the hostname to get the mail
@ -86,7 +86,7 @@ fi
# If the machine is behind a NAT, inside a VM, etc., it may not know
# its IP address on the public network / the Internet. Ask the Internet
# and possibly confirm with user.
if [ -z "$PUBLIC_IP" ]; then
if [ -z "${PUBLIC_IP:-}" ]; then
# Ask the Internet.
GUESSED_IP=$(get_publicip_from_web_service 4)
@ -105,11 +105,11 @@ if [ -z "$PUBLIC_IP" ]; then
PUBLIC_IP=$GUESSED_IP
fi
if [ -z "$PUBLIC_IP" ]; then
if [ -z "${PUBLIC_IP:-}" ]; then
input_box "Public IP Address" \
"Enter the public IP address of this machine, as given to you by your ISP.
\n\nPublic IP address:" \
$DEFAULT_PUBLIC_IP \
${DEFAULT_PUBLIC_IP:-} \
PUBLIC_IP
if [ -z "$PUBLIC_IP" ]; then
@ -121,27 +121,27 @@ fi
# Same for IPv6. But it's optional. Also, if it looks like the system
# doesn't have an IPv6, don't ask for one.
if [ -z "$PUBLIC_IPV6" ]; then
if [ -z "${PUBLIC_IPV6:-}" ]; then
# Ask the Internet.
GUESSED_IP=$(get_publicip_from_web_service 6)
MATCHED=0
if [[ -z "$DEFAULT_PUBLIC_IPV6" && ! -z "$GUESSED_IP" ]]; then
if [[ -z "${DEFAULT_PUBLIC_IPV6:-}" && ! -z "$GUESSED_IP" ]]; then
PUBLIC_IPV6=$GUESSED_IP
elif [[ "$DEFAULT_PUBLIC_IPV6" == "$GUESSED_IP" ]]; then
elif [[ "${DEFAULT_PUBLIC_IPV6:-}" == "$GUESSED_IP" ]]; then
# No IPv6 entered and machine seems to have none, or what
# the user entered matches what the Internet tells us.
PUBLIC_IPV6=$GUESSED_IP
MATCHED=1
elif [[ -z "$DEFAULT_PUBLIC_IPV6" ]]; then
elif [[ -z "${DEFAULT_PUBLIC_IPV6:-}" ]]; then
DEFAULT_PUBLIC_IP=$(get_default_privateip 6)
fi
if [[ -z "$PUBLIC_IPV6" && $MATCHED == 0 ]]; then
if [[ -z "${PUBLIC_IPV6:-}" && $MATCHED == 0 ]]; then
input_box "IPv6 Address (Optional)" \
"Enter the public IPv6 address of this machine, as given to you by your ISP.
\n\nLeave blank if the machine does not have an IPv6 address.
\n\nPublic IPv6 address:" \
$DEFAULT_PUBLIC_IPV6 \
${DEFAULT_PUBLIC_IPV6:-} \
PUBLIC_IPV6
if [ ! $PUBLIC_IPV6_EXITCODE ]; then
@ -154,10 +154,10 @@ fi
# Get the IP addresses of the local network interface(s) that are connected
# to the Internet. We need these when we want to have services bind only to
# the public network interfaces (not loopback, not tunnel interfaces).
if [ -z "$PRIVATE_IP" ]; then
if [ -z "${PRIVATE_IP:-}" ]; then
PRIVATE_IP=$(get_default_privateip 4)
fi
if [ -z "$PRIVATE_IPV6" ]; then
if [ -z "${PRIVATE_IPV6:-}" ]; then
PRIVATE_IPV6=$(get_default_privateip 6)
fi
if [[ -z "$PRIVATE_IP" && -z "$PRIVATE_IPV6" ]]; then
@ -186,11 +186,11 @@ fi
# Set STORAGE_USER and STORAGE_ROOT to default values (user-data and /home/user-data), unless
# we've already got those values from a previous run.
if [ -z "$STORAGE_USER" ]; then
STORAGE_USER=$([[ -z "$DEFAULT_STORAGE_USER" ]] && echo "user-data" || echo "$DEFAULT_STORAGE_USER")
if [ -z "${STORAGE_USER:-}" ]; then
STORAGE_USER=$([[ -z "${DEFAULT_STORAGE_USER:-}" ]] && echo "user-data" || echo "$DEFAULT_STORAGE_USER")
fi
if [ -z "$STORAGE_ROOT" ]; then
STORAGE_ROOT=$([[ -z "$DEFAULT_STORAGE_ROOT" ]] && echo "/home/$STORAGE_USER" || echo "$DEFAULT_STORAGE_ROOT")
if [ -z "${STORAGE_ROOT:-}" ]; then
STORAGE_ROOT=$([[ -z "${DEFAULT_STORAGE_ROOT:-}" ]] && echo "/home/$STORAGE_USER" || echo "$DEFAULT_STORAGE_ROOT")
fi
# Show the configuration, since the user may have not entered it manually.

View file

@ -60,8 +60,8 @@ source setup/questions.sh
# Run some network checks to make sure setup on this machine makes sense.
# Skip on existing installs since we don't want this to block the ability to
# upgrade, and these checks are also in the control panel status checks.
if [ -z "$DEFAULT_PRIMARY_HOSTNAME" ]; then
if [ -z "$SKIP_NETWORK_CHECKS" ]; then
if [ -z "${DEFAULT_PRIMARY_HOSTNAME:-}" ]; then
if [ -z "${SKIP_NETWORK_CHECKS:-}" ]; then
source setup/network-checks.sh
fi
fi

View file

@ -37,9 +37,9 @@ hostname $PRIMARY_HOSTNAME
# for reference
SWAP_MOUNTED=$(cat /proc/swaps | tail -n+2)
SWAP_IN_FSTAB=$(grep "swap" /etc/fstab)
ROOT_IS_BTRFS=$(grep "\/ .*btrfs" /proc/mounts)
TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}')
SWAP_IN_FSTAB=$(grep "swap" /etc/fstab || /bin/true)
ROOT_IS_BTRFS=$(grep "\/ .*btrfs" /proc/mounts || /bin/true)
TOTAL_PHYSICAL_MEM=$(head -n 1 /proc/meminfo | awk '{print $2}' || /bin/true)
AVAILABLE_DISK_SPACE=$(df / --output=avail | tail -n 1)
if
[ -z "$SWAP_MOUNTED" ] &&
@ -143,8 +143,8 @@ fi
# section) and syslog (see #328). There might be other issues, and it's
# not likely the user will want to change this, so we only ask on first
# setup.
if [ -z "$NONINTERACTIVE" ]; then
if [ ! -f /etc/timezone ] || [ ! -z $FIRST_TIME_SETUP ]; then
if [ -z "${NONINTERACTIVE:-}" ]; then
if [ ! -f /etc/timezone ] || [ ! -z ${FIRST_TIME_SETUP:-} ]; then
# If the file is missing or this is the user's first time running
# Mail-in-a-Box setup, run the interactive timezone configuration
# tool.
@ -239,7 +239,7 @@ EOF
# Various virtualized environments like Docker and some VPSs don't provide #NODOC
# a kernel that supports iptables. To avoid error-like output in these cases, #NODOC
# we skip this if the user sets DISABLE_FIREWALL=1. #NODOC
if [ -z "$DISABLE_FIREWALL" ]; then
if [ -z "${DISABLE_FIREWALL:-}" ]; then
# Install `ufw` which provides a simple firewall configuration.
apt_install ufw