#!/bin/sh # Copyright 1994, 1998, 2000 Patrick Volkerding, Concord, CA, USA # Copyright 2001, 2003 Slackware Linux, Inc., Concord, CA, USA # Copyright 2007 Patrick Volkerding, Sebeka, MN, USA # All rights reserved. # # Redistribution and use of this script, with or without modification, is # permitted provided that the following conditions are met: # # 1. Redistributions of this script must retain the above copyright # notice, this list of conditions and the following disclaimer. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # # Fri Dec 21 17:21:35 CST 2007 # Added a patch from Johnny Morano to work around package removal issues # caused by packages that do not comply with FHS combined with a grep # regex error in installpkg. Any package with a single-letter top- # level directory could not be removed. # # Shortened some of the top-line dialog output to avoid overflowing the # textbox (needed as some of the packages, especially in X, have very # long base package names now). # # Sun Nov 26 12:38:25 CST 1995 # Added patch from Glenn Moloney to allow # packages to be installed to directories other than /. # # Wed Mar 18 15:15:51 CST 1998 # Changed $TMP directory to /var/log/setup/tmp, and chmod'ed it 700 to close # some security holes. # If installpkg encounters a problem, it will return a non-zero error code. # If it finds more than one problem (i.e. with a list of packages) you'll only # hear about the most recent one. :) # 1 = tar returned error code # 2 = failed 'gzip -l package' # 3 = does not end in .tgz # 4 = not a file # 99 = user abort from menu mode EXITSTATUS=0 # So that we know what to expect... umask 022 TAR=tar-1.13 $TAR --help 1> /dev/null 2> /dev/null if [ ! $? = 0 ]; then TAR=tar fi if [ ! "`LC_MESSAGES=C $TAR --version`" = "tar (GNU tar) 1.13 Copyright (C) 1988, 92,93,94,95,96,97,98, 1999 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Written by John Gilmore and Jay Fenlason." ]; then echo "WARNING: pkgtools are unstable with tar > 1.13." echo " You should provide a \"tar-1.13\" in your \$PATH." sleep 5 fi usage() { cat << EOF Usage: installpkg [options] package_name Installpkg is used to install a .tgz package like this: installpkg xf_bin.tgz options: -warn (warn if files will be overwritten, but do not install) -root /mnt (install someplace else, like /mnt) -infobox (use dialog to draw an info box) -menu (confirm package installation with a menu, unless the priority is [required] or ADD) -ask (used with menu mode: always ask if a package should be installed regardless of what the package's priority is) -priority ADD|REC|OPT|SKP (provide a priority for the entire package list to use instead of the priority in the tagfile) -tagfile /somedir/tagfile (specify a different file to use for package priorities. The default is "tagfile" in the package's directory) EOF } # Eliminate whitespace function: crunch() { while read FOO ; do echo $FOO done } package_name() { STRING=`basename $1 .tgz` # Check for old style package name with one segment: if [ "`echo $STRING | cut -f 1 -d -`" = "`echo $STRING | cut -f 2 -d -`" ]; then echo $STRING else # has more than one dash delimited segment # Count number of segments: INDEX=1 while [ ! "`echo $STRING | cut -f $INDEX -d -`" = "" ]; do INDEX=`expr $INDEX + 1` done INDEX=`expr $INDEX - 1` # don't include the null value # If we don't have four segments, return the old-style (or out of spec) package name: if [ "$INDEX" = "2" -o "$INDEX" = "3" ]; then echo $STRING else # we have four or more segments, so we'll consider this a new-style name: NAME=`expr $INDEX - 3` NAME="`echo $STRING | cut -f 1-$NAME -d -`" echo $NAME # cruft for later ;) #VER=`expr $INDEX - 2` #VER="`echo $STRING | cut -f $VER -d -`" #ARCH=`expr $INDEX - 1` #ARCH="`echo $STRING | cut -f $ARCH -d -`" #BUILD="`echo $STRING | cut -f $INDEX -d -`" fi fi } # Parse options: MODE=install # standard text-mode while [ 0 ]; do if [ "$1" = "-warn" ]; then MODE=warn shift 1 elif [ "$1" = "-infobox" ]; then MODE=infobox shift 1 elif [ "$1" = "-menu" ]; then MODE=menu shift 1 elif [ "$1" = "-ask" ]; then ALWAYSASK="yes" shift 1 elif [ "$1" = "-tagfile" ]; then if [ -r "$2" ]; then USERTAGFILE="$2" elif [ -r "`pwd`/$2" ]; then USERTAGFILE="`pwd`/$2" else usage exit fi shift 2 elif [ "$1" = "-priority" ]; then if [ "$2" = "" ]; then usage exit fi USERPRIORITY="$2" shift 2 elif [ "$1" = "-root" ]; then if [ "$2" = "" ]; then usage exit fi ROOT="$2" shift 2 else break fi done # Set the prefix for the package database directories (packages, scripts). ADM_DIR="$ROOT/var/log" # If the directories don't exist, "initialize" the package database: for PKGDBDIR in packages removed_packages removed_scripts scripts setup ; do if [ ! -d $ADM_DIR/$PKGDBDIR ]; then rm -rf $ADM_DIR/$PKGDBDIR # make sure it's not a symlink or something stupid mkdir -p $ADM_DIR/$PKGDBDIR chmod 755 $ADM_DIR/$PKGDBDIR fi done # Make sure there's a proper temp directory: TMP=$ADM_DIR/setup/tmp # If the $TMP directory doesn't exist, create it: if [ ! -d $TMP ]; then rm -rf $TMP # make sure it's not a symlink or something stupid mkdir -p $TMP chmod 700 $TMP # no need to leave it open fi # usage(), exit if called with no arguments: if [ $# = 0 ]; then usage; exit fi # If -warn mode was requested, produce the output and then exit: if [ "$MODE" = "warn" ]; then while [ -f "$1" ]; do echo "#### Scanning the contents of $1..." mkdir -p $TMP/scan$$ ( cd $TMP/scan$$ ; $TAR xzf - install ) < $1 2> /dev/null if [ -r $TMP/scan$$/install/doinst.sh ]; then if cat $TMP/scan$$/install/doinst.sh | grep ' rm -rf ' 1>/dev/null 2>/dev/null ; then cat $TMP/scan$$/install/doinst.sh | grep ' rm -rf ' > $TMP/scan$$/install/delete echo "The following locations will be completely WIPED OUT to allow symbolic" echo "links to be made. (We're talking 'rm -rf') These locations may be files," echo "or entire directories. Be sure you've backed up anything at these" echo "locations that you want to save before you install this package:" cat $TMP/scan$$/install/delete | cut -f 3,7 -d ' ' | tr ' ' '/' fi if [ -d $TMP/scan$$ ]; then ( cd $TMP/scan$$ ; rm -rf install ) 2> /dev/null ( cd $TMP ; rmdir scan$$ ) 2> /dev/null fi fi echo "The following files will be overwritten when installing this package." echo "Be sure they aren't important before you install this package:" ( $TAR tzvvf - ) < $1 | grep -v 'drwx' echo shift 1 done exit fi # Main loop: for package in $* ; do # If someone left off the .tgz, try to figure that out: if [ ! -r "$package" -a -r "$package.tgz" ]; then package=$package.tgz fi # "shortname" isn't really THAT short... it's just the full name without ".tgz" shortname="`basename $package .tgz`" packagedir="`dirname $package`" # This is the base package name, used for grepping tagfiles and descriptions: packagebase="`package_name $shortname`" # Reject package if it does not end in '.tgz': if [ ! -r "`dirname $package`/$shortname.tgz" ]; then EXITSTATUS=3 if [ "$MODE" = "install" ]; then echo "Cannot install $package: package does not end in .tgz" fi continue; fi # Determine package's priority: unset PRIORITY if [ "$USERTAGFILE" = "" ]; then TAGFILE="`dirname $package`/tagfile" else TAGFILE="$USERTAGFILE" fi if [ ! -r "$TAGFILE" ]; then TAGFILE=/dev/null fi if grep "^$packagebase:" "$TAGFILE" | grep ADD > /dev/null 2> /dev/null ; then PRIORITY="ADD" elif grep "^$packagebase:" "$TAGFILE" | grep REC > /dev/null 2> /dev/null ; then PRIORITY="REC" elif grep "^$packagebase:" "$TAGFILE" | grep OPT > /dev/null 2> /dev/null ; then PRIORITY="OPT" elif grep "^$packagebase:" "$TAGFILE" | grep SKP > /dev/null 2> /dev/null ; then PRIORITY="SKP" fi if [ "$PRIORITY" = "ADD" ]; then PMSG="[ADD]" elif [ "$PRIORITY" = "REC" ]; then PMSG="[REC]" elif [ "$PRIORITY" = "OPT" ]; then PMSG="[OPT]" elif [ "$PRIORITY" = "SKP" ]; then PMSG="[SKP]" else PMSG="" fi # Locate package description file: DESCRIPTION="/dev/null" # First check the usual locations outside the package, since this is faster: for file in $packagedir/disk* $packagedir/package_descriptions $packagedir/$shortname.txt $packagedir/$packagebase.txt ; do if grep "^$packagebase:" "$file" 1> /dev/null 2> /dev/null ; then DESCRIPTION="$file" elif grep "^$shortname:" "$file" 1> /dev/null 2> /dev/null ; then DESCRIPTION="$file" fi done # If we still don't have anything, look inside the package. This requires a costly untar. if [ "$DESCRIPTION" = "/dev/null" ]; then mkdir -p $TMP/scan$$ ( cd $TMP/scan$$ ; $TAR xzf - install ) < $package 2> /dev/null if grep "^$packagebase:" "$TMP/scan$$/install/slack-desc" 1> /dev/null 2> /dev/null ; then DESCRIPTION="$TMP/scan$$/install/slack-desc" elif grep "^$shortname:" "$TMP/scan$$/install/slack-desc" 1> /dev/null 2> /dev/null ; then DESCRIPTION="$TMP/scan$$/install/slack-desc" fi fi # Simple package integrity check: if [ ! -f $package ]; then EXITSTATUS=4 if [ "$MODE" = "install" ]; then echo "Cannot install $package: package is not a regular file" fi continue; fi gzip -l $package 1> /dev/null 2> /dev/null if [ ! "$?" = "0" ]; then EXITSTATUS=2 # failed gzip -l if [ "$MODE" = "install" ]; then echo "Cannot install $package: package is corrupt (failed 'gzip -l $package')" fi continue; fi # Collect the package information into a temp file: COMPRESSED=`gzip -l $package | grep -v uncompressed_name | crunch | cut -f 1 -d ' '` UNCOMPRESSED=`gzip -l $package | grep -v uncompressed_name | crunch | cut -f 2 -d ' '` COMPRESSED="`expr $COMPRESSED / 1024` K" UNCOMPRESSED="`expr $UNCOMPRESSED / 1024` K" # MD5SUM=`md5sum $package | cut -f 1 -d ' '` cat $DESCRIPTION | grep "^$packagebase:" | cut -f 2- -d : | cut -b2- 1> $TMP/tmpmsg$$ 2> /dev/null if [ "$shortname" != "$packagebase" ]; then cat $DESCRIPTION | grep "^$shortname:" | cut -f 2- -d : | cut -b2- 1>> $TMP/tmpmsg$$ 2> /dev/null fi # Adjust the length here. This allows a slack-desc to be any size up to 13 lines instead of fixed at 11. LENGTH=`cat $TMP/tmpmsg$$ | wc -l` while [ $LENGTH -lt 12 ]; do echo >> $TMP/tmpmsg$$ LENGTH=`expr $LENGTH + 1` done echo "Size: Compressed: $COMPRESSED, uncompressed: $UNCOMPRESSED." >> $TMP/tmpmsg$$ # For recent versions of dialog it is necessary to add \n to the end of each line # or it will remove repeating spaces and mess up our careful formatting: cat << EOF > $TMP/controlns$$ \n \n \n \n \n \n \n \n \n \n \n \n \n EOF paste -d "" $TMP/tmpmsg$$ $TMP/controlns$$ > $TMP/pasted$$ rm -f $TMP/controlns$$ mv $TMP/pasted$$ $TMP/tmpmsg$$ # Emit information to the console: if [ "$MODE" = "install" ]; then if [ "$PMSG" = "" ]; then echo "Installing package $shortname... " else echo "Installing package $shortname ($PMSG)... " fi echo "PACKAGE DESCRIPTION:" cat $DESCRIPTION | grep "^$packagebase:" | uniq if [ "$shortname" != "$packagebase" ]; then cat $DESCRIPTION | grep "^$shortname:" | uniq fi elif [ "$MODE" = "infobox" -a ! "$PRIORITY" = "SKP" ]; then # install non-SKP infobox package dialog --title "Installing package $shortname $PMSG" --infobox "`cat $TMP/tmpmsg$$`" 0 0 elif [ "$MODE" = "menu" -a "$PRIORITY" = "ADD" -a ! "$ALWAYSASK" = "yes" ]; then # ADD overrides menu mode unless -ask was used dialog --title "Installing package $shortname $PMSG" --infobox "`cat $TMP/tmpmsg$$`" 0 0 elif [ "$MODE" = "menu" -a "$USERPRIORITY" = "ADD" ]; then # install no matter what $PRIORITY dialog --title "Installing package $shortname $PMSG" --infobox "`cat $TMP/tmpmsg$$`" 0 0 elif [ "$MODE" = "menu" -a "$PRIORITY" = "SKP" -a ! "$ALWAYSASK" = "yes" ]; then # SKP overrides menu mode unless -ask used rm -f $TMP/tmpmsg$$ continue # next package elif [ "$MODE" = "infobox" -a "$PRIORITY" = "SKP" -a ! "$ALWAYSASK" = "yes" ]; then # SKP overrides infobox mode, too rm -f $TMP/tmpmsg$$ continue else # we must need a full menu: dialog --title "Package Name: $shortname $PMSG" --menu "`cat $TMP/tmpmsg$$`" 0 0 3 \ "Yes" "Install package $shortname" \ "No" "Do not install package $shortname" \ "Quit" "Abort software installation completely" 2> $TMP/reply$$ if [ ! $? = 0 ]; then echo "No" > $TMP/reply$$ fi REPLY="`cat $TMP/reply$$`" rm -f $TMP/reply$$ $TMP/tmpmsg$$ if [ "$REPLY" = "Quit" ]; then exit 99 # EXIT STATUS 99 = ABORT! elif [ "$REPLY" = "No" ]; then continue # skip the package fi fi # Test tarball integrity, and make sure we're not installing files on top of existing symbolic links: $TAR tzf $package 1> $TMP/tmplist$$ 2> /dev/null TARERROR=$? if [ ! "$TARERROR" = "0" ]; then EXITSTATUS=1 # tar file corrupt if [ "$MODE" = "install" ]; then echo "Unable to install $package: tar archive is corrupt (tar returned error code $TARERROR)" fi rm -f $TMP/tmplist$$ continue fi cat $TMP/tmplist$$ | grep -v "/$" | while read file ; do if [ -L "$ROOT/$file" ]; then rm -f "$ROOT/$file" fi done rm -f $TMP/tmplist$$ # Write the package file database entry and install the package: echo "PACKAGE NAME: $shortname" > $ADM_DIR/packages/$shortname echo "COMPRESSED PACKAGE SIZE: $COMPRESSED" >> $ADM_DIR/packages/$shortname echo "UNCOMPRESSED PACKAGE SIZE: $UNCOMPRESSED" >> $ADM_DIR/packages/$shortname echo "PACKAGE LOCATION: $package" >> $ADM_DIR/packages/$shortname # echo "PACKAGE MD5SUM: $MD5SUM" >> $ADM_DIR/packages/$shortname echo "PACKAGE DESCRIPTION:" >> $ADM_DIR/packages/$shortname cat $DESCRIPTION | grep "^$packagebase:" >> $ADM_DIR/packages/$shortname 2> /dev/null if [ "$shortname" != "$packagebase" ]; then cat $DESCRIPTION | grep "^$shortname:" >> $ADM_DIR/packages/$shortname 2> /dev/null fi echo "FILE LIST:" >> $ADM_DIR/packages/$shortname ( cd $ROOT/ ; $TAR -xzlUpvf - ) < $package >> $TMP/$shortname 2> /dev/null if [ "`cat $TMP/$shortname | grep '^\./' | wc -l | tr -d ' '`" = "1" ]; then # Good. We have a package that meets the Slackware spec. cat $TMP/$shortname >> $ADM_DIR/packages/$shortname else # Some dumb bunny built a package with something other than makepkg. Bad! # Oh well. Bound to happen. Par for the course. Fix it and move on... echo './' >> $ADM_DIR/packages/$shortname cat $TMP/$shortname | grep -v '^./$' | cut -b3- >> $ADM_DIR/packages/$shortname fi rm -f $TMP/$shortname if [ -x /sbin/ldconfig ]; then /sbin/ldconfig fi if [ -f $ROOT/install/doinst.sh ]; then if [ "$MODE" = "install" ]; then echo "Executing install script for $shortname..." fi ( cd $ROOT/ ; sh install/doinst.sh -install; ) fi # Clean up the mess... if [ -d $ROOT/install ]; then if [ -r $ROOT/install/doinst.sh ]; then cp $ROOT/install/doinst.sh $ADM_DIR/scripts/$shortname chmod 755 $ADM_DIR/scripts/$shortname fi # /install/doinst.sh and /install/slack-* are reserved locations for the package system. ( cd $ROOT/install ; rm -f doinst.sh slack-* 1> /dev/null 2>&1 ) rmdir $ROOT/install 1> /dev/null 2>&1 fi # If we used a scan directory, get rid of it: if [ -d "$TMP/scan$$" ]; then rm -rf "$TMP/scan$$" fi rm -f $TMP/tmpmsg$$ $TMP/reply$$ if [ "$MODE" = "install" ]; then echo fi done exit $EXITSTATUS