#!/bin/sh # # scratchpkg # # Copyright (c) 2018 by Emmett1 (emmett1.2miligrams@gmail.com) # # 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 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # interrupted() { cleanup echo exit 1 } cleanup() { rm -f $FILE_LIST } usage() { cat << EOF Usage: $(basename $0) [option] [arg] Options: -a, --all print all affected files -r, --rebuild rebuild & reinstall broken package -p, --package check for certain package -f, --no-filter skip filter (exclude) dirs, files and libraries -e, --exclude exclude package when rebuild (use with -r/--rebuild) -y, --yes dont ask user confirmation to rebuild package (use with -r/--rebuild) -h, --help print this help message EOF } confirm() { printf "$1 (Y/n) " read -r response case "$response" in [Nn][Oo]|[Nn]) echo "$2"; exit 2 ;; *) : ;; esac } extract_opts() { while [ "$1" ]; do case $1 in --*) opts="$opts $1";; -*) char=${#1}; count=1 while [ "$count" != "$char" ]; do count=$((count+1)) opts="$opts -$(echo $1 | cut -c $count)" done;; *) opts="$opts $1" esac shift done echo $opts } parse_opt() { while [ $1 ]; do case $1 in -a|--all) PRINTALL=1 ;; -r|--rebuild) REBUILD=1 ;; -y|--yes) NOCONFIRM=1 ;; -f|--no-filter) NO_FILTER=1 ;; -e|--exclude) while [ "$2" ]; do case $2 in -*) break ;; *) [ -z "$expkg" ] && expkg="$2" || expkg="$expkg $2" esac shift done ;; -p|--package) PKG=$2; shift ;; -h|--help) usage; exit 0 ;; *) echo "Invalid option ($1)"; exit 1 ;; esac shift done } rebuild() { for allpkg in $(scratch deplist $ALLPKG | awk '{print $2}'); do for pkg in $ALLPKG; do if [ $pkg = $allpkg ]; then if [ -z "$order" ]; then order="$allpkg" else order="$order $allpkg" fi break fi done done if [ -n "$order" ]; then if [ "$NOCONFIRM" = "" ]; then echo echo "Package will be rebuild & reinstall by this order:" echo " $order" echo confirm "Continue rebuild & reinstall broken packages?" "Operation cancelled." fi for p in $order; do scratch build -f $p || { cleanup; exit 1; } scratch install -r $p || { cleanup; exit 1; } done fi } rev_exclude() { # excluded dirs EXCLUDE_DIRS="$(grep -v ^# /etc/revdep.conf 2>/dev/null | grep /$ | uniq | sed 's/\/*$//g')" EXCLUDE_DIRS="$EXCLUDE_DIRS $(grep -v ^# /etc/revdep.d/*.conf 2>/dev/null | cut -d : -f2 | grep /$ | uniq | sed 's/\/*$//g')" for dd in $EXCLUDE_DIRS; do if [ -d $dd ]; then _DIRS="$_DIRS $dd" ged="$ged -e ^$dd" fi done EXCLUDE_DIRS=$(echo $_DIRS | tr ' ' '\n' | sort | uniq) for d in $EXCLUDE_DIRS; do EXCLUDED_DIRS="$EXCLUDED_DIRS -path $d -prune -o " done # excluded files EXCLUDE_FILES="$(grep -v ^# /etc/revdep.conf 2>/dev/null | grep -v /$ | grep ^/)" EXCLUDE_FILES="$EXCLUDE_FILES $(grep -v ^# /etc/revdep.d/*.conf 2>/dev/null | cut -d : -f2 | grep -v /$ | grep ^/)" for ff in $EXCLUDE_FILES; do if [ -f $ff ]; then _FILES="$_FILES $ff" gef="$gef -e ^$ff$" fi done EXCLUDE_FILES=$(echo $_FILES | tr ' ' '\n' | sort | uniq) for f in $EXCLUDE_FILES; do EXCLUDED_FILES="$EXCLUDED_FILES ! -path $f " done # excluded libraries EXCLUDE_LIBS="$(grep -v ^# /etc/revdep.conf 2>/dev/null | grep -v ^/ | uniq | grep ".*.so.*")" EXCLUDE_LIBS="$EXCLUDE_LIBS $(grep -v ^# /etc/revdep.d/*.conf 2>/dev/null | cut -d : -f2 | grep -v ^/ | uniq | grep ".*.so.*")" EXCLUDED_LIBS=$(echo $EXCLUDE_LIBS | tr ' ' '\n' | sort | uniq) } trap "interrupted" 1 2 3 15 command -v pkgadd >/dev/null 2>&1 || { echo "'pkgadd' not found in \$PATH!" exit 1 } # package database directory PKGDB_DIR="$(pkgadd --print-dbdir)" SEARCH_DIRS="/bin /usr/bin /sbin /usr/sbin /lib /usr/lib /lib64 /usr/lib64 /usr/libexec" parse_opt $(extract_opts "$@") if [ "$(id -u)" != 0 ] && [ "$REBUILD" = 1 ]; then echo "$(basename $0) need to run as root to rebuild & reinstall package" exit 1 fi if [ "$PKG" ] && [ ! -f "$PKGDB_DIR/$PKG/.files" ]; then echo "ERROR: Package '$PKG' not installed" cleanup exit 1 fi # get search extra dirs while read -r line; do if [ "$(echo $line | cut -c 1)" = "/" ]; then EXTRA_SEARCH_DIRS="$EXTRA_SEARCH_DIRS $line " fi done < /etc/ld.so.conf if [ -d /etc/ld.so.conf.d/ ]; then for dir in /etc/ld.so.conf.d/*.conf; do while read -r line; do if [ "$(echo $line | cut -c 1)" = "/" ]; then EXTRA_SEARCH_DIRS="$EXTRA_SEARCH_DIRS $line " fi done < $dir done fi if [ "$NO_FILTER" != 1 ]; then rev_exclude fi # search dirs TARGET_SEARCH_DIRS="$SEARCH_DIRS $EXTRA_SEARCH_DIRS" FILE_LIST="/tmp/.revdep.$$" echo "SEARCH DIRS:" for d in $TARGET_SEARCH_DIRS; do if [ -d $d ]; then SEARCH_DIRS="$SEARCH_DIRS $d" echo " $d" fi done echo echo "EXCLUDED DIRS:" for dd in $EXCLUDE_DIRS; do echo " $dd" done echo echo "EXCLUDED FILES:" for ff in $EXCLUDE_FILES; do echo " $ff" done echo echo "EXCLUDED LIBS:" for ll in $EXCLUDED_LIBS; do echo " $ll" done echo if [ "$PKG" ]; then for D in $TARGET_SEARCH_DIRS; do gx="$gx -e ^$D" done gx="$gx -e *\.so -e *\.so\.*" if [ -n "$gef" ]; then filterfile="grep -v $gef" else filterfile=cat fi if [ -n "$ged" ]; then filterdir="grep -v $ged" else filterdir=cat fi printf "Find '$PKG' files... " sed 's/^/\//' $PKGDB_DIR/$PKG/.files | grep $gx | $filterfile | $filterdir > $FILE_LIST else printf "Find all files... " find $SEARCH_DIRS $EXCLUDED_DIRS $EXCLUDED_FILES -type f \( -perm /+u+x -o -name '*.so' -o -name '*.so.*' \) -print 2> /dev/null | sort -u > $FILE_LIST fi total=$(wc -l $FILE_LIST | awk '{print $1}') count=0 echo "$total files found" echo "Checking for broken linkage..." while read -r line; do count=$(( count + 1 )) libname=$(basename "$line") printf " $(( 100*count/total ))%% $libname\033[0K\r" case "$(file -bi "$line")" in *application/x-sharedlib* | *application/x-executable* | *application/x-pie-executable*) if ldd $line 2>/dev/null | grep -q "not found"; then LIB_NAME=$(ldd $line 2>/dev/null | grep "not found" | sort | uniq | awk '{print $1}') for l in $LIB_NAME; do if ! echo $EXCLUDED_LIBS | grep -qw $l; then NEW_LIB_NAME="$NEW_LIB_NAME $l" fi done LIB_NAME=$NEW_LIB_NAME [ "$LIB_NAME" ] || continue PKG_NAME=$(echo $line | sed 's#^/##') PKG_NAME=$(grep -Rx $PKG_NAME "$PKGDB_DIR"/*/.files | cut -d : -f1) [ "$PKG_NAME" ] || continue PKG_NAME=$(dirname $PKG_NAME) PKG_NAME=$(basename $PKG_NAME) echo $expkg | tr ' ' '\n' | grep -qx $PKG_NAME && continue REQ_LIB=$(objdump -p $line 2>/dev/null | grep NEEDED | awk '{print $2}' | tr '\n' ' ') for i in $LIB_NAME; do [ "$PRINTALL" = 1 ] && echo " $PKG_NAME -> $line (requires $i)" if echo $REQ_LIB | tr ' ' '\n' | grep -qx $i; then [ "$PRINTALL" = 1 ] || echo " $PKG_NAME -> $line (requires $i)" if echo "$ALLPKG" | tr ' ' '\n' | grep -qx "$PKG_NAME"; then continue else ALLPKG="$ALLPKG $PKG_NAME" fi fi done fi ;; esac unset NEW_LIB_NAME done < $FILE_LIST printf "\033[0K" if [ "$ALLPKG" ]; then echo echo "Broken package(s):" for rebuild in $ALLPKG; do echo " $rebuild" done if [ "$REBUILD" = 1 ]; then rebuild fi else echo "All packages is doing fine." fi cleanup exit 0