#!/usr/bin/env bash # A script to upgrade a RHEL derivative (Fedora, CentOS, ...). If you have ansible or puppet: # this is way more simple with an automation framework. This script works with the paradigm # that you only have a bash available and you want to see what happens without being murdered # by gazillions of output lines. # This script avoids combining options, e.g. dnf -C -q instead of dnf -Cq, since # dnf is perfectly capable of POSIX standards, but yum fails at combined parameters. # So we'll leave it this way in case of conversion of this script to yum or extension # to be capable of both. (tl;dr no combined options because yum sucks at POSIX standards) # LOGGING UNIT # We are not ignoring logging frameworks like many software out there. We'll be using logger(1) to # log what we are doing, and it's up to you to redirect it to a specific file if you need to. # Define the name of the logging unit/tag here: LOGUNIT="dnfu" # DETAILED LOG # To not spam the logging centre with all of our debug, we will create a temporary file nonetheless. # The script will log its destination in the end, so we can make use of mktemp(1) here. DLOG="$(mktemp -p /tmp dnfu.XXXXXX)"||exit 1 chmod a+r "$DLOG"||exit 1 LOGGER="$(which logger 2>/dev/null||/usr/bin/logger)" LCMD="$LOGGER -plocal0.info -it $LOGUNIT" LWCMD="$LOGGER -plocal0.warning -it $LOGUNIT" LECMD="$LOGGER -plocal0.err -it $LOGUNIT" ROK="\033[666D[ \033[32mOK\033[0m ]\033[u\033[K" RWRN="\033[666D[\033[33mWARN\033[0m]\033[u\033[K" RERR="\033[666D[\033[31mFAIL\033[0m]\033[u\033[K" RINF="\033[666D[\033[37mINFO\033[0m]\033[u\033[K" echo>"$DLOG" UPDAVAIL=1 SCMD="$(command -v snap 2>/dev/null)" UCMD="$(command -v dnf 2>/dev/null)" [ "$?" -ne 0 ]&&UCMD="$(command -v yum 2>/dev/null)" [ -z "$UCMD" ]&&printf "Neither yum nor dnf found!\n" >&2&&exit 1 TSCMD="$(command -v ts||echo '/usr/bin/ts')" [ ! -x "$TSCMD" ]&&printf "%b not executable, please install moreutils." "$TSCMD" >&2&&exit 1 TSPARMS=( '[%Y-%m-%d %H:%M:%S]' ) RETVAL=0 function radd { [ -z "$1" ]&&return 1 printf "\033[666D\033[u\033[K \033[37m(%b)\033[0m" "$1" } function rbeg { [ -z "$1" ]&&return 1 printf "[....] %b.\033[s.." "$1" } function rok { case "$1" in "") printf "%b\n" "$ROK";; *) printf "%b" "$ROK" radd "$1" printf "\n" ;; esac } function rwrn { case "$1" in "") printf "%b\n" "$RWRN";; *) printf "%b" "$RWRN" radd "$1" printf "\n" ;; esac } function rerr { case "$1" in "") printf "%b\n" "$RERR";; *) printf "%b" "$RERR" radd "$1" printf "\n" ;; esac } function rinf { case "$1" in "") printf "%b\n" "$RINF";; *) printf "%b" "$RINF" radd "$1" printf "\n" ;; esac } function supgrade { #printf "Listing updates...:\n" #sudo "${UCMD}" -C check-upgrade 2>&1|sed "s/^/$(date --rfc-3339=seconds) /">>"$DLOG" #case "${PIPESTATUS[0]}" in # 0) $LCMD "No updates found.";; # 100) $LCMD "Updates found.";; # *) $LECMD "check-upgrade went wrong!";RETVAL=1;return 1;; #esac USTART="$(date +%s)" rbeg "Upgrading system packages" sudo "${UCMD}" -y upgrade 2>&1|"${TSCMD}" "${TSPARMS[@]}">>"$DLOG" case "${PIPESTATUS[0]}" in 0) UFIN="$(date +%s)" $LCMD "Upgrade OK.";rok "Update took $((UFIN-USTART)) seconds." ;; 1) UFIN="$(date +%s)" $LWCMD "Error on upgrade, but handled by dnf." rwrn "error occurred but handled by dnf (duration: $((UFIN-USTART)) seconds)" case "RETVAL" in 0) RETVAL=255;; esac ;; 255) UFIN="$(date +%s)" $LWCMD "Upgrade finished, pending updates remaining." rwrn "Upgrade done but updates remaining (duration: $((UFIN-USTART)) seconds)" case "$RETVAL" in 0) RETVAL=255;; esac ;; *) $LECMD "Upgrade failed." rerr RETVAL=1;return 1 ;; esac unset UFIN USTART } function snrefresh { rbeg "Refreshing snapd snaps" sudo "${SCMD}" refresh 2>&1|"${TSCMD}" "${TSPARMS[@]}">>$DLOG case "${PIPESTATUS[0]}" in 0) $LCMD "Snaps refreshed.";rok;; *) $LECMD "Error on refreshing snaps.";rerr;RETVAL=1;return 1;; esac } CSTART="$(date +%s)" rbeg "Updating dnf cache" sudo "${UCMD}" makecache 2>&1|"${TSCMD}" "${TSPARMS[@]}">> "$DLOG" case "${PIPESTATUS[0]}" in 0) CFIN="$(date +%s)" $LCMD "DNF cache update OK.";rok "duration: $((CFIN-CSTART)) seconds" ;; 100) CFIN="$(date +%s)" $LWCMD "RC 100 on makecache.";rwrn "duration: $((CFIN-CSTART)) seconds" ;; *) $LECMD "DNF cache update failed!";rerr;exit 1;; esac unset CFIN CSTART rbeg "Checking for upgrades" sudo "${UCMD}" -C check-upgrade 2>&1|"${TSCMD}" "${TSPARMS[@]}">>"$DLOG" case "${PIPESTATUS[0]}" in 0) $LCMD "No updates found.";rinf "No updates" ;; 100) $LCMD "Updates found." rok "Updates found" UPDAVAIL=0 ;; *) $LECMD "check-upgrade went wrong!";rerr;exit 1;; esac if [ "$UPDAVAIL" -eq 0 ];then supgrade fi #printf "Checking for outdated running services or necessity to reboot...:\n" UPDNUM="$(sudo "${UCMD}" needs-restarting -C 2>/dev/null|grep -Pc '^[0-9]+[ ]+:')" if echo "$UPDNUM"|grep -P '^[0-9]+$' >/dev/null;then case "$UPDNUM" in 0) printf "No service requires reboot.\n";; 1) printf "1 service requires reboot.\n";sudo "${UCMD}" needs-restarting -C 2>/dev/null|tee -a "$DLOG";; *) printf "%b services require reboot.\n" "$UPDNUM";sudo "${UCMD}" needs-restarting -C 2>/dev/null|tee -a "$DLOG";; esac else printf "Error while fetching number of services requiring reboot.\n" >&2 fi sudo "${UCMD}" needs-restarting -C -r --color true 2>&1|tee -a "$DLOG" sudo "${UCMD}" needs-restarting -r >/dev/null 2>&1 ||$LWCMD "Reboot required."||: if [ -e "$SCMD" ];then snrefresh fi $LCMD "Detailed logfile is located at $DLOG."