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