- Disallow multiple type parameters applied to --create - Provide the ROOT type in pacwrap-utils replicate function - Updated help manual.
535 lines
14 KiB
Bash
Executable file
535 lines
14 KiB
Bash
Executable file
#!/bin/bash
|
|
#
|
|
# PacWrap -- Utility 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/>.
|
|
|
|
source pacwrap-common
|
|
export PACWRAP_UTILS=1
|
|
|
|
NUMFMT="numfmt --to=si --suffix=B --round=nearest --from-unit=1000 --format=%2f "
|
|
|
|
main () {
|
|
trap exit INT
|
|
|
|
local links=
|
|
|
|
parse_args $@
|
|
init
|
|
|
|
case $SWITCH in
|
|
Urc) refresh_configuration;;
|
|
Uc*) [[ $(type -p paccache) ]] && paccache --cachedir $INSTANCE_PACMAN_CACHE ${ARGS[@]};;
|
|
Uml) initialize_data_directory;;
|
|
Ud*|Ulsd|Urmd) desktop_entry;;
|
|
Uo*) open;;
|
|
Urm*) remove;;
|
|
Ur*) replicate;;
|
|
Uv*) edit_file 1;;
|
|
Ue*) edit_file;;
|
|
Uls*) summary;;
|
|
Ul*) link_root;;
|
|
*) log_invalid_arguments $1
|
|
esac
|
|
}
|
|
|
|
script_init () {
|
|
[[ $SWITCH != Uls* ]] && log_to_file "Running '$RUNTIME_EXEC $RUNTIME_ARGS'"
|
|
[[ $SWITCH == *du* ]] && SHOW_SIZE=1
|
|
[[ $SWITCH == *b* ]] && NUMFMT="echo"
|
|
[[ $SWITCH == *f* ]] && FORCE_ACTION=1
|
|
|
|
[[ $REPLICATION_CONFIG_DIR ]] &&
|
|
INSTANCE_CONFIG_DIR=$REPLICATION_CONFIG_DIR
|
|
|
|
local linklist=$(ls -U -1F "$INSTANCE_ROOT_DIR" 2>/dev/null | grep -i "@" | tr -d "@")
|
|
|
|
for f in $linklist; do
|
|
links+=($f)
|
|
done
|
|
|
|
touch $LOCK_FILE
|
|
trap on_exit EXIT
|
|
}
|
|
|
|
parse_args () {
|
|
for var in "$@"; do case $var in
|
|
--utils) continue;;
|
|
-v|--verbose) SWITCH+="v";;
|
|
--force|-f) SWITCH+="f";;
|
|
--noconfirm|-n) SWITCH+="n";;
|
|
--desktop-entry|-d) SWITCH+="d";;
|
|
--list|-ls) SWITCH+="ls";;
|
|
--show-bytes|-b) SWITCH+="b";;
|
|
--disk-depth|-dd) SWITCH+="dd";;
|
|
--disk-util|-du) SWITCH+="du";;
|
|
--disk-sum|-uu) SWITCH+="uu";;
|
|
--edit|-e) SWITCH+="e";;
|
|
--view|-v) SWITCH+="v";;
|
|
--inspect-pacman-sync|-ips) SWITCH+="vips";;
|
|
--inspect-pacman-syncdb|-idb) SWITCH+="vidb";;
|
|
--pacman-temmplate|-pt) SWITCH+="pt";;
|
|
--pacman|-p) SWITCH+="p";;
|
|
--config|-c) SWITCH+="c";;
|
|
--remove|-rm) SWITCH+="rm";;
|
|
--home|-h) SWITCH+="h";;
|
|
--replicate|-r|--root) SWITCH+="r";;
|
|
--link|-l|--log) SWITCH+="l";;
|
|
--tar|-t) SWITCH+="t";;
|
|
--open|-o) SWITCH+="o";;
|
|
--mirror-list|-ml) SWITCH+="ml";;
|
|
-V|--version) SWITCH="V";;
|
|
-U*) SWITCH="${var:1}";;
|
|
--cfg-dir=*) REPLICATION_CONFIG_DIR="${var:10}";;
|
|
*) ARGS+=("$var");;
|
|
esac; done
|
|
}
|
|
|
|
summary() {
|
|
declare -A size
|
|
local total=0
|
|
|
|
load_configuration_all
|
|
|
|
[[ $SHOW_SIZE ]] && parse_du
|
|
|
|
list_root | column -t -s "$DELIMITER"
|
|
|
|
if [[ $SHOW_SIZE ]] && [[ $SWITCH == *uu* ]]; then
|
|
local actual_size=${size["root"]}
|
|
local diff=$(($total - $actual_size))
|
|
|
|
[[ $diff -le 0 ]] && diff=0
|
|
|
|
printf "\n${BOLD}Diskspace utilization summary$RESET\n"
|
|
printf "\n%s\t\t%s\n" "Total" " $($NUMFMT $total)"
|
|
|
|
[[ $SWITCH != *dd* ]] && return
|
|
|
|
local actual=$($NUMFMT $actual_size)
|
|
local difference=$($NUMFMT $diff)
|
|
local space=
|
|
|
|
for ((i=${#difference}; i<=${#actual}; i++)); do space+=" "; done
|
|
|
|
printf "%s\t%s\n" "Difference" "$UNDERLINE- $($NUMFMT $diff)$space$RESET"
|
|
printf "%s\t\t%s\n" "${BOLD}Actual$RESET:" " $actual"
|
|
fi
|
|
}
|
|
|
|
parse_du() {
|
|
for string in $(du -d 1 "$INSTANCE_ROOT_DIR" | tr "$DELIMITER" '-'); do
|
|
local bytes=${string%%-*}
|
|
local instance=${string##*/}
|
|
|
|
size["$instance"]=$bytes
|
|
|
|
if [[ $SWITCH == *dd* ]]; then
|
|
[[ $instance == "root" ]] && continue
|
|
local dh=$(du -d 1 "$INSTANCE_ROOT_DIR/$instance" | tail -n-1); bytes=${dh%$DELIMITER*}
|
|
size["$instance"]=$bytes
|
|
fi
|
|
|
|
[[ $instance == "root" ]] && continue
|
|
((total+=bytes))
|
|
done
|
|
}
|
|
|
|
list_root() {
|
|
local roots=(${BASEROOTDEPS[@]} ${ROOTDEPS[@]} ${ROOTS[@]} ${links[@]})
|
|
|
|
printf "$BOLD%s\t%s\t%s\t%s$RESET\n" "Name" "Dependency" \
|
|
$([[ $SHOW_SIZE ]] && echo Size) "Type"
|
|
|
|
for instance in ${roots[@]}; do
|
|
local dep="-"
|
|
local type="LINK"
|
|
local bytes=0
|
|
|
|
if [[ -L "$INSTANCE_ROOT_DIR/$instance" ]]; then
|
|
dep="$(ls -l "$INSTANCE_ROOT_DIR/$instance" | sed -z "s,$INSTANCE_ROOT_DIR/,,g")"
|
|
else
|
|
bytes=${size["$instance"]}; type=$(return_type)
|
|
fi
|
|
|
|
[[ $type != BASE ]] && [[ $type != LINK ]] && dep=$(return_dependency)
|
|
|
|
printf "%s\t%s\t%s\t%s\n" "$instance" "${dep##*> }" \
|
|
$([[ $SHOW_SIZE ]] && echo "$($NUMFMT $bytes)") "$type"
|
|
done
|
|
}
|
|
|
|
desktop_entry() {
|
|
local instance=${ARGS[0]}
|
|
local application=${ARGS[1]}
|
|
local applications_dir="$HOME/.local/share/applications"
|
|
|
|
[[ ! -d "$applications_dir" ]] && log_error "Application directory '~/${applications_dir##$HOME/}' not found." 1
|
|
|
|
case $SWITCH in
|
|
*ls*) if [[ ! $instance ]]; then
|
|
local entries=($(ls "$applications_dir"/pacwrap.* 2>/dev/null))
|
|
|
|
printf "$BOLD%s$RESET:\n" "Desktop Entries"
|
|
[[ ${#entries[@]} -le 0 ]] && echo "No entries found." && return
|
|
|
|
for file in ${entries[@]}; do
|
|
echo ${file##*/}; done
|
|
else check_root $instance
|
|
local entries=($(ls "$INSTANCE_ROOT_DIR/$instance/usr/share/applications/" 2>/dev/null))
|
|
|
|
printf "$BOLD%s$RESET in $BOLD%s$RESET:\n" "Desktop Entries" "$instance"
|
|
[[ ${#entries[@]} -le 0 ]] && echo "No entries found." && return
|
|
|
|
for file in ${entries[@]}; do
|
|
echo ${file##*/}; done
|
|
fi;;
|
|
*e) edit_file;;
|
|
*rm*) local desktop_entry="$applications_dir/pacwrap.$instance.desktop"
|
|
|
|
test_argument "Desktop entry" $instance
|
|
check_file "Desktop entry" "$desktop_entry"
|
|
|
|
rm "$desktop_entry"
|
|
|
|
log "$ARROW_GREEN Deleted '$desktop_entry'.";;
|
|
*) local desktop_entry_local="$applications_dir/pacwrap.$application.desktop"
|
|
local desktop_entry="$INSTANCE_ROOT_DIR/$instance/usr/share/applications/$application.desktop"
|
|
|
|
test_argument "Application" $application
|
|
check_root $instance
|
|
check_file "Desktop entry" "$desktop_entry_local" 1
|
|
check_file "Container desktop entry" "$desktop_entry"
|
|
|
|
sed -z "s/Exec=/Exec=pacwrap \-E $instance /g" < "$desktop_entry" > "$desktop_entry_local"
|
|
|
|
log "$ARROW_GREEN Created desktop entry for $BOLD$application$RESET derived from container $BOLD$instance$RESET."
|
|
esac
|
|
}
|
|
|
|
link_root() {
|
|
local instance=${ARGS[0]}
|
|
local link=${ARGS[1]}
|
|
|
|
test_argument "Targets" $instance
|
|
check_root $link
|
|
check_root 1 $instance
|
|
|
|
[[ ! $SWITCH_NO_CONFIRM ]] && printf "$BAR %s\n\n$BOLD%s\n\n" "Create virtual instance" "$instance $ARROW $BOLD$link"
|
|
[[ ! "$(query_confirm_Yn "Proceed with creation?")" ]] && return
|
|
|
|
log "$BAR_GREEN Creating virtual instance $instance $ARROW $BOLD$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"
|
|
|
|
load_configuration $link; instance=${ARGS[0]}
|
|
|
|
TYPE=LINK
|
|
DEPS=(${INSTANCE_CONFIG[$link,$CONF_DEPS]} $link)
|
|
MOUNT="${INSTANCE_CONFIG[$link,$CONF_MOUNT]}"
|
|
PERMISSIONS="${INSTANCE_CONFIG[$link,$CONF_PERMS]}"
|
|
PERMISSIONS_DBUS="${INSTANCE_CONFIG[$link,$CONF_DBUS]}"
|
|
ENV_VARS="${INSTANCE_CONFIG[$link,$CONF_ENV]}"
|
|
|
|
populate_configuration
|
|
generate_config
|
|
|
|
log "$ARROW_GREEN Process complete!" \
|
|
"Created $instance -> $link"
|
|
}
|
|
|
|
replicate() {
|
|
BASEROOTDEPS=
|
|
ROOTDEPS=
|
|
ROOTS=
|
|
|
|
local instances=
|
|
|
|
if [[ $SWITCH == *t* ]]; then
|
|
local default=$([[ ${#ARGS[@]} -le 1 ]] && echo 1)
|
|
local tar_archive=${ARGS[0]}
|
|
|
|
test_argument "tar" "$tar_archive"
|
|
check_file "tar archive" "$tar_archive"
|
|
|
|
local tar_list=($(tar tf "${ARGS[0]}"))
|
|
|
|
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
|
|
reset_configuration_params
|
|
instances+=($instance)
|
|
done
|
|
else
|
|
instances=(${ARGS[@]})
|
|
[[ ${#ARGS[@]} -le 0 ]] && instances=$@
|
|
test_argument "Targets" $instances
|
|
|
|
for instance in ${instances[@]}; do
|
|
[[ ! -f "$INSTANCE_CONFIG_DIR/$instance.yml" ]] && continue
|
|
source_configuration
|
|
populate_configuration
|
|
populate_container_array
|
|
reset_configuration_params
|
|
done
|
|
fi
|
|
|
|
[[ ! $SWITCH_NO_CONFIRM ]] && echo -e "$BAR Replicate \n\n${instances[@]}\n"
|
|
[[ ! "$(query_confirm_yN "Proceed with replication?")" ]] && return
|
|
|
|
check_active_instances
|
|
|
|
invoke_replication ${BASEROOTDEPS[@]} ${ROOTDEPS[@]} ${ROOTS[@]} ${LINKS[@]}
|
|
|
|
}
|
|
|
|
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 "$BAR_GREEN %s$RESET\n" "Replication of root filesystems from configuration..."
|
|
|
|
declare -A completed
|
|
|
|
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
|
|
[[ ! -d "$INSTANCE_ROOT_DIR/$depr" ]] &&
|
|
log_error "Dependency $depr not fulfilled for $instance." 1
|
|
done
|
|
|
|
replicate_instance $instance
|
|
done
|
|
|
|
}
|
|
|
|
replicate_instance() {
|
|
local instance=$1
|
|
|
|
[[ ${completed[$instance]} ]] && return
|
|
|
|
log_to_file "Beginning replication of $instance."
|
|
|
|
local depend=
|
|
local type=$(return_type)
|
|
local params="c"
|
|
|
|
[[ $SWITCH == *v* ]] && params+="v"
|
|
[[ $type != BASE ]] && depend=$(return_dependency)
|
|
|
|
case $type in
|
|
ROOT) params+="r";;
|
|
BASE) params+="b";;
|
|
DEP) params+="d";;
|
|
LINK) ln -s "$INSTANCE_ROOT_DIR/$depend" "$INSTANCE_ROOT_DIR/$instance"
|
|
mkdir -p "$INSTANCE_HOME_DIR/$instance"
|
|
echo 'PS1="'$instance'> "' > "$INSTANCE_HOME_DIR/$instance/.bashrc"
|
|
generate_config
|
|
printf "$ARROW_GREEN %s$RESET\n" "Replication of $BOLD$instance$RESET complete!"
|
|
log_to_file "Replication of $instance complete!"
|
|
completed[$instance]=1
|
|
return;;
|
|
esac
|
|
|
|
local depend_params
|
|
|
|
for dep in $(return_dependencies); do
|
|
depend_params+="-t $dep "
|
|
done
|
|
|
|
$CREATE_SCRIPT -Syu$params -t $instance $(return_packages) $depend_params --noconfirm
|
|
|
|
if [[ $? == 0 ]]; then
|
|
printf " %s$RESET\n" "Replication of $BOLD$instance$RESET complete!"
|
|
log_to_file "Replication of $instance complete!"
|
|
fi
|
|
|
|
completed[$instance]=1
|
|
}
|
|
|
|
refresh_configuration() {
|
|
load_configuration_all
|
|
|
|
local roots=(${BASEROOTDEPS[@]} ${ROOTDEPS[@]} ${ROOTS[@]} ${LINKS[@]})
|
|
|
|
for instance in ${roots[@]}; do
|
|
generate_config $(return_packages)
|
|
done
|
|
|
|
log "$ARROW_GREEN Refreshed configurations."
|
|
}
|
|
|
|
remove() {
|
|
SWITCH=${SWITCH##*rm}
|
|
|
|
check_active_instances
|
|
|
|
[[ $SWITCH == *r* ]] && invoke_removal "root" "$INSTANCE_ROOT_DIR"
|
|
[[ $SWITCH == *h* ]] && invoke_removal "home" "$INSTANCE_HOME_DIR"
|
|
}
|
|
|
|
invoke_removal() {
|
|
local instances=${ARGS[@]};
|
|
|
|
test_argument "Targets" $instances
|
|
|
|
[[ ! $SWITCH_NO_CONFIRM ]] && 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
|
|
}
|
|
|
|
open() {
|
|
local directory=
|
|
local instance=${ARGS[0]};
|
|
check_root $instance
|
|
|
|
case $SWITCH in
|
|
*r*) directory=$INSTANCE_ROOT_DIR/$instance ;;
|
|
*h*) directory=$INSTANCE_HOME_DIR/$instance ;;
|
|
esac
|
|
|
|
test_argument "Directory Type" $directory
|
|
gio open $directory
|
|
}
|
|
|
|
edit_file() {
|
|
local instance=${ARGS[0]};
|
|
|
|
local create=
|
|
local dissolve=
|
|
local file=
|
|
local tmpfile=
|
|
local error=
|
|
|
|
[[ ! $EDITOR ]] && EDITOR="vi"
|
|
[[ ! -f $(type -P $EDITOR) ]] &&
|
|
log_error "\$EDITOR was not found at $EDITOR." 1
|
|
[[ $1 ]] && dissolve=1
|
|
|
|
tmpfile=$(mktemp)
|
|
|
|
case $SWITCH in
|
|
*ml*) file="$INSTANCE_PACMAN_MIRRORLIST"
|
|
error="Pacman Mirrorlist $file";;
|
|
*idb*) file=$(return_pacman_syncdb $instance)
|
|
error="instanced pacman $file"
|
|
printf "##\n## %s\n" "For the purposes of overview or diagnostics only." > $tmpfile
|
|
dissolve=1;;
|
|
*ips*) file=$(return_pacman_sync $instance)
|
|
error="instanced template $file"
|
|
printf "##\n## %s\n" "For the purposes of overview or diagnostics only." > $tmpfile
|
|
dissolve=1;;
|
|
*c*) file="$INSTANCE_CONFIG_DIR/$instance.yml"
|
|
error="config file";;
|
|
*d*) file="$HOME/.local/share/applications/pacwrap.$instance.desktop"
|
|
error="pacwrap.$instance.desktop";;
|
|
*l*) file="$PACWRAP_DATA_DIR/pacwrap.log"
|
|
error="$file"
|
|
dissolve=1;;
|
|
*pt*) file=$(return_pacman_template $instance)
|
|
error="pacman template $file";;
|
|
*p*) file="$INSTANCE_PACMAN_CFG_DIR/pacman.conf"
|
|
error="default pacman.conf";;
|
|
*) log_invalid_arguments 1
|
|
esac
|
|
|
|
|
|
if ([[ ! -f "$file" ]] && [[ $dissolve ]]) ||
|
|
([[ ! -f "$file" ]] && [[ ! $create ]]); then
|
|
log_error "$error not found."
|
|
rm $tmpfile
|
|
return
|
|
fi
|
|
|
|
[[ -f "$file" ]] &&
|
|
cat "$file" >> $tmpfile
|
|
|
|
$EDITOR $tmpfile
|
|
|
|
local tmp_sum=$(sha256sum $tmpfile)
|
|
local blank_sum=$(sha256sum /dev/null)
|
|
local config_sum=
|
|
|
|
if [[ "${tmp_sum% *}" != "${blank_sum% *}" ]] && [[ ! $dissolve ]]; then
|
|
[[ -f $file ]] && config_sum=$(sha256sum $file)
|
|
|
|
if [[ "${tmp_sum% *}" != "${config_sum% *}" ]]; then
|
|
cp $tmpfile "$file"
|
|
log "$ARROW_GREEN Changes written." \
|
|
"Changes written to $file."
|
|
else
|
|
log "$ARROW No changes made"
|
|
fi
|
|
else
|
|
log "$ARROW No changes made."
|
|
fi
|
|
|
|
rm $tmpfile
|
|
}
|
|
|
|
check_active_instances() {
|
|
[[ $FORCE_ACTION ]] && return
|
|
[[ ! ${ARGS[@]} ]] && log_invalid_arguments 1
|
|
local running_instances=($($PS_SCRIPT -Pid ${ARGS[@]} 2>/dev/null))
|
|
[[ ${#running_instances[@]} > 0 ]] &&
|
|
log_error $ARROW_RED "There are running instances of the containers specified." \
|
|
"Please close or terminate them to perform this operation." 1
|
|
}
|
|
|
|
on_exit () {
|
|
[[ -f "$LOCK_FILE" ]] && rm "$LOCK_FILE"
|
|
}
|
|
|
|
main $@
|