#!/bin/sh # Copyright (C) 2025 Michael Erdely All Rights Reserved. # SPDX-FileCopyrightText: 2024 Splunk, Inc. # SPDX-License-Identifier: Apache-2.0 # shellcheck disable=SC1091 . "$(dirname "$0")"/common.sh TMP_ERROR_FILTER_FILE=$(mktemp) # For filering out apt warning from stderr if [ "$KERNEL" = "Linux" ] ; then assertHaveCommand date OSName=$(cat /etc/*release | grep '\bNAME=' | cut -d '=' -f2 | tr ' ' '_' | cut -d\" -f2) OS_FILE=/etc/os-release # Ubuntu doesn't have yum installed by default hence apt is being used to get the list of upgradable packages if [ "$OSName" = "Ubuntu" ] || [ "$OSName" = "Debian_GNU/Linux" ]; then assertHaveCommand apt assertHaveCommand sed # For this to work properly, add a line to /etc/sudoers like this: # splunk ALL=(root) NOPASSWD: /usr/bin/apt update # Without the above line, 'apt list --upgradable' will not show updated packages unless the package databases were updated outside of this script # sed command here replaces '/, [, ]' with ' ' if [ $(id -u) != 0 ]; then CMD='eval date ; sudo -n /usr/bin/apt update > /dev/null 2>&1 ; eval apt list --upgradable | sed "s/\// /; s/\[/ /; s/\]/ /"' else CMD='eval date ; /usr/bin/apt update > /dev/null 2>&1 ; eval apt list --upgradable | sed "s/\// /; s/\[/ /; s/\]/ /"' fi # shellcheck disable=SC2016 PARSE_0='NR==1 {DATE=$0}' # shellcheck disable=SC2016 PARSE_1='NR>2 { printf "%s package=%s ubuntu_update_stream=%s latest_package_version=%s ubuntu_architecture=%s current_package_version=%s\n", DATE, $1, $2, $3, $4, $7}' MESSAGE="$PARSE_0 $PARSE_1" elif echo "$OS_ID" | grep -qi suse; then assertHaveCommand zypper # shellcheck disable=SC2016 CMD='eval date ; zypper list-updates' # shellcheck disable=SC2016 PARSE_0='NR==1 {DATE=$0}' # shellcheck disable=SC2016 PARSE_1='/^[\-+]+/ {header_found = 1; next}' # shellcheck disable=SC2016 PARSE_2='header_found { gsub(/[[:space:]]*\|[[:space:]]*/, "|"); split($0, arr, /\|/); printf "%s repository=%s package=%s current_package_version=%s latest_package_version=%s sles_architecture=%s\n", DATE, arr[2], arr[3], arr[4], arr[5], arr[6]}' MESSAGE="$PARSE_0 $PARSE_1 $PARSE_2" elif [ "$OSName" = "Arch_Linux" ] || [ "$OSName" = "Arch_Linux_ARM" ]; then assertHaveCommand checkupdates assertHaveCommand sed # For this to work properly, add a line to /etc/sudoers like this: # splunk ALL=(root) NOPASSWD: /usr/bin/pacman -Syy # Without the above line, checkupdates will not show updated packages unless the package databases were updated outside of this script (similar to Debian's apt update) if [ $(id -u) != 0 ]; then CMD='eval date ; eval uname -m | sed -r "s/(armv7l|aarch64)/arm64/;s/x86_64/amd64/"; sudo -n /usr/bin/pacman -Syy > /dev/null 2>&1 ; eval checkupdates' else CMD='eval date ; eval uname -m | sed -r "s/(armv7l|aarch64)/arm64/;s/x86_64/amd64/"; /usr/bin/pacman -Syy > /dev/null 2>&1 ; eval checkupdates' fi # shellcheck disable=SC2016 PARSE_0='NR==1 {DATE=$0}' PARSE_1='NR==2 {ARCH=$0}' PARSE_2='NR>2 {printf "%s arch_architecture=%s package=%s current_package_version=%s latest_package_version=%s\n", DATE, ARCH, $1, $2, $4}' MESSAGE="$PARSE_0 $PARSE_1 $PARSE_2" else assertHaveCommand yum CMD='eval date ; yum check-update' # shellcheck disable=SC2016 PARSE_0='NR==1 { DATE=$0 PROCESS=0 UPDATES["addons"]=0 UPDATES["base"]=0 UPDATES["extras"]=0 UPDATES["updates"]=0 }' # Skip extraneous text up to first blank line. # shellcheck disable=SC2016 PARSE_1='NR>1 && PROCESS==0 && $0 ~ /^[[:blank:]]*$|^$/ { PROCESS=1 }' # shellcheck disable=SC2016 PARSE_2='NR>1 && PROCESS==1 { num = split($0, update_array) if (num == 3) { # Record the update count UPDATES[update_array[3]] = UPDATES[update_array[3]]+1 printf "%s package=\"%s\" package_type=\"%s\"\n", DATE, update_array[1], update_array[3] } else if (num==2 && update_array[1] != "") { printf "%s package=\"%s\"\n", DATE, update_array[1] } }' PARSE_3='END { TOTALS="" for (key in UPDATES) { TOTALS=TOTALS key "=" UPDATES[key] " " } printf "%s %s\n", DATE, TOTALS }' MESSAGE="$PARSE_0 $PARSE_1 $PARSE_2 $PARSE_3" fi elif [ "$KERNEL" = "Darwin" ] ; then assertHaveCommand date assertHaveCommand softwareupdate CMD='eval date ; softwareupdate -l 2>&1 | grep -v "XType: Using static font registry"' # shellcheck disable=SC2016 PARSE_0='NR==1 { DATE=$0 PROCESS=0 TOTAL=0 }' # If the first non-space character is an asterisk, assume this is the name # of the update. Otherwise, print the update. # shellcheck disable=SC2016 PARSE_1='NR>1 && PROCESS==1 && $0 !~ /^[[:blank:]]*$/ { if ( $1 == "Title:" ) { line = $0; gsub(/^.*Title: /, "", line); gsub(/, Version:.*$/, "", line); PACKAGE="package=\"" line "\"" version = $0; gsub(/^.*Title: [^,]+, Version: /, "", version); gsub(/, Size:.*$/, "", version); VERSION="latest_package_version=\"" version "\"" RECOMMENDED="" RESTART="" TOTAL=TOTAL+1 if ( $0 ~ /Recommended: YES/ ) { RECOMMENDED="is_recommended=\"true\"" } if ( $0 ~ /Action: restart/ ) { RESTART="restart_required=\"true\"" } printf "%s %s %s %s\n", DATE, PACKAGE, VERSION, RECOMMENDED, RESTART } }' # Use sentinel value to skip all text prior to update list. # shellcheck disable=SC2016 PARSE_2='NR>1 && PROCESS==0 && $0 ~ /found[[:blank:]]the[[:blank:]]following/ { PROCESS=1 }' PARSE_3='END { printf "%s total_updates=%s\n", DATE, TOTAL }' MESSAGE="$PARSE_0 $PARSE_1 $PARSE_2 $PARSE_3" elif [ "$KERNEL" = "OpenBSD" ] ; then CMD="eval pkg_add -usv 2>&1 | grep -vE '(Adding quirks-|pkg_add should be run as root)' | grep ^Adding | sed -E 's/^Adding ([^:]+:)?(.*)->(.*)\(pretending\)/\2 \3/' | while read pkg ver; do name=\$(pkg_info -P \$pkg | grep -A1 ^Pkgpath:|tail -n1|cut -d/ -f2-); date \"+%a %b %e %H:%M:%S %Z %Y arch_architecture=\$(arch -s) package=\$name current_package_version=\$(echo \$pkg | sed -E \"s/\$name-//\") latest_package_version=\$ver\"; done" #CMD="eval for f in \$(pkg_add -usv 2>&1 | grep -vE \"(Adding quirks-|pkg_add should be run as root)\" | grep ^Adding | sed -E \"s/^Adding ([^:]+:)?(.*)->(.*)\(pretending\)/\2 \3/\"); do echo \$f; done" MESSAGE="{print}" else # Exits failUnsupportedScript fi # shellcheck disable=SC2086 $CMD 2> $TMP_ERROR_FILTER_FILE | tee "$TEE_DEST" | $AWK "$MESSAGE" # shellcheck disable=SC2086 grep -Ev "apt does not have a stable CLI interface|^[[:space:]]*$" < $TMP_ERROR_FILTER_FILE 1>&2 # shellcheck disable=SC2086 rm $TMP_ERROR_FILTER_FILE 2>/dev/null echo "Cmd = [$CMD]; | $AWK '$MESSAGE'" >> "$TEE_DEST"