#!/bin/sh # delete obsolete binary packages # 1] Condition for deleting a package A is that: # a) nothing on the build-list (make|check|)depends on A and # b) no built package B which is not being deleted depends on A # "Package x depends on package y" means, that something needed by x # is provided by y and no other package which will not be deleted. # shellcheck disable=SC2039,SC2119,SC2120 # shellcheck source=../lib/load-configuration . "${0%/*}/../lib/load-configuration" # shellcheck disable=SC2016 usage() { >&2 echo '' >&2 echo 'delete-packages [options]:' >&2 echo ' delete obsolete binary packages.' >&2 echo '' >&2 echo 'possible options:' >&2 echo ' -b|--build-support $package-list:' >&2 echo ' Only delete the matching packages from [build-support].' >&2 echo ' conflicts -f; only accepted once' >&2 echo ' -f|--force $id:' >&2 echo ' Only delete the selected packages - but delete in any case.' >&2 echo ' conflicts -b' >&2 echo ' -h|--help:' >&2 echo ' Show this help and exit.' >&2 echo ' -i|--ignore-insanity:' >&2 echo ' Do not abort when insane.' >&2 echo ' -n|--no-action:' >&2 echo ' Only print what would be deleted.' >&2 echo ' -w|--wait:' >&2 echo ' If necessary, wait for lock blocking.' [ -z "$1" ] && exit 1 || exit "$1" } eval set -- "$( getopt -o b:f:hinw \ --long build-support: \ --long force: \ --long help \ --long ignore-insanity \ --long no-action \ --long wait \ -n "$(basename "$0")" -- "$@" || \ echo usage )" block_flag='-n' build_support='' force_ids='' ignore_insanity=false no_action=false while true do case "$1" in -b|--build-support) if [ -n "${build_support}" ]; then >&2 printf 'Option given multiple times: %s\n' "$1" usage fi shift build_support="$1" ;; -f|--force) shift force_ids="${force_ids} $1" ;; -h|--help) usage 0 ;; -i|--ignore-insanity) ignore_insanity=true ;; -n|--no-action) no_action=true ;; -w|--wait) block_flag='' ;; --) shift break ;; *) >&2 echo 'Whoops, forgot to implement option "'"$1"'" internally.' exit 42 ;; esac shift done if [ -n "${build_support}" ] && \ [ -n "${force_ids}" ]; then >&2 echo 'Conflicting flags: -b & -f.' usage fi if [ -s "${work_dir}/build-master-sanity" ]; then >&2 echo 'Build master is not sane.' if ! ${ignore_insanity}; then exit fi fi # Create a lock file and a trap. if ! ${no_action}; then # exec 9> "${build_list_lock_file}" # if ! verbose_flock ${block_flag} 9; then # >&2 echo 'come back (shortly) later - I cannot lock build list.' # exit 0 # fi exec 8> "${package_database_lock_file}" if ! verbose_flock ${block_flag} 8; then >&2 echo 'come back (shortly) later - I cannot lock package database.' exit 0 fi exec 7> "${sanity_check_lock_file}" if ! verbose_flock -s ${block_flag} 7; then >&2 echo 'come back (shortly) later - sanity-check running.' exit 0 fi fi tmp_dir=$(mktemp -d "${work_dir}/tmp.delete-packages.XXXXXXXXXX") trap 'rm -rf --one-file-system "${tmp_dir}"' EXIT export TMPDIR="${tmp_dir}" # shellcheck disable=SC2016 { printf 'CREATE TEMPORARY TABLE `bpir_to_delete` (`id` BIGINT, UNIQUE KEY (`id`));\n' printf 'INSERT IGNORE INTO `bpir_to_delete`' if [ -n "${force_ids}" ]; then printf ' VALUES ' # shellcheck disable=SC2086 printf '%s\n' ${force_ids} | \ base64_encode_each | \ sed ' s/^/(from_base64("/ s/$/")),/ $ s/,$/;/ ' elif [ -n "${build_support}" ]; then printf ' SELECT DISTINCT `binary_packages_in_repositories`.`id`' printf ' FROM `binary_packages_in_repositories`' mysql_join_binary_packages_in_repositories_binary_packages mysql_join_binary_packages_in_repositories_repositories mysql_join_binary_packages_architectures mysql_join_repositories_architectures '' 'r_a' printf ' WHERE `repositories`.`name`="build-support"' printf ' AND CONCAT(`r_a`.`name`," ",' mysql_package_name_query printf ') IN (' base64_encode_each < "${build_support}" | \ sed ' s/^/from_base64("/ s/$/"),/ $ s/,$/);/ ' else printf ' SELECT DISTINCT `binary_packages_in_repositories`.`id`' printf ' FROM `binary_packages_in_repositories`' mysql_join_binary_packages_in_repositories_binary_packages mysql_join_binary_packages_in_repositories_repositories printf ' LEFT' # should not be necessary, but is formally more correct mysql_join_binary_packages_install_target_providers printf ' WHERE `binary_packages_in_repositories`.`is_to_be_deleted`' printf ' AND `repositories`.`is_on_master_mirror`' printf ' AND NOT EXISTS (' # no packages depending on that one exist printf 'SELECT 1 FROM `dependencies`' mysql_join_dependencies_dependency_types printf ' AND `dependency_types`.`relevant_for_binary_packages`' mysql_join_dependencies_binary_packages '' 'd_bp' mysql_join_binary_packages_binary_packages_in_repositories 'd_bp' 'd_bpir' # deliberately break dependencies of deletion-list packages printf ' AND NOT `d_bpir`.`is_to_be_deleted`' mysql_join_binary_packages_in_repositories_repositories 'd_bpir' 'd_r' # this is deliberately less restrict than `d_r`.`is_on_master_mirror` # shellcheck disable=SC2154 printf ' AND `d_r`.`stability` NOT IN (%s,%s)' \ "${repository_stability_ids__forbidden}" \ "${repository_stability_ids__virtual}" printf ' WHERE `install_target_providers`.`install_target`=`dependencies`.`depending_on`' printf ' AND `d_r`.`architecture`=`repositories`.`architecture`' printf ' AND NOT EXISTS (' printf 'SELECT 1 FROM `binary_packages` AS `s_bp`' mysql_join_binary_packages_binary_packages_in_repositories 's_bp' 's_bpir' printf ' AND NOT `s_bpir`.`is_to_be_deleted`' mysql_join_binary_packages_install_target_providers 's_bp' 's_itp' mysql_join_binary_packages_in_repositories_repositories 's_bpir' 's_r' printf ' AND `s_r`.`is_on_master_mirror`' printf ' JOIN `repository_stability_relations`' printf ' ON `repository_stability_relations`.`more_stable`=`s_r`.`stability`' printf ' WHERE `s_itp`.`install_target`=`install_target_providers`.`install_target`' printf ' AND `s_r`.`architecture`=`repositories`.`architecture`' printf ' AND `repository_stability_relations`.`less_stable`=`d_r`.`stability`' printf ')' printf ');\n' fi # first enter all potentially to-be-deleted binary_packages into the table printf 'CREATE TEMPORARY TABLE `bp_to_delete` (`id` BIGINT, UNIQUE KEY (`id`));\n' printf 'INSERT IGNORE INTO `bp_to_delete`' printf ' SELECT `binary_packages_in_repositories`.`package`' printf ' FROM `bpir_to_delete`' printf ' JOIN `binary_packages_in_repositories` ON `bpir_to_delete`.`id`=`binary_packages_in_repositories`.`id`;\n' # then remove all bp_to_delete which are actually still needed printf 'DELETE `bp_to_delete` FROM `bp_to_delete`' printf ' JOIN `binary_packages` ON `binary_packages`.`id`=`bp_to_delete`.`id`' mysql_join_binary_packages_binary_packages_in_repositories printf ' WHERE NOT EXISTS (' printf 'SELECT 1' printf ' FROM `bpir_to_delete`' printf ' WHERE `bpir_to_delete`.`id`=`binary_packages_in_repositories`.`id`' printf ');\n' printf 'SELECT DISTINCT' printf ' "repo",' printf '`architectures`.`name`,' printf '`repositories`.`name`' printf ' FROM `bpir_to_delete`' printf ' JOIN `binary_packages_in_repositories` ON `bpir_to_delete`.`id`=`binary_packages_in_repositories`.`id`' mysql_join_binary_packages_in_repositories_binary_packages mysql_join_binary_packages_in_repositories_repositories mysql_join_repositories_architectures printf ';\n' printf 'SELECT' printf ' "package",' printf '`architectures`.`name`,' printf '`repositories`.`name`,' printf '`binary_packages`.`pkgname`' printf ' FROM `bpir_to_delete`' printf ' JOIN `binary_packages_in_repositories` ON `bpir_to_delete`.`id`=`binary_packages_in_repositories`.`id`' mysql_join_binary_packages_in_repositories_binary_packages mysql_join_binary_packages_in_repositories_repositories mysql_join_repositories_architectures printf ';\n' printf 'SELECT "package-file",CONCAT(' printf '`r_a`.`name`,"/",' printf '`repositories`.`name`,"/",' mysql_package_name_query printf ') FROM `bpir_to_delete`' printf ' JOIN `binary_packages_in_repositories` ON `bpir_to_delete`.`id`=`binary_packages_in_repositories`.`id`' mysql_join_binary_packages_in_repositories_binary_packages mysql_join_binary_packages_in_repositories_repositories mysql_join_binary_packages_architectures mysql_join_repositories_architectures '' 'r_a' printf ';\n' printf 'SELECT "package-in-repository-id",`bpir_to_delete`.`id`' printf ' FROM `bpir_to_delete`' printf ';\n' printf 'SELECT "package-file",CONCAT(' printf '"pool/",' mysql_package_name_query printf ') FROM `bp_to_delete`' printf ' JOIN `binary_packages` ON `bp_to_delete`.`id`=`binary_packages`.`id`' mysql_join_binary_packages_architectures printf ';\n' # we do not need the ids of the to-be-deleted binary_packages, because # they stay in the database and are merely moved to the deletion-list } | \ mysql_run_query | \ sed ' y/\t/ / /^repo /{ s/^\S\+ // w '"${tmp_dir}"'/repositories d } /^package /{ s/^\S\+ // w '"${tmp_dir}"'/packages d } /^package-file /{ s/^\S\+ // w '"${tmp_dir}"'/package-files s/$/.sig/ w '"${tmp_dir}"'/package-files d } /^package-in-repository-id /{ s/^\S\+ // w '"${tmp_dir}"'/package-in-repository-ids d } ' if [ -n "${build_support}" ] && \ [ -w "${build_support}" ] && \ [ ! -p "${build_support}" ]; then sed -n ' \,^pool/, d s/\.sig$// T p ' "${tmp_dir}/package-files" > \ "${build_support}" fi if [ ! -s "${tmp_dir}/packages" ]; then printf 'Nothing to delete.\n' exit fi mkdir "${tmp_dir}/repos" while read -r arch repo; do mkdir -p "${tmp_dir}/repos/${arch}" ${master_mirror_rsync_command} \ "${master_mirror_rsync_directory}/${arch}/${repo}/${repo}.db.tar.gz" \ "${master_mirror_rsync_directory}/${arch}/${repo}/${repo}.files.tar.gz" \ "${tmp_dir}/repos/${arch}" # shellcheck disable=SC2046 repo-remove "${tmp_dir}/repos/${arch}/${repo}.db.tar.gz" \ $( grep "^$(str_to_regex "${arch} ${repo}") " "${tmp_dir}/packages" | \ cut -d' ' -f3 ) if ! ${no_action}; then recompress_gz \ "${tmp_dir}" \ "${tmp_dir}/repos/${arch}/${repo}."*".tar.gz" \ "${tmp_dir}/repos/${arch}/${repo}."*".tar.gz.old" ${master_mirror_rsync_command} \ "${tmp_dir}/repos/${arch}/${repo}.db."* \ "${tmp_dir}/repos/${arch}/${repo}.files."* \ "${master_mirror_rsync_directory}/${arch}/${repo}/" fi done < \ "${tmp_dir}/repositories" if ${no_action}; then printf 'Now, I would remove the packages from the database and delete the following files from the master mirror:\n' sed ' s,^, , ' "${tmp_dir}/package-files" exit fi # shellcheck disable=SC2016 { printf 'CREATE TEMPORARY TABLE `bpir_to_delete` (`id` BIGINT, UNIQUE KEY (`id`));\n' printf 'LOAD DATA LOCAL INFILE "%s" INTO TABLE `bpir_to_delete`;\n' \ "${tmp_dir}/package-in-repository-ids" printf 'UPDATE `binary_packages_in_repositories`' printf ' JOIN `bpir_to_delete`' printf ' ON `bpir_to_delete`.`id`=`binary_packages_in_repositories`.`id`' mysql_join_binary_packages_in_repositories_repositories printf ' JOIN `repositories` AS `d_r`' printf ' ON `d_r`.`stability`=%s' \ "${repository_stability_ids__forbidden}" printf ' AND `d_r`.`architecture`=`repositories`.`architecture`' printf ' SET `binary_packages_in_repositories`.`repository`=`d_r`.`id`' # shellcheck disable=SC2154 printf ' WHERE `repositories`.`stability`!=%s;\n' \ "${repository_stability_ids__standalone}" printf 'DELETE `binary_packages_in_repositories`' printf ' FROM `binary_packages_in_repositories`' printf ' JOIN `bpir_to_delete`' printf ' ON `bpir_to_delete`.`id`=`binary_packages_in_repositories`.`id`' mysql_join_binary_packages_in_repositories_repositories printf ' WHERE `repositories`.`stability`!=%s;\n' \ "${repository_stability_ids__forbidden}" printf 'DELETE `binary_packages`' printf ' FROM `binary_packages`' printf ' WHERE NOT EXISTS (' printf 'SELECT 1 FROM `binary_packages_in_repositories`' printf ' WHERE `binary_packages_in_repositories`.`package`=`binary_packages`.`id`' printf ');\n' } | \ mysql_run_query sed ' s/^/rm "/ s/$/"/ ' "${tmp_dir}/package-files" | \ ${master_mirror_sftp_command}