summaryrefslogtreecommitdiffstats
path: root/tools/LibraryReport
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>1997-01-02 03:49:35 +0000
committermsmith <msmith@FreeBSD.org>1997-01-02 03:49:35 +0000
commit9bd58b13fcbf62ce89b2f17d5660ce18d7a516bd (patch)
treef779280e35ea7721e1426877a443b54ec814f216 /tools/LibraryReport
downloadFreeBSD-src-9bd58b13fcbf62ce89b2f17d5660ce18d7a516bd.zip
FreeBSD-src-9bd58b13fcbf62ce89b2f17d5660ce18d7a516bd.tar.gz
LibraryReport is a Tcl script that attempts to report on the status of
shared libraries in the system. It locates libraries using the output of 'ldconfig -r' to find directories containing libraries, and then performs a recursive descent of the entire filesystem heirachy looking for dynamically linked executables and recording the libraries that they use. After scanning, a report is produced listing used libraries and the executable(s) that use them, and seperately listing unused libraries.
Diffstat (limited to 'tools/LibraryReport')
-rwxr-xr-xtools/LibraryReport/LibraryReport.tcl234
1 files changed, 234 insertions, 0 deletions
diff --git a/tools/LibraryReport/LibraryReport.tcl b/tools/LibraryReport/LibraryReport.tcl
new file mode 100755
index 0000000..e1bc8bd
--- /dev/null
+++ b/tools/LibraryReport/LibraryReport.tcl
@@ -0,0 +1,234 @@
+#!/bin/sh
+# tcl magic \
+exec tclsh $0 $*
+################################################################################
+#
+# LibraryReport; produce a list of shared libraries on the system, and a list of
+# all executables that use them.
+#
+################################################################################
+#
+# Stage 1 looks for shared libraries; the output of 'ldconfig -r' is examined
+# for hints as to where to look for libraries (but not trusted as a complete
+# list).
+#
+# These libraries each get an entry in the global 'Libs()' array.
+#
+# Stage 2 walks the entire system directory heirachy looking for executable
+# files, applies 'ldd' to them and attempts to determine which libraries are
+# used. The path of the executable is then added to the 'Libs()' array
+# for each library used.
+#
+# Stage 3 reports on the day's findings.
+#
+################################################################################
+#
+# $Id$
+#
+
+#########################################################################################
+# findLibs
+#
+# Ask ldconfig where it thinks libraries are to be found. Go look for them, and
+# add an element to 'Libs' for everything that looks like a library.
+#
+proc findLibs {} {
+
+ global Libs stats verbose;
+
+ # Older ldconfigs return a junk value when asked for a report
+ if {[catch {set liblist [exec ldconfig -r]} err]} { # get ldconfig output
+ puts stderr "ldconfig returned nonzero, persevering.";
+ set liblist $err; # there's junk in this
+ }
+
+ # remove hintsfile name, convert to list
+ set liblist [lrange [split $liblist "\n"] 1 end];
+
+ set libdirs ""; # no directories yet
+ foreach line $liblist {
+ # parse ldconfig output
+ if {[scan $line "%s => %s" junk libname] == 2} {
+ # find directory name
+ set libdir [file dirname $libname];
+ # have we got this one already?
+ if {[lsearch -exact $libdirs $libdir] == -1} {
+ lappend libdirs $libdir;
+ }
+ } else {
+ puts stderr "Unparseable ldconfig output line :";
+ puts stderr $line;
+ }
+ }
+
+ # libdirs is now a list of directories that we might find libraries in
+ foreach dir $libdirs {
+ # get the names of anything that looks like a library
+ set libnames [glob -nocomplain "$dir/lib*.so.*"]
+ foreach lib $libnames {
+ set Libs($lib) ""; # add it to our list
+ if {$verbose} {puts "+ $lib";}
+ }
+ }
+ set stats(libs) [llength [array names Libs]];
+}
+
+################################################################################
+# findLibUsers
+#
+# Look in the directory (dir) for executables. If we find any, call
+# examineExecutable to see if it uses any shared libraries. Call ourselves
+# on any directories we find.
+#
+# Note that the use of "*" as a glob pattern means we miss directories and
+# executables starting with '.'. This is a Feature.
+#
+proc findLibUsers {dir} {
+
+ global stats verbose;
+
+ if {[catch {
+ set ents [glob -nocomplain "$dir/*"];
+ } msg]} {
+ if {$msg == ""} {
+ set msg "permission denied";
+ }
+ puts stderr "Can't search under '$dir' : $msg";
+ return ;
+ }
+
+ if {$verbose} {puts "===>> $dir";}
+ incr stats(dirs);
+
+ # files?
+ foreach f $ents {
+ # executable?
+ if {[file executable $f]} {
+ # really a file?
+ if {[file isfile $f]} {
+ incr stats(files);
+ examineExecutable $f;
+ }
+ }
+ }
+ # subdirs?
+ foreach f $ents {
+ # maybe a directory with more files?
+ # don't use 'file isdirectory' because that follows symlinks
+ if {[catch {set type [file type $f]}]} {
+ continue ; # may not be able to stat
+ }
+ if {$type == "directory"} {
+ findLibUsers $f;
+ }
+ }
+}
+
+################################################################################
+# examineExecutable
+#
+# Look at (fname) and see if ldd thinks it references any shared libraries.
+# If it does, update Libs with the information.
+#
+proc examineExecutable {fname} {
+
+ global Libs stats verbose;
+
+ # ask Mr. Ldd.
+ if {[catch {set result [exec ldd $fname]} msg]} {
+ return ; # not dynamic
+ }
+
+ if {$verbose} {puts -nonewline "$fname : ";}
+ incr stats(execs);
+
+ # For a non-shared executable, we get a single-line error message.
+ # For a shared executable, we get a heading line, so in either case
+ # we can discard the first line and any subsequent lines are libraries
+ # that are required.
+ set llist [lrange [split $result "\n"] 1 end];
+ set uses "";
+
+ foreach line $llist {
+ if {[scan $line "%s => %s %s" junk1 lib junk2] == 3} {
+ lappend Libs($lib) $fname;
+ lappend uses $lib;
+ } else {
+ puts stderr "Unparseable ldd putput line :";
+ puts stderr $line;
+ }
+ }
+ if {$verbose} {puts "$uses";}
+}
+
+################################################################################
+# emitLibDetails
+#
+# Emit a listing of libraries and the executables that use them.
+#
+proc emitLibDetails {} {
+
+ global Libs;
+
+ # divide into used/unused
+ set used "";
+ set unused "";
+ foreach lib [array names Libs] {
+ if {$Libs($lib) == ""} {
+ lappend unused $lib;
+ } else {
+ lappend used $lib;
+ }
+ }
+
+ # emit used list
+ puts "== Current Shared Libraries ==================================================";
+ foreach lib [lsort $used] {
+ # sort executable names
+ set users [lsort $Libs($lib)];
+ puts [format "%-30s %s" $lib $users];
+ }
+ # emit unused
+ puts "== Stale Shared Libraries ====================================================";
+ foreach lib [lsort $unused] {
+ # sort executable names
+ set users [lsort $Libs($lib)];
+ puts [format "%-30s %s" $lib $users];
+ }
+}
+
+################################################################################
+# Run the whole shebang
+#
+proc main {} {
+
+ global stats verbose argv;
+
+ set verbose 0;
+ foreach arg $argv {
+ switch -- $arg {
+ -v {
+ set verbose 1;
+ }
+ default {
+ puts stderr "Unknown option '$arg'";
+ exit ;
+ }
+ }
+ }
+
+ set stats(libs) 0;
+ set stats(dirs) 0;
+ set stats(files) 0;
+ set stats(execs) 0
+
+ findLibs;
+ findLibUsers "/";
+ emitLibDetails;
+
+ puts [format "Searched %d directories, %d executables (%d dynamic) for %d libraries" \
+ $stats(dirs) $stats(files) $stats(execs) $stats(libs)];
+}
+
+################################################################################
+main;
OpenPOWER on IntegriCloud