diff --git a/contrib/devtools/bitcoind-exit-on-log.sh b/contrib/devtools/bitcoind-exit-on-log.sh new file mode 100755 index 000000000..77f7c12de --- /dev/null +++ b/contrib/devtools/bitcoind-exit-on-log.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash + +export LC_ALL=C + +set -euxo pipefail + +TOPLEVEL=$(git rev-parse --show-toplevel) +DEFAULT_BITCOIND="${TOPLEVEL}/build/src/bitcoind" +DEFAULT_LOG_FILE=~/".bitcoin/debug.log" + +help_message() { + set +x + echo "Run bitcoind until a given log message is encountered, then kill bitcoind." + echo "" + echo "Example usages:" + echo "$0 --grep 'progress=1.000000' --params \"-datadir=~/.bitcoin\" --callback mycallback" + echo "" + echo "Options:" + echo "-h, --help Display this help message." + echo "" + echo "-g, --grep (required) The grep pattern to look for." + echo "" + echo "-c, --callback (optional) Bash command to execute as a callback. This is useful for interacting with bitcoind before it is killed (to run tests, for example)." + echo "-p, --params (optional) Parameters to provide to bitcoind." + echo "" + echo "Environment Variables:" + echo "BITCOIND Default: ${DEFAULT_BITCOIND}" + echo "LOG_FILE Default: ${DEFAULT_LOG_FILE}" + set -x +} + +: "${BITCOIND:=${DEFAULT_BITCOIND}}" +: "${LOG_FILE:=${DEFAULT_LOG_FILE}}" +GREP_PATTERN="" +CALLBACK="" +BITCOIND_PARAMS="" + +# Parse command line arguments +while [[ $# -gt 0 ]]; do +case $1 in + -h|--help) + help_message + exit 0 + ;; + + # Required params + -g|--grep) + GREP_PATTERN="$2" + shift # shift past argument + shift # shift past value + ;; + + # Optional params + -c|--callback) + CALLBACK="$2" + shift # shift past argument + shift # shift past value + ;; + -p|--params) + BITCOIND_PARAMS="$2" + shift # shift past argument + shift # shift past value + ;; + + *) + echo "Unknown argument: $1" + help_message + exit 1 + ;; +esac +done + +if [ -z "${GREP_PATTERN}" ]; then + echo "Error: A grep pattern was not specified." + echo "" + help_message + exit 1 +fi + +# Make sure the debug log exists so that tail does not fail +touch "${LOG_FILE}" + +# Launch bitcoind using custom parameters +read -a BITCOIND_PARAMS <<< "${BITCOIND_PARAMS}" +START_TIME=$(date +%s) +if [ "${#BITCOIND_PARAMS[@]}" -gt 0 ]; then + "${BITCOIND}" "${BITCOIND_PARAMS[@]}" & +else + "${BITCOIND}" & +fi +BITCOIND_PID=$! + +# Wait for log checking to finish and kill the daemon +( + ( + # Ignore the broken pipe when tail tries to write pipe closed by grep + set +o pipefail + tail -f "${LOG_FILE}" | grep -m 1 "${GREP_PATTERN}" + ) + + END_TIME=$(date +%s) + RUNTIME=$((END_TIME - START_TIME)) + HUMAN_RUNTIME=$(eval "echo $(date -ud "@${RUNTIME}" "+\$((%s/3600)) hours, %M minutes, %S seconds")") + + echo "Grep pattern '${GREP_PATTERN}' found after ${HUMAN_RUNTIME}." + + # Optional callback for interacting with bitcoind before it's killed + if [ -n "${CALLBACK}" ]; then + "${CALLBACK}" + fi +) & +LOG_PID=$! + +# When the log subshell finishes, kill bitcoind +( + # Disable verbosity to avoid bloating the output with sleep prints + set +x + while [ -e /proc/${LOG_PID} ]; do sleep 0.1; done + set -x + + echo "Cleaning up bitcoin daemon (PID: ${BITCOIND_PID})." + kill ${BITCOIND_PID} +) & + +# Wait for bitcoind to exit, whether it exited on its own or the log subshell finished +wait ${BITCOIND_PID} +BITCOIND_EXIT_CODE=$? + +if [ "${BITCOIND_EXIT_CODE}" -ne "0" ]; then + echo "bitcoind exited unexpectedly with code: ${BITCOIND_EXIT_CODE}" + exit ${BITCOIND_EXIT_CODE} +fi + +# Get the exit code for the log subshell, which should have exited already +wait ${LOG_PID} +LOG_EXIT_CODE=$? + +# The log subshell should only exit with a non-zero code if the callback +# failed. +if [ "${LOG_EXIT_CODE}" -ne "0" ]; then + echo "Log subshell failed with code: ${LOG_EXIT_CODE}" + exit ${LOG_EXIT_CODE} +fi diff --git a/contrib/teamcity/ibd.sh b/contrib/teamcity/ibd.sh index 916a21f7a..b97fb25d1 100755 --- a/contrib/teamcity/ibd.sh +++ b/contrib/teamcity/ibd.sh @@ -1,79 +1,40 @@ #!/usr/bin/env bash export LC_ALL=C set -euxo pipefail ### # Initial Block Download script. # # Runs a bitcoind process until initial block download is complete. # Forwards the exit code from bitcoind onward. ### MYPID=$$ # Setup DATA_DIR="ibd" mkdir -p "${DATA_DIR}" DEBUG_LOG="${DATA_DIR}/debug.log" -touch "${DEBUG_LOG}" chmod +x bitcoind -# Launch bitcoind using this script's parameters -./bitcoind "-datadir=${DATA_DIR}" "$@" & -BITCOIND_PID=$! - cleanup() { # Cleanup background processes spawned by this script. pkill -P ${MYPID} tail || true } trap "cleanup" EXIT # Show some progress tail -f "${DEBUG_LOG}" | grep 'UpdateTip' | awk 'NR % 10000 == 0' & -# Wait for IBD to finish and kill the daemon -( - ( - # Ignore the broken pipe when tail tries to write pipe closed by grep - set +o pipefail - tail -f "${DEBUG_LOG}" | grep -m 1 'progress=1.000000' - ) +callback() { echo "Initial block download complete." # TODO Add more checks to see if IBD completed as expected, # These checks will exit the subshell with a non-zero exit code. -) & -IBD_PID=$! - -# When the IBD subshell finishes, kill bitcoind -( - # Disable verbosity to avoid bloating the output with sleep prints - set +x - while [ -e /proc/${IBD_PID} ]; do sleep 0.1; done - set -x - - echo "Cleaning up bitcoin daemon (PID: ${BITCOIND_PID})." - kill ${BITCOIND_PID} -) & - -# Wait for bitcoind to exit, whether it exited on its own or IBD finished -wait ${BITCOIND_PID} -BITCOIND_EXIT_CODE=$? - -if [ "${BITCOIND_EXIT_CODE}" -ne "0" ]; then - echo "bitcoind exited unexpectedly with code: ${BITCOIND_EXIT_CODE}" - exit ${BITCOIND_EXIT_CODE} -fi - -# Get the exit code for the IBD subshell, which should have exited already -wait ${IBD_PID} -IBD_EXIT_CODE=$? +} +export -f callback -# The IBD subshell should only exit with a non-zero code if one of the tests -# failed. -if [ "${IBD_EXIT_CODE}" -ne "0" ]; then - echo "IBD tests failed with code: ${IBD_EXIT_CODE}" - exit ${IBD_EXIT_CODE} -fi +TOPLEVEL=$(git rev-parse --show-toplevel) +LOG_FILE="${DEBUG_LOG}" "${TOPLEVEL}/contrib/devtools/bitcoind-exit-on-log.sh" --grep 'progress=1.000000' --params "-datadir=${DATA_DIR}" --callback callback