#!/bin/bash # # 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 . # RED='\e[0;31m' GREEN='\e[0;32m' YELLOW='\e[0;33m' CYAN='\e[0;36m' CRESET='\e[0m' trap "interrupted" SIGHUP SIGINT SIGQUIT SIGTERM interrupted() { echo ret 1 } nocolor() { RED= GREEN= YELLOW= CYAN= CRESET= } msg() { echo -e "${GREEN}==>${CRESET} $1" } msg2() { echo -e " ${CYAN}*${CRESET} $1" } msgerr() { echo -e "${RED}==> ERROR:${CRESET} $1" } msgwarn() { echo -e "${YELLOW}==> WARNING:${CRESET} $1" } runremovehooks() { if [ "${#runthishook[@]}" -gt 0 ]; then for hook in ${runthishook[@]}; do description=$(cat "$hook" | grep ^"# description" | sed 's/\://' | cut -d ' ' -f 3-) operation=$(cat "$hook" | grep ^"# operation" | sed 's/\://' | cut -d ' ' -f 3-) target=$(cat "$hook" | grep ^"# target" | sed 's/\://' | cut -d ' ' -f 3-) if [ -n "$description" ] && [ -n "$operation" ] && [ -n "$target" ]; then [ "$SILENT_REMOVE" ] || msg2 "$description" . $hook if [ "`type -t exechook`" = "function" ]; then exechook fi fi unset description operation target unset -f exechook done fi } runpreremovehooks() { for hook in $HOOK_DIR/*.hook; do [ -f $hook ] || continue operation=$(cat "$hook" | grep ^"# operation" | sed 's/\://' | cut -d ' ' -f 3-) target=$(cat "$hook" | grep ^"# target" | sed 's/\://' | cut -d ' ' -f 3-) if [ "$(echo $operation | grep -w "remove" )" ]; then if [ "$(grep $target $INDEX_DIR/$1/.files)" ]; then runthishook+=($hook) fi fi unset operation target done } help() { cat << EOF Usage: $(basename $0) [ ] Options: -v, --verbose print removed files -h, --help show this help message -s, --silent print remove message in simple format --no-preremove don't run pre-remove script --no-postremove don't run post-remove script --no-color disable colour for output --no-hook skip executing hook --root= remove package from custom root directory Example: $(basename $0) firefox -dv --no-preremove remove package firefox, skipping dependency check, print deleted files and skipp pre-remove script EOF } extract_opt() { for opt in $@; do case $opt in --*) OPTS+=($opt) ;; -*) for (( i=1; i<${#opt}; i++ )); do OPTS+=(-${opt:$i:1}); done ;; *) OPTS+=($opt) ;; esac done echo ${OPTS[@]} } parse_opts() { if [ -z "$1" ]; then SHOWHELP=yes else while [ "$1" ]; do case $1 in -v | --verbose) VERBOSE_REMOVE=yes ;; -h | --help) SHOWHELP=yes ;; -s | --silent) SILENT_REMOVE=yes ;; --no-preremove) NO_PREREMOVE=yes ;; --no-postremove) NO_POSTREMOVE=yes ;; --no-color) NOCOLOR=yes ;; --no-hook) NOHOOK=yes ;; --root=*) ROOT="${1#*=}" ;; -*) msg "Invalid option: ($1)"; exit 1 ;; *) RMNAME=$1 ;; esac shift done fi } ret() { # remove lock file on exit rm -f $LOCK_FILE exit $1 } isinstalled() { if [ -s $INDEX_DIR/$1/.pkginfo ] && [[ $(grep $1 $INDEX_DIR/$1/.pkginfo) ]]; then return 0 else return 1 fi } parse_opts $(extract_opt $@) INDEX_DIR="$ROOT/var/lib/scratchpkg/index" PKGDEL_DIR="$ROOT/var/lib/scratchpkg" LOCK_FILE="$PKGDEL_DIR/spkg.lock" HOOK_DIR="/etc/hooks" # show help page if [ "$SHOWHELP" ] || [ -z "$RMNAME" ]; then help ret 0 fi # disable color for output if [ "$NOCOLOR" ]; then nocolor fi # check for lock file if [ -f $LOCK_FILE ]; then msgerr "Cant install/remove package simultaneously." msgerr "remove '$LOCK_FILE' if no install/remove package process running." exit 1 else touch $LOCK_FILE if [ "$?" != 0 ]; then msgerr "Cant create lock file in '$LOCK_FILE'" exit 1 fi fi if ! isinstalled $RMNAME; then msgerr "Package '$RMNAME' not installed." ret 1 fi # check for root access if [ "$UID" != "0" ]; then echo "Installing package need root access!" ret 1 fi name=$(cat $INDEX_DIR/$RMNAME/.pkginfo | grep ^name | cut -d " " -f3-) version=$(cat $INDEX_DIR/$RMNAME/.pkginfo | grep ^version | cut -d " " -f3-) release=$(cat $INDEX_DIR/$RMNAME/.pkginfo | grep ^release | cut -d " " -f3-) if [ -z $name ] && [ -z $version ] && [ -z $release ]; then msgerr "Package '$RMNAME' not installed but exist in database." ret 1 fi if [ "$SILENT_REMOVE" ]; then echo -ne "Removing $name-$version-$release " else msg "Removing '$name-$version-$release'..." fi # source .install script if [ "$ROOT" = "" ]; then if [ -f $INDEX_DIR/$name/.pkginstall ]; then source $INDEX_DIR/$name/.pkginstall fi fi if [ ! "$NO_PREREMOVE" ]; then if [ "`type -t pre_remove`" = "function" ]; then [ "$SILENT_REMOVE" ] || msg2 "Running preremove script..." pre_remove "$version" &>/dev/null fi fi if [ "$ROOT" = "" ] && [ "$NOHOOK" != "yes" ]; then runpreremovehooks $name fi if [ -f "$INDEX_DIR/$name/.bkpfiles" ]; then [ "$SILENT_REMOVE" ] || msg2 "Removing backup files..." while IFS=' ' read -r line; do [ -e "$ROOT/$line" ] && rm "$ROOT/$line" done < <(tac $INDEX_DIR/$name/.bkpfiles) fi [ "$SILENT_REMOVE" ] || msg2 "Removing files & dirs..." while IFS=' ' read -r line; do rm "$ROOT/$line" &>/dev/null done < <(tac $INDEX_DIR/$name/.files | grep -v '/$') while IFS=' ' read -r line; do if [ ! "$(grep -R --exclude-dir="$name" -w "$line" "$INDEX_DIR")" ]; then rmdir "$ROOT/$line" &>/dev/null fi done < <(tac $INDEX_DIR/$name/.files | grep '/$') if [ ! "$NO_POSTREMOVE" ]; then if [ "`type -t post_remove`" = "function" ]; then [ "$SILENT_REMOVE" ] || msg2 "Running postremove script..." post_remove "$version" &>/dev/null fi fi rm -rf $INDEX_DIR/$name if [ "$ROOT" = "" ] && [ "$NOHOOK" != "yes" ]; then runremovehooks fi if [ "$SILENT_REMOVE" ]; then echo "[ done ]" else msg "Package '$name-$version-$release' removed." fi # running ldconfig if [ "$ROOT" = "" ] && [ -x /sbin/ldconfig ]; then /sbin/ldconfig fi ret 0