#!/usr/bin/python
#
# LC
#
# base class for the fingerprint plugin classes
#
import os
from subprocess import PIPE, Popen
import StringIO
import re
import logging
import string
logger = logging.getLogger('fingerprint')
import FingerPrint.utils
from FingerPrint.swirl import SwirlFile, Dependency
from FingerPrint.plugins import PluginManager
from FingerPrint.utils import getOutputAsList
"""This is the implementation for ELF files
Requirements:
- /usr/lib/rpm/find-requires /usr/lib/rpm/find-provides from rpm
- lsconfig in the path
"""
[docs]class ElfPlugin(PluginManager):
"""
This plugin manages all ELF file format. This class requires the
find-provides and find-requires script present in this folder
which require: objdump, awk, sed, grep.
For nicer documentation on this functions see
:class:`FingerPrint.plugins.PluginManager`
"""
pluginName="ELF"
#internal
_ldconfig_64bits = "x86-64"
_pathCache = {}
#may in the future we could also use
#objdump -p
_RPM_FIND_DEPS=os.path.dirname( globals()["__file__"] ) + "/find-requires"
_RPM_FIND_PROV=os.path.dirname( globals()["__file__"] ) + "/find-provides"
@classmethod
[docs] def getPathToLibrary(cls, dependency, useCache = True, rpath = []):
""" given a dependency it find the path of the library which provides
that dependency """
soname = dependency.getMajor()
if useCache and dependency.getName() in cls._pathCache :
return cls._pathCache[dependency.getName()]
#for each library we have in the system
pathToScan = cls.systemPath[:] + rpath
if "LD_LIBRARY_PATH" in os.environ:
#we need to scan the LD_LIBRARY_PATH too
pathToScan += os.environ["LD_LIBRARY_PATH"].split(':')
# remove duplicate
pathToScan = list(set(pathToScan))
for path in pathToScan:
path = os.path.normpath(path)
provider = path + '/' + soname
if os.path.isfile(provider) and \
cls._checkMinor(provider, dependency.getName()):
#we found the soname and minor are there return true
cls._pathCache[dependency.getName()] = provider
return provider
for line in getOutputAsList(["/sbin/ldconfig","-p"])[0]:
# TODO it needs to handle in a better way the hwcap field
# if dependency is 64 and library is 64 or
# dependency is 32 and library is 32:
if len(line) > 0 and soname in line and 'hwcap' not in line and \
( (dependency.is64bits() and cls._ldconfig_64bits in line) or \
(dependency.is32bits() and not cls._ldconfig_64bits in line) ):
temp = line.split('=>')
if len(temp) == 2:
provider=temp[1].strip()
if cls._checkMinor(provider, dependency.getName()):
cls._pathCache[dependency.getName()] = provider
return provider
#the dependency could not be located
return None
@classmethod
def _checkMinor(cls, libPath, depName):
""" check if libPath provides the depName (major and minor) """
realProvider = os.path.realpath(libPath)
for line in getOutputAsList(['bash', cls._RPM_FIND_PROV], realProvider)[0]:
if len(line) > 0 and depName in line:
return True
return False
@classmethod
def _setDepsRequs(cls, swirlFile, swirl):
"""
given a SwirlFile object add all the dependency and all the provides
to it
:type swirlFile: :class:`FingerPrint.swirl.SwirlFile`
:param swirlFile: the SwirlFile we need to find all the dependencies
:type swirl: :class:`FingerPrint.swirl.Swirl`
:param swirl: the Swirl that will be used to first lookup already
discovered dependencies
"""
# find rpath first
rpath = getOutputAsList(["bash","-c", "objdump -x %s |grep RPATH|awk '{print $2}'" % swirlFile.path ])[0]
if len( rpath ) > 0:
if "$ORIGIN" in rpath[0]:
rpath[0] = string.replace(rpath[0], "$ORIGIN", os.path.dirname(swirlFile.path))
swirlFile.rpaths = rpath[0].split(":")
# check LD_LIBRARY_PATH
ld_library = FingerPrint.utils.getLDLibraryPath(swirlFile.env)
#find deps
for line in getOutputAsList(['bash', cls._RPM_FIND_DEPS], swirlFile.path)[0]:
if len(line) > 0:
newDep = Dependency.fromString( line )
swirlFile.addDependency( newDep )
p = cls.getPathToLibrary( newDep , useCache = True,
rpath = swirlFile.rpaths + ld_library)
if not p:
# a dependency was not found complain loudly
logger.error("Unable to find library %s" % newDep)
continue
if p and not swirl.isFileTracked(p):
# p not null and p is not already in swirl
cls.getSwirl(p, swirl, swirlFile.env)
#find provides
for line in getOutputAsList(['bash', cls._RPM_FIND_PROV], swirlFile.path)[0]:
if len(line) > 0 :
newProv = Dependency.fromString(line)
swirlFile.addProvide(newProv)
@classmethod
[docs] def getSwirl(cls, fileName, swirl, env = None):
"""helper function given a filename it return a SwirlFile
if the given plugin does not support the given fileName should just
return None
ATT: only one plugin should return a SwirlFile for a given file
"""
fd=open(fileName)
magic = fd.read(4)
if magic == '\x7f\x45\x4c\x46':
#it's an elf see specs
#http://www.sco.com/developers/gabi/1998-04-29/ch4.eheader.html#elfid
swirlFile = swirl.createSwirlFile( fileName )
if swirlFile.staticDependencies :
# we already did this file, do not do it again
return swirlFile
swirlFile.setPluginName( cls.pluginName )
else:
#not an elf
return None
bitness = fd.read(1)
if bitness == '\x01':
swirlFile.set32bits()
elif bitness == '\x02':
swirlFile.set64bits()
fd.seek(11, 1)
execness = fd.read(2)
if execness == '\x02\x00':
# it is an executable
swirlFile.executable = True
else:
swirlFile.executable = False
swirlFile.type = 'ELF'
fd.close()
if env:
swirlFile.env = env
cls._setDepsRequs(swirlFile, swirl)
return swirlFile