freeradius/find_module_deps

386 lines
11 KiB
Python

#!/usr/bin/env python
import exceptions
import getopt
import os
import re
import rpm
import select
import subprocess
import sys
#------------------------------------------------------------------------------
def get_rlms(root):
rlm_re = re.compile(r'^rlm_')
version_re = re.compile(r'-[0-9.]+\.so$')
names = os.listdir(root)
names = [x for x in names if rlm_re.search(x)]
names = [x for x in names if not version_re.search(x)]
names.sort()
return names
#------------------------------------------------------------------------------
debug = False
verbose = False
exclude_rpms = ['glibc']
build = '2.0.2-1.fc8'
root_template = '/var/tmp/freeradius-%s-root-jdennis/usr/lib/freeradius'
libdirs = ['/lib','/usr/lib']
#------------------------------------------------------------------------------
def get_rpm_nvr_from_header(hdr):
'Given an RPM header return the package NVR as a string'
name = hdr['name']
version = hdr['version']
release = hdr['release']
return "%s-%s-%s" % (name, version, release)
def get_rpm_hdr_by_file_path(path):
if path is None:
return None
hdr = None
try:
ts = rpm.ts()
mi = ts.dbMatch(rpm.RPMTAG_BASENAMES, path)
for hdr in mi: break
except Exception, e:
print >> sys.stderr, "failed to retrieve rpm hdr for %s, %s" %(path, e)
hdr = None
return hdr
def get_rpm_nvr_by_file_path(path):
if path is None:
return None
hdr = get_rpm_hdr_by_file_path(path)
if not hdr:
print >> sys.stderr, "failed to retrieve rpm info for %s" %(path)
nvr = get_rpm_nvr_from_header(hdr)
return nvr
def get_rpm_name_by_file_path(path):
if path is None:
return None
hdr = get_rpm_hdr_by_file_path(path)
if not hdr:
print >> sys.stderr, "failed to retrieve rpm info for %s" %(path)
name = hdr['name']
return name
#------------------------------------------------------------------------------
class CmdError(exceptions.Exception):
def __init__(self, errno, msg):
self.errno = errno
self.msg = msg
class Command:
def __init__(self, cmd):
self.cmd = cmd
self.sub_process = None
self.bufsize = 1024
self.stdout_buf = ''
self.stderr_buf = ''
self.stdout_lines = []
self.stderr_lines = []
def run(self, stdout_callback=None, stderr_callback=None):
self.sub_process = subprocess.Popen(self.cmd, \
stdin=None, stdout=subprocess.PIPE, stderr=subprocess.PIPE, \
close_fds=True, shell=True)
self.stdout = self.sub_process.stdout
self.stderr = self.sub_process.stderr
read_watch = [self.stdout, self.stderr]
while read_watch:
readable = select.select(read_watch, [], [])[0]
for fd in readable:
if fd == self.stdout:
data = os.read(fd.fileno(), self.bufsize)
if not data:
read_watch.remove(fd)
else:
self.stdout_buf += data
for line in self.burst_lines('stdout_buf'):
if stdout_callback: stdout_callback(line)
self.stdout_lines.append(line)
if fd == self.stderr:
data = os.read(fd.fileno(), self.bufsize)
if not data:
read_watch.remove(fd)
else:
self.stderr_buf += data
for line in self.burst_lines('stderr_buf'):
if stdout_callback: stderr_callback(line)
self.stderr_lines.append(line)
self.returncode = self.sub_process.wait()
if self.returncode:
raise CmdError(self.returncode, "cmd \"%s\"\nreturned status %d\n%s" % (self.cmd, self.returncode, ''.join(self.stderr_lines)))
return self.returncode
def burst_lines(self, what):
buf = getattr(self, what)
start = 0
end = buf.find('\n', start)
while end >= 0:
end += 1 # include newline
line = buf[start:end]
yield line
start = end
end = buf.find('\n', start)
buf = buf[start:]
setattr(self, what, buf)
#------------------------------------------------------------------------------
def get_so_requires(path):
requires = {}
cmd = 'ldd %s' % (path)
so_re = re.compile(r'^\s*(\S+)\s+=>\s+(\S+)')
c = Command(cmd)
status = c.run()
for line in c.stdout_lines:
line = line.strip()
match = so_re.search(line)
if match:
so_name = match.group(1)
if match.group(2).startswith('/'):
so_path = match.group(2)
else:
so_path = None
requires[so_name] = so_path
return requires
def get_so_needed(path):
needed = []
cmd = 'readelf -d %s' % (path)
so_re = re.compile(r'\(NEEDED\)\s+Shared library:\s+\[([^\]]+)\]')
c = Command(cmd)
status = c.run()
for line in c.stdout_lines:
line = line.strip()
match = so_re.search(line)
if match:
so_name = match.group(1)
needed.append(so_name)
return needed
def format_size(size):
if size > 1000000000:
return '%.1f GB' % (size/1000000000.0)
if size > 1000000:
return '%.1f MB' % (size/1000000.0)
if size > 1000:
return '%.1f KB' % (size/1000.0)
return '%d' % (size)
#------------------------------------------------------------------------------
class RPM_Prop:
def __init__(self, path=None, name=None):
self.name = name
self.paths = {}
self.rpm_hdr = None
self.used_by = {}
if path:
self.register_path(path)
if not self.rpm_hdr:
self.rpm_hdr = get_rpm_hdr_by_file_path(path)
if self.rpm_hdr:
if not self.name:
self.name = self.rpm_hdr[rpm.RPMTAG_NAME]
self.size = self.rpm_hdr[rpm.RPMTAG_SIZE]
def __str__(self):
return "name=%s paths=%s" % (self.name, ','.join(self.paths.keys()))
def register_path(self, path, name=None):
if debug: print "%s.register_path: path=%s" % (self.__class__.__name__, path)
return self.paths.setdefault(path, path)
class RPM_Collection:
def __init__(self):
self.names = {}
self.paths = {}
def __str__(self):
text = ''
names = self.get_names()
for name in names:
text += "%s: %s\n" % (name, self.names[name])
return text
def register_path(self, path):
if debug: print "%s.register_path: path=%s" % (self.__class__.__name__, path)
rpm_prop = self.paths.get(path)
if not rpm_prop:
rpm_prop = self.paths.setdefault(path, RPM_Prop(path=path))
self.names.setdefault(rpm_prop.name, rpm_prop)
return rpm_prop
def get_names(self):
names = self.names.keys()
names.sort()
return names
def get_name(self, name):
return self.names.get(name)
class SO_File:
def __init__(self, name=None, path=None):
self.name = name
self.path = path
self.rpm = None
def __str__(self):
if self.rpm:
rpm_name = self.rpm.name
else:
rpm_name = None
return "name=%s rpm=%s" % (self.name, rpm_name)
class SO_Collection:
def __init__(self):
self.names = {}
self.paths = {}
def __str__(self):
text = ''
names = self.get_names()
for name in names:
text += "%s: %s\n" % (name, self.names[name])
return text
def register_path(self, path, name=None):
if debug: print "%s.register_path: path=%s" % (self.__class__.__name__, path)
so_prop = self.paths.get(path)
if not so_prop:
so_prop = self.paths.setdefault(path, SO_File(name, path=path))
self.names.setdefault(name, so_prop)
return so_prop
def get_names(self):
names = self.names.keys()
names.sort()
return names
class LoadableModule:
def __init__(self, path, name=None):
if name is None:
name = os.path.basename(path)
self.name = name
self.path = path
self.rpm_names = {}
self.sos = SO_Collection()
self.get_so_requires()
def __str__(self):
text = '%s\n' % (self.name)
text += " RPM's: %s\n" % (','.join(self.get_rpm_names()))
text += " SO's: %s\n" % (','.join(self.sos.get_names()))
return text
def get_so_requires(self):
requires = get_so_requires(self.path)
needed = get_so_needed(self.path)
#print "%s requires=%s" % (self.name, requires)
#print "%s needed=%s" % (self.name, needed)
for so_name, so_path in requires.items():
if so_name not in needed: continue
if so_path:
so_prop = self.sos.register_path(so_path, so_name)
rpm_prop = rpms.register_path(so_prop.path)
rpm_prop.used_by[self.name] = 1
self.rpm_names.setdefault(rpm_prop.name, rpm_prop.name)
so_prop.rpm = rpm_prop
else:
so_prop = None
if verbose: print "found so='%s' %s" % (so_name, so_prop)
def register_so(self, so):
if debug: print "%s.register_so: so=%s" % (self.__class__.__name__, so)
self.sos.setdefault(so, so)
self.names.setdefault(so.name, so)
return so
def get_rpm_names(self):
rpm_names = self.rpm_names.keys()
rpm_names.sort()
return rpm_names
def get_sos(self):
sos = self.sos.keys()
sos.sort(lambda a,b: cmp(a.name, b.name))
return sos
#------------------------------------------------------------------------------
#------------------------------------------------------------------------------
opts, args = getopt.getopt(sys.argv[1:], "b:v", ['build=','verbose'])
for o, a in opts:
if o in ['-b', '--build']:
build = a
elif o in ['-v', '--verbose']:
verbose = True
else:
print >> sys.stderr, "Unknown arg: %s" % o
sys.exit(1)
root = root_template % build
modules = get_rlms(root)
module_paths = [os.path.join(root,x) for x in modules]
rpms = RPM_Collection()
lms = []
for module_path in module_paths[:]:
lm = LoadableModule(module_path)
lms.append(lm)
print "RLM Modules(%s): %s\n" % (len(modules), ','.join(modules))
for lm in lms:
rpm_names = [x for x in lm.get_rpm_names() if x not in exclude_rpms]
if rpm_names:
print lm.name
print ' %s' % (','.join(rpm_names))
print "--------------"
rpm_props = [x for x in rpms.names.values() if len(x.used_by) and x.name not in exclude_rpms]
rpm_props.sort(lambda a,b: cmp(a.name, b.name))
for rpm_prop in rpm_props:
used_by = rpm_prop.used_by.keys()
used_by.sort()
print "%s: %s" % (rpm_prop.name, ','.join(used_by))
print "--------------"
rpm_props.sort(lambda a,b: cmp(a.size, b.size))
for rpm_prop in rpm_props:
print '%10s %s' % (format_size(rpm_prop.size), rpm_prop.name)
print "--------------"
for lm in lms:
print lm