11b56bb4cSMike Smith#!/bin/sh 21b56bb4cSMike Smith# tcl magic \ 31b56bb4cSMike Smithexec tclsh $0 $* 41b56bb4cSMike Smith################################################################################ 51b56bb4cSMike Smith# 61b56bb4cSMike Smith# LibraryReport; produce a list of shared libraries on the system, and a list of 71b56bb4cSMike Smith# all executables that use them. 81b56bb4cSMike Smith# 91b56bb4cSMike Smith################################################################################ 101b56bb4cSMike Smith# 111b56bb4cSMike Smith# Stage 1 looks for shared libraries; the output of 'ldconfig -r' is examined 121b56bb4cSMike Smith# for hints as to where to look for libraries (but not trusted as a complete 131b56bb4cSMike Smith# list). 141b56bb4cSMike Smith# 151b56bb4cSMike Smith# These libraries each get an entry in the global 'Libs()' array. 161b56bb4cSMike Smith# 171b56bb4cSMike Smith# Stage 2 walks the entire system directory heirachy looking for executable 181b56bb4cSMike Smith# files, applies 'ldd' to them and attempts to determine which libraries are 191b56bb4cSMike Smith# used. The path of the executable is then added to the 'Libs()' array 201b56bb4cSMike Smith# for each library used. 211b56bb4cSMike Smith# 221b56bb4cSMike Smith# Stage 3 reports on the day's findings. 231b56bb4cSMike Smith# 241b56bb4cSMike Smith################################################################################ 251b56bb4cSMike Smith# 261b56bb4cSMike Smith# $Id$ 271b56bb4cSMike Smith# 281b56bb4cSMike Smith 291b56bb4cSMike Smith######################################################################################### 301b56bb4cSMike Smith# findLibs 311b56bb4cSMike Smith# 321b56bb4cSMike Smith# Ask ldconfig where it thinks libraries are to be found. Go look for them, and 331b56bb4cSMike Smith# add an element to 'Libs' for everything that looks like a library. 341b56bb4cSMike Smith# 351b56bb4cSMike Smithproc findLibs {} { 361b56bb4cSMike Smith 371b56bb4cSMike Smith global Libs stats verbose; 381b56bb4cSMike Smith 391b56bb4cSMike Smith # Older ldconfigs return a junk value when asked for a report 401b56bb4cSMike Smith if {[catch {set liblist [exec ldconfig -r]} err]} { # get ldconfig output 411b56bb4cSMike Smith puts stderr "ldconfig returned nonzero, persevering."; 421b56bb4cSMike Smith set liblist $err; # there's junk in this 431b56bb4cSMike Smith } 441b56bb4cSMike Smith 451b56bb4cSMike Smith # remove hintsfile name, convert to list 461b56bb4cSMike Smith set liblist [lrange [split $liblist "\n"] 1 end]; 471b56bb4cSMike Smith 481b56bb4cSMike Smith set libdirs ""; # no directories yet 491b56bb4cSMike Smith foreach line $liblist { 501b56bb4cSMike Smith # parse ldconfig output 511b56bb4cSMike Smith if {[scan $line "%s => %s" junk libname] == 2} { 521b56bb4cSMike Smith # find directory name 531b56bb4cSMike Smith set libdir [file dirname $libname]; 541b56bb4cSMike Smith # have we got this one already? 551b56bb4cSMike Smith if {[lsearch -exact $libdirs $libdir] == -1} { 561b56bb4cSMike Smith lappend libdirs $libdir; 571b56bb4cSMike Smith } 581b56bb4cSMike Smith } else { 591b56bb4cSMike Smith puts stderr "Unparseable ldconfig output line :"; 601b56bb4cSMike Smith puts stderr $line; 611b56bb4cSMike Smith } 621b56bb4cSMike Smith } 631b56bb4cSMike Smith 641b56bb4cSMike Smith # libdirs is now a list of directories that we might find libraries in 651b56bb4cSMike Smith foreach dir $libdirs { 661b56bb4cSMike Smith # get the names of anything that looks like a library 671b56bb4cSMike Smith set libnames [glob -nocomplain "$dir/lib*.so.*"] 681b56bb4cSMike Smith foreach lib $libnames { 691b56bb4cSMike Smith set Libs($lib) ""; # add it to our list 701b56bb4cSMike Smith if {$verbose} {puts "+ $lib";} 711b56bb4cSMike Smith } 721b56bb4cSMike Smith } 731b56bb4cSMike Smith set stats(libs) [llength [array names Libs]]; 741b56bb4cSMike Smith} 751b56bb4cSMike Smith 761b56bb4cSMike Smith################################################################################ 771b56bb4cSMike Smith# findLibUsers 781b56bb4cSMike Smith# 791b56bb4cSMike Smith# Look in the directory (dir) for executables. If we find any, call 801b56bb4cSMike Smith# examineExecutable to see if it uses any shared libraries. Call ourselves 811b56bb4cSMike Smith# on any directories we find. 821b56bb4cSMike Smith# 831b56bb4cSMike Smith# Note that the use of "*" as a glob pattern means we miss directories and 841b56bb4cSMike Smith# executables starting with '.'. This is a Feature. 851b56bb4cSMike Smith# 861b56bb4cSMike Smithproc findLibUsers {dir} { 871b56bb4cSMike Smith 881b56bb4cSMike Smith global stats verbose; 891b56bb4cSMike Smith 901b56bb4cSMike Smith if {[catch { 911b56bb4cSMike Smith set ents [glob -nocomplain "$dir/*"]; 921b56bb4cSMike Smith } msg]} { 931b56bb4cSMike Smith if {$msg == ""} { 941b56bb4cSMike Smith set msg "permission denied"; 951b56bb4cSMike Smith } 961b56bb4cSMike Smith puts stderr "Can't search under '$dir' : $msg"; 971b56bb4cSMike Smith return ; 981b56bb4cSMike Smith } 991b56bb4cSMike Smith 1001b56bb4cSMike Smith if {$verbose} {puts "===>> $dir";} 1011b56bb4cSMike Smith incr stats(dirs); 1021b56bb4cSMike Smith 1031b56bb4cSMike Smith # files? 1041b56bb4cSMike Smith foreach f $ents { 1051b56bb4cSMike Smith # executable? 1061b56bb4cSMike Smith if {[file executable $f]} { 1071b56bb4cSMike Smith # really a file? 1081b56bb4cSMike Smith if {[file isfile $f]} { 1091b56bb4cSMike Smith incr stats(files); 1101b56bb4cSMike Smith examineExecutable $f; 1111b56bb4cSMike Smith } 1121b56bb4cSMike Smith } 1131b56bb4cSMike Smith } 1141b56bb4cSMike Smith # subdirs? 1151b56bb4cSMike Smith foreach f $ents { 1161b56bb4cSMike Smith # maybe a directory with more files? 1171b56bb4cSMike Smith # don't use 'file isdirectory' because that follows symlinks 1181b56bb4cSMike Smith if {[catch {set type [file type $f]}]} { 1191b56bb4cSMike Smith continue ; # may not be able to stat 1201b56bb4cSMike Smith } 1211b56bb4cSMike Smith if {$type == "directory"} { 1221b56bb4cSMike Smith findLibUsers $f; 1231b56bb4cSMike Smith } 1241b56bb4cSMike Smith } 1251b56bb4cSMike Smith} 1261b56bb4cSMike Smith 1271b56bb4cSMike Smith################################################################################ 1281b56bb4cSMike Smith# examineExecutable 1291b56bb4cSMike Smith# 1301b56bb4cSMike Smith# Look at (fname) and see if ldd thinks it references any shared libraries. 1311b56bb4cSMike Smith# If it does, update Libs with the information. 1321b56bb4cSMike Smith# 1331b56bb4cSMike Smithproc examineExecutable {fname} { 1341b56bb4cSMike Smith 1351b56bb4cSMike Smith global Libs stats verbose; 1361b56bb4cSMike Smith 1371b56bb4cSMike Smith # ask Mr. Ldd. 1381b56bb4cSMike Smith if {[catch {set result [exec ldd $fname]} msg]} { 1391b56bb4cSMike Smith return ; # not dynamic 1401b56bb4cSMike Smith } 1411b56bb4cSMike Smith 1421b56bb4cSMike Smith if {$verbose} {puts -nonewline "$fname : ";} 1431b56bb4cSMike Smith incr stats(execs); 1441b56bb4cSMike Smith 1451b56bb4cSMike Smith # For a non-shared executable, we get a single-line error message. 1461b56bb4cSMike Smith # For a shared executable, we get a heading line, so in either case 1471b56bb4cSMike Smith # we can discard the first line and any subsequent lines are libraries 1481b56bb4cSMike Smith # that are required. 1491b56bb4cSMike Smith set llist [lrange [split $result "\n"] 1 end]; 1501b56bb4cSMike Smith set uses ""; 1511b56bb4cSMike Smith 1521b56bb4cSMike Smith foreach line $llist { 1531b56bb4cSMike Smith if {[scan $line "%s => %s %s" junk1 lib junk2] == 3} { 1541b56bb4cSMike Smith lappend Libs($lib) $fname; 1551b56bb4cSMike Smith lappend uses $lib; 1561b56bb4cSMike Smith } else { 1571b56bb4cSMike Smith puts stderr "Unparseable ldd putput line :"; 1581b56bb4cSMike Smith puts stderr $line; 1591b56bb4cSMike Smith } 1601b56bb4cSMike Smith } 1611b56bb4cSMike Smith if {$verbose} {puts "$uses";} 1621b56bb4cSMike Smith} 1631b56bb4cSMike Smith 1641b56bb4cSMike Smith################################################################################ 1651b56bb4cSMike Smith# emitLibDetails 1661b56bb4cSMike Smith# 1671b56bb4cSMike Smith# Emit a listing of libraries and the executables that use them. 1681b56bb4cSMike Smith# 1691b56bb4cSMike Smithproc emitLibDetails {} { 1701b56bb4cSMike Smith 1711b56bb4cSMike Smith global Libs; 1721b56bb4cSMike Smith 1731b56bb4cSMike Smith # divide into used/unused 1741b56bb4cSMike Smith set used ""; 1751b56bb4cSMike Smith set unused ""; 1761b56bb4cSMike Smith foreach lib [array names Libs] { 1771b56bb4cSMike Smith if {$Libs($lib) == ""} { 1781b56bb4cSMike Smith lappend unused $lib; 1791b56bb4cSMike Smith } else { 1801b56bb4cSMike Smith lappend used $lib; 1811b56bb4cSMike Smith } 1821b56bb4cSMike Smith } 1831b56bb4cSMike Smith 1841b56bb4cSMike Smith # emit used list 1851b56bb4cSMike Smith puts "== Current Shared Libraries =================================================="; 1861b56bb4cSMike Smith foreach lib [lsort $used] { 1871b56bb4cSMike Smith # sort executable names 1881b56bb4cSMike Smith set users [lsort $Libs($lib)]; 1891b56bb4cSMike Smith puts [format "%-30s %s" $lib $users]; 1901b56bb4cSMike Smith } 1911b56bb4cSMike Smith # emit unused 1921b56bb4cSMike Smith puts "== Stale Shared Libraries ===================================================="; 1931b56bb4cSMike Smith foreach lib [lsort $unused] { 1941b56bb4cSMike Smith # sort executable names 1951b56bb4cSMike Smith set users [lsort $Libs($lib)]; 1961b56bb4cSMike Smith puts [format "%-30s %s" $lib $users]; 1971b56bb4cSMike Smith } 1981b56bb4cSMike Smith} 1991b56bb4cSMike Smith 2001b56bb4cSMike Smith################################################################################ 2011b56bb4cSMike Smith# Run the whole shebang 2021b56bb4cSMike Smith# 2031b56bb4cSMike Smithproc main {} { 2041b56bb4cSMike Smith 2051b56bb4cSMike Smith global stats verbose argv; 2061b56bb4cSMike Smith 2071b56bb4cSMike Smith set verbose 0; 2081b56bb4cSMike Smith foreach arg $argv { 2091b56bb4cSMike Smith switch -- $arg { 2101b56bb4cSMike Smith -v { 2111b56bb4cSMike Smith set verbose 1; 2121b56bb4cSMike Smith } 2131b56bb4cSMike Smith default { 2141b56bb4cSMike Smith puts stderr "Unknown option '$arg'"; 2151b56bb4cSMike Smith exit ; 2161b56bb4cSMike Smith } 2171b56bb4cSMike Smith } 2181b56bb4cSMike Smith } 2191b56bb4cSMike Smith 2201b56bb4cSMike Smith set stats(libs) 0; 2211b56bb4cSMike Smith set stats(dirs) 0; 2221b56bb4cSMike Smith set stats(files) 0; 2231b56bb4cSMike Smith set stats(execs) 0 2241b56bb4cSMike Smith 2251b56bb4cSMike Smith findLibs; 2261b56bb4cSMike Smith findLibUsers "/"; 2271b56bb4cSMike Smith emitLibDetails; 2281b56bb4cSMike Smith 2291b56bb4cSMike Smith puts [format "Searched %d directories, %d executables (%d dynamic) for %d libraries" \ 2301b56bb4cSMike Smith $stats(dirs) $stats(files) $stats(execs) $stats(libs)]; 2311b56bb4cSMike Smith} 2321b56bb4cSMike Smith 2331b56bb4cSMike Smith################################################################################ 2341b56bb4cSMike Smithmain; 235