#!/bin/bash
#
# Copyright (c) 2016 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#

if [[ $EUID -ne 0 ]]; then
   echo "This utility must be run as root." >&2
   exit 1
fi

function show_usage()
{
    cat <<EOF
Usage: [ --include-pyc ] [ --include-cfg ] --skip-multi [ pkg ... ]

This utility scans the installed RPMs to compare checksums of files.
By default, files flagged as config are skipped, as are python pyc files.

Optional arguments:
    --include-pyc : Include pyc files in check
    --include-cfg : Include config files in check
    --skip-links  : Skip symlink check
    --skip-multi  : Skip the search for files with multiple owners
    pkg           : Specify one or more packages to limit the scan
                    (implies --skip-multi)

EOF

    exit 1
}

declare INCLUDE_PYTHON_FILES="no"
declare INCLUDE_CFG_FILES="no"
declare CHECK_FOR_MULTI="yes"
declare CHECK_LINKS="yes"
declare TIS_ONLY="yes"

declare CHECK_RPM=

for arg in "$@"
do
    case $arg in
        -h|--help)
            show_usage
            ;;
        --include-pyc)
            INCLUDE_PYTHON_FILES="yes"
            ;;
        --include-cfg)
            INCLUDE_CFG_FILES="yes"
            ;;
        --skip-links)
            CHECK_LINKS="no"
            ;;
        --skip-multi)
            CHECK_FOR_MULTI="no"
            ;;
        --all-rpms)
            TIS_ONLY="no"
            ;;
        *)
            CHECK_RPM="$CHECK_RPM $arg"
            CHECK_FOR_MULTI="no"
            ;;
    esac
done

function rpm_list()
{
    if [ -n "$CHECK_RPM" ]
    then
        for pkg in $CHECK_RPM
        do
            echo $pkg
        done
    elif [ "$TIS_ONLY" = "yes" ]
    then
        rpm -qa | grep '\.tis\.' | sort
    else
        rpm -qa | sort
    fi
}

rpm_list | while read pkg
do
    # Get the --dump from the pkg
    rpm -q --queryformat "[%{FILENAMES}|%{FILEMD5S}|%{FILEFLAGS:fflags}|%{FILELINKTOS}\n]" $pkg | \
    while IFS='|' read pname psum pflags plinkto
    do
        if [[ $pname == "(contains" ]]
        then
            # (contains no files)
            continue
        fi

        if [[ $INCLUDE_CFG_FILES == "no" && $pflags =~ c ]]
        then
            # Skip file already flagged as config
            continue
        fi

        if [[ $INCLUDE_PYTHON_FILES == "no" && $pname =~ \.py[co]$ ]]
        then
            # Skip python .pyo or .pyc file
            continue
        fi

        # Directories and symlinks will have no checksum
        if [[ -z $psum ]]
        then
            if [[ -n $plinkto && $CHECK_LINKS == "yes" ]]
            then
                # Check the symlink pointer
                flinkto=$(readlink $pname)
                if [[ "$flinkto" != "$plinkto" ]]
                then
                    echo "Link Mismatch: $pname ($pkg)"
                fi
            fi
            continue
        fi

        # Does the file exist?
        if [ ! -e "$pname" ]
        then
            echo "Missing: $pname ($pkg)"
            continue
        fi

        # Has the file been replaced by a symlink? ie. update-alternatives
        if [ -L "$pname" ]
        then
            continue
        fi

        let -i sumlen=$(echo -n $psum | wc -c)
        if [ $sumlen = 64 ]
        then
            sumcmd=sha256sum
        else
            sumcmd=md5sum
        fi

        echo $psum $pname | $sumcmd --check --status
        if [ $? -ne 0 ]
        then
            echo "Mismatch: $pname ($pkg)"
        fi
    done
done


function check_for_multi_master()
{
    # Search for files owned by multiple packages
    prev=
    rpm_list | xargs rpm -q --queryformat "[%{FILENAMES}|%{=NAME}\n]" | sort | while IFS='|' read f p
    do
        if [ "$f" = "$prev" ]
        then
            echo $f
        fi
        prev=$f
    done | sort -u | while read f
    do
        if [ ! -d "$f" ]
        then
            echo $f
        fi
    done
}

if [ $CHECK_FOR_MULTI = "yes" ]
then
    echo
    echo
    echo "The following files belong to multiple packages:"
    echo
    check_for_multi_master
fi

