File cleanoldsepoldir.sh of Package selinux-policy

#!/bin/bash
#set -x

# Function to check overlayfs directory and snapper
check_overlayfs() {
  local overlayfs_dir="/var/lib/overlay"
  local overlay_numbers="0"
  local snapper_path=`command -v snapper`
  #local tu_system=`grep micro /etc/issue`
  local tu_path=`command -v transactional-update`

  if [ -e /etc/selinux/${target_file} ]; then
    if [ -x $snapper_path ]; then
      if [[ -n ${tu_path} ]]; then
        tu_version=`transactional-update --version | grep -oe "transactional-update ."`
        if [ "${tu_version##*[!0-9]}" -lt "5" ]; then
          echo "INFO: Detected t-u version <5.0, fallback to overlayfs."
          overlayfs_fallback=true
          if [ ! -d $overlayfs_dir ]; then
          echo "INFO: Overlayfs directory does not exist, skipping."
          #exit 0
          fi
        else
          echo "INFO: Detected t-u version <5.0."
        fi
      else
         echo "INFO: Detected non-transactional system."
      fi
       #overlay_layers=`ls -rd ${overlayfs_dir}/*| grep -oE '[0-9]+'`
      overlay_layers=`/usr/bin/snapper --no-headers --machine-readable csv list | awk -F , '$3+0>=1 {print $3}'`
    else
      echo "ERROR: no snapper found on the system."
      exit 1
    fi
  else
    echo "ERROR: ${target_file} does not exists on the filesystem. Exiting."
    exit 1
  fi

}

# Function to check if a file exists in a given overlay layer
check_overlay_layer() {
  local layer_number="$1"
  local filename="$2"
  local overlayfs_fallback="${3:-false}"
  local base_overlay_path="/.snapshots/${layer_number}/snapshot"
#  echo "-$base_overlay_path"
  if "$overlayfs_fallback" ; then
#    echo "promnnena $overlayfs_fallback"
    local base_overlay_path="/var/lib/overlay/${layer_number}"
  fi
  local full_path="$base_overlay_path/etc/selinux/$filename"
  #local full_path="$base_overlay_path/$layer_number/snapshot/etc/selinux/$filename"
#  echo "+$base_overlay_path"

  if [ -e "$full_path" ]; then
    echo "     Found: $full_path in layer $layer_number"
    return 0 # File exists
  else
    echo "     Not found: $full_path in layer $layer_number"
    return 1 # File does not exist
  fi
}

