pacwrap v0.9.8

- Heavy refactor with code cleanup
- Completed common logging infrastructure
- Distribution agnosticism attained!
- pacwrap-common: Initial commit
- pacwrap-common: Dependency check
- pacwrap-utils: Replication functionality
- pacwrap-sync: Increased resolution of linker progress
- pacwrap-sync: Container configuration with explicit package array
- pacwrap-sync: Improved UX, catching erronous exit codes from pacman
- pacwrap-create: Refactor and cleanup
- pacwrap-create: Replaced pacstrap with tarball installer
This commit is contained in:
Xavier Moffett 2023-04-23 06:41:49 -04:00
parent 72e88098c3
commit a16f74191d
5 changed files with 847 additions and 568 deletions

View file

@ -18,33 +18,27 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
BOLD=$(tput bold)
RED=$(tput setaf 1)
RESET=$(tput sgr0)
source pacwrap-common
VER="v0.9.7 R4 ${BOLD}BETA$RESET"
EXEC_NAME="pacwrap"
UTILS_SCRIPT="pacwrap-utils"
main () {
distro_check
parse_args "$@"
init_structures
distro_warn
case $SWITCH in
C)
pacwrap-create ${ARGS[@]}
$CREATE_SCRIPT ${ARGS[@]}
;;
E)
pacwrap-exec ${ARGS[@]}
$EXEC_SCRIPT ${ARGS[@]}
;;
S)
pacwrap-sync ${ARGS[@]}
$SYNC_SCRIPT ${ARGS[@]}
;;
U)
pacwrap-utils ${ARGS[@]}
$UTILS_SCRIPT ${ARGS[@]}
;;
h)
help_operation $HELP
@ -63,21 +57,6 @@ main () {
esac
}
init_structures() {
INSTANCE_DATA_DIR="$HOME/.local/share/pacwrap"
INSTANCE_CACHE_DIR="$HOME/.cache/pacwrap/pkg"
INSTANCE_CONFIG_DIR="$HOME/.config/pacwrap"
if [[ $PACWRAP_DEBUG ]]; then
INSTANCE_DATA_DIR="$PACWRAP_DEBUG"
INSTANCE_CONFIG_DIR="$PACWRAP_DEBUG/cfg"
fi
if [[ ! -d $INSTANCE_DATA_DIR ]] || [[ ! -d $INSTANCE_DATA_DIR ]] || [[ ! -d $INSTANCE_DATA_DIR ]]; then
$UTILS_SCRIPT -Ui
fi
}
parse_args () {
ARGS=()
for var in "$@"; do
@ -115,30 +94,6 @@ parse_args () {
done
}
distro_check() {
source /etc/os-release
if [[ ! -f /usr/bin/pacman ]]; then
version
cat << _WARN
$BOLD${RED}error:$RESET An attempt was made to execute pachwrap on an unsupported distribution.
pachwrap is currently only supported on distributions utilising Arch Linux's pacman package manager.
Support for other package managers with a similar, requisite subset of features may be possible in future.
_WARN
exit 1
fi
if [[ ! $ID == "arch" ]]; then cat << _WARN
$BOLD${RED}WARNING:$RESET You are running pachwrap on an unsupported derviative of Arch Linux.
Support is not guaranteed. Use at your ${BOLD}OWN$RESET risk.
_WARN
fi
}
version () {
if [[ ! $VER_DISPLAY ]]; then
@ -283,4 +238,14 @@ _USAGE
esac
}
function distro_warn() {
source /etc/os-release
[[ ! $ID == "arch" ]] && cat << _WARN
$BOLD${RED}WARNING:$RESET You are running pacwrap on an unsupported distribution of Linux or Unix.
Support is not guaranteed. Use at your ${BOLD}OWN$RESET risk.
_WARN
}
main $@

343
bin/pacwrap-common Executable file
View file

@ -0,0 +1,343 @@
#!/bin/bash
#
# PacWrap -- common script
#
# 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/>.
RUNTIME_ARGS="$0 $@"
BOLD=$(tput bold)
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
CYAN=$(tput setaf 6)
YELLOW=$(tput setaf 11)
RESET=$(tput sgr0)
UNDERLINE=$(tput smul)
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"
VER="v0.9.8 ${BOLD}BETA$RESET"
EXEC_SCRIPT="pacwrap-exec"
CREATE_SCRIPT="pacwrap-create"
UTILS_SCRIPT="pacwrap-utils"
SYNC_SCRIPT="pacwrap-sync"
LOG_ERR_HELP=1
LOG_ERR_WARN=2
LOG_ERR_ERROR=3
declare -A INSTANCE_CONFIG
[[ ! $PACWRAP_DATA_DIR ]] && PACWRAP_DATA_DIR="$HOME/.local/share/pacwrap"
[[ ! $PACWRAP_CACHE_DIR ]] && PACWRAP_CACHE_DIR="$HOME/.cache/pacwrap"
[[ ! $PACWRAP_CONFIG_DIR ]] && PACWRAP_CONFIG_DIR="$HOME/.config/pacwrap"
VERBOSE="/dev/null"
LOCK_FILE="$PACWRAP_DATA_DIR/pacwrap.lck"
LOG_FILE="$PACWRAP_DATA_DIR/pacwrap.log"
OUTPUT_DEST=$LOG_FILE
INSTANCE_ROOT_DIR=$PACWRAP_DATA_DIR/root
INSTANCE_HOME_DIR=$PACWRAP_DATA_DIR/home
INSTANCE_CONFIG_DIR=$PACWRAP_CONFIG_DIR/root
INSTANCE_DB_DIR=$PACWRAP_DATA_DIR/database
INSTANCE_PACMAN_DIR="$PACWRAP_DATA_DIR/pacman"
INSTANCE_PACMAN_GNUPG="$INSTANCE_PACMAN_DIR/gnupg"
INSTANCE_PACMAN_SYNC="$INSTANCE_PACMAN_DIR/sync"
INSTANCE_PACMAN_MIRRORLIST="$PACWRAP_CONFIG_DIR/pacman.d/mirrorlist"
INSTANCE_PACMAN_CACHE="$PACWRAP_CACHE_DIR/pkg"
INSTANCE_PACMAN_CFG_DIR="$PACWRAP_CONFIG_DIR/pacman"
init() {
runtime_check
[[ $SWITCH == *n* ]] && SWITCH_NOCONFIRM=1
[[ $SWITCH == *v* ]] && VERBOSE="/dev/stdout"
[[ ! -f $LOG_FILE ]] && touch $LOG_FILE
[[ ! $1 ]] && [[ -f $LOCK_FILE ]] &&
log_error "pacwrap is locked: $LOCK_FILE$RESET" 2
local list="ls -U -1F $INSTANCE_ROOT_DIR"
local rootlist=$($list 2>/dev/null | grep -i "/" | tr -d "/")
for instance in $rootlist; do
source_configuration
populate_configuration
populate_container_array
done
if [[ $SWITCH == v ]]; then
VER_DISPLAY=$EXEC_NAME pacwrap -v
exit
fi
script_init
perform_datadir_check
}
populate_container_array() {
case $TYPE in
BASE)
baserootdeps+=($instance)
;;
DEP)
rootdeps+=($instance)
;;
*)
roots+=($instance)
;;
esac
}
source_configuration() {
source $INSTANCE_CONFIG_DIR/$instance
}
populate_configuration() {
INSTANCE_CONFIG[$instance,0]=$TYPE
INSTANCE_CONFIG[$instance,1]="deps=(${DEPS[@]})"
INSTANCE_CONFIG[$instance,2]=$SYNC_PACMANDB
INSTANCE_CONFIG[$instance,3]="pkgs=(${PKG[@]})"
}
return_sync_pacmandb() {
echo ${INSTANCE_CONFIG[$instance,2]}
}
return_base() {
[[ $(return_type) == "BASE" ]] && echo $instance && return
eval ${INSTANCE_CONFIG[$instance,1]}
echo ${deps[0]}
}
return_dependencies() {
eval ${INSTANCE_CONFIG[$instance,1]}
echo ${deps[@]}
}
return_packages() {
eval ${INSTANCE_CONFIG[$instance,3]}
echo ${pkgs[@]}
}
return_type() {
echo ${INSTANCE_CONFIG[$instance,0]}
}
return_dependency() {
eval ${INSTANCE_CONFIG[$instance,1]}
echo ${deps[$((${#deps[@]} - 1))]}
}
return_pacman_template() {
echo "$INSTANCE_PACMAN_CFG_DIR/template/pacman.$1$INSTANCE.conf"
}
return_pacman_sync() {
echo "$INSTANCE_PACMAN_CFG_DIR/sync/pacman.$1$INSTANCE.conf"
}
return_pacman_syncdb() {
echo "$INSTANCE_PACMAN_CFG_DIR/syncdb/pacman.$1$INSTANCE.conf"
}
log () {
printf "%s$RESET\n" "$1"
[[ $2 ]] && case $2 in
1)
log_to_file "$1"
;;
*)
log_to_file "$2"
;;
esac
}
log_error() {
case $1 in
$LOG_ERR_HELP)
printf "%s %s\n%s\n" "$EXEC_NAME error:" "$2" \
"Try 'pacwrap -h' for more information on valid operational parameters."
exit
;;
$ARROW_RED)
printf "$ARROW_RED %s\n %s\n" "$2" "$3"
log_to_file "$2"
[[ $4 ]] && exit $4
;;
$LOG_ERR_WARN)
printf "$BOLD$YELLOW%s$RESET %s\n" "warning:" "$2"
[[ $3 ]] && exit $3
;;
*)
printf "$BOLD$RED%s$RESET %s\n" "error:" "$1"
[[ $2 ]] && exit $2
;;
esac
}
log_to_file() {
printf "[%s] [%s] %s\n" $(date '+%FT%H:%M:%S%z') $EXEC_NAME "$1" >>$LOG_FILE
}
print_progress_bar() {
printf "\r$PROGRESS_LABEL$RESET"
printf "%-*s" $(($2+1)) '[' | tr ' ' '#'
printf "%*s% 3d%%\r" $(($1-$2)) "]" "$3"
}
init_progress() {
TTY_SIZE=($(stty size))
PROGRESS_LENGTH=$((${TTY_SIZE[1]}/2))
}
set_progress_label() {
[[ $PROGRESS_OFF ]] && printf "\r%s" "$2 $1" && return
PROGRESS_LABEL="$1"
local label_length=${#PROGRESS_LABEL}
local padding_length=$(($PROGRESS_LENGTH-$label_length-8))
[[ $2 ]] && PROGRESS_LABEL="$2 $1" && padding_length=$(($padding_length-3))
for ((i=0; i<=padding_length; i+=1)); do
PROGRESS_LABEL+=" "
done
progress_bar
}
progress_bar() {
[[ $PROGRESS_OFF ]] && return
local bar_percent=$(($((PROGRESS_LENGTH*2))*$amt_done/$amt % 2 + $PROGRESS_LENGTH*$amt_done/$amt))
local percent=$((200*$amt_done/$amt % 2 + 100*$amt_done/$amt))
print_progress_bar $((PROGRESS_LENGTH+1)) $bar_percent $percent
}
query_confirm_yN () {
if [[ $SWITCH == *n* ]]; then
echo 1
return
fi
read -rp "$BAR $@ [y/N]$RESET " input
if [[ "$input" != "y" ]] &&
[[ "$input" != "Y" ]] &&
([[ "$input" == "" ]] ||
[[ "$input" != "" ]]); then
return
fi
echo 1
}
query_confirm_Yn () {
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
}
perform_datadir_check() {
([[ ! -d $PACWRAP_DATA_DIR ]] ||
[[ ! -d $PACWRAP_CACHE_DIR ]] ||
[[ ! -d $PACWRAP_CONFIG_DIR ]]) &&
initialize_data_directory
([[ ! -d $PACWRAP_DATA_DIR ]] ||
[[ ! -d $PACWRAP_CACHE_DIR ]] ||
[[ ! -d $PACWRAP_CONFIG_DIR ]]) &&
printf "$BOLD$RED%s$RESET %s\n%s" "error:" "Data directories not found." && exit 2
}
initialize_data_directory() {
mkdir -p $PACWRAP_DATA_DIR/root \
$PACWRAP_DATA_DIR/home \
$PACWRAP_DATA_DIR/database \
$PACWRAP_DATA_DIR/pacman/sync \
$PACWRAP_DATA_DIR/pacman/gnupg \
$PACWRAP_CACHE_DIR/pkg \
$PACWRAP_CONFIG_DIR/root \
$PACWRAP_CONFIG_DIR/pacman.d \
$PACWRAP_CONFIG_DIR/pacman/sync \
$PACWRAP_CONFIG_DIR/pacman/syncdb \
$PACWRAP_CONFIG_DIR/pacman/template \
$PACWRAP_CONFIG_DIR/bwrap
local ins_string="\[options\]"
local template="\n\n###IGNOREPKG###\n"
([[ ! -f $INSTANCE_PACMAN_MIRRORLIST ]] || [[ $SWITCH == *m* ]]) && cp /etc/pacman.d/mirrorlist $INSTANCE_PACMAN_MIRRORLIST
[[ ! -f $INSTANCE_PACMAN_CFG_DIR/pacman.conf ]] && echo -e $(pacman_conf) > $INSTANCE_PACMAN_CFG_DIR/pacman.conf
cat $INSTANCE_PACMAN_CFG_DIR/pacman.conf | sed -z "s,$ins_string,$ins_string$template,g" > $INSTANCE_PACMAN_CFG_DIR/pacman.template.conf
}
pacman_conf() {
echo $(cat << _CONFIG
[options]\nHoldPkg = pacman glibc\nArchitecture = auto\nNoExtract = pacman-mirrorlist\n\nColor\nCheckSpace
\n#ParallelDownloads = 5\n\nSigLevel = Required DatabaseOptional\nLocalFileSigLevel = Optional\n\n#[testing]
\n#Include = /etc/pacman.d/mirrorlist\n\n[core]\nInclude = /etc/pacman.d/mirrorlist\n\n[extra]
\nInclude = /etc/pacman.d/mirrorlist\n\n#[community-testing]\n#Include = /etc/pacman.d/mirrorlist
\n\n[community]\nInclude = /etc/pacman.d/mirrorlist\n\n#[multilib-testing]\n#Include = /etc/pacman.d/mirrorlist
\n\n#[multilib]\n#Include = /etc/pacman.d/mirrorlist
_CONFIG
)
}
generate_config() {
local config=$(cat << _CONFIG
##\n## Configuration for $instance container\n## generated on $(date "+%F %T") by $EXEC_NAME.
\n##\n\n# Container type (DO NOT EDIT)\nTYPE=($(return_type))\n\n# Container dependency array (DO NOT EDIT)
\nDEPS=($(return_dependencies))\n\n# Container explicit install array\nPKG=($@)\n\n# Forcibly apply pacman -Sy to container
\n# Not applicable to ROOT containers\nSYNC_PACMANDB=$(return_sync_pacmandb)
_CONFIG
)
echo -e $config > $INSTANCE_CONFIG_DIR/$instance
}
runtime_check() {
if [[ ! $(type -P bwrap) ]] || [[ ! $(type -P bwrap) ]] || [[ ! $(type -P pacman-key gpg) ]]; then
cat << _WARN
$BOLD${RED}error:$RESET Requisite dependencies are missing.
Please make sure that the following binaries are present in \$PATH:
${BOLD}bwrap$RESET, ${BOLD}zstd$RESET, ${BOLD}pacman-key$RESET or ${BOLD}gpg$RESET.
_WARN
exit 2
fi
}

View file

@ -18,33 +18,22 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
BOLD=$(tput bold)
RED=$(tput setaf 1)
RESET=$(tput sgr0)
source pacwrap-common
EXEC_NAME="pacwrap-exec"
main () {
export PACWRAP_EXEC=1
parse_args "$@"
init 1
if [[ $SWITCH == v ]]; then
VER_DISPLAY=$EXEC_NAME pachwrap -v
exit
fi
[[ ! $INSTANCE ]] &&
log_error $LOG_ERR_HELP "Target not specified." 1
if [[ $SANDBOX == "" ]]; then
echo "$EXEC_NAME $BOLD${RED}ERROR$RESET: Target not specified."
echo "Try 'pacwrap -h' for more information on valid operational parameters."
exit 1
fi
init_vars
if [[ ! -d $INSTANCE_ROOT ]] || [[ ! -d $INSTANCE_HOME ]]; then
echo "$EXEC_NAME $BOLD${RED}ERROR$RESET: Sandbox $SANDBOX not found."
echo "Try 'pacwrap -h' for more information on valid operational parameters."
exit 1
fi
[[ ! -d $INSTANCE_ROOT ]] || [[ ! -d $INSTANCE_HOME ]] &&
log_error $LOG_ERR_HELP "Container $INSTANCE not found." 1
case $SWITCH in
*rc*|*cr*)
@ -62,24 +51,12 @@ main () {
esac
}
init_vars () {
INSTANCE_DATA_DIR="$HOME/.local/share/pacwrap"
INSTANCE_CACHE_DIR="$HOME/.cache/pacwrap/pkg"
INSTANCE_CONFIG_DIR="$HOME/.config/pacwrap"
if [[ $PACWRAP_DEBUG ]]; then
INSTANCE_DATA_DIR="$PACWRAP_DEBUG"
INSTANCE_CONFIG_DIR="$PACWRAP_DEBUG/cfg"
fi
LOG_FILE="$INSTANCE_DATA_DIR/pacwrap.log"
INSTANCE_ROOT_DIR=$INSTANCE_DATA_DIR/root
INSTANCE_HOME_DIR=$INSTANCE_DATA_DIR/home
INSTANCE_ROOT=$INSTANCE_ROOT_DIR/$SANDBOX
INSTANCE_HOME=$INSTANCE_HOME_DIR/$SANDBOX
script_init () {
INSTANCE_ROOT=$INSTANCE_ROOT_DIR/$INSTANCE
INSTANCE_HOME=$INSTANCE_HOME_DIR/$INSTANCE
INSTANCE_USER=user
INSTANCE_HOME_MOUNT=/home/$INSTANCE_USER
INSTANCE_SCRIPT="$INSTANCE_CONFIG_DIR/bwrap/$SANDBOX.sh"
INSTANCE_SCRIPT="$PACWRAP_CONFIG_DIR/bwrap/$INSTANCE.sh"
}
@ -106,7 +83,7 @@ parse_args () {
;;
*)
if [[ ! $sbdefined ]]; then
SANDBOX=$var
INSTANCE=$var
sbdefined=1
continue
fi
@ -134,16 +111,16 @@ execute_fakeroot() {
--ro-bind /usr/bin/fakechroot /usr/bin/fakechroot \
--ro-bind /usr/bin/faked /usr/bin/faked \
--ro-bind /etc/resolv.conf /etc/resolv.conf \
--ro-bind $INSTANCE_CONFIG_DIR/pacman/sync/pacman.$SANDBOX.conf /etc/pacman.conf \
--ro-bind /etc/localtime /etc/localtime \
--bind $INSTANCE_DATA_DIR/pacman/sync /var/lib/pacman/sync \
--bind $INSTANCE_DATA_DIR/pacman/gnupg /etc/pacman.d/gnupg \
--bind $INSTANCE_CACHE_DIR /var/cache/pacman/pkg \
--bind $INSTANCE_PACMAN_SYNC /var/lib/pacman/sync \
--bind $INSTANCE_PACMAN_GNUPG /etc/pacman.d/gnupg \
--bind $INSTANCE_PACMAN_CACHE /var/cache/pacman/pkg \
--dev /dev \
--proc /proc \
--tmpfs /tmp \
--ro-bind $INSTANCE_CONFIG_DIR/pacman/syncdb/pacman.$SANDBOX.conf /tmp/pacman.conf \
--ro-bind $INSTANCE_CONFIG_DIR/pacman.d/mirrorlist /etc/pacman.d/mirrorlist \
--ro-bind $(return_pacman_syncdb) /tmp/pacman.conf \
--ro-bind $(return_pacman_sync) /etc/pacman.conf \
--ro-bind $INSTANCE_PACMAN_MIRRORLIST /etc/pacman.d/mirrorlist \
--bind $LOG_FILE /tmp/pacman.log \
--bind $INSTANCE_HOME $INSTANCE_HOME_MOUNT \
--unshare-all \

View file

@ -18,17 +18,11 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
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"
source pacwrap-common
declare -A updated
declare -A synced
declare -A syncreq
EXEC_NAME="pacwrap-sync"
EXEC_SCRIPT="pacwrap-exec --root --exec"
@ -36,50 +30,58 @@ EXEC_SCRIPT="pacwrap-exec --root --exec"
LINKFILES=("bin" "lib" "lib32" "share")
main () {
parse_args $@
if [[ $SWITCH == v ]]; then
VER_DISPLAY=$EXEC_NAME pacwrap -v
exit
fi
export PACWRAP_UTILS=1
trap abort INT
local roots=()
local rootdeps=()
local baserootdeps=()
local syncroots=()
local syncreq=
init_vars
parse_args $@
init $PACWRAP_CREATE
[[ ! $SWITCH ]] && log_error "No switch provided." 1
log_to_file "Running '$RUNTIME_ARGS'"
if [[ $SWITCH == *g* ]]; then
generate_pacman_conf ${ARGS[@]}
return
log "$ARROW Configuring container..."
local instance=${ARGS[0]}
check_root "pacman.conf generation"
sync_pacman_conf
exit
fi
[[ $SWITCH == *yyyy* ]] && SYNCREQ=1
if [[ $SWITCH == *u* ]] || ([[ $SWITCH == *y* ]] && [[ ! $SYNCREQ ]]); then
log $BAR "Update container \n\n${baserootdeps[@]} ${rootdeps[@]} ${roots[@]}\n"
[[ ! "$(query_confirm "Proceed with update?")" ]] && return
generate_pacman_conf ${baserootdeps[@]} ${rootdeps[@]}
if [[ $SYNC_UPGRADE ]] || ([[ $SYNC_UPDATE ]] && [[ ! $SYNC_FORCE ]]); then
log "$BAR_GREEN Starting container upgrade..."
[[ ! $SWITCH_NOCONFIRM ]] && echo -e $BAR "Update container \n\n${baserootdeps[@]} ${rootdeps[@]} ${roots[@]}\n"
[[ ! "$(query_confirm_Yn "Proceed with update?")" ]] && return
configure_containers ${baserootdeps[@]} ${rootdeps[@]}
update ${baserootdeps[@]} ${rootdeps[@]}
fi
if [[ $SWITCH == *yyy* ]] || [[ $SYNCREQ == 1 ]]; then
[[ $PACWRAP_CREATE ]] && syncroots=${roots[@]}
log $BAR_GREEN "Synchronizing container structures..."
generate_cache ${baserootdeps[@]}
update_links ${rootdeps[@]} ${syncroots[@]}
cleanup_cache ${rootdeps[@]} ${baserootdeps[@]}
[[ $SWITCH == *yyyy* ]] && return
fi
[[ ${#roots[@]} > 0 ]] && [[ ! $SYNC_FORCE ]] &&
log "$BAR_GREEN Starting contingent container upgrade..."
if [[ $SWITCH == *u* ]] || [[ $SWITCH == *y* ]]; then
generate_pacman_conf ${roots[@]}
for b in ${baserootdeps[@]}; do
[[ ${syncreq[$(return_base)]} ]] && syncreq=1 && break; done
if [[ $SYNC_OVERRIDE ]] || [[ $syncreq ]]; then
update_links ${syncroots[@]}
cleanup_cache ${rootdeps[@]} ${baserootdeps[@]}
[[ $SYNC_FORCE ]] && return
fi
[[ ${#roots[@]} -le 0 ]] && return
if [[ $SYNC_UPGRADE ]] || [[ $SYNC_UPDATE ]]; then
configure_containers ${roots[@]}
update ${roots[@]}
if [[ -f /usr/bin/paccache ]]; then
paccache --cachedir $INSTANCE_CACHE_DIR -r -k 1 --min-mtime "14 days ago"
fi
[[ -f /usr/bin/paccache ]] &&
paccache --cachedir $INSTANCE_PACMAN_CACHE -r -k 1 --min-mtime "14 days ago"
fi
}
@ -98,8 +100,8 @@ parse_args () {
--sync|-y)
SWITCH=y$SWITCH
;;
-*)
SWITCH=$(echo $var | cut -c 2-)
-S*)
SWITCH=$(echo $var | cut -c 3-)
;;
*)
ARGS+=("$var")
@ -108,60 +110,39 @@ parse_args () {
done
}
init_vars() {
INSTANCE_DATA_DIR="$HOME/.local/share/pacwrap"
INSTANCE_CACHE_DIR="$HOME/.cache/pacwrap/pkg"
INSTANCE_CONFIG_DIR="$HOME/.config/pacwrap"
script_init () {
[[ $SWITCH == *y* ]] && SYNC_UPDATE=1
[[ $SWITCH == *u* ]] && SYNC_UPGRADE=1
[[ $SWITCH == *yy* ]] && SYNC_FOREIGN=1
[[ $SWITCH == *yyy* ]] && SYNC_OVERRIDE=1
[[ $SWITCH == *yyyy* ]] && SYNC_FORCE=1
if [[ $PACWRAP_DEBUG ]]; then
INSTANCE_DATA_DIR="$PACWRAP_DEBUG"
INSTANCE_CONFIG_DIR="$PACWRAP_DEBUG/cfg"
fi
VERBOSE="/dev/null"
[[ $SWITCH == *v* ]] && VERBOSE="/dev/stdout"
LOG_FILE="$INSTANCE_DATA_DIR/pacwrap.log"
OUTPUT_DEST=$LOG_FILE
INSTANCE_ROOT=$INSTANCE_DATA_DIR/root
INSTANCE_DB_ROOT=$INSTANCE_DATA_DIR/database
INSTANCE_CONFIG_ROOT=$INSTANCE_CONFIG_DIR/root
INSTANCE_PACMAN_SYNC=$INSTANCE_CONFIG_DIR/pacman/sync
INSTANCE_PACMAN_SYNCDB=$INSTANCE_CONFIG_DIR/pacman/syncdb
INSTANCE_PACMAN_TEMPLATE=$INSTANCE_CONFIG_DIR/pacman/template
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 rootlist=$(ls -U -1F $INSTANCE_ROOT | grep -i "/" | tr -d "/")
for sandbox in $rootlist; do
local type=$(return_type)
case $type in
BASE)
baserootdeps+=("$sandbox")
;;
DEP)
rootdeps+=("$sandbox")
;;
*)
roots+=("$sandbox")
;;
esac
done
syncroots=${roots[@]}
syncroots=(${roots[@]})
if [[ ${ARGS[@]} ]]; then
roots=()
for sandbox in $ARGS; do
for instance in $ARGS; do
check_root "container"
[[ $? == 1 ]] && continue
roots+=("$sandbox")
[[ $(return_type) != "ROOT" ]] &&
[[ ! $PACWRAP_CREATE ]] && continue
roots+=("$instance")
done
fi
[[ $SYNC_FORCE ]] && syncroots+=(${rootdeps[@]})
if [[ $PACWRAP_CREATE ]]; then
local instance=${roots[0]}
syncroots=($instance)
for instance in $(return_dependencies); do
[[ $(return_type) == "BASE" ]] && continue
syncroots+=($instance)
done
fi
touch $LOCK_FILE
trap on_exit EXIT
}
invoke_link_deletion() {
@ -179,133 +160,161 @@ invoke_link_deletion() {
}
invoke_update_link() {
local sandbox=$1
if [[ ${synced[$sandbox]} ]]; then
return
fi
if [[ $(return_type) == "BASE" ]]; then
return
fi
local instance=$1
local linkfiles=${LINKFILES[@]}
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
# log_to_file "Deleted stray files from $dep/$item in $sandbox."
done
[[ ${synced[$instance]} ]] && return
if [[ $(return_type) == "BASE" ]]; then
invoke_generate_cache
synced[$instance]=1
return
fi
local dep=$(return_dependency)
for item in ${linkfiles[@]}; do
local root=$INSTANCE_ROOT/$sandbox
local source=$INSTANCE_ROOT/$dep
local root=$INSTANCE_ROOT_DIR/$instance
local link=$INSTANCE_DB_DIR/$dep
invoke_link_deletion
#log_to_file "Deleted stray files from $dep/$item in $instance."
if [[ $amt ]]; then
((amt_done++))
progress_bar
fi
done
for item in ${linkfiles[@]}; do
local root=$INSTANCE_ROOT_DIR/$instance
local source=$INSTANCE_ROOT_DIR/$dep
local src=$source/usr/$item
local dest=$root/usr/
([[ ! -d $src ]] || [[ ! -d $dest ]]) && continue
([[ ! -d $src ]] || [[ ! -d $dest ]]) && ((amt_done++)) && continue
cp -flR $src $dest 2>>$OUTPUT_DEST 1>>$OUTPUT_DEST
log_to_file "Linked $sandbox/$item against $dep/$item."
log_to_file "Linked $instance/$item against $dep/$item."
if [[ $amt ]]; then
((amt_done++))
progress_bar
fi
done
if [[ $(return_type) == "DEP" ]]; then
invoke_generate_cache
fi
synced[$sandbox]=1
[[ $(return_type) == "DEP" ]] && invoke_generate_cache
[[ $ABORT ]] && exit 1
synced[$instance]=1
}
update_links() {
declare -A synced
local amt=${#@}
local amt_done=0
local amt=$((${#@} * 8))
local check_root="link synchronization"
SYNC=1
init_progress
set_progress_label " Linking structures"
set_progress_label "Linking structures" $ARROW
for sandbox in "$@"; do
((amt_done++))
progress_bar
check_root "link synchronization"
for instance in "$@"; do check_root $check_root
[[ $? == 1 ]] && continue
for dep in $(return_dependencies); do
check_root "link synchronization"
[[ $? == 1 ]] && continue
invoke_update_link $dep
local syncreq=$SYNC_OVERRIDE
for instance_dep in $(return_dependencies); do check_root $check_root
[[ $? == 1 ]] && break
[[ $syncreq ]] && invoke_update_link $instance_dep
[[ ${synced[$instance_dep]} ]] && syncreq=1
done
invoke_update_link $sandbox
done; echo
if [[ $syncreq ]]; then
invoke_update_link $instance
else
((amt_done+=8))
progress_bar
fi
done; SYNC= && echo
}
cleanup_cache() {
for sandbox in "$@"; do
for instance in "$@"; do
check_root "cache file operation"
[[ $? == 1 ]] && continue
local linkfiles=${LINKFILES[@]}
for item in ${linkfiles[@]}; do
[[ -f $INSTANCE_DB_ROOT/$sandbox/$item.old.zst ]] &&
rm $INSTANCE_DB_ROOT/$sandbox/$item.old.zst 2>>$OUTPUT_DEST 1>>$OUTPUT_DEST
[[ -f $INSTANCE_DB_DIR/$instance/$item.old.zst ]] &&
rm $INSTANCE_DB_DIR/$instance/$item.old.zst 2>>$OUTPUT_DEST 1>>$OUTPUT_DEST
done
done
}
invoke_generate_cache() {
local dep=$sandbox
local dep=$instance
local linkfiles=${LINKFILES[@]}
for item in ${linkfiles[@]}; do
local root=$INSTANCE_ROOT/$sandbox
local link=$INSTANCE_DB_ROOT/$sandbox
local root=$INSTANCE_ROOT_DIR/$instance
local link=$INSTANCE_DB_DIR/$instance
local rdir=$root/usr/
local tdir=$root/usr/$item
if ([[ ! -d $root ]] || [[ ! -d $tdir ]]); then
log $ARROW_RED$RESET "Root or link target for $BOLD$sandbox$RESET not found..."
log_to_file "Root or link target for $sandbox not found during synchronization operation."
log_error "Root or link target for $BOLD$instance$RESET not found..."
return
fi
[[ -f $link/$item.zst ]] && mv $link/$item.zst $link/$item.old.zst 2>>$OUTPUT_DEST 1>>$OUTPUT_DEST
find $tdir -type f,d,l | sed -z "s,$rdir,,g" | zstd -fq -o $link/$item.zst
done
log_to_file "Generated link cache for $sandbox!"
}
generate_cache() {
for sandbox in "$@"; do
check_root "cache"
[[ $? == 1 ]] && continue
invoke_generate_cache
done
log_to_file "Generated link cache for $instance!"
}
check_root() {
if [[ ! -d $INSTANCE_ROOT/$sandbox ]]; then
log $ARROW_RED$RESET "Root for $BOLD$sandbox$RESET not found.$RESET\n Skipping $@..."
if [[ ! -d $INSTANCE_ROOT_DIR/$instance ]]; then
log_error $ARROW_RED "Root for $BOLD$instance$RESET not found." "Skipping..."
return 1
fi
}
invoke_vdb () {
local root=$INSTANCE_ROOT/$sandbox
local dep=
update_config() {
declare -A skip
local i=
local local_pkgs=$($EXEC_SCRIPT $instance pacman -Qqe)
local old_local_pkgs=$(return_packages)
local package_list=()
if [[ $(return_type) != "ROOT" ]]; then
dep=$sandbox
else
dep=$(return_dependency)
if [[ $(return_type) != "BASE" ]]; then
local pkgs=$($EXEC_SCRIPT $(return_dependency) pacman -Qqe)
for pkg in $pkgs; do
skip[$pkg]=1
done
fi
declare -A skip
local template="cat $INSTANCE_PACMAN_TEMPLATE/pacman.$dep.conf"
for pkg in $local_pkgs; do
[[ ${skip[$pkg]} ]] && continue
package_list[i++]="$pkg"
done
local pacmanconf=$INSTANCE_PACMAN_SYNC/pacman.$sandbox.conf
[[ "$(echo ${package_list[@]})" != "$(echo ${old_local_pkgs[@]})" ]] &&
generate_config ${package_list[@]}
((amt_done++))
progress_bar
}
sync_pacman_conf () {
local dep=$instance
[[ $(return_type) == "ROOT" ]] && dep=$(return_dependency)
declare -A skip
local template="cat $(return_pacman_template $dep)"
local pacmanconf=$(return_pacman_sync $instance)
local ignorepkg="## Start of automated configuration ##\n"
if [[ $(return_type) != "BASE" ]]; then
@ -333,12 +342,18 @@ invoke_vdb () {
echo -e $header > $pacmanconf
$template | sed -z "s/###IGNOREPKG###/$ignorepkg/g" >> $pacmanconf
log_to_file "Generated $pacmanconf."
invoke_vdb_update
if [[ $amt ]]; then
((amt_done++))
progress_bar
fi
syncdb_pacman_conf
}
invoke_vdb_update() {
local pacmanconf=$INSTANCE_PACMAN_SYNCDB/pacman.$sandbox.conf
local local_pkgs=$($EXEC_SCRIPT $sandbox pacman -Qq)
syncdb_pacman_conf() {
local pacmanconf=$(return_pacman_syncdb $instance)
local local_pkgs=$($EXEC_SCRIPT $instance pacman -Qq)
local ignorepkg="## Start of automated configuration ##\n"
if [[ $(return_type) != "BASE" ]]; then
@ -352,145 +367,128 @@ invoke_vdb_update() {
echo -e $header > $pacmanconf
$template | sed -z "s/###IGNOREPKG###/$ignorepkg/g" >> $pacmanconf
log_to_file "Generated $pacmanconf."
if [[ $amt ]]; then
((amt_done++))
progress_bar
fi
}
invoke_update() {
log_to_file "Checking $sandbox container for updates..."
log $BAR_GREEN "Checking $sandbox for updates..."
local instance=$1
[[ ${updated[$instance]} ]] && return
updated[$instance]=1
log "$BAR_GREEN Checking $instance for updates..." \
"Checking $instance container for updates..."
local type=$(return_type);
local syncdb=$(return_pacman_syncdb)
local syncdb=$(return_sync_pacmandb)
if [[ $SWITCH == *y* ]] && ([[ $type == "BASE" ]] || [[ $type == "DEP" ]]); then
([[ ! $PMSYNCED ]] || ([[ $syncdb ]] && [[ $syncdb == 1 ]])) &&
$EXEC_SCRIPT $sandbox pacman --logfile /tmp/pacman.log -Sy
if [[ $SYNC_UPDATE ]] && [[ $type != "ROOT" ]]; then
if [[ ! $PMSYNCED ]] || [[ $syncdb == 1 ]]; then
$EXEC_SCRIPT $instance pacman --logfile /tmp/pacman.log -Sy
[[ $? != 0 ]] &&
log_error $ARROW_RED "Remote database synchronization failed." \
"Use pacwrap -Sv and examine stdout for details." 1
fi
PMSYNCED=1
fi
local result=$($EXEC_SCRIPT $sandbox pacman --color always -Qu | grep -v "ignored")
local result=$($EXEC_SCRIPT $instance pacman --color always -Qu | grep -v "ignored")
if ([[ $SYNC_OVERRIDE ]] || [[ ${syncreq[$(return_base)]} ]]) &&
[[ $type != "ROOT" ]]; then
log "$ARROW Linking structures..."
invoke_update_link $instance
fi
if ([[ $SYNC_FOREIGN ]] || ([[ $result ]] && [[ $SYNC_UPGRADE ]])) &&
[[ $(return_type) != "BASE" ]]; then
log "$ARROW Synchronizing database against foreign packages..."
$EXEC_SCRIPT $instance pacman -Su \
--logfile /tmp/pacman.log \
--dbonly --noconfirm \
--config=/tmp/pacman.conf 2>/dev/null 1>$VERBOSE
[[ $? != 0 ]] &&
log_error $ARROW_RED "Foreign database synchronization failed." \
"Use pacwrap -Sv and examine stdout for details." 1
if ([[ $SWITCH == *yy* ]] || ([[ $result ]] && [[ $SWITCH == *u* ]])) && [[ $(return_type) != "BASE" ]]; then
log $ARROW "Synchronizing database against foreign packages..."
$EXEC_SCRIPT $sandbox pacman -Su --logfile /tmp/pacman.log --dbonly --noconfirm --config=/tmp/pacman.conf 2>/dev/null 1>$VERBOSE
log " Database synchronization complete!"
fi
if [[ ! $result ]]; then
log "$ARROW Container $BOLD$sandbox$RESET is up to date!"
log_to_file "Database up-to-date for $sandbox."
log "$ARROW Container $BOLD$instance$RESET is up to date!" \
"Database up-to-date for $instance."
return
fi
echo -e "$BAR Package changes: $RESET \n\n$result\n"
[[ $SWITCH != *u* ]] && return
[[ ! $(query_confirm "Proceed with installation on $sandbox") ]] && return
log_to_file "Updating $sandbox container.."
[[ ! $SYNC_UPGRADE ]] && return
[[ ! $(query_confirm_Yn "Proceed with installation on $instance") ]] && return
SYNCREQ=1
$EXEC_SCRIPT $sandbox pacman -Su --logfile /tmp/pacman.log --noconfirm 2>/dev/null
log $ARROW_GREEN "Upgrade complete!"
log_to_file "Updating $instance container.."
$EXEC_SCRIPT $instance pacman -Su \
--logfile /tmp/pacman.log \
--noconfirm 2>/dev/null
[[ $? != 0 ]] &&
log_error $ARROW_RED "Upgrade for $instance failed." \
"Use pacwrap -Sv and examine stdout for details." 1
if [[ $(return_type) != "ROOT" ]]; then
log "$ARROW Generating link cache..."
invoke_generate_cache $instance
synced[$instance]=1
fi
syncreq[$(return_base)]=1
log "$ARROW_GREEN Upgrade complete!"
}
update () {
for sandbox in "$@"; do
update () {
for instance in "$@"; do
check_root "update"
[[ $? == 1 ]] && continue
invoke_update
for dep in $(return_dependencies); do
invoke_update $dep
done
invoke_update $instance
done
}
print_progress_bar() {
printf "$PROGRESS_LABEL"
printf "%-*s" $(($2+1)) '[' | tr ' ' '#'
printf "%*s% 3d%%\r" $(($1-$2)) "]" "$3"
}
init_progress() {
TTY_SIZE=($(stty size))
PROGRESS_LENGTH=$((${TTY_SIZE[1]}/2))
}
set_progress_label() {
PROGRESS_LABEL="$1"
local label_length=${#PROGRESS_LABEL}
local padding_length=$(($PROGRESS_LENGTH-$label_length-8))
for ((i=0; i<=padding_length; i+=1)); do
PROGRESS_LABEL+=" "
done
}
progress_bar() {
local bar_percent=$(($((PROGRESS_LENGTH*2))*$amt_done/$amt % 2 + $PROGRESS_LENGTH*$amt_done/$amt))
local percent=$((200*$amt_done/$amt % 2 + 100*$amt_done/$amt))
print_progress_bar $((PROGRESS_LENGTH+1)) $bar_percent $percent
}
generate_pacman_conf () {
log $BAR_GREEN "Generating pacman configuration..."
init_progress
set_progress_label " pacman.conf"
local amt=${#@}
configure_containers () {
local amt=$((${#@}*3))
local amt_done=0
for sandbox in "$@"; do
((amt_done++))
progress_bar
check_root "pacman.conf generation"
init_progress
set_progress_label "Configuring containers" $ARROW
for instance in "$@"; do check_root "pacman.conf generation"
[[ $? == 1 ]] && continue
invoke_vdb
sync_pacman_conf
done
for instance in "$@"; do check_root "configuration update"
[[ $? == 1 ]] && continue
update_config
done; echo
}
return_pacman_syncdb() {
source $INSTANCE_CONFIG_ROOT/$sandbox
echo $SYNC_PACMANDB
abort () {
echo
ABORT=1
[[ ! $SYNC ]] && exit
}
return_type() {
source $INSTANCE_CONFIG_ROOT/$sandbox
echo $TYPE
}
return_dependencies() {
source $INSTANCE_CONFIG_ROOT/$sandbox
echo ${DEPS[@]}
}
return_base() {
source $INSTANCE_CONFIG_ROOT/$sandbox
echo ${DEPS[0]}
}
return_dependency() {
source $INSTANCE_CONFIG_ROOT/$sandbox
echo ${DEPS[$((${#DEPS[@]} - 1))]}
}
log () {
echo -e "$@ $RESET"
}
log_to_file() {
echo -e "[$(date '+%FT%H:%M:%S%z')] [$EXEC_NAME] $@" >>$LOG_FILE
}
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
on_exit() {
[[ ! $PACWRAP_CREATE ]] && [[ -f $LOCK_FILE ]] && rm $LOCK_FILE
}
main $@

View file

@ -18,45 +18,29 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
BOLD=$(tput bold)
RED=$(tput setaf 1)
GREEN=$(tput setaf 2)
CYAN=$(tput setaf 6)
YELLOW=$(tput setaf 11)
RESET=$(tput sgr0)
UNDERLINE=$(tput smul)
NUMFMT="numfmt --to=si --suffix=B --round=nearest --from-unit=1000 --format=%2f "
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"
source pacwrap-common
EXEC_NAME="pacwrap-utils"
EXEC_SCRIPT="pacwrap-exec"
NUMFMT="numfmt --to=si --suffix=B --round=nearest --from-unit=1000 --format=%2f "
main () {
parse_args $@
export PACWRAP_UTILS=1
trap exit INT
if [[ $SWITCH == v ]]; then
VER_DISPLAY=$EXEC_NAME pacwrap -v
exit
fi
local roots=;
local rootdeps=
local baserootdeps=
local roots=()
local rootdeps=()
local baserootdeps=()
local syncroots=()
local links=
local total=0
init_vars
parse_args $@
init
if [[ ! -d $INSTANCE_ROOT ]] && [[ $SWITCH != i* ]]; then
echo $BOLD$RED"error:$RESET Sandbox root is either missing or an environmental variable is misconfigured.$RESET"
exit
fi
[[ $SWITCH != h* ]] &&
[[ $SWITCH != v ]] &&
[[ $SWITCH != ls* ]] &&
[[ $SWITCH != v* ]] &&
log_to_file "Running '$RUNTIME_ARGS'"
case $SWITCH in
c*)
@ -64,10 +48,12 @@ main () {
paccache --cachedir $INSTANCE_CACHE_DIR ${ARGS[@]}
fi
;;
i*)
initialize_data_directory
;;
r*)
replicate ${baserootdeps[@]} ${rootdeps[@]} ${roots[@]}
;;
v*)
edit_file 1
;;
@ -81,8 +67,8 @@ main () {
link_root
;;
d*)
[[ $SWITCH == *r* ]] && delete_root
[[ $SWITCH == *h* ]] && delete_home
[[ $SWITCH == *r* ]] && delete "root" "$INSTANCE_ROOT_DIR"
[[ $SWITCH == *h* ]] && delete "home" "$INSTANCE_HOME_DIR"
;;
h*)
pacwrap --help=utils
@ -95,12 +81,26 @@ main () {
fi
echo "Try '$EXEC_NAME -h' for more information on valid operational parameters."
;;
esac
}
summary() {
script_init () {
[[ $REPLICATION_CONFIG_DIR ]] &&
INSTANCE_CONFIG_DIR=$REPLICATION_CONFIG_DIR
local linklist=$($list 2>/dev/null | grep -i "@" | tr -d "@")
for f in $linklist; do
links+=($f)
done
touch $LOCK_FILE
trap on_exit EXIT
}
summary() {
declare -A size
local total=0
parse_du
@ -111,8 +111,7 @@ summary() {
rm $tmpfile
fi
if [[ $SWITCH == *d* ]]; then
if [[ $SWITCH == *d* ]]; then
local actual_size=${size["root"]}
local diff=$(($total - $actual_size))
@ -140,6 +139,9 @@ parse_args () {
-U*)
SWITCH=$(echo $var | cut -c 3-)
;;
--cfg-dir=*)
REPLICATION_CONFIG_DIR=$(echo $var | cut -c 11-)
;;
*)
ARGS+=("$var")
;;
@ -147,148 +149,190 @@ parse_args () {
done
}
init_vars() {
INSTANCE_DATA_DIR="$HOME/.local/share/pacwrap"
INSTANCE_CACHE_DIR="$HOME/.cache/pacwrap/pkg"
INSTANCE_CONFIG_DIR="$HOME/.config/pacwrap"
replicate() {
local instances=()
local baserootdeps=
local rootdeps=
local roots=
if [[ $PACWRAP_DEBUG ]]; then
INSTANCE_DATA_DIR="$PACWRAP_DEBUG"
INSTANCE_CONFIG_DIR="$PACWRAP_DEBUG/cfg"
fi
if [[ $SWITCH == *t* ]]; then
local default=$([[ ${#ARGS[@]} -le 1 ]] && echo 1)
local tar_archive=${ARGS[0]}
INSTANCE_ROOT=$INSTANCE_DATA_DIR/root
INSTANCE_HOME=$INSTANCE_DATA_DIR/home
INSTANCE_DB_ROOT=$INSTANCE_DATA_DIR/database
INSTANCE_CONFIG_ROOT=$INSTANCE_CONFIG_DIR/root
INSTANCE_PACMAND=$INSTANCE_CONFIG_DIR/etc/pacman.d
local list="ls -U -1F $INSTANCE_ROOT"
local rootlist=$($list | grep -i "/" | tr -d "/")
local linklist=$($list | grep -i "@" | tr -d "@")
for instance in $rootlist; do
local type=$(return_type)
case $type in
BASE)
baserootdeps+=($instance)
;;
DEP)
rootdeps+=($instance)
;;
*)
roots+=($instance)
;;
esac
done
for f in $linklist; do
links+=($f)
done
}
[[ ! $tar_archive ]] && log_error $LOG_ERR_HELP "tar not specified." 1
[[ ! -f $tar_archive ]] && log_error "tar archive $tar_archive not found." 1
delete_home() {
local instances=${ARGS[@]};
if [[ ! $instances ]]; then
printf "%s\n%s\n" "$EXEC_NAME: instance not specified." \
"Try '$EXEC_NAME -h' for more information on valid operational parameters."
return
fi
printf "%s\n\n$BOLD%s\n\n$RESET" "$BAR Delete home" "$instances"
if [[ "$(query_confirm "Proceed with deletion?")" ]]; then
local tar_list=($(tar tf ${ARGS[0]}))
for instance in $instances; do
if [[ ! -d $INSTANCE_HOME/$instance ]]; then
echo "$RED${BOLD}error:$RESET Home $instance does not exist."
continue
fi
rm -rf $INSTANCE_HOME/$instance
printf "$ARROW_GREEN %s$RESET\n" "Deletion of $instance's home complete!"
for file in ${tar_list[@]}; do
local instance=${file##*/}
local this=$default
[[ ! $this ]] && for instance_list in ${ARGS[@]}; do
[[ "$instance" == "$instance_list" ]] && this=1 && break; done
[[ ! $instance ]] || [[ ! $this ]] && continue
eval $(tar axfO $tar_archive $file | grep -v "#")
populate_configuration
populate_container_array
instances+=($instance)
done
else
instances=(${ARGS[@]})
[[ ${#ARGS[@]} -le 0 ]] && instances=$@
[[ ! $instances ]] && log_error $LOG_ERR_HELP "Instance not specified." 1
for instance in ${instances[@]}; do
[[ ! -f $INSTANCE_CONFIG_DIR/$instance ]] && continue
source_configuration
populate_configuration
populate_container_array
done
fi
[[ ! $SWITCH_NOCONFIRM ]] && echo -e "$BAR Replicate \n\n${instances[@]}\n"
[[ ! "$(query_confirm_yN "Proceed with replication?")" ]] && return
invoke_replication ${baserootdeps[@]} ${rootdeps[@]} ${roots[@]}
}
delete_root() {
local instances=${ARGS[@]};
if [[ ! $instances ]]; then
printf "%s\n%s\n" "$EXEC_NAME: instance not specified." \
"Try '$EXEC_NAME -h' for more information on valid operational parameters."
return
invoke_replication() {
if [[ $SWITCH = *d* ]]; then
printf "$BAR_GREEN %s$RESET\n" "Deleting root instances..."
for instance in $@; do
[[ ! -d $INSTANCE_ROOT_DIR/$instance ]] && continue
rm -rf $INSTANCE_ROOT_DIR/$instance
printf "$ARROW_GREEN %s$RESET\n" "Deletion of $instance's root complete!"
log_to_file "Deleted root $instance."
done
fi
printf "%s\n\n$BOLD%s\n\n$RESET" "$BAR Delete instance" "$instances"
printf "$BAR_GREEN %s$RESET\n" "Replication of root filesystems from configuration..."
if [[ "$(query_confirm "Proceed with deletion?")" ]]; then
declare -A completed
for instance in $instances; do
if [[ ! -d $INSTANCE_ROOT/$instance ]]; then
echo "$RED${BOLD}error:$RESET $instance does not exist."
continue
fi
rm -rf $INSTANCE_ROOT/$instance
printf "$ARROW_GREEN %s$RESET\n" "Deletion of $instance complete!"
for instance in $@; do
local deps_return=$(return_dependencies)
local deps=()
for dep in ${baserootdeps[@]}; do
for depr in $deps_return; do
[[ $dep == $depr ]] && deps+=($dep)
done
done
for dep in ${rootdeps[@]}; do
for depr in $deps_return; do
[[ $dep == $depr ]] && deps+=($dep)
done
done
for dep in ${deps[@]}; do
replicate_instance $dep
done
for depr in $deps_return; do
if [[ ! -d $INSTANCE_ROOT_DIR/$depr ]]; then
log_error $LOG_ERR_HELP "Dependency $depr not fulfilled for $instance."
fi
done
replicate_instance $instance
done
}
replicate_instance() {
local instance=$1
[[ ${completed[$instance]} ]] && return
log_to_file "Beginning replication of $instance."
local type=$(return_type)
local params="-Cn"
[[ $SWITCH == *v* ]] && params+="v"
case $type in
BASE)
params+="b"
;;
DEP)
params+="d --dep=$(return_dependency)"
;;
ROOT)
params+=" --dep=$(return_dependency)"
;;
esac
$CREATE_SCRIPT $params $instance $(return_packages)
if [[ $? == 0 ]]; then
printf " %s$RESET\n" "Replication of $BOLD$instance$RESET complete!"
log_to_file "Replication of $instance complete!"
fi
completed[$instance]=1
}
delete() {
local instances=${ARGS[@]};
[[ ! $instances ]] && log_error $LOG_ERR_HELP "Instance not specified."
[[ ! $SWITCH_NOCONFIRM ]] && printf "%s\n\n$BOLD%s\n\n$RESET" "$BAR Delete $1" "$instances"
[[ ! "$(query_confirm_yN "Proceed with $1?")" ]] && return
for instance in $instances; do
if [[ ! -d $2/$instance ]]; then
log_error $LOG_ERR_WARN "$instance does not exist."
continue
fi
rm -rf $2/$instance
printf "$ARROW_GREEN %s$RESET\n" "Deletion of $instance's $1 complete!"
log_to_file "Deleted $instance's $1."
done
}
link_root() {
local instance=${ARGS[1]};
local link=${ARGS[0]};
local instance=${ARGS[0]};
local link=${ARGS[1]};
if [[ ! $instance ]] || [[ ! $link ]]; then
printf "%s\n%s\n" "$EXEC_NAME: instance or link not specified." \
"Try '$EXEC_NAME -h' for more information on valid operational parameters."
return
fi
[[ ! $instance ]] && log_error $LOG_ERR_HELP "Instance not specified."
[[ ! $link ]] && log_error $LOG_ERR_HELP "Link not specified."
[[ -d $INSTANCE_ROOT_DIR/$instance ]] && log_error "$instance already exists." 1
[[ ! -d $INSTANCE_ROOT_DIR/$link ]] && log_error "Instance $link doesn't exist." 1
if [[ -d $INSTANCE_ROOT/$instance ]]; then
echo "$RED${BOLD}error:$RESET $instance already exists."
return
fi
[[ ! $SWITCH_NOCONFIRM ]] && printf "$BAR%s\n\n$BOLD%s\n\n" "Create virtual instance" "$instance $ARROW $BOLD$link"
[[ ! "$(query_confirm_Yn "Proceed with creation?")" ]] && return
if [[ ! -d $INSTANCE_ROOT/$link ]]; then
echo "$RED${BOLD}error:$RESET $link doesn't exist."
return
fi
log "$BAR_GREEN Creating virtual instance $instance $ARROW $BOLD$link"
printf "%s\n\n$BOLD%s\n\n" "$BAR Create virtual instance" "$instance $ARROW $link"
ln -s $INSTANCE_ROOT_DIR/$link $INSTANCE_ROOT_DIR/$instance
mkdir -p $INSTANCE_HOME_DIR/$instance
echo 'PS1="'$instance'> "' > $INSTANCE_HOME_DIR/$instance/.bashrc
if [[ "$(query_confirm "Proceed with creation?")" ]]; then
ln -s $INSTANCE_ROOT/$link $INSTANCE_ROOT/$instance
mkdir -p $INSTANCE_HOME/$instance
echo 'PS1="'$instance'> "' > $INSTANCE_HOME/$instance/.bashrc
printf "$BAR_GREEN %s$RESET\n" "Creation of virtual instance $instance complete!"
fi
log "$ARROW_GREEN Process complete!" \
"Created $instance -> $link"
}
parse_du() {
for string in $(du -d 1 $INSTANCE_ROOT | tr ' ' '-'); do
for string in $(du -d 1 $INSTANCE_ROOT_DIR | tr ' ' '-'); do
local bytes=${string%%-*}
local instance=${string##*/}
size["$instance"]=$bytes
if [[ $SWITCH == *dd* ]] && [[ $SWITCH != *dddd* ]]; then
[[ $instance == "root" ]] && continue
local dh=$(du -d 1 $INSTANCE_ROOT/$instance | tail -n-1)
local dh=$(du -d 1 $INSTANCE_ROOT_DIR/$instance | tail -n-1)
bytes=${dh% *}
size["$instance"]=$bytes
fi
fi
[[ $instance == "root" ]] && continue
((total+=bytes))
@ -305,7 +349,7 @@ list_root() {
local bytes=0
if [[ ! ${size["$instance"]} ]]; then
dep="$(ls -l $INSTANCE_ROOT/$instance | sed -z "s,$INSTANCE_ROOT/,,g")"
dep="$(ls -l $INSTANCE_ROOT_DIR/$instance | sed -z "s,$INSTANCE_ROOT_DIR/,,g")"
type="LINK"
else
bytes=${size["$instance"]}
@ -334,10 +378,8 @@ edit_file() {
EDITOR="vi"
fi
if [[ ! -f $EDITOR ]]; then
printf "%s%s\n" "$RED$BOLD" "error:$RESET \$EDITOR was not found at $EDITOR."
exit
fi
[[ ! -f $(type -P $EDITOR) ]] &&
log_error "\$EDITOR was not found at $EDITOR." 1
[[ $1 ]] && dissolve=1
@ -345,37 +387,37 @@ edit_file() {
case $SWITCH in
*c*)
file="$INSTANCE_CONFIG_ROOT/$instance"
file="$INSTANCE_CONFIG_DIR/$instance"
error="config file"
;;
*b*)
file="$INSTANCE_CONFIG_DIR/bwrap/$instance.sh"
file="$PACWRAP_CONFIG_DIR/bwrap/$instance.sh"
error="bubblewrap script $file"
create=1
;;
*ips*)
file="$INSTANCE_CONFIG_DIR/pacman/syncdb/pacman.$instance.conf"
file=$(return_pacman_syncdb $instance)
error="instanced pacman $file"
printf "##\n## %s\n" "For the purposes of overview or diagnostics only." > $tmpfile
dissolve=1
;;
*ip*)
file="$INSTANCE_CONFIG_DIR/pacman/sync/pacman.$instance.conf"
file=$(return_pacman_sync $instance)
error="instanced template $file"
printf "##\n## %s\n" "For the purposes of overview or diagnostics only." > $tmpfile
dissolve=1
;;
*l*)
file="$INSTANCE_DATA_DIR/pacwrap.log"
file="$PACWRAP_DATA_DIR/pacwrap.log"
error="$file"
dissolve=1
;;
*pt*)
file="$INSTANCE_CONFIG_DIR/pacman/template/pacman.$instance.conf"
file=$(return_pacman_template $instance)
error="pacman template $file"
;;
*p*)
file="$INSTANCE_CONFIG_DIR/pacman/pacman.conf"
file="$INSTANCE_PACMAN_CFG_DIR/pacman.conf"
error="default pacman.conf"
;;
*)
@ -392,7 +434,6 @@ edit_file() {
return
fi
#sed -r "s/(\x1B\^O|\x1B\[[0-9;]*[JKmsu])//g"
cat $file >> $tmpfile
$EDITOR $tmpfile
@ -404,65 +445,20 @@ edit_file() {
if [[ "${tmp_sum% *}" != "${config_sum% *}" ]]; then
cp $tmpfile $file
log "$ARROW_GREEN Changes written." \
"Changes written to $file."
else
echo "$BAR_GREEN No changes made.$RESET"
log "$BAR_GREEN No changes made"
fi
else
echo "$BAR_GREEN No changes made.$RESET"
log "$BAR_GREEN No changes made."
fi
rm $tmpfile
}
initialize_data_directory() {
mkdir -v -p $INSTANCE_DATA_DIR/root \
$INSTANCE_DATA_DIR/home \
$INSTANCE_DATA_DIR/database \
$INSTANCE_DATA_DIR/pacman/sync \
$INSTANCE_DATA_DIR/pacman/gnupg \
$INSTANCE_CACHE_DIR \
$INSTANCE_CONFIG_DIR/root \
$INSTANCE_CONFIG_DIR/pacman.d \
$INSTANCE_CONFIG_DIR/pacman/sync \
$INSTANCE_CONFIG_DIR/pacman/syncdb \
$INSTANCE_CONFIG_DIR/pacman/template \
$INSTANCE_CONFIG_DIR/bwrap
local ins_string="\[options\]\n"
local template="\n###IGNOREPKG###\n\n"
local install="\nNoExtract = usr/lib/*\nNoExtract = usr/lib32/*\nNoExtract = usr/bin/*\nNoExtract = usr/share/*\n\n"
([[ ! -f $INSTANCE_CONFIG_DIR/pacman.d/mirrorlist ]] || [[ $SWITCH == *m* ]]) && cp /etc/pacman.d/mirrorlist $INSTANCE_CONFIG_DIR/pacman.d/mirrorlist
[[ ! -f $INSTANCE_CONFIG_DIR/pacman/pacman.conf ]] && cp /etc/pacman.conf $INSTANCE_CONFIG_DIR/pacman/pacman.conf
cat $INSTANCE_CONFIG_DIR/pacman/pacman.conf | sed -z "s,$ins_string,$ins_string$template,g" > $INSTANCE_CONFIG_DIR/pacman/pacman.template.conf
cat $INSTANCE_CONFIG_DIR/pacman/pacman.conf | sed -z "s,$ins_string,$ins_string$install,g" > $INSTANCE_CONFIG_DIR/pacman/pacman.install.conf
}
return_type() {
source $INSTANCE_CONFIG_ROOT/$instance
echo $TYPE
}
return_dependency() {
source $INSTANCE_CONFIG_ROOT/$instance
echo ${DEPS[$((${#DEPS[@]} - 1))]}
}
query_confirm () {
if [[ $SWITCH == *n* ]]; then
echo 1
return
fi
read -rp "$BAR $@ [y/N]$RESET " input
if [[ "$input" != "y" ]] &&
[[ "$input" != "Y" ]] &&
([[ "$input" == "" ]] ||
[[ "$input" != "" ]]); then
return
fi
echo 1
on_exit () {
[[ -f $LOCK_FILE ]] && rm $LOCK_FILE
}
main $@