xref: /freebsd/tools/LibraryReport/LibraryReport.tcl (revision 1b56bb4ca7afcf0f76feac28ddb97f5b78923913)
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