# Function to print out custom selinux policy modules in /var/lib/selinux
check_custom_selinux_modules () {
    echo "INFO: Checking for possibly not migrated custom selinux modules in /var/lib/selinux/..."
    local old_selinux_dir="/var/lib/selinux"
    local new_selinux_dir="/etc/selinux"
    local custom_package_dir="/usr/share/selinux/packages" # Ensure this is correct

    # Collect all module directories and their basenames exept base "100"
    # Collect all 'active' module directories from old_selinux_dir
    local old_modules=()
    for i in minimum sandbox targeted; do
        if [ -d "${old_selinux_dir}/${i}/active/modules" ]; then
            mapfile -t current_modules < <(find "${old_selinux_dir}/${i}/active/modules/" -maxdepth 2 -type d '!' -empty | grep -vE '/(modules/|100|200|400|disabled)$')
            #mapfile -t current_modules < <(find "${old_selinux_dir}/${i}/active/modules/" -maxdepth 2 -type d '!' -empty |grep -vE '/100/' | grep -vE '/(modules/|100|200|400|disabled)$')
            old_modules+=("${current_modules[@]}")
        fi
    done
    # Sort and unique the old_modules basenames
    IFS=$'\n' read -r -d '' -a old_modules_unique < <(printf "%s\n" "${old_modules[@]}" | sort -u && printf '\0')

    # Collect all 'active' module directories from new_selinux_dir
    local new_modules=()
    for i in minimum sandbox targeted; do
        if [ -d "${new_selinux_dir}/${i}/active/modules" ]; then
            mapfile -t current_modules < <(find "${new_selinux_dir}/${i}/active/modules/" -maxdepth 2 -type d '!' -empty | grep -vE '/(modules/100|200|400|disabled)$')
            new_modules+=("${current_modules[@]}")
        fi
    done
    # Sort and unique the new_modules basenames
    IFS=$'\n' read -r -d '' -a new_modules_unique < <(printf "%s\n" "${new_modules[@]}" | sort -u && printf '\0')

    local custom_modules=()
    if [ -d "${custom_package_dir}" ]; then
        mapfile -t current_modules < <(find "${custom_package_dir}" -maxdepth 2 -type f '!' -empty)
        custom_modules+=("${current_modules[@]}")
    fi
    # Sort and unique the new_modules basenames
    IFS=$'\n' read -r -d '' -a custom_modules_unique < <(printf "%s\n" "${custom_modules[@]}" | sort -u && printf '\0')

    local missing_modules=()
    local modules_with_packages=()
    local modules_without_packages=()

    # Compare old_modules_unique with new_modules_unique
    local -A new_modules_unique_basename
    for new_module_name in "${new_modules_unique[@]}"; do
        local new_module_name_basename=$(basename "$new_module_name")
        new_modules_unique_basename["$new_module_name_basename"]=1
    done

    for old_module_name in "${old_modules_unique[@]}"; do
        local old_module_name_basename=$(basename "$old_module_name")
        if [[ -z "${new_modules_unique_basename[$old_module_name_basename]}" ]]; then
            missing_modules+=("$old_module_name_basename  (module dir: $old_module_name)")
        fi
    done
#    for old_module_name in "${old_modules_unique[@]}"; do
#	echo "  $old_module_name"
#	local old_module_name_basename=$(basename "${old_module_name}")
#        local found_in_new="false"
#        # Check if old_module_name exists in new_modules_unique
#        # Using a loop or 'printf "%s\n" ... | grep -Fxq ...' is more robust than =~
#	for new_module_name in "${new_modules_unique[@]}"; do
#	    local new_module_name_basename=$(basename "${new_module_name}")
#            if [ "$old_module_name_basename" == "$new_module_name_basename" ]; then
#                found_in_new="true"
#                break
#            fi
#        done
#
#        if [ "$found_in_new" == "false" ]; then
#	    missing_modules+=("$old_module_name_basename  (module dir: $old_module_name)")
#        fi
#    done

    # Now, process the missing modules to determine if they have packages
    if [ ${#missing_modules[@]} -eq 0 ]; then
        echo "INFO: No custom modules found missing from /etc/selinux."
    else
        echo "INFO: Found possible missing custom selinux modules:"
        for module_name in "${missing_modules[@]}"; do
            local package_found="false"
            # Check for files like module_name.pp under custom_package_dir/module_name/
                # Search for any file inside that directory, indicating it was part of a custom package structure

	  for custom_module_name in "${custom_modules[@]}"; do
              local short_module_name=$(basename "${module_name##*/}")
	      local module_basename="${short_module_name%)}"
	      if [[ ${custom_module_name} =~ ${module_basename}.pp.(bz2|gz) ]]; then
              # Get RPM name that owns this file
                  local rpm_query_name=$(rpm -qf --queryformat "%{NAME}" "${custom_module_name}" 2>/dev/null)
                  if [[ -n "$rpm_query_name" ]]; then
                      modules_with_packages+=("$module_name (from package: $rpm_query_name)")
                      package_found="true"
                  fi
            fi
	  done

            if [ "$package_found" == "false" ]; then
                modules_without_packages+=("$module_name")
            fi
        done

	echo "---"
        echo "These modules have corresponding packages (if file belongs to any), try to reinstall them:"
        if [ ${#modules_with_packages[@]} -eq 0 ]; then
            echo " (None)"
        else
            printf " * %s\n" "${modules_with_packages[@]}"
        fi

	echo "These modules do not have coresponding packages available."
	echo " Modules in \`200 or 400 or disabled\` directory could require manual intervention," \
             "if you did not install these or you believe there is an error, please open a bug:"
	echo " Modules in \`*/modules/100/*\` directory are in base policy and can be ignored."
        if [ ${#modules_without_packages[@]} -eq 0 ]; then
            echo " (None)"
        else
            printf " * %s\n" "${modules_without_packages[@]}"
        fi
	echo "---"
        echo "INFO: To manually check it again run /usr/libexec/selinux/cleanoldsepoldir.sh --check-custom-selinux-modules"
    fi
    echo
}

## Main function

# Definitions

# Variables
target_file_dir="/etc/selinux"
target_file="${1:-tmpselipoldir_migrated}"
first_layer_with_file=0
custom_package_dir="/usr/share/selinux/packages"

# Script parameters
while [[ $# -gt 0 ]]; do
  case $1 in
    -h|--help)
          echo "This script is part of tools used to check if it is safe to remove old selinux directory /var/lib/selinux"
          echo "Usage: $0 -h|--help, -f $target_file, --check-custom-selinux-modules"
          exit 0
      ;;
    -f|--file)
          shift
          target_file="$1"
      ;;
    --check-custom-selinux-modules)
          check_custom_selinux_modules
          exit 0
      ;;
     *)
          echo "Wrong param: $1, use -h for help"
          exit 1

  esac
  shift
done

# variables
target_file_dir="/etc/selinux"
target_file="${target_file:-tmpselipoldir_created}"
first_layer_with_file=0

if [ $TRANSACTIONAL_UPDATE ];then
  echo "INFO: Cannot run in transactional-shell, skipping."
  exit 0;
fi

check_overlayfs
[ -n "$overlay_layers" ] && echo "Checking for file: ${target_file_dir}/${target_file}"

for i in $(echo ${overlay_layers} | awk '{ for(i=NF;i>0;i--) printf "%s ", $i }'); do
  first_layer="$i"
  shift 1 # Remove the lower and upper numbers from the arguments
  check_overlay_layer "$first_layer" "$target_file"
  return_value=$?
  if (( $return_value == 0 )); then
    first_layer_with_file=$i
  fi
  if [ "$overlayfs_fallback" ]; then
    check_overlay_layer "$first_layer" "$target_file" "$overlayfs_fallback"
    echo
  fi
done

# check if all snapshots have ${target_file}
# !!! TODO - FIX issue on TW, all snapshots has to have tmpselipoldir_created before deleting /var/lib
if [[ ${#summary_not_found_array[@]} -eq 0 ]]; then
  if (( $first_layer ==  $first_layer_with_file )); then
    echo "INFO: Lowest numbered layer matches first numbered layer with file, deleting /var/lib/selinux."
    echo "DEBUG: these files would be normally deleted | head."
    find  /var/lib/selinux -type d -print | head -10 #-delete
    if [[ -f %{_sysconfdir}/selinux/tmpselipoldir_migrated ]]; then
      rm /etc/selinux/tmpselipoldir_migrated
      echo "DO NOT DELETE THIS FILE - Part of SELinux policy migration" > %{_sysconfdir}/selinux/tmpselipoldir_deleted
    fi
  fi
else
  echo "INFO: Layer without $target_file exists," \
     "that means older snapshot expecting selinux policy in /var/lib/selinux" \
     "therefore not deleting /var/lib/selinux."
fi

exit 0

openSUSE Build Service is sponsored by