1*86d7f5d3SJohn Marino#!/bin/sh 2*86d7f5d3SJohn Marino# tcl magic \ 3*86d7f5d3SJohn Marinoexec tclsh $0 $* 4*86d7f5d3SJohn Marino################################################################################ 5*86d7f5d3SJohn Marino# Copyright (C) 1997 6*86d7f5d3SJohn Marino# Michael Smith. All rights reserved. 7*86d7f5d3SJohn Marino# 8*86d7f5d3SJohn Marino# Redistribution and use in source and binary forms, with or without 9*86d7f5d3SJohn Marino# modification, are permitted provided that the following conditions 10*86d7f5d3SJohn Marino# are met: 11*86d7f5d3SJohn Marino# 1. Redistributions of source code must retain the above copyright 12*86d7f5d3SJohn Marino# notice, this list of conditions and the following disclaimer. 13*86d7f5d3SJohn Marino# 2. Redistributions in binary form must reproduce the above copyright 14*86d7f5d3SJohn Marino# notice, this list of conditions and the following disclaimer in the 15*86d7f5d3SJohn Marino# documentation and/or other materials provided with the distribution. 16*86d7f5d3SJohn Marino# 3. Neither the name of the author nor the names of any co-contributors 17*86d7f5d3SJohn Marino# may be used to endorse or promote products derived from this software 18*86d7f5d3SJohn Marino# without specific prior written permission. 19*86d7f5d3SJohn Marino# 20*86d7f5d3SJohn Marino# THIS SOFTWARE IS PROVIDED BY Michael Smith AND CONTRIBUTORS ``AS IS'' AND 21*86d7f5d3SJohn Marino# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22*86d7f5d3SJohn Marino# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23*86d7f5d3SJohn Marino# ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith OR CONTRIBUTORS BE LIABLE 24*86d7f5d3SJohn Marino# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25*86d7f5d3SJohn Marino# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26*86d7f5d3SJohn Marino# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27*86d7f5d3SJohn Marino# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28*86d7f5d3SJohn Marino# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29*86d7f5d3SJohn Marino# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30*86d7f5d3SJohn Marino# SUCH DAMAGE. 31*86d7f5d3SJohn Marino################################################################################ 32*86d7f5d3SJohn Marino# 33*86d7f5d3SJohn Marino# LibraryReport; produce a list of shared libraries on the system, and a list of 34*86d7f5d3SJohn Marino# all executables that use them. 35*86d7f5d3SJohn Marino# 36*86d7f5d3SJohn Marino################################################################################ 37*86d7f5d3SJohn Marino# 38*86d7f5d3SJohn Marino# Stage 1 looks for shared libraries; the output of 'ldconfig -r' is examined 39*86d7f5d3SJohn Marino# for hints as to where to look for libraries (but not trusted as a complete 40*86d7f5d3SJohn Marino# list). 41*86d7f5d3SJohn Marino# 42*86d7f5d3SJohn Marino# These libraries each get an entry in the global 'Libs()' array. 43*86d7f5d3SJohn Marino# 44*86d7f5d3SJohn Marino# Stage 2 walks the entire system directory heirachy looking for executable 45*86d7f5d3SJohn Marino# files, applies 'ldd' to them and attempts to determine which libraries are 46*86d7f5d3SJohn Marino# used. The path of the executable is then added to the 'Libs()' array 47*86d7f5d3SJohn Marino# for each library used. 48*86d7f5d3SJohn Marino# 49*86d7f5d3SJohn Marino# Stage 3 reports on the day's findings. 50*86d7f5d3SJohn Marino# 51*86d7f5d3SJohn Marino################################################################################ 52*86d7f5d3SJohn Marino# 53*86d7f5d3SJohn Marino# $FreeBSD: src/tools/LibraryReport/LibraryReport.tcl,v 1.5 1999/08/28 00:54:21 peter Exp $ 54*86d7f5d3SJohn Marino# $DragonFly: src/tools/LibraryReport/LibraryReport.tcl,v 1.2 2003/06/17 04:29:11 dillon Exp $ 55*86d7f5d3SJohn Marino# 56*86d7f5d3SJohn Marino 57*86d7f5d3SJohn Marino######################################################################################### 58*86d7f5d3SJohn Marino# findLibs 59*86d7f5d3SJohn Marino# 60*86d7f5d3SJohn Marino# Ask ldconfig where it thinks libraries are to be found. Go look for them, and 61*86d7f5d3SJohn Marino# add an element to 'Libs' for everything that looks like a library. 62*86d7f5d3SJohn Marino# 63*86d7f5d3SJohn Marinoproc findLibs {} { 64*86d7f5d3SJohn Marino 65*86d7f5d3SJohn Marino global Libs stats verbose; 66*86d7f5d3SJohn Marino 67*86d7f5d3SJohn Marino # Older ldconfigs return a junk value when asked for a report 68*86d7f5d3SJohn Marino if {[catch {set liblist [exec ldconfig -r]} err]} { # get ldconfig output 69*86d7f5d3SJohn Marino puts stderr "ldconfig returned nonzero, persevering."; 70*86d7f5d3SJohn Marino set liblist $err; # there's junk in this 71*86d7f5d3SJohn Marino } 72*86d7f5d3SJohn Marino 73*86d7f5d3SJohn Marino # remove hintsfile name, convert to list 74*86d7f5d3SJohn Marino set liblist [lrange [split $liblist "\n"] 1 end]; 75*86d7f5d3SJohn Marino 76*86d7f5d3SJohn Marino set libdirs ""; # no directories yet 77*86d7f5d3SJohn Marino foreach line $liblist { 78*86d7f5d3SJohn Marino # parse ldconfig output 79*86d7f5d3SJohn Marino if {[scan $line "%s => %s" junk libname] == 2} { 80*86d7f5d3SJohn Marino # find directory name 81*86d7f5d3SJohn Marino set libdir [file dirname $libname]; 82*86d7f5d3SJohn Marino # have we got this one already? 83*86d7f5d3SJohn Marino if {[lsearch -exact $libdirs $libdir] == -1} { 84*86d7f5d3SJohn Marino lappend libdirs $libdir; 85*86d7f5d3SJohn Marino } 86*86d7f5d3SJohn Marino } else { 87*86d7f5d3SJohn Marino puts stderr "Unparseable ldconfig output line :"; 88*86d7f5d3SJohn Marino puts stderr $line; 89*86d7f5d3SJohn Marino } 90*86d7f5d3SJohn Marino } 91*86d7f5d3SJohn Marino 92*86d7f5d3SJohn Marino # libdirs is now a list of directories that we might find libraries in 93*86d7f5d3SJohn Marino foreach dir $libdirs { 94*86d7f5d3SJohn Marino # get the names of anything that looks like a library 95*86d7f5d3SJohn Marino set libnames [glob -nocomplain "$dir/lib*.so.*"] 96*86d7f5d3SJohn Marino foreach lib $libnames { 97*86d7f5d3SJohn Marino set type [file type $lib]; # what is it? 98*86d7f5d3SJohn Marino switch $type { 99*86d7f5d3SJohn Marino file { # looks like a library 100*86d7f5d3SJohn Marino # may have already been referenced by a symlink 101*86d7f5d3SJohn Marino if {![info exists Libs($lib)]} { 102*86d7f5d3SJohn Marino set Libs($lib) ""; # add it to our list 103*86d7f5d3SJohn Marino if {$verbose} {puts "+ $lib";} 104*86d7f5d3SJohn Marino } 105*86d7f5d3SJohn Marino } 106*86d7f5d3SJohn Marino link { # symlink; probably to another library 107*86d7f5d3SJohn Marino # If the readlink fails, the symlink is stale 108*86d7f5d3SJohn Marino if {[catch {set ldest [file readlink $lib]}]} { 109*86d7f5d3SJohn Marino puts stderr "Symbolic link points to nothing : $lib"; 110*86d7f5d3SJohn Marino } else { 111*86d7f5d3SJohn Marino # may have already been referenced by another symlink 112*86d7f5d3SJohn Marino if {![info exists Libs($lib)]} { 113*86d7f5d3SJohn Marino set Libs($lib) ""; # add it to our list 114*86d7f5d3SJohn Marino if {$verbose} {puts "+ $lib";} 115*86d7f5d3SJohn Marino } 116*86d7f5d3SJohn Marino # list the symlink as a consumer of this library 117*86d7f5d3SJohn Marino lappend Libs($ldest) "($lib)"; 118*86d7f5d3SJohn Marino if {$verbose} {puts "-> $ldest";} 119*86d7f5d3SJohn Marino } 120*86d7f5d3SJohn Marino } 121*86d7f5d3SJohn Marino } 122*86d7f5d3SJohn Marino } 123*86d7f5d3SJohn Marino } 124*86d7f5d3SJohn Marino set stats(libs) [llength [array names Libs]]; 125*86d7f5d3SJohn Marino} 126*86d7f5d3SJohn Marino 127*86d7f5d3SJohn Marino################################################################################ 128*86d7f5d3SJohn Marino# findLibUsers 129*86d7f5d3SJohn Marino# 130*86d7f5d3SJohn Marino# Look in the directory (dir) for executables. If we find any, call 131*86d7f5d3SJohn Marino# examineExecutable to see if it uses any shared libraries. Call ourselves 132*86d7f5d3SJohn Marino# on any directories we find. 133*86d7f5d3SJohn Marino# 134*86d7f5d3SJohn Marino# Note that the use of "*" as a glob pattern means we miss directories and 135*86d7f5d3SJohn Marino# executables starting with '.'. This is a Feature. 136*86d7f5d3SJohn Marino# 137*86d7f5d3SJohn Marinoproc findLibUsers {dir} { 138*86d7f5d3SJohn Marino 139*86d7f5d3SJohn Marino global stats verbose; 140*86d7f5d3SJohn Marino 141*86d7f5d3SJohn Marino if {[catch { 142*86d7f5d3SJohn Marino set ents [glob -nocomplain "$dir/*"]; 143*86d7f5d3SJohn Marino } msg]} { 144*86d7f5d3SJohn Marino if {$msg == ""} { 145*86d7f5d3SJohn Marino set msg "permission denied"; 146*86d7f5d3SJohn Marino } 147*86d7f5d3SJohn Marino puts stderr "Can't search under '$dir' : $msg"; 148*86d7f5d3SJohn Marino return ; 149*86d7f5d3SJohn Marino } 150*86d7f5d3SJohn Marino 151*86d7f5d3SJohn Marino if {$verbose} {puts "===>> $dir";} 152*86d7f5d3SJohn Marino incr stats(dirs); 153*86d7f5d3SJohn Marino 154*86d7f5d3SJohn Marino # files? 155*86d7f5d3SJohn Marino foreach f $ents { 156*86d7f5d3SJohn Marino # executable? 157*86d7f5d3SJohn Marino if {[file executable $f]} { 158*86d7f5d3SJohn Marino # really a file? 159*86d7f5d3SJohn Marino if {[file isfile $f]} { 160*86d7f5d3SJohn Marino incr stats(files); 161*86d7f5d3SJohn Marino examineExecutable $f; 162*86d7f5d3SJohn Marino } 163*86d7f5d3SJohn Marino } 164*86d7f5d3SJohn Marino } 165*86d7f5d3SJohn Marino # subdirs? 166*86d7f5d3SJohn Marino foreach f $ents { 167*86d7f5d3SJohn Marino # maybe a directory with more files? 168*86d7f5d3SJohn Marino # don't use 'file isdirectory' because that follows symlinks 169*86d7f5d3SJohn Marino if {[catch {set type [file type $f]}]} { 170*86d7f5d3SJohn Marino continue ; # may not be able to stat 171*86d7f5d3SJohn Marino } 172*86d7f5d3SJohn Marino if {$type == "directory"} { 173*86d7f5d3SJohn Marino findLibUsers $f; 174*86d7f5d3SJohn Marino } 175*86d7f5d3SJohn Marino } 176*86d7f5d3SJohn Marino} 177*86d7f5d3SJohn Marino 178*86d7f5d3SJohn Marino################################################################################ 179*86d7f5d3SJohn Marino# examineExecutable 180*86d7f5d3SJohn Marino# 181*86d7f5d3SJohn Marino# Look at (fname) and see if ldd thinks it references any shared libraries. 182*86d7f5d3SJohn Marino# If it does, update Libs with the information. 183*86d7f5d3SJohn Marino# 184*86d7f5d3SJohn Marinoproc examineExecutable {fname} { 185*86d7f5d3SJohn Marino 186*86d7f5d3SJohn Marino global Libs stats verbose; 187*86d7f5d3SJohn Marino 188*86d7f5d3SJohn Marino # ask Mr. Ldd. 189*86d7f5d3SJohn Marino if {[catch {set result [exec ldd $fname]} msg]} { 190*86d7f5d3SJohn Marino return ; # not dynamic 191*86d7f5d3SJohn Marino } 192*86d7f5d3SJohn Marino 193*86d7f5d3SJohn Marino if {$verbose} {puts -nonewline "$fname : ";} 194*86d7f5d3SJohn Marino incr stats(execs); 195*86d7f5d3SJohn Marino 196*86d7f5d3SJohn Marino # For a non-shared executable, we get a single-line error message. 197*86d7f5d3SJohn Marino # For a shared executable, we get a heading line, so in either case 198*86d7f5d3SJohn Marino # we can discard the first line and any subsequent lines are libraries 199*86d7f5d3SJohn Marino # that are required. 200*86d7f5d3SJohn Marino set llist [lrange [split $result "\n"] 1 end]; 201*86d7f5d3SJohn Marino set uses ""; 202*86d7f5d3SJohn Marino 203*86d7f5d3SJohn Marino foreach line $llist { 204*86d7f5d3SJohn Marino if {[scan $line "%s => %s %s" junk1 lib junk2] == 3} { 205*86d7f5d3SJohn Marino if {$lib == "not"} { # "not found" error 206*86d7f5d3SJohn Marino set mlname [string range $junk1 2 end]; 207*86d7f5d3SJohn Marino puts stderr "$fname : library '$mlname' not known."; 208*86d7f5d3SJohn Marino } else { 209*86d7f5d3SJohn Marino lappend Libs($lib) $fname; 210*86d7f5d3SJohn Marino lappend uses $lib; 211*86d7f5d3SJohn Marino } 212*86d7f5d3SJohn Marino } else { 213*86d7f5d3SJohn Marino puts stderr "Unparseable ldd output line :"; 214*86d7f5d3SJohn Marino puts stderr $line; 215*86d7f5d3SJohn Marino } 216*86d7f5d3SJohn Marino } 217*86d7f5d3SJohn Marino if {$verbose} {puts "$uses";} 218*86d7f5d3SJohn Marino} 219*86d7f5d3SJohn Marino 220*86d7f5d3SJohn Marino################################################################################ 221*86d7f5d3SJohn Marino# emitLibDetails 222*86d7f5d3SJohn Marino# 223*86d7f5d3SJohn Marino# Emit a listing of libraries and the executables that use them. 224*86d7f5d3SJohn Marino# 225*86d7f5d3SJohn Marinoproc emitLibDetails {} { 226*86d7f5d3SJohn Marino 227*86d7f5d3SJohn Marino global Libs; 228*86d7f5d3SJohn Marino 229*86d7f5d3SJohn Marino # divide into used/unused 230*86d7f5d3SJohn Marino set used ""; 231*86d7f5d3SJohn Marino set unused ""; 232*86d7f5d3SJohn Marino foreach lib [array names Libs] { 233*86d7f5d3SJohn Marino if {$Libs($lib) == ""} { 234*86d7f5d3SJohn Marino lappend unused $lib; 235*86d7f5d3SJohn Marino } else { 236*86d7f5d3SJohn Marino lappend used $lib; 237*86d7f5d3SJohn Marino } 238*86d7f5d3SJohn Marino } 239*86d7f5d3SJohn Marino 240*86d7f5d3SJohn Marino # emit used list 241*86d7f5d3SJohn Marino puts "== Current Shared Libraries =================================================="; 242*86d7f5d3SJohn Marino foreach lib [lsort $used] { 243*86d7f5d3SJohn Marino # sort executable names 244*86d7f5d3SJohn Marino set users [lsort $Libs($lib)]; 245*86d7f5d3SJohn Marino puts [format "%-30s %s" $lib $users]; 246*86d7f5d3SJohn Marino } 247*86d7f5d3SJohn Marino # emit unused 248*86d7f5d3SJohn Marino puts "== Stale Shared Libraries ===================================================="; 249*86d7f5d3SJohn Marino foreach lib [lsort $unused] { 250*86d7f5d3SJohn Marino # sort executable names 251*86d7f5d3SJohn Marino set users [lsort $Libs($lib)]; 252*86d7f5d3SJohn Marino puts [format "%-30s %s" $lib $users]; 253*86d7f5d3SJohn Marino } 254*86d7f5d3SJohn Marino} 255*86d7f5d3SJohn Marino 256*86d7f5d3SJohn Marino################################################################################ 257*86d7f5d3SJohn Marino# Run the whole shebang 258*86d7f5d3SJohn Marino# 259*86d7f5d3SJohn Marinoproc main {} { 260*86d7f5d3SJohn Marino 261*86d7f5d3SJohn Marino global stats verbose argv; 262*86d7f5d3SJohn Marino 263*86d7f5d3SJohn Marino set verbose 0; 264*86d7f5d3SJohn Marino foreach arg $argv { 265*86d7f5d3SJohn Marino switch -- $arg { 266*86d7f5d3SJohn Marino -v { 267*86d7f5d3SJohn Marino set verbose 1; 268*86d7f5d3SJohn Marino } 269*86d7f5d3SJohn Marino default { 270*86d7f5d3SJohn Marino puts stderr "Unknown option '$arg'."; 271*86d7f5d3SJohn Marino exit ; 272*86d7f5d3SJohn Marino } 273*86d7f5d3SJohn Marino } 274*86d7f5d3SJohn Marino } 275*86d7f5d3SJohn Marino 276*86d7f5d3SJohn Marino set stats(libs) 0; 277*86d7f5d3SJohn Marino set stats(dirs) 0; 278*86d7f5d3SJohn Marino set stats(files) 0; 279*86d7f5d3SJohn Marino set stats(execs) 0 280*86d7f5d3SJohn Marino 281*86d7f5d3SJohn Marino findLibs; 282*86d7f5d3SJohn Marino findLibUsers "/"; 283*86d7f5d3SJohn Marino emitLibDetails; 284*86d7f5d3SJohn Marino 285*86d7f5d3SJohn Marino puts [format "Searched %d directories, %d executables (%d dynamic) for %d libraries." \ 286*86d7f5d3SJohn Marino $stats(dirs) $stats(files) $stats(execs) $stats(libs)]; 287*86d7f5d3SJohn Marino} 288*86d7f5d3SJohn Marino 289*86d7f5d3SJohn Marino################################################################################ 290*86d7f5d3SJohn Marinomain; 291