#!/usr/bin/perl
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (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 (c) 2000 by Sun Microsystems, Inc.
# All rights reserved.
#
# ident "%Z%%M% %I% %E% SMI"
$PkgDir = "/var/sadm/pkg"; # where to find the pkg directories
$PROGRAM_NAME = "make_pkg_db";
$DBM_DIR_CHARACTERIZATION = "directory for the dbm databases";
$INPUT_FILES_CHARACTERIZATION = "one or more files in /var/sadm/install/contents format";
$PKGDEFS_DIRECTORY = "package pool directory";
$Usage =
"Usage: $PROGRAM_NAME
[-ifiles <$INPUT_FILES_CHARACTERIZATION>]
[-pkgdef <$PKGDEFS_DIRECTORY>]
-dbdir <$DBM_DIR_CHARACTERIZATION>
[-h for help]\n";
$Help =
"This program initializes a set of dbm databases with information
from /var/sadm/install/contents or a user-defined package pool directory.
There is one required argument:
-dbdir
the $DBM_DIR_CHARACTERIZATION
\nThe optional argument -h produces this message instead of any processing.
\nThe optional argument -ifiles is used for symbolic link resolution.
\nThe optional argument -pkgdef creates the databases based upon a package \npool directory instead of /var/sadm/install/contents on the local machine.
";
#
# check for perl5 -- we use things unavailable in perl4
#
die "Sorry, this program requires perl version 5.000 or up. You have $]. Stopping" if $] < 5.000;
#
# process arguments
#
$PKGDefs = "";
while (@ARGV) {
$arg = shift (@ARGV);
if ($arg eq "-h") {
print "$Help\n$Usage";
exit 0;
} elsif ($arg eq "-ifiles") {
while (($ARGV[0] !~ /^-/) && (@ARGV)){
push (@IFiles, shift(@ARGV));
}
} elsif ($arg eq "-dbdir") {
$DBDir = shift(@ARGV) unless ($ARGV[0] =~ /^-/);
} elsif ($arg eq "-pkgdef") {
$PKGDefs = shift(@ARGV) unless ($ARGV[0] =~ /^-/);
} else {
print STDERR "Unrecognized argument $arg. \n$Usage";
exit 1;
}
}
# make sure the package pool directory exists
if (($PKGDefs) && !(-d $PKGDefs)) {
print STDERR "Cannot open the directory $PKGDefs\n";
exit 1;
}
# Here we define the input files which will be parsed
if ($PKGDefs) {
$dirs = `ls $PKGDefs`;
@dirlist = split(/\s*\n\s*/, $dirs);
foreach $dir (@dirlist) {
push(@IFiles, "$PKGDefs/$dir/pkgmap");
}
reverse(@IFiles);
}
else {
push(@IFiles, "/var/sadm/install/contents");
}
if (!@IFiles) {
print STDERR "Required argument -ifiles missing. \n$Usage";
exit 1;
}
if (!$DBDir) {
print STDERR "Required argument -dbdir missing. \n$Usage";
exit 1;
}
$Struct = \%struct; # here is the structure we'll store everything in
#
# now open the dbm databases we will initialize
#
&yelp ("...initializing the databases\n");
unless (-d "$DBDir") {
&yelp("Creating directory $DBDir\n");
mkdir($DBDir, 0777);
}
# db for package names from the /var/sadm/pkg/foo/pkginfo files
dbmopen(%PKGNAMES, "$DBDir/PKGNAMES", 0644) || die"Cannot open dbm db $DBDir/PKGNAMES\n";
# db for entity file types
dbmopen(%FTYPE, "$DBDir/FTYPE", 0664) || die"Cannot open dbm db $DBDir/FTYPE\n";
# db for entity modes types
dbmopen(%MODE, "$DBDir/MODE", 0664) || die"Cannot open dbm db $DBDir/MODE\n";
# db for entity packages
dbmopen(%PKGS, "$DBDir/PKGS", 0664) || die"Cannot open dbm db $DBDir/PKGS\n";
# db for absolute link targets
dbmopen(%ABSLINK, "$DBDir/ABSLINK", 0664) || die"Cannot open dbm db $DBDir/ABSLINK\n";
undef %FTYPE; # remove existing records, if any
undef %MODE;
undef %PKGS;
undef %ABSLINK;
undef %PKGNAMES;
$Debug = 1; # print extra gibberish
#
# go make the package names db
#
&MakePackageNamesDB($PkgDir);
#
# read and parse each input file in contents file format
#
&yelp ("...making the FTYPE MODE and PKGS databases\n");
foreach $IFile (@IFiles) {
if ($PKGDefs) {
unless (-r $IFile) {
print STDERR "Could not open file: $IFile\n";
next;
}
@pkgname = split("/", $IFile);
$thisPkg = @pkgname[($#pkgname-1)];
$pkgInfo="$PKGDefs/$thisPkg/pkginfo";
$thisBaseDir="";
if (-r $pkgInfo) {
$BASEDIR = `grep '^BASEDIR' $pkgInfo`;
$BASEDIR =~ s/^BASEDIR=//;
chomp($BASEDIR);
$thisBaseDir = $BASEDIR;
}
}
open (IFILE, "$IFile") || die "cannot open input file $IFile\n";
# Tell the user what we are looking at UNLESS they are looking at a package
# pool. A package pool could have hundreds of entries which just creates
# a lot of useless (and confusing) output.
&yelp("...opening $IFile\n") unless ($PKGDefs);
while () { # loop over file line-at-a-time
if ($PKGDefs) {
next if /^:/; # ignore these lines from a pkgmap
next if (/(\S+)\s+[i]\s+/);
}
else {
next if /^#/; # ignore comments
next if /^\s*$/; # ignore blanks
}
chop;
undef $FType;
undef $Mode;
$line=$_;
if ($PKGDefs) {
&ParsePkgmapEntry($line);
@Pkgs = $thisPkg;
}
else {
&ParseContentsEntry($_);
}
# if this entry was supplied by a earlier file, skip it
if ($FTYPE{$Entity} =~ /\w/) {
# don't bother complaining about directories, we know the same
# directory could exist in multiple packages
next if ($FTYPE{$Entity} eq "d");
if ($PKGDefs) {
# In the case where we are going through a package pool, we
# expect that a file may reside in multiple packages. If
# that is detected, we simply add this package to the list of
# packages for that file
$currPkgs = $PKGS{$Entity};
next if ($FTYPE{$Entity} eq "s");
$PKGS{$Entity} = "$currPkgs $thisPkg";
}
else {
# In the case where we are reading in from
# /var/sadm/install.contents, we do not expect to see any
# over-ridden files EXCEPT when the "-ifiles" option is used.
&yelp("...OVERRIDDEN: $line\n");
}
next;
} else {
$Package = join(" ",@Pkgs);# store supplying packages sep by " "
# This is a hack. In the case of directories like /bin which
# would belong in many packages, the $PKGS hash would not
# be able to handle such a long entry. So for directories, I
# just place the first package I find. For this tool, it doesn't
# matter since this tool does not report which directories come
# from which package.
if ($FType eq "d") {
@FirstPackage = split(" ", $Package);
$PKGS{$Entity} = $FirstPackage[0];
}
else {
$PKGS{$Entity} = $Package; # update PKGS database
}
}
#
# put what we need from this entry line into the dbs
#
&yelp ("***NO FILETYPE! IGNORING ENTRY: $_\n") unless $FType;
$FTYPE{$Entity} = $FType; # update the FTYPE database
#
# now collect the possible paths for each basename
#
($path, $base) = $Entity =~ /(.*\/)(.*)/;
push(@{$Struct->{"PATHS"}->{$base}}, $Entity);
if ($FType =~ /[ls]/) { # link
$rellinkent = "$Entity;$RelEntity";
push (@RelLinkEnts,$rellinkent); # make list of ents to resolve
} else {
$MODE{$Entity} = $Mode if $Mode ne ""; # update MODE database
}
}
close IFILE;
} # end foreach $IFile
#
# now convert the relative links into absolute ones
#
&yelp ("...making the ABSLINK database\n");
foreach $rellinkent (@RelLinkEnts) {
($Entity, $RelEntity) = split(/;/, $rellinkent);
$AbsLink = &GetAbsLink($Entity, $RelEntity);
$ABSLINK{$Entity} = $AbsLink;
}
#
# close the dbs -- we're done
#
dbmclose (FTYPE);
dbmclose (MODE);
dbmclose (PKGS);
dbmclose (ABSLINK);
dbmclose (PKGNAMES);
&yelp ("...DONE\n");
#===========================END OF MAIN====================================
sub GetAbsLink { # convert relative link to actual one
local ($entry, $rellink) = @_;
return $rellink if $rellink =~ /^\//; # just return if abs already
@RelPath = split(/\//,$rellink);
@EntryPath = split(/\//,$entry);
#
# get the filename part
#
undef @AbsPath;
@AbsPath = (pop(@RelPath)) if $RelPath[$#RelPath] =~ /w/;
pop @EntryPath;
#
# pop the relative path until a relative dir shows up
#
while (@RelPath) {
$relhere = pop(@RelPath);
if ($relhere =~ /\w/) { # there's a letter or number
unshift (@AbsPath, $relhere); # its a dirname; keep it
} elsif ($relhere =~ /^\.\.$/) { # its a .. pop up one dir
pop(@EntryPath);
} elsif ($relhere =~ /^\.$/) { # it's a . -- stop
last;
}
}
while (@EntryPath) { # complete the path
unshift(@AbsPath, pop(@EntryPath)); # ...from the remaining entry
}
$abspath = join("/", @AbsPath);
if (!$FTYPE{$abspath}) { # no installed entity !
# NICKI - for now
&yelp("***CANNOT FIND ABSOLUTE PATH $abspath FOR ENTRY: $entry=$rellink\n");
# &yelp("***CANNOT RESOLVE ABSOLUTE PATH $abspath\n");
# COMMENTED OUT BY NICKI
# $base = $rellink;
# $base =~ s/.*\///; # get basename we're looking for
# @cans = @{$Struct->{"PATHS"}->{$base}}; # get all entities ...
# $numcans = $#cans + 1; # ... with this base
# &yelp(" There are $numcans entries with this basename:\n");
# foreach $can (@cans) {
# &yelp(" $can\n");
# }
# $abspath = "";
}
return $abspath;
}
sub ParseContentsEntry {
#invocation: &ParseContentsEntry($l); # $l is a line in the file
local ($l) = @_;
#
# look for b or c entries, like:
# /devices/pseudo/openeepr@0:openprom c none 38 0 0640 root sys SUNWcsd
#
if (($Entity,$FType,$Class,$Maj,$Min,$Mode,$Owner,$Group,@Pkgs) =
($l =~ /^(\S+)\s+([bc])\s+(\w+)\s+([0-9]+)\s+([0-9]+)\s+([0-7]+)\s+([a-z]+)\s+([a-z]+)\s+([A-Z].*)/)) {
#
# look for d entries, like
# /devices/pseudo d none 0755 root sys SUNWcsd
#
} elsif (($Entity,$FType,$Class,$Mode,$Owner,$Group,@Pkgs) =
($l =~ /^(\S+)\s+([d])\s+(\w+)\s+([0-7]+)\s+([a-z]+)\s+([a-z]+)\s+([A-Z].*)/)) {
#
# look for f or e or v entries, like
# /etc/asppp.cf f none 0744 root sys 360 27915 801314234 SUNWapppr
#
} elsif (($Entity,$FType,$Class,$Mode,$Owner,$Group,
$Size,$Checksum,$Modtime,@Pkgs) =
($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].*)/)) {
#
# look for l or s entries, like
# /bin=./usr/bin s none SUNWcsr
#
} elsif (($Entity,$RelEntity,$FType,$Class,@Pkgs) =
($l =~ /^([^=]+)=(\S+)\s+([ls])\s+(\w+)\s+([A-Z].*)/)) {
} else {
print STDERR "Unrecognized entry in $IFile: $l\n";
}
}
sub ParsePkgmapEntry {
local ($line) = @_;
# for validation of input
$Unresolved = true;
# look for d entries, like
# 1 d root etc 775 root sys
if (($Part,$FType,$Class,$Entity,$Mode,$Owner,$Group) =
($line =~ /^(\S+)\s+([d])\s+(\w+)\s+(\S+)\s+(\d+)\s+(\w+)\s+(\w+)/)) {
# prepend a install root
if ($thisBaseDir eq "/") {
$Entity = "/$Entity";
}
else {
$Entity = "$thisBaseDir/$Entity";
}
$Unresolved = false;
}
# look for e,f or v entries, like
# 1 e master boot/solaris/devicedb/master 0644 root sys 75 5775 940882596
elsif (($Part,$FType,$Class,$Entity,$Mode,$Owner,$Group,$Size,$Checksum,$Modtime) =
($line =~ /^(\S+)\s+([efv])\s+(\w+)\s+(\S+)\s+(\d+)\s+(\w+)\s+(\w+)/)) {
# prepend a install root
if ($thisBaseDir eq "/") {
$Entity = "/$Entity";
}
else {
$Entity = "$thisBaseDir/$Entity";
}
$Unresolved = false;
}
elsif (($Part, $FType, $Class, $Entity, $RelEntity) =
($line =~ /^(\S+)\s+([ls])\s+(\w+)\s+(\S+)[=](\S+)/)) {
# prepend a install root
if ($thisBaseDir eq "/") {
$Entity = "/$Entity";
}
else {
$Entity = "$thisBaseDir/$Entity";
}
$Unresolved = false;
}
print ("UNRESOLVED: $line\n") if ($Unresolved eq true);
}
sub ParsePrototypeEntry {
#invocation: &ParsePrototypeEntry($l); # $l is a line in the file
local ($l) = @_;
#
# look for b or c entries, like:
# /devices/pseudo/openeepr@0:openprom c none 38 0 0640 root sys SUNWcsd
#
if (($Entity,$FType,$Class,$Maj,$Min,$Mode,$Owner,$Group,@Pkgs) =
($l =~ /^(\S+)\s+([bc])\s+(\w+)\s+([0-9]+)\s+([0-9]+)\s+([0-7]+)\s+([a-z]+)\s+([a-z]+)\s+([A-Z].*)/)) {
#
# look for d entries, like
# d root etc 775 root sys
#
} elsif (($FType,$Class,$Entity,$Mode,$Owner,$Group) =
($l =~ /^([d])\s+(\w+)\s+(\S+)\s+([0-7]+)\s+(\w+)\s+(\w+)/)) {
#
# look for f or e or v entries, like
# e preserve etc/acct/holidays 664 bin bin
#
} elsif (($FType,$Class,$Entity,$Mode,$Owner,$Group) =
($l =~ /^([fev])\s+(\w+)\s+(\S+)\s+([0-7]+)\s+(\w+)\s+(\w+)/)) {
#
# look for l or s entries, like
# l root etc/rc2.d/S21perf=../../etc/init.d/perf
#
} elsif (($FType,$Class,$Entity,$RelEntity) =
($l =~ /^([ls])\s+(\w+)\s+([^=]+)=(\S+)/)) {
} else {
print STDERR "Unrecognized Prototype File entry: $l\n";
}
}
sub yelp {
local($String) = @_;
print "$String";
}
sub MakePackageNamesDB {
#invocation: &MakePackageNamesDB($PkgDir);
local ($PkgDir) = @_; # argument is parent directory of pkg dirs
#$PkgDir = "/var/sadm/pkg";
opendir(PKGDIR, "$PkgDir") || die "Cannot open package directory $PkgDir\n";
@Pkgs = grep(/^[A-Z]/,readdir(PKGDIR)); # list of all package directories
foreach $Pkg (@Pkgs) { # loop over 'em
$InfoFile = "$PkgDir/$Pkg/pkginfo"; # full name of the pkginfo file
if (-r $InfoFile) { # if we can read it
$str = `grep '^NAME=' $InfoFile`; # just grep the entry
$str =~ s/\s*\n$//; # trim trailing ws
$str =~ s/.*=\s*//; # trim leading NAME=
if ($str =~ /\w/) { # if the name has a letter or number in it
$PKGNAMES{$Pkg} = $str;
} else {
&yelp("***Cannot find usable NAME entry in $InfoFile\n");
}
} else {
&yelp("***Cannot find readable file $InfoFile\n");
}
} # end of loop over package directories
}