spm

Unnamed repository; edit this file 'description' to name the repository.
git clone https://codeberg.org/emmett1/spm
Log | Files | Refs | README | LICENSE

spm (9584B)


      1 #!/bin/sh
      2 #   ______   ______  __    __
      3 #  /\  ___\ /\  == \/\ "-./  \
      4 #  \ \___  \\ \  _-/\ \ \-./\ \
      5 #   \/\_____\\ \_\   \ \_\ \ \_\
      6 #    \/_____/ \/_/    \/_/  \/_/
      7 #
      8 # SPM - Simple Package manager (C) 2023-2025 Emmett1
      9 #
     10 #MARKER
     11 
     12 msg()        { echo "[${name:-...}] $@"; }
     13 msgerr()     { echo "[${name:-...}] ERROR: $@"; exit 1; }
     14 needarg()    { [ "$1" ]      || msgerr "This operation need argument(s). Aborted."; }
     15 checkdbdir() { [ -d $PKGDB ] || msgerr "Database directory '$PKGDB' not exist. Aborted"; }
     16 
     17 checkpkgname() {
     18 	name=${1%#*}
     19 	version=${1#*#}; version=${version%%-*}
     20 	release=${1##*-}; release=${release%.spm}
     21 	
     22 	[ "$name#$version-$release.spm" = "$1" ] || \
     23 		msgerr "Valid 'spm' package format is '<name>#<version>-<release>.spm'"
     24 }
     25 
     26 spm_build() {
     27 	needarg $1
     28 	[ -d "${1%/*}" ] || msgerr "'${1%/*}' directory not exist."
     29 	
     30 	checkpkgname ${1##*/}
     31 
     32 	if [ -f "/tmp/spmpackage.lock" ]; then
     33 		msg "Theres existing packaging process running..."
     34 		msg "remove '/tmp/spmpackage.lock' if theres no packaging process running."
     35 	fi
     36 	while [ -f "/tmp/spmpackage.lock" ]; do
     37 		count=$((count+1))
     38 		printf "[${name:-...}] wait for existing packaging process complete ($count)...\033[0K\r"
     39 		sleep 1
     40 	done
     41 	printf "\033[0K"
     42 	
     43 	touch "/tmp/spmpackage.lock"
     44 	
     45 	rm -f $1
     46 	
     47 	# move all files in etc/ to .new suffix
     48 	if [ -d etc ]; then
     49 		for d in $(find etc -type f); do
     50 			[ -f $d ] || continue
     51 			case $d in *.new) continue;; esac
     52 			mv $d $d.new
     53 		done
     54 	fi
     55 	
     56 	SPM_COMPRESSION=${SPM_COMPRESSION:-gzip}
     57 	case $SPM_COMPRESSION in
     58 		bzip2) sc=-j;;
     59 		   xz) sc=-J;;
     60 		    *) sc=-z;;
     61 	esac
     62 	
     63 	msg "Packaging $PWD."
     64 	tar -c $sc -f $1 * || {
     65 		msgerr "Failed packaging $PWD."
     66 	}
     67 	rm -f "/tmp/spmpackage.lock"
     68 	
     69 	tar -tvf $1
     70 	msg "Package created: $1."
     71 	
     72 	exit 0
     73 }
     74 
     75 spm_install() {
     76 	checkdbdir
     77 	needarg $1
     78 	if [ "$(id -u)" != 0 ] || [ "$FAKEROOTKEY" ]; then
     79 		msgerr "root required to install package into system."
     80 	fi
     81 		
     82 	[ -f "$1" ] || msgerr "Package '$1' not exist."
     83 	
     84 	checkpkgname ${1##*/}
     85 	
     86 	# if upgrade but not already installed, exit
     87 	if [ "$upgrade" ] && [ ! -s $PKGDB/$name ]; then
     88 		msgerr "Package '$name' not installed, use '-i' to install."
     89 	fi
     90 	
     91 	# if already installed and not an upgrade, exit
     92 	if [ -s $PKGDB/$name ] && [ ! "$upgrade" ]; then
     93 		msgerr "Package '$name' is installed, use '-u' to reinstall/upgrade."
     94 	fi
     95 	
     96 	if [ -f "/tmp/spminstall.lock" ]; then
     97 		msg "Theres existing install process running..."
     98 		msg "remove '/tmp/spminstall.lock' if theres no install process running."
     99 	fi
    100 	while [ -f "/tmp/spminstall.lock" ]; do
    101 		count=$((count+1))
    102 		printf "[${name:-...}] wait for existing install process complete ($count)...\033[0K\r"
    103 		sleep 1
    104 	done
    105 	printf "\033[0K"
    106 	
    107 	touch "/tmp/spminstall.lock"	
    108 	rm -f "$SPMDIR"/*.list
    109 	
    110 	# check if package is corrupt
    111 	msg "Verify package..."
    112 	tar -tvf "$1" > $SPMDIR/files.list 2>/dev/null || {
    113 		msg "Package '$1' is corrupted. Aborted."
    114 		rm -f "$SPMDIR"/*.list "/tmp/spminstall.lock"
    115 		exit 1
    116 	}
    117 	
    118 	# check conflicting files
    119 	msg "Checking for conflicts..."
    120 	if [ "$upgrade" ]; then
    121 		tail -n+2 $PKGDB/$name > $SPMDIR/installed.list
    122 		awk '{print $6}' $SPMDIR/files.list | sed 's/\.new$//g' > $SPMDIR/files2.list
    123 		grep -Fxv -f $SPMDIR/installed.list $SPMDIR/files2.list > $SPMDIR/newfiles.list
    124 	else
    125 		awk '{print $6}' $SPMDIR/files.list | sed 's/\.new$//g' > $SPMDIR/newfiles.list
    126 	fi
    127 	cat $SPMDIR/newfiles.list | while read -r line; do
    128 		[ "$line" = "${line%.new}.new" ] && line=${line%.new}
    129 		# instant conflict if cross, mean file to directory and vice versa
    130 		case $line in
    131 			*/[|*/[[) continue;; # TODO: fix detected '[' and '[[' as conflicted
    132 			*/) if [ -e "${SPM_ROOT%/}"/"$line" ] && [ ! -d "${SPM_ROOT%/}"/"$line" ]; then
    133 					echo "$line" >> $SPMDIR/conflict.list; continue
    134 				fi;;
    135 			*) 	if [ -e "${SPM_ROOT%/}"/"$line" ] && [ -d "${SPM_ROOT%/}"/"$line" ] && [ ! -L "${SPM_ROOT%/}"/"$line" ]; then
    136 					echo "$line" >> $SPMDIR/conflict.list; continue
    137 				fi;;
    138 		esac
    139 		if [ -e "${SPM_ROOT%/}"/"$line" ]; then
    140 			case $line in */) continue;; esac # if directory not conflict
    141 			echo "$line" >> $SPMDIR/conflict.list
    142 		fi
    143 	done
    144 	
    145 	if [ -f "$SPMDIR/conflict.list" ] && [ ! "$SPM_FORCEINSTALL" ]; then
    146 		cat "$SPMDIR/conflict.list"
    147 		msg "File conflict found."
    148 		rm -f "$SPMDIR"/*.list "/tmp/spminstall.lock"
    149 		exit 1
    150 	fi
    151 	
    152 	msg "$([ $upgrade ] && echo Upgrading || echo Installing) package..."
    153 	tar -xvhpf "$1" -C "${SPM_ROOT%/}"/ | while read -r line; do
    154 		if [ "$line" = "${line%.new}.new" ]; then
    155 			line=${line%.new}
    156 			# if file not exist or same file, just move it
    157 			if [ ! -e "$SPM_ROOT/$line" ] || diff "$SPM_ROOT/$line".new "$SPM_ROOT/$line" >/dev/null; then
    158 				mv -f "$SPM_ROOT/$line".new "$SPM_ROOT/$line"
    159 			fi
    160 		fi
    161 		[ "$SPM_VERBOSE" ] && echo "installed '$line'"
    162 		echo "$line" >> $SPMDIR/install.list
    163 	done
    164 
    165 	if [ "$upgrade" ]; then
    166 		cd $PKGDB
    167 		# remove files from old package thats not in new package
    168 		tail -n+2 $name > $SPMDIR/old.list
    169 		grep -Fxv -f $SPMDIR/install.list $SPMDIR/old.list | tac | while read -r line; do
    170 			case $line in
    171 				*/) grep -x "$line" * | grep -v $name: >/dev/null || rmdir $VERBOSE "${SPM_ROOT%/}"/"$line";;
    172 				*) rm $VERBOSE "${SPM_ROOT%/}"/"$line";;
    173 			esac
    174 		done
    175 		cd - >/dev/null
    176 	fi		
    177 		
    178 	# register package into db
    179 	mkdir -p $PKGDB
    180 	echo "$version-$release" > $PKGDB/$name
    181 	cat $SPMDIR/install.list >> $PKGDB/$name
    182 	
    183 	# permission
    184 	mkdir -p $PERMDIR $OWNDIR
    185 	rm -f "$PERMDIR/$name" "$OWNDIR/$name"
    186 	grep ^d $SPMDIR/files.list | awk '{print $1,$2,$6}' | while read -r perms owner dir; do
    187 		if [ "$perms" != "drwxr-xr-x" ] && [ "$perms" != "drwxrwxr-x" ]; then
    188 			echo "$(cvperms $perms) $dir" >> $PERMDIR/$name
    189 		fi
    190 		[ "$owner" = "root/root" ]  || echo "$owner $dir" | sed 's,/,:,' >> $OWNDIR/$name
    191 	done
    192 	
    193 	# correct dir permission
    194 	[ "$(ls -1 $PERMDIR)" ] && {
    195 		cat $PERMDIR/* | while read -r perms dir; do
    196 			[ -d "${SPM_ROOT%/}"/"$dir" ] || continue
    197 			chmod "$perms" "${SPM_ROOT%/}"/"$dir"
    198 		done
    199 	}
    200 	# correct dir ownership
    201 	[ "$(ls -1 $OWNDIR)" ] && {
    202 		cat $OWNDIR/* | while read -r owner dir; do
    203 			[ -d "${SPM_ROOT%/}"/"$dir" ] || continue
    204 			chown "$owner" "${SPM_ROOT%/}"/"$dir"
    205 		done
    206 	}
    207 	
    208 	[ -x /sbin/ldconfig ] && /sbin/ldconfig -r "${SPM_ROOT:-/}"
    209 	
    210 	msg "Package '$name#$version-$release' $([ $upgrade ] && echo upgraded. || echo installed.)"
    211 	
    212 	# remove lock file & temporary list files
    213 	rm -f "/tmp/spminstall.lock" "$SPMDIR"/*.list
    214 	
    215 	exit 0
    216 }
    217 
    218 cvperms() {
    219 	# converts symbolic to numeric permissions
    220 	# required an input (symbolic, eg: drwxr-xr-x)
    221 	s=0; n=0; count=0
    222 	for i in $(echo "$1" | sed -e 's/\(.\)/\1\n/g'); do
    223 		count=$((count+1))
    224 		case $i in
    225 			d) ;;
    226 			r) n=$((n+4));;
    227 			w) n=$((n+2));;
    228 			x) n=$((n+1));;
    229 			s) [ $count = 4 ] && s=$((s+4)) || s=$((s+2)); n=$((n+1));;
    230 			t) s=$((s+1));n=$((n+1));;
    231 			S) s=$((s+2));;
    232 			T) s=$((s+1));;
    233 		esac
    234 		[ "$count" = 4 ] && {
    235 			user=$n; n=0
    236 		}
    237 		[ "$count" = 7 ] && {
    238 			group=$n; n=0
    239 		}
    240 		[ "$count" = 10 ] && {
    241 			other=$n; n=0
    242 		}
    243 	done
    244 	echo "$s$user$group$other"
    245 }
    246 
    247 
    248 spm_remove() {
    249 	[ "$(id -u)" = 0 ] || msgerr "root required to remove package from system."
    250 	checkdbdir
    251 	needarg $1
    252 	if [ ! -s $PKGDB/$1 ]; then
    253 		msgerr "Package '$1' not installed."
    254 	fi
    255 	
    256 	cd "${SPM_ROOT:-/}"
    257 	
    258 	# while loop is way to slow, but need to use it only for space separated filenames
    259 	grep ' ' $PKGDB/$1 | sort | while read -r line; do
    260 		case $line in
    261 			*/) rmdir $VERBOSE "$line";;
    262 			*)  rm $VERBOSE "$line";;
    263 		esac
    264 		sed "\|^$line$|d" -i $PKGDB/$1
    265 	done
    266 	
    267 	# remove files
    268 	rm $VERBOSE $(tail -n+2 $PKGDB/$1 | tac | grep -v /$)
    269 	
    270 	# list all dirs except for target pkg
    271 	grep /$ $PKGDB/* | grep -v /$1: | awk -F : '{print $2}' | sort | uniq > $SPMDIR/remove.all
    272 	grep /$ $PKGDB/* | grep /$1:    | awk -F : '{print $2}' | sort | uniq > $SPMDIR/remove.target
    273 	grep -Fxv -f $SPMDIR/remove.all $SPMDIR/remove.target | sort | tac > $SPMDIR/remove.rmlist
    274 	[ -s $SPMDIR/remove.rmlist ] && rmdir $VERBOSE $(sort $SPMDIR/remove.rmlist | tac)
    275 	
    276 	rm -f $PKGDB/$1 $PERMDIR/$1 $OWNDIR/$1 $SPMDIR/remove.*
    277 	msg "Package '$1' removed."
    278 	
    279 	exit 0
    280 }
    281 
    282 spm_allinstalled() {
    283 	checkdbdir
    284 	(cd $PKGDB && echo * | tr ' ' '\n')
    285 	exit 0
    286 }
    287 
    288 spm_listfiles() {
    289 	checkdbdir
    290 	needarg $1
    291 	[ -s $PKGDB/$1 ] || msgerr "Package '$1' not installed."
    292 	tail -n+2 $PKGDB/$1
    293 	exit 0
    294 }
    295 
    296 spm_owner() {
    297 	checkdbdir
    298 	needarg $1
    299 	set -- ${1#/}
    300 	(cd $PKGDB && grep $1 * | tr ':' ' ')
    301 	exit 0
    302 }
    303 
    304 
    305 spm_help() {
    306 	cat << EOF
    307 usage: ${0##*/} -[a|b|h|i|l|r|o|u] <arg(s)>
    308 
    309 options:
    310   -a         print all installed packages
    311   -b <pkg>   path to package name
    312   -h         print this help message
    313   -i <file>  install <file> package into system
    314   -l <pkg>   list files installed by <pkg>
    315   -r <name>  remove installed <name> from system
    316   -o <file>  print owner of <file>
    317   -u <pkg>   reinstall/upgrade <pkg>
    318   
    319 environments:
    320   SPM_ROOT          override default root (/) location
    321   SPM_FORCEINSTALL  force package installation
    322   SPM_VERBOSE       be verbose
    323   SPM_COMPRESSION   packaging compression; gzip (default) / bzip2 / gzip
    324   
    325 EOF
    326 exit 0
    327 }
    328 
    329 if [ "$SPM_ROOT" ]; then
    330 	[ -d "$SPM_ROOT" ] || msgerr "'$SPM_ROOT' directory not exist."
    331 	SPM_ROOT=$(realpath $SPM_ROOT)
    332 fi
    333 
    334 if [ "$SPM_VERBOSE" ]; then
    335 	VERBOSE=-v
    336 else
    337 	VERBOSE=
    338 fi
    339 
    340 HERE=$PWD
    341 SPMDIR=$SPM_ROOT/var/lib/spm
    342 PKGDB=$SPMDIR/db
    343 PERMDIR=$SPMDIR/perms
    344 OWNDIR=$SPMDIR/owner
    345 
    346 if [ "$1" ]; then
    347 	case $1 in
    348 		-b) spm_build $2;;
    349 		-i) spm_install $2;;
    350 		-u) upgrade=1; spm_install $2;;
    351 		-r) spm_remove $2;;
    352 		-a) spm_allinstalled;;
    353 		-l) spm_listfiles $2;;
    354 		-o) spm_owner $2;;
    355 		-h) spm_help;;
    356 	esac
    357 else
    358 	echo "run '${0##*/} -h' to see help message."
    359 	exit 0
    360 fi
    361 
    362 exit 0