Home | History | Annotate | Download | only in common_files
      1 #!/bin/sh
      2 #
      3 # CDDL HEADER START
      4 #
      5 # The contents of this file are subject to the terms of the
      6 # Common Development and Distribution License (the "License").
      7 # You may not use this file except in compliance with the License.
      8 #
      9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
     10 # or http://www.opensolaris.org/os/licensing.
     11 # See the License for the specific language governing permissions
     12 # and limitations under the License.
     13 #
     14 # When distributing Covered Code, include this CDDL HEADER in each
     15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     16 # If applicable, add the following below this CDDL HEADER, with the
     17 # fields enclosed by brackets "[]" replaced with your own identifying
     18 # information: Portions Copyright [yyyy] [name of copyright owner]
     19 #
     20 # CDDL HEADER END
     21 #
     22 #
     23 # Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24 # Use is subject to license terms.
     25 #
     26 # ident	"%Z%%M%	%I%	%E% SMI"
     27 #
     28 
     29 merge_ipnodes() {
     30 
     31 	/usr/bin/nawk '
     32 	function getaddress (lineindex,  entryfields) {
     33 		split(lines[lineindex, LINE], entryfields);
     34 		return entryfields[1];
     35 	}
     36 
     37 	#
     38 	# Return a string comprised of space delimited names.  This function
     39 	# handles continuation lines.
     40 	#
     41 	function getnames (lineindex,  i, morenames, names, nameindex, n, comment) {
     42 		i = lineindex;
     43 		morenames = 0;
     44 		names = "";
     45 		do {
     46 			# We only want to look at the text before any trailing comment
     47 			comment = (split(lines[i, LINE], linebreakdown, "#") == 2);
     48 			#
     49 			# Now split the stuff before the comment into individual names
     50 			# Note that the names begin at the second entry, except
     51 			# for continuation lines, for which the names begin at the
     52 			# first entry:
     53 			# <addr> <name> [<name> ...] \
     54 			#     <name> [<name> ...]
     55 			#
     56 			n = split(linebreakdown[1], namesarray);
     57 			for (nameindex = morenames ? 1 : 2; \
     58 			    nameindex <= n && namesarray[nameindex] != "\\"; \
     59 			    nameindex++) {
     60 				names = names namesarray[nameindex] " ";
     61 			}
     62 
     63 			#
     64 			# Check for a continuation line with more entries only if
     65 			# the line didnt end in a comment.
     66 			#
     67 			morenames = 0;
     68 			if (!comment && match(lines[i, LINE], /\\$/)) {
     69 				i++;
     70 				morenames = 1;
     71 			}
     72 		} while (morenames);
     73 
     74 		return names;
     75 	}
     76 
     77 	# delete a line and any potential continuation of that line
     78 	function deleteline (deleteindex,  i, n, num, 
     79 			namestr, names, name) {
     80 
     81 		lines[deleteindex, TYPE] = DELETED_LINE;
     82 		# delete the continuation lines if present
     83 		for (i = deleteindex + 1; lines[i, TYPE] == CONTINUATION_LINE; i++)
     84 			lines[i, TYPE] = DELETED_LINE;
     85 		#
     86 		# If this entry was preceded by comment lines, delete
     87 		# the comments.  We assume the comments were only meaningful in the
     88 		# context of the entry we just deleted.
     89 		#
     90 		for (i = deleteindex - 1; i>firstentryline && lines[i, TYPE] != ENTRY_LINE; i--) {
     91 			if (lines[i, TYPE] == COMMENT_LINE)
     92 				lines[i, TYPE] = DELETED_LINE;
     93 			else
     94 				break;
     95 		}
     96 
     97 		# Update the name cache
     98 		namestr = getnames(deleteindex);
     99 		if (namestr == "")
    100 			return;
    101 		num = split(namestr, names);
    102 	        for (i=1; i<=num; i++) {
    103 			name = names[i];
    104 			for (n=1; n<=namecache[name,0]; n++) {
    105 				if ( namecache[name,n] == deleteindex)
    106 					namecache[name,n] = "";
    107 			}
    108 		}	
    109 	}
    110 
    111 	#
    112 	# Count the number of lines that the line at lineindex spans.  For example,
    113 	# a line with one continuation line spans two lines.
    114 	#
    115 	function linespan (lineindex,  i) {
    116 		for (i = 1; lines[lineindex + i, TYPE] == CONTINUATION_LINE; i++);
    117 		return i;
    118 	}
    119 
    120 	#
    121 	# Take the line at "fromindex" and insert it at "toindex", bumping down
    122 	# anything currently at and after "toindex" to make the line fit.
    123 	#
    124 	function insertline (fromindex, toindex,  i, linecount,
    125 		       moveindex, namestr, names, name, m, num) {
    126 		if (toindex >= fromindex) {
    127 			print "ERROR, moving a line forward from index " fromindex \
    128 			    " to " toindex;
    129 			exit 1;
    130 		}
    131 
    132 		#
    133 		# We do not re-arrange the array to move the lines around as it is expensive.
    134 		# Instead we mark the moved line(s) as deleted and maintain 
    135 		# a array list of the appended line(s) in the target line.
    136 		# MOVEDLINES is the index where we maitain refs/linenos.
    137 		#
    138 		deleteline(fromindex); 	# mark moved lines as deleted.
    139 
    140 		#
    141 		# If the target line at "toindex" spans several lines
    142 		# then maintain the list in the last line.
    143 		#
    144 		toindex = toindex + linespan(toindex) - 2; 
    145 		moveindex = lines[toindex, MOVEDLINES, 0]
    146 		linecount = linespan(fromindex);
    147 		for (i = 0; i < linecount; i++) {
    148 			moveindex++;
    149 			lines[toindex, MOVEDLINES, moveindex] = fromindex + i;
    150 		}
    151 		lines[toindex, MOVEDLINES, 0] = moveindex;	
    152 
    153 		# Update the name cache
    154 		namestr = getnames(fromindex);
    155 		if (namestre == "")
    156 			return;
    157 		num = split(namestr, names);
    158 	        for (i=1; i<=num; i++) {
    159 			name = names[i];
    160 			for(m=1; m<= namecache[name,0]; m++) {
    161 				if (namecache[name,m] == fromindex)
    162 					namecache[name,m] = toindex;
    163 			}
    164 		}		
    165 	}
    166 
    167 	# lookup multiple lines with same host name using namecache
    168 	function findname (name, curline, ln, lncount, matchline) {
    169 		lncount = namecache[name, 0];
    170 		if (lncount < 2)
    171 			return 0;
    172 		for (ln = 1; ln <= lncount; ln++) {
    173 			matchline = namecache[name, ln];
    174 			if (matchline == "")
    175 				continue;
    176 			if (matchline >= curline)
    177 				continue;
    178 			if (lines[matchline, TYPE] != ENTRY_LINE)
    179 				continue;
    180 			return matchline;
    181 		}
    182 		return 0;
    183 	}
    184 
    185 	function arelinesadjacent(a, b,  i) {
    186 		if (a + 1 == b)
    187 			return 1;
    188 		for (i = a + 1; i < b; i++) {
    189 			if (lines[i, TYPE] == ENTRY_LINE) 
    190 				return 0;
    191 		}
    192 		return 1;
    193 	}
    194 
    195 	function printmovedlines(movedline, mcount, mline, m) {
    196 		mcount =  lines[movedline, MOVEDLINES, 0];
    197 		if (mcount > 0) {
    198 			for (m = 1; m <= mcount; m++) {
    199 				mline = lines[movedline, MOVEDLINES, m];
    200 				print lines[mline, LINE];
    201 				printmovedlines(mline);
    202 			}
    203 		}
    204 	}
    205 
    206 	function printmergeinfo() {
    207 		"/usr/bin/date" | getline date;
    208 		printf "\n#\n# Merged entries from ipnodes" \
    209 			" into hosts on <%s>\n",date;
    210 		printf "# Backup files saved in /etc/inet/ directory:" \
    211 		       " hosts.premerge, ipnodes.premerge\n#\n";
    212 	}
    213 
    214 	function printhostsfile (i) {
    215 		for (i = 1; i <= linecount; i++) {
    216 			# Add comment about occurrence of merge.
    217 			if (!cadded && (lines[i, TYPE] != COMMENT_LINE)) {
    218 					printmergeinfo();
    219 					cadded = 1;
    220 			}
    221 
    222 			if (lines[i, TYPE] != DELETED_LINE)
    223 				print lines[i, LINE];
    224 			#
    225 			# Check for moved lines and print them as they should be
    226 		        # appended after this line.	
    227 			#
    228 			printmovedlines(i);
    229 		}
    230 	}
    231 
    232 	BEGIN {
    233 		# line types.  These are strings to help with debugging
    234 		ENTRY_LINE = "entry";
    235 		COMMENT_LINE = "comment";
    236 		BLANK_LINE = "blankline";
    237 		DELETED_LINE = "deleted";
    238 		CONTINUATION_LINE = "continuation";
    239 
    240 		# indices to the data contained in the lines array
    241 		TYPE = 1;
    242 		LINE = 2;
    243 		MOVEDLINES = 3;  # index to array of append. line nos
    244 
    245 		# regular expressions
    246 		space = "[ \t]";
    247 		blanks = space "*";
    248 		blankline = "^" blanks "$";
    249 		comment = "^" blanks "#";
    250 
    251 		linecount = 0;
    252 		tobecontinued = 0;
    253 	}
    254 
    255 	$0 ~ comment {
    256 		linecount++;
    257 		if (tobecontinued) {
    258 			lines[linecount, TYPE] = CONTINUATION_LINE;
    259 			tobecontinued = 0;
    260 		} else {
    261 			lines[linecount, TYPE] = COMMENT_LINE;
    262 		}
    263 		lines[linecount, LINE] = $0;
    264 		next;
    265 	}
    266 
    267 	$0 ~ blankline {
    268 		linecount++;
    269 		if (tobecontinued) {
    270 			lines[linecount, TYPE] = CONTINUATION_LINE;
    271 			tobecontinued = 0;
    272 		} else {
    273 			lines[linecount, TYPE] = BLANK_LINE;
    274 		}
    275 		next;
    276 	}
    277 
    278 	{
    279 		linecount++;
    280 		if (firstentryline == "")
    281 			firstentryline = linecount;
    282 
    283 		if (tobecontinued) {
    284 			lines[linecount, TYPE] = CONTINUATION_LINE;
    285 			tobecontinued = 0;
    286 		} else {
    287 			lines[linecount, TYPE] = ENTRY_LINE;
    288 		}
    289 		lines[linecount, LINE] = $0;
    290 	}
    291 
    292 	/\\$/ {
    293 		#
    294 		# This matches a line that is continued on a subsequent line.  It
    295 		# doesnt match the continuation itself.  We only need to flag that
    296 		# this line is continued so that subsequent records can be tagged
    297 		# as continuations.
    298 		#
    299 		tobecontinued = 1;
    300 	}
    301 
    302 	#
    303 	# We now have an array of lines, one for each line of input.
    304 	#
    305 	END {
    306 		#
    307 		# Start by removing duplicate lines. We look for two lines 
    308 		# that are for the same address and have the same hostname 
    309 		# information (and listed in the same order). If we find such 
    310 		# a pair, we delete the second line.
    311 		#
    312 		for (i = 1; i <= linecount; i++) {
    313 			if (lines[i, TYPE] != ENTRY_LINE)
    314 				continue;
    315 
    316 			# Extract the address from the line
    317 			address = getaddress(i);
    318 
    319 			# Extract the hostnames from the line
    320 			namestr = getnames(i);
    321 			if (namestr == "")
    322 				continue;
    323 			#
    324 			# We keep an array of address indices.  This lets us know
    325 			# when we have duplicates without having to go back and
    326 			# search the lines array. Since there can be multiple host 
    327 			# entries of the same address it is a two-dimensional array.
    328 			#
    329 			if (addrindex[address] == "") {
    330 				# We have not seen this address before
    331 				addrindex[address] = 1;
    332 				# The second dimension stores the line number.
    333 				addrindex[address, 1] = i;
    334 			} else {
    335 				#
    336 				# We have a duplicate if hostname
    337 				# information is identical. If duplicate 
    338 				# just mark the second entry as deleted.
    339 				#
    340 				for (l = 1; l <= addrindex[address]; l++) {
    341 					if (namestr == \
    342 				           getnames(addrindex[address, l])) {
    343 						deleteline(i);
    344 						break;
    345 					}
    346 				}
    347 				
    348 				#
    349 				# If it was not deleted as a duplicate, add this
    350 				# address to the index array.
    351 				#
    352 				if (l > addrindex[address]) {
    353 					addrindex[address]++;
    354 					addrindex[address, addrindex[address]] = i;
    355 				}
    356 			}
    357 
    358 			# Maintain a name cache array. This is used
    359 			# next to coalesce entries with same host
    360 			# name. Line deletions and insert lines do
    361 			# update the name cache.
    362 
    363 			num = split(namestr, names);
    364 			for (n = 1; n <= num; n++) {
    365 				# 0 index node holds count of lines
    366 				# with same host name
    367 				namecache[names[n],0]++;
    368 				namecache[names[n], 
    369 					namecache[names[n],0]] = i;
    370 			}
    371 		}
    372 		
    373 		#
    374 		# We now need to bring together entries that contain the same name.
    375 		# The hosts and ipnodes back-end requires that entries for the same
    376 		# name but for different addresses be on adjacent lines.  See
    377 		# hosts(4).
    378 		#
    379 		for (i = 1; i <= linecount; i++) {
    380 			if (lines[i, TYPE] != ENTRY_LINE)
    381 				continue;
    382 
    383 			namestr = getnames(i);
    384 			if (namestr == "")
    385 				continue;
    386 			num = split(namestr, names);
    387 			for (n = 1; n <= num; n++) {
    388 				if ((nameindex = findname(names[n], i )) != 0) {
    389 					if (!arelinesadjacent(nameindex, i)) {
    390 						for (newindex = nameindex + 1; \
    391 						     lines[newindex, TYPE] == \
    392 							 CONTINUATION_LINE; \
    393 						     newindex++);
    394 						insertline(i, newindex);
    395 					}
    396 					break;
    397 				}
    398 			}
    399 		}
    400 
    401 		# Remove any trailing blank lines
    402 		for (i = linecount; i > 0; i--) {
    403 			type = lines[i, TYPE];
    404 			if (type == ENTRY_LINE || type == COMMENT_LINE)
    405 				break;
    406 			if (type == DELETED_LINE)
    407 				continue;
    408 			if (type == BLANK_LINE)
    409 				deleteline(i);
    410 		}
    411 
    412 		printhostsfile();
    413 	}' $1 $2;
    414 
    415 }
    416 
    417 #
    418 # deliver_hosts: This function merges /etc/inet/hosts and /etc/inet/ipnodes
    419 # into a single hosts file (/etc/inet/hosts) only when ipnodes file exists in
    420 # the system.  /etc/inet/ipnodes is now a symlink to /etc/inet/hosts.
    421 #
    422 deliver_hosts() {
    423 	saved_ipnodes_file=$BASEDIR/etc/inet/ipnodes.hostsmerge
    424 	temp_ipnodes_file=/tmp/ipnodes.hostsmerge
    425 	temp_merged_file=/tmp/hosts.hostsmerged
    426 	
    427 	# if /etc/inet/hosts doesn't exist (fresh install)
    428 	# then same action as 'i.preserve' i.e
    429 	# copy default /etc/inet/hosts in place.
    430 
    431 	if [ ! -f $dest ] ; then
    432 		cp $src $dest
    433 	fi
    434 
    435 	if [ -f $saved_ipnodes_file ] ; then
    436 
    437 		# Save copies before merge
    438 		cp -pf $dest $BASEDIR/etc/inet/hosts.premerge
    439 		cp -pf $saved_ipnodes_file $BASEDIR/etc/inet/ipnodes.premerge 
    440 
    441 		# Remove redundant header lines from ipnodes file 
    442 		/usr/bin/sed -e '
    443 		/^# CDDL HEADER START$/,/^# CDDL HEADER END$/ d
    444 		/^# Copyright .* Sun Microsystems, Inc/ d
    445 		/^# Use is subject to license terms/ d
    446 		/^# Internet host table$/ d
    447 		' $saved_ipnodes_file | /usr/bin/sed -e ' /^#$/ {
    448 		# Remove blank comment line pairs
    449 			$!N
    450 			/\n#$/ d
    451 		}' > $temp_ipnodes_file
    452 
    453 		merge_ipnodes $dest $temp_ipnodes_file \
    454 			> $temp_merged_file
    455 		if [ $? -ne 0 ] ; then
    456 			echo "$0 : failed to merge \
    457 			       	$saved_ipnodes_file with $dest"
    458 			exit_status=2
    459 			continue
    460 		fi
    461 
    462 		mv -f $temp_merged_file $dest
    463 		if [ $? -ne 0 ] ; then
    464 			echo "$0 : failed to move \
    465 				$temp_merged_file to $dest"
    466 			exit_status=2
    467 			continue
    468 		fi
    469 	fi
    470 }
    471 	
    472 # main
    473 exit_status=0
    474 
    475 while read src dest; do
    476 	dest_name=`basename "$dest"`
    477 
    478 	case "${dest_name}" in 
    479 		"hosts") deliver_hosts ;;
    480 		*) ;;
    481 	esac
    482 done
    483 
    484 exit $exit_status 
    485 
    486