#ident   "@(#)i.rbac 1.1     00/03/23 SMI"
# i.rbac
#
# Copyright (c) 2000, 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
