2009-01-24 05:18:08 +08:00
|
|
|
# -*- coding: utf-8 -*-
|
1999-10-01 15:15:25 +08:00
|
|
|
#############################################################################
|
2005-11-28 05:49:15 +08:00
|
|
|
# File : Pkg.py
|
|
|
|
# Package : rpmlint
|
|
|
|
# Author : Frederic Lepied
|
|
|
|
# Created on : Tue Sep 28 07:18:06 1999
|
|
|
|
# Version : $Id$
|
|
|
|
# Purpose : provide an API to handle a rpm package either by accessing
|
2006-04-01 16:09:17 +08:00
|
|
|
# the rpm file or by accessing the files contained inside.
|
1999-10-01 15:15:25 +08:00
|
|
|
#############################################################################
|
|
|
|
|
|
|
|
import commands
|
2009-01-27 06:54:05 +08:00
|
|
|
import os
|
1999-10-01 15:15:25 +08:00
|
|
|
import re
|
2009-01-27 06:54:05 +08:00
|
|
|
import stat
|
|
|
|
import sys
|
2007-05-14 03:15:39 +08:00
|
|
|
import tempfile
|
2000-02-28 22:35:17 +08:00
|
|
|
import types
|
2009-01-28 23:39:38 +08:00
|
|
|
import subprocess
|
2009-01-27 06:54:05 +08:00
|
|
|
|
2009-02-28 18:49:57 +08:00
|
|
|
try:
|
|
|
|
import magic
|
2009-08-31 05:04:24 +08:00
|
|
|
# TODO: magic.MAGIC_COMPRESS when PkgFile gets decompress support.
|
2009-02-28 18:49:57 +08:00
|
|
|
_magic = magic.open(magic.MAGIC_NONE)
|
|
|
|
_magic.load()
|
|
|
|
except:
|
|
|
|
_magic = None
|
2009-01-27 06:54:05 +08:00
|
|
|
import rpm
|
1999-10-01 15:15:25 +08:00
|
|
|
|
2009-11-06 04:08:59 +08:00
|
|
|
import Filter
|
|
|
|
|
|
|
|
# Python 2/3 compatibility/convenience wrapper for printing to stderr with
|
|
|
|
# less concerns of UnicodeErrors than plain sys.stderr.write.
|
2009-10-28 04:57:49 +08:00
|
|
|
if sys.version_info[0] > 2:
|
2009-11-06 04:08:59 +08:00
|
|
|
# Blows up with Python < 3 without the exec() hack
|
|
|
|
exec('def warn(s): print (s, file=sys.stderr)')
|
2009-10-28 04:57:49 +08:00
|
|
|
else:
|
2009-11-06 04:08:59 +08:00
|
|
|
def warn(s): print >> sys.stderr, s
|
2009-10-28 04:57:49 +08:00
|
|
|
|
2009-01-27 06:54:05 +08:00
|
|
|
|
2001-11-26 05:32:34 +08:00
|
|
|
# utilities
|
|
|
|
|
2009-06-21 17:43:02 +08:00
|
|
|
# 64: RPMSENSE_PREREQ is 0 with recent rpm versions, we want 64 here in order
|
|
|
|
# to do the right thing with packages built with older rpm versions
|
|
|
|
PREREQ_FLAG = (rpm.RPMSENSE_PREREQ or 64) | \
|
2009-03-18 07:15:17 +08:00
|
|
|
rpm.RPMSENSE_SCRIPT_PRE | \
|
|
|
|
rpm.RPMSENSE_SCRIPT_POST | \
|
|
|
|
rpm.RPMSENSE_SCRIPT_PREUN | \
|
2009-03-18 07:20:11 +08:00
|
|
|
rpm.RPMSENSE_SCRIPT_POSTUN
|
2009-03-18 07:15:17 +08:00
|
|
|
|
2008-10-30 07:01:44 +08:00
|
|
|
var_regex = re.compile('^(.*)\${?(\w+)}?(.*)$')
|
2006-10-12 00:53:41 +08:00
|
|
|
|
2001-11-26 05:32:34 +08:00
|
|
|
def shell_var_value(var, script):
|
2008-10-30 07:01:44 +08:00
|
|
|
assign_regex = re.compile('\\b' + re.escape(var) + '\s*=\s*(.+)\s*(#.*)*$',
|
|
|
|
re.MULTILINE)
|
|
|
|
res = assign_regex.search(script)
|
2001-11-26 05:32:34 +08:00
|
|
|
if res:
|
2006-10-12 00:53:41 +08:00
|
|
|
res2 = var_regex.search(res.group(1))
|
|
|
|
if res2:
|
|
|
|
if res2.group(2) == var: # infinite loop
|
|
|
|
return None
|
2001-11-26 05:32:34 +08:00
|
|
|
return substitute_shell_vars(res.group(1), script)
|
|
|
|
else:
|
|
|
|
return None
|
|
|
|
|
|
|
|
def substitute_shell_vars(val, script):
|
2008-10-30 07:01:44 +08:00
|
|
|
res = var_regex.search(val)
|
2001-11-26 05:32:34 +08:00
|
|
|
if res:
|
2008-10-30 07:01:44 +08:00
|
|
|
value = shell_var_value(res.group(2), script)
|
2001-11-26 05:32:34 +08:00
|
|
|
if not value:
|
2008-10-30 07:01:44 +08:00
|
|
|
value = ''
|
2010-02-21 19:28:42 +08:00
|
|
|
return res.group(1) + value + \
|
|
|
|
substitute_shell_vars(res.group(3), script)
|
2001-11-26 05:32:34 +08:00
|
|
|
else:
|
|
|
|
return val
|
|
|
|
|
2009-06-17 06:13:20 +08:00
|
|
|
def getstatusoutput(cmd, stdoutonly = False):
|
2006-09-15 06:18:32 +08:00
|
|
|
'''A version of commands.getstatusoutput() which can take cmd as a
|
2009-01-30 04:31:55 +08:00
|
|
|
sequence, thus making it potentially more secure.'''
|
2006-09-15 06:18:32 +08:00
|
|
|
if stdoutonly:
|
2010-02-21 19:28:42 +08:00
|
|
|
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
|
|
|
stdout=subprocess.PIPE, close_fds=True)
|
2006-09-15 06:18:32 +08:00
|
|
|
else:
|
2010-02-21 19:28:42 +08:00
|
|
|
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE,
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
stderr=subprocess.STDOUT, close_fds=True)
|
2009-01-28 23:39:38 +08:00
|
|
|
proc.stdin.close()
|
|
|
|
text = proc.stdout.read()
|
2006-09-15 06:18:32 +08:00
|
|
|
sts = proc.wait()
|
|
|
|
if sts is None: sts = 0
|
2009-01-27 07:10:01 +08:00
|
|
|
if text.endswith('\n'):
|
|
|
|
text = text[:-1]
|
2006-09-15 06:18:32 +08:00
|
|
|
return sts, text
|
|
|
|
|
2008-10-30 07:01:44 +08:00
|
|
|
bz2_regex = re.compile('\.t?bz2?$')
|
2009-08-19 17:29:26 +08:00
|
|
|
xz_regex = re.compile('\.(t[xl]z|xz|lzma)$')
|
2006-01-15 17:59:04 +08:00
|
|
|
|
2010-05-18 03:35:04 +08:00
|
|
|
def catcmd(fname):
|
|
|
|
"""Get a 'cat' command that handles possibly compressed files."""
|
|
|
|
cat = 'gzip -dcf'
|
|
|
|
if bz2_regex.search(fname):
|
|
|
|
cat = 'bzip2 -dcf'
|
|
|
|
elif xz_regex.search(fname):
|
|
|
|
cat = 'xz -dc'
|
|
|
|
return cat
|
|
|
|
|
2007-03-16 03:15:27 +08:00
|
|
|
# TODO: is_utf8 could probably be implemented natively without iconv...
|
2006-01-15 17:59:04 +08:00
|
|
|
|
|
|
|
def is_utf8(fname):
|
2006-09-15 06:18:32 +08:00
|
|
|
# TODO: better shell escaping or sequence based command invocation
|
2010-02-21 19:28:42 +08:00
|
|
|
cmd = commands.getstatusoutput(
|
2010-05-18 03:35:04 +08:00
|
|
|
'%s "%s" | iconv -f utf-8 -t utf-8 -o /dev/null' %
|
|
|
|
(catcmd(fname), fname))
|
2006-01-15 17:59:04 +08:00
|
|
|
return not cmd[0]
|
|
|
|
|
|
|
|
def is_utf8_str(s):
|
2007-03-16 03:15:27 +08:00
|
|
|
try:
|
|
|
|
s.decode('UTF-8')
|
|
|
|
except:
|
2009-06-17 06:13:20 +08:00
|
|
|
return False
|
|
|
|
return True
|
2006-01-15 17:59:04 +08:00
|
|
|
|
2007-11-28 04:28:06 +08:00
|
|
|
def to_utf8(string):
|
|
|
|
if string is None:
|
|
|
|
return ''
|
|
|
|
elif isinstance(string, unicode):
|
|
|
|
return string
|
|
|
|
try:
|
|
|
|
x = unicode(string, 'ascii')
|
|
|
|
return string
|
|
|
|
except UnicodeError:
|
|
|
|
encodings = ['utf-8', 'iso-8859-1', 'iso-8859-15', 'iso-8859-2']
|
|
|
|
for enc in encodings:
|
|
|
|
try:
|
|
|
|
x = unicode(string, enc)
|
|
|
|
except UnicodeError:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
if x.encode(enc) == string:
|
|
|
|
return x.encode('utf-8')
|
|
|
|
newstring = ''
|
|
|
|
for char in string:
|
|
|
|
if ord(char) > 127:
|
|
|
|
newstring = newstring + '?'
|
|
|
|
else:
|
|
|
|
newstring = newstring + char
|
|
|
|
return newstring
|
|
|
|
|
2007-05-14 03:15:39 +08:00
|
|
|
def readlines(path):
|
2007-05-14 03:26:06 +08:00
|
|
|
fobj = open(path, "r")
|
2007-05-14 03:15:39 +08:00
|
|
|
try:
|
|
|
|
return fobj.readlines()
|
|
|
|
finally:
|
|
|
|
fobj.close()
|
|
|
|
|
|
|
|
def mktemp():
|
2009-02-13 03:40:50 +08:00
|
|
|
tmpfd, tmpname = tempfile.mkstemp(prefix = 'rpmlint.')
|
2008-10-30 04:40:29 +08:00
|
|
|
tmpfile = os.fdopen(tmpfd, 'w')
|
2007-05-14 03:15:39 +08:00
|
|
|
return tmpfile, tmpname
|
|
|
|
|
2009-03-24 01:46:47 +08:00
|
|
|
slash_regex = re.compile('/+')
|
|
|
|
slashdot_regex = re.compile('/(\.(/|$))+')
|
2009-03-24 02:07:33 +08:00
|
|
|
slashend_regex = re.compile('([^/])/+$')
|
2009-03-24 01:46:47 +08:00
|
|
|
|
|
|
|
def safe_normpath(path):
|
|
|
|
"""Like os.path.normpath but normalizes less aggressively thus being
|
|
|
|
potentially safer for paths containing symlinks."""
|
|
|
|
ret = slash_regex.sub('/', path)
|
|
|
|
ret = slashdot_regex.sub('\\2', ret)
|
2009-03-24 02:07:33 +08:00
|
|
|
ret = slashend_regex.sub('\\1', ret)
|
2009-03-24 01:46:47 +08:00
|
|
|
return ret
|
|
|
|
|
2009-11-26 01:24:33 +08:00
|
|
|
def get_default_valid_rpmgroups(filename = None):
|
|
|
|
"""Get default rpm groups from filename, or try to look them up from
|
|
|
|
the rpm package (if installed) if no filename is given"""
|
2009-01-22 07:01:18 +08:00
|
|
|
groups = []
|
|
|
|
if not filename:
|
2009-11-26 01:24:33 +08:00
|
|
|
try:
|
|
|
|
p = InstalledPkg('rpm')
|
2010-01-26 05:39:38 +08:00
|
|
|
except:
|
2009-11-26 01:24:33 +08:00
|
|
|
pass
|
|
|
|
else:
|
|
|
|
groupsfiles = [x for x in p.files() if x.endswith('/GROUPS')]
|
|
|
|
if groupsfiles:
|
|
|
|
filename = groupsfiles[0]
|
2009-01-22 07:01:18 +08:00
|
|
|
if filename and os.path.exists(filename):
|
|
|
|
fobj = open(filename)
|
|
|
|
try:
|
|
|
|
groups = fobj.read().strip().splitlines()
|
|
|
|
finally:
|
|
|
|
fobj.close()
|
2009-03-24 02:47:03 +08:00
|
|
|
if 'Development/Debug' not in groups:
|
2009-01-22 07:01:18 +08:00
|
|
|
groups.append('Development/Debug')
|
2009-03-24 02:47:03 +08:00
|
|
|
groups.sort()
|
2009-01-22 07:01:18 +08:00
|
|
|
return groups
|
|
|
|
|
2010-06-06 03:44:09 +08:00
|
|
|
# from yum 3.2.27, rpmUtils.miscutils, with rpmlint modifications
|
2009-06-22 03:36:43 +08:00
|
|
|
def compareEVR((e1, v1, r1), (e2, v2, r2)):
|
|
|
|
# return 1: a is newer than b
|
|
|
|
# 0: a and b are the same version
|
|
|
|
# -1: b is newer than a
|
2010-06-06 03:44:09 +08:00
|
|
|
# rpmlint mod: don't stringify None epochs to 'None' strings
|
|
|
|
if e1 is not None:
|
|
|
|
e1 = str(e1)
|
2009-06-22 03:36:43 +08:00
|
|
|
v1 = str(v1)
|
|
|
|
r1 = str(r1)
|
2010-06-06 03:44:09 +08:00
|
|
|
if e2 is not None:
|
|
|
|
e2 = str(e2)
|
2009-06-22 03:36:43 +08:00
|
|
|
v2 = str(v2)
|
|
|
|
r2 = str(r2)
|
|
|
|
rc = rpm.labelCompare((e1, v1, r1), (e2, v2, r2))
|
|
|
|
return rc
|
|
|
|
|
2010-06-06 03:55:53 +08:00
|
|
|
# from yum 3.2.27, rpmUtils.miscutils, with rpmlint modifications
|
2009-06-22 03:36:43 +08:00
|
|
|
def rangeCompare(reqtuple, provtuple):
|
|
|
|
"""returns true if provtuple satisfies reqtuple"""
|
|
|
|
(reqn, reqf, (reqe, reqv, reqr)) = reqtuple
|
|
|
|
(n, f, (e, v, r)) = provtuple
|
|
|
|
if reqn != n:
|
|
|
|
return 0
|
|
|
|
|
2010-06-06 01:01:30 +08:00
|
|
|
# unversioned satisfies everything
|
2009-06-22 03:36:43 +08:00
|
|
|
if not f or not reqf:
|
|
|
|
return 1
|
|
|
|
|
|
|
|
# and you thought we were done having fun
|
|
|
|
# if the requested release is left out then we have
|
|
|
|
# to remove release from the package prco to make sure the match
|
|
|
|
# is a success - ie: if the request is EQ foo 1:3.0.0 and we have
|
|
|
|
# foo 1:3.0.0-15 then we have to drop the 15 so we can match
|
|
|
|
if reqr is None:
|
|
|
|
r = None
|
2010-06-06 03:55:53 +08:00
|
|
|
# rpmlint mod: don't mess with provided Epoch, doing so breaks e.g.
|
|
|
|
# "Requires: foo < 1.0" should not be satisfied by "Provides: foo = 1:0.5"
|
|
|
|
#if reqe is None:
|
|
|
|
# e = None
|
2010-06-06 01:01:30 +08:00
|
|
|
if reqv is None: # just for the record if ver is None then we're going to segfault
|
2009-06-22 03:36:43 +08:00
|
|
|
v = None
|
|
|
|
|
|
|
|
# if we just require foo-version, then foo-version-* will match
|
|
|
|
if r is None:
|
|
|
|
reqr = None
|
|
|
|
|
|
|
|
rc = compareEVR((e, v, r), (reqe, reqv, reqr))
|
|
|
|
|
|
|
|
# does not match unless
|
|
|
|
if rc >= 1:
|
|
|
|
if reqf in ['GT', 'GE', 4, 12]:
|
|
|
|
return 1
|
|
|
|
if reqf in ['EQ', 8]:
|
2010-06-06 01:01:30 +08:00
|
|
|
if f in ['LE', 10, 'LT', 2]:
|
|
|
|
return 1
|
|
|
|
if reqf in ['LE', 'LT', 'EQ', 10, 2, 8]:
|
|
|
|
if f in ['LE', 'LT', 10, 2]:
|
2009-06-22 03:36:43 +08:00
|
|
|
return 1
|
2010-06-06 01:01:30 +08:00
|
|
|
|
2009-06-22 03:36:43 +08:00
|
|
|
if rc == 0:
|
|
|
|
if reqf in ['GT', 4]:
|
|
|
|
if f in ['GT', 'GE', 4, 12]:
|
|
|
|
return 1
|
|
|
|
if reqf in ['GE', 12]:
|
|
|
|
if f in ['GT', 'GE', 'EQ', 'LE', 4, 12, 8, 10]:
|
|
|
|
return 1
|
|
|
|
if reqf in ['EQ', 8]:
|
|
|
|
if f in ['EQ', 'GE', 'LE', 8, 12, 10]:
|
|
|
|
return 1
|
|
|
|
if reqf in ['LE', 10]:
|
|
|
|
if f in ['EQ', 'LE', 'LT', 'GE', 8, 10, 2, 12]:
|
|
|
|
return 1
|
|
|
|
if reqf in ['LT', 2]:
|
|
|
|
if f in ['LE', 'LT', 10, 2]:
|
|
|
|
return 1
|
|
|
|
if rc <= -1:
|
|
|
|
if reqf in ['GT', 'GE', 'EQ', 4, 12, 8]:
|
|
|
|
if f in ['GT', 'GE', 4, 12]:
|
|
|
|
return 1
|
|
|
|
if reqf in ['LE', 'LT', 10, 2]:
|
|
|
|
return 1
|
2010-06-06 01:01:30 +08:00
|
|
|
# if rc >= 1:
|
|
|
|
# if reqf in ['GT', 'GE', 4, 12]:
|
|
|
|
# return 1
|
|
|
|
# if rc == 0:
|
|
|
|
# if reqf in ['GE', 'LE', 'EQ', 8, 10, 12]:
|
|
|
|
# return 1
|
|
|
|
# if rc <= -1:
|
|
|
|
# if reqf in ['LT', 'LE', 2, 10]:
|
|
|
|
# return 1
|
2009-06-22 03:36:43 +08:00
|
|
|
|
|
|
|
return 0
|
|
|
|
|
2010-04-16 05:42:11 +08:00
|
|
|
# from yum 3.2.23, rpmUtils.miscutils, with rpmlint modifications
|
|
|
|
def formatRequire(name, flags, evr):
|
2009-06-22 03:36:43 +08:00
|
|
|
s = name
|
|
|
|
|
|
|
|
if flags:
|
|
|
|
if flags & (rpm.RPMSENSE_LESS | rpm.RPMSENSE_GREATER |
|
|
|
|
rpm.RPMSENSE_EQUAL):
|
|
|
|
s = s + " "
|
|
|
|
if flags & rpm.RPMSENSE_LESS:
|
|
|
|
s = s + "<"
|
|
|
|
if flags & rpm.RPMSENSE_GREATER:
|
|
|
|
s = s + ">"
|
|
|
|
if flags & rpm.RPMSENSE_EQUAL:
|
|
|
|
s = s + "="
|
2010-04-16 05:42:11 +08:00
|
|
|
s = "%s %s" % (s, versionToString(evr))
|
2009-06-22 03:36:43 +08:00
|
|
|
return s
|
|
|
|
|
2010-04-16 05:42:11 +08:00
|
|
|
def versionToString(evr):
|
|
|
|
if not isinstance(evr, tuple) and not isinstance(evr, list):
|
|
|
|
# assume string
|
|
|
|
return evr
|
|
|
|
ret = ""
|
|
|
|
if evr[0] is not None and evr[0] != "":
|
|
|
|
ret += str(evr[0]) + ":"
|
|
|
|
ret += evr[1]
|
|
|
|
if evr[2] is not None and evr[2] != "":
|
|
|
|
ret += "-" + evr[2]
|
|
|
|
return ret
|
|
|
|
|
|
|
|
# from yum 3.2.23, rpmUtils.miscutils, with some rpmlint modifications
|
2009-06-22 03:36:43 +08:00
|
|
|
def stringToVersion(verstring):
|
|
|
|
if verstring in [None, '']:
|
|
|
|
return (None, None, None)
|
2010-04-16 05:42:11 +08:00
|
|
|
epoch = None
|
2009-06-22 03:36:43 +08:00
|
|
|
i = verstring.find(':')
|
|
|
|
if i != -1:
|
|
|
|
try:
|
|
|
|
epoch = str(long(verstring[:i]))
|
|
|
|
except ValueError:
|
2010-04-16 05:42:11 +08:00
|
|
|
# garbage in epoch, ignore it
|
|
|
|
pass
|
2009-06-22 03:36:43 +08:00
|
|
|
j = verstring.find('-')
|
|
|
|
if j != -1:
|
|
|
|
if verstring[i + 1:j] == '':
|
|
|
|
version = None
|
|
|
|
else:
|
|
|
|
version = verstring[i + 1:j]
|
|
|
|
release = verstring[j + 1:]
|
|
|
|
else:
|
|
|
|
if verstring[i + 1:] == '':
|
|
|
|
version = None
|
|
|
|
else:
|
|
|
|
version = verstring[i + 1:]
|
|
|
|
release = None
|
|
|
|
return (epoch, version, release)
|
|
|
|
|
2010-04-17 02:51:00 +08:00
|
|
|
def parse_deps(line):
|
|
|
|
'''Parse provides/requires/conflicts/obsoletes line to list of
|
|
|
|
(name, flags, (epoch, version, release)) tuples.'''
|
|
|
|
|
|
|
|
prcos = []
|
|
|
|
tokens = re.split('[\s,]+', line.strip())
|
|
|
|
|
|
|
|
# Drop line continuation backslash in multiline macro definition (for
|
|
|
|
# spec file parsing), e.g.
|
|
|
|
# [...] \
|
|
|
|
# Obsoletes: foo-%1 <= 1.0.0 \
|
|
|
|
# [...] \
|
|
|
|
# (yes, this is an ugly hack and we probably have other problems with
|
|
|
|
# multiline macro definitions elsewhere...)
|
|
|
|
if tokens[-1] == '\\':
|
|
|
|
del tokens[-1]
|
|
|
|
|
|
|
|
prco = []
|
|
|
|
while tokens:
|
|
|
|
token = tokens.pop(0)
|
|
|
|
if not token:
|
|
|
|
# skip empty tokens
|
|
|
|
continue
|
|
|
|
|
|
|
|
plen = len(prco)
|
|
|
|
|
|
|
|
if plen == 0:
|
|
|
|
prco.append(token)
|
|
|
|
|
|
|
|
elif plen == 1:
|
|
|
|
flags = 0
|
|
|
|
if token[0] in ("=", "<", "<=", ">", ">="):
|
|
|
|
# versioned, flags
|
|
|
|
if "=" in token:
|
|
|
|
flags |= rpm.RPMSENSE_EQUAL
|
|
|
|
if "<" in token:
|
|
|
|
flags |= rpm.RPMSENSE_LESS
|
|
|
|
if ">" in token:
|
|
|
|
flags |= rpm.RPMSENSE_GREATER
|
|
|
|
prco.append(flags)
|
|
|
|
else:
|
|
|
|
# no flags following name, treat as unversioned, add and reset
|
|
|
|
prco.extend((flags, (None, None, None)))
|
|
|
|
prcos.append(tuple(prco))
|
|
|
|
prco = [token]
|
|
|
|
|
|
|
|
elif plen == 2:
|
|
|
|
# last token of versioned one, add and reset
|
|
|
|
prco.append(stringToVersion(token))
|
|
|
|
prcos.append(tuple(prco))
|
|
|
|
prco = []
|
|
|
|
|
|
|
|
plen = len(prco)
|
|
|
|
if plen:
|
|
|
|
if plen == 1:
|
|
|
|
prco.extend((0, (None, None, None)))
|
|
|
|
elif plen == 2:
|
|
|
|
prco.append((None, None, None))
|
|
|
|
prcos.append(tuple(prco))
|
|
|
|
|
|
|
|
return prcos
|
|
|
|
|
|
|
|
|
2001-11-26 05:32:34 +08:00
|
|
|
# classes representing package
|
|
|
|
|
1999-10-01 15:15:25 +08:00
|
|
|
class Pkg:
|
2008-10-30 07:01:44 +08:00
|
|
|
|
2009-08-31 05:04:24 +08:00
|
|
|
_magic_from_compressed_re = re.compile('\([^)]+\s+compressed\s+data\\b')
|
|
|
|
|
2009-06-17 06:13:20 +08:00
|
|
|
def __init__(self, filename, dirname, header = None, is_source = False):
|
2008-10-30 07:01:44 +08:00
|
|
|
self.filename = filename
|
2009-06-17 06:13:20 +08:00
|
|
|
self.extracted = False
|
2008-10-30 07:01:44 +08:00
|
|
|
self.dirname = dirname
|
|
|
|
self.current_linenum = None
|
|
|
|
self._config_files = None
|
|
|
|
self._doc_files = None
|
2009-02-02 05:58:38 +08:00
|
|
|
self._noreplace_files = None
|
2008-10-30 07:01:44 +08:00
|
|
|
self._ghost_files = None
|
2009-02-02 05:58:38 +08:00
|
|
|
self._missingok_files = None
|
2008-10-30 07:01:44 +08:00
|
|
|
self._files = None
|
|
|
|
self._requires = None
|
|
|
|
self._req_names = -1
|
2005-11-28 05:49:15 +08:00
|
|
|
|
2000-10-17 02:51:12 +08:00
|
|
|
if header:
|
2008-10-30 07:01:44 +08:00
|
|
|
self.header = header
|
|
|
|
self.is_source = is_source
|
2000-10-17 02:51:12 +08:00
|
|
|
else:
|
|
|
|
# Create a package object from the file name
|
2009-02-11 05:16:43 +08:00
|
|
|
ts = rpm.TransactionSet()
|
|
|
|
# Don't check signatures here...
|
|
|
|
ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
|
|
|
|
fd = os.open(filename, os.O_RDONLY)
|
|
|
|
try:
|
|
|
|
self.header = ts.hdrFromFdno(fd)
|
|
|
|
finally:
|
|
|
|
os.close(fd)
|
|
|
|
self.is_source = not self.header[rpm.RPMTAG_SOURCERPM]
|
2000-10-17 02:51:12 +08:00
|
|
|
|
2008-10-30 07:01:44 +08:00
|
|
|
self.name = self.header[rpm.RPMTAG_NAME]
|
2007-05-15 05:50:24 +08:00
|
|
|
if self.isNoSource():
|
|
|
|
self.arch = 'nosrc'
|
|
|
|
elif self.isSource():
|
|
|
|
self.arch = 'src'
|
|
|
|
else:
|
|
|
|
self.arch = self.header[rpm.RPMTAG_ARCH]
|
1999-10-06 21:00:23 +08:00
|
|
|
|
2003-03-25 20:11:32 +08:00
|
|
|
# Return true if the package is a source package
|
1999-10-01 15:15:25 +08:00
|
|
|
def isSource(self):
|
2005-11-28 05:49:15 +08:00
|
|
|
return self.is_source
|
1999-10-06 21:00:23 +08:00
|
|
|
|
2003-03-25 20:11:32 +08:00
|
|
|
# Return true if the package is a nosource package.
|
|
|
|
# NoSource files are ghosts in source packages.
|
|
|
|
def isNoSource(self):
|
|
|
|
return self.is_source and self.ghostFiles()
|
|
|
|
|
1999-10-06 21:00:23 +08:00
|
|
|
# access the tags like an array
|
1999-10-01 15:15:25 +08:00
|
|
|
def __getitem__(self, key):
|
2006-01-09 21:19:34 +08:00
|
|
|
try:
|
|
|
|
val = self.header[key]
|
|
|
|
except:
|
|
|
|
val = []
|
2004-01-19 19:25:36 +08:00
|
|
|
if val == []:
|
|
|
|
return None
|
|
|
|
else:
|
|
|
|
return val
|
1999-10-01 15:15:25 +08:00
|
|
|
|
1999-10-06 21:00:23 +08:00
|
|
|
# return the name of the directory where the package is extracted
|
1999-10-01 15:15:25 +08:00
|
|
|
def dirName(self):
|
2005-11-28 05:49:15 +08:00
|
|
|
if not self.extracted:
|
|
|
|
self._extract()
|
|
|
|
return self.dirname
|
1999-10-01 15:15:25 +08:00
|
|
|
|
2008-04-11 03:24:56 +08:00
|
|
|
# extract rpm contents
|
1999-10-01 15:15:25 +08:00
|
|
|
def _extract(self):
|
2008-10-30 07:01:44 +08:00
|
|
|
s = os.stat(self.dirname)
|
1999-10-01 15:15:25 +08:00
|
|
|
if not stat.S_ISDIR(s[stat.ST_MODE]):
|
2009-11-06 04:08:59 +08:00
|
|
|
warn('Unable to access dir %s' % self.dirname)
|
2001-11-15 00:34:02 +08:00
|
|
|
return None
|
1999-10-01 15:15:25 +08:00
|
|
|
else:
|
2009-02-13 03:40:50 +08:00
|
|
|
self.dirname = tempfile.mkdtemp(
|
|
|
|
prefix = 'rpmlint.%s.' % os.path.basename(self.filename),
|
|
|
|
dir = self.dirname)
|
2006-09-15 06:18:32 +08:00
|
|
|
# TODO: better shell escaping or sequence based command invocation
|
2010-02-21 19:28:42 +08:00
|
|
|
command_str = \
|
|
|
|
'rpm2cpio "%s" | (cd "%s"; cpio -id); chmod -R +rX "%s"' % \
|
|
|
|
(self.filename, self.dirname, self.dirname)
|
2008-10-30 07:01:44 +08:00
|
|
|
cmd = commands.getstatusoutput(command_str)
|
2009-06-17 06:13:20 +08:00
|
|
|
self.extracted = True
|
2001-11-15 00:34:02 +08:00
|
|
|
return cmd
|
2005-11-28 05:49:15 +08:00
|
|
|
|
2001-06-07 00:18:31 +08:00
|
|
|
def checkSignature(self):
|
2006-09-15 06:18:32 +08:00
|
|
|
return getstatusoutput(('env', 'LC_ALL=C', 'rpm', '-K', self.filename))
|
2005-11-28 05:49:15 +08:00
|
|
|
|
1999-10-06 21:00:23 +08:00
|
|
|
# remove the extracted files from the package
|
1999-10-01 15:15:25 +08:00
|
|
|
def cleanup(self):
|
2006-09-15 06:18:32 +08:00
|
|
|
if self.extracted and self.dirname:
|
|
|
|
getstatusoutput(('rm', '-rf', self.dirname))
|
1999-10-06 20:35:40 +08:00
|
|
|
|
2006-08-26 21:22:08 +08:00
|
|
|
def grep(self, regex, filename):
|
|
|
|
"""Grep regex from a file, return matching line numbers."""
|
|
|
|
ret = []
|
|
|
|
lineno = 0
|
|
|
|
in_file = None
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
in_file = open(self.dirName() + '/' + filename)
|
2007-08-13 00:48:08 +08:00
|
|
|
for line in in_file:
|
2006-08-26 21:22:08 +08:00
|
|
|
lineno += 1
|
|
|
|
if regex.search(line):
|
|
|
|
ret.append(str(lineno))
|
|
|
|
break
|
|
|
|
except Exception, e:
|
2009-10-28 05:38:10 +08:00
|
|
|
Filter.printWarning(self, 'read-error', filename, e)
|
2006-08-26 21:22:08 +08:00
|
|
|
finally:
|
|
|
|
if in_file:
|
|
|
|
in_file.close()
|
|
|
|
return ret
|
|
|
|
|
2009-09-24 04:08:12 +08:00
|
|
|
def langtag(self, tag, lang):
|
|
|
|
"""Get value of tag in the given language."""
|
|
|
|
# LANGUAGE trumps other env vars per GNU gettext docs, see also #166
|
|
|
|
orig = os.environ.get('LANGUAGE')
|
|
|
|
os.environ['LANGUAGE'] = lang
|
|
|
|
ret = self[tag]
|
|
|
|
if orig is not None:
|
|
|
|
os.environ['LANGUAGE'] = orig
|
|
|
|
return ret
|
|
|
|
|
1999-10-06 21:00:23 +08:00
|
|
|
# return the associative array indexed on file names with
|
|
|
|
# the values as: (file perm, file owner, file group, file link to)
|
|
|
|
def files(self):
|
2009-01-27 07:10:01 +08:00
|
|
|
if self._files is not None:
|
2005-11-28 05:49:15 +08:00
|
|
|
return self._files
|
2009-02-02 05:58:38 +08:00
|
|
|
|
2005-11-28 05:49:15 +08:00
|
|
|
self._gatherFilesInfo()
|
|
|
|
return self._files
|
1999-10-06 21:00:23 +08:00
|
|
|
|
|
|
|
# return the list of config files
|
1999-10-06 20:35:40 +08:00
|
|
|
def configFiles(self):
|
2009-01-27 07:10:01 +08:00
|
|
|
if self._config_files is not None:
|
2005-11-28 05:49:15 +08:00
|
|
|
return self._config_files
|
2009-02-02 05:58:38 +08:00
|
|
|
|
|
|
|
self._config_files = [x.name for x in self.files().values()
|
|
|
|
if x.is_config]
|
2005-11-28 05:49:15 +08:00
|
|
|
return self._config_files
|
1999-10-06 21:00:23 +08:00
|
|
|
|
2000-08-10 20:24:45 +08:00
|
|
|
# return the list of noreplace files
|
|
|
|
def noreplaceFiles(self):
|
2009-01-27 07:10:01 +08:00
|
|
|
if self._noreplace_files is not None:
|
2005-11-28 05:49:15 +08:00
|
|
|
return self._noreplace_files
|
2009-02-02 05:58:38 +08:00
|
|
|
|
|
|
|
self._noreplace_files = [x.name for x in self.files().values()
|
|
|
|
if x.is_noreplace]
|
2005-11-28 05:49:15 +08:00
|
|
|
return self._noreplace_files
|
2000-08-10 20:24:45 +08:00
|
|
|
|
1999-10-06 21:00:23 +08:00
|
|
|
# return the list of documentation files
|
|
|
|
def docFiles(self):
|
2009-01-27 07:10:01 +08:00
|
|
|
if self._doc_files is not None:
|
2005-11-28 05:49:15 +08:00
|
|
|
return self._doc_files
|
2009-02-02 05:58:38 +08:00
|
|
|
|
|
|
|
self._doc_files = [x.name for x in self.files().values() if x.is_doc]
|
2005-11-28 05:49:15 +08:00
|
|
|
return self._doc_files
|
1999-10-06 21:00:23 +08:00
|
|
|
|
1999-10-12 12:38:55 +08:00
|
|
|
# return the list of ghost files
|
|
|
|
def ghostFiles(self):
|
2009-01-27 07:10:01 +08:00
|
|
|
if self._ghost_files is not None:
|
2005-11-28 05:49:15 +08:00
|
|
|
return self._ghost_files
|
2009-02-02 05:58:38 +08:00
|
|
|
|
|
|
|
self._ghost_files = [x.name for x in self.files().values()
|
|
|
|
if x.is_ghost]
|
2005-11-28 05:49:15 +08:00
|
|
|
return self._ghost_files
|
1999-10-12 12:38:55 +08:00
|
|
|
|
2007-08-13 02:19:03 +08:00
|
|
|
def missingOkFiles(self):
|
2009-02-02 05:58:38 +08:00
|
|
|
if self._missingok_files is not None:
|
|
|
|
return self._missingok_files
|
|
|
|
|
|
|
|
self._missingok_files = [x.name for x in self.files().values()
|
|
|
|
if x.is_missingok]
|
|
|
|
return self._missingok_files
|
2007-08-13 02:19:03 +08:00
|
|
|
|
1999-10-06 21:00:23 +08:00
|
|
|
# extract information about the files
|
|
|
|
def _gatherFilesInfo(self):
|
2005-11-28 05:49:15 +08:00
|
|
|
|
2008-10-30 07:01:44 +08:00
|
|
|
self._files = {}
|
|
|
|
flags = self.header[rpm.RPMTAG_FILEFLAGS]
|
|
|
|
modes = self.header[rpm.RPMTAG_FILEMODES]
|
|
|
|
users = self.header[rpm.RPMTAG_FILEUSERNAME]
|
|
|
|
groups = self.header[rpm.RPMTAG_FILEGROUPNAME]
|
|
|
|
links = self.header[rpm.RPMTAG_FILELINKTOS]
|
|
|
|
sizes = self.header[rpm.RPMTAG_FILESIZES]
|
|
|
|
md5s = self.header[rpm.RPMTAG_FILEMD5S]
|
|
|
|
mtimes = self.header[rpm.RPMTAG_FILEMTIMES]
|
|
|
|
rdevs = self.header[rpm.RPMTAG_FILERDEVS]
|
2009-01-28 04:03:00 +08:00
|
|
|
langs = self.header[rpm.RPMTAG_FILELANGS]
|
2009-02-01 23:10:58 +08:00
|
|
|
inodes = self.header[rpm.RPMTAG_FILEINODES]
|
2010-04-17 03:07:15 +08:00
|
|
|
requires = self.header[rpm.RPMTAG_FILEREQUIRE]
|
2010-04-17 03:17:24 +08:00
|
|
|
provides = self.header[rpm.RPMTAG_FILEPROVIDE]
|
2009-02-11 05:16:43 +08:00
|
|
|
files = self.header[rpm.RPMTAG_FILENAMES]
|
2009-02-28 18:49:57 +08:00
|
|
|
magics = self.header[rpm.RPMTAG_FILECLASS]
|
2010-11-05 00:44:11 +08:00
|
|
|
try: # rpm >= 4.7.0
|
|
|
|
filecaps = self.header[rpm.RPMTAG_FILECAPS]
|
|
|
|
except:
|
|
|
|
filecaps = None
|
2009-02-01 23:10:58 +08:00
|
|
|
|
|
|
|
# rpm-python < 4.6 does not return a list for this (or FILEDEVICES,
|
|
|
|
# FWIW) for packages containing exactly one file
|
|
|
|
if not isinstance(inodes, types.ListType):
|
|
|
|
inodes = [inodes]
|
|
|
|
|
2005-11-28 05:49:15 +08:00
|
|
|
if files:
|
|
|
|
for idx in range(0, len(files)):
|
2009-02-02 05:58:38 +08:00
|
|
|
pkgfile = PkgFile(files[idx])
|
2009-02-28 18:48:05 +08:00
|
|
|
# Do not use os.path.join here, pkgfile.name can start with a
|
|
|
|
# / which would result in self.dirName being ignored
|
|
|
|
pkgfile.path = os.path.normpath(
|
|
|
|
self.dirName() + '/' + pkgfile.name)
|
2009-02-02 05:58:38 +08:00
|
|
|
pkgfile.flags = flags[idx]
|
|
|
|
pkgfile.mode = modes[idx]
|
|
|
|
pkgfile.user = users[idx]
|
|
|
|
pkgfile.group = groups[idx]
|
2009-03-24 02:07:33 +08:00
|
|
|
pkgfile.linkto = links[idx] and safe_normpath(links[idx])
|
2009-02-02 05:58:38 +08:00
|
|
|
pkgfile.size = sizes[idx]
|
|
|
|
pkgfile.md5 = md5s[idx]
|
|
|
|
pkgfile.mtime = mtimes[idx]
|
|
|
|
pkgfile.rdev = rdevs[idx]
|
|
|
|
pkgfile.inode = inodes[idx]
|
2010-04-17 03:07:15 +08:00
|
|
|
pkgfile.requires = parse_deps(requires[idx])
|
2010-04-17 03:17:24 +08:00
|
|
|
pkgfile.provides = parse_deps(provides[idx])
|
2009-02-02 05:58:38 +08:00
|
|
|
pkgfile.lang = langs[idx]
|
2009-02-28 18:49:57 +08:00
|
|
|
pkgfile.magic = magics[idx]
|
|
|
|
if not pkgfile.magic and _magic:
|
|
|
|
pkgfile.magic = _magic.file(pkgfile.path)
|
|
|
|
if pkgfile.magic is None:
|
|
|
|
pkgfile.magic = ''
|
2009-08-31 05:04:24 +08:00
|
|
|
elif Pkg._magic_from_compressed_re.search(pkgfile.magic):
|
|
|
|
# Discard magic from inside compressed files ("file -z")
|
|
|
|
# until PkgFile gets decompression support. We may get
|
|
|
|
# such magic strings from package headers already now;
|
|
|
|
# for example Fedora's rpmbuild as of F-11's 4.7.1 is
|
|
|
|
# patched so it generates them.
|
|
|
|
pkgfile.magic = ''
|
2010-11-05 00:44:11 +08:00
|
|
|
if filecaps:
|
|
|
|
pkgfile.filecaps = filecaps[idx]
|
2009-02-02 05:58:38 +08:00
|
|
|
self._files[pkgfile.name] = pkgfile
|
2000-08-18 13:39:25 +08:00
|
|
|
|
2000-06-15 20:07:22 +08:00
|
|
|
# API to access dependency information
|
2001-08-22 00:53:05 +08:00
|
|
|
def obsoletes(self):
|
2010-04-16 05:42:11 +08:00
|
|
|
"""Get package Obsoletes as list of
|
|
|
|
(name, flags, (epoch, version, release)) tuples."""
|
2001-08-22 00:53:05 +08:00
|
|
|
self._gatherDepInfo()
|
|
|
|
return self._obsoletes
|
2005-11-28 05:49:15 +08:00
|
|
|
|
2000-06-15 20:07:22 +08:00
|
|
|
def requires(self):
|
2010-04-16 05:42:11 +08:00
|
|
|
"""Get package Requires as list of
|
|
|
|
(name, flags, (epoch, version, release)) tuples."""
|
2000-06-15 20:07:22 +08:00
|
|
|
self._gatherDepInfo()
|
|
|
|
return self._requires
|
2005-11-28 05:49:15 +08:00
|
|
|
|
2000-06-15 20:07:22 +08:00
|
|
|
def prereq(self):
|
2010-04-16 05:42:11 +08:00
|
|
|
"""Get package PreReqs as list of
|
|
|
|
(name, flags, (epoch, version, release)) tuples."""
|
2000-06-15 20:07:22 +08:00
|
|
|
self._gatherDepInfo()
|
|
|
|
return self._prereq
|
|
|
|
|
2001-02-17 00:36:10 +08:00
|
|
|
def req_names(self):
|
|
|
|
if self._req_names == -1:
|
2009-01-27 07:10:01 +08:00
|
|
|
self._req_names = [x[0] for x in self.requires() + self.prereq()]
|
2001-02-17 00:36:10 +08:00
|
|
|
return self._req_names
|
2002-02-08 04:06:50 +08:00
|
|
|
|
|
|
|
def check_versioned_dep(self, name, version):
|
2009-07-27 02:35:04 +08:00
|
|
|
# try to match name%_isa as well (e.g. "foo(x86-64)", "foo(x86-32)")
|
|
|
|
name_re = re.compile('^%s(\(\w+-\d+\))?$' % re.escape(name))
|
2008-10-30 07:01:44 +08:00
|
|
|
for d in self.requires() + self.prereq():
|
2009-07-27 02:35:04 +08:00
|
|
|
if name_re.match(d[0]):
|
2010-04-17 03:16:52 +08:00
|
|
|
if d[1] & rpm.RPMSENSE_EQUAL != rpm.RPMSENSE_EQUAL \
|
|
|
|
or d[2][1] != version:
|
2009-06-17 06:13:20 +08:00
|
|
|
return False
|
|
|
|
return True
|
|
|
|
return False
|
2002-02-08 04:06:50 +08:00
|
|
|
|
2000-06-15 20:07:22 +08:00
|
|
|
def conflicts(self):
|
2010-04-16 05:42:11 +08:00
|
|
|
"""Get package Conflicts as list of
|
|
|
|
(name, flags, (epoch, version, release)) tuples."""
|
2000-06-15 20:07:22 +08:00
|
|
|
self._gatherDepInfo()
|
|
|
|
return self._conflicts
|
2005-11-28 05:49:15 +08:00
|
|
|
|
2000-06-15 20:07:22 +08:00
|
|
|
def provides(self):
|
2010-04-16 05:42:11 +08:00
|
|
|
"""Get package Provides as list of
|
|
|
|
(name, flags, (epoch, version, release)) tuples."""
|
2000-06-15 20:07:22 +08:00
|
|
|
self._gatherDepInfo()
|
|
|
|
return self._provides
|
|
|
|
|
|
|
|
# internal function to gather dependency info used by the above ones
|
2010-04-16 05:42:11 +08:00
|
|
|
def _gather_aux(self, header, list, nametag, flagstag, versiontag,
|
2010-02-21 19:28:42 +08:00
|
|
|
prereq = None):
|
2001-08-22 00:53:05 +08:00
|
|
|
names = header[nametag]
|
|
|
|
flags = header[flagstag]
|
2010-04-16 05:42:11 +08:00
|
|
|
versions = header[versiontag]
|
2005-08-10 13:05:21 +08:00
|
|
|
|
2001-08-22 00:53:05 +08:00
|
|
|
if versions:
|
|
|
|
for loop in range(len(versions)):
|
2010-04-16 05:42:11 +08:00
|
|
|
evr = stringToVersion(versions[loop])
|
2009-01-27 07:10:01 +08:00
|
|
|
if prereq is not None and flags[loop] & PREREQ_FLAG:
|
2010-04-16 05:42:11 +08:00
|
|
|
prereq.append((names[loop], flags[loop] & (~PREREQ_FLAG),
|
|
|
|
evr))
|
2001-08-22 00:53:05 +08:00
|
|
|
else:
|
2010-04-16 05:42:11 +08:00
|
|
|
list.append((names[loop], flags[loop], evr))
|
2005-11-28 05:49:15 +08:00
|
|
|
|
2000-06-15 20:07:22 +08:00
|
|
|
def _gatherDepInfo(self):
|
2009-01-27 07:10:01 +08:00
|
|
|
if self._requires is None:
|
2000-06-15 20:07:22 +08:00
|
|
|
self._requires = []
|
|
|
|
self._prereq = []
|
|
|
|
self._provides = []
|
2000-06-27 16:12:48 +08:00
|
|
|
self._conflicts = []
|
2001-08-22 00:53:05 +08:00
|
|
|
self._obsoletes = []
|
|
|
|
|
|
|
|
self._gather_aux(self.header, self._requires,
|
|
|
|
rpm.RPMTAG_REQUIRENAME,
|
|
|
|
rpm.RPMTAG_REQUIREFLAGS,
|
2010-04-16 05:42:11 +08:00
|
|
|
rpm.RPMTAG_REQUIREVERSION,
|
2001-08-22 00:53:05 +08:00
|
|
|
self._prereq)
|
|
|
|
self._gather_aux(self.header, self._conflicts,
|
|
|
|
rpm.RPMTAG_CONFLICTNAME,
|
2010-04-16 05:42:11 +08:00
|
|
|
rpm.RPMTAG_CONFLICTFLAGS,
|
|
|
|
rpm.RPMTAG_CONFLICTVERSION)
|
2001-08-22 00:53:05 +08:00
|
|
|
self._gather_aux(self.header, self._provides,
|
|
|
|
rpm.RPMTAG_PROVIDENAME,
|
2010-04-16 05:42:11 +08:00
|
|
|
rpm.RPMTAG_PROVIDEFLAGS,
|
|
|
|
rpm.RPMTAG_PROVIDEVERSION)
|
2001-08-22 00:53:05 +08:00
|
|
|
self._gather_aux(self.header, self._obsoletes,
|
|
|
|
rpm.RPMTAG_OBSOLETENAME,
|
2010-04-16 05:42:11 +08:00
|
|
|
rpm.RPMTAG_OBSOLETEFLAGS,
|
|
|
|
rpm.RPMTAG_OBSOLETEVERSION)
|
2001-06-07 00:18:31 +08:00
|
|
|
|
2006-06-06 04:45:07 +08:00
|
|
|
def getInstalledPkgs(name):
|
|
|
|
"""Get list of installed package objects by name."""
|
2009-02-11 05:16:43 +08:00
|
|
|
|
2006-06-06 04:45:07 +08:00
|
|
|
pkgs = []
|
2009-02-11 05:16:43 +08:00
|
|
|
ts = rpm.TransactionSet()
|
2009-09-03 02:23:11 +08:00
|
|
|
if re.search('[?*]|\[.+\]', name):
|
2009-02-11 05:16:43 +08:00
|
|
|
mi = ts.dbMatch()
|
|
|
|
mi.pattern("name", rpm.RPMMIRE_GLOB, name)
|
2006-06-06 04:45:07 +08:00
|
|
|
else:
|
2009-02-11 05:16:43 +08:00
|
|
|
mi = ts.dbMatch("name", name)
|
|
|
|
|
|
|
|
for hdr in mi:
|
|
|
|
pkgs.append(InstalledPkg(name, hdr))
|
2010-03-20 02:40:59 +08:00
|
|
|
|
2006-06-06 04:45:07 +08:00
|
|
|
return pkgs
|
|
|
|
|
2001-06-07 00:18:31 +08:00
|
|
|
# Class to provide an API to an installed package
|
|
|
|
class InstalledPkg(Pkg):
|
2009-02-11 05:16:43 +08:00
|
|
|
def __init__(self, name, hdr = None):
|
|
|
|
if not hdr:
|
|
|
|
ts = rpm.TransactionSet()
|
|
|
|
mi = ts.dbMatch('name', name)
|
|
|
|
if not mi:
|
|
|
|
raise KeyError(name)
|
|
|
|
try:
|
|
|
|
hdr = mi.next()
|
|
|
|
except StopIteration:
|
|
|
|
raise KeyError(name)
|
|
|
|
|
|
|
|
Pkg.__init__(self, name, '/', hdr)
|
|
|
|
|
2009-06-17 06:13:20 +08:00
|
|
|
self.extracted = True
|
2002-04-25 04:03:00 +08:00
|
|
|
# create a fake filename to satisfy some checks on the filename
|
2010-02-21 19:28:42 +08:00
|
|
|
self.filename = '%s-%s-%s.%s.rpm' % \
|
|
|
|
(self.name, self[rpm.RPMTAG_VERSION], self[rpm.RPMTAG_RELEASE],
|
|
|
|
self[rpm.RPMTAG_ARCH])
|
2005-11-28 05:49:15 +08:00
|
|
|
|
2001-06-07 00:18:31 +08:00
|
|
|
def cleanup(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def checkSignature(self):
|
|
|
|
return (0, 'fake: pgp md5 OK')
|
|
|
|
|
2007-05-15 06:02:01 +08:00
|
|
|
# Class to provide an API to a "fake" package, eg. for specfile-only checks
|
|
|
|
class FakePkg:
|
|
|
|
def __init__(self, name):
|
|
|
|
self.name = name
|
|
|
|
self.arch = None
|
2009-02-26 03:51:33 +08:00
|
|
|
self.current_linenum = None
|
2007-05-15 06:02:01 +08:00
|
|
|
|
|
|
|
def cleanup(self):
|
|
|
|
pass
|
|
|
|
|
2009-02-02 05:58:38 +08:00
|
|
|
# Class for files in packages
|
|
|
|
class PkgFile(object):
|
|
|
|
|
|
|
|
def __init__(self, name):
|
|
|
|
self.name = name
|
|
|
|
# Real path to the file (taking extract dir into account)
|
|
|
|
self.path = name
|
|
|
|
self.flags = 0
|
|
|
|
self.mode = 0
|
|
|
|
self.user = None
|
|
|
|
self.group = None
|
|
|
|
self.linkto = ''
|
|
|
|
self.size = None
|
|
|
|
self.md5 = None
|
|
|
|
self.mtime = 0
|
|
|
|
self.rdev = ''
|
|
|
|
self.inode = 0
|
2010-04-17 03:07:15 +08:00
|
|
|
self.requires = []
|
2010-04-17 03:17:24 +08:00
|
|
|
self.provides = []
|
2009-02-02 05:58:38 +08:00
|
|
|
self.lang = ''
|
2009-08-31 05:04:24 +08:00
|
|
|
self.magic = ''
|
2010-11-05 00:44:11 +08:00
|
|
|
self.filecaps = None
|
2009-08-31 05:04:24 +08:00
|
|
|
|
|
|
|
# TODO: decompression support
|
2009-02-02 05:58:38 +08:00
|
|
|
|
2009-02-11 05:16:43 +08:00
|
|
|
is_config = property(lambda self: self.flags & rpm.RPMFILE_CONFIG)
|
|
|
|
is_doc = property(lambda self: self.flags & rpm.RPMFILE_DOC)
|
|
|
|
is_noreplace = property(lambda self: self.flags & rpm.RPMFILE_NOREPLACE)
|
|
|
|
is_ghost = property(lambda self: self.flags & rpm.RPMFILE_GHOST)
|
|
|
|
is_missingok = property(lambda self: self.flags & rpm.RPMFILE_MISSINGOK)
|
2009-02-02 05:58:38 +08:00
|
|
|
|
|
|
|
|
2000-06-15 20:07:22 +08:00
|
|
|
if __name__ == '__main__':
|
|
|
|
for p in sys.argv[1:]:
|
2008-10-30 07:01:44 +08:00
|
|
|
pkg = Pkg(sys.argv[1], tempfile.gettempdir())
|
2009-11-06 04:08:59 +08:00
|
|
|
print ('Requires: %s' % pkg.requires())
|
|
|
|
print ('Prereq: %s' % pkg.prereq())
|
|
|
|
print ('Conflicts: %s' % pkg.conflicts())
|
|
|
|
print ('Provides: %s' % pkg.provides())
|
|
|
|
print ('Obsoletes: %s' % pkg.obsoletes())
|
2000-06-15 20:07:22 +08:00
|
|
|
pkg.cleanup()
|
2005-11-28 05:49:15 +08:00
|
|
|
|
1999-10-01 15:15:25 +08:00
|
|
|
# Pkg.py ends here
|
2006-04-01 16:09:17 +08:00
|
|
|
|
|
|
|
# Local variables:
|
|
|
|
# indent-tabs-mode: nil
|
|
|
|
# py-indent-offset: 4
|
|
|
|
# End:
|
|
|
|
# ex: ts=4 sw=4 et
|