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