dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / tools / depcheck / get_depend_info
blob74a52c1ef42cb0495e6e7188e5fcf0d6f8c97656
1 #!/usr/bin/perl
3 # CDDL HEADER START
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.
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]
21 # CDDL HEADER END
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";
38 $Usage =
39 "Usage: get_depend_info
40 -dbdir <$DBM_DIR_CHARACTERIZATION> [ -s ] [ -cons ] [ -root directory ]
41 [ -f ] [ -p ] [ -pkg SUNWxxx ] [ filename ]
42 [-h for help]\n";
44 $Help =
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
56 run /usr/bin/strings.
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.
66 Some Examples:
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
79 # process arguments
82 @PkgList = "";
83 $PackageOnly = false;
84 $FileOnly = false;
85 $Verbose = true;
86 $Silent = false;
87 $SymLink = false;
88 $NoStrings = false;
89 $Root = "";
91 while (@ARGV) {
92 $arg = shift (@ARGV);
93 if ($arg eq "-h") {
94 print "$Help\n$Usage";
95 exit 0;
96 } elsif ($arg eq "-dbdir") {
97 $DBDir = shift(@ARGV) unless ($ARGV[0] =~ /^-/);
98 } elsif ($arg eq "-s") {
99 $SymLink = true;
100 $Silent = true;
101 } elsif ($arg eq "-p") {
102 $PackageOnly = true;
103 $Verbose = false;
104 } elsif ($arg eq "-f") {
105 $FileOnly = true;
106 $Verbose = false;
107 } elsif ($arg eq "-cons") {
108 $NoStrings = true;
109 } elsif ($arg eq "-pkg") {
110 $PKGName = shift(@ARGV) unless ($ARGV[0] =~ /^-/);
111 } elsif ($arg eq "-root") {
112 $Root = shift(@ARGV) unless ($ARGV[0] =~ /^-/);
113 }else {
114 push(@filelist, $arg);
118 if (!$DBDir) {
119 print STDERR "Required argument -dbdir missing. \n$Usage";
120 exit 1;
123 if ($PKGName) {
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) {
130 $Silent = true;
134 &OpenDBs;
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
144 if (!(-r $entry)) {
145 print STDERR "Could not open file $entry\n";
146 next;
150 $file = $FTYPE{$entry};
151 $pkgs = $PKGS{$entry};
152 $abslink = $ABSLINK{$entry};
154 if ($file eq "d") {
155 print("Input file is a directory\n") unless ($Silent eq true);
156 next;
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");
167 else {
168 $destfile = $entry;
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");
183 next;
186 # Kernel modules are handled separately
187 if ($destfile =~ /\/kernel\//) {
188 &HandleKernelMod($destfile, $FTYPE{$entry});
189 &OutputPackageList if (($PackageOnly eq true) && !($PKGName));
190 next;
193 # take care of scripts
194 if (($filecmd =~ /script/) || ($filecmd =~ /text/)) {
195 &HandleScripts($destfile);
196 &OutputPackageList if (($PackageOnly eq true) && !($PKGName));
197 next;
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");
207 &OutputPackageList;
210 &CloseDBs;
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);
228 # $err = "";
229 # $string = `/usr/bin/ldd $FileID 2>&1`;
230 # if ($?) { # if some error (don't just get wait status here)
231 # $errnum = 0 + $!;
232 # $err = "==$?==$errnum==";
233 # if (($err eq "==256==29==") || ($err eq "==256==0==")) {
234 # $err = "*"; # these are normal ldd returns
235 # } else {
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
240 # $err = "*";
243 $outstring = "";
244 $string = `/usr/bin/ldd $FileID 2>&1`;
245 # on a non-zero ldd, return nothing
246 return ($outstring) if ($?);
249 $outstring = "";
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";
258 return ($outstring);
261 sub CloseDBs {
262 # close the dbs
263 dbmclose(FTYPE);
264 dbmclose(MODE);
265 dbmclose(PKGS);
266 dbmclose(ABSLINK);
267 dbmclose(PKGNAMES);
270 sub OpenDBs {
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"
295 $index += 6;
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
303 $LddInfo = "";
304 $LddInfo = &GetLddInfo($entry, $ftype);
306 if ($LddInfo =~ /\w/) {
307 @list = "";
308 @list = split(/\n/, $LddInfo);
309 foreach $file (@list) {
310 $found = 0;
312 # first, check to see if there is a module relative to
313 # this file
314 if ($FTYPE{"$KernelPath/$file"} =~ /\w/){
315 &Output("$KernelPath/$file");
316 $found++;
319 # Haven't found it yet, check /kernel
320 if (($FTYPE{"/kernel/$file"} =~ /\w/) && ($found == 0)){
321 &Output("/kernel/$file");
322 $found++;
325 # Haven't found it yet, check /usr/kernel
326 if (($FTYPE{"/usr/kernel/$file"} =~ /\w/) && ($found == 0)){
327 &Output("/usr/kernel/$file");
328 $found++;
331 if ($found == 0) {
332 print("Could not resolve $file\n");
335 return;
338 # the ldd failed, so now let's look at the string output
339 $string = "";
340 @infolines = "";
341 @outlines = "";
343 $string = `/usr/bin/strings $entry 2>&1`;
344 @infolines = split(/\s*\n\s*/, $string);
346 foreach $line (@infolines) {
347 if ($line =~ /\//){
348 push (@outlines,$line);
352 foreach $line (@outlines) {
353 @words = split(/\s/, $line);
354 foreach $word (@words) {
355 $found = 0;
357 # first, check to see if there is a module relative to
358 # this file
359 if ($FTYPE{"$KernelPath/$word"} =~ /\w/){
360 &Output("$KernelPath/$word");
361 $found++;
364 # Haven't found it yet, check /kernel
365 if (($FTYPE{"/kernel/$word"} =~ /\w/) && ($found == 0)){
366 &Output("/kernel/$word");
367 $found++;
370 # Haven't found it yet, check /usr/kernel
371 if (($FTYPE{"/usr/kernel/$word"} =~ /\w/) && ($found == 0)){
372 &Output("/usr/kernel/$word");
373 $found++;
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";
389 return ($outstring);
391 $err = "";
392 $string = "";
393 $string = `/usr/bin/strings $FileID 2>&1`;
395 $outstring = "";
396 @infolines = "";
397 @outlines = "";
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";
411 $lastword = $word;
413 return ($outstring);
416 sub HandleScripts {
417 local ($filename) = @_;
418 open(SCRIPT, $filename);
420 undef @output;
421 while (<SCRIPT>) {
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
428 $line = $_;
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);
438 $count = 0;
440 # make sure we don't output dupes
441 foreach $file (@output) {
442 if ($count == 0) {
443 &Output($file);
446 if (($count > 0) && ($output[$count] ne $output[$count-1])) {
447 &Output($file);
449 $count++;
452 # remember to play nice
453 close(SCRIPT);
456 sub HandleBinaries {
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) {
465 $found = 0;
466 if ($FTYPE{"/kernel/$file"} =~ /\w/){
467 &Output("/kernel/$file");
468 $found++;
471 if ($FTYPE{"/usr/lib/$file"} =~ /\w/){
472 &Output("/usr/lib/$file");
473 $found++;
476 if ($found == 0) {
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")) {
486 &Output($file);
491 sub Output {
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
496 # this list.
497 if ($PKGName) {
498 push(@PkgList, "$PKGS{$filename}\n");
501 if ($Verbose eq true) {
502 print("$filename:$PKGS{$filename}\n");
503 return;
506 # If they want a package listing, a unique sorted list
507 # will be outputted later. Here we simply push elements onto
508 # this list.
509 if ($PackageOnly eq true) {
510 push(@PkgList, "$PKGS{$filename}\n");
511 return;
514 if ($FileOnly eq true) {
515 print("$filename\n");
516 return;
520 sub HandlePackageName {
521 local($pkg) = @_;
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.
534 $lastpkg = "";
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
541 # printing it
542 if ($lastpkg ne $pkg) {
543 print("P $pkg\t$PKGNAMES{$pkg}\n");
546 $lastpkg = $pkg;
549 # reset the list for the next entry
550 @PkgList = "";