xref: /freebsd/tools/LibraryReport/LibraryReport.tcl (revision fa3da92ad61e5fa90227db688446366a8c748b5f)
11b56bb4cSMike Smith#!/bin/sh
21b56bb4cSMike Smith# tcl magic \
31b56bb4cSMike Smithexec tclsh $0 $*
41b56bb4cSMike Smith################################################################################
5fa3da92aSMike Smith# Copyright (C) 1997
6fa3da92aSMike Smith#      Michael Smith.  All rights reserved.
7fa3da92aSMike Smith#
8fa3da92aSMike Smith# Redistribution and use in source and binary forms, with or without
9fa3da92aSMike Smith# modification, are permitted provided that the following conditions
10fa3da92aSMike Smith# are met:
11fa3da92aSMike Smith# 1. Redistributions of source code must retain the above copyright
12fa3da92aSMike Smith#    notice, this list of conditions and the following disclaimer.
13fa3da92aSMike Smith# 2. Redistributions in binary form must reproduce the above copyright
14fa3da92aSMike Smith#    notice, this list of conditions and the following disclaimer in the
15fa3da92aSMike Smith#    documentation and/or other materials provided with the distribution.
16fa3da92aSMike Smith# 3. Neither the name of the author nor the names of any co-contributors
17fa3da92aSMike Smith#    may be used to endorse or promote products derived from this software
18fa3da92aSMike Smith#    without specific prior written permission.
19fa3da92aSMike Smith#
20fa3da92aSMike Smith# THIS SOFTWARE IS PROVIDED BY Michael Smith AND CONTRIBUTORS ``AS IS'' AND
21fa3da92aSMike Smith# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22fa3da92aSMike Smith# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23fa3da92aSMike Smith# ARE DISCLAIMED.  IN NO EVENT SHALL Michael Smith OR CONTRIBUTORS BE LIABLE
24fa3da92aSMike Smith# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25fa3da92aSMike Smith# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26fa3da92aSMike Smith# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27fa3da92aSMike Smith# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28fa3da92aSMike Smith# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29fa3da92aSMike Smith# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30fa3da92aSMike Smith# SUCH DAMAGE.
31fa3da92aSMike Smith################################################################################
321b56bb4cSMike Smith#
331b56bb4cSMike Smith# LibraryReport; produce a list of shared libraries on the system, and a list of
341b56bb4cSMike Smith# all executables that use them.
351b56bb4cSMike Smith#
361b56bb4cSMike Smith################################################################################
371b56bb4cSMike Smith#
381b56bb4cSMike Smith# Stage 1 looks for shared libraries; the output of 'ldconfig -r' is examined
391b56bb4cSMike Smith# for hints as to where to look for libraries (but not trusted as a complete
401b56bb4cSMike Smith# list).
411b56bb4cSMike Smith#
421b56bb4cSMike Smith# These libraries each get an entry in the global 'Libs()' array.
431b56bb4cSMike Smith#
441b56bb4cSMike Smith# Stage 2 walks the entire system directory heirachy looking for executable
451b56bb4cSMike Smith# files, applies 'ldd' to them and attempts to determine which libraries are
461b56bb4cSMike Smith# used.  The path of the executable is then added to the 'Libs()' array
471b56bb4cSMike Smith# for each library used.
481b56bb4cSMike Smith#
491b56bb4cSMike Smith# Stage 3 reports on the day's findings.
501b56bb4cSMike Smith#
511b56bb4cSMike Smith################################################################################
521b56bb4cSMike Smith#
53fa3da92aSMike Smith# $Id: LibraryReport.tcl,v 1.1.1.1 1997/01/02 03:49:35 msmith Exp $
541b56bb4cSMike Smith#
551b56bb4cSMike Smith
561b56bb4cSMike Smith#########################################################################################
571b56bb4cSMike Smith# findLibs
581b56bb4cSMike Smith#
591b56bb4cSMike Smith# Ask ldconfig where it thinks libraries are to be found.  Go look for them, and
601b56bb4cSMike Smith# add an element to 'Libs' for everything that looks like a library.
611b56bb4cSMike Smith#
621b56bb4cSMike Smithproc findLibs {} {
631b56bb4cSMike Smith
641b56bb4cSMike Smith    global Libs stats verbose;
651b56bb4cSMike Smith
661b56bb4cSMike Smith    # Older ldconfigs return a junk value when asked for a report
671b56bb4cSMike Smith    if {[catch {set liblist [exec ldconfig -r]} err]} {	# get ldconfig output
681b56bb4cSMike Smith	puts stderr "ldconfig returned nonzero, persevering.";
691b56bb4cSMike Smith	set liblist $err;				# there's junk in this
701b56bb4cSMike Smith    }
711b56bb4cSMike Smith
721b56bb4cSMike Smith    # remove hintsfile name, convert to list
731b56bb4cSMike Smith    set liblist [lrange [split $liblist "\n"] 1 end];
741b56bb4cSMike Smith
751b56bb4cSMike Smith    set libdirs "";				# no directories yet
761b56bb4cSMike Smith    foreach line $liblist {
771b56bb4cSMike Smith	# parse ldconfig output
781b56bb4cSMike Smith	if {[scan $line "%s => %s" junk libname] == 2} {
791b56bb4cSMike Smith	    # find directory name
801b56bb4cSMike Smith	    set libdir [file dirname $libname];
811b56bb4cSMike Smith	    # have we got this one already?
821b56bb4cSMike Smith	    if {[lsearch -exact $libdirs $libdir] == -1} {
831b56bb4cSMike Smith		lappend libdirs $libdir;
841b56bb4cSMike Smith	    }
851b56bb4cSMike Smith	} else {
861b56bb4cSMike Smith	    puts stderr "Unparseable ldconfig output line :";
871b56bb4cSMike Smith	    puts stderr $line;
881b56bb4cSMike Smith	}
891b56bb4cSMike Smith    }
901b56bb4cSMike Smith
911b56bb4cSMike Smith    # libdirs is now a list of directories that we might find libraries in
921b56bb4cSMike Smith    foreach dir $libdirs {
931b56bb4cSMike Smith	# get the names of anything that looks like a library
941b56bb4cSMike Smith	set libnames [glob -nocomplain "$dir/lib*.so.*"]
951b56bb4cSMike Smith	foreach lib $libnames {
96fa3da92aSMike Smith	    set type [file type $lib];			# what is it?
97fa3da92aSMike Smith	    switch $type {
98fa3da92aSMike Smith		file {		# looks like a library
99fa3da92aSMike Smith		    # may have already been referenced by a symlink
100fa3da92aSMike Smith		    if {![info exists Libs($lib)]} {
1011b56bb4cSMike Smith			set Libs($lib) "";		# add it to our list
1021b56bb4cSMike Smith			if {$verbose} {puts "+ $lib";}
1031b56bb4cSMike Smith		    }
1041b56bb4cSMike Smith		}
105fa3da92aSMike Smith		link {		# symlink; probably to another library
106fa3da92aSMike Smith		    # If the readlink fails, the symlink is stale
107fa3da92aSMike Smith		    if {[catch {set ldest [file readlink $lib]}]} {
108fa3da92aSMike Smith			puts stderr "Symbolic link points to nothing : $lib";
109fa3da92aSMike Smith		    } else {
110fa3da92aSMike Smith			# may have already been referenced by another symlink
111fa3da92aSMike Smith			if {![info exists Libs($lib)]} {
112fa3da92aSMike Smith			    set Libs($lib) "";		# add it to our list
113fa3da92aSMike Smith			    if {$verbose} {puts "+ $lib";}
114fa3da92aSMike Smith			}
115fa3da92aSMike Smith			# list the symlink as a consumer of this library
116fa3da92aSMike Smith			lappend Libs($ldest) "($lib)";
117fa3da92aSMike Smith			if {$verbose} {puts "-> $ldest";}
118fa3da92aSMike Smith		    }
119fa3da92aSMike Smith		}
120fa3da92aSMike Smith	    }
121fa3da92aSMike Smith	}
122fa3da92aSMike Smith    }
1231b56bb4cSMike Smith    set stats(libs) [llength [array names Libs]];
1241b56bb4cSMike Smith}
1251b56bb4cSMike Smith
1261b56bb4cSMike Smith################################################################################
1271b56bb4cSMike Smith# findLibUsers
1281b56bb4cSMike Smith#
1291b56bb4cSMike Smith# Look in the directory (dir) for executables.  If we find any, call
1301b56bb4cSMike Smith# examineExecutable to see if it uses any shared libraries.  Call ourselves
1311b56bb4cSMike Smith# on any directories we find.
1321b56bb4cSMike Smith#
1331b56bb4cSMike Smith# Note that the use of "*" as a glob pattern means we miss directories and
1341b56bb4cSMike Smith# executables starting with '.'.  This is a Feature.
1351b56bb4cSMike Smith#
1361b56bb4cSMike Smithproc findLibUsers {dir} {
1371b56bb4cSMike Smith
1381b56bb4cSMike Smith    global stats verbose;
1391b56bb4cSMike Smith
1401b56bb4cSMike Smith    if {[catch {
1411b56bb4cSMike Smith	set ents [glob -nocomplain "$dir/*"];
1421b56bb4cSMike Smith    } msg]} {
1431b56bb4cSMike Smith	if {$msg == ""} {
1441b56bb4cSMike Smith	    set msg "permission denied";
1451b56bb4cSMike Smith	}
1461b56bb4cSMike Smith	puts stderr "Can't search under '$dir' : $msg";
1471b56bb4cSMike Smith	return ;
1481b56bb4cSMike Smith    }
1491b56bb4cSMike Smith
1501b56bb4cSMike Smith    if {$verbose} {puts "===>> $dir";}
1511b56bb4cSMike Smith    incr stats(dirs);
1521b56bb4cSMike Smith
1531b56bb4cSMike Smith    # files?
1541b56bb4cSMike Smith    foreach f $ents {
1551b56bb4cSMike Smith	# executable?
1561b56bb4cSMike Smith	if {[file executable $f]} {
1571b56bb4cSMike Smith	    # really a file?
1581b56bb4cSMike Smith	    if {[file isfile $f]} {
1591b56bb4cSMike Smith		incr stats(files);
1601b56bb4cSMike Smith		examineExecutable $f;
1611b56bb4cSMike Smith	    }
1621b56bb4cSMike Smith	}
1631b56bb4cSMike Smith    }
1641b56bb4cSMike Smith    # subdirs?
1651b56bb4cSMike Smith    foreach f $ents {
1661b56bb4cSMike Smith	# maybe a directory with more files?
1671b56bb4cSMike Smith	# don't use 'file isdirectory' because that follows symlinks
1681b56bb4cSMike Smith	if {[catch {set type [file type $f]}]} {
1691b56bb4cSMike Smith	    continue ;		# may not be able to stat
1701b56bb4cSMike Smith	}
1711b56bb4cSMike Smith	if {$type == "directory"} {
1721b56bb4cSMike Smith	    findLibUsers $f;
1731b56bb4cSMike Smith	}
1741b56bb4cSMike Smith    }
1751b56bb4cSMike Smith}
1761b56bb4cSMike Smith
1771b56bb4cSMike Smith################################################################################
1781b56bb4cSMike Smith# examineExecutable
1791b56bb4cSMike Smith#
1801b56bb4cSMike Smith# Look at (fname) and see if ldd thinks it references any shared libraries.
1811b56bb4cSMike Smith# If it does, update Libs with the information.
1821b56bb4cSMike Smith#
1831b56bb4cSMike Smithproc examineExecutable {fname} {
1841b56bb4cSMike Smith
1851b56bb4cSMike Smith    global Libs stats verbose;
1861b56bb4cSMike Smith
1871b56bb4cSMike Smith    # ask Mr. Ldd.
1881b56bb4cSMike Smith    if {[catch {set result [exec ldd $fname]} msg]} {
1891b56bb4cSMike Smith	return ;	# not dynamic
1901b56bb4cSMike Smith    }
1911b56bb4cSMike Smith
1921b56bb4cSMike Smith    if {$verbose} {puts -nonewline "$fname : ";}
1931b56bb4cSMike Smith    incr stats(execs);
1941b56bb4cSMike Smith
1951b56bb4cSMike Smith    # For a non-shared executable, we get a single-line error message.
1961b56bb4cSMike Smith    # For a shared executable, we get a heading line, so in either case
1971b56bb4cSMike Smith    # we can discard the first line and any subsequent lines are libraries
1981b56bb4cSMike Smith    # that are required.
1991b56bb4cSMike Smith    set llist [lrange [split $result "\n"] 1 end];
2001b56bb4cSMike Smith    set uses "";
2011b56bb4cSMike Smith
2021b56bb4cSMike Smith    foreach line $llist {
2031b56bb4cSMike Smith	if {[scan $line "%s => %s %s" junk1 lib junk2] == 3} {
204fa3da92aSMike Smith	    if {$lib == "not"} {	# "not found" error
205fa3da92aSMike Smith		set mlname [string range $junk1 2 end];
206fa3da92aSMike Smith		puts stderr "$fname : library '$mlname' not known.";
207fa3da92aSMike Smith	    } else {
2081b56bb4cSMike Smith		lappend Libs($lib) $fname;
2091b56bb4cSMike Smith		lappend uses $lib;
210fa3da92aSMike Smith	    }
2111b56bb4cSMike Smith	} else {
212fa3da92aSMike Smith	    puts stderr "Unparseable ldd output line :";
2131b56bb4cSMike Smith	    puts stderr $line;
2141b56bb4cSMike Smith	}
2151b56bb4cSMike Smith    }
2161b56bb4cSMike Smith    if {$verbose} {puts "$uses";}
2171b56bb4cSMike Smith}
2181b56bb4cSMike Smith
2191b56bb4cSMike Smith################################################################################
2201b56bb4cSMike Smith# emitLibDetails
2211b56bb4cSMike Smith#
2221b56bb4cSMike Smith# Emit a listing of libraries and the executables that use them.
2231b56bb4cSMike Smith#
2241b56bb4cSMike Smithproc emitLibDetails {} {
2251b56bb4cSMike Smith
2261b56bb4cSMike Smith    global Libs;
2271b56bb4cSMike Smith
2281b56bb4cSMike Smith    # divide into used/unused
2291b56bb4cSMike Smith    set used "";
2301b56bb4cSMike Smith    set unused "";
2311b56bb4cSMike Smith    foreach lib [array names Libs] {
2321b56bb4cSMike Smith	if {$Libs($lib) == ""} {
2331b56bb4cSMike Smith	    lappend unused $lib;
2341b56bb4cSMike Smith	} else {
2351b56bb4cSMike Smith	    lappend used $lib;
2361b56bb4cSMike Smith	}
2371b56bb4cSMike Smith    }
2381b56bb4cSMike Smith
2391b56bb4cSMike Smith    # emit used list
2401b56bb4cSMike Smith    puts "== Current Shared Libraries ==================================================";
2411b56bb4cSMike Smith    foreach lib [lsort $used] {
2421b56bb4cSMike Smith	# sort executable names
2431b56bb4cSMike Smith	set users [lsort $Libs($lib)];
2441b56bb4cSMike Smith	puts [format "%-30s  %s" $lib $users];
2451b56bb4cSMike Smith    }
2461b56bb4cSMike Smith    # emit unused
2471b56bb4cSMike Smith    puts "== Stale Shared Libraries ====================================================";
2481b56bb4cSMike Smith    foreach lib [lsort $unused] {
2491b56bb4cSMike Smith	# sort executable names
2501b56bb4cSMike Smith	set users [lsort $Libs($lib)];
2511b56bb4cSMike Smith	puts [format "%-30s  %s" $lib $users];
2521b56bb4cSMike Smith    }
2531b56bb4cSMike Smith}
2541b56bb4cSMike Smith
2551b56bb4cSMike Smith################################################################################
2561b56bb4cSMike Smith# Run the whole shebang
2571b56bb4cSMike Smith#
2581b56bb4cSMike Smithproc main {} {
2591b56bb4cSMike Smith
2601b56bb4cSMike Smith    global stats verbose argv;
2611b56bb4cSMike Smith
2621b56bb4cSMike Smith    set verbose 0;
2631b56bb4cSMike Smith    foreach arg $argv {
2641b56bb4cSMike Smith	switch -- $arg {
2651b56bb4cSMike Smith	    -v {
2661b56bb4cSMike Smith		set verbose 1;
2671b56bb4cSMike Smith	    }
2681b56bb4cSMike Smith	    default {
269fa3da92aSMike Smith		puts stderr "Unknown option '$arg'.";
2701b56bb4cSMike Smith		exit ;
2711b56bb4cSMike Smith	    }
2721b56bb4cSMike Smith	}
2731b56bb4cSMike Smith    }
2741b56bb4cSMike Smith
2751b56bb4cSMike Smith    set stats(libs) 0;
2761b56bb4cSMike Smith    set stats(dirs) 0;
2771b56bb4cSMike Smith    set stats(files) 0;
2781b56bb4cSMike Smith    set stats(execs) 0
2791b56bb4cSMike Smith
2801b56bb4cSMike Smith    findLibs;
2811b56bb4cSMike Smith    findLibUsers "/";
2821b56bb4cSMike Smith    emitLibDetails;
2831b56bb4cSMike Smith
284fa3da92aSMike Smith    puts [format "Searched %d directories, %d executables (%d dynamic) for %d libraries." \
2851b56bb4cSMike Smith	      $stats(dirs) $stats(files) $stats(execs) $stats(libs)];
2861b56bb4cSMike Smith}
2871b56bb4cSMike Smith
2881b56bb4cSMike Smith################################################################################
2891b56bb4cSMike Smithmain;
290