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
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.
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]
24 # Copyright (c) 2000 by Sun Microsystems, Inc.
25 # All rights reserved.
28 # ident "%Z%%M% %I% %E% SMI"
31 # check for perl5 -- we use things unavailable in perl4
34 die "Sorry, this program requires perl version 5.000 or up. You have $]. Stopping" if $] < 5.000;
36 $DBM_DIR_CHARACTERIZATION = "directory for the dbm databases";
39 "Usage: get_depend_info
40 -dbdir <$DBM_DIR_CHARACTERIZATION> [ -s ] [ -cons ] [ -root directory ]
41 [ -f ] [ -p ] [ -pkg SUNWxxx ] [ filename ]
45 "This program statically analyzes executable files and their
46 symbolic links using /usr/bin/ldd and /usr/bin/strings. It
47 can accept filename(s) or packages as the list of files to be
48 analyzed. By default, the program will report the file
49 dependencies and which packages those dependencies came from.
50 There is one required argument:
52 -dbdir <dir> the $DBM_DIR_CHARACTERIZATION
54 The optional argument -h produces this message instead of any processing.
55 The optional argument -cons tells the tool to be conservative and not to
57 The optional argument -root allows you to specify a new root (useful for
58 doing analysis on build trees).
59 The optional argument -pkg allows you to specify a package name.
60 The optional argument -f only outputs the filename of the dependencies
61 The optional argument -p only outputs the packanames of the dependencies
63 The optional argument -s ONLY outputs symbolic links for files or packages.
64 No ldd or strings analysis is done.
67 get_depend_info -dbdir ./DBM /usr/bin/ls
68 get_depend_info -dbdir ./DBM /usr/bin/*
69 get_depend_info -dbdir ./DBM -pkg SUNWnisu
70 get_depend_info -f -dbdir ./DBM -pkg SUNWnisu
71 get_depend_info -s -dbdir ./DBM /usr/bin/*
72 get_depend_info -s -dbdir ./DBM -pkg SUNWnisu
75 NOTE: Run make_pkg_db to create the database directory for get_depend_info
94 print "$Help\n$Usage";
96 } elsif ($arg eq "-dbdir") {
97 $DBDir = shift(@ARGV) unless ($ARGV[0] =~ /^-/);
98 } elsif ($arg eq "-s") {
101 } elsif ($arg eq "-p") {
104 } elsif ($arg eq "-f") {
107 } elsif ($arg eq "-cons") {
109 } elsif ($arg eq "-pkg") {
110 $PKGName = shift(@ARGV) unless ($ARGV[0] =~ /^-/);
111 } elsif ($arg eq "-root") {
112 $Root = shift(@ARGV) unless ($ARGV[0] =~ /^-/);
114 push(@filelist, $arg);
119 print STDERR
"Required argument -dbdir missing. \n$Usage";
124 # For a given pkg definition directory, this subroutine will
125 # go through the proto files and look for executable files.
126 # It will then put all the executable files into @filelist
127 &HandlePackageName
($PKGName);
129 if ($PackageOnly eq true
) {
136 $Silent = true
if (($Verbose eq false
) && ($PackageOnly eq false
)
137 && ($FileOnly eq false
));
139 foreach $entry (@filelist) {
141 print("\n\nAnalyzing $Root$entry:\n") unless ($Silent eq true
);
143 # make sure file exists
145 print STDERR
"Could not open file $entry\n";
150 $file = $FTYPE{$entry};
151 $pkgs = $PKGS{$entry};
152 $abslink = $ABSLINK{$entry};
155 print("Input file is a directory\n") unless ($Silent eq true
);
159 # destfile represents the actual file we are interested in!
160 if ($abslink =~ /\w/) {
161 $destfile = $abslink;
163 if (($FTYPE{$entry} eq "s") && ($SymLink eq true
)) {
164 print("$entry is linked to $destfile:$PKGS{$destfile}\n");
171 # if the -s flag is set, tell 'em about sym links and go to the next file
172 next if ($SymLink eq true
);
174 $mode = $MODE{$destfile};
176 # Handle the case where the user resets $ROOT
177 $destfile = "$Root$destfile" if ($Root =~ /\w/);
178 $filecmd = `/usr/bin/file $destfile 2>&1`;
180 # make sure we are dealing with an executable
181 if (($mode !~ /(.)(.*)7/) && ($mode !~ /(.)(.*)5/) && ($mode !~ /(.)(.*)3/) && ($mode !~ /(.)(.*)1/)){
182 print("Input file is not an executable\n");
186 # Kernel modules are handled separately
187 if ($destfile =~ /\/kernel\
//) {
188 &HandleKernelMod
($destfile, $FTYPE{$entry});
189 &OutputPackageList
if (($PackageOnly eq true
) && !($PKGName));
193 # take care of scripts
194 if (($filecmd =~ /script/) || ($filecmd =~ /text/)) {
195 &HandleScripts
($destfile);
196 &OutputPackageList
if (($PackageOnly eq true
) && !($PKGName));
200 # Its not a script, not a kernel module, so its get to be a binary
201 &HandleBinaries
($destfile);
202 &OutputPackageList
if (($PackageOnly eq true
) && !($PKGName));
205 if (($PKGName) && ($SymLink eq false
)) {
206 print ("\n\nPackage dependencies for $PKGName:\n");
212 #===========================END OF MAIN====================================
214 sub GetLddInfo
{ # return multi-line string of ldd info for File
215 local ($FileID, $FileType) = @_;
217 $outstring = "* Not a File\n";
218 return ($outstring) if $FileType =~ /[Mlsdc]/; # ldd results not useful here
221 # use map file to see if this is a file that gives a known bad ldd return
224 # if ($Unsup{$FileID} == 1) {
225 # $outstring = "* unsupported or unknown file type, per map file";
226 # return ($outstring);
229 # $string = `/usr/bin/ldd $FileID 2>&1`;
230 # if ($?) { # if some error (don't just get wait status here)
232 # $err = "==$?==$errnum==";
233 # if (($err eq "==256==29==") || ($err eq "==256==0==")) {
234 # $err = "*"; # these are normal ldd returns
236 # die "Unexpected ldd return $? $!";
238 # $string =~ s/\/usr\/bin\/ldd:[^\0]*://g; # trim up error line
239 # } elsif ($string =~ s/warning:.*://) { # other normal ldd returns
244 $string = `/usr/bin/ldd $FileID 2>&1`;
245 # on a non-zero ldd, return nothing
246 return ($outstring) if ($?
);
250 @infolines = split(/\s*\n\s*/, $string);
251 foreach $line (@infolines) {
252 $line =~ s/^\s+//; # trim leading ws
253 next unless $line; # skip if blank
254 @words = split(/\s/, $line);
255 $filename = $words[0];
256 $outstring .= "$filename\n";
271 # open the databases for read-only
272 dbmopen(%FTYPE, "$DBDir/FTYPE", 0664) ||
273 die"Cannot open dbm db $DBDir/FTYPE\n";
275 dbmopen(%MODE, "$DBDir/MODE", 0664) ||
276 die"Cannot open dbm db $DBDir/MODE\n";
278 dbmopen(%PKGS, "$DBDir/PKGS", 0664) ||
279 die"Cannot open dbm db $DBDir/PKGS\n";
281 dbmopen(%ABSLINK, "$DBDir/ABSLINK", 0664) ||
282 die"Cannot open dbm db $DBDir/ABSLINK \n";
284 dbmopen(%PKGNAMES, "$DBDir/PKGNAMES", 0644) ||
285 die"Cannot open dbm db $DBDir/PKGNAMES\n";
288 sub HandleKernelMod
{
289 local ($entry, $ftype) = @_;
291 # search for the magic right, starting from the right (ie. end of path)
292 $index = rindex($entry, "kernel");
293 # rindex() returns where the "kernel" began, add 6 to get
294 # "{some path}/kernel"
296 # OK, now pull out the absolute path
297 $KernelPath = substr($entry, 0, $index);
299 # There are two ways to figure out the dependencies.
300 # First, we check to see if /usr/bin/ldd will tell us.
301 # If ldd fails, then we need to look at the output of /usr/bin/strings
304 $LddInfo = &GetLddInfo
($entry, $ftype);
306 if ($LddInfo =~ /\w/) {
308 @list = split(/\n/, $LddInfo);
309 foreach $file (@list) {
312 # first, check to see if there is a module relative to
314 if ($FTYPE{"$KernelPath/$file"} =~ /\w
/){
315 &Output
("$KernelPath/$file");
319 # Haven't found it yet, check /kernel
320 if (($FTYPE{"/kernel/$file"} =~ /\w/) && ($found == 0)){
321 &Output
("/kernel/$file");
325 # Haven't found it yet, check /usr/kernel
326 if (($FTYPE{"/usr/kernel/$file"} =~ /\w
/) && ($found == 0)){
327 &Output
("/usr/kernel/$file");
332 print("Could not resolve $file\n");
338 # the ldd failed, so now let's look at the string output
343 $string = `/usr/bin/strings $entry 2>&1`;
344 @infolines = split(/\s*\n\s*/, $string);
346 foreach $line (@infolines) {
348 push (@outlines,$line);
352 foreach $line (@outlines) {
353 @words = split(/\s/, $line);
354 foreach $word (@words) {
357 # first, check to see if there is a module relative to
359 if ($FTYPE{"$KernelPath/$word"} =~ /\w
/){
360 &Output
("$KernelPath/$word");
364 # Haven't found it yet, check /kernel
365 if (($FTYPE{"/kernel/$word"} =~ /\w/) && ($found == 0)){
366 &Output
("/kernel/$word");
370 # Haven't found it yet, check /usr/kernel
371 if (($FTYPE{"/usr/kernel/$word"} =~ /\w
/) && ($found == 0)){
372 &Output
("/usr/kernel/$word");
379 sub GetStringsInfo
{ # return multi-line string of ldd info for File
380 local ($FileID, $FileType) = @_;
382 $outstring = "* Not a File\n";
383 return ($outstring) if $FileType =~ /[Mlsdc]/; # ldd results not useful here
384 return ($outstring) if ($NoStrings eq true
); # we are running in conservative mode
386 # use map file to see if this is a file that gives a known bad ldd return
387 if ($Unsup{$FileID} == 1) {
388 $outstring = "* unsupported or unknown file type, per map file";
393 $string = `/usr/bin/strings $FileID 2>&1`;
398 @infolines = split(/\s*\n\s*/, $string);
400 foreach $line (@infolines) {
401 if (($line =~ /\//) && !($line =~ /%/) && !($line =~ m%/$%)){
402 push (@outlines,$line);
405 @outlines = sort(@outlines);
407 foreach $word (@outlines) {
408 if ($lastword ne $word) {
409 $outstring .= $word; $outstring .= "\n";
417 local ($filename) = @_;
418 open(SCRIPT
, $filename);
422 s/^\s+//; # trim leading ws
423 s/=/ /g; # get rid of all =
424 s/\`/ /g; # get rid of all `
425 next if ($_ =~ /^#/); # strip out obvious comments
426 next unless $_; # skip if blank
429 @words = split(/\s/, $line);
430 foreach $word (@words) {
431 if (($PKGS{$word} =~ /\w/) && ($FTYPE{$word} ne "d")) {
432 push(@output, $word);
437 @output = sort(@output);
440 # make sure we don't output dupes
441 foreach $file (@output) {
446 if (($count > 0) && ($output[$count] ne $output[$count-1])) {
452 # remember to play nice
457 local ($filename) = @_;
458 $LddInfo = &GetLddInfo
($destfile, $FTYPE{$filename});
459 $StringInfo = &GetStringsInfo
($destfile, $FTYPE{$filename});
461 # Parse the ldd output.
462 # Libs can be found in /kernel or /usr/lib
463 @list = split(/\n/, $LddInfo);
464 foreach $file (@list) {
466 if ($FTYPE{"/kernel/$file"} =~ /\w/){
467 &Output
("/kernel/$file");
471 if ($FTYPE{"/usr/lib/$file"} =~ /\w
/){
472 &Output
("/usr/lib/$file");
477 print("Could not resolve $file\n");
481 # For the strings output, we parse it to see if we can match it to
482 # any files distributed in a package.
483 @list = split(/\n/, $StringInfo);
484 foreach $file (@list) {
485 if (($FTYPE{$file} =~ /\w/) && ($FTYPE{$file} ne "d")) {
492 local($filename) = @_;
494 # If they want a package listing, a unique sorted list
495 # will be outputted later. Here we simply push elements onto
498 push(@PkgList, "$PKGS{$filename}\n");
501 if ($Verbose eq true
) {
502 print("$filename:$PKGS{$filename}\n");
506 # If they want a package listing, a unique sorted list
507 # will be outputted later. Here we simply push elements onto
509 if ($PackageOnly eq true
) {
510 push(@PkgList, "$PKGS{$filename}\n");
514 if ($FileOnly eq true
) {
515 print("$filename\n");
520 sub HandlePackageName
{
522 $pkgchk = `/usr/sbin/pkgchk -l $pkg | grep Pathname | sed 's/Pathname: //'`;
524 @files = split(/\n/, $pkgchk);
525 foreach $file (@files) {
526 push(@filelist, $file);
530 sub OutputPackageList
{
531 local($filename) = @_;
532 # If the user specified a package list, here we sort
533 # the list and make sure we don't output dupes.
535 @outPkgs = sort(@PkgList);
537 foreach $pkg (@outPkgs) {
538 $pkg =~ s/\s*$//; # trim extra space off the end
540 # make sure this entry isn't a dupe before
542 if ($lastpkg ne $pkg) {
543 print("P $pkg\t$PKGNAMES{$pkg}\n");
549 # reset the list for the next entry