#!/bin/bash
usage()
{
echo "Check whether a given list of commit is available in"
echo "a given list of branches."
echo
echo "Usage: ${0##*/} [branches.conf] term..."
echo
echo "Parameters:"
echo " branches.conf: file with the list of branches to be checked"
echo " one per line; must be the very first argument"
echo " term: hash of the commit|CVE|bsc to be found"
echo " -g <pattern>: pattern for grep to filter out branches you are interested in"
echo " -h: show this message"
echo " -c: show colored results always"
echo " -C: show colored results only if stdout is connected to the terminal"
echo " --: end of command line options"
echo
echo "Environment:"
echo " LINUX_GIT: This environment variable should specify the location"
echo " of a clone of the upstream Linux git repository"
}
fetch_branches()
{
local CACHED_BRANCHES="/tmp/$USER-branches.conf"
local URL="https://kerncvs.suse.de/branches.conf"
local EXPIRE=7
branches=$CACHED_BRANCHES
if [[ $(find "$CACHED_BRANCHES" -mtime -$EXPIRE -print 2>/dev/null) \
&& -s "$CACHED_BRANCHES" ]]; then
echo "Using cached $CACHED_BRANCHES" >&2
return
fi
curl "$URL" -o "$CACHED_BRANCHES"
}
setup_colors()
{
RED_FONT="$(tput setaf 1)$(tput bold)"
GREEN_FONT="$(tput setaf 2)$(tput bold)"
YELLOW_FONT="$(tput setaf 3)$(tput bold)"
MAGENTA_FONT="$(tput setaf 5)$(tput bold)"
NORMAL_FONT="$(tput sgr0)"
}
if [ $# -lt 1 ] ; then
usage
exit 1
fi
[[ -z $LINUX_GIT ]] && echo "LINUX_GIT is unset, therefore I cannot check the base kernel and false negatives are possible!" >&2
# Ideally, this would be under -f option, but for now I'm leaving it here for backwards compatibility. !!! TODO !!!
branches=$1
if [ ! -f "$branches" ] ; then
echo "Branches file not specified, trying to fetch it..." >&2
if ! fetch_branches ; then
"Error: Can't find the file with the list of branches: $branches nor fetch it"
exit 1
fi
else
shift;
fi
KBC_CHECK_TERMS=()
while [[ $# -gt 0 ]]
do
case "$1" in
-h | --h | --he | --hel | --help)
usage
exit 0
;;
-g | --g | --gr | --gre | --grep)
GREP_PATTERN="$2"
if [[ -z $GREP_PATTERN ]] || [[ $GREP_PATTERN = -- ]]; then
echo "Pattern must be non-empty!" >&2
exit 1
fi
shift 2
;;
-c | --c | --co | --col | --colo | --color | --colou | --colour)
setup_colors
shift 1
;;
-C | --C | --Co | --Col | --Colo | --Color | --Colou | --Colour)
[[ -t 1 ]] && setup_colors
shift 1
;;
--)
shift
KBC_CHECK_TERMS+=($*)
shift $#
;;
-*)
echo "Skipping unrecognized option: $1" >&2
shift 1
;;
*)
KBC_CHECK_TERMS+=("$1")
shift 1
;;
esac
done
term2regex()
{
shopt -q nocasematch
local t=$1
case $t in
# CVEs first
2[0-9][0-9][0-9]-*)
t=cve-$t
;&
cve-*)
echo "^References:.*$t"
;;
# looks like a hash, look for commits
[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]*)
echo "^Git-commit:.*$t"
;;
# treat rest as a generic reference
*)
echo "^References:.*$t"
;;
esac
}
check_with_upstream_tag()
{
local ret=1
local line="$(git grep -e SRCVERSION "remotes/origin/$1" -- rpm/config.sh)"
local tag_suffix="${line##*=}"
if [[ -n $tag_suffix ]] ; then
git --git-dir="$LINUX_GIT/.git" merge-base --is-ancestor "$2" "v$tag_suffix"
ret=$?
fi
return $ret
}
declare -A TBL_GREP_PATCHES
check_branch()
{
local verbose=0
if [ "$1" = "-v" ] ; then
verbose=1
shift
fi
local branch="$1"
git show-ref --verify --quiet "refs/remotes/origin/${branch}" || return 42
local found=""
local blacklisted=""
local missing=""
local term
for term in "${KBC_CHECK_TERMS[@]}" ; do
term_to_search_for="$(term2regex $term)"
if [[ $term_to_search_for == ^Git-commit* ]] && [[ -n $LINUX_GIT ]] && check_with_upstream_tag "$branch" "$term" ; then
found="$found $term"
continue
fi
local tbl_patches_ret
local key="${term_to_search_for};${branch}"
if [[ -z ${TBL_GREP_PATCHES["$key"]} ]]; then
git grep -qi "$term_to_search_for" "remotes/origin/$branch" -- 'patches.*' 2>/dev/null
tbl_patches_ret=$?
TBL_GREP_PATCHES["$key"]=$tbl_patches_ret
else
tbl_patches_ret=${TBL_GREP_PATCHES["$key"]}
fi
if [ $tbl_patches_ret -eq 0 ] ; then
found="$found $term"
else
git grep -qi "$term" "remotes/origin/$branch" -- 'blacklist.conf' 2>/dev/null
if [ $? -eq 0 ] ; then
blacklisted="$blacklisted $term"
else
missing="$missing $term"
fi
fi
done
# found
if [ -z "$missing" ] && [ -z "$blacklisted" ] ; then
return 0
fi
# blacklisted
if [ -z "$found" ] && [ -z "$missing" ] ; then
return 1
fi
# missing
if [ -z "$found" ] && [ -z "$blacklisted" ] ; then
return 3
fi
# partly
if [ $verbose -ne 0 ] ; then
if [ -n "$missing" ] ; then
echo " missing hash:"
local hash
for hash in $missing ; do
echo " $hash"
done
fi
if [ -n "$blacklisted" ] ; then
echo " blacklisted hash:"
local hash
for hash in $blacklisted ; do
echo " $hash"
done
fi
echo
fi
return 2
}
check_parents()
{
local last_branch=""
local branch
for branch in "$@" ; do
check_branch $branch
case $? in
0)
echo " (found in $branch)"
return
;;
2)
echo " (partly in $branch)"
return
;;
*)
;;
esac
last_branch="$branch"
done
# not found anywhere
echo " (not even in $last_branch)"
}
grep -w build "$branches" | grep -v -E "^(master|vanilla|linux-next|cve)" | \
while read line ; do
line=${line%%\#*}
branch=${line%%:*}
# empty line or comment
if [ -z "$branch" ] ; then
continue
fi
if [[ -n $GREP_PATTERN ]] && echo "$branch" | grep -q -E -v -- "$GREP_PATTERN"; then
continue
fi
# always check also the _EMBARGO branch as a possible parent
parents="${branch}_EMBARGO"
set dummy ${line#$branch:}
while [ $# -gt 0 ] ; do
shift
[[ "$1" =~ "merge:" ]] || continue
tmp="${1//*merge:-/}"
parents="$parents ${tmp//*merge:/}"
done
printf "%-23s" "$branch"
check_branch "$branch"
case $? in
42)
echo "branch does not exist"
;;
0)
echo "${GREEN_FONT}<ok>${NORMAL_FONT}"
;;
1)
echo "${MAGENTA_FONT}<blacklisted>${NORMAL_FONT}"
;;
2)
echo -n "${YELLOW_FONT}<partly>${NORMAL_FONT} "
check_parents $parents
# print missing commits
check_branch -v "$branch"
;;
*)
echo -n "${RED_FONT}<missing>${NORMAL_FONT}"
check_parents "${branch}_EMBARGO" $parents
esac
done