#!/bin/bash -e shopt -s nullglob dry_run=0 debug_flag=0 haz_error=0 mode="" test_mode="" kernel_name="" # set by generate_mode_list mode_list=() # set by generate_mode_list test_commands_list=() # set by generate_test_commands_list update_funcs=() # set by generate_mode_list commands=() # setup signal traps trap "trap_quit" TERM HUP QUIT trap "trap_abort" INT trap "trap_usr1" USR1 trap "trap_exit" EXIT # check if messages are to be printed using color unset all_off bold black blue green red yellow white default cyan magenta all_off="\e[0m" bold="\e[1m" black="${bold}\e[30m" red="${bold}\e[31m" green="${bold}\e[32m" yellow="${bold}\e[33m" blue="${bold}\e[34m" magenta="${bold}\e[35m" cyan="${bold}\e[36m" white="${bold}\e[37m" default="${bold}\e[39m" readonly all_off bold black blue green red yellow white default cyan magenta plain() { local mesg=$1; shift printf "${all_off}%s${all_off}\n\n" "${mesg}" if [[ $# -gt 0 ]]; then printf '%s ' "${@}" printf '\n\n' fi } plain_one_line() { local mesg=$1; shift printf "${all_off}${all_off}%s %s\n\n" "${mesg}" "${@}" } msg() { local mesg=$1; shift printf "${green}====${all_off} ${bold}%s${all_off}\n\n" "$mesg" if [[ $# -gt 0 ]]; then printf '%s ' "${@}" printf '\n\n' fi } msg2() { local mesg=$1; shift printf "${blue}++++ ${all_off}${bold}${mesg}${all_off}\n\n" "$mesg" if [[ $# -gt 0 ]]; then printf '%s ' "${@}" printf '\n\n' fi } warning() { local mesg=$1; shift printf "${yellow}==== WARNING: ${all_off}${bold}${mesg}${all_off}\n\n" "$mesg" 1>&2 if [[ $# -gt 0 ]]; then printf '%s ' "${@}" 1>&2 printf '\n\n' fi } error() { local mesg=$1; shift printf "${red}==== ERROR: ${all_off}${red}${bold}${mesg}${all_off}\n\n" "$mesg" 1>&2 if [[ $# -gt 0 ]]; then printf '%s ' "${@}" 1>&2 printf '\n\n' fi } debug() { # $1: The message to print. if [[ ${debug_flag} -eq 1 ]]; then local mesg=$1; shift printf "${magenta}~~~~ DEBUG: ${all_off}${bold}${mesg}${all_off}\n\n" "$mesg" 1>&2 if [[ $# -gt 0 ]]; then printf '%s ' "${@}" 1>&2 printf '\n\n' fi fi } test_pass() { local mesg=$1; shift printf "${green}==== PASS: ${all_off}${bold}${mesg}${all_off}\n\n" "$mesg" 1>&2 if [[ $# -gt 0 ]]; then printf '%s ' "${@}" 1>&2 printf '\n\n' fi } test_fail() { local mesg=$1; shift printf "${red}==== FAILED: ${all_off}${red}${bold}${mesg}${all_off}\n\n" "$mesg" 1>&2 if [[ $# -gt 0 ]]; then printf '%s ' "${@}" 1>&2 printf '\n\n' fi } trap_exit() { local sig=$? debug "trap_exit: args \$?=$?" if [[ ${sig} -eq 0 ]]; then msg "$(date) :: Done" # 155 is set when command args are not met, we don't need to print anything in that case elif [[ ${sig} -eq 1 && ${sig} -ne 155 ]]; then msg "$(date) :: EXIT" fi } trap_abort() { debug "trap_abort: args \$?=$?" msg "$(date) :: EXIT (abort)" } trap_quit() { debug "trap_quit: args \$?=$?" msg "$(date) :: EXIT (TERM, HUP, QUIT)" } trap_usr1() { debug "trap_usr1: args \$?=$?" error "$(date) :: EXIT: An unkown error has occurred." } # Check a symlink, re-create it if the target is not correct function check_symlink() { # $1 = Symlink # $2 = Symlink target if [[ "$1" = "$(readlink $2)" ]]; then return elif [[ -L "$2" ]]; then rm "$2" fi ln -s "$1" "$2" } # Converts an absolute path into a relative path using python and prints on # stdout. function relativePath() { # $1: Path that should be converted to relative path # $2: The start path, usually $PWD python -c "import os.path; print(os.path.relpath('$1', '$2'))" } norun() { local mesg=$1; shift printf "${magenta}XXXX NORUN: ${all_off}${bold}${mesg}${all_off}\n\n" "$mesg" if [[ $# -gt 0 ]]; then printf '%s ' "$@" printf '\n\n' fi } # Runs a command. Ouput is not captured # To use this function, define the following in your calling script: # run_cmd_return="" run_cmd() { run_cmd_return=0 run_cmd_return=0 # $@: The command and args to run if [[ ${dry_run} -eq 1 ]]; then norun "CMD:" "$@" else plain "Running command:" "$@" plain_one_line "Output:" echo -e "$@" | source /dev/stdin run_cmd_return=$? echo plain_one_line "Command returned:" "${run_cmd_return}" fi } run_cmd_check() { # $1 Exit code # $2 Error string if defined with print an error message if [[ ${run_cmd_return} -eq 0 ]]; then return fi if [[ -n $2 ]]; then error "$2" fi exit $1 } # Runs a command. Ouput is not captured. dry_run=1 is ignored. # To use this function, define the following in your calling script: # run_cmd_return="" run_cmd_no_dry_run() { run_cmd_return=0 # $@: The command and args to run plain "Running command:" "$@" plain_one_line "Output:" echo -e "$@" | source /dev/stdin run_cmd_return=$? echo plain_one_line "Command returned:" "${run_cmd_return}" } # Runs a command, capture the output in run_cmd_output, but also show stdout. # To use this function, define the following in your calling script: # run_cmd_output="" # run_cmd_return="" run_cmd_show_and_capture_output() { run_cmd_output="" run_cmd_return=0 # $@: The command and args to run if [[ ${dry_run} -eq 1 ]]; then norun "CMD:" "$@" else plain "Running command:" "$@" plain_one_line "Output:" # The following allows command output to be displayed in jenkins and stored in the variable simultaneously # http://www.tldp.org/LDP/abs/html/x17974.html # WARNING: This function sometimes results in the following error: # lib.sh: line 145: /usr/bin/tee: Argument list too long # lib.sh: line 145: /bin/cat: Argument list too long exec 6>&1 # Link file descriptor 6 with stdout. run_cmd_output=$(echo -e "$@" | source /dev/stdin | tee >(cat - >&6); exit ${PIPESTATUS[1]}) exec 1>&6 6>&- # Restore stdout and close file descriptor #6. run_cmd_return=$? echo plain_one_line "Command returned:" "${run_cmd_return}" fi } # Runs a command, capture the output in run_cmd_output, but also show stdout. Ignores dry_run=1. # To use this function, define the following in your calling script: # run_cmd_output="" # run_cmd_return="" run_cmd_show_and_capture_output_no_dry_run() { run_cmd_output="" run_cmd_return=0 # $@: The command and args to run plain "Running command:" "$@" plain_one_line "Output:" # The following allows command output to be displayed in jenkins and stored in the variable simultaneously # http://www.tldp.org/LDP/abs/html/x17974.html # WARNING: This function sometimes results in the following error: # lib.sh: line 145: /usr/bin/tee: Argument list too long # lib.sh: line 145: /bin/cat: Argument list too long exec 6>&1 # Link file descriptor 6 with stdout. run_cmd_output=$(echo -e "$@" | source /dev/stdin | tee >(cat - >&6); exit ${PIPESTATUS[1]}) exec 1>&6 6>&- # Restore stdout and close file descriptor #6. run_cmd_return=$? echo plain_one_line "Command returned:" "${run_cmd_return}" } # Runs the command, does not show output to stdout # To use this function, define the following in your calling script: # run_cmd_output="" # run_cmd_return="" run_cmd_no_output() { run_cmd_output="" run_cmd_return=0 # $@: The command and args to run if [[ ${dry_run} -eq 1 ]]; then norun "CMD:" "$@" else plain "Running command:" "$@" run_cmd_output=$(echo -e "$@" | source /dev/stdin) run_cmd_return=$? plain_one_line "Command returned:" "${run_cmd_return}" fi } # Runs the command, does not show output to stdout, ignores dry_run=1 # To use this function, define the following in your calling script: # run_cmd_output="" # run_cmd_return="" run_cmd_no_output_no_dry_run() { run_cmd_output="" run_cmd_return=0 # $@: The command and args to run plain "Running command:" "${@}" run_cmd_output=$(echo -e "${@}" | source /dev/stdin ) run_cmd_return=$? plain_one_line "Command returned:" "${run_cmd_return}" } package_arch_from_path() { # $1: Package path pacman -Qip "$1" | grep "Architecture" | cut -d : -f 2 | tr -d ' ' return $? } package_name_from_path() { # $1: Package path pacman -Qip "$1" | grep "Name" | cut -d : -f 2 | tr -d ' ' return $? } package_version_from_path() { # $1: Package path pacman -Qip "$1" | grep "Version" | cut -d : -f 2 | tr -d ' ' return $? } package_version_from_syncdb() { # $1: Package name pacman -Si "$1" | grep "Version" | cut -d : -f 2 | tr -d ' ' return $? } kernel_version_has_minor_version() { # $1: the kernel version # returns: 0 if the version contains a minor version and 1 if it does not if [[ ${1} =~ ^[[:digit:]]+\.[[:digit:]]+\.([[:digit:]]+) ]]; then debug "kernel_version_has_minor_version: Have kernel with minor version!" return 0 fi debug "kernel_version_has_minor_version: Have kernel without minor version!" return 1 } # Returns the full kernel version. If $1 is "3.14-1" then kernel_version_full returns "3.14.0-1". kernel_version_full() { # $1: the kernel version local arg=$1 if ! kernel_version_has_minor_version $1; then debug "kernel_version_full: Have kernel without minor version!" # Kernel version has the format 3.14, so add a 0. local arg=$(echo ${arg} | cut -f1 -d-) local rev=$(echo ${1} | cut -f2 -d-) printf "${arg}.0-${rev}" return 0 fi printf ${arg} } # Returns the full kernel version. If $1 is "3.14-1" then kernel_version_full returns "3.14.0_1". kernel_version_full_no_hyphen() { # $1: The full kernel version # returns: output is printed to stdout echo $(kernel_version_full ${1} | sed s/-/_/g) } # from makepkg source_safe() { export script_dir mode kernel_name shopt -u extglob if ! source "$@"; then error "Failed to source $1" exit 1 fi shopt -s extglob } check_internet() { if [[ $(ping -w 1 -c 1 8.8.8.8 &> /dev/null; echo $?) != 0 ]]; then return 1 fi return 0 } check_webpage() { # $1: The url to scrape # $2: The Perl regex to match with # $3: Expect match debug "Checking webpage: $1" debug "Using regex: `printf "%q" "$2"`" debug "Expecting: $3" run_cmd_no_output "curl -sL ${1}" if [[ ${dry_run} -eq 1 ]]; then return 0 fi if [[ $(echo ${run_cmd_output} | \grep -q "504 Gateway Timeout"; echo $?) -eq 0 ]]; then return -1 elif [[ $(echo ${run_cmd_output} | \grep -q "503 Service Unavailable"; echo $?) -eq 0 ]]; then return -1 elif [[ ${run_cmd_output} == "RETVAL: 7" ]]; then return -1 fi local scraped_string=$(echo "${run_cmd_output}" | \grep -Po -m 1 "${2}") debug "Got \"${scraped_string}\" from webpage." if [[ ${scraped_string} != "$3" ]]; then error "Checking '$1' expected '$3' got '${scraped_string}'" debug "Returning 1 from check_webpage()" return 1 fi return 0 } check_result() { # $1 current line # $2 changed line # $3 the return code from check_webpage if [[ ${?} -eq 0 ]]; then msg2 "The $1 version is current." elif [[ ${?} -eq 1 ]]; then error "The $2 is out-of-date!" haz_error=1 elif [[ ${?} -eq -1 ]]; then warning "The $2 package page was unreachable!" else error "Check returned ${?}" haz_error=1 fi } check_archiso() { # # Check archiso kernel version (this will change when the archiso is updated) # msg "Checking archiso download page for linux kernel version changes..." check_webpage "https://www.archlinux.org/download/" "(?<=Included Kernel: )[\d\.]+" \ "${kernel_version_archiso}" check_result "archiso kernel version" "archiso" "$?" } check_linux_kernel() { # # Check x86_64 linux kernel version # msg "Checking the online package database for x86_64 linux kernel version changes..." check_webpage "https://www.archlinux.org/packages/core/x86_64/linux/" "(?<=