# 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 # ### pkg CAS merge BEGIN ### #!/bin/sh # # Copyright 2007 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # ident "@(#)i.hosts 1.2 07/04/21 SMI" # merge_ipnodes() { /usr/bin/nawk ' function getaddress (lineindex, entryfields) { split(lines[lineindex, LINE], entryfields); return entryfields[1]; } # # Return a string comprised of space delimited names. This function # handles continuation lines. # function getnames (lineindex, i, morenames, names, nameindex, n, comment) { i = lineindex; morenames = 0; names = ""; do { # We only want to look at the text before any trailing comment comment = (split(lines[i, LINE], linebreakdown, "#") == 2); # # Now split the stuff before the comment into individual names # Note that the names begin at the second entry, except # for continuation lines, for which the names begin at the # first entry: # [ ...] \ # [ ...] # n = split(linebreakdown[1], namesarray); for (nameindex = morenames ? 1 : 2; \ nameindex <= n && namesarray[nameindex] != "\\"; \ nameindex++) { names = names namesarray[nameindex] " "; } # # Check for a continuation line with more entries only if # the line didnt end in a comment. # morenames = 0; if (!comment && match(lines[i, LINE], /\\$/)) { i++; morenames = 1; } } while (morenames); return names; } # delete a line and any potential continuation of that line function deleteline (deleteindex, i, n, num, namestr, names, name) { lines[deleteindex, TYPE] = DELETED_LINE; # delete the continuation lines if present for (i = deleteindex + 1; lines[i, TYPE] == CONTINUATION_LINE; i++) lines[i, TYPE] = DELETED_LINE; # # If this entry was preceded by comment lines, delete # the comments. We assume the comments were only meaningful in the # context of the entry we just deleted. # for (i = deleteindex - 1; i>firstentryline && lines[i, TYPE] != ENTRY_LINE; i--) { if (lines[i, TYPE] == COMMENT_LINE) lines[i, TYPE] = DELETED_LINE; else break; } # Update the name cache namestr = getnames(deleteindex); if (namestr == "") return; num = split(namestr, names); for (i=1; i<=num; i++) { name = names[i]; for (n=1; n<=namecache[name,0]; n++) { if ( namecache[name,n] == deleteindex) namecache[name,n] = ""; } } } # # Count the number of lines that the line at lineindex spans. For example, # a line with one continuation line spans two lines. # function linespan (lineindex, i) { for (i = 1; lines[lineindex + i, TYPE] == CONTINUATION_LINE; i++); return i; } # # Take the line at "fromindex" and insert it at "toindex", bumping down # anything currently at and after "toindex" to make the line fit. # function insertline (fromindex, toindex, i, linecount, moveindex, namestr, names, name, m, num) { if (toindex >= fromindex) { print "ERROR, moving a line forward from index " fromindex \ " to " toindex; exit 1; } # # We do not re-arrange the array to move the lines around as it is expensive. # Instead we mark the moved line(s) as deleted and maintain # a array list of the appended line(s) in the target line. # MOVEDLINES is the index where we maitain refs/linenos. # deleteline(fromindex); # mark moved lines as deleted. # # If the target line at "toindex" spans several lines # then maintain the list in the last line. # toindex = toindex + linespan(toindex) - 2; moveindex = lines[toindex, MOVEDLINES, 0] linecount = linespan(fromindex); for (i = 0; i < linecount; i++) { moveindex++; lines[toindex, MOVEDLINES, moveindex] = fromindex + i; } lines[toindex, MOVEDLINES, 0] = moveindex; # Update the name cache namestr = getnames(fromindex); if (namestre == "") return; num = split(namestr, names); for (i=1; i<=num; i++) { name = names[i]; for(m=1; m<= namecache[name,0]; m++) { if (namecache[name,m] == fromindex) namecache[name,m] = toindex; } } } # lookup multiple lines with same host name using namecache function findname (name, curline, ln, lncount, matchline) { lncount = namecache[name, 0]; if (lncount < 2) return 0; for (ln = 1; ln <= lncount; ln++) { matchline = namecache[name, ln]; if (matchline == "") continue; if (matchline >= curline) continue; if (lines[matchline, TYPE] != ENTRY_LINE) continue; return matchline; } return 0; } function arelinesadjacent(a, b, i) { if (a + 1 == b) return 1; for (i = a + 1; i < b; i++) { if (lines[i, TYPE] == ENTRY_LINE) return 0; } return 1; } function printmovedlines(movedline, mcount, mline, m) { mcount = lines[movedline, MOVEDLINES, 0]; if (mcount > 0) { for (m = 1; m <= mcount; m++) { mline = lines[movedline, MOVEDLINES, m]; print lines[mline, LINE]; printmovedlines(mline); } } } function printmergeinfo() { "/usr/bin/date" | getline date; printf "\n#\n# Merged entries from ipnodes" \ " into hosts on <%s>\n",date; printf "# Backup files saved in /etc/inet/ directory:" \ " hosts.premerge, ipnodes.premerge\n#\n"; } function printhostsfile (i) { for (i = 1; i <= linecount; i++) { # Add comment about occurrence of merge. if (!cadded && (lines[i, TYPE] != COMMENT_LINE)) { printmergeinfo(); cadded = 1; } if (lines[i, TYPE] != DELETED_LINE) print lines[i, LINE]; # # Check for moved lines and print them as they should be # appended after this line. # printmovedlines(i); } } BEGIN { # line types. These are strings to help with debugging ENTRY_LINE = "entry"; COMMENT_LINE = "comment"; BLANK_LINE = "blankline"; DELETED_LINE = "deleted"; CONTINUATION_LINE = "continuation"; # indices to the data contained in the lines array TYPE = 1; LINE = 2; MOVEDLINES = 3; # index to array of append. line nos # regular expressions space = "[ \t]"; blanks = space "*"; blankline = "^" blanks "$"; comment = "^" blanks "#"; linecount = 0; tobecontinued = 0; } $0 ~ comment { linecount++; if (tobecontinued) { lines[linecount, TYPE] = CONTINUATION_LINE; tobecontinued = 0; } else { lines[linecount, TYPE] = COMMENT_LINE; } lines[linecount, LINE] = $0; next; } $0 ~ blankline { linecount++; if (tobecontinued) { lines[linecount, TYPE] = CONTINUATION_LINE; tobecontinued = 0; } else { lines[linecount, TYPE] = BLANK_LINE; } next; } { linecount++; if (firstentryline == "") firstentryline = linecount; if (tobecontinued) { lines[linecount, TYPE] = CONTINUATION_LINE; tobecontinued = 0; } else { lines[linecount, TYPE] = ENTRY_LINE; } lines[linecount, LINE] = $0; } /\\$/ { # # This matches a line that is continued on a subsequent line. It # doesnt match the continuation itself. We only need to flag that # this line is continued so that subsequent records can be tagged # as continuations. # tobecontinued = 1; } # # We now have an array of lines, one for each line of input. # END { # # Start by removing duplicate lines. We look for two lines # that are for the same address and have the same hostname # information (and listed in the same order). If we find such # a pair, we delete the second line. # for (i = 1; i <= linecount; i++) { if (lines[i, TYPE] != ENTRY_LINE) continue; # Extract the address from the line address = getaddress(i); # Extract the hostnames from the line namestr = getnames(i); if (namestr == "") continue; # # We keep an array of address indices. This lets us know # when we have duplicates without having to go back and # search the lines array. Since there can be multiple host # entries of the same address it is a two-dimensional array. # if (addrindex[address] == "") { # We have not seen this address before addrindex[address] = 1; # The second dimension stores the line number. addrindex[address, 1] = i; } else { # # We have a duplicate if hostname # information is identical. If duplicate # just mark the second entry as deleted. # for (l = 1; l <= addrindex[address]; l++) { if (namestr == \ getnames(addrindex[address, l])) { deleteline(i); break; } } # # If it was not deleted as a duplicate, add this # address to the index array. # if (l > addrindex[address]) { addrindex[address]++; addrindex[address, addrindex[address]] = i; } } # Maintain a name cache array. This is used # next to coalesce entries with same host # name. Line deletions and insert lines do # update the name cache. num = split(namestr, names); for (n = 1; n <= num; n++) { # 0 index node holds count of lines # with same host name namecache[names[n],0]++; namecache[names[n], namecache[names[n],0]] = i; } } # # We now need to bring together entries that contain the same name. # The hosts and ipnodes back-end requires that entries for the same # name but for different addresses be on adjacent lines. See # hosts(4). # for (i = 1; i <= linecount; i++) { if (lines[i, TYPE] != ENTRY_LINE) continue; namestr = getnames(i); if (namestr == "") continue; num = split(namestr, names); for (n = 1; n <= num; n++) { if ((nameindex = findname(names[n], i )) != 0) { if (!arelinesadjacent(nameindex, i)) { for (newindex = nameindex + 1; \ lines[newindex, TYPE] == \ CONTINUATION_LINE; \ newindex++); insertline(i, newindex); } break; } } } # Remove any trailing blank lines for (i = linecount; i > 0; i--) { type = lines[i, TYPE]; if (type == ENTRY_LINE || type == COMMENT_LINE) break; if (type == DELETED_LINE) continue; if (type == BLANK_LINE) deleteline(i); } printhostsfile(); }' $1 $2; } # # deliver_hosts: This function merges /etc/inet/hosts and /etc/inet/ipnodes # into a single hosts file (/etc/inet/hosts) only when ipnodes file exists in # the system. /etc/inet/ipnodes is now a symlink to /etc/inet/hosts. # deliver_hosts() { saved_ipnodes_file=$BASEDIR/etc/inet/ipnodes.hostsmerge temp_ipnodes_file=/tmp/ipnodes.hostsmerge temp_merged_file=/tmp/hosts.hostsmerged # if /etc/inet/hosts doesn't exist (fresh install) # then same action as 'i.preserve' i.e # copy default /etc/inet/hosts in place. if [ ! -f $dest ] ; then cp $src $dest fi if [ -f $saved_ipnodes_file ] ; then # Save copies before merge cp -pf $dest $BASEDIR/etc/inet/hosts.premerge cp -pf $saved_ipnodes_file $BASEDIR/etc/inet/ipnodes.premerge # Remove redundant header lines from ipnodes file /usr/bin/sed -e ' /^# CDDL HEADER START$/,/^# CDDL HEADER END$/ d /^# ident[ \t]*/ d /^# Copyright 1999 Sun Microsystems, Inc/ d /^# Use is subject to license terms/ d /^# Internet host table$/ d ' $saved_ipnodes_file | /usr/bin/sed -e ' /^#$/ { # Remove blank comment line pairs $!N /\n#$/ d }' > $temp_ipnodes_file merge_ipnodes $dest $temp_ipnodes_file \ > $temp_merged_file if [ $? -ne 0 ] ; then echo "$0 : failed to merge \ $saved_ipnodes_file with $dest" exit_status=2 continue fi mv -f $temp_merged_file $dest if [ $? -ne 0 ] ; then echo "$0 : failed to move \ $temp_merged_file to $dest" exit_status=2 continue fi # Create a copy of the merged hosts file to support # patch backout! cp -pf $BASEDIR/etc/inet/hosts \ $BASEDIR/etc/inet/hosts.postmerge fi } i_hosts() { # main exit_status=0 while read src dest; do dest_name=`basename "$dest"` case "${dest_name}" in "hosts") deliver_hosts ;; *) ;; esac done return $exit_status } ### pkg CAS merge END ### # "e" backout magic definitions BEGIN DIFF="/usr/bin/diff" OLD_PATCH_SEPARATOR="___Old_File_Diff_Separator_aBcDeFgHiJkLmN___OjK___5924894_6548915___" # "e" backout magic definitions END PATH=/usr/sadm/bin:$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) }'` ## # Once off change for u4 FKU SUNWcsr. Needed to work around a deficiency in pkg ## # level script templates causing them not to populate prototype file for undo pkg ## # correctly across object type/class changes. ## # ## # See full comments in checkinstall. true_class=$Class [ "$BASEDIR" = "/" ] && hosts_file=/etc/inet/hosts || hosts_file="$BASEDIR"/etc/inet/hosts ## # Fix section end. # 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 ## # Once off change for u4 FKU SUNWcsr. Needed to work around a deficiency in pkg ## # level script templates causing them not to populate prototype file for undo pkg ## # correctly across object type/class changes. ## # ## # See full comments in checkinstall. if [ "$hosts_file" = "$dst" -a -n "$IPNODES_IS_FILE" ]; then Class=preserve else Class=$true_class fi ## # Fix section end. 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. # $PKGPROTO -c $Class $dst=$FS_Path | \ $SED -e "s|^f |e |" \ -e "s|=$dst|=$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# # # "e" backout magic prepare BEGIN DST_EXIST="false" if [ "${PATCH_NO_UNDO}" != "true" ]; then if [ -f "${dst}" ] ; then DST_EXIST="true" else DST_EXIST="false" fi if [ "${DST_EXIST}" = "true" ] ; then #Save original file $MKDIR -p "`dirname $Build_Path`" $CP "${dst}" "$Build_Path.orig" $CP "${dst}" "$Build_Path.old_and_patch" fi fi # "e" backout magic prepare END ### call pkg CAS: BEGIN ### echo $src $dst | i_hosts ### call pkg CAS: END ### procIdCtr=`expr $procIdCtr + 1` # "e" backout magic store BEGIN if [ "${DST_EXIST}" = "true" ] ; then echo "${OLD_PATCH_SEPARATOR}" >> "$Build_Path.old_and_patch" $DIFF -C 3 "$Build_Path.orig" "$dst" >> "$Build_Path.old_and_patch" if [ "$?" -ne 0 ] ; then #Create a repository for original file and diff in the undo package $ECHO "e $Class $FS_Path.old_and_patch=$Build_Path.old_and_patch" >>$BUILD_DIR/prototype #Make sure this file does not remain on the system after the backout $ECHO "$FS_Path.old_and_patch" >>"$BO_Deletes" fi fi # "e" backout magic store END done # If additional operations are required for this package, place # those package-specific commands here. #XXXSpecial_CommandsXXX# # # Release the dynamic libraries # if [ -d "$LD_LIB_DIR" ]; then $RM -fr $LD_LIB_DIR fi exit 0