#!/bin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright 2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # 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: # <addr> <name> [<name> ...] \ # <name> [<name> ...] # 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 /^# Copyright .* 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 fi # Set correct permissions on hosts file chmod 0644 $dest } # main exit_status=0 while read src dest; do dest_name=`basename "$dest"` case "${dest_name}" in "hosts") deliver_hosts ;; *) ;; esac done exit $exit_status