# This class action script copies the files being replaced
# into a package being constructed in $BUILD_DIR. This class
# action script is only appropriate for regular files that
# are installed by simply copying them into place.
#
# For special package objects such as edittable files, the patch
# producer must supply appropriate class action scripts.
#
# directory format options.
#
#       @(#)i.script 1.15 04/04/08 SMI
#
# Copyright (c) 2004 by Sun Microsystems, Inc.
# All rights reserved
#

# class action script for "rbac" class files
# installed by pkgadd
#
# Files in "rbac" class:
#
# /etc/security{prof_attr,exec_attr,auth_attr}
# /etc/user_attr
#
#  Allowable exit codes
#
# 0 - success
# 2 - warning or possible error condition. Installation continues. A warning
#     message is displayed at the time of completion. 
#

tmp_dir=/tmp

rm_cmd=/usr/bin/rm
mv_cmd=/usr/bin/mv
sed_cmd=/usr/bin/sed
nawk_cmd=/usr/bin/nawk
dirname_cmd=usr/bin/dirname
basename_cmd=/usr/bin/basename

# $1 is the type
# $2 is the "old/existing file"
# $3 is the "new (to be merged)" file
# $4 is the output file
# returns 0 on success
# returns 2 on failure if nawk fails with non-zero exit status
#
dbmerge() {

#!/usr/bin/nawk -f
#
#       dbmerge type=[auth|prof|user|exec] old-file new-file
#
#       Merge two versions of an RBAC database file. The output
#       consists of the lines from the new-file, while preserving
#       user customizations in the old-file. Specifically, the
#       keyword/value section of each record contains the union
#       of the entries found in both files. The value for each
#       keyword is the value from the new-file, except for three
#       keywords ("auths", "profiles", "roles") where the values
#       from the old and new files are merged.
#
# 

    $nawk_cmd  \
\
'BEGIN { \
        FS=":" \
} \
 \
/^#/ { \
        if (NR != FNR) \
                print ; \
        continue; \
} \
 \
type == "auth" { \
        key = $1 ":" $2 ":" $3 ; \
        if (NR == FNR) { \
                short_comment[key] = $4 ; \
		long_comment[key] = $5; \
                record[key] = $6; \
        } \
        else { \
		if ( $4 != "" ) { \
		    short_comment[key] = $4 ; \
		} \
		if ( $5 != "" ) { \
		    long_comment[key] =  $5 ; \
		} \
                print key ":" short_comment[key] ":" long_comment[key] ":" \
						merge_attrs(record[key], $6); \
                delete record[key]; \
        } \
} \
 \
type == "prof" { \
        key = $1 ":" $2 ":" $3 ; \
        if (NR == FNR) { \
                comment[key] = $4; \
                record[key] = $5; \
        } \
        else { \
		if ( $4 != "" ) { \
		    comment[key] = $4 ; \
		} \
                print key ":" comment[key] ":" merge_attrs(record[key], $5); \
                delete record[key]; \
        } \
} \
 \
type == "exec" { \
        key = $1 ":" $2 ":" $3 ":" $4 ":" $5 ":" $6 ; \
        if (NR == FNR) \
                record[key] = $7; \
        else { \
                print key ":" merge_attrs(record[key], $7); \
                delete record[key]; \
        } \
} \
 \
type == "user" { \
        key = $1 ":" $2 ":" $3 ":" $4 ; \
        if (NR == FNR) \
                record[key] = $5; \
        else { \
                print key ":" merge_attrs(record[key], $5); \
                delete record[key]; \
        } \
} \
 \
END { \
        for (key in record) { \
                if (type == "prof") { \
                        print key ":" comment[key] ":" record[key]; \
		} else \
		if (type == "auth") { \
                        print key ":" short_comment[key] ":"  \
				long_comment[key] ":" record[key]; \
		} else \
                        print key ":" record[key]; \
        } \
} \
 \
function merge_attrs(old, new, cnt, new_cnt, i, j, list, new_list, keyword) \
{ \
        cnt = split(old, list, ";"); \
        new_cnt = split(new, new_list, ";"); \
        for (i = 1; i <= new_cnt; i++) { \
                keyword = substr(new_list[i], 1, index(new_list[i], "=")-1); \
                for (j = 1; j <= cnt; j++) { \
                        if (match(list[j], "^" keyword "=")) { \
                                list[j] = merge_values(keyword, list[j], \
                                        new_list[i]); \
                                break; \
                        } \
                } \
                if (j > cnt) \
                        list[++cnt] = new_list[i]; \
        } \
 \
        return unsplit(list, cnt, ";"); \
} \
 \
function merge_values(keyword, old, new, cnt, new_cnt, i, j, list, new_list) \
{ \
        if (keyword != "auths" && keyword != "profiles") \
                return new; \
 \
        cnt = split(substr(old, length(keyword)+2), list, ","); \
        new_cnt = split(substr(new, length(keyword)+2), new_list, ","); \
        for (i = 1; i <= new_cnt; i++) { \
                for (j = 1; j <= cnt; j++) { \
                        if (list[j] == new_list[i]) \
                                break; \
                } \
                if (j > cnt) \
                        list[++cnt] = new_list[i]; \
        } \
 \
        return keyword "=" unsplit(list, cnt, ","); \
} \
 \
function unsplit(list, cnt, delim, str) \
{ \
        str = list[1]; \
        for (i = 2; i <= cnt; i++) \
                str = str delim list[i]; \
        return str; \
}' \
    type=$1 $2 $3 > $4

    return $?
}

