i.hosts revision cdf0c1d55d9b3b6beaf994835440dfb01aef5cf0
#!/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 2008 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% 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:
# <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
}
# 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