1 #!/usr/bin/perl 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, Version 1.0 only 7 # (the "License"). You may not use this file except in compliance 8 # with the License. 9 # 10 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 11 # or http://www.opensolaris.org/os/licensing. 12 # See the License for the specific language governing permissions 13 # and limitations under the License. 14 # 15 # When distributing Covered Code, include this CDDL HEADER in each 16 # file and include the License file at usr/src/OPENSOLARIS.LICENSE. 17 # If applicable, add the following below this CDDL HEADER, with the 18 # fields enclosed by brackets "[]" replaced with your own identifying 19 # information: Portions Copyright [yyyy] [name of copyright owner] 20 # 21 # CDDL HEADER END 22 # 23 # 24 # Copyright (c) 2000 by Sun Microsystems, Inc. 25 # All rights reserved. 26 # 27 28 # ident "%Z%%M% %I% %E% SMI" 29 30 $PkgDir = "/var/sadm/pkg"; # where to find the pkg directories 31 $PROGRAM_NAME = "make_pkg_db"; 32 $DBM_DIR_CHARACTERIZATION = "directory for the dbm databases"; 33 $INPUT_FILES_CHARACTERIZATION = "one or more files in /var/sadm/install/contents format"; 34 $PKGDEFS_DIRECTORY = "package pool directory"; 35 36 $Usage = 37 "Usage: $PROGRAM_NAME 38 [-ifiles <$INPUT_FILES_CHARACTERIZATION>] 39 [-pkgdef <$PKGDEFS_DIRECTORY>] 40 -dbdir <$DBM_DIR_CHARACTERIZATION> 41 [-h for help]\n"; 42 43 $Help = 44 "This program initializes a set of dbm databases with information 45 from /var/sadm/install/contents or a user-defined package pool directory. 46 There is one required argument: 47 48 -dbdir <dir> the $DBM_DIR_CHARACTERIZATION 49 50 \nThe optional argument -h produces this message instead of any processing. 51 \nThe optional argument -ifiles is used for symbolic link resolution. 52 \nThe optional argument -pkgdef creates the databases based upon a package \npool directory instead of /var/sadm/install/contents on the local machine. 53 "; 54 55 56 # 57 # check for perl5 -- we use things unavailable in perl4 58 # 59 60 die "Sorry, this program requires perl version 5.000 or up. You have $]. Stopping" if $] < 5.000; 61 62 # 63 # process arguments 64 # 65 66 $PKGDefs = ""; 67 68 while (@ARGV) { 69 $arg = shift (@ARGV); 70 if ($arg eq "-h") { 71 print "$Help\n$Usage"; 72 exit 0; 73 } elsif ($arg eq "-ifiles") { 74 while (($ARGV[0] !~ /^-/) && (@ARGV)){ 75 push (@IFiles, shift(@ARGV)); 76 } 77 } elsif ($arg eq "-dbdir") { 78 $DBDir = shift(@ARGV) unless ($ARGV[0] =~ /^-/); 79 } elsif ($arg eq "-pkgdef") { 80 $PKGDefs = shift(@ARGV) unless ($ARGV[0] =~ /^-/); 81 } else { 82 print STDERR "Unrecognized argument $arg. \n$Usage"; 83 exit 1; 84 } 85 } 86 87 # make sure the package pool directory exists 88 if (($PKGDefs) && !(-d $PKGDefs)) { 89 print STDERR "Cannot open the directory $PKGDefs\n"; 90 exit 1; 91 } 92 93 # Here we define the input files which will be parsed 94 if ($PKGDefs) { 95 96 $dirs = `ls $PKGDefs`; 97 @dirlist = split(/\s*\n\s*/, $dirs); 98 99 foreach $dir (@dirlist) { 100 push(@IFiles, "$PKGDefs/$dir/pkgmap"); 101 } 102 103 reverse(@IFiles); 104 } 105 else { 106 push(@IFiles, "/var/sadm/install/contents"); 107 } 108 109 if (!@IFiles) { 110 print STDERR "Required argument -ifiles missing. \n$Usage"; 111 exit 1; 112 } 113 114 if (!$DBDir) { 115 print STDERR "Required argument -dbdir missing. \n$Usage"; 116 exit 1; 117 } 118 119 $Struct = \%struct; # here is the structure we'll store everything in 120 121 122 123 # 124 # now open the dbm databases we will initialize 125 # 126 &yelp ("...initializing the databases\n"); 127 128 unless (-d "$DBDir") { 129 &yelp("Creating directory $DBDir\n"); 130 mkdir($DBDir, 0777); 131 } 132 133 # db for package names from the /var/sadm/pkg/foo/pkginfo files 134 dbmopen(%PKGNAMES, "$DBDir/PKGNAMES", 0644) || die"Cannot open dbm db $DBDir/PKGNAMES\n"; 135 136 # db for entity file types 137 dbmopen(%FTYPE, "$DBDir/FTYPE", 0664) || die"Cannot open dbm db $DBDir/FTYPE\n"; 138 139 # db for entity modes types 140 dbmopen(%MODE, "$DBDir/MODE", 0664) || die"Cannot open dbm db $DBDir/MODE\n"; 141 142 # db for entity packages 143 dbmopen(%PKGS, "$DBDir/PKGS", 0664) || die"Cannot open dbm db $DBDir/PKGS\n"; 144 145 # db for absolute link targets 146 dbmopen(%ABSLINK, "$DBDir/ABSLINK", 0664) || die"Cannot open dbm db $DBDir/ABSLINK\n"; 147 148 149 undef %FTYPE; # remove existing records, if any 150 undef %MODE; 151 undef %PKGS; 152 undef %ABSLINK; 153 undef %PKGNAMES; 154 155 $Debug = 1; # print extra gibberish 156 157 # 158 # go make the package names db 159 # 160 161 &MakePackageNamesDB($PkgDir); 162 163 # 164 # read and parse each input file in contents file format 165 # 166 167 &yelp ("...making the FTYPE MODE and PKGS databases\n"); 168 foreach $IFile (@IFiles) { 169 if ($PKGDefs) { 170 unless (-r $IFile) { 171 print STDERR "Could not open file: $IFile\n"; 172 next; 173 } 174 175 @pkgname = split("/", $IFile); 176 $thisPkg = @pkgname[($#pkgname-1)]; 177 $pkgInfo="$PKGDefs/$thisPkg/pkginfo"; 178 $thisBaseDir=""; 179 if (-r $pkgInfo) { 180 $BASEDIR = `grep '^BASEDIR' $pkgInfo`; 181 $BASEDIR =~ s/^BASEDIR=//; 182 chomp($BASEDIR); 183 $thisBaseDir = $BASEDIR; 184 } 185 } 186 187 open (IFILE, "$IFile") || die "cannot open input file $IFile\n"; 188 189 # Tell the user what we are looking at UNLESS they are looking at a package 190 # pool. A package pool could have hundreds of entries which just creates 191 # a lot of useless (and confusing) output. 192 &yelp("...opening $IFile\n") unless ($PKGDefs); 193 194 while (<IFILE>) { # loop over file line-at-a-time 195 if ($PKGDefs) { 196 next if /^:/; # ignore these lines from a pkgmap 197 next if (/(\S+)\s+[i]\s+/); 198 } 199 else { 200 next if /^#/; # ignore comments 201 next if /^\s*$/; # ignore blanks 202 } 203 204 205 chop; 206 undef $FType; 207 undef $Mode; 208 209 $line=$_; 210 211 if ($PKGDefs) { 212 &ParsePkgmapEntry($line); 213 @Pkgs = $thisPkg; 214 } 215 else { 216 &ParseContentsEntry($_); 217 } 218 219 # if this entry was supplied by a earlier file, skip it 220 221 if ($FTYPE{$Entity} =~ /\w/) { 222 223 # don't bother complaining about directories, we know the same 224 # directory could exist in multiple packages 225 next if ($FTYPE{$Entity} eq "d"); 226 227 if ($PKGDefs) { 228 # In the case where we are going through a package pool, we 229 # expect that a file may reside in multiple packages. If 230 # that is detected, we simply add this package to the list of 231 # packages for that file 232 233 $currPkgs = $PKGS{$Entity}; 234 next if ($FTYPE{$Entity} eq "s"); 235 $PKGS{$Entity} = "$currPkgs $thisPkg"; 236 } 237 else { 238 # In the case where we are reading in from 239 # /var/sadm/install.contents, we do not expect to see any 240 # over-ridden files EXCEPT when the "-ifiles" option is used. 241 &yelp("...OVERRIDDEN: $line\n"); 242 } 243 next; 244 } else { 245 $Package = join(" ",@Pkgs);# store supplying packages sep by " " 246 247 # This is a hack. In the case of directories like /bin which 248 # would belong in many packages, the $PKGS hash would not 249 # be able to handle such a long entry. So for directories, I 250 # just place the first package I find. For this tool, it doesn't 251 # matter since this tool does not report which directories come 252 # from which package. 253 254 if ($FType eq "d") { 255 @FirstPackage = split(" ", $Package); 256 $PKGS{$Entity} = $FirstPackage[0]; 257 } 258 else { 259 $PKGS{$Entity} = $Package; # update PKGS database 260 } 261 } 262 263 # 264 # put what we need from this entry line into the dbs 265 # 266 267 &yelp ("***NO FILETYPE! IGNORING ENTRY: $_\n") unless $FType; 268 $FTYPE{$Entity} = $FType; # update the FTYPE database 269 270 # 271 # now collect the possible paths for each basename 272 # 273 274 ($path, $base) = $Entity =~ /(.*\/)(.*)/; 275 push(@{$Struct->{"PATHS"}->{$base}}, $Entity); 276 if ($FType =~ /[ls]/) { # link 277 $rellinkent = "$Entity;$RelEntity"; 278 push (@RelLinkEnts,$rellinkent); # make list of ents to resolve 279 } else { 280 $MODE{$Entity} = $Mode if $Mode ne ""; # update MODE database 281 } 282 } 283 close IFILE; 284 } # end foreach $IFile 285 286 # 287 # now convert the relative links into absolute ones 288 # 289 290 &yelp ("...making the ABSLINK database\n"); 291 foreach $rellinkent (@RelLinkEnts) { 292 ($Entity, $RelEntity) = split(/;/, $rellinkent); 293 $AbsLink = &GetAbsLink($Entity, $RelEntity); 294 $ABSLINK{$Entity} = $AbsLink; 295 } 296 297 # 298 # close the dbs -- we're done 299 # 300 301 dbmclose (FTYPE); 302 dbmclose (MODE); 303 dbmclose (PKGS); 304 dbmclose (ABSLINK); 305 dbmclose (PKGNAMES); 306 307 &yelp ("...DONE\n"); 308 #===========================END OF MAIN==================================== 309 310 sub GetAbsLink { # convert relative link to actual one 311 local ($entry, $rellink) = @_; 312 313 return $rellink if $rellink =~ /^\//; # just return if abs already 314 315 @RelPath = split(/\//,$rellink); 316 @EntryPath = split(/\//,$entry); 317 318 # 319 # get the filename part 320 # 321 322 undef @AbsPath; 323 @AbsPath = (pop(@RelPath)) if $RelPath[$#RelPath] =~ /w/; 324 pop @EntryPath; 325 326 # 327 # pop the relative path until a relative dir shows up 328 # 329 330 while (@RelPath) { 331 $relhere = pop(@RelPath); 332 if ($relhere =~ /\w/) { # there's a letter or number 333 unshift (@AbsPath, $relhere); # its a dirname; keep it 334 } elsif ($relhere =~ /^\.\.$/) { # its a .. pop up one dir 335 pop(@EntryPath); 336 } elsif ($relhere =~ /^\.$/) { # it's a . -- stop 337 last; 338 } 339 } 340 341 while (@EntryPath) { # complete the path 342 unshift(@AbsPath, pop(@EntryPath)); # ...from the remaining entry 343 } 344 $abspath = join("/", @AbsPath); 345 if (!$FTYPE{$abspath}) { # no installed entity ! 346 # NICKI - for now 347 &yelp("***CANNOT FIND ABSOLUTE PATH $abspath FOR ENTRY: $entry=$rellink\n"); 348 # &yelp("***CANNOT RESOLVE ABSOLUTE PATH $abspath\n"); 349 350 # COMMENTED OUT BY NICKI 351 # $base = $rellink; 352 # $base =~ s/.*\///; # get basename we're looking for 353 # @cans = @{$Struct->{"PATHS"}->{$base}}; # get all entities ... 354 # $numcans = $#cans + 1; # ... with this base 355 356 # &yelp(" There are $numcans entries with this basename:\n"); 357 # foreach $can (@cans) { 358 # &yelp(" $can\n"); 359 # } 360 # $abspath = ""; 361 } 362 return $abspath; 363 } 364 365 sub ParseContentsEntry { 366 #invocation: &ParseContentsEntry($l); # $l is a line in the file 367 local ($l) = @_; 368 369 # 370 # look for b or c entries, like: 371 # /devices/pseudo/openeepr@0:openprom c none 38 0 0640 root sys SUNWcsd 372 # 373 374 if (($Entity,$FType,$Class,$Maj,$Min,$Mode,$Owner,$Group,@Pkgs) = 375 ($l =~ /^(\S+)\s+([bc])\s+(\w+)\s+([0-9]+)\s+([0-9]+)\s+([0-7]+)\s+([a-z]+)\s+([a-z]+)\s+([A-Z].*)/)) { 376 377 # 378 # look for d entries, like 379 # /devices/pseudo d none 0755 root sys SUNWcsd 380 # 381 382 } elsif (($Entity,$FType,$Class,$Mode,$Owner,$Group,@Pkgs) = 383 ($l =~ /^(\S+)\s+([d])\s+(\w+)\s+([0-7]+)\s+([a-z]+)\s+([a-z]+)\s+([A-Z].*)/)) { 384 385 # 386 # look for f or e or v entries, like 387 # /etc/asppp.cf f none 0744 root sys 360 27915 801314234 SUNWapppr 388 # 389 390 } elsif (($Entity,$FType,$Class,$Mode,$Owner,$Group, 391 $Size,$Checksum,$Modtime,@Pkgs) = 392 ($l =~ /^(\S+)\s+([fev])\s+(\w+)\s+([0-7]+)\s+([a-z]+)\s+([a-z]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([A-Z].*)/)) { 393 394 # 395 # look for l or s entries, like 396 # /bin=./usr/bin s none SUNWcsr 397 # 398 399 } elsif (($Entity,$RelEntity,$FType,$Class,@Pkgs) = 400 ($l =~ /^([^=]+)=(\S+)\s+([ls])\s+(\w+)\s+([A-Z].*)/)) { 401 } else { 402 print STDERR "Unrecognized entry in $IFile: $l\n"; 403 } 404 } 405 406 sub ParsePkgmapEntry { 407 local ($line) = @_; 408 409 # for validation of input 410 $Unresolved = true; 411 412 # look for d entries, like 413 # 1 d root etc 775 root sys 414 415 if (($Part,$FType,$Class,$Entity,$Mode,$Owner,$Group) = 416 ($line =~ /^(\S+)\s+([d])\s+(\w+)\s+(\S+)\s+(\d+)\s+(\w+)\s+(\w+)/)) { 417 # prepend a install root 418 if ($thisBaseDir eq "/") { 419 $Entity = "/$Entity"; 420 } 421 else { 422 $Entity = "$thisBaseDir/$Entity"; 423 } 424 $Unresolved = false; 425 } 426 427 # look for e,f or v entries, like 428 # 1 e master boot/solaris/devicedb/master 0644 root sys 75 5775 940882596 429 430 elsif (($Part,$FType,$Class,$Entity,$Mode,$Owner,$Group,$Size,$Checksum,$Modtime) = 431 ($line =~ /^(\S+)\s+([efv])\s+(\w+)\s+(\S+)\s+(\d+)\s+(\w+)\s+(\w+)/)) { 432 433 # prepend a install root 434 if ($thisBaseDir eq "/") { 435 $Entity = "/$Entity"; 436 } 437 else { 438 $Entity = "$thisBaseDir/$Entity"; 439 } 440 $Unresolved = false; 441 } 442 elsif (($Part, $FType, $Class, $Entity, $RelEntity) = 443 ($line =~ /^(\S+)\s+([ls])\s+(\w+)\s+(\S+)[=](\S+)/)) { 444 445 # prepend a install root 446 if ($thisBaseDir eq "/") { 447 $Entity = "/$Entity"; 448 } 449 else { 450 $Entity = "$thisBaseDir/$Entity"; 451 } 452 $Unresolved = false; 453 } 454 455 print ("UNRESOLVED: $line\n") if ($Unresolved eq true); 456 } 457 458 sub ParsePrototypeEntry { 459 #invocation: &ParsePrototypeEntry($l); # $l is a line in the file 460 local ($l) = @_; 461 462 # 463 # look for b or c entries, like: 464 # /devices/pseudo/openeepr@0:openprom c none 38 0 0640 root sys SUNWcsd 465 # 466 467 if (($Entity,$FType,$Class,$Maj,$Min,$Mode,$Owner,$Group,@Pkgs) = 468 ($l =~ /^(\S+)\s+([bc])\s+(\w+)\s+([0-9]+)\s+([0-9]+)\s+([0-7]+)\s+([a-z]+)\s+([a-z]+)\s+([A-Z].*)/)) { 469 470 # 471 # look for d entries, like 472 # d root etc 775 root sys 473 # 474 475 } elsif (($FType,$Class,$Entity,$Mode,$Owner,$Group) = 476 ($l =~ /^([d])\s+(\w+)\s+(\S+)\s+([0-7]+)\s+(\w+)\s+(\w+)/)) { 477 478 # 479 # look for f or e or v entries, like 480 # e preserve etc/acct/holidays 664 bin bin 481 # 482 483 } elsif (($FType,$Class,$Entity,$Mode,$Owner,$Group) = 484 ($l =~ /^([fev])\s+(\w+)\s+(\S+)\s+([0-7]+)\s+(\w+)\s+(\w+)/)) { 485 486 # 487 # look for l or s entries, like 488 # l root etc/rc2.d/S21perf=../../etc/init.d/perf 489 # 490 491 } elsif (($FType,$Class,$Entity,$RelEntity) = 492 ($l =~ /^([ls])\s+(\w+)\s+([^=]+)=(\S+)/)) { 493 } else { 494 print STDERR "Unrecognized Prototype File entry: $l\n"; 495 } 496 } 497 498 sub yelp { 499 local($String) = @_; 500 print "$String"; 501 } 502 503 504 505 sub MakePackageNamesDB { 506 #invocation: &MakePackageNamesDB($PkgDir); 507 local ($PkgDir) = @_; # argument is parent directory of pkg dirs 508 509 #$PkgDir = "/var/sadm/pkg"; 510 opendir(PKGDIR, "$PkgDir") || die "Cannot open package directory $PkgDir\n"; 511 @Pkgs = grep(/^[A-Z]/,readdir(PKGDIR)); # list of all package directories 512 foreach $Pkg (@Pkgs) { # loop over 'em 513 $InfoFile = "$PkgDir/$Pkg/pkginfo"; # full name of the pkginfo file 514 if (-r $InfoFile) { # if we can read it 515 $str = `grep '^NAME=' $InfoFile`; # just grep the entry 516 $str =~ s/\s*\n$//; # trim trailing ws 517 $str =~ s/.*=\s*//; # trim leading NAME= 518 if ($str =~ /\w/) { # if the name has a letter or number in it 519 $PKGNAMES{$Pkg} = $str; 520 } else { 521 &yelp("***Cannot find usable NAME entry in $InfoFile\n"); 522 } 523 } else { 524 &yelp("***Cannot find readable file $InfoFile\n"); 525 } 526 } # end of loop over package directories 527 }