121183Smsmith#!/bin/sh 221183Smsmith# tcl magic \ 321183Smsmithexec tclsh $0 $* 421183Smsmith################################################################################ 521359Smsmith# Copyright (C) 1997 621359Smsmith# Michael Smith. All rights reserved. 721183Smsmith# 821359Smsmith# Redistribution and use in source and binary forms, with or without 921359Smsmith# modification, are permitted provided that the following conditions 1021359Smsmith# are met: 1121359Smsmith# 1. Redistributions of source code must retain the above copyright 1221359Smsmith# notice, this list of conditions and the following disclaimer. 1321359Smsmith# 2. Redistributions in binary form must reproduce the above copyright 1421359Smsmith# notice, this list of conditions and the following disclaimer in the 1521359Smsmith# documentation and/or other materials provided with the distribution. 1621359Smsmith# 3. Neither the name of the author nor the names of any co-contributors 1721359Smsmith# may be used to endorse or promote products derived from this software 1821359Smsmith# without specific prior written permission. 1921359Smsmith# 2021359Smsmith# THIS SOFTWARE IS PROVIDED BY Michael Smith AND CONTRIBUTORS ``AS IS'' AND 2121359Smsmith# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2221359Smsmith# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2321359Smsmith# ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith OR CONTRIBUTORS BE LIABLE 2421359Smsmith# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2521359Smsmith# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2621359Smsmith# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2721359Smsmith# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2821359Smsmith# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2921359Smsmith# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3021359Smsmith# SUCH DAMAGE. 3121359Smsmith################################################################################ 3221359Smsmith# 3321183Smsmith# LibraryReport; produce a list of shared libraries on the system, and a list of 3421183Smsmith# all executables that use them. 3521183Smsmith# 3621183Smsmith################################################################################ 3721183Smsmith# 3821183Smsmith# Stage 1 looks for shared libraries; the output of 'ldconfig -r' is examined 3921183Smsmith# for hints as to where to look for libraries (but not trusted as a complete 4021183Smsmith# list). 4121183Smsmith# 4221183Smsmith# These libraries each get an entry in the global 'Libs()' array. 4321183Smsmith# 4421183Smsmith# Stage 2 walks the entire system directory heirachy looking for executable 4521183Smsmith# files, applies 'ldd' to them and attempts to determine which libraries are 4621183Smsmith# used. The path of the executable is then added to the 'Libs()' array 4721183Smsmith# for each library used. 4821183Smsmith# 4921183Smsmith# Stage 3 reports on the day's findings. 5021183Smsmith# 5121183Smsmith################################################################################ 5221183Smsmith# 5350477Speter# $FreeBSD$ 5421183Smsmith# 5521183Smsmith 5621183Smsmith######################################################################################### 5721183Smsmith# findLibs 5821183Smsmith# 5921183Smsmith# Ask ldconfig where it thinks libraries are to be found. Go look for them, and 6021183Smsmith# add an element to 'Libs' for everything that looks like a library. 6121183Smsmith# 6221183Smsmithproc findLibs {} { 6321183Smsmith 6421183Smsmith global Libs stats verbose; 6521183Smsmith 6621183Smsmith # Older ldconfigs return a junk value when asked for a report 6721183Smsmith if {[catch {set liblist [exec ldconfig -r]} err]} { # get ldconfig output 6821183Smsmith puts stderr "ldconfig returned nonzero, persevering."; 6921183Smsmith set liblist $err; # there's junk in this 7021183Smsmith } 7121183Smsmith 7221183Smsmith # remove hintsfile name, convert to list 7321183Smsmith set liblist [lrange [split $liblist "\n"] 1 end]; 7421183Smsmith 7521183Smsmith set libdirs ""; # no directories yet 7621183Smsmith foreach line $liblist { 7721183Smsmith # parse ldconfig output 7821183Smsmith if {[scan $line "%s => %s" junk libname] == 2} { 7921183Smsmith # find directory name 8021183Smsmith set libdir [file dirname $libname]; 8121183Smsmith # have we got this one already? 8221183Smsmith if {[lsearch -exact $libdirs $libdir] == -1} { 8321183Smsmith lappend libdirs $libdir; 8421183Smsmith } 8521183Smsmith } else { 8621183Smsmith puts stderr "Unparseable ldconfig output line :"; 8721183Smsmith puts stderr $line; 8821183Smsmith } 8921183Smsmith } 9021183Smsmith 9121183Smsmith # libdirs is now a list of directories that we might find libraries in 9221183Smsmith foreach dir $libdirs { 9321183Smsmith # get the names of anything that looks like a library 9421183Smsmith set libnames [glob -nocomplain "$dir/lib*.so.*"] 9521183Smsmith foreach lib $libnames { 9621359Smsmith set type [file type $lib]; # what is it? 9721359Smsmith switch $type { 9821359Smsmith file { # looks like a library 9921359Smsmith # may have already been referenced by a symlink 10021359Smsmith if {![info exists Libs($lib)]} { 10121359Smsmith set Libs($lib) ""; # add it to our list 10221359Smsmith if {$verbose} {puts "+ $lib";} 10321359Smsmith } 10421359Smsmith } 10521359Smsmith link { # symlink; probably to another library 10621359Smsmith # If the readlink fails, the symlink is stale 10721359Smsmith if {[catch {set ldest [file readlink $lib]}]} { 10821359Smsmith puts stderr "Symbolic link points to nothing : $lib"; 10921359Smsmith } else { 11021359Smsmith # may have already been referenced by another symlink 11121359Smsmith if {![info exists Libs($lib)]} { 11221359Smsmith set Libs($lib) ""; # add it to our list 11321359Smsmith if {$verbose} {puts "+ $lib";} 11421359Smsmith } 11521359Smsmith # list the symlink as a consumer of this library 11621359Smsmith lappend Libs($ldest) "($lib)"; 11721359Smsmith if {$verbose} {puts "-> $ldest";} 11821359Smsmith } 11921359Smsmith } 12021359Smsmith } 12121183Smsmith } 12221183Smsmith } 12321183Smsmith set stats(libs) [llength [array names Libs]]; 12421183Smsmith} 12521183Smsmith 12621183Smsmith################################################################################ 12721183Smsmith# findLibUsers 12821183Smsmith# 12921183Smsmith# Look in the directory (dir) for executables. If we find any, call 13021183Smsmith# examineExecutable to see if it uses any shared libraries. Call ourselves 13121183Smsmith# on any directories we find. 13221183Smsmith# 13321183Smsmith# Note that the use of "*" as a glob pattern means we miss directories and 13421183Smsmith# executables starting with '.'. This is a Feature. 13521183Smsmith# 13621183Smsmithproc findLibUsers {dir} { 13721183Smsmith 13821183Smsmith global stats verbose; 13921183Smsmith 14021183Smsmith if {[catch { 14121183Smsmith set ents [glob -nocomplain "$dir/*"]; 14221183Smsmith } msg]} { 14321183Smsmith if {$msg == ""} { 14421183Smsmith set msg "permission denied"; 14521183Smsmith } 14621183Smsmith puts stderr "Can't search under '$dir' : $msg"; 14721183Smsmith return ; 14821183Smsmith } 14921183Smsmith 15021183Smsmith if {$verbose} {puts "===>> $dir";} 15121183Smsmith incr stats(dirs); 15221183Smsmith 15321183Smsmith # files? 15421183Smsmith foreach f $ents { 15521183Smsmith # executable? 15621183Smsmith if {[file executable $f]} { 15721183Smsmith # really a file? 15821183Smsmith if {[file isfile $f]} { 15921183Smsmith incr stats(files); 16021183Smsmith examineExecutable $f; 16121183Smsmith } 16221183Smsmith } 16321183Smsmith } 16421183Smsmith # subdirs? 16521183Smsmith foreach f $ents { 16621183Smsmith # maybe a directory with more files? 16721183Smsmith # don't use 'file isdirectory' because that follows symlinks 16821183Smsmith if {[catch {set type [file type $f]}]} { 16921183Smsmith continue ; # may not be able to stat 17021183Smsmith } 17121183Smsmith if {$type == "directory"} { 17221183Smsmith findLibUsers $f; 17321183Smsmith } 17421183Smsmith } 17521183Smsmith} 17621183Smsmith 17721183Smsmith################################################################################ 17821183Smsmith# examineExecutable 17921183Smsmith# 18021183Smsmith# Look at (fname) and see if ldd thinks it references any shared libraries. 18121183Smsmith# If it does, update Libs with the information. 18221183Smsmith# 18321183Smsmithproc examineExecutable {fname} { 18421183Smsmith 18521183Smsmith global Libs stats verbose; 18621183Smsmith 18721183Smsmith # ask Mr. Ldd. 18821183Smsmith if {[catch {set result [exec ldd $fname]} msg]} { 18921183Smsmith return ; # not dynamic 19021183Smsmith } 19121183Smsmith 19221183Smsmith if {$verbose} {puts -nonewline "$fname : ";} 19321183Smsmith incr stats(execs); 19421183Smsmith 19521183Smsmith # For a non-shared executable, we get a single-line error message. 19621183Smsmith # For a shared executable, we get a heading line, so in either case 19721183Smsmith # we can discard the first line and any subsequent lines are libraries 19821183Smsmith # that are required. 19921183Smsmith set llist [lrange [split $result "\n"] 1 end]; 20021183Smsmith set uses ""; 20121183Smsmith 20221183Smsmith foreach line $llist { 20321183Smsmith if {[scan $line "%s => %s %s" junk1 lib junk2] == 3} { 20421359Smsmith if {$lib == "not"} { # "not found" error 20521359Smsmith set mlname [string range $junk1 2 end]; 20621359Smsmith puts stderr "$fname : library '$mlname' not known."; 20721359Smsmith } else { 20821359Smsmith lappend Libs($lib) $fname; 20921359Smsmith lappend uses $lib; 21021359Smsmith } 21121183Smsmith } else { 21221359Smsmith puts stderr "Unparseable ldd output line :"; 21321183Smsmith puts stderr $line; 21421183Smsmith } 21521183Smsmith } 21621183Smsmith if {$verbose} {puts "$uses";} 21721183Smsmith} 21821183Smsmith 21921183Smsmith################################################################################ 22021183Smsmith# emitLibDetails 22121183Smsmith# 22221183Smsmith# Emit a listing of libraries and the executables that use them. 22321183Smsmith# 22421183Smsmithproc emitLibDetails {} { 22521183Smsmith 22621183Smsmith global Libs; 22721183Smsmith 22821183Smsmith # divide into used/unused 22921183Smsmith set used ""; 23021183Smsmith set unused ""; 23121183Smsmith foreach lib [array names Libs] { 23221183Smsmith if {$Libs($lib) == ""} { 23321183Smsmith lappend unused $lib; 23421183Smsmith } else { 23521183Smsmith lappend used $lib; 23621183Smsmith } 23721183Smsmith } 23821183Smsmith 23921183Smsmith # emit used list 24021183Smsmith puts "== Current Shared Libraries =================================================="; 24121183Smsmith foreach lib [lsort $used] { 24221183Smsmith # sort executable names 24321183Smsmith set users [lsort $Libs($lib)]; 24421183Smsmith puts [format "%-30s %s" $lib $users]; 24521183Smsmith } 24621183Smsmith # emit unused 24721183Smsmith puts "== Stale Shared Libraries ===================================================="; 24821183Smsmith foreach lib [lsort $unused] { 24921183Smsmith # sort executable names 25021183Smsmith set users [lsort $Libs($lib)]; 25121183Smsmith puts [format "%-30s %s" $lib $users]; 25221183Smsmith } 25321183Smsmith} 25421183Smsmith 25521183Smsmith################################################################################ 25621183Smsmith# Run the whole shebang 25721183Smsmith# 25821183Smsmithproc main {} { 25921183Smsmith 26021183Smsmith global stats verbose argv; 26121183Smsmith 26221183Smsmith set verbose 0; 26321183Smsmith foreach arg $argv { 26421183Smsmith switch -- $arg { 26521183Smsmith -v { 26621183Smsmith set verbose 1; 26721183Smsmith } 26821183Smsmith default { 26921359Smsmith puts stderr "Unknown option '$arg'."; 27021183Smsmith exit ; 27121183Smsmith } 27221183Smsmith } 27321183Smsmith } 27421183Smsmith 27521183Smsmith set stats(libs) 0; 27621183Smsmith set stats(dirs) 0; 27721183Smsmith set stats(files) 0; 27821183Smsmith set stats(execs) 0 27921183Smsmith 28021183Smsmith findLibs; 28121183Smsmith findLibUsers "/"; 28221183Smsmith emitLibDetails; 28321183Smsmith 28421359Smsmith puts [format "Searched %d directories, %d executables (%d dynamic) for %d libraries." \ 28521183Smsmith $stats(dirs) $stats(files) $stats(execs) $stats(libs)]; 28621183Smsmith} 28721183Smsmith 28821183Smsmith################################################################################ 28921183Smsmithmain; 290