# $1 is the merged file
# $2 is the target file
#
commit() {
    $mv_cmd $1 $2
    return $?
}

outfile=""
type=""
set_type_and_outfile() {

    # Assumes basename $1 returns one of
    # prof_attr, exec_attr, auth_attr, or user_attr
    #
    fname=`$basename_cmd $1`
    type=`echo $fname | $sed_cmd -e s'/^\([a-z][a-z]*\)_attr$/\1/' `
    case "$type" in
    "prof"|"exec"|"user"|"auth") ;;
    *) return 2 ;;
    esac

    outfile=$tmp_dir/rbac_${PKGINST}_${fname}_merge

    return 0
}

cleanup() {
    $rm_cmd -f $outfile
    return 0
}

exit_status=0

# main

while read newfile oldfile ; do

    if [ ! -f $oldfile ] ; then
        cp $newfile $oldfile
    else
        set_type_and_outfile $newfile
        if [ $? -ne 0 ]; then
    	    echo \
	    "$0 : $newfile not one of prof_attr, exec_attr, auth_attr, user_attr"
	    exit_status=2
	    continue
        fi

        dbmerge $type $oldfile $newfile $outfile
        if [ $? -ne 0 ]; then
	    echo "$0 : failed to merge $newfile with $oldfile"
	    cleanup
	    exit_status=2
	    continue
        fi

        commit $outfile $oldfile 
        if [ $? -ne 0 ]; then
	    echo "$0 : failed to mv $outfile to $2"
	    cleanup
	    exit_status=2
	    continue
        fi

        cleanup
    fi
done

if [ "$1" = "ENDOFCLASS" ]; then
    exit 0
fi

exit $exit_status

PATH=/usr/sadm/bin:$PATH
PATH="/usr/bin:/usr/sbin:${PATH}"

ECHO="/usr/bin/echo"
SED="/usr/bin/sed"
PKGPROTO="/usr/bin/pkgproto"
EXPR="/usr/bin/expr"	# used by dirname
MKDIR="/usr/bin/mkdir"
CP="/usr/bin/cp"
RM="/usr/bin/rm"
MV="/usr/bin/mv"
KSH="/usr/bin/ksh"

recovery="no"
Pn=$$
procIdCtr=0

CMDS_USED="$KSH $ECHO $SED $PKGPROTO $EXPR $MKDIR $CP $RM $MV"
LIBS_USED=""

if [ "$PKG_INSTALL_ROOT" = "/" ]; then
	PKG_INSTALL_ROOT=""
fi

# Check to see if this is a patch installation retry.
if [ "$INTERRUPTION" = "yes" ]; then
	if [ -d "$PKG_INSTALL_ROOT/var/tmp/$SUNW_PATCHID.$PKGINST" ] || [ -d "$PATCH_BUILD_DIR/$SUNW_PATCHID.$PKGINST" ]; then
		recovery="yes"
	fi
fi

if [ -n "$PATCH_BUILD_DIR" -a -d "$PATCH_BUILD_DIR" ]; then
	BUILD_DIR="$PATCH_BUILD_DIR/$SUNW_PATCHID.$PKGINST"
else
	BUILD_DIR="$PKG_INSTALL_ROOT/var/tmp/$SUNW_PATCHID.$PKGINST"
fi

FILE_DIR=$BUILD_DIR/files
RELOC_DIR=$FILE_DIR/reloc
ROOT_DIR=$FILE_DIR/root
BO_Deletes=$FILE_DIR/deletes
PROGNAME=`basename $0`

if [ "$PATCH_PROGRESSIVE" = "true" ]; then
	PATCH_NO_UNDO="true"
fi

# Since this is generic, figure out the class.
Class=`echo $PROGNAME | nawk ' { print substr($0, 3)  }'`

# Since this is an update, $BASEDIR is guaranteed to be correct
BD=${BASEDIR:-/}

cd $BD

