diff options
Diffstat (limited to 'build/mac/install-pcp')
-rwxr-xr-x | build/mac/install-pcp | 837 |
1 files changed, 837 insertions, 0 deletions
diff --git a/build/mac/install-pcp b/build/mac/install-pcp new file mode 100755 index 0000000..8578789 --- /dev/null +++ b/build/mac/install-pcp @@ -0,0 +1,837 @@ +#! /bin/sh +# +# Copyright (c) 2003-2004 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +# Process a gendist-like idb file to collect, install, upgrade +# and remove files. +# +# The format of the IDB file is: +# +# The first line must contain a version string "# Version 1" +# +# All following lines must contain: +# d|f|h|s mode owner group destination source option +# +# One of: +# d directory to be created at destination (source ignored) +# f file to be installed from source to destination +# h hard link to be created at destination to source +# s soft link to be created at destination to source +# +# mode four digit mode bits +# owner file's owner +# group file's group +# destination create file/directory/link here +# source where file came from, or what link points to +# +# One of these options: replace, update, suggest, check or noupdate. +# +# Optionally, a os version number (uname -r), which ensures the file will only +# get installed on that os version, or a minimum and a maximum version number (inclusive). +# +# If the target does not exist, all options just install the file. +# +# If the target already exists, the options do the following: +# +# Option Type Operation +# --------- ---- --------------------------------------------------- +# replace d update permissions +# fhs remove and install replacement +# +# update d hs illegal option +# f move existing file to file.bak, install new file +# +# suggest d hs illegal option +# f install new file as file.new +# +# check dfhs do nothing +# +# noupdate dfhs do nothing +# +# +# If removing the target, the target is removed unless the option is +# noupdate. Directories will not be removed if they are not empty. +# +# Lines starting with '#' or empty lines are ignored. +# + +tmp=`mktemp -d /tmp/pcp.XXXXXXXXX` || exit 1 +prog=`basename $0` +status=1 +result=0 # non-fatal errors will set this to 1 +now=`date` +host=`hostname` +dump_mesg=false + +trap "rm -rf $tmp; exit \$status" 0 1 2 15 + +MKDIR=/bin/mkdir +CP=/bin/cp +MV=/bin/mv +LN=/bin/ln +RM=/bin/rm +RMDIR=/bin/rmdir +CHMOD=/bin/chmod +CHOWN=/usr/sbin/chown +CHGRP=/usr/bin/chgrp +DIRNAME=/usr/bin/dirname +TEE=/usr/bin/tee +JOIN=/usr/bin/join +SED=/usr/bin/sed +AWK=/usr/bin/awk +SORT=/usr/bin/sort +UNAME=/usr/bin/uname + +OS_VERSION=`$UNAME -r` + +_usage() +{ + cat << EOF +Usage: $prog [-cinpr] [-C os_version] [-l logfile] [-u origidb] [-R root] idbfile + + -l logfile Save important output to logfile + -n Show actions but do not execute + -R root Collect or install files using root rather + than WORKAREA or / + -v Verbose output + -C os_ver (Only used for collect phase) Only collect files which will be used for os_ver + + One of: + -c Collect source files into current dir + -i Install files from current directory + -p Parse and check idb is valid + -r Remove installed files. + -u origidb Remove files not in new IDB and then install +EOF + exit 1 +} + +_echo() +{ + if $log + then + echo "$*" | $TEE -a $logfile + else + echo "$*" + fi +} + +_error() +{ + _echo "$prog: Error: $*" | $TEE -a $mesg + dump_mesg=true + result=1 + exit 1 +} + +_warn() +{ + _echo "$prog: Warning: $*" | $TEE -a $mesg + dump_mesg=true +} + +_debug() +{ + sed -e "s/^/$prog: Debug: /" | $TEE -a $mesg + dump_mesg=true +} + +_mesg_head() +{ + _echo "$*" | $TEE -a $mesg +} + +_mesg() +{ + _echo "$prog: $*" | $TEE -a $mesg + dump_mesg=true +} + +_note() +{ + _echo "$prog: Note: $*" +} + +_cmd() +{ + $verbose && _echo "$*" + $* +} + +_get_numeric_version() +{ + echo $1 | awk ' + { n = split($0, v, "."); + if (n == 2) + v[2] = 0; + print (v[0] * 100) + (v[1] * 10) + v[2]; } + ' +} + +_test_version() +{ + local _min_numeric + local _max_numeric + local _os_numeric + local _min_version=$1 + local _max_version=$2 + local _temp_numeric + + if [ -z "$_min_version" -o "$_min_version" = "$OS_VERSION" ]; then + # No version, or exact match + return 0 + elif [ -z "$_max_version" ]; then + # No max and no exact match with min + return 1 + else + # max and min without exact match, so test range + _min_numeric=`_get_numeric_version $_min_version` + _max_numeric=`_get_numeric_version $_max_version` + _os_numeric=`_get_numeric_version $OS_VERSION` + if [ $_min_numeric -gt $_max_numeric ]; then + _temp_numeric=$_min_numeric + _min_numeric=$_max_numeric + _max_numeric=$_temp_numeric + fi + if [ $_os_numeric -ge $_min_numeric -a $_os_numeric -le $_max_numeric ]; then + return 0 + else + return 1 + fi + fi +} + +_remove_file() +{ + if _test_version $2 $3 + then + if [ -f $root/$1 ] + then + if ! _cmd $RM $root/$1 + then + _warn "Unable to remove $root/$1, continuing" + fi + else + _note "$root/$1 does not exist for removal, skipping" + fi + fi +} + +_remove_dir() +{ + if _test_version $2 $3 + then + if [ -d $root/$1 ] + then + if ! _cmd $RMDIR $root/$1 + then + _warn "Unable to remove $root/$1, continuing" + fi + else + _note "$root/$1 does not exist for removal, skipping" + fi + fi +} + +# $1 is path to file in tree +# $2 is path to the file in the workarea +# path is created in current directory and the file is copied +# +_collect_file() +{ + file=$root/$1 + dir=`$DIRNAME $2` + + if [ ! -z "$collect_version" ]; then + # override the OS_VERSION, which is used in test_version() + OS_VERSION=$collect_version + if ! _test_version $3 $4 + then + # version doesn't match, so skip it + return + fi + fi + + if [ ! -r $file ] + then + _error "Unable to find $file" + fi + + if [ ! -d $dir ] + then + if _cmd $MKDIR -p $dir + then + : + else + _error "Unable to create $dir" + fi + fi + + if ! _cmd $CP $file $2 + then + _error "Unable to copy $file" + fi +} + +# Takes the idb line +# d|f|h|s mode owner group destination source option +_install_file() +{ + type=$1 + target=$root/$5 + source=$6 + option=$7 + chopts= + + if _test_version $8 $9 + then + # Set to true if we need to set mode, uid and gid + installed=false + + if [ $type = "d" ] + then + if [ ! -d $target ] + then + if _cmd $MKDIR -p $target + then + installed=true + else + _error "Unable to create dir $target" + fi + elif [ -f $target ] + then + if [ $option = "replace" ] + then + if ! _cmd $RM $target + then + _error "Unable to replace existing $target with directory" + fi + if _cmd $MKDIR -p $target + then + installed=true + else + _error "Unable to create dir $target" + fi + else + _error "$target exists but is not a directory" + fi + fi + elif [ $type = "f" ] + then + if [ ! -f $source ] + then + echo "cwd=`pwd`" >$tmp/debug + echo "source dir=`dirname $source`" >>$tmp/debug + ls -l `dirname $source` >>$tmp/debug + _debug <$tmp/debug + _error "Unable to find file ($source) for $target" + fi + if [ -f $target ] + then + case $option in + replace) + if ! _cmd $RM $target + then + _warn "Unable to remove existing $target, continuing" + fi + if _cmd $MV $source $target + then + installed=true + else + _error "Unable to install $target" + fi + ;; + update) + if ! _cmd $CP $target $target.bak + then + _warn "Unable to copy existing $target to $target.bak, continuing" + fi + + if ! _cmd $RM $target + then + _warn "Unable to remove existing $target, continuing" + fi + + if _cmd $MV $source $target + then + _mesg "Update to $target installed, original backed up at $target.bak" + installed=true + else + if _cmd $CP $target.bak $target + then + _error "Unable to install $target, original moved back" + else + _error "Unable to install $target, original now $target.bak" + fi + fi + ;; + + suggest) + if _cmd $MV $source $target.new + then + _mesg "Suggested update to $target installed at $target.new" + installed=true + target=$target.new + else + _warn "Unable to install $target as $target.new, continuing" + fi + ;; + + check) + # Nothing to do if file exists + ;; + noupdate) + # Nothing to do if file exists + ;; + *) + _error "Unsupported IDB option ($option) for $target" + ;; + esac + elif [ -d $target ] + then + _error "$target exists but is a directory" + else + if _cmd $MV $source $target + then + installed=true + else + _error "Unable to install $target" + fi + fi + elif [ $type = "h" ] + then + if [ -f $target ] + then + case $option in + replace) + if ! _cmd $RM $target + then + _warn "Unable to remove existing $target, continuing" + fi + if _cmd $LN $root/$source $target + then + installed=true + else + _error "Unable to install $target" + fi + ;; + update) + _error "Illegal update option for hard link $target to $root/$source" + ;; + suggest) + _error "Illegal suggest option for hard link $target to $root/$source" + ;; + check) + # Nothing to do if file exists + ;; + noupdate) + # Nothing to do if file exists + ;; + esac + elif [ -d $target ] + then + _error "$target exists but is a directory" + else + if _cmd $LN $root/$source $target + then + installed=true + else + _error "Unable to install $target" + fi + fi + elif [ $type = "s" ] + then + chopts="-h" + if [ -f $target ] + then + case $option in + replace) + if ! _cmd $RM $target + then + _warn "Unable to remove existing $target, continuing" + fi + if _cmd $LN -s -f $root/$source $target + then + installed=true + else + _error "Unable to install $target" + fi + ;; + update) + _error "Illegal update option for soft link $target to $root/$source" + ;; + suggest) + _error "Illegal suggest option for soft link $target to $root/$source" + ;; + check) + # Nothing to do if file exists + ;; + noupdate) + # Nothing to do if file exists + ;; + esac + elif [ -d $target ] + then + _error "$target exists but is a directory" + else + if _cmd $LN -s -f $root/$source $target + then + installed=true + else + _error "Unable to install $target" + fi + fi + else + _error "Unrecognised file type ($type)" + fi + + if $installed + then + if [ "$type" != "s" ] ; then + if ! _cmd $CHMOD $2 $target + then + _warn "Unable to change mode of $target to $2, continuing" + fi + fi + if ! _cmd $CHOWN $chopts $3 $target + then + _warn "Unable to change ownership of $target to $3, continuing" + fi + if ! _cmd $CHGRP $chopts $4 $target + then + _warn "Unable to change group of $target to $4, continuing" + fi + fi + fi +} + +_remove() +{ + # + # Directories are removed after all files are removed + # If directory is not empty, default is to ignore and move on + # + sort -k1r,1 -k5r,5 $1 < $1 \ + | $AWK >> $tmp/cmds ' +$7 == "noupdate" { next } +$1 == "d" { printf("_remove_dir %s %s %s\n", $5, $8, $9); next } + { printf("_remove_file %s %s %s\n", $5, $8, $9) } +' +} + +_install() +{ + # + # Directories are installed before all files are installed + # Links are installed after normal files + # + sed < $tmp/idb >> $tmp/cmds -e 's/^/_install_file /' +} + +show=false +collect=false +install=false +remove=false +upgrade=false +verbose=false +logfile= +log=false +mesg=$tmp/mesg +root= +collect_version= +parse=false +if set -- `getopt cC:il:nprR:u:v $*` +then + for i in $* + do + case $i in + -C) + $install && _usage + $remove && _usage + $upgrade && _usage + $parse && _usage + collect=true + collect_version=$2 + echo "Collecting for $collect_version" + shift 2;; + -c) + $install && _usage + $remove && _usage + $upgrade && _usage + $parse && _usage + collect=true + shift;; + -i) + $collect && _usage + $remove && _usage + $upgrade && _usage + $parse && _usage + install=true + shift;; + -l) + log=true + logfile=$2 + touch $logfile + if [ ! -w $logfile ] + then + log=false + _warn "Unable to open logfile ($logfile)" + fi + shift 2;; + -n) + show=true + shift;; + -p) + $collect && _usage + $install && _usage + $upgrade && _usage + $remove && _usage + parse=true + shift;; + -r) + $collect && _usage + $install && _usage + $upgrade && _usage + $parse && _usage + remove=true + shift;; + -R) + root=$2 + shift 2;; + -u) + $collect && _usage + $install && _usage + $remove && _usage + $parse && _usage + upgrade=true + oidb=$2 + shift 2;; + -v) + verbose=true + shift;; + --) + break + shift;; + esac + done + shift +else + _usage +fi + +if [ $# -ne 1 ] +then + _usage +fi + +idb=$1 + +if [ ! -r $idb ] +then + echo "$prog: Unable to open $idb" + exit 1 +fi + +$AWK < $idb > $tmp/err -v prog=$prog -v idb=$idb ' +BEGIN { fail = 0; version = 0; entries = 0 } + +NR == 1 && ( NF != 3 || $1 != "#" || $2 != "Version" || $3 != "1" ) { + printf("%s: Error: Line %d of %s has an invalid version string (%s)\n", + prog, NR, idb, $0); + fail = 1; + exit(1); +} + +NR == 1 { version = 1; next } +/^#/ { next } +NF == 0 { next } + +NF != 7 && NF != 8 && NF != 9 { + printf("%s: Error: Line %d of %s does not have 7 or 8 or 9 entries (%d)\n", + prog, NR, idb, NF); + fail = 1; + exit(1); +} + +$1 != "d" && $1 != "f" && $1 != "h" && $1 != "s" { + printf("%s: Error: Line %d of %s has invalid file type (%s)\n", + prog, NR, idb, $1); + fail = 1; + exit(1); +} + +$7 != "replace" && $7 != "update" && $7 != "suggest" && $7 != "check" && $7 != "noupdate" { + printf("%s: Error: Line %d of %s has invalid file option (%s)\n", + prog, NR, idb, $7); + fail = 1; + exit(1); +} + +$1 != "f" && ($7 == "update" || $7 == "suggest") { + printf("%s: Error: Line %d of %s has unsupported combination of %s and %s\n", + prog, NR, idb, $1, $7); + fail = 1; + exit(1); +} + +$5 ~ "^/" { + printf("%s: Error: Line %d of %s does not have relative path (%s)\n", + prog, NR, idb, $5); + fail = 1; + exit(1); +} + +$6 ~ "^/" { + printf("%s: Error: Line %d of %s does not have relative path (%s)\n", + prog, NR, idb, $6); + fail = 1; + exit(1); +} + + { entries++ } + +END { + if (!fail) { + if (!version) { + printf("%s: Error: No version string in %s\n", + prog, idb); + exit(1); + } + if (!entries) { + printf("%s: Error: No entries in %s\n", + prog, idb); + exit(1); + } + } +} +' + +if [ -s $tmp/err ] +then + echo "$prog: Invalid IDB file detected:" + cat $tmp/err + exit 1 +elif $parse +then + status=0 + exit +fi + +$RM -f $tmp/dirs $tmp/cmds $tmp/files $tmp/idb $tmp/tidb +touch $tmp/dirs $tmp/cmds $tmp/files + +sed < $idb -e '/^#/d' -e '/^$/d' \ +| $SORT -k1,1 -k5,5 \ +> $tmp/idb + +cat << EOF > $mesg + +======================================================================= +Installation issues that may require further investigation: +EOF + +if $remove +then + + _mesg_head "Removing all installed PCP files" + _mesg_head "Date: "`date` + _mesg_head "User: $USER" + _remove $tmp/idb + + +# +# Collect files out of source tree and copy here using the same +# directory structure +# + +elif $collect +then + _mesg_head "Collecting all files to be included in package" + _mesg_head "Date: "`date` + _mesg_head "User: $USER" + + [ "X$root" = "X" ] && root=$WORKAREA + + if [ "X$root" = "X" ] + then + _error "Neither \$WORKAREA or -R were set" + exit 1 + fi + + $AWK < $tmp/idb > $tmp/cmds ' +$1 == "f" { printf("_collect_file %s %s %s %s\n", $5, $6, $8, $9) }' + +# +# Install files assuming that they do not already exist +# +elif $install +then + _mesg_head "Installing PCP files" + _mesg_head "Date: "`date` + _mesg_head "User: $USER" + _install + +elif $upgrade +then + _mesg_head "Upgrade of PCP files" + _mesg_head "Date: "`date` + _mesg_head "User: $USER" + + # Determine which files need to be removed since they + # are not in the new set of files. Files which are + # in both sets will be upgraded depending on their + # IDB option. + + if [ ! -r $oidb ] + then + _warn "Upgrade in progress but cannot find old inventory ($oidb) to delete files that are no longer required" + else + sed < $oidb -e '/^#/d' -e '/^$/d' \ + | $SORT -k1,1 -k5,5 \ + > $tmp/oidb + + # explicity name all fields so that the extra os_version field doesnt confuse join + $JOIN -t' ' -v 2 -1 5 -2 5 $tmp/idb $tmp/oidb > $tmp/extra + if [ -s $tmp/extra ] + then + _remove $tmp/extra + else + _note "No files need to be removed before upgrade" + fi + fi + _install + +else + echo "$prog: Must select one of -r, -c, -i or -u options" + exit 1 +fi + +if $show +then + cat $tmp/cmds +else + $verbose && cat $tmp/cmds +. $tmp/cmds +fi + +if $dump_mesg +then + $log && cat $mesg >> $logfile + cat $mesg +fi + +status=$result |