Skip to content

Instantly share code, notes, and snippets.

@protonesso

protonesso/pkgadd

Last active Jun 13, 2020
Embed
What would you like to do?
pkgadd
#!/usr/bin/env bash
#
# Package installer which uses zstd, similar to .deb
#
umask 022
unalias -a
pushd() { command pushd "$1" > /dev/null; }
popd() { command popd "$1" > /dev/null; }
msg() { printf "\033[1;34m::\033[0m %s\n" "$@"; }
warn() { printf "\033[1;33m::\033[0m %s\n" "$@"; }
cleanup() { rm -rf "$tmpdir"; }
die() { printf "\033[1;31m::\033[0m %s\n" "$@"; cleanup; exit 1; }
check_root() {
[[ $EUID -ne 0 ]] && die "You must be root to run 'metaspace'"
}
is_installed() {
[ -n "$(grep -E '(^|\s)Package: '${1}'($|\s)' $2/status)" ] && return 0
}
run_script() {
local root="$1"
local script="$2"
msg "Running $script script"
cp "$script" "${root}script"
chroot "$root" /usr/bin/sh /script
rm "${root}script"
}
initdb() {
local datadir="$1"
[ ! -d "$datadir" ] && mkdir -p "$datadir"
[ ! -d "$datadir/info" ] && mkdir -p "$datadir/info"
[ ! -f "$datadir/status" ] && touch "$datadir/status"
}
## Taken from ipkg
protect_slashes() {
sed -e 's/\//\\\//g'
}
status_remove_sd() {
local sd="$1"
local pkg="$2"
sed -ne "/Package:[[:space:]]*$pkg[[:space:]]*\$/,/^\$/!p" < "$sd/status" > "$sd/status.new"
mv "$sd/status.new" "$sd/status"
}
extract_field() {
local field="$1"
# blacker magic...
sed -ne "
: TOP
/^$field:/{
p
n
b FIELD
}
d
: FIELD
/^$/b TOP
/^[^[:space:]]/b TOP
p
n
b FIELD
"
}
status_update_sd() {
local sd="$1"
local pkg="$2"
status_remove_sd $sd $pkg
extract_field "\(Package\|Status\|Essential\|Version\|Conffiles\|Section\|Priority\|Description\|Maintainer\|Homepage\|Installed-Size\|Depends\|Recommends\|Conflicts\)" >> "$sd/status"
echo "" >> "$sd/status"
}
## Taken end
## Taken from apk-tools
list_subtract() {
(
cat "$1" "$2" | sort | uniq -u
cat "$1"
) | sort | uniq -d
}
list_uninstall() {
local f p
local files=$(sort -r "$1" | sed 's:^:'"${SAVEROOTDIR}"': ; s:/^[^\.]\./::g; s:/\{2,\}:/:g; s:/\./:/:g')
if [ -z "$DRYRUN" ] ; then
echo "$files" | tr '\n' '\0' | xargs -0 rm 2>/dev/null
echo "$files" | tr '\n' '\0' | xargs -0 rmdir 2>/dev/null
[ "$2" ] && echo "$files" >> "$2"
fi
return 0
}
## Taken end
install_package() {
local i conflicts conffiles
local update="0"
local hasdata="0"
local pkg="$1"
local root="$2"
if [ -z "$root" ]; then
root="/"
else
root="${rootdir}/"
fi
[ ! -f "$pkg" ] && die "Package not found"
[ ! -d "$root" ] && die "Root path is not a directory"
pkg="$(realpath $pkg)"
case $root in
/) local datadir="/var/lib/metas" ;;
*) local datadir="${root%/}/var/lib/metas" ;;
esac
initdb "$datadir"
local ext="$(echo $pkg | sed 's/.*\.//')"
case $ext in
metas|deb) true ;;
*) die "Unknown extension: $ext" ;;
esac
export tmpdir="$(mktemp -d)" || die "Cannot create temporary directory"
[ -z "$(ar t $pkg | grep debian-binary)" ] && die "It's not a 'metaspace' package"
[ -z "$(ar t $pkg | grep control.tar.zst)" ] && die "It's not a 'metaspace' package"
[ -n "$(ar t $pkg | grep data.tar.zst)" ] && hasdata="1"
pushd "$tmpdir"
ar x "$pkg"
bsdtar -xf control.tar.zst
local name="$(sed '/^ *Package:/!d;s/.*: //' control)"
is_installed "$name" "$datadir"
if [ $? -eq 0 ]; then
if [ "$needed" = "yes" ]; then
warn "$name is already installed"
return 0
else
msg "Upgrading package: $name"
update="1"
fi
else
msg "Installing package: $name"
update="0"
fi
trap "" INT
if [ "$force" != "yes" ] && [ "$update" != "1" ] && [ "$hasdata" = "1" ]; then
msg "Checking file conflicts"
if ! is_installed "$name" "$datadir"; then
for i in $(bsdtar -tf "data.tar.zst" | sed -e 's,\./,,g'); do
if [ -f "${root}${i}" ] || [ -L "${root}${i}" ]; then
# better format
if [ -z "$conflicts" ]; then
conflicts="$i"
else
conflicts="$conflicts $i"
fi
fi
done
if [ -n "$conflicts" ]; then
msg "Following files couldn't be overwritten"
for i in $conflicts; do echo $i; done
cleanup
exit 1
fi
fi
fi
if [ "$noscripts" != "yes" ]; then
[ -f "preinst" ] && run_script "$root" preinst
fi
if [ "$nobackup" != "yes" ]; then
if [ -f "conffiles" ]; then
msg "Saving files"
for i in $(cat conffiles); do
if [ -f "${root%/}${i}" ]; then
mv "${root%/}${i}" "${root%/}${i}.save" || die "Failed to save file: '${i}'"
fi
done
fi
fi
if [ "$hasdata" = "1" ]; then
msg "Extracting files"
bsdtar -tf data.tar.zst | sed -e 's/^\.//' &> "$tmpdir/list"
bsdtar -C "$root" -xf data.tar.zst || die "Failed to install package contents"
if [ "$update" = "1" ] && [ -f "${datadir}/info/$name.list" ]; then
list_subtract "${datadir}/info/$name.list" "$tmpdir/list" | list_uninstall - || true
fi
fi
if [ "$nobackup" != "yes" ]; then
if [ -f "conffiles" ]; then
msg "Restoring files"
for i in $(cat conffiles); do
if [ -f "${root%/}${i}.save" ]; then
mv "${root%/}${i}.save" "${root%/}${i}" || die "Failed to restore file: '${i}'"
fi
done
fi
fi
if [ "$noscripts" != "yes" ]; then
[ -f "postinst" ] && run_script "$root" postinst
fi
msg "Configuring package"
if [ -f "conffiles" ]; then
for i in $(cat conffiles); do
# better format
if [ -z "$conffiles" ]; then
conffiles="$i"
else
conffiles="$conffiles $i"
fi
done
fi
[ -n "$conffiles" ] && conffiles="Conffiles: $(echo $conffiles | protect_slashes)"
sed -e "s/\(Package:.*\)/\1\\
Status: install ok installed\\
${conffiles}/" "$tmpdir/control" | status_update_sd "$datadir" "$name"
for i in conffiles list post{inst,rm} pre{inst,rm} triggers; do
if [ -f "$i" ]; then
mv "$tmpdir/$i" "$datadir/info/$name.$i"
fi
done
# run_triggers $localname
trap INT
msg "Package has been installed successfuly"
popd
}
main() {
export LC_ALL="POSIX"
topdir="$PWD"
version="MAINVER"
maskfile="/etc/metas/mask"
force="no"
needed="no"
nobackup="no"
noscripts="no"
notriggers="no"
while getopts :fnbstm:r: opts; do
case $opts in
m) maskfile="$(realpath $OPTARG)" ;;
r) rootdir="$(realpath $OPTARG)"; rootdir="${rootdir%/}" ;;
f) force="yes" ;;
n) needed="yes" ;;
b) nobackup="yes" ;;
s) noscripts="yes" ;;
t) notriggers="yes" ;;
esac
done
shift $((OPTIND - 1))
for pkg in $@; do
install_package "$pkg" "$rootdir"
done
}
main "$@"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.