#! /bin/sh # pmpkginstall: a poor man's installer script. # # This script does everything Installer.app does, except for fancy graphics # and getting the correct permissions to write a receipt (since we cannot # make it setuid just for that; an obvious fix is to rewrite it in Perl). # # This version now supports the OSXPM package manager to be called by a Cocoa wrapper # first re-write by Chris Roberts chris@osxgnu.org # last update: Apr 24 2002 by: Chris Roberts # Fixed receipt errors in structure on install # Fixed odd chars in delete path and handling of missing InstallOnly flag in .info files # Added support for .dep files # Added .installer file to receipt # Added date string to .status file # Added support for bzip2 archives # last update: May 11 2002 by: Chris Roberts # Added delete support for non-standard package formats # Last update: Sep 2, 2002 by: Steve Lazarus Stephen.M.Lazarus.C69@alumni.upenn.edu # Fixed usage on OSX 10.2 (Jaguar) caused by /bin/sh being a link to /bin/bash # bash does not like empty if statements -- added echo "" >/dev/null # added quotation marks to an echo statement bash disliked # $Id: pkgInstall,v 1.9 2002/08/30 14:41:01 chris Exp $ # Orignal author Yves Arrouye, 1997 : ${TMPDIR:=/tmp} me=`basename $0` version="1.9" #set defaults interactive=yes action=install receipt=1 verbose=0 dwiw=0 if [ -x /usr/bin/lsbom ] then lsbom=/usr/bin/lsbom else lsbom=/usr/etc/lsbom fi # which() { for d in `echo $PATH | tr ':' ' '` do if [ -f ${d}/"$1" -a -x ${d}/"$1" ] then echo ${d}/"$1" return fi done } usage() { bye=$1 : ${bye:=1} if [ $bye -eq 0 ] then echo -n U else echo -n u exec >&2 fi echo "usage: $me [ --help ] [ --version ] [ --limitations ]" echo " $me [ --help ] [ --interactive | --automatic ] [ --verbose ] [ --install ] [ --dwiw ] [ --noreceipt ] package [ installdir ]" echo " $me [ --help ] [ --interactive | --automatic ] --delete [ --dwiw ] package" if [ $bye -eq 0 ] then cat </dev/null mkdir -p "/Library/Receipts" fi if [ ! -w "/Library/Receipts" ] then receiptsdir=$HOME/Library/Receipts else receiptsdir=/Library/Receipts fi languages=`2>/dev/null defaults read NSGlobalDomain NSLanguages` : ${languages="(English, French, German, Spanish, Italian, Swedish)"} languages=`echo "$languages" | sed 's/[^a-zA-Z]/ /g'` findfile() { languages="English French German Spanish Italian Swedish" #echo lang = $languages for language in $languages do if [ -r "$2/English.lproj/$3.$1" ] then echo "$2/English.lproj/$3.$1" return fi done if [ -r "$2/$3.$1" ] then echo "$2/$3.$1" return else for lproj in `find "$2" -name English.lproj -print` do if [ -r "$lproj"/"$3"."$1" ] then echo "$lproj"/"$3"."$1" return fi done fi echo "$2/$3.$1" } findinfo() { findfile info "$1" "$2" } findarchive() { for _compressExt in .gz .Z .bz2 do for _archExt in .pax .tar do if [ -f "$1"/"$2"${_archExt}${_compressExt} ] then echo "$1"/"$2"${_archExt}${_compressExt} fi done done } check_uid() { if [ "`whoami`" = root ] then uID=0 else if [ "$uID" = "" ] then uID=-1 fi fi export uID } check_auth() { needsauth=`getinfokey NeedsAuthorization | tr '[a-z]' '[A-Z]'` if [ "$needsauth" = YES ] then : ${action:="mess with"} check_uid if [ $uID != 0 ] then echo $me: you must be root to $action this package exit 7 fi fi } run_script() { _scriptoutput=${TMPDIR}/.`basename "$1"`.$$ _scriptexit=${TMPDIR}/.`basename "$1"`.exit.$$ (sh -c '$1' 2>&1; echo $? >$_scriptexit) | tee $_scriptoutput if [ ! -f $_scriptexit ] then _exitcode=1 else _exitcode=`cat $_scriptexit` fi if [ -z "`2>/dev/null cat $_scriptoutput`" ] then if [ $_exitcode -eq 0 ] then echo "OK." else echo "ERR (script exited with code $_exitcode)." fi fi /bin/rm -f $_scriptoutput $_scriptexit if [ $_exitcode -ne 0 ] then exit $_exitcode fi } ############################# Install Routine ################### install_package() { if [ ! -d "$package" ] then if [ ! -f "$package" ] then echo INSTALL ERROR : $package does not exist else echo INSTALL ERROR : $package does not look like an Installer package fi exit 2 else case "$package" in /*) ;; *) package=`/bin/pwd`"/$package" ;; esac #echo `/bin/pwd` this is pwd package_name=`basename "$package" .pkg` package_name=`basename "$package_name" .xpm` package_name=`basename "$package_name" .opm` # check for "new" format package if [ -d "$package/Contents" ] then package="$package/Contents/Resources" # echo " " # echo new format detected for $package fi package_info=`findinfo "$package" "$package_name"` #echo " " echo "PHASE: Found Package Info File: $package_info" src_location=`getinfokey SourceLocation` if [ -z "$src_location" ] then package_archive=`findarchive "$package" "$package_name"` if [ ! -f "$package_info" -o ! -f "$package_archive" ] then echo INSTALL ERROR : --lacks components required for its installation: $package_info or $package_archive exit 2 fi else case "$src_location" in /*) ;; *) # .. is needed because a relative src_location is relative # to package loc, not package contents src_location="$package/../$src_location" ;; esac if [ ! -f "$package_info" -o ! -d "$src_location" ] then echo INSTALL ERROR : lacks components required for its installation: $package_info or $src_location exit 2 fi fi package_sizes="$package/$package_name.sizes" package_bom="$package/$package_name.bom" if [ ! -f "$package_sizes" -o ! -f "$package_bom" ] then echo INSTALL WARNING: $package lacks components required by OSXPM echo INSTALL WARNING $package will be installed anyway, but should be corrected fi fi relocatable=`getinfokey Relocatable | tr '[a-z]' '[A-Z]'` # if [ "$relocatable" != YES -a ! -z "$installdir" ] # then # if [ $dwiw -eq 0 ] # then # echo $me: package $package is not relocatable # exit 3 # fi # fi default_location=`getinfokey DefaultLocation` : ${installdir:=$default_location} echo Package Installs in $default_location if [ -z "$src_location" ] then # this used to look for installer.app to use it's tar versions uses_gnutar=1 gnutar=`which gnutar` : ${gnutar:=/usr/bin/gnutar} if [ ! -x "$gnutar" ] then echo "INSTALL ERROR :couldn't execute \`$gnutar' for gnutar" #exit 7 # if theres no gnutar, use normal tar reg_tar=`which tar` : ${reg_tar:=/usr/bin/tar} if [ ! -x $reg_tar ]; then echo "INSTALL ERROR : couldn't execute \`$reg_tar' for tar" exit 7 else installer_tar=$reg_tar installer_tar_opts=p fi else installer_tar=$gnutar installer_tar_opts=p fi case "$installer_tar" in gnutar|*/gnutar) uses_gnutar=1 #echo Package Uses GNUTAR Archive ;; *) uses_gnutar=0 ;; esac uses_bzip=0 uses_pax=0 case "$package_archive" in *.Z) package_tar=$TMPDIR/`basename "$package_archive" .Z` ;; *.pax.gz) uses_pax=0 echo Package is a PAX Archive fixing the Archive... mkdir "/tmp" mkdir "/tmp/OSXPM" mkdir "/tmp/OSXPM/package" echo step 1... mv "$package_archive" /tmp/OSXPM (cd /tmp/OSXPM/package; /bin/pax -z -r -pe -f "../$package_name.pax.gz") echo step 2.... (cd /tmp/OSXPM/package; /usr/local/bin/gnutar jcf "/tmp/OSXPM/$package_name.tar.bz2" ".") # package_archive= "$package/$package_name.tar.bz2" echo step 3..... /bin/cp "/tmp/OSXPM/$package_name.tar.bz2" "$package" (cd /tmp; /bin/rm -rf OSXPM) package_archive=`findarchive "$package" "$package_name"` echo done fixing PAX archive. package_tar=$TMPDIR/`basename "$package/$package_name.tar.bz2" .bz2` uses_bzip=1 bzip2=`which bzip2` echo Package Now Uses Bzip2 compression and a GNUTAR Archive it will not break sym-links.. ;; *.gz) package_tar=$TMPDIR/`basename "$package_archive" .gz` echo Package Uses GZIP compression ;; *.bz2) package_tar=$TMPDIR/`basename "$package_archive" .bz2` uses_bzip=1 bzip2=`which bzip2` echo Package Uses Bzip2 compression and GNUTAR Archive if [ ! -x "$bzip2" ] then echo "INSTALL ERROR : I couldn't execute bzip2 tool. I am unable to install BZIP2 Archives bzip2 may not be installed" exit 7 fi ;; esac fi needs_copy=0 echo "PHASE: Finding Install Scripts (if any)" pre_install=`findfile pre_install "$package" "$package_name"` post_install=`findfile post_install "$package" "$package_name"` if [ -f "$package/preflight" ] then preflight="$package/preflight" fi if [ -f "$package/postflight" ] then postflight="$package/postflight" fi if [ -f "$pre_install" -o -f "$post_install" ] then if [ ! -z "$interactive" ] then echo "This package contains script that will be run during the installation..." echo -n "Do you want to run the scripts? [y] " read ans case "$ans" in y*|Y*|"") ;; *) if [ $dwiw -ne 0 ] then echo "INSTALL WARNING: Scripts not run. Installation may be incomplete." scripts=no else echo "INSTALL ERROR : Can not Run scripts.. Aborting installation." exit 0 fi ;; esac fi fi check_auth echo " " echo PHASE: Installing package: $package echo " " if [ ! -d $installdir ] then if [ -x /bin/mkdirs ] then /bin/mkdirs $installdir else /bin/mkdir -p $installdir fi code=$? if [ $code -ne 0 ] then exit $code fi fi if cd $installdir then : else echo INSTALL ERROR : cannot cd to $installdir exit 4 fi if [ -f "$preflight" ] then echo -n Running preflight script...\ `run_script "$preflight" "$package" "$installdir"` fi if [ -f "$pre_install" ] then echo -n Running pre_install script...\ `run_script "$pre_install" "$package" "$installdir"` fi if [ "$uID" = 0 ] then # we'll use ditto echo "" >/dev/null # bash does not like empty ifs fi if [ -z "$src_location" ] then echo -n PHASE: Extracting package contents...\ tarerrs=${TMPDIR}/.$me.tar.$$ trap "/bin/rm -f $tarerrs; bye" 1 2 3 15 if [ "$uses_pax" = 1 ] then /bin/pax -z -r -pe -f "$package_archive" 2>$tarerrs code=$? else if [ $needs_copy -eq 1 ] then echo -n Copying... \ cp "$package_archive" $TMPDIR /bin/rm -f "$package_tar" gzip -d $package_archive $installer_tar x${installer_tar_opts}f "$package_tar" 2>$tarerrs else if [ $uses_bzip -eq 1 ] then $installer_tar xvj${installer_tar_opts}f "$package_archive" 2>$tarerrs cat $tarerrs else if [ $uses_gnutar -eq 1 ] then $installer_tar xvz${installer_tar_opts}f "$package_archive" 2>$tarerrs cat $tarerrs else gzip -dc "$package_archive" | "$installer_tar" x${installer_tar_opts} - 2>$tarerrs fi fi fi code=$? /bin/rm -f "$package_tar" fi if [ $code -ne 0 ] then echo "INSTALL ERROR : (extract failed)." echo Log follows: cat $tarerrs /bin/rm -f $tarerrs exit $code else echo OK. fi /bin/rm -f $tarerrs else # we have a src path, so use ditto echo -n PHASE: Extracting package contents...\ dittoerrs=${TMPDIR}/.$me.ditto.$$ trap "/bin/rm -f $dittoerrs; bye" 1 2 3 15 if [ $verbose -ne 0 ] then dittoFlags=-V echo echo Source: $src_location echo Destination: `pwd` fi if [ -r "$package_bom" ] then dittoBOMFlags="-bom $package_bom" fi ditto $dittoFlags $dittoBOMFlags $src_location . 2>$dittoerrs code=$? if [ $code -ne 0 ] then echo "INSTALL ERROR : (ditto failed)." if [ $verbose -ne 0 ] then echo Ditto log follows: cat $dittoerrs fi /bin/rm -f $dittoerrs exit $code else echo OK. fi fi echo " " if [ -f "$post_install" ] then echo -n Running post_install script...\ `run_script "$post_install" "$package" "$installdir"` echo " " fi if [ -f "$postflight" ] then echo -n Running postflight script...\ `run_script "$postflight" "$package" "$installdir"` echo " " fi echo " " if [ $receipt -ne 0 ] then if [ ! -d $receiptsdir ] then 2>/dev/null mkdir -p $receiptsdir fi if [ ! -w $receiptsdir ] then receipt_failed=yes else if [ -d "$receiptsdir/$package_name.pkg" ] then if [ ! -w "$receiptsdir/$package_name.pkg" ] then receipt_failed=yes fi fi fi if [ -z "$receipt_failed" ] then echo -n "Creating receipt for package in $receiptsdir... " /bin/rm -rf "$receiptsdir/$package_name.pkg" if [ -x /bin/mkdirs ] then /bin/mkdirs "$receiptsdir/$package_name.pkg" /bin/mkdirs "$receiptsdir/$package_name.pkg/Contents" /bin/mkdirs "$receiptsdir/$package_name.pkg/Contents/Resources" else /bin/mkdir -p "$receiptsdir/$package_name.pkg" /bin/mkdir -p "$receiptsdir/$package_name.pkg/Contents" /bin/mkdir -p "$receiptsdir/$package_name.pkg/Contents/Resources" fi for f in info bom sizes tiff do if [ -f "$package/$package_name.$f" ] then cp "$package/$package_name.$f" "$receiptsdir/$package_name.pkg/Contents/Resources" fi done for f in software_version do if [ -f "$package/$f" ] then cp "$package/$f" "$receiptsdir/$package_name.pkg/Contents/Resources" else if [ -f "/System/Library/CoreServices/$f" ] then cp "/System/Library/CoreServices/$f" "$receiptsdir/Contents/Resources/$package_name.pkg" fi fi done if false then if [ ! -f "$receiptsdir/$package_name.pkg/Contents/Resources/$package_name.info" ] then firstinfo=`find "$package" -name \*.info -print | head -1` if [ ! -z "$firstinfo" ] then cp $firstinfo "$receiptsdir/$package_name.pkg/Contents/Resources" fi fi else find "$package" -name \*.lproj -print | while read d; # for d in `find $package -name \*.lproj -print` do (cd "$package"; gnutar cf - `basename $d`) | (cd "$receiptsdir/$package_name.pkg/Contents/Resources"; gnutar xpf -) done fi fi for f in pre_install post_install pre_delete post_delete depend backup do if [ -f "$package/$package_name.$f" ] then cp "$package/$package_name.$f" "$receiptsdir/$package_name.pkg/Contents/Resources" fi done for f in Welcome License ReadMe preflight postflight do for x in txt html rtf rtfd do if [ -f "$package/$f" ] then cp "$package/$f" "$receiptsdir/$package_name.pkg/Contents/Resources" fi if [ -f "$package/$f.$x" ] then cp "$package/$f.$x" "$receiptsdir/$package_name.pkg/Contents/Resources" fi if [ -d "$package/$f.$x" ] then cp -R "$package/$f.$x" "$receiptsdir/$package_name.pkg/Contents/Resources" fi done done dater=`/bin/date` echo "$installdir" >"$receiptsdir/$package_name.pkg/Contents/Resources/$package_name.location" echo "installed $dater" >$receiptsdir/$package_name.pkg/Contents/Resources/$package_name.status echo "installed by OSXPM shell component $version" >$receiptsdir/$package_name.pkg/Contents/Resources/$package_name.installer echo PHASE: Installing Receipt echo OK. else echo INSTALL WARNING: cannot write a receipt for the package in $receiptsdir fi echo "PHASE: INSTALL DONE" echo "Finished Installing $package_name..... OK." } ########################### Delete routine ######################## delete_package() { echo "DELETE PHASE : Starting to Delete $package" if [ ! -d "$package" ] then if [ ! -f "$package" ] then echo "ERROR: $package does not exist" else echo "ERROR: $package does not look like an Installer package" fi exit 2 else case "$package" in /*) ;; *) package=`/bin/pwd`/$package ;; esac package_base_dir=$package package_name=`basename $package .pkg` # check for "new" format package if [ -d '$package/Contents' ] then if [ -d '$package/Contents/Resources' ] then package='$package/Contents/Resources' echo DELETE PHASE : Standard directory format detected for $package else package='$package/Contents' echo DELETE PHASE : NON-Standard directory format detected for $package fi fi package_info=`findinfo $package $package_name` echo "DELETE PHASE : Package Information found in $package_info" echo "Looking for Installed Location... " echo " " package_location='$package/$package_name.location' if [ ! -r '$package_location' ] then # almost last try: /Library/Receipts/$package/$package_name.location package=/Library/Receipts/$package_name.pkg package_receipts_dir=$package package_location=$package/$package_name.location if [ ! -r $package_location ] then # last try: /Library/Receipts/$package/Contents/Resources/$package_name.log package=/Library/Receipts/$package_name.pkg/Contents/Resources package_location=$package/$package_name.location fi fi installdir=`cat $package_location` if [ -z "$installdir" -o ! -d "$installdir" ] then installdir=`getinfokey DefaultLocation` echo Warning: cannot figure out the installation directory for $package echo Warning: Assuming package $package was installed in its default location: $installdir else echo "This Package was Installed in Directory: $installdir" fi package_bom=$package/$package_name.bom check_auth deletewarning=`getinfokey DeleteWarning` if [ ! -z "$deletewarning" ] then echo This package has a delete Warning Message. echo WARNING: $deletewarning fi installonly=`getinfokey InstallOnly | tr '[a-z]' '[A-Z]'` # echo install only is $installonly if [ "$installonly" = YES ] then echo ERROR: This Package can only be installed, not deleted it claims to be required by the system. exit 4 fi if [ ! -r $package_bom ] then echo "ERROR: $package lacks a bill of materials and cannot be deleted" exit 3 fi pre_delete=`findfile pre_delete $package $package_name` post_delete=`findfile post_delete $package $package_name` echo "DELETE PHASE : Checking for scripts to run..." if [ -f "$pre_delete" ] then echo -n INFO: Running pre_delete script...\ run_script $pre_delete $package $installdir fi dirsfile=${TMPDIR}/.$me.$$ /bin/rm -f "$dirsfile" trap "/bin/rm -f $dirsfile; bye" 1 2 3 15 echo " " echo -n DELETE PHASE : Deleting package contents...\ for i in `$lsbom $package_bom | sed 's/[ ][ ]*[0-9].*//'` do what="$installdir"/"$i" if [ -d "$what" ] then echo " " echo "Now Deleting Package files in Dir $what" (cd "$what"; /bin/pwd) >>$dirsfile else 2>/dev/null /bin/rm -f "$what" code=$? if [ $code -ne 0 ] then echo "ERROR: (removing $what)" exit $code else echo "OK.." fi fi done echo " " for i in `sort -r $dirsfile` do if [ -d "$i" ] then 2>/dev/null /bin/rmdir "$i" fi done /bin/rm -f "$dirsfile" echo "DELETE PHASE : Done Deleting Contents.... OK." echo "Checking for Scripts to run... " echo " " if [ -f "$post_delete" ] then echo -n Running post_delete script...\ run_script $post_delete $package $installdir echo " OK.." else echo "No Scripts Found.... OK." fi echo " " echo DELETE PHASE : Deleting Receipt $package_base_dir /bin/rm -rf "$package" echo " OK." if [ -d "$package_base_dir" ] then /bin/rm -rf "$package_base_dir" fi echo "DELETE PHASE : DONE" echo "Deleted package $package_base_dir.... OK" fi } info_package() { if [ ! -d $package ] then if [ ! -f $package ] then echo $me: $package does not exist else echo $me: $package does not look like an Installer package fi exit 2 else case "$package" in /*) ;; *) package=`/bin/pwd`/$package ;; esac package_name=`basename "$package" .pkg` package_name=`basename "$package_name" .xpm` package_name=`basename "$package_name" .opm` package_info=`findinfo "$package" "$package_name"` if [ ! -f "$package_info" ] then echo $me: $package does not have a readable info file exit 3 fi title=`getinfokey Title` if [ ! -z "$title" ] then echo $title fi version=`getinfokey Version` if [ ! -z "$version" ] then echo Version: $version fi descr=`getinfokey Description` if [ ! -z "$descr" ] then echo Description: $descr fi if [ -r $package/software_version ] then echo Software Version: `cat $package/software_version` fi package_archive=`findarchive $package $package_name` case "$package_archive" in *.Z) if [ $verbose -ne 0 ] then echo Archive: Apparently made with BSD compress fi ;; *.gz) if [ $verbose -ne 0 ] then echo "Archive: Apparently made with GNU gzip" fi ;; esac src_location =`getinfokey SourceLocation` if [ ! -z "$src_location" ] then if [ $verbose -ne 0 ] then echo Non-archive: source located at $src_location fi fi status="not installed" if [ -r $package/$package_name.status ] then status=`cat $package/$package_name.status` if [ -r $package/$package_name.location ] then status="$status (in `cat $package/$package_name.location`)" fi else default_location=`getinfokey DefaultLocation` relocatable=`getinfokey Relocatable` if [ ! -z default_location ] then status="$status (will install in $default_location" if [ "$relocatable" = YES ] then status="$status by default" fi status="$status)" fi fi echo Status: `echo $status | sed 's/^\(.\).*/\1/' | tr '[a-z]' '[A-Z]'``echo $status | sed 's/.\(.*\)$/\1/'` fi } list_package() { if [ ! -d $package ] then if [ ! -f $package ] then echo $me: $package does not exist else echo $me: $package does not look like an Installer package fi exit 2 else case "$package" in /*) ;; *) package=`/bin/pwd`/$package ;; esac package_name=`basename $package .pkg` package_name=`basename $package_name .xpm` package_name=`basename $package_name .opm` package_bom=$package/$package_name.bom if [ ! -r $package_bom ] then echo $me: $package does not have a bill of materials exit 3 fi package_location=$package/$package_name.location if [ -r "$package_location" ] then installdir="`cat $package_location`" fi $lsbom $package_bom | \ sed -e 's/[ ][ ]*[0-9].*//' \ -e 's,^\./,,' -e "s,^,$installdir," fi } ${action}_package