#
# First, figure out the dynamic libraries that can trip us up.
#
if [ -z "$PKG_INSTALL_ROOT" ]; then
	if [ -x /usr/bin/ldd ]; then
		LIB_LIST=`/usr/bin/ldd $CMDS_USED | sort -u | nawk '
			$1 ~ /\// { continue; }
			{ printf "%s ", $3 } '`
	else
		LIB_LIST="/usr/lib/libc.so.1 /usr/lib/libdl.so.1 /usr/lib/libw.so.1 /usr/lib/libintl.so.1 /usr/lib/libgen.so.1 /usr/lib/libadm.so.1 /usr/lib/libelf.so.1"
	fi
fi

#
# Now read the list of files in this class to be replaced. If the file
# is already in place, then this is a change and we need to copy it
# over to the build directory if undo is allowed. If it's a new entry
# (No $dst), then it goes in the deletes file for the backout package.
#

LD_LIB_DIR=$PKG_INSTALL_ROOT/var/tmp/LDLIB.$$

procIdCtr=0
while read src dst; do
	if [ -z "$PKG_INSTALL_ROOT" ]; then
		Chk_Path=$dst
		for library in $LIB_LIST; do
			if [ "$Chk_Path" = "$library" ]; then
				if [ ! -d "$LD_LIB_DIR" ]; then
					$MKDIR $LD_LIB_DIR
				fi
				$CP $dst $LD_LIB_DIR
				LD_LIBRARY_PATH=$LD_LIB_DIR
				export LD_LIBRARY_PATH
			fi
		done
	fi

	if [ "$PATCH_PROGRESSIVE" = "true" ]; then
		# If this is being used in an old-style patch, insert
		# the old-style script commands here.

		#XXXOld_CommandsXXX#
		echo >/dev/null	# dummy
	fi

	if [ "${PATCH_NO_UNDO}" != "true" ]; then
		#
		# Here we construct the path to the appropriate source
		# tree for the build. First we try to strip BASEDIR. If
		# there's no BASEDIR in the path, we presume that it is
		# absolute and construct the target as an absolute path
		# by stripping PKG_INSTALL_ROOT. FS_Path is the path to
		# the file on the file system (for deletion purposes).
		# Build_Path is the path to the object in the build
		# environment.
		#
		# The following rootPath variable accounts for a BASEDIR
		# that is used as a regular variable within the path and
		# not as a variable that a path needs to be relocated to.

		rootPath=`$ECHO $src | $SED s@"$INST_DATADIR/$PKGINST"@@ | \
			nawk -F/ '{print $2}'`
		if [ "$rootPath" = "root" ]; then
			FS_Path=$dst
		elif [ "$BD" = "/" ]; then
			FS_Path=`$ECHO $dst | $SED s@"$BD"@@`
		else
			FS_Path=`$ECHO $dst | $SED "s|^$BD/||"`
		fi

		# If it's an absolute path the attempt to strip the
		# BASEDIR will have failed.
		if [ "$dst" = "$FS_Path" ]; then
			if [ -z "$PKG_INSTALL_ROOT" ]; then
				FS_Path=$dst
				Build_Path="$ROOT_DIR$dst"
			else
				Build_Path="$ROOT_DIR"`echo $dst | \
				    $SED "s|$PKG_INSTALL_ROOT||"`
				FS_Path=`echo $dst | \
				    $SED "s|$PKG_INSTALL_ROOT||"`
			fi
		else
			Build_Path="$RELOC_DIR/$FS_Path"
		fi

		if [ -f "$dst" ]; then	# If this is replacing something
			cd $FILE_DIR
			#
			# Construct the prototype file entry. We replace
			# the pointer to the filesystem object with the
			# build directory object.
			#
		        #
                        echo "e rbac $FS_Path=$Build_Path ? ? ?" >> \
                                $BUILD_DIR/prototype

			# Now copy over the file
			if [ "$recovery" = "no" ]; then
				DirName=`dirname $Build_Path`
				$MKDIR -p $DirName
				$CP -p $dst $Build_Path
			else
				# If this file is already in the build area skip it
				if [ -f "$Build_Path" ]; then
					cd $BD
					continue
				else
					DirName=`dirname $Build_Path`
					if [ ! -d "$DirName" ]; then
						$MKDIR -p $DirName
					fi
					$CP -p $dst $Build_Path
				fi
			fi

			cd $BD
		else	# It's brand new
			$ECHO $FS_Path >> $BO_Deletes
		fi
	fi

	# If special processing is required for each src/dst pair,
	# add that here.
	#
	#XXXSpecial_CommandsXXX#
	#

	$CP -p $src $dst.$$$procIdCtr
	if [ $? -ne 0 ]; then
		$RM $dst.$$$procIdCtr 1>/dev/null 2>&1
	else
		$MV -f $dst.$$$procIdCtr $dst
		for library in $LIB_LIST; do
			if [ "$library" = "$dst" ]; then
				unset $LD_LIBRARY_PATH
			fi
		done
	fi
	procIdCtr=`expr $procIdCtr + 1`
done
 
# If additional operations are required for this package, place
# those package-specific commands here.

#XXXSpecial_CommandsXXX#

exit 0

