350 lines
8 KiB
Bash
Executable file
350 lines
8 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
# Pacshrap -- chroot synchronization utility
|
|
#
|
|
# Copyright (C) 2023 Xavier R.M.
|
|
# sapphirus(at)azorium(dot)net
|
|
#
|
|
#
|
|
# 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, with only version 3 of the License.
|
|
#
|
|
# 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 <https://www.gnu.org/licenses/>.
|
|
|
|
INSTANCE_ROOT=$SANDBOX_BASE/fs/root
|
|
INSTANCE_DB_ROOT=$SANDBOX_BASE/etc/db
|
|
INSTANCE_DEPS_ROOT=$SANDBOX_BASE/etc/deps
|
|
INSTANCE_PACMAND=$SANDBOX_BASE/etc/pacman.d
|
|
|
|
BOLD=$(tput bold)
|
|
RED=$(tput setaf 1)
|
|
GREEN=$(tput setaf 2)
|
|
CYAN=$(tput setaf 6)
|
|
YELLOW=$(tput setaf 11)
|
|
RESET=$(tput sgr0)
|
|
BAR="$RED$BOLD::$RESET$BOLD"
|
|
BAR_GREEN="$GREEN$BOLD::$RESET$BOLD"
|
|
ARROW="$CYAN$BOLD->$RESET"
|
|
ARROW_GREEN="$GREEN$BOLD->$RESET"
|
|
ARROW_RED="$RED$BOLD->$RESET"
|
|
|
|
EXEC_NAME="pachrap-sync"
|
|
EXEC_SCRIPT="pachrap-exec"
|
|
PARAMS="--root --exec"
|
|
DEP_PARAMS="--dep --root --exec"
|
|
LINKFILES=("bin" "lib" "lib32" "share")
|
|
|
|
main () {
|
|
parse_args $@
|
|
|
|
if [[ $SWITCH == *v* ]]; then
|
|
VER_DISPLAY=$EXEC_NAME pacshrap -v
|
|
exit
|
|
fi
|
|
|
|
if [[ ! -d $INSTANCE_ROOT ]]; then
|
|
echo $BOLD$RED"error:$RESET Sandbox root is either missing or an environmental variable is misconfigured.$RESET"
|
|
exit
|
|
fi
|
|
|
|
local roots=
|
|
local rootdeps=
|
|
local baserootdeps=
|
|
|
|
init_vars
|
|
|
|
if [[ $SWITCH == *g* ]] && [[ $SWITCH == *b* ]]; then
|
|
generate_pacman_conf ${ARGS[@]}
|
|
return
|
|
fi
|
|
|
|
if [[ $SWITCH != *b* ]]; then
|
|
log $BAR "Update dependencies \n\n$baserootdeps$rootdeps\n"
|
|
|
|
if [[ "$(query_confirm "Proceed with update?")" ]]; then
|
|
generate_pacman_conf $baserootdeps$rootdeps
|
|
update $baserootdeps$rootdeps
|
|
fi
|
|
fi
|
|
|
|
if [[ $SWITCH == *s* ]] || [[ $SYNCREQ == 1 ]]; then
|
|
log $BAR "Synchronization event triggered..."
|
|
generate_cache $baserootdeps
|
|
update_links $rootdeps$roots
|
|
cleanup_cache $rootdeps$baserootdeps
|
|
log $BAR_GREEN "Synchronization complete!"
|
|
fi
|
|
|
|
if [[ $SWITCH != *b* ]]; then
|
|
log $BAR "Update sandbox \n\n$roots\n"
|
|
if [[ "$(query_confirm "Proceed with update?")" ]]; then
|
|
generate_pacman_conf $roots
|
|
update $roots
|
|
fi
|
|
fi
|
|
}
|
|
|
|
parse_args () {
|
|
for var in "$@"; do
|
|
case $var in
|
|
--generate-config|-g)
|
|
SWITCH=g$SWITCH
|
|
;;
|
|
--bypass-update|-b)
|
|
SWITCH=b$SWITCH
|
|
;;
|
|
--no-confirm|-n)
|
|
SWITCH=n$SWITCH
|
|
;;
|
|
--sync|-s)
|
|
SWITCH=s$SWITCH
|
|
;;
|
|
-*)
|
|
SWITCH=$(echo $var | cut -c 2-)
|
|
;;
|
|
*)
|
|
ARGS+=("$var")
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
init_vars() {
|
|
local rootlist=$(ls -U -1F $INSTANCE_ROOT | grep -i "/" | tr -d "/")
|
|
for f in $rootlist; do
|
|
if [[ -f $INSTANCE_ROOT/$f/.root ]]; then
|
|
baserootdeps+="$f "
|
|
elif [[ -f $INSTANCE_ROOT/$f/.dep ]]; then
|
|
rootdeps+="$f "
|
|
else
|
|
roots+="$f "
|
|
fi
|
|
done
|
|
}
|
|
|
|
invoke_link_deletion() {
|
|
local pwd=$PWD
|
|
local file_old=$link/$item.old.zst
|
|
local file_new=$link/$item.zst
|
|
|
|
([[ ! -f $file_new ]] || [[ ! -f $file_old ]]) && return
|
|
|
|
cd $root/usr
|
|
diff --unchanged-group-format= --new-line-format='%L' \
|
|
-biw <(echo "$(zstd -fd < $file_new)") <(echo "$(zstd -fd < $file_old)") | xargs rm -f
|
|
cd $pwd
|
|
}
|
|
|
|
invoke_update_link() {
|
|
local sandbox=$1
|
|
|
|
if [[ ${synced[$sandbox]} ]]; then
|
|
return
|
|
fi
|
|
if [[ -f $INSTANCE_ROOT/$sandbox/.root ]]; then
|
|
return
|
|
fi
|
|
|
|
local linkfiles=${LINKFILES[@]}
|
|
|
|
log $ARROW "Synchronizing links for dependency $BOLD$dep$RESET in $BOLD$sandbox$RESET...$RESET"
|
|
|
|
local dep=$(return_dependency)
|
|
|
|
if [[ $SWITCH != *f* ]]; then
|
|
for item in ${linkfiles[@]}; do
|
|
local root=$INSTANCE_ROOT/$sandbox
|
|
local link=$INSTANCE_DB_ROOT/$dep
|
|
invoke_link_deletion
|
|
done
|
|
fi
|
|
|
|
for item in ${linkfiles[@]}; do
|
|
local root=$INSTANCE_ROOT/$sandbox
|
|
local source=$INSTANCE_ROOT/$dep
|
|
local src=$source/usr/$item
|
|
local dest=$root/usr/
|
|
|
|
cp -flR $src $dest 2>/dev/null
|
|
log " Synchronization complete for $BOLD$item$RESET in $BOLD$sandbox$RESET!"
|
|
done
|
|
|
|
if [[ -f $INSTANCE_ROOT/$sandbox/.dep ]]; then
|
|
invoke_generate_cache
|
|
fi
|
|
synced[$sandbox]=1
|
|
}
|
|
|
|
update_links() {
|
|
declare -A synced
|
|
for sandbox in "$@"; do
|
|
check_root "link synchronization"
|
|
[[ $? == 1 ]] && continue
|
|
for dep in $(return_dependencies); do
|
|
check_root "link synchronization"
|
|
[[ $? == 1 ]] && continue
|
|
invoke_update_link $dep
|
|
done
|
|
invoke_update_link $sandbox
|
|
done
|
|
}
|
|
|
|
cleanup_cache() {
|
|
for sandbox in "$@"; do
|
|
check_root "cache file operation"
|
|
[[ $? == 1 ]] && continue
|
|
|
|
local linkfiles=${LINKFILES[@]}
|
|
|
|
for item in ${linkfiles[@]}; do
|
|
rm $INSTANCE_DB_ROOT/$sandbox/$item.old.zst
|
|
done
|
|
done
|
|
}
|
|
|
|
invoke_generate_cache() {
|
|
local dep=$sandbox
|
|
local linkfiles=${LINKFILES[@]}
|
|
|
|
for item in ${linkfiles[@]}; do
|
|
local root=$INSTANCE_ROOT/$sandbox
|
|
local link=$INSTANCE_DB_ROOT/$sandbox
|
|
local rdir=$root/usr/
|
|
local tdir=$root/usr/$item
|
|
|
|
mv $link/$item.zst $link/$item.old.zst
|
|
find $tdir -type f,l | sed -z "s,$rdir,,g" | zstd -fq -o $link/$item.zst
|
|
done
|
|
log $ARROW_GREEN "Generated link cache for $BOLD$sandbox$RESET!"
|
|
}
|
|
|
|
generate_cache() {
|
|
for sandbox in "$@"; do
|
|
check_root "cache"
|
|
[[ $? == 1 ]] && continue
|
|
|
|
invoke_generate_cache
|
|
done
|
|
}
|
|
|
|
check_root() {
|
|
if [[ ! -d $INSTANCE_ROOT/$sandbox ]]; then
|
|
log $BAR$RESET "Root for $BOLD$sandbox$RESET not found.$RESET\n Skipping $@..."
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
invoke_vdb () {
|
|
local root=$INSTANCE_ROOT/$sandbox
|
|
local dep=$(return_dependency)
|
|
|
|
if [[ -f $root/.dep ]]; then
|
|
dep=$sandbox
|
|
fi
|
|
|
|
local template=$INSTANCE_PACMAND/tpl/pacman.$dep.conf
|
|
local pacmanconf=$INSTANCE_PACMAND/pacman.$sandbox.conf
|
|
local header="##\n## THIS pacman.conf WAS AUTOMATICALLY GENERATED on $(date "+%F %T"). \n## DO NOT EDIT\n##\n"
|
|
local ignorepkg=
|
|
|
|
if [[ ! -f $root/.root ]]; then
|
|
declare -A skip
|
|
local skiplist=
|
|
ignorepkg="## Start of automated configuration ##\n"
|
|
|
|
for depc in $(return_dependencies); do
|
|
local pkgs=$($EXEC_SCRIPT $depc $PARAMS pacman -Qq)
|
|
for pkg in $pkgs; do
|
|
[[ ${skip[$pkg]} ]] && continue
|
|
ignorepkg+="IgnorePkg = $pkg\n"
|
|
done
|
|
skiplist=$pkgs$skiplist
|
|
|
|
for pkg in $skiplist; do
|
|
skip[$pkg]=1
|
|
done
|
|
done
|
|
ignorepkg=$ignorepkg"## End of automated configuration ##"
|
|
fi
|
|
|
|
echo -e $header > $pacmanconf
|
|
cat $template | sed -z "s/###IGNOREPKG###/$ignorepkg/g" >> $pacmanconf
|
|
log "$BAR_GREEN Generated pacman.conf for $sandbox."
|
|
}
|
|
|
|
invoke_update() {
|
|
local params=$PARAMS
|
|
|
|
log $BAR "Checking $sandbox instance for updates..."
|
|
|
|
$EXEC_SCRIPT $sandbox $params pacman -Sy
|
|
local result=$($EXEC_SCRIPT $sandbox $params pacman --color always -Qu | grep -v "ignored")
|
|
|
|
if [[ ! $result ]]; then
|
|
log $BAR_GREEN "Packages are up-to-date for $sandbox!"
|
|
return
|
|
fi
|
|
|
|
echo -e "$BAR Packages to be installed or upgraded: $RESET \n\n$result\n"
|
|
[[ ! $(query_confirm "Confirm update and database synchronization on $sandbox") ]] && return
|
|
SYNCREQ=1
|
|
$EXEC_SCRIPT $sandbox $params pacman -Su --noconfirm 2>/dev/null
|
|
log $BAR "Synchronizing pacman database for foreign packages..."
|
|
$EXEC_SCRIPT $sandbox $params pacman -Su --dbonly --noconfirm --config=/tmp/pacman.conf 2>/dev/null
|
|
log $BAR_GREEN "Synchronization and upgrades are complete!"
|
|
}
|
|
|
|
update () {
|
|
for sandbox in "$@"; do
|
|
check_root "update"
|
|
[[ $? == 1 ]] && continue
|
|
invoke_update
|
|
done
|
|
}
|
|
|
|
generate_pacman_conf () {
|
|
for sandbox in "$@"; do
|
|
check_root "pacman.conf generation"
|
|
[[ $? == 1 ]] && continue
|
|
invoke_vdb
|
|
done
|
|
}
|
|
|
|
return_dependencies() {
|
|
echo $(cat $INSTANCE_DEPS_ROOT/$sandbox 2>/dev/null)
|
|
}
|
|
|
|
return_base() {
|
|
echo $(cat $INSTANCE_DEPS_ROOT/$sandbox 2>/dev/null | head -n 1)
|
|
}
|
|
|
|
return_dependency() {
|
|
echo $(cat $INSTANCE_DEPS_ROOT/$sandbox 2>/dev/null | tail -n 1)
|
|
}
|
|
|
|
log () {
|
|
echo -e "$@ $RESET"
|
|
}
|
|
|
|
query_confirm () {
|
|
if [[ $SWITCH == *n* ]]; then
|
|
echo 1
|
|
return
|
|
fi
|
|
read -rp "$BAR $@ [Y/n]$RESET " input
|
|
if [[ "$input" != "Y" ]] &&
|
|
[[ "$input" != "y" ]] &&
|
|
[[ "$input" != "" ]]; then
|
|
return
|
|
fi
|
|
echo 1
|
|
}
|
|
|
|
main $@
|