From dab70709d90df1d83b920eccc6c0386b283d677e Mon Sep 17 00:00:00 2001 From: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com> Date: Mon, 6 Mar 2023 10:06:50 +0100 Subject: [PATCH] scripts: improve panic helpers (#3155) --- target/scripts/helpers/accounts.sh | 2 +- target/scripts/helpers/dns.sh | 2 +- target/scripts/helpers/error.sh | 40 ++++++++++++++------ target/scripts/helpers/ssl.sh | 18 ++++----- target/scripts/helpers/utils.sh | 6 +-- target/scripts/start-mailserver.sh | 4 +- target/scripts/startup/check-stack.sh | 2 +- target/scripts/startup/daemons-stack.sh | 2 +- target/scripts/startup/setup-stack.sh | 5 ++- target/scripts/startup/setup.d/dovecot.sh | 4 +- target/scripts/startup/setup.d/networking.sh | 2 +- 11 files changed, 53 insertions(+), 34 deletions(-) diff --git a/target/scripts/helpers/accounts.sh b/target/scripts/helpers/accounts.sh index 9d6c44e6..fcc6cf96 100644 --- a/target/scripts/helpers/accounts.sh +++ b/target/scripts/helpers/accounts.sh @@ -144,7 +144,7 @@ function _create_dovecot_alias_dummy_accounts if [[ -z ${REAL_ACC[1]} ]] then - dms_panic__misconfigured 'postfix-accounts.cf' 'alias configuration' + _dms_panic__misconfigured 'postfix-accounts.cf' 'alias configuration' 'immediate' fi # test if user has a defined quota diff --git a/target/scripts/helpers/dns.sh b/target/scripts/helpers/dns.sh index 1a471d16..4301fa7d 100644 --- a/target/scripts/helpers/dns.sh +++ b/target/scripts/helpers/dns.sh @@ -29,7 +29,7 @@ function _obtain_hostname_and_domainname # will result in an error that returns an empty value. This warrants a panic. if [[ -z ${HOSTNAME} ]] then - dms_panic__misconfigured 'obtain_hostname' '/etc/hosts' + _dms_panic__misconfigured 'obtain_hostname' '/etc/hosts' fi # If the `HOSTNAME` is more than 2 labels long (eg: mail.example.com), diff --git a/target/scripts/helpers/error.sh b/target/scripts/helpers/error.sh index 1dd81e55..98e75892 100644 --- a/target/scripts/helpers/error.sh +++ b/target/scripts/helpers/error.sh @@ -22,9 +22,10 @@ function _exit_with_error # PANIC_SCOPE => Optionally provide a string for debugging to better identify/locate the source of the panic. function dms_panic { - local PANIC_TYPE=${1} - local PANIC_INFO=${2} - local PANIC_SCOPE=${3} #optional + local PANIC_TYPE=${1:-} + local PANIC_INFO=${2:-} + local PANIC_SCOPE=${3-} # optional, must not be :- but just - + local PANIC_STRATEGY=${4:-} # optional local SHUTDOWN_MESSAGE @@ -49,6 +50,10 @@ function dms_panic SHUTDOWN_MESSAGE="Invalid value for ${PANIC_INFO}!" ;; + ( 'general' ) + SHUTDOWN_MESSAGE=${PANIC_INFO} + ;; + ( * ) # `dms_panic` was called directly without a valid PANIC_TYPE SHUTDOWN_MESSAGE='Something broke :(' ;; @@ -56,27 +61,38 @@ function dms_panic if [[ -n ${PANIC_SCOPE:-} ]] then - _shutdown "${PANIC_SCOPE} | ${SHUTDOWN_MESSAGE}" + _shutdown "${PANIC_SCOPE} | ${SHUTDOWN_MESSAGE}" "${PANIC_STRATEGY}" else - _shutdown "${SHUTDOWN_MESSAGE}" + _shutdown "${SHUTDOWN_MESSAGE}" "${PANIC_STRATEGY}" fi } # Convenience wrappers based on type: -function dms_panic__fail_init { dms_panic 'fail-init' "${1}" "${2}"; } -function dms_panic__no_env { dms_panic 'no-env' "${1}" "${2}"; } -function dms_panic__no_file { dms_panic 'no-file' "${1}" "${2}"; } -function dms_panic__misconfigured { dms_panic 'misconfigured' "${1}" "${2}"; } -function dms_panic__invalid_value { dms_panic 'invalid-value' "${1}" "${2}"; } +function _dms_panic__fail_init { dms_panic 'fail-init' "${1:-}" "${2:-}" "${3:-}" ; } +function _dms_panic__no_env { dms_panic 'no-env' "${1:-}" "${2:-}" "${3:-}" ; } +function _dms_panic__no_file { dms_panic 'no-file' "${1:-}" "${2:-}" "${3:-}" ; } +function _dms_panic__misconfigured { dms_panic 'misconfigured' "${1:-}" "${2:-}" "${3:-}" ; } +function _dms_panic__invalid_value { dms_panic 'invalid-value' "${1:-}" "${2:-}" "${3:-}" ; } +function _dms_panic__general { dms_panic 'general' "${1:-}" "${2:-}" "${3:-}" ; } # Call this method when you want to panic (i.e. emit an 'ERROR' log, and exit uncleanly). # `dms_panic` methods should be preferred if your failure type is supported. function _shutdown { - _log 'error' "${1}" + _log 'error' "${1:-_shutdown called without message}" _log 'error' 'Shutting down' sleep 1 kill 1 - exit 1 + + if [[ ${2:-wait} == 'immediate' ]] + then + # In case the user requested an immediate exit, he ensure he is not in a subshell + # call and exiting the whole script is safe. This way, we make the shutdown quicker. + exit 1 + else + # We can simply wait until Supervisord has terminated all processes; this way, + # we do not return from a subshell call and continue as if nothing happened. + sleep 1000 + fi } diff --git a/target/scripts/helpers/ssl.sh b/target/scripts/helpers/ssl.sh index 70683331..7bd7c0df 100644 --- a/target/scripts/helpers/ssl.sh +++ b/target/scripts/helpers/ssl.sh @@ -229,7 +229,7 @@ function _setup_ssl _log 'trace' "SSL configured with 'CA signed/custom' certificates" else - dms_panic__no_file "${TMP_KEY_WITH_FULLCHAIN}" "${SCOPE_SSL_TYPE}" + _dms_panic__no_file "${TMP_KEY_WITH_FULLCHAIN}" "${SCOPE_SSL_TYPE}" fi ;; @@ -243,7 +243,7 @@ function _setup_ssl # Fail early: if [[ -z ${SSL_KEY_PATH} ]] && [[ -z ${SSL_CERT_PATH} ]] then - dms_panic__no_env 'SSL_KEY_PATH or SSL_CERT_PATH' "${SCOPE_SSL_TYPE}" + _dms_panic__no_env 'SSL_KEY_PATH or SSL_CERT_PATH' "${SCOPE_SSL_TYPE}" fi if [[ -n ${SSL_ALT_KEY_PATH} ]] \ @@ -251,7 +251,7 @@ function _setup_ssl && [[ ! -f ${SSL_ALT_KEY_PATH} ]] \ && [[ ! -f ${SSL_ALT_CERT_PATH} ]] then - dms_panic__no_file "(ALT) ${SSL_ALT_KEY_PATH} or ${SSL_ALT_CERT_PATH}" "${SCOPE_SSL_TYPE}" + _dms_panic__no_file "(ALT) ${SSL_ALT_KEY_PATH} or ${SSL_ALT_CERT_PATH}" "${SCOPE_SSL_TYPE}" fi if [[ -f ${SSL_KEY_PATH} ]] && [[ -f ${SSL_CERT_PATH} ]] @@ -280,7 +280,7 @@ function _setup_ssl _log 'trace' "SSL configured with 'Manual' certificates" else - dms_panic__no_file "${SSL_KEY_PATH} or ${SSL_CERT_PATH}" "${SCOPE_SSL_TYPE}" + _dms_panic__no_file "${SSL_KEY_PATH} or ${SSL_CERT_PATH}" "${SCOPE_SSL_TYPE}" fi ;; @@ -325,7 +325,7 @@ function _setup_ssl _log 'trace' "SSL configured with 'self-signed' certificates" else - dms_panic__no_file "${SS_KEY} or ${SS_CERT}" "${SCOPE_SSL_TYPE}" + _dms_panic__no_file "${SS_KEY} or ${SS_CERT}" "${SCOPE_SSL_TYPE}" fi ;; @@ -381,7 +381,7 @@ function _setup_ssl ;; ( * ) # Unknown option, panic. - dms_panic__invalid_value 'SSL_TYPE' "${SCOPE_TLS_LEVEL}" + _dms_panic__invalid_value 'SSL_TYPE' "${SCOPE_TLS_LEVEL}" ;; esac @@ -404,7 +404,7 @@ function _find_letsencrypt_domain LETSENCRYPT_DOMAIN=${DOMAINNAME} else _log 'error' "Cannot find a valid DOMAIN for '/etc/letsencrypt/live//', tried: '${SSL_DOMAIN}', '${HOSTNAME}', '${DOMAINNAME}'" - dms_panic__misconfigured 'LETSENCRYPT_DOMAIN' '_find_letsencrypt_domain' + _dms_panic__misconfigured 'LETSENCRYPT_DOMAIN' '_find_letsencrypt_domain' fi echo "${LETSENCRYPT_DOMAIN}" @@ -418,7 +418,7 @@ function _find_letsencrypt_key local LETSENCRYPT_DOMAIN=${1} if [[ -z ${LETSENCRYPT_DOMAIN} ]] then - dms_panic__misconfigured 'LETSENCRYPT_DOMAIN' '_find_letsencrypt_key' + _dms_panic__misconfigured 'LETSENCRYPT_DOMAIN' '_find_letsencrypt_key' fi if [[ -e /etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/privkey.pem ]] @@ -429,7 +429,7 @@ function _find_letsencrypt_key LETSENCRYPT_KEY='key' else _log 'error' "Cannot find key file ('privkey.pem' or 'key.pem') in '/etc/letsencrypt/live/${LETSENCRYPT_DOMAIN}/'" - dms_panic__misconfigured 'LETSENCRYPT_KEY' '_find_letsencrypt_key' + _dms_panic__misconfigured 'LETSENCRYPT_KEY' '_find_letsencrypt_key' fi echo "${LETSENCRYPT_KEY}" diff --git a/target/scripts/helpers/utils.sh b/target/scripts/helpers/utils.sh index 2ef76def..bd4d8e3c 100644 --- a/target/scripts/helpers/utils.sh +++ b/target/scripts/helpers/utils.sh @@ -92,13 +92,13 @@ function _replace_by_env_in_file { if [[ -z ${1+set} ]] then - dms_panic__invalid_value 'first argument unset' 'utils.sh:_replace_by_env_in_file' + _dms_panic__invalid_value 'first argument unset' 'utils.sh:_replace_by_env_in_file' 'immediate' elif [[ -z ${2+set} ]] then - dms_panic__invalid_value 'second argument unset' 'utils.sh:_replace_by_env_in_file' + _dms_panic__invalid_value 'second argument unset' 'utils.sh:_replace_by_env_in_file' 'immediate' elif [[ ! -f ${2} ]] then - dms_panic__invalid_value "file '${2}' does not exist" 'utils.sh:_replace_by_env_in_file' + _dms_panic__invalid_value "file '${2}' does not exist" 'utils.sh:_replace_by_env_in_file' 'immediate' fi local ENV_PREFIX=${1} CONFIG_FILE=${2} diff --git a/target/scripts/start-mailserver.sh b/target/scripts/start-mailserver.sh index 82d48013..c98639b8 100755 --- a/target/scripts/start-mailserver.sh +++ b/target/scripts/start-mailserver.sh @@ -60,11 +60,11 @@ function _register_functions ;; ( 'OIDC' ) - _shutdown 'OIDC user account provisioning is not yet implemented' + _dms_panic__fail_init 'OIDC user account provisioning - it is not yet implemented' '' 'immediate' ;; ( * ) - _shutdown "'${ACCOUNT_PROVISIONER}' is not a valid value for ACCOUNT_PROVISIONER" + _dms_panic__invalid_value "'${ACCOUNT_PROVISIONER}' is not a valid value for ACCOUNT_PROVISIONER" '' 'immediate' ;; esac diff --git a/target/scripts/startup/check-stack.sh b/target/scripts/startup/check-stack.sh index 301e1079..8fb7073f 100644 --- a/target/scripts/startup/check-stack.sh +++ b/target/scripts/startup/check-stack.sh @@ -38,7 +38,7 @@ function _check_hostname # HOSTNAME should be an FQDN (eg: hostname.domain) if ! grep -q -E '^(\S+[.]\S+)$' <<< "${HOSTNAME}" then - _shutdown 'Setting hostname/domainname is required' + _dms_panic__general 'Setting hostname/domainname is required' '' 'immediate' fi } diff --git a/target/scripts/startup/daemons-stack.sh b/target/scripts/startup/daemons-stack.sh index 99c57df6..ac2a85c2 100644 --- a/target/scripts/startup/daemons-stack.sh +++ b/target/scripts/startup/daemons-stack.sh @@ -29,7 +29,7 @@ function _default_start_daemon if [[ ${?} -ne 0 ]] then _log 'error' "${RESULT}" - dms_panic__fail_init "${1}" + _dms_panic__fail_init "${1}" '' 'immediate' fi } diff --git a/target/scripts/startup/setup-stack.sh b/target/scripts/startup/setup-stack.sh index ca2cfc12..0cd35727 100644 --- a/target/scripts/startup/setup-stack.sh +++ b/target/scripts/startup/setup-stack.sh @@ -87,7 +87,10 @@ function _setup_apply_fixes_after_configuration touch /dev/shm/supervisor.sock _log 'debug' 'Checking /var/mail permissions' - _chown_var_mail_if_necessary || _shutdown 'Failed to fix /var/mail permissions' + if ! _chown_var_mail_if_necessary + then + _dms_panic__general 'Failed to fix /var/mail permissions' '' 'immediate' + fi _log 'debug' 'Removing files and directories from older versions' rm -rf /var/mail-state/spool-postfix/{dev,etc,lib,pid,usr,private/auth} diff --git a/target/scripts/startup/setup.d/dovecot.sh b/target/scripts/startup/setup.d/dovecot.sh index 478610c5..cf77c979 100644 --- a/target/scripts/startup/setup.d/dovecot.sh +++ b/target/scripts/startup/setup.d/dovecot.sh @@ -182,7 +182,7 @@ function _setup_dovecot_local_user fi done - _shutdown 'No accounts provided - Dovecot could not be started' + _dms_panic__fail_init 'accounts provisioning because no accounts were provided - Dovecot could not be started' '' 'immediate' } __wait_until_an_account_is_added_or_shutdown @@ -206,7 +206,7 @@ function _setup_dovecot_inet_protocols PROTOCOL='[::]' # IPv6 only else # Unknown value, panic. - dms_panic__invalid_value 'DOVECOT_INET_PROTOCOLS' "${DOVECOT_INET_PROTOCOLS}" + _dms_panic__invalid_value 'DOVECOT_INET_PROTOCOLS' "${DOVECOT_INET_PROTOCOLS}" 'immediate' fi sedfile -i "s|^#listen =.*|listen = ${PROTOCOL}|g" /etc/dovecot/dovecot.conf diff --git a/target/scripts/startup/setup.d/networking.sh b/target/scripts/startup/setup.d/networking.sh index febd703a..396db875 100644 --- a/target/scripts/startup/setup.d/networking.sh +++ b/target/scripts/startup/setup.d/networking.sh @@ -22,7 +22,7 @@ function _setup_docker_permit if [[ -z ${CONTAINER_IP} ]] then _log 'error' 'Detecting the container IP address failed' - dms_panic__misconfigured 'NETWORK_INTERFACE' 'Network Setup [docker_permit]' + _dms_panic__misconfigured 'NETWORK_INTERFACE' 'Network Setup [docker_permit]' 'immediate' fi while read -r IP