From: Michael Tremer Date: Sun, 31 May 2009 11:11:08 +0000 (+0200) Subject: Initial import of pakfire. X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3db0947cc44b52901fa0a4ae2e0ffc26d34fcaf6;p=ipfire-3.x.git Initial import of pakfire. --- diff --git a/lfs/pakfire b/lfs/pakfire new file mode 100644 index 000000000..d9e46778c --- /dev/null +++ b/lfs/pakfire @@ -0,0 +1,82 @@ +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007, 2008, 2009 Michael Tremer & Christian Schmidt # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +############################################################################### +# Definitions +############################################################################### + +include Config + +PKG_NAME = pakfire +PKG_VER = 0.9 +PKG_REL = 0 + +THISAPP = $(PKG_NAME)-$(PKG_VER) +DIR_APP = $(DIR_SOURCE)/$(PKG_NAME) + +OBJECT = $(DIR_INFO)/$(STAGE_ORDER)_$(STAGE)/$(THISAPP) + +MAINTAINER = Michael Tremer +GROUP = System/Packaging +CORE = yes +EXTRA = no +DEBUG = no +BUILD_DEPS = +DEPS = cpio xz python python-sqlite2 python-urlgrabber system-release + +URL = http://www.ipfire.org/ +LICENSE = GPLv3+ +SHORT_DESC = Package installer/updater. + +define LONG_DESC + Pakfire optains package lists from the mirrors and can install and update \ + packages. +endef + +############################################################################### +# Top-level Rules +############################################################################### + +objects = + +download: $(objects) + +info: + $(DO_PKG_INFO) + +install: $(OBJECT) + +package: + @$(DO_PACKAGE) + +$(objects): + @$(LOAD) + +############################################################################### +# Installation Details +############################################################################### + +$(OBJECT): $(objects) + @$(PREBUILD) + cd $(DIR_APP) && make VERSION="$(PKG_VER)" $(PARALLELISMFLAGS) + cd $(DIR_APP) && make install + cd $(DIR_APP) && make clean + $(PYTHON_COMPILE) + @$(POSTBUILD) diff --git a/make.sh b/make.sh index bf45e3970..1975e4270 100755 --- a/make.sh +++ b/make.sh @@ -254,6 +254,7 @@ ipfire_build() { ipfire_make nss_ldap ipfire_make ldapvi ipfire_make sqlite + ipfire_make python-sqlite2 ipfire_make curl ipfire_make pinentry ipfire_make gnupg2 @@ -344,6 +345,7 @@ ipfire_build() { ipfire_make pyfire ipfire_make network ipfire_make firewall + ipfire_make pakfire } ################################################################################ diff --git a/src/pakfire/Makefile b/src/pakfire/Makefile new file mode 100644 index 000000000..ed4061df1 --- /dev/null +++ b/src/pakfire/Makefile @@ -0,0 +1,19 @@ + +SUBSTITUDE = sed -e "s/@VERSION@/$(VERSION)/g" + +PYTHON_MAJ = "2.6" +PYTHON_DIR = $(DESTDIR)/usr/lib/python$(PYTHON_MAJ)/site-packages/pakfire + +all: pakfire + +pakfire: pakfire.in + $(SUBSTITUDE) pakfire.in > pakfire + +clean: + rm -vf pakfire python/*.py[co] + +install: pakfire + install -v -m 755 pakfire $(DESTDIR)/usr/bin + -mkdir -pv $(PYTHON_DIR) + cp -vf python/*.py $(PYTHON_DIR)/ + -mkdir -pv $(DESTDIR)/etc/pakfire.repos.d $(DESTDIR)/var/cache/pakfire diff --git a/src/pakfire/functions b/src/pakfire/functions new file mode 100755 index 000000000..3d9e02312 --- /dev/null +++ b/src/pakfire/functions @@ -0,0 +1,49 @@ +#!/bin/bash +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007, 2008, 2009 Michael Tremer & Christian Schmidt # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +function pkg_get_file() { + cpio --extract --quiet --to-stdout $1 < $2 +} + +function pkg_info() { + pkg_get_file info $1 +} + +function pkg_data() { + pkg_get_file data.img $1 +} + +function pkg_control() { + pkg_get_file control $1 +} + +function pkg_verify() { + local pak=$1 + ( + eval $(pkg_info $pak | grep ^PKG_DATA_SHA1) + if [ "$(pkg_data $pak | sha1sum | awk '{ print $1 }')" = "$PKG_DATA_SHA1" ]; then + exit 0 + else + exit 1 + fi + ) + return $? +} diff --git a/src/pakfire/pakfire.in b/src/pakfire/pakfire.in new file mode 100644 index 000000000..c861c33a1 --- /dev/null +++ b/src/pakfire/pakfire.in @@ -0,0 +1,61 @@ +#!/usr/bin/python + +import sys + +from optparse import OptionParser + +import pakfire +from pakfire.package import Package +from pakfire.transactionset import Transactionset + +__version__ = "@VERSION@" + +op = OptionParser(usage="%prog [options] ...", version="%prog " + __version__) +op.add_option("-r", "--root", dest="root", metavar="DIR") +op.add_option("-v", "--verbose", dest="verbose", + action="store_true", default=False, + help="Be verbose.") +op.add_option("-y", "--yes", dest="allyes", + action="store_true", default=False, + help="Answer all questions with \"yes\".") +op.add_option("--nogpgcheck", dest="gpgcheck", + action="store_false", default=True, + help="Omit gpg check (unsafe).") + +def usage(exit=1): + op.print_help() + sys.exit(exit) + +def parse_options(): + (options, args) = op.parse_args() + + # When no command was given + if not args: + usage() + + # The first argument is the action + action = args.pop(0) + if not action in ["install", "remove", "info", "provides",]: + op.error("\"%s\" is not a valid action." % action) + + if not args: + op.error("Action \"%s\" needs at least one argument.") + + return (action, options, args) + +(action, options, args) = parse_options() + +pakfire = pakfire.Pakfire() + +if action == "install": + pakfire.ts = Transactionset() + for a in args: + p = Package(a) + pakfire.ts.addPackage(p) + + pakfire.ts.run() + +elif action == "info": + for a in args: + p = Package(a) + print p.print_info() diff --git a/src/pakfire/python/__init__.py b/src/pakfire/python/__init__.py new file mode 100644 index 000000000..4cdc25344 --- /dev/null +++ b/src/pakfire/python/__init__.py @@ -0,0 +1,11 @@ +#!/usr/bin/python + +from repo import Repositories +from transactionset import Transactionset + +class Pakfire(object): + repos = Repositories() + ts = Transactionset() + + def __init__(self): + pass diff --git a/src/pakfire/python/db.py b/src/pakfire/python/db.py new file mode 100644 index 000000000..986b44d36 --- /dev/null +++ b/src/pakfire/python/db.py @@ -0,0 +1,52 @@ + + +from pysqlite2 import dbapi2 as sqlite + +DATABASE_PATH = "." + +class Database(object): + def __init__(self, filename): + self.filename = filename + + self.connection = sqlite.connect(self.filename) + + def add(self, table): + c = self.cursor + c.executescript("CREATE TABLE IF NOT EXISTS %s(id, key, value);" % table) + c.close() + + def commit(self): + self.connection.commit() + + def destroy(self, table): + c = self.cursor + c.execute("DELETE FROM %s" % table) + c.close() + self.commit() + + def get(self, table, id, key): + ret = None + c = self.cursor + c.execute("SELECT value FROM %s WHERE id='%s' AND key='%s';" % \ + (table, id, key,)) + try: + ret = c.fetchone()[0] + except TypeError: + pass + c.close() + return ret + + def set(self, table, id, key, value): + c = self.cursor + if not self.get(id, key): + c.execute("INSERT INTO %s(id, key, value) VALUES('%s', '%s', '%s');" \ + % (table, id, key, value,)) + else: + c.execute("UPDATE %s SET value='%s' WHERE id='%s' AND key='%s';" \ + % (table, value, id, key,)) + c.close() + self.commit() + + @property + def cursor(self): + return self.connection.cursor() diff --git a/src/pakfire/python/io.py b/src/pakfire/python/io.py new file mode 100644 index 000000000..2e142de24 --- /dev/null +++ b/src/pakfire/python/io.py @@ -0,0 +1,262 @@ +#!/usr/bin/python + +import grp +import os +import pwd +import stat +import time + +def ftype(mode): + if stat.S_ISBLK(mode): + return "b" + elif stat.S_ISCHR(mode): + return "c" + elif stat.S_ISDIR(mode): + return "d" + elif stat.S_ISREG(mode): + return "-" + elif stat.S_ISFIFO(mode): + return "p" + elif stat.S_ISLINK(mode): + return "l" + elif stat.S_ISSOCK(mode): + return "s" + return "?" + +def rwx(mode): + ret = "" + if mode & stat.S_IRUSR: + ret += "r" + else: + ret += "-" + + if mode & stat.S_IWUSR: + ret += "w" + else: + ret += "-" + + if mode & stat.S_IXUSR: + ret += "x" + else: + ret += "-" + + return ret + +def fmode(mode): + ret = ftype(mode) + ret += rwx((mode & 0700) << 0) + ret += rwx((mode & 0070) << 3) + ret += rwx((mode & 0007) << 6) + return ret + +class CpioError(Exception): + pass + + +class CpioEntry(object): + def __init__(self, hdr, archive, offset): + self.archive = archive + self.hdr = hdr + + self.offset = offset + 110 + self.namesize + self.offset += (4 - (self.offset % 4)) % 4 + self.current = 0 + + self.closed = False + + if len(self.hdr) < 110: + raise CpioError("Header too short.") + + if not self.hdr.startswith("070701") and not self.hdr.startswith("070702"): + raise CpioError("Invalid header: %s" % self.hdr[:6]) + + def close(self): + self.closed = True + + def flush(self): + pass # noop + + def read(self, size=None): + """Read data from the entry. + + Keyword arguments: + size -- Number of bytes to read (default: whole entry) + """ + if self.closed: + raise ValueError("Read operation on closed file.") + + self.archive.file.seek(self.offset + self.current, os.SEEK_SET) + + if size and size < self.size - self.current: + ret = self.archive.file.read(size) + else: + ret = self.archive.file.read(self.size - self.current) + self.current += len(ret) + return ret + + def seek(self, offset, whence=0): + """Move to new position within an entry. + + Keyword arguments: + offset -- Byte count + whence -- Describes how offset is used. + 0: From beginning of file + 1: Forwards from current position + 2: Backwards from current position + Other values are ignored. + """ + if self.closed: + raise ValueError("Seek operation on closed file.") + + if whence == os.SEEK_SET: + self.current = offset + elif whence == os.SEEK_REL: + self.current += offset + elif whence == os.SEEK_END: + self.current -= offset + + self.current = min(max(0, self.current), self.size) + + def tell(self): + """Get current position within an entry""" + if self.closed: + raise ValueError("Tell operation on closed file.") + return self.current + + def __repr__(self): + return "" % (self.name, self.checksum,) + + @property + def checksum(self): + return int(self.hdr[102:110], 16) + + @property + def devmajor(self): + return int(self.hdr[62:70], 16) + + @property + def devminor(self): + return int(self.hdr[70:78], 16) + + @property + def gid(self): + return int(self.hdr[30:38], 16) + + @property + def inode(self): + return int(self.hdr[6:14], 16) + + @property + def mode(self): + return int(self.hdr[14:22], 16) + + @property + def mtime(self): + return int(self.hdr[46:54], 16) + + @property + def name(self): + end = 110 + self.namesize - 1 + return self.hdr[110:end] + + @property + def namesize(self): + return int(self.hdr[94:102], 16) + + @property + def nlinks(self): + return int(self.hdr[38:46], 16) + + @property + def rdevmajor(self): + return int(self.hdr[78:86], 16) + + @property + def rdevminor(self): + return int(self.hdr[86:94], 16) + + @property + def size(self): + return int(self.hdr[54:62], 16) + + @property + def uid(self): + return int(self.hdr[22:30], 16) + + +class CpioArchive(object): + _entries = [] + file = None + + def __init__(self, filename): + + self.filename = filename + self.file = open(self.filename, "r") + self.__readfile() + + self.closed = False + + def close(self): + if self.closed: + return + self.closed = True + + self.file.close() + + def __readfile(self): + if not self.file: + raise CpioError("File was not yet opened.") + + self._entries = [] + sposition = self.file.tell() + hdr = self.file.read(110) + while hdr: + namelen = int(hdr[94:102], 16) # Length of the name + hdr += self.file.read(namelen) + ce = CpioEntry(hdr, self, sposition) + if ce.name == "TRAILER!!!": + return + self._entries.append(ce) + + self.file.seek((4 - (self.file.tell()-sposition) % 4) % 4, os.SEEK_CUR) + self.file.seek(ce.size, os.SEEK_CUR) + self.file.seek((4 - (self.file.tell()-sposition) % 4) % 4, os.SEEK_CUR) + + sposition = self.file.tell() + hdr = self.file.read(110) + else: + raise CpioError("Premature end of headers.") + + @property + def entries(self): + return sorted(self._entries) + + @property + def size(self): + return os.path.getsize(self.filename) + + def ls(self): + for x in self.entries: + print x.name + + def ll(self): + for x in self.entries: + print "%s %s %s %s %9d %s %s" % \ + (fmode(x.mode), + x.nlinks, + pwd.getpwuid(x.uid)[0], + grp.getgrgid(x.gid)[0], + x.size, + time.strftime("%Y-%m-%d %H:%M", time.localtime(x.mtime)), + x.name,) + + def get(self, item): + for x in self.entries: + if x.name == item: + return x + raise KeyError("No such file or directory.") + + def __getitem__(self, item): + x = self.get(item) + x.seek(0) + return x.read() diff --git a/src/pakfire/python/package.py b/src/pakfire/python/package.py new file mode 100644 index 000000000..4bbcb24f9 --- /dev/null +++ b/src/pakfire/python/package.py @@ -0,0 +1,151 @@ +#!/usr/bin/python + +import hashlib +import os +import subprocess + +import io + +class Package(object): + _info = {} + + def __init__(self, archive): + self.archive = io.CpioArchive(archive) + + def check(self): + print "Checking package %s..." % self.name + return self.verify() + + def extract(self, root="/"): + if not os.path.exists(root): + os.makedirs(root) + + lzma = subprocess.Popen(["lzma", "-dc"], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE,) + + cpio = subprocess.Popen(["cpio", + "--quiet", + "--extract", + "--unconditional", + "--make-directories", + "--no-absolute-filenames",], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + cwd=root) + + BUF = 1024 + file = self.archive["data.img"] + + # Decompress in one big swoop. + lzma.stdin.write(file) + lzma.stdin.close() + + lzmaerr = lzma.stderr.read() + if lzmaerr: + raise Exception("Decompression error: %s" % lzmaerr) + + while True: + buf = lzma.stdout.read(BUF) + if not buf: + break + cpio.stdin.write(buf) + lzma.stdout.close() + + cpioerr = cpio.stderr.read() + if cpioerr: + raise Exception("Archiving error: %s" % cpioerr) + + def install(self, root="/"): + print "Installing %s..." % self.name + self.extract(root) + + def print_info(self): + ret = "" + info = (("Name", self.name), + ("Version", self.version), + ("Arch", self.arch), + ("Release", self.release), + ("Size", self.size), + ("Summary", self.summary), + ("URL", self.url), + ("License", self.license), + ("Description", self.description)) + + for (key, value) in info: + ret += "%-12s: %s\n" % (key, value,) + + if self.verify: + ret += "%-12s: %s\n" % ("Signature", "OK") + else: + ret += "%-12s: %s\n" % ("Signature", "Broken") + + return ret + + def verify(self): + hash = hashlib.sha1(self.archive["data.img"]).hexdigest() + if hash == self.sha1: + return True + return False + + @property + def arch(self): + return self.info.get("PKG_ARCH", None) + + @property + def deps(self): + return self.info.get("PKG_DEPS", None) + + @property + def description(self): + return self.info.get("PKG_DESC", None) + + @property + def group(self): + return self.info.get("PKG_GROUP", None) + + @property + def license(self): + return self.info.get("PKG_LICENSE", None) + + @property + def name(self): + return self.info.get("PKG_NAME", None) + + @property + def info(self): + if not self._info: + self._info = {} + for line in self.archive["info"].split("\n"): + if not line or line.startswith("#"): + continue + (key, value) = line.split("=") + self._info[key] = value.strip("\"") + return self._info + + @property + def release(self): + return self.info.get("PKG_REL", None) + + @property + def sha1(self): + return self.info.get("PKG_DATA_SHA1", None) + + @property + def size(self): + return self.archive.size + + @property + def summary(self): + return self.info.get("PKG_SUMMARY", None) + + @property + def url(self): + return self.info.get("PKG_URL", None) + + @property + def version(self): + return self.info.get("PKG_VER", None) + diff --git a/src/pakfire/python/repo.py b/src/pakfire/python/repo.py new file mode 100644 index 000000000..2e6214f95 --- /dev/null +++ b/src/pakfire/python/repo.py @@ -0,0 +1,88 @@ + +import os +import ConfigParser as configparser +import urlgrabber + +import db as database +from servers import Servers + +REPOS_PATH = "/etc/pakfire.repos.d" + +class Repositories(object): + _repositories = [] + + def __init__(self): + for file in os.listdir(REPOS_PATH): + if not file.endswith(".repo"): + continue + cp = configparser.ConfigParser() + cp.read(os.path.join(REPOS_PATH, file)) + for section in cp.sections(): + self._repositories.append(Repository(section, cp.items(section))) + + @property + def all(self): + return sorted(self._repositories) + + @property + def enabled(self): + ret = [] + for r in self._repositories: + if r.enabled: + ret.append(r) + return ret + + @property + def repositories(self): + return self.enabled + +class Repository(object): + def __init__(self, name, items=None): + self.name = name + + if items: + config = {} + for (key, value) in items: + config[key] = value + self.config = config + + self.db = database.Database("%s.db" % self.name) + self.servers = Servers(self.db) + + def __cmp__(self, other): + return cmp(self.name, other.name) + + def __str__(self): + return self.name + + def __repr__(self): + return "" % self.name + + def update_mirrorlist(self, mirrorlist=None): + if not mirrorlist: + mirrorlist = self.mirrorlist + f = urlgrabber.urlopen(mirrorlist) + for line in f.readlines(): + self.servers.add(line) + f.close() + + @property + def enabled(self): + value = self.config.get("enabled") + if value == "1": + return True + return False + + @property + def gpgkey(self): + return self.config.get("gpgkey", None) + + @property + def mirrorlist(self): + return self.config.get("mirrorlist", None) + + +if __name__ == "__main__": + rs = Repositories() + for r in rs.repositories: + r.update_mirrorlist() diff --git a/src/pakfire/python/servers.py b/src/pakfire/python/servers.py new file mode 100644 index 000000000..d27617d53 --- /dev/null +++ b/src/pakfire/python/servers.py @@ -0,0 +1,88 @@ + +import random +import urlparse +import uuid + +SERVER_DEFAULT_PROTOCOL = "http" +SERVER_DEFAULT_PORT = "80" +SERVER_DEFAULT_PATH = "" + +class Servers(object): + table = "servers" + + def __init__(self, db): + self.db = db + self.db.add(self.table) + + def add(self, url): + p = urlparse.urlparse(url) + for server in self.all: + if str(server) == url: + return + server = Server(self.db) + (server.protocol, server.hostname, server.port, server.path) = \ + (p.scheme, p.hostname, p.port, p.path) + + @property + def all(self): + ret = [] + c = self.db.cursor + c.execute("SELECT DISTINCT id FROM %s" % self.table) + for id in c.fetchall(): + s = Server(self.db, id) + ret.append(s) + return sorted(ret) + + @property + def random(self): + return random.choice(self.all) + + +class Server(object): + table = Servers.table + + def __init__(self, db, id=None): + self.db = db + + if not id: + id = str(uuid.uuid4()) + self.id = "%s" % id + + def __str__(self): + return urlparse.urlunparse((self.protocol, \ + "%s:%s" % (self.hostname, self.port), self.path, None, None, None)) + + def __repr__(self): + return "" % self.id + + def _getHostname(self): + return self.db.get(self.table, self.id, "hostname") + + def _setHostname(self, hostname): + return self.db.set(self.table, self.id, "hostname", hostname) + + hostname = property(_getHostname, _setHostname) + + def _getProtocol(self): + return self.db.get(self.table, self.id, "protocol") or SERVER_DEFAULT_PROTOCOL + + def _setProtocol(self, protocol): + return self.db.set(self.table, self.id, "protocol", protocol) + + protocol = property(_getProtocol, _setProtocol) + + def _getPort(self): + return self.db.get(self.table, self.id, "port") or SERVER_DEFAULT_PORT + + def _setPort(self, port): + return self.db.set(self.table, self.id, "port", port) + + port = property(_getPort, _setPort) + + def _getPath(self): + return self.db.get(self.table, self.id, "path") or SERVER_DEFAULT_PATH + + def _setPath(self, path): + return self.db.set(self.table, self.id, "path", path) + + path = property(_getPath, _setPath) diff --git a/src/pakfire/python/transactionset.py b/src/pakfire/python/transactionset.py new file mode 100644 index 000000000..7a1c95f6f --- /dev/null +++ b/src/pakfire/python/transactionset.py @@ -0,0 +1,35 @@ + + +class Transactionset(object): + _packages = [] + + def __init__(self): + pass + + def addPackage(self, package): + self._packages.append(package) + + def check(self): + print "Checking Transactionset..." + for package in self.packages: + if not package.check(): + return False + return True + + def install(self, root="/"): + for package in self.packages: + package.install(root) + + def resolveDeps(self): + pass + + def run(self, root="/"): + print "Running transactionset..." + if self.check(): + self.install(root) + else: + print "Error, when verifying the packages..." + + @property + def packages(self): + return sorted(self._packages) diff --git a/src/rootfiles/core/pakfire b/src/rootfiles/core/pakfire new file mode 100644 index 000000000..d34124594 --- /dev/null +++ b/src/rootfiles/core/pakfire @@ -0,0 +1,25 @@ +etc/pakfire.repos.d +usr/bin/pakfire +usr/lib/python2.6/site-packages/pakfire +usr/lib/python2.6/site-packages/pakfire/__init__.py +usr/lib/python2.6/site-packages/pakfire/__init__.pyc +usr/lib/python2.6/site-packages/pakfire/__init__.pyo +usr/lib/python2.6/site-packages/pakfire/db.py +usr/lib/python2.6/site-packages/pakfire/db.pyc +usr/lib/python2.6/site-packages/pakfire/db.pyo +usr/lib/python2.6/site-packages/pakfire/io.py +usr/lib/python2.6/site-packages/pakfire/io.pyc +usr/lib/python2.6/site-packages/pakfire/io.pyo +usr/lib/python2.6/site-packages/pakfire/package.py +usr/lib/python2.6/site-packages/pakfire/package.pyc +usr/lib/python2.6/site-packages/pakfire/package.pyo +usr/lib/python2.6/site-packages/pakfire/repo.py +usr/lib/python2.6/site-packages/pakfire/repo.pyc +usr/lib/python2.6/site-packages/pakfire/repo.pyo +usr/lib/python2.6/site-packages/pakfire/servers.py +usr/lib/python2.6/site-packages/pakfire/servers.pyc +usr/lib/python2.6/site-packages/pakfire/servers.pyo +usr/lib/python2.6/site-packages/pakfire/transactionset.py +usr/lib/python2.6/site-packages/pakfire/transactionset.pyc +usr/lib/python2.6/site-packages/pakfire/transactionset.pyo +var/cache/pakfire