#!/bin/bash # # prtwash - a simple bash script for cleaning the port tree # of the CRUX Linux distribution. # # Copyright (c) 2003 by Simone Rota # Revised 2021 by John McQuah # # ************************************************************************* # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU General Public License as published by * # * the Free Software Foundation; either version 2 of the License, or * # * (at your option) any later version. * # * * # ************************************************************************* # # Took some inspiration and code from Martin Opel's prtsweep v1.6 script, # and comments by therealfun on FlySpray bug 1851. # # **** USE AT YOUR OWN RISK **** # # TODO # - a summary of files / bytes removed after the # execution would be useful. # - verify signatures before sourcing each Pkgfile. # VERSION="0.9" usage() { echo "Usage: prtwash [-p] [-s] [-d] [-t] [-a] [ ...]" exit 1 } showversion() { echo "prtwash" $VERSION echo "(c) 2003 by Simone Rota, revised 2021 by John McQuah" echo "This program is distributed under the GNU GPL license" } interrupted() { echo "=======> operation interrupted." exit 1 } checkparams() { # Do some test on given parameters. if [ "$auto" = "0" ]; then if [ -z "$prtdirs" ]; then usage exit -1 fi for (( p=0 ; p<=${#prtdirs[@]} ; p++ )); do if [ -z "${prtdirs[$p]}" ]; then unset prtdirs[$p] continue fi if [ ! -d "${prtdirs[$p]}" ]; then echo "WARN: '${prtdirs[$p]}' is not the directory for a port. Skipping." unset prtdirs[$p] continue fi if [ ! -f "${prtdirs[$p]}"/Pkgfile ]; then echo "WARN: no Pkgfile found in '${prtdirs[$p]}'. Skipping." unset prtdirs[$p] fi done fi } getoptions () { # Name says it all. while getopts bpsdathv opt; do case "$opt" in b) removeoldbuilds=1;; p) removepackage=1;; s) removesources=1;; d) removeaddonfiles=1;; t) test=1;; a) auto=1;; h) usage exit 0 ;; v) showversion exit 0 ;; \?) usage exit -1 ;; esac done shift $(($OPTIND - 1)) prtdirs=("$@") } getdirs() { # scans /etc/prt-get.conf for port dirs to process current="" for s in `sed 's/^[ \t]*//;s/[ \t]*$//' \ $CONF_PGET|grep '^prtdir.*:'|sed 's/:/ /;s/,/ /;s/prtdir//;s/#.*//'` do if [ "`echo $s|grep '/'`" != "" ]; then current=$s else if [ "$current" != "" ]; then singledirs=(${singledirs[*]} $current/$s) fi fi done for s in `sed 's/^[ \t]*//;s/[ \t]*$//;/:/d' \ $CONF_PGET|grep '^prtdir.*'|sed 's/prtdir//;s/#.*//'` do basedirs=(${basedirs[*]} $s) done } remove() { # Removes a file/directory if [ "$test" = 1 ]; then echo "+ (t) removing" $1 else echo "+ removing" $1 rm -r $1 fi } swash() { # There are faster and safer ways of cleaning a directory shared among all ports than # to iterate through the ports collection itself. # Inform the admin that this script is ill-suited for their configuration. echo "It looks like you have defined common directories for downloads or packages." >&2 echo "For such a configuration, this script is slower and less reliable" >&2 echo "than a custom tool would be." >&2 echo "If you intended to clean out your ports repositories instead," >&2 echo "consider the companion tool prtsweep." >&2 echo "Proceed anyway? (y/n)" >&2 read CONFIRM if [ "$CONFIRM" != "y" ]; then exit 0 fi } finalwarning() { echo "Last chance to bail out!" >&2 echo "This script will remove from the common download directories more files " >&2 echo "than perhaps you intended." >&2 echo "Proceed anyway? (y/n)" >&2 read CONFIRM if [ "$CONFIRM" != "y" ]; then exit 0 fi } nwash() { if [ "$auto" = 0 ]; then for p in ${prtdirs[@]}; do echo "Washing the directory of port $p:" wash $p done exit 0 else if [ ! -f "$CONF_PGET" ]; then echo "ERROR: cannot find configuration file '$CONF_PGET'" exit -1 fi echo "Analyzing port tree..." getdirs # wash base dirs for d in ${basedirs[*]}; do if [ -d "$d" ]; then for p in "$d"/*; do if [ -d "$p" ]; then wash $p fi done else echo "ERROR: directory '$d' not found, check your configuration file!" fi done # wash custom dirs (see prt-get.conf) for p in ${singledirs[*]}; do if [ -d "$p" ]; then wash $p else echo "ERROR: directory '$p' not found, check your configuration file!" fi done exit 0 fi exit 0 } wash() { # Does the actual removal work. local tosave name version source renames pkgfiles packagename portname SAVELOC PKGLOC local dir=$1 if [ ! -f "$dir"/Pkgfile ]; then # tested before, the test here is for # auto (-a) command echo "WARN: no Pkgfile found in $dir. Skipping." else declare -a tosave # array of files not to be deleted pkgfiles=(`( . $dir/Pkgfile; PKGLOC=$(eval "printf '%s' $PKGGLOB"); \ PKGFILE=$(eval "printf '%s' $BLTGLOB"); \ SAVELOC=$(eval "printf '%s' $SRCGLOB"); \ for (( p=0; p<${#source[@]}; p++ )); do \ [ -n "${renames[$p]}" -a "${renames[$p]}" != "SKIP" ] && source[$p]="ftp://host/${renames[$p]}"; \ done; \ printf '%s ' "${SAVELOC:-$dir}"; \ printf '%s ' "${PKGLOC:-$dir}/$PKGFILE"; \ printf '%s ' "${source[@]}")`) SAVELOC=${pkgfiles[0]} packagename=`basename ${pkgfiles[1]}` PKGLOC=`dirname ${pkgfiles[1]}` # keep sources if [ "$removesources" = 0 ]; then for (( p=2; p<${#pkgfiles[@]}; p++ )) ; do tosave=( "${tosave[@]}" `get_filename "${pkgfiles[$p]}"` ) done fi # new in version 0.3 we (try to) always save files that are # retrieved by rsync/httpup (non http(s) or ftp in source) # ie patches, etc. These can be deleted with -d (extra files) # option if [ "$removesources" = 1 ] && [ "$removeaddonfiles" = 0 ]; then for (( p=2; p<${#pkgfiles[@]}; p++ )) ; do src="${pkgfiles[$p]}" pkgfiles[$p]=$(get_filename $src) [ "${src:0:7}" = "ftp://" \ -o "${src:0:8}" = "http://" \ -o "${src:0:6}" = "https://" ] || tosave=( "${tosave[@]}" "${pkgfiles[$p]}" ) done fi # Now that the distinction between files obtained via rsync/httpup # and files downloaded during `pkgmk -d` has been encoded in the # array $tosave, clean up the filenames in the $pkgfiles array. if [ "$removesources" = 0 ] || [ "$removeaddonfiles" = 1 ]; then for (( p=2; p<${#pkgfiles[@]}; p++ )) ; do pkgfiles[$p]=$(get_filename ${pkgfiles[$p]}) done fi if [ ! "$removepackage" = 1 ]; then # keep package tosave=( "${tosave[@]}" "$packagename" ) fi if [ ! "$removeaddonfiles" = 1 ]; then # keep additional and dot files tosave=( "${tosave[@]}" \ ".signature" \ ".32bit" \ ".footprint" \ ".nostrip" \ "README" \ "FAQ" \ "pre-install" \ "post-install" ) fi # keep Pkgfile. We always want to keep this nice funny file. tosave=( "${tosave[@]}" "Pkgfile" ) unset pkgfiles[0] pkgfiles[1] # trust that our parsing of the Pkgfile has not set the wrong source directory. # But if an absolute path is specified, restrict the `ls -A` output # to avoid overzealous deletions. cd $SAVELOC if [ "${SAVELOC:0:1}" != "/" ]; then for f in `ls -A`; do keep $f || remove $f done elif [ ${#pkgfiles[@]} -gt 0 ]; then for f in ${pkgfiles[@]}; do for match in `ls -A ${f}* 2>/dev/null`; do keep $match || remove $match done done fi cd - &>/dev/null # same trust as to the location of the built packages, if different from SAVELOC. # Older versions can be discarded too, if $removeoldbuilds = 1. if [ "$removepackage" = 1 ]; then (cd $PKGLOC; [ -f $packagename ] && keep "$packagename" || remove "$packagename") fi if [ "$removeoldbuilds" = 1 ]; then portname=$(echo "$packagename" | sed 's/#.*//') (cd $PKGLOC; for f in `ls ${portname}*.pkg.tar.[bgx]z* 2>/dev/null`; do keep $f || remove $f; done) fi fi } keep() { # Checks if filename item is in tosave[] array # return 0 if present, 1 if not. item=$1 local excode excode=1 # assume not in the array until proven otherwise for i in "${tosave[@]}"; do if [ "$i" = "$item" ]; then [ "$test" = "1" ] && echo "= (t) ... keeping $item" || echo "= ... keeping $item" excode=0 break fi done return $excode } get_filename() { if [[ $1 =~ ^(http|https|ftp|file)://.*/(.+) ]]; then echo "${BASH_REMATCH[2]}" else echo $1 fi } printparams() { echo "removepackage: $removepackage" echo "removeoldbuilds: $removeoldbuilds" echo "removesources: $removesources" echo "removeaddonfiles: $removeaddonfiles" echo "auto: $auto" echo "test: $test" } ################################################### # Main ################################################### trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM if [ $# -lt 1 ]; then usage exit -1 fi removepackage=0 removesources=0 removeaddonfiles=0 removeoldbuilds=0 test=0 auto=0 basedirs=() singledirs=() prtdirs=() CONF_PGET="/etc/prt-get.conf" CONF_PMK="/etc/pkgmk.conf" getoptions $@ checkparams $@ COMPRESSION_MODE="gz" if [ -f $PKGCONFIG ]; then COMPRESS_CFG=$(grep "PKGMK_COMPRESSION_MODE" $CONF_PMK | \ sed 's/[\t w]*#.*//;s/.*=//;s/\"//g') [ -n "$COMPRESS_CFG" ] && COMPRESSION_MODE=$COMPRESS_CFG SRCGLOB=$(grep "PKGMK_SOURCE_DIR" $CONF_PMK | sed 's/[\t w]*#.*//; s/.*=//; s/\"//g') PKGGLOB=$(grep "PKGMK_PACKAGE_DIR" $CONF_PMK | sed 's/[\t w]*#.*//; s/.*=//; s/\"//g') fi BLTGLOB='$name#$version-$release.pkg.tar.$COMPRESSION_MODE' # First determine the type of fs layout the admin configured for source downloads. # Layouts 1 and 2: at least one directory is shared among all ports (for sources or built packages), # or the admin has organized downloads in some other way outside of $PORTS_ROOT. if [ -n "${SRCGLOB}" ] && echo "${SRCGLOB}" | grep -q -v '\$name'; then swash nwash elif [ -n "${PKGGLOB}" ] && echo "${PKGGLOB}" | grep -q -v '\$name'; then swash nwash fi # Layout 3: separate directories, named according to port, are potentially cluttered with downloads. # Unlike prtsweep, actually try to follow these dynamically-defined paths and clean them up. # $PORTS_ROOT might still remain cluttered after this "wash", but the admin can just run prtsweep. # For such filesystem layouts, the two scripts complement each other nicely. if [ -z "${SRCGLOB}" -o -z "${PKGGLOB}" ]; then nwash elif [ -n "${SRCGLOB}" ] && echo "$SRCGLOB" | grep -q '\$name'; then nwash elif [ -n "${PKGGLOB}" ] && echo "$PKGGLOB" | grep -q '\$name'; then nwash else # Should have exited by now, but if not, # offer another chance to abort. finalwarning nwash fi