diff options
| -rwxr-xr-x | spm | 314 |
1 files changed, 314 insertions, 0 deletions
@@ -0,0 +1,314 @@ +#!/bin/sh + +msg() { echo "[${name:-...}] $@"; } +msgerr() { echo "[${name:-...}] ERROR: $@"; exit 1; } + +spm_build() { + needarg $1 + if [ ! -d "$1" ]; then + msgerr "'$1' directory not exist." + fi + + rm -f $HERE/$packagename + cd "$1" + + # move all files in etc/ to .new suffix + if [ -d etc ]; then + for d in $(find etc -type f); do + [ -f $d ] || continue + case $d in *.new) continue;; esac + mv $d $d.new + done + fi + + # -J for xz + # -z for gzip + # -j for bzip2 + msg "Packaging $PWD." + tar -c -z -f $HERE/$packagename * || { + msgerr "Failed packaging $PWD." + } + + tar -tvf $HERE/$packagename + msg "Package created: $HERE/$packagename." + + exit 0 +} + +spm_install() { + checkdbdir + needarg $1 + if [ "$(id -u)" != 0 ] || [ "$FAKEROOTKEY" ]; then + msgerr "root required to install package into system." + fi + + if [ ! -f "$1" ]; then + msgerr "Package '$1' not exist." + fi + + pkgname=${1##*/} + + name=${pkgname%#*} + version=${pkgname#*#}; version=${version%%-*} + release=${pkgname##*-}; release=${release%.spm} + + if [ "$name#$version-$release.spm" != "$pkgname" ]; then + msgerr "Valid 'spm' package format is '<name>#<version>-<release>.spm'" + fi + + # if upgrade but not already installed, exit + if [ "$upgrade" ] && [ ! -f $PKGDB/$name ]; then + msgerr "Package '$name' not installed, use '-i' to install." + fi + + # if already installed and not an upgrade, exit + if [ -f $PKGDB/$name ] && [ ! "$upgrade" ]; then + msgerr "Package '$name' is installed, use '-u' to reinstall/upgrade." + fi + + # check if package is corrupt + msg "Verify package..." + tar -tvf "$1" > $SPMDIR/files.list 2>/dev/null || { + msg "Package '$1' is corrupted. Aborted." + rm -f $SPMDIR/files.list + exit 1 + } + + # check conflicting files + msg "Checking for conflicts..." + rm -f $SPMDIR/conflict.list + awk '{print $6}' $SPMDIR/files.list | while read -r line; do + [ "$line" = "${line%.new}.new" ] && line=${line%.new} + # instant conflict if cross, mean file to directory and vice versa + case $line in + */[|*/[[) continue;; # TODO: fix detected '[' and '[[' as conflicted + */) if [ -e "${SPM_ROOT%/}"/"$line" ] && [ ! -d "${SPM_ROOT%/}"/"$line" ]; then + echo "$line" >> $SPMDIR/conflict.list; continue + fi;; + *) if [ -e "${SPM_ROOT%/}"/"$line" ] && [ -d "${SPM_ROOT%/}"/"$line" ] && [ ! -L "${SPM_ROOT%/}"/"$line" ]; then + echo "$line" >> $SPMDIR/conflict.list; continue + fi;; + esac + if [ -e "${SPM_ROOT%/}"/"$line" ]; then + case $line in */) continue;; esac # if directory not conflict + if [ "$upgrade" ] && grep -qx "$line" $PKGDB/$name; then + continue # if upgrade and exist in previous version then not conflict + else + echo "$line" >> $SPMDIR/conflict.list + fi + fi + done + + if [ -f "$SPMDIR/conflict.list" ] && [ ! "$SPM_FORCEINSTALL" ]; then + cat "$SPMDIR/conflict.list" + msg "File conflict found. Enable 'SPM_FORCEINSTALL' env to force install." + rm -f "$SPMDIR/conflict.list" + exit 1 + fi + + msg "$([ $upgrade ] && echo Upgrading || echo Installing) package..." + rm -f $SPMDIR/install.list + tar -xvhpf "$1" -C "${SPM_ROOT%/}"/ | while read -r line; do + if [ "$line" = "${line%.new}.new" ]; then + line=${line%.new} + # if file not exist or same file, just move it + if [ ! -e "$SPM_ROOT/$line" ] || diff "$SPM_ROOT/$line".new "$SPM_ROOT/$line" >/dev/null; then + mv -f "$SPM_ROOT/$line".new "$SPM_ROOT/$line" + fi + fi + echo "$line" >> $SPMDIR/install.list + done + + if [ "$upgrade" ]; then + cd $PKGDB + # remove files from old package thats not in new package + tail -n+2 $name > $SPMDIR/old.list + grep -Fxv -f $SPMDIR/install.list $SPMDIR/old.list | tac | while read -r line; do + case $line in + */) grep -x "$line" * | grep -v $name: >/dev/null || rmdir "${SPM_ROOT%/}"/"$line";; + *) rm "${SPM_ROOT%/}"/"$line";; + esac + done + rm -f $SPMDIR/old.list + cd - >/dev/null + fi + + # register package into db + mkdir -p $PKGDB + echo "$version-$release" > $PKGDB/$name + cat $SPMDIR/install.list >> $PKGDB/$name + rm -f $SPMDIR/install.list + + # permission + mkdir -p $PERMDIR $OWNDIR + rm -f $PERMDIR/$name $OWNDIR/$name + grep ^d $SPMDIR/files.list | awk '{print $1,$2,$6}' | while read -r perms owner dir; do + [ "$perms" = "drwxr-xr-x" ] || echo "$(cvperms $perms) $dir" >> $PERMDIR/$name + [ "$owner" = "root/root" ] || echo "$owner $dir" | sed 's,/,:,' >> $OWNDIR/$name + done + rm -f $SPMDIR/files.list + + # correct dir permission + [ "$(ls -1 $PERMDIR)" ] && { + cat $PERMDIR/* | while read -r perms dir; do + [ -d "${SPM_ROOT%/}"/"$dir" ] || continue + chmod "$perms" "${SPM_ROOT%/}"/"$dir" + done + } + # correct dir ownership + [ "$(ls -1 $OWNDIR)" ] && { + cat $OWNDIR/* | while read -r owner dir; do + [ -d "${SPM_ROOT%/}"/"$dir" ] || continue + chown "$owner" "${SPM_ROOT%/}"/"$dir" + done + } + + [ -x /sbin/ldconfig ] && { + /sbin/ldconfig -r "${SPM_ROOT:-/}" + } + + msg "Package '${pkgname%.spm}' $([ $upgrade ] && echo upgraded. || echo installed.)" + + exit 0 +} + +cvperms() { + # converts symbolic to numeric permissions + # required an input (symbolic, eg: drwxr-xr-x) + s=0; n=0; count=0 + for i in $(echo "$1" | sed -e 's/\(.\)/\1\n/g'); do + count=$((count+1)) + case $i in + d) ;; + r) n=$((n+4));; + w) n=$((n+2));; + x) n=$((n+1));; + s) [ $count = 4 ] && s=$((s+4)) || s=$((s+2)); n=$((n+1));; + t) s=$((s+1));n=$((n+1));; + S) s=$((s+2));; + T) s=$((s+1));; + esac + [ "$count" = 4 ] && { + user=$n; n=0 + } + [ "$count" = 7 ] && { + group=$n; n=0 + } + [ "$count" = 10 ] && { + other=$n; n=0 + } + done + echo "$s$user$group$other" +} + + +spm_remove() { + checkdbdir + needarg $1 + if [ ! -s $PKGDB/$1 ]; then + msgerr "Package '$1' not installed." + fi + + cd $PKGDB + tail -n+2 $1 | tac | while read -r line; do + case $line in + */) grep -x "$line" * | grep -v $1: >/dev/null || rmdir "${SPM_ROOT%/}"/"$line";; + *) rm "${SPM_ROOT%/}"/"$line";; + esac + done + + rm -f $1 $PERMDIR/$1 $OWNDIR/$1 + msg "Package '$1' removed." + + exit 0 +} + +spm_allinstalled() { + checkdbdir + for i in $PKGDB/*; do + echo "${i##*/} $(head -n1 $i)" + done + exit 0 +} + +spm_listfiles() { + checkdbdir + needarg $1 + tail -n+2 $PKGDB/$1 + exit 0 +} + +spm_owner() { + checkdbdir + needarg $1 + set -- ${1#/} + cd $PKGDB + grep $1 * | tr ':' ' ' | column -t + exit 0 +} + +checkdbdir() { + if [ ! -d $PKGDB ]; then + msgerr "Database directory '$PKGDB' not exist. Aborted" + fi +} + +needarg() { + [ "$1" ] || { + msgerr "This operation need argument(s). Aborted." + } +} + +spm_help() { + cat << EOF +usage: ${0##*/} -[a|b|h|i|l|r|o|u] <arg(s)> + +options: + -a print all installed packages + -b <path> build <path> directory into package + -h print this help message + -i <file> install <file> package into system + -l <pkg> list files installed by <pkg> + -r <name> remove installed <name> from system + -o <file> print owner of <file> + -u <file> re-install/upgrade <pkg> + +environments: + SPM_ROOT override default root (/) location + SPM_FORCEINSTALL force package installation + +EOF +exit 0 +} + +if [ "$SPM_ROOT" ]; then + if [ ! -d "$SPM_ROOT" ]; then + msgerr "'$SPM_ROOT' directory not exist." + fi + SPM_ROOT=$(realpath $SPM_ROOT) +fi + +HERE=$PWD +SPMDIR=$SPM_ROOT/var/lib/spm +PKGDB=$SPMDIR/db +PERMDIR=$SPMDIR/perms +OWNDIR=$SPMDIR/owner +packagename="package.spm" + +if [ "$1" ]; then + case $1 in + -b) spm_build $2;; + -i) spm_install $2;; + -u) upgrade=1; spm_install $2;; + -r) spm_remove $2;; + -a) spm_allinstalled;; + -l) spm_listfiles $2;; + -o) spm_owner $2;; + -h) spm_help;; + esac +else + echo "run '${0##*/} -h' to see help message." + exit 0 +fi + +exit 0 |